Skip to content

Commit

Permalink
Merge pull request #322 from SteveL-MSFT/concat-array
Browse files Browse the repository at this point in the history
Fix `concat()` function and add `createArray()` function
  • Loading branch information
SteveL-MSFT authored Feb 23, 2024
2 parents de958e9 + 746632b commit 07c646e
Show file tree
Hide file tree
Showing 18 changed files with 360 additions and 177 deletions.
2 changes: 1 addition & 1 deletion dsc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@ serde_json = { version = "1.0", features = ["preserve_order"] }
serde_yaml = { version = "0.9.3" }
syntect = { version = "5.0", features = ["default-fancy"], default-features = false }
sysinfo = { version = "0.29.10" }
thiserror = "1.0"
thiserror = "1.0.52"
tracing = { version = "0.1.37" }
tracing-subscriber = { version = "0.3.17", features = ["ansi", "env-filter", "json"] }
2 changes: 1 addition & 1 deletion dsc/tests/dsc_config_get.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Describe 'dsc config get tests' {
- name: Echo
type: Test/Echo
properties:
text: hello
output: hello
"@
$null = $config_yaml | dsc config get --format pretty-json | Out-String
$LASTEXITCODE | Should -Be 0
Expand Down
6 changes: 3 additions & 3 deletions dsc/tests/dsc_functions.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Describe 'tests for function expressions' {
It 'function works: <text>' -TestCases @(
@{ text = "[concat('a', 'b')]"; expected = 'ab' }
@{ text = "[concat('a', 'b', 'c')]"; expected = 'abc' }
@{ text = "[concat('a', 1, concat(2, 'b'))]"; expected = 'a12b' }
@{ text = "[concat('a', concat('b', 'c'))]"; expected = 'abc' }
@{ text = "[base64('ab')]"; expected = 'YWI=' }
@{ text = "[base64(concat('a','b'))]"; expected = 'YWI=' }
@{ text = "[base64(base64(concat('a','b')))]"; expected = 'WVdJPQ==' }
Expand All @@ -19,9 +19,9 @@ Describe 'tests for function expressions' {
- name: Echo
type: Test/Echo
properties:
text: '$escapedText'
output: '$escapedText'
"@
$out = $config_yaml | dsc config get | ConvertFrom-Json
$out.results[0].result.actualState.text | Should -Be $expected
$out.results[0].result.actualState.output | Should -Be $expected
}
}
57 changes: 36 additions & 21 deletions dsc/tests/dsc_parameters.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = 'hello' }} | ConvertTo-Json

Expand All @@ -31,16 +31,16 @@ Describe 'Parameters tests' {
}

$LASTEXITCODE | Should -Be 0
$out.results[0].result.actualState.text | Should -BeExactly 'hello'
$out.results[0].result.actualState.output | Should -BeExactly 'hello'
}

It 'Input is <type>' -TestCases @(
@{ type = 'string'; value = 'hello'; expected = 'hello' }
@{ type = 'int'; value = 42; expected = 42 }
@{ type = 'bool'; value = $true; expected = $true }
@{ type = 'array'; value = @('hello', 'world'); expected = '["hello","world"]' }
@{ type = 'string'; value = 'hello' }
@{ type = 'int'; value = 42}
@{ type = 'bool'; value = $true}
@{ type = 'array'; value = @('hello', 'world')}
) {
param($type, $value, $expected)
param($type, $value)

$config_yaml = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
Expand All @@ -51,13 +51,13 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json

$out = $config_yaml | dsc config -p $params_json get | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.results[0].result.actualState.text | Should -BeExactly $expected
$out.results[0].result.actualState.output | Should -BeExactly $value
}

