mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Add parameter SchemaFile to Test-Json cmdlet (#11934)
This commit is contained in:
@@ -2,11 +2,12 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Internal;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Security;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using NJsonSchema;
|
||||
|
||||
@@ -15,50 +16,122 @@ namespace Microsoft.PowerShell.Commands
|
||||
/// <summary>
|
||||
/// This class implements Test-Json command.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsDiagnostic.Test, "Json", HelpUri = "")]
|
||||
[Cmdlet(VerbsDiagnostic.Test, "Json", DefaultParameterSetName = ParameterAttribute.AllParameterSets, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096609")]
|
||||
public class TestJsonCommand : PSCmdlet
|
||||
{
|
||||
private const string SchemaFileParameterSet = "SchemaFile";
|
||||
private const string SchemaStringParameterSet = "SchemaString";
|
||||
|
||||
/// <summary>
|
||||
/// An JSON to be validated.
|
||||
/// Gets or sets JSON string to be validated.
|
||||
/// </summary>
|
||||
[Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)]
|
||||
public string Json { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A schema to validate the JSON against.
|
||||
/// Gets or sets schema to validate the JSON against.
|
||||
/// This is optional parameter.
|
||||
/// If the parameter is absent the cmdlet only attempts to parse the JSON string.
|
||||
/// If the parameter present the cmdlet attempts to parse the JSON string and
|
||||
/// then validates the JSON against the schema. Before testing the JSON string,
|
||||
/// the cmdlet parses the schema doing implicitly check the schema too.
|
||||
/// </summary>
|
||||
[Parameter(Position = 1)]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[Parameter(Position = 1, ParameterSetName = SchemaStringParameterSet)]
|
||||
[ValidateNotNullOrEmpty]
|
||||
public string Schema { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets path to the file containg schema to validate the JSON string against.
|
||||
/// This is optional parameter.
|
||||
/// </summary>
|
||||
[Parameter(Position = 1, ParameterSetName = SchemaFileParameterSet)]
|
||||
[ValidateNotNullOrEmpty]
|
||||
public string SchemaFile { get; set; }
|
||||
|
||||
private JsonSchema _jschema;
|
||||
|
||||
/// <summary>
|
||||
/// Prepare an JSON schema.
|
||||
/// Process all exceptions in the AggregateException.
|
||||
/// Unwrap TargetInvocationException if any and
|
||||
/// rethrow inner exception without losing the stack trace.
|
||||
/// </summary>
|
||||
/// <param name="e">AggregateException to be unwrapped.</param>
|
||||
/// <returns>Return value is unreachable since we always rethrow.</returns>
|
||||
private static bool UnwrapException(Exception e)
|
||||
{
|
||||
if (e is TargetInvocationException)
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(e.InnerException).Throw();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExceptionDispatchInfo.Capture(e).Throw();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepare a JSON schema.
|
||||
/// </summary>
|
||||
protected override void BeginProcessing()
|
||||
{
|
||||
if (Schema != null)
|
||||
string resolvedpath = string.Empty;
|
||||
|
||||
try
|
||||
{
|
||||
try
|
||||
if (Schema != null)
|
||||
{
|
||||
_jschema = JsonSchema.FromJsonAsync(Schema).Result;
|
||||
try
|
||||
{
|
||||
_jschema = JsonSchema.FromJsonAsync(Schema).Result;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
// Even if only one exception is thrown, it is still wrapped in an AggregateException exception
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/exception-handling-task-parallel-library
|
||||
ae.Handle(UnwrapException);
|
||||
}
|
||||
}
|
||||
catch (Exception exc)
|
||||
else if (SchemaFile != null)
|
||||
{
|
||||
Exception exception = new Exception(TestJsonCmdletStrings.InvalidJsonSchema, exc);
|
||||
ThrowTerminatingError(new ErrorRecord(exception, "InvalidJsonSchema", ErrorCategory.InvalidData, null));
|
||||
try
|
||||
{
|
||||
resolvedpath = Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(SchemaFile);
|
||||
_jschema = JsonSchema.FromFileAsync(resolvedpath).Result;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
ae.Handle(UnwrapException);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (
|
||||
// Handle exceptions related to file access to provide more specific error message
|
||||
// https://docs.microsoft.com/en-us/dotnet/standard/io/handling-io-errors
|
||||
e is IOException ||
|
||||
e is UnauthorizedAccessException ||
|
||||
e is NotSupportedException ||
|
||||
e is SecurityException
|
||||
)
|
||||
{
|
||||
Exception exception = new Exception(
|
||||
string.Format(
|
||||
CultureInfo.CurrentUICulture,
|
||||
TestJsonCmdletStrings.JsonSchemaFileOpenFailure,
|
||||
resolvedpath),
|
||||
e);
|
||||
ThrowTerminatingError(new ErrorRecord(exception, "JsonSchemaFileOpenFailure", ErrorCategory.OpenError, resolvedpath));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Exception exception = new Exception(TestJsonCmdletStrings.InvalidJsonSchema, e);
|
||||
ThrowTerminatingError(new ErrorRecord(exception, "InvalidJsonSchema", ErrorCategory.InvalidData, resolvedpath));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate an JSON.
|
||||
/// Validate a JSON.
|
||||
/// </summary>
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
|
||||
@@ -126,4 +126,7 @@
|
||||
<data name="InvalidJsonAgainstSchema" xml:space="preserve">
|
||||
<value>The JSON is not valid with the schema.</value>
|
||||
</data>
|
||||
<data name="JsonSchemaFileOpenFailure" xml:space="preserve">
|
||||
<value>Can not open JSON schema file: {0}</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -3,6 +3,12 @@
|
||||
|
||||
Describe "Test-Json" -Tags "CI" {
|
||||
BeforeAll {
|
||||
$validSchemaJsonPath = Join-Path -Path (Join-Path $PSScriptRoot -ChildPath assets) -ChildPath valid_schema_reference.json
|
||||
|
||||
$invalidSchemaJsonPath = Join-Path -Path (Join-Path $PSScriptRoot -ChildPath assets) -ChildPath invalid_schema_reference.json
|
||||
|
||||
$missingSchemaJsonPath = Join-Path -Path (Join-Path $PSScriptRoot -ChildPath assets) -ChildPath no_such_file.json
|
||||
|
||||
$validSchemaJson = @"
|
||||
{
|
||||
'description': 'A person',
|
||||
@@ -61,40 +67,73 @@ Describe "Test-Json" -Tags "CI" {
|
||||
"@
|
||||
}
|
||||
|
||||
It "Missing JSON schema file doesn't exist" {
|
||||
Test-Path -LiteralPath $missingSchemaJsonPath | Should -BeFalse
|
||||
}
|
||||
|
||||
It "Json is valid" {
|
||||
Test-Json -Json $validJson | Should -BeTrue
|
||||
}
|
||||
|
||||
It "Json is valid against a valid schema" {
|
||||
It "Json is valid against a valid schema from string" {
|
||||
Test-Json -Json $validJson -Schema $validSchemaJson | Should -BeTrue
|
||||
}
|
||||
|
||||
It "Json is valid against a valid schema from file" {
|
||||
Test-Json -Json $validJson -SchemaFile $validSchemaJsonPath | Should -BeTrue
|
||||
}
|
||||
|
||||
It "Json is invalid" {
|
||||
Test-Json -Json $invalidNodeInJson -ErrorAction SilentlyContinue | Should -BeFalse
|
||||
}
|
||||
|
||||
It "Json is invalid against a valid schema" {
|
||||
It "Json is invalid against a valid schema from string" {
|
||||
Test-Json -Json $invalidTypeInJson2 -Schema $validSchemaJson -ErrorAction SilentlyContinue | Should -BeFalse
|
||||
Test-Json -Json $invalidNodeInJson -Schema $validSchemaJson -ErrorAction SilentlyContinue | Should -BeFalse
|
||||
}
|
||||
|
||||
It "Test-Json throw if a schema is invalid" {
|
||||
It "Json is invalid against a valid schema from file" {
|
||||
Test-Json -Json $invalidTypeInJson2 -SchemaFile $validSchemaJsonPath -ErrorAction SilentlyContinue | Should -BeFalse
|
||||
Test-Json -Json $invalidNodeInJson -SchemaFile $validSchemaJsonPath -ErrorAction SilentlyContinue | Should -BeFalse
|
||||
}
|
||||
|
||||
It "Test-Json throw if a schema from string is invalid" {
|
||||
{ Test-Json -Json $validJson -Schema $invalidSchemaJson -ErrorAction Stop } | Should -Throw -ErrorId "InvalidJsonSchema,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
}
|
||||
|
||||
It "Test-Json write an error on invalid (<name>) Json against a valid schema" -TestCases @(
|
||||
@{ name = "type"; json = $invalidTypeInJson; errorId = "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand" }
|
||||
@{ name = "node"; json = $invalidNodeInJson; errorId = "InvalidJson,Microsoft.PowerShell.Commands.TestJsonCommand" }
|
||||
) {
|
||||
param ($json, $errorId)
|
||||
|
||||
$errorVar = $null
|
||||
Test-Json -Json $json -Schema $validSchemaJson -ErrorVariable errorVar -ErrorAction SilentlyContinue
|
||||
|
||||
$errorVar.FullyQualifiedErrorId | Should -BeExactly $errorId
|
||||
It "Test-Json throw if a schema from file is invalid" {
|
||||
{ Test-Json -Json $validJson -SchemaFile $invalidSchemaJsonPath -ErrorAction Stop } | Should -Throw -ErrorId "InvalidJsonSchema,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
}
|
||||
|
||||
It "Test-Json return all errors when check invalid Json against a valid schema" {
|
||||
It "Test-Json throw if a path to a schema from file is invalid" {
|
||||
{ Test-Json -Json $validJson -SchemaFile $missingSchemaJsonPath -ErrorAction Stop } | Should -Throw -ErrorId "JsonSchemaFileOpenFailure,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
}
|
||||
|
||||
It "Test-Json write an error on invalid (<name>) Json against a valid schema from string" -TestCases @(
|
||||
@{ name = "type"; json = $invalidTypeInJson; errorId = "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand" }
|
||||
@{ name = "node"; json = $invalidNodeInJson; errorId = "InvalidJson,Microsoft.PowerShell.Commands.TestJsonCommand" }
|
||||
) {
|
||||
param ($json, $errorId)
|
||||
|
||||
$errorVar = $null
|
||||
Test-Json -Json $json -Schema $validSchemaJson -ErrorVariable errorVar -ErrorAction SilentlyContinue
|
||||
|
||||
$errorVar.FullyQualifiedErrorId | Should -BeExactly $errorId
|
||||
}
|
||||
|
||||
It "Test-Json write an error on invalid (<name>) Json against a valid schema from file" -TestCases @(
|
||||
@{ name = "type"; json = $invalidTypeInJson; errorId = "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand" }
|
||||
@{ name = "node"; json = $invalidNodeInJson; errorId = "InvalidJson,Microsoft.PowerShell.Commands.TestJsonCommand" }
|
||||
) {
|
||||
param ($json, $errorId)
|
||||
|
||||
$errorVar = $null
|
||||
Test-Json -Json $json -SchemaFile $validSchemaJsonPath -ErrorVariable errorVar -ErrorAction SilentlyContinue
|
||||
|
||||
$errorVar.FullyQualifiedErrorId | Should -BeExactly $errorId
|
||||
}
|
||||
|
||||
It "Test-Json return all errors when check invalid Json against a valid schema from string" {
|
||||
$errorVar = $null
|
||||
Test-Json -Json $invalidTypeInJson2 -Schema $validSchemaJson -ErrorVariable errorVar -ErrorAction SilentlyContinue
|
||||
|
||||
@@ -103,4 +142,14 @@ Describe "Test-Json" -Tags "CI" {
|
||||
$errorVar[0].FullyQualifiedErrorId | Should -BeExactly "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
$errorVar[1].FullyQualifiedErrorId | Should -BeExactly "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
}
|
||||
|
||||
It "Test-Json return all errors when check invalid Json against a valid schema from file" {
|
||||
$errorVar = $null
|
||||
Test-Json -Json $invalidTypeInJson2 -SchemaFile $validSchemaJsonPath -ErrorVariable errorVar -ErrorAction SilentlyContinue
|
||||
|
||||
# '$invalidTypeInJson2' contains two errors in property types.
|
||||
$errorVar.Count | Should -Be 2
|
||||
$errorVar[0].FullyQualifiedErrorId | Should -BeExactly "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
$errorVar[1].FullyQualifiedErrorId | Should -BeExactly "InvalidJsonAgainstSchema,Microsoft.PowerShell.Commands.TestJsonCommand"
|
||||
}
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"definitions": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"hobbies"
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "invalid_schema_definitions.json#/definitions/name"
|
||||
},
|
||||
"hobbies": {
|
||||
"$ref": "invalid_schema_definitions.json#/definitions/hobbies"
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"definitions": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"hobbies": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"description": "A person",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"$ref": "valid_schema_definitions.json#/definitions/name"
|
||||
},
|
||||
"hobbies": {
|
||||
"$ref": "valid_schema_definitions.json#/definitions/hobbies"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user