diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 03df232..79512eb 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "csharpier": { - "version": "1.2.1", + "version": "1.2.4", "commands": [ "csharpier" ], diff --git a/.csharpierignore b/.csharpierignore index 341ae1b..5bd2c71 100644 --- a/.csharpierignore +++ b/.csharpierignore @@ -1 +1,5 @@ .nuget +.idea +*.slnx +*.csproj +*.props \ No newline at end of file diff --git a/.idea/.idea.OpenApiValidate/.idea/copilot.data.migration.ask2agent.xml b/.idea/.idea.OpenApiValidate/.idea/copilot.data.migration.ask2agent.xml new file mode 100644 index 0000000..1f2ea11 --- /dev/null +++ b/.idea/.idea.OpenApiValidate/.idea/copilot.data.migration.ask2agent.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index 1224431..10060ca 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,9 +4,9 @@ true - + - + all diff --git a/src/OpenApiValidate/Helpers/OpenApiExtensions.cs b/src/OpenApiValidate/Helpers/OpenApiExtensions.cs index d7ce52e..68058d3 100644 --- a/src/OpenApiValidate/Helpers/OpenApiExtensions.cs +++ b/src/OpenApiValidate/Helpers/OpenApiExtensions.cs @@ -5,18 +5,26 @@ namespace OpenApiValidate; internal static class OpenApiExtensions { + private static readonly OpenApiWriterSettings OpenApiWriterSettings = new() + { + InlineExternalReferences = true, + InlineLocalReferences = true, + }; + + private static readonly BuildOptions JsonSchemaBuildOptions = new() + { + Dialect = Json.Schema.OpenApi.Dialect.OpenApi_31, + }; + public static JsonSchema ToJsonSchema(this IOpenApiSchema schema) { var writer = new StringWriter(); - var writerSettings = new OpenApiWriterSettings - { - InlineExternalReferences = true, - InlineLocalReferences = true, - }; - schema.SerializeAsV31(new OpenApiJsonWriter(writer, writerSettings)); + + schema.SerializeAsV31(new OpenApiJsonWriter(writer, OpenApiWriterSettings)); var json = writer.ToString(); - return JsonSchema.FromText(json); + + return JsonSchema.FromText(json, JsonSchemaBuildOptions); } public static bool TryMatchResponse( @@ -84,7 +92,7 @@ private static bool IsPathMatch(PathString specPath, PathString requestPath) { var segment = specPath.Segments[i]; - if (segment.StartsWith("{") && segment.EndsWith("}")) + if (segment.StartsWith('{') && segment.EndsWith('}')) { // Is template parameter, so skip checking continue; diff --git a/src/OpenApiValidate/OpenApiValidate.csproj b/src/OpenApiValidate/OpenApiValidate.csproj index db5acb8..ecd1142 100644 --- a/src/OpenApiValidate/OpenApiValidate.csproj +++ b/src/OpenApiValidate/OpenApiValidate.csproj @@ -5,7 +5,7 @@ enable - + diff --git a/src/OpenApiValidate/OpenApiValidator.cs b/src/OpenApiValidate/OpenApiValidator.cs index 9b05854..4656590 100644 --- a/src/OpenApiValidate/OpenApiValidator.cs +++ b/src/OpenApiValidate/OpenApiValidator.cs @@ -1,4 +1,5 @@ using System.Text; +using System.Text.Json; using System.Text.Json.Nodes; using System.Web; using Json.Schema; @@ -320,15 +321,20 @@ out List validationErrors var jsonSchema = schema.ToJsonSchema(); var validationResult = jsonSchema.Evaluate( - JsonNode.Parse(body), + JsonDocument.Parse(body).RootElement, JsonSchemaEvaluationOptions ); - if (!validationResult.IsValid) + if (validationResult.IsValid) { - var message = new StringBuilder(); + return true; + } + + var message = new StringBuilder(); - foreach (var detail in validationResult.Details.Where(d => d.HasErrors)) + if (validationResult.Details != null) + { + foreach (var detail in validationResult.Details.Where(d => d.Errors != null)) { var path = detail.EvaluationPath.ToString(); @@ -337,15 +343,13 @@ out List validationErrors message.AppendLine($"[{error.Key}] {path}: {error.Value}"); } } - - validationErrors.Add( - new ValidationError($"{bodyType} body failed schema validation: \n\n" + message) - ); - - return false; } - return true; + validationErrors.Add( + new ValidationError($"{bodyType} body failed schema validation: \n\n" + message) + ); + + return false; } private OpenApiServer? FindServer(Request request) diff --git a/src/OpenApiValidate/packages.lock.json b/src/OpenApiValidate/packages.lock.json index 1e206d3..b26d64a 100644 --- a/src/OpenApiValidate/packages.lock.json +++ b/src/OpenApiValidate/packages.lock.json @@ -2,121 +2,121 @@ "version": 2, "dependencies": { "net8.0": { - "JsonSchema.Net": { + "JsonSchema.Net.OpenApi": { "type": "Direct", - "requested": "[7.4.0, )", - "resolved": "7.4.0", - "contentHash": "5T3DWENwuCzLwFWz0qjXXVWA8+5+gC95OLkhqUBWpVpWBMr9gwfhWNeX8rWyr+fLQ7pIQ+lWuHIrmXRudxOOSw==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "h7MIvE30KsozuK7JzfTt8gO72o3Qqxgu3ruyw44LmE9wH1FVVcH00keUhjQI+FyoZbO5QTMq2NJJmslQK8ZKcA==", "dependencies": { - "JsonPointer.Net": "5.3.1" + "JsonSchema.Net": "8.0.0" } }, "Microsoft.OpenApi.YamlReader": { "type": "Direct", - "requested": "[3.0.1, )", - "resolved": "3.0.1", - "contentHash": "zKac6o3McDkjZOm51l8t5DmHX+1HlUZ4xzGjTd0StJMhsOYC0BK1EQaD51IdUQiLWDAsFPD6ES5P1hdTI5uQtg==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "KYt4L25yAsLRVb9dhbfXGGcAQZQMbiLcTemDpCIiNaN+7DNygN+boUQXS5ByANyWzndJ5iKCgt2yWkspJXzwEQ==", "dependencies": { - "Microsoft.OpenApi": "3.0.1", - "SharpYaml": "2.1.4", - "System.Text.Json": "8.0.5" + "Microsoft.OpenApi": "3.1.1", + "SharpYaml": "2.1.4" } }, "Humanizer.Core": { "type": "Transitive", - "resolved": "2.14.1", - "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + "resolved": "3.0.1", + "contentHash": "scB3+KcxNmEjZK5V8rKCW2gIiL8m8KH91w14FuuExyhi9xTyAJ+jr+DDxGdy12mHmioe2uvjxTfMgM7WmSUFlw==" }, "Json.More.Net": { "type": "Transitive", - "resolved": "2.1.1", - "contentHash": "ZXAKl2VsdnIZeUo1PFII3Oi1m1L4YQjEyDjygHfHln5vgsjgIo749X6xWkv7qFYp8RROES+vOEfDcvvoVgs8kA==" + "resolved": "2.2.0", + "contentHash": "fiyEZJNgiCzDa7/N9bZ+CnM5qnawyJ54+CkHNZ2svwxWBWNNFzydJ7RlroqMdOjokGBiLcKIxdajNvOklzlYqQ==" }, "JsonPointer.Net": { "type": "Transitive", - "resolved": "5.3.1", - "contentHash": "3e2OJjU0OaE26XC/klgxbJuXvteFWTDJIJv0ITYWcJEoskq7jzUwPSC1s0iz4wPPQnfN7vwwFmg2gJfwRAPwgw==", + "resolved": "6.0.0", + "contentHash": "OOObaRq33mNkoMFM1JfdW3nD6U6xF0MHHxPFAxSpKHNNTK6Zbt8U9grquJ+m2onZ1M7cruho0fWzHjhpjEXjog==", "dependencies": { - "Humanizer.Core": "2.14.1", - "Json.More.Net": "2.1.1" + "Humanizer.Core": "3.0.1", + "Json.More.Net": "2.2.0" } }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "3.0.1", - "contentHash": "bEUxdaMrfX/1T/U0I9GOxi3nmZTlMZd+/rOPWVKnfIMlzV28VPXmLg7OiQvFa1ivOEruLSdZCUSc4KgroZZHpg==", - "dependencies": { - "System.Text.Json": "8.0.5" - } + "resolved": "3.1.1", + "contentHash": "/NcgAtqQzs6Djm7sJ3DYaFTTUiXOVmJVAljaQ+IQFV5ZiRGheER8OKLlHgke+Yb0CquiqtdgA4DN+98+Uu9G/Q==" }, "SharpYaml": { "type": "Transitive", "resolved": "2.1.4", "contentHash": "/iwULhVBpTjD4wPZhLU+eUWBanDvri/2AGx5YbaAj5kp9kXzhqUfJEy56H5Yi+c+OXsdm/oKD1aTKB24BFp8cw==" }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "8.0.5", - "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + "JsonSchema.Net": { + "type": "CentralTransitive", + "requested": "[8.0.5, )", + "resolved": "8.0.0", + "contentHash": "YQQo7/ozc7kbmTaCE4p1127z3LyHuWLGfXk/7sTqNVVpUawEIop9mtlyMKSOPZshM777oTjaTHc1f1/S/Gxnjw==", + "dependencies": { + "JsonPointer.Net": "6.0.0" + } } }, "net9.0": { - "JsonSchema.Net": { + "JsonSchema.Net.OpenApi": { "type": "Direct", - "requested": "[7.4.0, )", - "resolved": "7.4.0", - "contentHash": "5T3DWENwuCzLwFWz0qjXXVWA8+5+gC95OLkhqUBWpVpWBMr9gwfhWNeX8rWyr+fLQ7pIQ+lWuHIrmXRudxOOSw==", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "h7MIvE30KsozuK7JzfTt8gO72o3Qqxgu3ruyw44LmE9wH1FVVcH00keUhjQI+FyoZbO5QTMq2NJJmslQK8ZKcA==", "dependencies": { - "JsonPointer.Net": "5.3.1" + "JsonSchema.Net": "8.0.0" } }, "Microsoft.OpenApi.YamlReader": { "type": "Direct", - "requested": "[3.0.1, )", - "resolved": "3.0.1", - "contentHash": "zKac6o3McDkjZOm51l8t5DmHX+1HlUZ4xzGjTd0StJMhsOYC0BK1EQaD51IdUQiLWDAsFPD6ES5P1hdTI5uQtg==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "KYt4L25yAsLRVb9dhbfXGGcAQZQMbiLcTemDpCIiNaN+7DNygN+boUQXS5ByANyWzndJ5iKCgt2yWkspJXzwEQ==", "dependencies": { - "Microsoft.OpenApi": "3.0.1", - "SharpYaml": "2.1.4", - "System.Text.Json": "8.0.5" + "Microsoft.OpenApi": "3.1.1", + "SharpYaml": "2.1.4" } }, "Humanizer.Core": { "type": "Transitive", - "resolved": "2.14.1", - "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + "resolved": "3.0.1", + "contentHash": "scB3+KcxNmEjZK5V8rKCW2gIiL8m8KH91w14FuuExyhi9xTyAJ+jr+DDxGdy12mHmioe2uvjxTfMgM7WmSUFlw==" }, "Json.More.Net": { "type": "Transitive", - "resolved": "2.1.1", - "contentHash": "ZXAKl2VsdnIZeUo1PFII3Oi1m1L4YQjEyDjygHfHln5vgsjgIo749X6xWkv7qFYp8RROES+vOEfDcvvoVgs8kA==" + "resolved": "2.2.0", + "contentHash": "fiyEZJNgiCzDa7/N9bZ+CnM5qnawyJ54+CkHNZ2svwxWBWNNFzydJ7RlroqMdOjokGBiLcKIxdajNvOklzlYqQ==" }, "JsonPointer.Net": { "type": "Transitive", - "resolved": "5.3.1", - "contentHash": "3e2OJjU0OaE26XC/klgxbJuXvteFWTDJIJv0ITYWcJEoskq7jzUwPSC1s0iz4wPPQnfN7vwwFmg2gJfwRAPwgw==", + "resolved": "6.0.0", + "contentHash": "OOObaRq33mNkoMFM1JfdW3nD6U6xF0MHHxPFAxSpKHNNTK6Zbt8U9grquJ+m2onZ1M7cruho0fWzHjhpjEXjog==", "dependencies": { - "Humanizer.Core": "2.14.1", - "Json.More.Net": "2.1.1" + "Humanizer.Core": "3.0.1", + "Json.More.Net": "2.2.0" } }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "3.0.1", - "contentHash": "bEUxdaMrfX/1T/U0I9GOxi3nmZTlMZd+/rOPWVKnfIMlzV28VPXmLg7OiQvFa1ivOEruLSdZCUSc4KgroZZHpg==", - "dependencies": { - "System.Text.Json": "8.0.5" - } + "resolved": "3.1.1", + "contentHash": "/NcgAtqQzs6Djm7sJ3DYaFTTUiXOVmJVAljaQ+IQFV5ZiRGheER8OKLlHgke+Yb0CquiqtdgA4DN+98+Uu9G/Q==" }, "SharpYaml": { "type": "Transitive", "resolved": "2.1.4", "contentHash": "/iwULhVBpTjD4wPZhLU+eUWBanDvri/2AGx5YbaAj5kp9kXzhqUfJEy56H5Yi+c+OXsdm/oKD1aTKB24BFp8cw==" }, - "System.Text.Json": { - "type": "Transitive", - "resolved": "8.0.5", - "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==" + "JsonSchema.Net": { + "type": "CentralTransitive", + "requested": "[8.0.5, )", + "resolved": "8.0.0", + "contentHash": "YQQo7/ozc7kbmTaCE4p1127z3LyHuWLGfXk/7sTqNVVpUawEIop9mtlyMKSOPZshM777oTjaTHc1f1/S/Gxnjw==", + "dependencies": { + "JsonPointer.Net": "6.0.0" + } } } } diff --git a/test/OpenApiValidate.Tests/packages.lock.json b/test/OpenApiValidate.Tests/packages.lock.json index 543a3ba..75f12d6 100644 --- a/test/OpenApiValidate.Tests/packages.lock.json +++ b/test/OpenApiValidate.Tests/packages.lock.json @@ -53,21 +53,21 @@ }, "Humanizer.Core": { "type": "Transitive", - "resolved": "2.14.1", - "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + "resolved": "3.0.1", + "contentHash": "scB3+KcxNmEjZK5V8rKCW2gIiL8m8KH91w14FuuExyhi9xTyAJ+jr+DDxGdy12mHmioe2uvjxTfMgM7WmSUFlw==" }, "Json.More.Net": { "type": "Transitive", - "resolved": "2.1.1", - "contentHash": "ZXAKl2VsdnIZeUo1PFII3Oi1m1L4YQjEyDjygHfHln5vgsjgIo749X6xWkv7qFYp8RROES+vOEfDcvvoVgs8kA==" + "resolved": "2.2.0", + "contentHash": "fiyEZJNgiCzDa7/N9bZ+CnM5qnawyJ54+CkHNZ2svwxWBWNNFzydJ7RlroqMdOjokGBiLcKIxdajNvOklzlYqQ==" }, "JsonPointer.Net": { "type": "Transitive", - "resolved": "5.3.1", - "contentHash": "3e2OJjU0OaE26XC/klgxbJuXvteFWTDJIJv0ITYWcJEoskq7jzUwPSC1s0iz4wPPQnfN7vwwFmg2gJfwRAPwgw==", + "resolved": "6.0.0", + "contentHash": "OOObaRq33mNkoMFM1JfdW3nD6U6xF0MHHxPFAxSpKHNNTK6Zbt8U9grquJ+m2onZ1M7cruho0fWzHjhpjEXjog==", "dependencies": { - "Humanizer.Core": "2.14.1", - "Json.More.Net": "2.1.1" + "Humanizer.Core": "3.0.1", + "Json.More.Net": "2.2.0" } }, "Microsoft.ApplicationInsights": { @@ -95,8 +95,8 @@ }, "Microsoft.OpenApi": { "type": "Transitive", - "resolved": "3.0.1", - "contentHash": "bEUxdaMrfX/1T/U0I9GOxi3nmZTlMZd+/rOPWVKnfIMlzV28VPXmLg7OiQvFa1ivOEruLSdZCUSc4KgroZZHpg==", + "resolved": "3.1.1", + "contentHash": "/NcgAtqQzs6Djm7sJ3DYaFTTUiXOVmJVAljaQ+IQFV5ZiRGheER8OKLlHgke+Yb0CquiqtdgA4DN+98+Uu9G/Q==", "dependencies": { "System.Text.Json": "8.0.5" } @@ -287,26 +287,35 @@ "openapivalidate": { "type": "Project", "dependencies": { - "JsonSchema.Net": "[7.4.0, )", - "Microsoft.OpenApi.YamlReader": "[3.0.1, )" + "JsonSchema.Net.OpenApi": "[4.0.0, )", + "Microsoft.OpenApi.YamlReader": "[3.1.1, )" } }, "JsonSchema.Net": { "type": "CentralTransitive", - "requested": "[7.4.0, )", - "resolved": "7.4.0", - "contentHash": "5T3DWENwuCzLwFWz0qjXXVWA8+5+gC95OLkhqUBWpVpWBMr9gwfhWNeX8rWyr+fLQ7pIQ+lWuHIrmXRudxOOSw==", + "requested": "[8.0.5, )", + "resolved": "8.0.0", + "contentHash": "YQQo7/ozc7kbmTaCE4p1127z3LyHuWLGfXk/7sTqNVVpUawEIop9mtlyMKSOPZshM777oTjaTHc1f1/S/Gxnjw==", "dependencies": { - "JsonPointer.Net": "5.3.1" + "JsonPointer.Net": "6.0.0" + } + }, + "JsonSchema.Net.OpenApi": { + "type": "CentralTransitive", + "requested": "[4.0.0, )", + "resolved": "4.0.0", + "contentHash": "h7MIvE30KsozuK7JzfTt8gO72o3Qqxgu3ruyw44LmE9wH1FVVcH00keUhjQI+FyoZbO5QTMq2NJJmslQK8ZKcA==", + "dependencies": { + "JsonSchema.Net": "8.0.0" } }, "Microsoft.OpenApi.YamlReader": { "type": "CentralTransitive", - "requested": "[3.0.1, )", - "resolved": "3.0.1", - "contentHash": "zKac6o3McDkjZOm51l8t5DmHX+1HlUZ4xzGjTd0StJMhsOYC0BK1EQaD51IdUQiLWDAsFPD6ES5P1hdTI5uQtg==", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "KYt4L25yAsLRVb9dhbfXGGcAQZQMbiLcTemDpCIiNaN+7DNygN+boUQXS5ByANyWzndJ5iKCgt2yWkspJXzwEQ==", "dependencies": { - "Microsoft.OpenApi": "3.0.1", + "Microsoft.OpenApi": "3.1.1", "SharpYaml": "2.1.4", "System.Text.Json": "8.0.5" }