-
Notifications
You must be signed in to change notification settings - Fork 54
Add join() function #1086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add join() function #1086
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
dc0635f
Add join() function
Gijsreyn 56b21d7
Rewrite examples for real-world examples
Gijsreyn 8e16b51
Add join() function
Gijsreyn 79f8e32
Rewrite examples for real-world examples
Gijsreyn 71b5b3a
Merge branch 'add-join-function' of https://github.com/Gijsreyn/opera…
Gijsreyn 90a6b65
Convert function to accept only array
Gijsreyn f2c505e
Remark CoPilot
Gijsreyn 45eae9e
update test
Gijsreyn d1293b4
Merge branch 'main' of https://github.com/Gijsreyn/operation-methods …
Gijsreyn c7bb77b
Fix remarks
Gijsreyn 02a7d5b
Localize string messages
Gijsreyn File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| --- | ||
| description: Reference for the 'join' DSC configuration document function | ||
| ms.date: 08/29/2025 | ||
| ms.topic: reference | ||
| title: join | ||
| --- | ||
|
|
||
| ## Synopsis | ||
|
|
||
| Joins an array into a single string, separated using a delimiter. | ||
|
|
||
| ## Syntax | ||
|
|
||
| ```Syntax | ||
| join(inputArray, delimiter) | ||
| ``` | ||
|
|
||
| ## Description | ||
|
|
||
| The `join()` function takes an array and a delimiter. | ||
|
|
||
| - Each array element is converted to a string and concatenated with the | ||
| delimiter between elements. | ||
|
|
||
| The `delimiter` can be any value; it’s converted to a string. | ||
|
|
||
| ## Examples | ||
|
|
||
| ### Example 1 - Produce a list of servers | ||
|
|
||
| Create a comma-separated string from a list of host names to pass to tools or | ||
| APIs that accept CSV input. This example uses [`createArray()`][02] to build | ||
| the server list and joins with ", ". | ||
|
|
||
| ```yaml | ||
| # join.example.1.dsc.config.yaml | ||
| $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json | ||
| resources: | ||
| - name: Echo | ||
| type: Microsoft.DSC.Debug/Echo | ||
| properties: | ||
| output: "[join(createArray('web01','web02','web03'), ', ')]" | ||
| ``` | ||
|
|
||
| ```bash | ||
| dsc config get --file join.example.1.dsc.config.yaml | ||
| ``` | ||
|
|
||
| ```yaml | ||
| results: | ||
| - name: Echo | ||
| type: Microsoft.DSC.Debug/Echo | ||
| result: | ||
| actualState: | ||
| output: web01, web02, web03 | ||
| messages: [] | ||
| hadErrors: false | ||
| ``` | ||
|
|
||
| ### Example 2 - Build a file system path from segments | ||
|
|
||
| Join path segments into a single path string. This is useful when composing | ||
| paths dynamically from parts. | ||
|
|
||
| ```yaml | ||
| # join.example.2.dsc.config.yaml | ||
| $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json | ||
| resources: | ||
| - name: Echo | ||
| type: Microsoft.DSC.Debug/Echo | ||
| properties: | ||
| output: "[join(createArray('/etc','nginx','sites-enabled'), '/')]" | ||
| ``` | ||
|
|
||
| ```bash | ||
| dsc config get --file join.example.2.dsc.config.yaml | ||
| ``` | ||
|
|
||
| ```yaml | ||
| results: | ||
| - name: Echo | ||
| type: Microsoft.DSC.Debug/Echo | ||
| result: | ||
| actualState: | ||
| output: /etc/nginx/sites-enabled | ||
| messages: [] | ||
| hadErrors: false | ||
| ``` | ||
|
|
||
| ### Example 3 - Format a version string from numeric parts | ||
|
|
||
| Convert version components (numbers) into a dotted version string. Non-string | ||
| elements are converted to strings automatically. | ||
|
|
||
| ```yaml | ||
| # join.example.3.dsc.config.yaml | ||
| $schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json | ||
| resources: | ||
| - name: Echo | ||
| type: Microsoft.DSC.Debug/Echo | ||
| properties: | ||
| output: "[join(createArray(1,2,3), '.')]" | ||
| ``` | ||
|
|
||
| ```bash | ||
| dsc config get --file join.example.3.dsc.config.yaml | ||
| ``` | ||
|
|
||
| ```yaml | ||
| results: | ||
| - name: Echo | ||
| type: Microsoft.DSC.Debug/Echo | ||
| result: | ||
| actualState: | ||
| output: 1.2.3 | ||
| messages: [] | ||
| hadErrors: false | ||
| ``` | ||
|
|
||
| ## Parameters | ||
|
|
||
| ### inputArray | ||
|
|
||
| The array whose elements will be concatenated. | ||
|
|
||
| ```yaml | ||
| Type: array | ||
| Required: true | ||
| Position: 1 | ||
| ``` | ||
|
|
||
| ### delimiter | ||
|
|
||
| Any value used between elements. Converted to a string. | ||
|
|
||
| ```yaml | ||
| Type: any | ||
| Required: true | ||
| Position: 2 | ||
| ``` | ||
|
|
||
| ## Output | ||
|
|
||
| Returns a string containing the joined result. | ||
|
|
||
| ```yaml | ||
| Type: string | ||
| ``` | ||
|
|
||
| ## Related functions | ||
|
|
||
| - [`concat()`][00] - Concatenates strings together | ||
| - [`string()`][01] - Converts values to strings | ||
|
|
||
| <!-- Link reference definitions --> | ||
| [00]: ./concat.md | ||
| [01]: ./string.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| use crate::DscError; | ||
| use crate::configure::context::Context; | ||
| use crate::functions::{FunctionArgKind, Function, FunctionCategory, FunctionMetadata}; | ||
| use rust_i18n::t; | ||
| use serde_json::Value; | ||
| use tracing::debug; | ||
|
|
||
| #[derive(Debug, Default)] | ||
| pub struct Join {} | ||
|
|
||
| fn stringify_value(v: &Value) -> Result<String, DscError> { | ||
| match v { | ||
| Value::String(s) => Ok(s.clone()), | ||
| Value::Number(n) => Ok(n.to_string()), | ||
| Value::Bool(b) => Ok(b.to_string()), | ||
| Value::Null => Err(DscError::Parser(t!("functions.join.invalidNullElement").to_string())), | ||
| Value::Array(_) => Err(DscError::Parser(t!("functions.join.invalidArrayElement").to_string())), | ||
| Value::Object(_) => Err(DscError::Parser(t!("functions.join.invalidObjectElement").to_string())), | ||
| } | ||
| } | ||
|
|
||
| impl Function for Join { | ||
| fn get_metadata(&self) -> FunctionMetadata { | ||
| FunctionMetadata { | ||
| name: "join".to_string(), | ||
| description: t!("functions.join.description").to_string(), | ||
| category: FunctionCategory::String, | ||
| min_args: 2, | ||
| max_args: 2, | ||
| accepted_arg_ordered_types: vec![ | ||
| vec![FunctionArgKind::Array], | ||
| vec![FunctionArgKind::String], | ||
| ], | ||
| remaining_arg_accepted_types: None, | ||
| return_types: vec![FunctionArgKind::String], | ||
| } | ||
| } | ||
|
|
||
| fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> { | ||
| debug!("{}", t!("functions.join.invoked")); | ||
|
|
||
| let delimiter = args[1].as_str().unwrap(); | ||
|
|
||
| if let Some(array) = args[0].as_array() { | ||
| let items: Result<Vec<String>, DscError> = array.iter().map(stringify_value).collect(); | ||
| let items = items?; | ||
| return Ok(Value::String(items.join(delimiter))); | ||
| } | ||
|
|
||
| Err(DscError::Parser(t!("functions.join.invalidArrayArg").to_string())) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use crate::configure::context::Context; | ||
| use crate::parser::Statement; | ||
| use super::Join; | ||
| use crate::functions::Function; | ||
|
|
||
| #[test] | ||
| fn join_array_of_strings() { | ||
| let mut parser = Statement::new().unwrap(); | ||
| let result = parser.parse_and_execute("[join(createArray('a','b','c'), '-')]", &Context::new()).unwrap(); | ||
| assert_eq!(result, "a-b-c"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn join_empty_array_returns_empty() { | ||
| let mut parser = Statement::new().unwrap(); | ||
| let result = parser.parse_and_execute("[join(createArray(), '-')]", &Context::new()).unwrap(); | ||
| assert_eq!(result, ""); | ||
| } | ||
|
|
||
| #[test] | ||
| fn join_array_of_integers() { | ||
| let mut parser = Statement::new().unwrap(); | ||
| let result = parser.parse_and_execute("[join(createArray(1,2,3), ',')]", &Context::new()).unwrap(); | ||
| assert_eq!(result, "1,2,3"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn join_array_with_null_fails() { | ||
| let mut parser = Statement::new().unwrap(); | ||
| let result = parser.parse_and_execute("[join(createArray('a', null()), ',')]", &Context::new()); | ||
| assert!(result.is_err()); | ||
| // The error comes from argument validation, not our function | ||
| assert!(result.unwrap_err().to_string().contains("does not accept null arguments")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn join_array_with_array_fails() { | ||
| let mut parser = Statement::new().unwrap(); | ||
| let result = parser.parse_and_execute("[join(createArray('a', createArray('b')), ',')]", &Context::new()); | ||
| assert!(result.is_err()); | ||
| let error_msg = result.unwrap_err().to_string(); | ||
| assert!(error_msg.contains("Arguments must all be arrays") || error_msg.contains("mixed types")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn join_array_with_object_fails() { | ||
| let mut parser = Statement::new().unwrap(); | ||
| let result = parser.parse_and_execute("[join(createArray('a', createObject('key', 'value')), ',')]", &Context::new()); | ||
| assert!(result.is_err()); | ||
| let error_msg = result.unwrap_err().to_string(); | ||
| assert!(error_msg.contains("Arguments must all be") || error_msg.contains("mixed types")); | ||
| } | ||
|
|
||
| #[test] | ||
| fn join_direct_test_with_mixed_array() { | ||
| use serde_json::json; | ||
| use crate::configure::context::Context; | ||
|
|
||
| let join_fn = Join::default(); | ||
| let args = vec![ | ||
| json!(["hello", {"key": "value"}]), // Array with string and object | ||
| json!(",") | ||
| ]; | ||
| let result = join_fn.invoke(&args, &Context::new()); | ||
|
|
||
| assert!(result.is_err()); | ||
| let error_msg = result.unwrap_err().to_string(); | ||
| assert!(error_msg.contains("Array elements cannot be objects")); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.