It 'Input is incorrect type <type>' -TestCases @(
Expand All @@ -77,7 +77,7 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json

Expand All @@ -104,7 +104,7 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json

Expand All @@ -130,7 +130,7 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json

Expand All @@ -154,7 +154,7 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json

Expand All @@ -180,7 +180,7 @@ Describe 'Parameters tests' {
- name: Echo
type: Test/Echo
properties:
text: '[parameters(''param1'')]'
output: '[parameters(''param1'')]'
"@
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json

Expand All @@ -192,28 +192,43 @@ Describe 'Parameters tests' {
$config_yaml = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
parameters:
param1:
paramString:
type: string
defaultValue: 'hello'
param2:
paramInt:
type: int
defaultValue: 7
param3:
paramBool:
type: bool
defaultValue: false
param4:
paramArray:
type: array
defaultValue: ['hello', 'world']
resources:
- name: Echo
- name: String
type: Test/Echo
properties:
output: '[parameters(''paramString'')]'
- name: Int
type: Test/Echo
properties:
output: '[parameters(''paramInt'')]'
- name: Bool
type: Test/Echo
properties:
output: '[parameters(''paramBool'')]'
- name: Array
type: Test/Echo
properties:
text: '[concat(parameters(''param1''),'','',parameters(''param2''),'','',parameters(''param3''),'','',parameters(''param4''))]'
output: '[parameters(''paramArray'')]'
"@

$out = $config_yaml | dsc config get | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.results[0].result.actualState.text | Should -BeExactly 'hello,7,false,["hello","world"]'
$out.results[0].result.actualState.output | Should -BeExactly 'hello'
$out.results[1].result.actualState.output | Should -BeExactly 7
$out.results[2].result.actualState.output | Should -BeExactly $false
$out.results[3].result.actualState.output | Should -BeExactly @('hello', 'world')
}

It 'property value uses parameter value' {
Expand Down
2 changes: 1 addition & 1 deletion dsc/tests/dsc_test.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Describe 'config test tests' {
}

It 'can accept the use of --format as a subcommand' {
$null = "text: hello" | dsc resource test -r Test/Echo --format pretty-json
$null = "output: hello" | dsc resource test -r Test/Echo --format pretty-json
$LASTEXITCODE | Should -Be 0
}
}
10 changes: 8 additions & 2 deletions dsc_lib/src/configure/depends_on.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statem
if let Some(depends_on) = resource.depends_on.clone() {
for dependency in depends_on {
let statement = parser.parse_and_execute(&dependency, context)?;
let (resource_type, resource_name) = get_type_and_name(&statement)?;
let Some(string_result) = statement.as_str() else {
return Err(DscError::Validation(format!("'dependsOn' syntax is incorrect: {dependency}")));
};
let (resource_type, resource_name) = get_type_and_name(string_result)?;

// find the resource by name
let Some(dependency_resource) = config.resources.iter().find(|r| r.name.eq(resource_name)) else {
Expand Down Expand Up @@ -64,7 +67,10 @@ pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statem
let resource_index = order.iter().position(|r| r.name == resource.name && r.resource_type == resource.resource_type).ok_or(DscError::Validation("Resource not found in order".to_string()))?;
for dependency in depends_on {
let statement = parser.parse_and_execute(dependency, context)?;
let (resource_type, resource_name) = get_type_and_name(&statement)?;
let Some(string_result) = statement.as_str() else {
return Err(DscError::Validation(format!("'dependsOn' syntax is incorrect: {dependency}")));
};
let (resource_type, resource_name) = get_type_and_name(string_result)?;
let dependency_index = order.iter().position(|r| r.name == resource_name && r.resource_type == resource_type).ok_or(DscError::Validation("Dependency not found in order".to_string()))?;
if resource_index < dependency_index {
return Err(DscError::Validation(format!("Circular dependency detected for resource named '{0}'", resource.name)));
Expand Down
16 changes: 13 additions & 3 deletions dsc_lib/src/configure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use self::config_result::{ConfigurationGetResult, ConfigurationSetResult, Config
use self::contraints::{check_length, check_number_limits, check_allowed_values};
use serde_json::{Map, Value};
use std::collections::{HashMap, HashSet};
use tracing::debug;
use tracing::{debug, trace};

pub mod context;
pub mod config_doc;
Expand Down Expand Up @@ -73,6 +73,7 @@ pub fn add_resource_export_results_to_configuration(resource: &DscResource, prov
// for values returned by resources, they may look like expressions, so we make sure to escape them in case
// they are re-used to apply configuration
fn escape_property_values(properties: &Map<String, Value>) -> Result<Option<Map<String, Value>>, DscError> {
debug!("Escape returned property values");
let mut result: Map<String, Value> = Map::new();
for (name, value) in properties {
match value {
Expand Down Expand Up @@ -400,13 +401,15 @@ impl Configurator {
}

fn invoke_property_expressions(&mut self, properties: &Option<Map<String, Value>>) -> Result<Option<Map<String, Value>>, DscError> {
debug!("Invoke property expressions");
if properties.is_none() {
return Ok(None);
}

let mut result: Map<String, Value> = Map::new();
if let Some(properties) = properties {
for (name, value) in properties {
trace!("Invoke property expression for {name}: {value}");
match value {
Value::Object(object) => {
let value = self.invoke_property_expressions(&Some(object.clone()))?;
Expand All @@ -431,7 +434,10 @@ impl Configurator {
return Err(DscError::Parser("Array element could not be transformed as string".to_string()));
};
let statement_result = self.statement_parser.parse_and_execute(statement, &self.context)?;
result_array.push(Value::String(statement_result));
let Some(string_result) = statement_result.as_str() else {
return Err(DscError::Parser("Array element could not be transformed as string".to_string()));
};
result_array.push(Value::String(string_result.to_string()));
}
_ => {
result_array.push(element.clone());
Expand All @@ -446,7 +452,11 @@ impl Configurator {
return Err(DscError::Parser(format!("Property value '{value}' could not be transformed as string")));
};
let statement_result = self.statement_parser.parse_and_execute(statement, &self.context)?;
result.insert(name.clone(), Value::String(statement_result));
if let Some(string_result) = statement_result.as_str() {
result.insert(name.clone(), Value::String(string_result.to_string()));
} else {
result.insert(name.clone(), statement_result);
};
},
_ => {
result.insert(name.clone(), value.clone());
Expand Down
12 changes: 5 additions & 7 deletions dsc_lib/src/functions/base64.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ use base64::{Engine as _, engine::general_purpose};

use crate::DscError;
use crate::configure::context::Context;
use crate::parser::functions::{FunctionArg, FunctionResult};
use super::{Function, AcceptedArgKind};
use crate::functions::AcceptedArgKind;
use serde_json::Value;
use super::Function;

#[derive(Debug, Default)]
pub struct Base64 {}
Expand All @@ -24,11 +25,8 @@ impl Function for Base64 {
1
}

fn invoke(&self, args: &[FunctionArg], _context: &Context) -> Result<FunctionResult, DscError> {
let FunctionArg::String(arg) = args.first().unwrap() else {
return Err(DscError::Parser("Invalid argument type".to_string()));
};
Ok(FunctionResult::String(general_purpose::STANDARD.encode(arg)))
fn invoke(&self, args: &[Value], _context: &Context) -> Result<Value, DscError> {
Ok(Value::String(general_purpose::STANDARD.encode(args[0].as_str().unwrap_or_default())))
}
}

Expand Down
Loading

0 comments on commit 07c646e

Please sign in to comment.