diff --git a/dsc/locales/en-us.toml b/dsc/locales/en-us.toml index 523fba99b..2bb56e7e2 100644 --- a/dsc/locales/en-us.toml +++ b/dsc/locales/en-us.toml @@ -73,6 +73,7 @@ implementedAs = "implemented as" invalidOperationOnAdapter = "Can not perform this operation on the adapter itself" setInputEmpty = "Desired input is empty" testInputEmpty = "Expected input is required" +jsonError = "JSON: %{err}" [subcommand] actualStateNotObject = "actual_state is not an object" @@ -108,6 +109,7 @@ tableHeader_capabilities = "Capabilities" tableHeader_adapter = "RequireAdapter" tableHeader_description = "Description" invalidManifest = "Error in manifest for" +jsonArrayNotSupported = "JSON array output format is only supported for `--all'" [util] failedToConvertJsonToString = "Failed to convert JSON to string" diff --git a/dsc/src/args.rs b/dsc/src/args.rs index 01b8027b6..f17683254 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -15,6 +15,14 @@ pub enum OutputFormat { Yaml, } +#[derive(Debug, Clone, PartialEq, Eq, ValueEnum)] +pub enum GetOutputFormat { + Json, + JsonArray, + PrettyJson, + Yaml, +} + #[derive(Debug, Clone, PartialEq, Eq, ValueEnum)] pub enum ListOutputFormat { Json, @@ -195,7 +203,7 @@ pub enum ResourceSubCommand { #[clap(short = 'f', long, help = t!("args.file").to_string(), conflicts_with = "input")] file: Option, #[clap(short = 'o', long, help = t!("args.outputFormat").to_string())] - output_format: Option, + output_format: Option, }, #[clap(name = "set", about = "Invoke the set operation to a resource", arg_required_else_help = true)] Set { diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 11a6e0ddd..34301e039 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::args::OutputFormat; +use crate::args::{GetOutputFormat, OutputFormat}; use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, EXIT_DSC_RESOURCE_NOT_FOUND, write_object}; use dsc_lib::configure::config_doc::{Configuration, ExecutionKind}; use dsc_lib::configure::add_resource_export_results_to_configuration; @@ -47,7 +47,7 @@ pub fn get(dsc: &DscManager, resource_type: &str, input: &str, format: Option<&O } } -pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputFormat>) { +pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&GetOutputFormat>) { let input = String::new(); let Some(resource) = get_resource(dsc, resource_type) else { error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string()); @@ -68,6 +68,18 @@ pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputForm } }; + if format == Some(&GetOutputFormat::JsonArray) { + let json = match serde_json::to_string(&export_result.actual_state) { + Ok(json) => json, + Err(err) => { + error!("{}", t!("resource_command.jsonError", err = err)); + exit(EXIT_JSON_ERROR); + } + }; + write_object(&json, Some(&OutputFormat::Json), false); + return; + } + let mut include_separator = false; for instance in export_result.actual_state { @@ -78,10 +90,15 @@ pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputForm let json = match serde_json::to_string(&get_result) { Ok(json) => json, Err(err) => { - error!("JSON Error: {err}"); + error!("{}", t!("resource_command.jsonError", err = err)); exit(EXIT_JSON_ERROR); } }; + let format = match format { + Some(&GetOutputFormat::PrettyJson) => Some(&OutputFormat::PrettyJson), + Some(&GetOutputFormat::Yaml) => Some(&OutputFormat::Yaml), + _ => Some(&OutputFormat::Json), + }; write_object(&json, format, include_separator); include_separator = true; } diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index f568adaee..fab90a778 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::args::{ConfigSubCommand, DscType, ExtensionSubCommand, ListOutputFormat, OutputFormat, ResourceSubCommand}; +use crate::args::{ConfigSubCommand, DscType, ExtensionSubCommand, GetOutputFormat, ListOutputFormat, OutputFormat, ResourceSubCommand}; use crate::resolve::{get_contents, Include}; use crate::resource_command::{get_resource, self}; use crate::tablewriter::Table; @@ -590,7 +590,17 @@ pub fn resource(subcommand: &ResourceSubCommand, progress_format: ProgressFormat if *all { resource_command::get_all(&dsc, resource, output_format.as_ref()); } else { let parsed_input = get_input(input.as_ref(), path.as_ref()); - resource_command::get(&dsc, resource, &parsed_input, output_format.as_ref()); + let format = match output_format { + Some(GetOutputFormat::Json) => Some(OutputFormat::Json), + Some(GetOutputFormat::JsonArray) => { + error!("{}", t!("subcommand.jsonArrayNotSupported")); + exit(EXIT_INVALID_ARGS); + }, + Some(GetOutputFormat::PrettyJson) => Some(OutputFormat::PrettyJson), + Some(GetOutputFormat::Yaml) => Some(OutputFormat::Yaml), + None => None, + }; + resource_command::get(&dsc, resource, &parsed_input, format.as_ref()); } }, ResourceSubCommand::Set { resource, input, file: path, output_format } => { diff --git a/dsc/tests/dsc_get.tests.ps1 b/dsc/tests/dsc_resource_get.tests.ps1 similarity index 88% rename from dsc/tests/dsc_get.tests.ps1 rename to dsc/tests/dsc_resource_get.tests.ps1 index dbc9527c8..bef70f0d0 100644 --- a/dsc/tests/dsc_get.tests.ps1 +++ b/dsc/tests/dsc_resource_get.tests.ps1 @@ -48,4 +48,10 @@ Describe 'resource get tests' { $testError[0] | SHould -match 'error:' $LASTEXITCODE | Should -Be 2 } + + It '--output-format json-array returns single object' { + $out = dsc resource get -r Microsoft/Process --all --output-format json-array + $LASTEXITCODE | Should -Be 0 + ($out | Measure-Object).Count | Should -Be 1 + } }