diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4d50ecf..57dcba5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,14 +3,12 @@ { "name": "C# (.NET)", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/dotnet:0-6.0", + "image": "mcr.microsoft.com/devcontainers/dotnet:0-8.0", "features": { "ghcr.io/devcontainers/features/dotnet:2": {} } - // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, - // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [5000, 5001], // "portsAttributes": { @@ -18,13 +16,10 @@ // "protocol": "https" // } // } - // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "dotnet restore", - // Configure tool-specific properties. // "customizations": {}, - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. // "remoteUser": "root" -} +} \ No newline at end of file diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4c8e7e5..06e2232 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,7 +7,6 @@ on: env: nuget_directory: ${{ github.workspace}}/artifacts - jobs: create: runs-on: ubuntu-latest @@ -17,14 +16,13 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.x 8.x - name: Build run: dotnet build -c Release - name: Pack run: dotnet pack -c Release -o ${{ env.nuget_directory }} - name: Display created package files - run: ls -R ${{ env.nuget_directory }} + run: ls -R ${{ env.nuget_directory }} - uses: actions/upload-artifact@v4 if: always() with: @@ -45,7 +43,3 @@ jobs: run: ls -R ${{ env.nuget_directory }} - name: Publish all packages to NuGet run: dotnet nuget push ${{ env.nuget_directory }}/*.nupkg --api-key "${{ secrets.NUGET_API_KEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate - - - - diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a2b64af..bed4412 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -12,7 +12,6 @@ jobs: uses: actions/setup-dotnet@v4 with: dotnet-version: | - 6.x 8.x - name: Build run: dotnet build diff --git a/.gitignore b/.gitignore index 9b9682a..591b03a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ obj # NUnit Criteo.OpenApi.Comparator.sln.DotSettings.user +/.vs/Criteo.OpenApi.Comparator diff --git a/.vs/ProjectEvaluation/criteo.openapi.comparator.metadata.v9.bin b/.vs/ProjectEvaluation/criteo.openapi.comparator.metadata.v9.bin new file mode 100644 index 0000000..adef2cd Binary files /dev/null and b/.vs/ProjectEvaluation/criteo.openapi.comparator.metadata.v9.bin differ diff --git a/.vs/ProjectEvaluation/criteo.openapi.comparator.projects.v9.bin b/.vs/ProjectEvaluation/criteo.openapi.comparator.projects.v9.bin new file mode 100644 index 0000000..0cb6699 Binary files /dev/null and b/.vs/ProjectEvaluation/criteo.openapi.comparator.projects.v9.bin differ diff --git a/.vs/ProjectEvaluation/criteo.openapi.comparator.strings.v9.bin b/.vs/ProjectEvaluation/criteo.openapi.comparator.strings.v9.bin new file mode 100644 index 0000000..1699a0b Binary files /dev/null and b/.vs/ProjectEvaluation/criteo.openapi.comparator.strings.v9.bin differ diff --git a/README.md b/README.md index 84b46d4..7640541 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ dotnet add package Criteo.OpenApi.Comparator Here is an example of how to use the Comparator: ```C# -var differences = OpenApiComparator.Compare( +var result = OpenApiComparator.Compare( + out var differences, oldOpenApiSpec, newOpenApiSpec ); @@ -50,6 +51,33 @@ Each comparison rule is documented in the [documentation section](https://github Internally, the comparator uses [microsoft/OpenAPI.NET](https://github.com/microsoft/OpenAPI.NET/) which currently supports OpenAPI 2.0 to 3.0.0. +## Versions + +### 1.0 +- Made breaking changes to support that additive changes are allowed by default +- Compare now returns a Change level that reflects the result or all found changes. +- Removed strict mode. + - When not in strict mode there were very few errors in previous versions + - A major version update in the spec is now considered an Breaking rule in that your API can intentionally change. +- Combined the Parsing Errors with the Comparsion Messages +- On the message itself a code will now always uniquely identify the message severity + - Severity now includes a Breaking severity +- Split up several Code so that they are easier to identify and customize + - ConstrantIsStronger + - ResponseConstraintIsStronger + - EnumConstrantIsStronger + - AddedPropertyInResponse + - AddedBreakingPropertyInResponse + - ConstraintChanged + - EnumConstraintChanged + - MultipleOfConstraintChanged + - UniqueItemsConstraintChanged + - Added new Codes + - MajorVersionChange + - MinorVersionChange + - NonSemanticVersion +- Removed .NET 6 support as its no longer supported by Microsoft + ## Contributing Any contribution is more than welcomed. For now, no specific rule must be applied to contribute, just create an Issue or a Pull Request and we'll try to handle it ASAP. diff --git a/documentation/rules/1002.md b/documentation/rules/1002.md deleted file mode 100644 index 8631aae..0000000 --- a/documentation/rules/1002.md +++ /dev/null @@ -1,35 +0,0 @@ -### 1002 - ServerNoLongerSupported - -**Description**: Checks whether any supported `servers` is changed from the previous specification. - -**Cause**: This is considered a breaking change. - -**Example**: `Staging` server is no longer supported in the new version. - -Old specification -```json5 -{ - "servers": [ - { - "url": "https://development.gigantic-server.com/v1", - "description": "Development server" - }, - { - "url": "https://staging.gigantic-server.com/v1", - "description": "Staging server" - } - ] -} -``` - -New specification -```json5 -{ - "servers": [ - { - "url": "https://development.gigantic-server.com/v1", - "description": "Development server" - } - ] -} -``` diff --git a/documentation/rules/1041.md b/documentation/rules/1041.md index ceedb43..960a59f 100644 --- a/documentation/rules/1041.md +++ b/documentation/rules/1041.md @@ -2,7 +2,8 @@ **Description**: Checks whether a property is added to the response model from the previous specification. -**Cause**: This is considered a breaking change. +**Cause**: This is considered a regular change. This is a warning in that legacy serializers can have issues but shouldn't. According to the spec the default value for + **Example**: Property `petType` is being added into a response model in the new version. diff --git a/documentation/rules/1045.md b/documentation/rules/1045.md index ce23f15..3f31d58 100644 --- a/documentation/rules/1045.md +++ b/documentation/rules/1045.md @@ -2,7 +2,7 @@ **Description**: Checks whether a property is added to the model from the previous specification. The model includes all the models that referenced by any request or response. -**Cause**: This is considered a breaking change. +**Cause**: This is considered a breaking change if it is a required property on a request. **Example**: Not requiered property `age` is being added into the `User` schema. @@ -34,11 +34,12 @@ New specification ```json5 { "components": { - "shema": { + "schemas": { "User": { "type": "object", "required": [ - "name" + "name", + "age" ], "properties": { "name": { diff --git a/documentation/rules/1049.md b/documentation/rules/1049.md new file mode 100644 index 0000000..f68102c --- /dev/null +++ b/documentation/rules/1049.md @@ -0,0 +1,33 @@ +### 1049 - NonSemanticVersion + +**Description**: Checks whether the version number follows semantic conventions. https://spec.openapis.org/oas/latest.html#versions + +**Cause**: If the schema is not in the format Major.Minor.Patch. Valid Examples 1.0.0, 1.0.0-alpha + +**Example**: Version is missing minor and patch in the new spec. + +Old specification +```json5 +{ + "openapi": "3.0.2", + "info": { + "title": "New API", + "version": "1.0.0", + "description": "A brand new API with no content. Go nuts!" + }, + "paths": {} +} +``` + +New specification +```json5 +{ + "openapi": "3.0.2", + "info": { + "title": "New API", + "version": "2", + "description": "A brand new API with no content. Go nuts!" + }, + "paths": {} +} +``` diff --git a/documentation/rules/1050.md b/documentation/rules/1050.md new file mode 100644 index 0000000..3001638 --- /dev/null +++ b/documentation/rules/1050.md @@ -0,0 +1,32 @@ +### 1050 - MajorVersionChange + +**Description**: A major version change. This signifies breaking changes may be made. https://spec.openapis.org/oas/latest.html#versions + +**Example**: Version is incremented in the new spec. + +Old specification +```json5 +{ + "openapi": "3.0.2", + "info": { + "title": "New API", + "version": "1.0.0", + "description": "A brand new API with no content. Go nuts!" + }, + "paths": {} +} +``` + +New specification +```json5 + +{ + "openapi": "3.0.2", + "info": { + "title": "New API", + "version": "2.0.0", + "description": "A brand new API with no content. Go nuts!" + }, + "paths": {} +} +``` diff --git a/documentation/rules/1051.md b/documentation/rules/1051.md new file mode 100644 index 0000000..352c715 --- /dev/null +++ b/documentation/rules/1051.md @@ -0,0 +1,32 @@ +### 1051 - MinorVersionChange + +**Description**: A minor version change. This signifies additive changes or occasionally, non-backwards compatible changes may be made in minor version where impact is believed to be low relative to the benefit provided. https://spec.openapis.org/oas/latest.html#versions + +**Example**: Version is incremented in the new spec. + +Old specification +```json5 +{ + "openapi": "3.0.2", + "info": { + "title": "New API", + "version": "1.0.0", + "description": "A brand new API with no content. Go nuts!" + }, + "paths": {} +} +``` + +New specification +```json5 + +{ + "openapi": "3.0.2", + "info": { + "title": "New API", + "version": "1.1.0", + "description": "A brand new API with no content. Go nuts!" + }, + "paths": {} +} +``` diff --git a/src/Criteo.OpenApi.Comparator.Cli/Criteo.OpenApi.Comparator.Cli.csproj b/src/Criteo.OpenApi.Comparator.Cli/Criteo.OpenApi.Comparator.Cli.csproj index 18052c5..c65e252 100644 --- a/src/Criteo.OpenApi.Comparator.Cli/Criteo.OpenApi.Comparator.Cli.csproj +++ b/src/Criteo.OpenApi.Comparator.Cli/Criteo.OpenApi.Comparator.Cli.csproj @@ -1,6 +1,6 @@ - net6;net8; + net8.0 Latest Exe true @@ -15,7 +15,7 @@ Criteo Criteo Copyright (c) Criteo Technology. All rights reserved. - 0.8.3 + 1.0.0 https://github.com/criteo/openapi-comparator https://github.com/criteo/openapi-comparator Criteo, OpenApi, OpenApi-Comparator, OpenApi-Diff, Swagger diff --git a/src/Criteo.OpenApi.Comparator.UTest/Criteo.OpenApi.Comparator.UTest.csproj b/src/Criteo.OpenApi.Comparator.UTest/Criteo.OpenApi.Comparator.UTest.csproj index efd0446..346d484 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Criteo.OpenApi.Comparator.UTest.csproj +++ b/src/Criteo.OpenApi.Comparator.UTest/Criteo.OpenApi.Comparator.UTest.csproj @@ -1,6 +1,6 @@  - net6;net8; + net8.0 false Criteo.OpenApi.Comparator.UTest 1591 @@ -10,6 +10,89 @@ Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + diff --git a/src/Criteo.OpenApi.Comparator.UTest/OpenApiComparatorTests.cs b/src/Criteo.OpenApi.Comparator.UTest/OpenApiComparatorTests.cs new file mode 100644 index 0000000..2f86da9 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/OpenApiComparatorTests.cs @@ -0,0 +1,41 @@ +// Copyright (c) Criteo Technology. All rights reserved. +// Licensed under the Apache 2.0 License. See LICENSE in the project root for license information. + +using System.IO; +using System.Linq; +using System.Reflection; +using Criteo.OpenApi.Comparator.Logging; +using NUnit.Framework; + +namespace Criteo.OpenApi.Comparator.UTest +{ + [TestFixture] + public class OpenApiComparatorTests + { + + private static string ReadOpenApiFile(string fileName) + { + var baseDir = Directory.GetParent(typeof(OpenApiParserTests).GetTypeInfo().Assembly.Location) + .ToString(); + var filePath = Path.Combine(baseDir, "Resource", fileName); + return File.ReadAllText(filePath); + } + + + /// + /// Info NoVersionChange message should not be coming back + /// + [Test] + public void OpenApiComparator_WithStrict_ShouldNotHaveVersionWarning() + { + var oldYaml = ReadOpenApiFile("added_required_parameter/old.yaml"); + var newYaml = ReadOpenApiFile("added_required_parameter/new.yaml"); + + var results = OpenApiComparator.Compare(oldYaml, newYaml, out _, true); + + Assert.That(results.All(r => r.Id != ComparisonRules.NoVersionChange.Id), + "NoVersionChange warning (Id 1001) should not be present in the results."); + + } + } +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareLegacyTests.cs b/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareLegacyTests.cs new file mode 100644 index 0000000..4dac89f --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareLegacyTests.cs @@ -0,0 +1,99 @@ +// Copyright (c) Criteo Technology. All rights reserved. +// Licensed under the Apache 2.0 License. See LICENSE in the project root for license information. + +using System.IO; +using NUnit.Framework; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; +using Criteo.OpenApi.Comparator.Logging; +using System.Collections.Generic; +using System.Linq; +using System.Text.Encodings.Web; + +namespace Criteo.OpenApi.Comparator.UTest; + +/// +/// This class contains tests for the logic comparing two swagger specifications, +/// an older version against newer version. +/// +/// For all but the tests that verify that version checks are done properly, the +/// old and new specifications have the same version number, which should force +/// the comparison logic to produce errors rather than warnings for each breaking +/// change. +/// +/// Non-breaking changes are always presented as informational messages, regardless +/// of whether the version has changed or not. +/// +[TestFixture] +public class OpenApiSpecificationsCompareLegacyTests +{ + private static readonly JsonSerializerOptions serializerOptions = new() + { + WriteIndented = true, + Converters = { new JsonStringEnumConverter() }, + Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, + }; + + [TestCaseSource(nameof(TestCases))] + public void CompareOAS(string testcase) + { + var resourceDirectory = GetResourceDirectory(); + var oldFileName = Path.Combine(resourceDirectory, testcase, "old.yaml"); + var newFileName = Path.Combine(resourceDirectory, testcase, "new.yaml"); + var diffFileName = Path.Combine(resourceDirectory, testcase, "diff.json"); + + var differences = OpenApiComparator + .Compare(File.ReadAllText(oldFileName), File.ReadAllText(newFileName), out _); + + var expectedDifferencesText = File.ReadAllText(diffFileName); + var expectedDifferences = JsonSerializer + .Deserialize(expectedDifferencesText, serializerOptions); + + Assert.That(differences, + Is.EquivalentTo(expectedDifferences).Using(Comparer), + $"Expected differences:\n{expectedDifferencesText}\nActual differences:\n{JsonSerializer.Serialize(differences, serializerOptions)}"); + } + + private static IEnumerable TestCases() + { + var resourceDirectory = GetResourceDirectory(); + return Directory + .GetDirectories(resourceDirectory) + .Where(directory => File.Exists(Path.Combine(directory, "diff.json"))) + .Select(Path.GetFileName); + } + + private static string GetResourceDirectory() + { + var assemblyLocation = typeof(OpenApiSpecificationsCompareTests).GetTypeInfo().Assembly.Location; + var baseDirectory = Directory.GetParent(assemblyLocation).ToString(); + return Path.Combine(baseDirectory, "Resource"); + } + + private static bool Comparer(ComparisonMessage message, ComparisonMessageModel model) + { + return message.Severity == model.Severity + && message.Message == model.Message + && message.OldJsonRef == model.OldJsonRef + && message.NewJsonRef == model.NewJsonRef + && message.OldJsonPath == model.OldJsonPath + && message.NewJsonPath == model.NewJsonPath + && message.Id == model.Id + && message.Code == model.Code + && message.Mode == model.Mode; + } +} + +internal class ComparisonMessageModel +{ + public MessageSeverity Severity { get; set; } + public string Message { get; set; } + public string OldJsonRef { get; set; } + public string NewJsonRef { get; set; } + public string OldJsonPath { get; set; } + public string NewJsonPath { get; set; } + public int Id { get; set; } + public string Code { get; set; } + public MessageType Mode { get; set; } +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs b/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs index 241b213..04148df 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs +++ b/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs @@ -43,16 +43,18 @@ public void CompareOAS(string testcase) var newFileName = Path.Combine(resourceDirectory, testcase, "new.yaml"); var diffFileName = Path.Combine(resourceDirectory, testcase, "diff.json"); - var differences = OpenApiComparator - .Compare(File.ReadAllText(oldFileName), File.ReadAllText(newFileName), out _); + var result = OpenApiComparator + .Compare(out var differences, File.ReadAllText(oldFileName), File.ReadAllText(newFileName)); var expectedDifferencesText = File.ReadAllText(diffFileName); var expectedDifferences = JsonSerializer - .Deserialize(expectedDifferencesText, serializerOptions); + .Deserialize(expectedDifferencesText, serializerOptions); Assert.That(differences, - Is.EquivalentTo(expectedDifferences).Using(Comparer), - $"Expected differences:\n{expectedDifferencesText}\nActual differences:\n{JsonSerializer.Serialize(differences, serializerOptions)}"); + Is.EquivalentTo(expectedDifferences.Messages).Using(Comparer), + $"Expected differences:\n{JsonSerializer.Serialize(expectedDifferences.Messages, serializerOptions)}\nActual differences:\n{JsonSerializer.Serialize(differences, serializerOptions)}"); + Assert.That(result, Is.EqualTo(expectedDifferences.Result)); + } private static IEnumerable TestCases() @@ -68,7 +70,7 @@ private static string GetResourceDirectory() { var assemblyLocation = typeof(OpenApiSpecificationsCompareTests).GetTypeInfo().Assembly.Location; var baseDirectory = Directory.GetParent(assemblyLocation).ToString(); - return Path.Combine(baseDirectory, "Resource"); + return Path.Combine(baseDirectory, "Resource1.0"); } private static bool Comparer(ComparisonMessage message, ComparisonMessageModel model) @@ -85,15 +87,8 @@ private static bool Comparer(ComparisonMessage message, ComparisonMessageModel m } } -internal class ComparisonMessageModel +internal class TestResult { - public Severity Severity { get; set; } - public string Message { get; set; } - public string OldJsonRef { get; set; } - public string NewJsonRef { get; set; } - public string OldJsonPath { get; set; } - public string NewJsonPath { get; set; } - public int Id { get; set; } - public string Code { get; set; } - public MessageType Mode { get; set; } + public ChangeLevel Result { get; set; } + public List Messages { get; set; } } diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_enum_value/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_enum_value/diff.json index bf11da5..fb4b450 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_enum_value/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_enum_value/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/Pet/properties/petType/enum", "NewJsonPath": "#/components/schemas/Pet/properties/petType/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" }, { @@ -29,7 +29,7 @@ "OldJsonPath": "#/components/schemas/Pet/properties/accountType/enum", "NewJsonPath": "#/components/schemas/Pet/properties/accountType/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_header/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_header/diff.json index 02cad21..f128888 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_header/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_header/diff.json @@ -1,7 +1,7 @@ [ { "Severity": "Info", - "Message": "The new version adds a required header \u0027x-d\u0027.", + "Message": "The new version adds a header \u0027x-d\u0027.", "OldJsonRef": "#/paths/~1api~1Parameters/get/responses/200/headers/x-d", "NewJsonRef": "#/paths/~1api~1Parameters/get/responses/200/headers/x-d", "OldJsonPath": "#/paths/~1api~1Parameters/get/responses/200/headers/x-d", @@ -12,7 +12,7 @@ }, { "Severity": "Info", - "Message": "The new version adds a required header \u0027x-c\u0027.", + "Message": "The new version adds a header \u0027x-c\u0027.", "OldJsonRef": "#/paths/~1api~1Responses/get/responses/200/headers/x-c", "NewJsonRef": "#/paths/~1api~1Responses/get/responses/200/headers/x-c", "OldJsonPath": "#/paths/~1api~1Responses/get/responses/200/headers/x-c", diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/diff.json new file mode 100644 index 0000000..8bdeef7 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/diff.json @@ -0,0 +1,46 @@ +[ + { + "Severity": "Error", + "Message": "The new version has a new property \u0027petType\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "OldJsonPath": "#/components/schemas/Pet/properties/petType", + "NewJsonPath": "#/components/schemas/Pet/properties/petType", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Error", + "Message": "The new version has a new property \u0027petAge\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "OldJsonPath": "#/components/schemas/Pet/properties/petAge", + "NewJsonPath": "#/components/schemas/Pet/properties/petAge", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Error", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "OldJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Error", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "OldJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "NewJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + } +] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/new.yaml new file mode 100644 index 0000000..b068478 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/new.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.0 +info: + version: 1.0.1 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + additionalProperties: false + properties: + error: + type: string + message: + required: true + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + properties: + name: + type: string + petType: + type: string + petAge: + type: integer + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + additionalProperties: false + properties: + error: + type: string + message: + required: true + type: string + diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/old.yaml new file mode 100644 index 0000000..cddb804 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_no_additional_properties/old.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + properties: + error: + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + additionalProperties: false + properties: + name: + type: string + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + properties: + error: + type: string diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/diff.json new file mode 100644 index 0000000..5885b40 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/diff.json @@ -0,0 +1,46 @@ +[ + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027petType\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "OldJsonPath": "#/components/schemas/Pet/properties/petType", + "NewJsonPath": "#/components/schemas/Pet/properties/petType", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027petAge\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "OldJsonPath": "#/components/schemas/Pet/properties/petAge", + "NewJsonPath": "#/components/schemas/Pet/properties/petAge", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "OldJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "OldJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "NewJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + } +] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/new.yaml new file mode 100644 index 0000000..646cfd9 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/new.yaml @@ -0,0 +1,63 @@ +openapi: 3.0.0 +info: + version: 1.0.1 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + properties: + error: + type: string + message: + required: true + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + properties: + name: + type: string + petType: + type: string + petAge: + type: integer + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + properties: + error: + type: string + message: + required: true + type: string diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/old.yaml new file mode 100644 index 0000000..4d67371 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_property_in_response_same_minor_version/old.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + properties: + error: + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + properties: + name: + type: string + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + properties: + error: + type: string diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/diff.json new file mode 100644 index 0000000..2c3da83 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/diff.json @@ -0,0 +1,13 @@ +[ + { + "Severity": "Error", + "Message": "The required parameter \u0027limitParam\u0027 was added in the new version.", + "OldJsonRef": null, + "NewJsonRef": "#/paths/~1api~1Parameters/put/parameters/1", + "OldJsonPath": null, + "NewJsonPath": "#/paths/~1api~1Parameters/put/parameters/1", + "Id": 1010, + "Code": "AddingRequiredParameter", + "Mode": "Addition" + } +] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/new.yaml new file mode 100644 index 0000000..181e758 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/new.yaml @@ -0,0 +1,34 @@ +openapi: 3.0 +info: + title: removed_definition + version: "1.0.1" +servers: + - url: http://localhost:8000 + - url: https://localhost:8000 +paths: + /api/Parameters: + put: + tags: + - Parameters + operationId: Parameters_Put + parameters: + - $ref: '#/components/parameters/skipParam' + - $ref: '#/components/parameters/limitParam' +components: + parameters: + skipParam: + name: skipParam + in: query + description: number of items to skip + required: true + schema: + type: integer + format: int32 + limitParam: + name: limitParam + in: query + description: max records to return + required: true + schema: + type: integer + format: int32 diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/old.yaml new file mode 100644 index 0000000..47bd12d --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_parameter_same_minor_version/old.yaml @@ -0,0 +1,33 @@ +openapi: 3.0 +info: + title: removed_definition + version: "1.0" +servers: + - url: http://localhost:8000 + - url: https://localhost:8000 +paths: + /api/Parameters: + put: + tags: + - Parameters + operationId: Parameters_Put + parameters: + - $ref: '#/components/parameters/skipParam' +components: + parameters: + skipParam: + name: skipParam + in: query + description: number of items to skip + required: true + schema: + type: integer + format: int32 + limitParam: + name: limitParam + in: query + description: max records to return + required: true + schema: + type: integer + format: int32 diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_property/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_property/diff.json index 3ad3ec9..0963e69 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_property/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/added_required_property/diff.json @@ -1,24 +1,24 @@ [ { "Severity": "Warning", - "Message": "The new version has new required property 'petType' that was not found in the old version.", + "Message": "The new version has new required response property 'petType' that was not found in the old version.", "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items", "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items", "OldJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items", "NewJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items", "Id": 1034, - "Code": "AddedRequiredProperty", + "Code": "AddedRequiredResponseProperty", "Mode": "Addition" }, { "Severity": "Warning", - "Message": "The new version has new required property 'message' that was not found in the old version.", + "Message": "The new version has new required response property 'message' that was not found in the old version.", "OldJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema", "NewJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema", "OldJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema", "NewJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema", "Id": 1034, - "Code": "AddedRequiredProperty", + "Code": "AddedRequiredResponseProperty", "Mode": "Addition" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/constant_status_has_changed/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/constant_status_has_changed/diff.json index 2fba715..d53c586 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/constant_status_has_changed/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/constant_status_has_changed/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/parameters/limitParam/schema/enum", "NewJsonPath": "#/components/parameters/limitParam/schema/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { @@ -29,7 +29,7 @@ "OldJsonPath": "#/components/parameters/limitParam/schema/enum", "NewJsonPath": "#/components/parameters/limitParam/schema/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_changed/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_changed/diff.json index b78cd5e..4c7865b 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_changed/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_changed/diff.json @@ -1,29 +1,29 @@ [ { "Severity": "Warning", - "Message": "The new version has a different \u0027pattern\u0027 value than the previous one.", + "Message": "The new version has a different 'pattern' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/accessKey/pattern", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/accessKey/pattern", "OldJsonPath": "#/components/schemas/limitParam/properties/accessKey/pattern", "NewJsonPath": "#/components/schemas/limitParam/properties/accessKey/pattern", "Id": 1036, - "Code": "ConstraintChanged", + "Code": "PatternConstraintChanged", "Mode": "Update" }, { "Severity": "Warning", - "Message": "The new version has a different \u0027uniqueItems\u0027 value than the previous one.", + "Message": "The new version has a different 'uniqueItems' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/uniqueItems", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/uniqueItems", "OldJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/uniqueItems", "NewJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/uniqueItems", "Id": 1036, - "Code": "ConstraintChanged", + "Code": "UniqueItemsConstraintChanged", "Mode": "Update" }, { "Severity": "Warning", - "Message": "The new version is removing enum value(s) \u0027item3\u0027 from the old version.", + "Message": "The new version is removing enum value(s) 'item3' from the old version.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "OldJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", @@ -34,35 +34,35 @@ }, { "Severity": "Warning", - "Message": "The new version is adding enum value(s) \u0027item4\u0027 from the old version.", + "Message": "The new version is adding enum value(s) 'item4' from the old version.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "OldJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "NewJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { "Severity": "Info", - "Message": "The new version has a different \u0027enum\u0027 value than the previous one.", + "Message": "The new version has a different 'enum' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "OldJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "NewJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "Id": 1036, - "Code": "ConstraintChanged", + "Code": "EnumConstraintChanged", "Mode": "Update" }, { "Severity": "Warning", - "Message": "The new version has a different \u0027multipleOf\u0027 value than the previous one.", + "Message": "The new version has a different 'multipleOf' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/maxRequest/multipleOf", "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/maxRequest/multipleOf", "OldJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/maxRequest/multipleOf", "NewJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/maxRequest/multipleOf", "Id": 1036, - "Code": "ConstraintChanged", + "Code": "MultipleOfConstraintChanged", "Mode": "Update" } -] +] \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_stronger/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_stronger/diff.json index 7b582fb..e43c5e0 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_stronger/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_stronger/diff.json @@ -1,7 +1,7 @@ [ { "Severity": "Warning", - "Message": "The new version has a more constraining \u0027maximum\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'maximum' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minLimit/maximum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minLimit/maximum", "OldJsonPath": "#/components/schemas/limitParam/properties/minLimit/maximum", @@ -12,7 +12,7 @@ }, { "Severity": "Warning", - "Message": "The new version has a more constraining \u0027minimum\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'minimum' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/exclusiveMin/minimum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/exclusiveMin/minimum", "OldJsonPath": "#/components/schemas/limitParam/properties/exclusiveMin/minimum", @@ -23,7 +23,7 @@ }, { "Severity": "Warning", - "Message": "The new version has a more constraining \u0027maxLength\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'maxLength' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/maxTextSize/maxLength", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/maxTextSize/maxLength", "OldJsonPath": "#/components/schemas/limitParam/properties/maxTextSize/maxLength", @@ -34,7 +34,7 @@ }, { "Severity": "Warning", - "Message": "The new version has a more constraining \u0027minLength\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'minLength' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minTextSize/minLength", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minTextSize/minLength", "OldJsonPath": "#/components/schemas/limitParam/properties/minTextSize/minLength", @@ -45,7 +45,7 @@ }, { "Severity": "Warning", - "Message": "The new version has a more constraining \u0027maxItems\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'maxItems' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/maxItems", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/maxItems", "OldJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/maxItems", @@ -56,7 +56,7 @@ }, { "Severity": "Warning", - "Message": "The new version has a more constraining \u0027minItems\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'minItems' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/minItems", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/minItems", "OldJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/minItems", @@ -67,35 +67,35 @@ }, { "Severity": "Info", - "Message": "The new version has a more constraining \u0027maximum\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'maximum' value than the previous one for a response schema.", "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", "OldJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", "NewJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "ResponseConstraintIsStronger", "Mode": "Update" }, { "Severity": "Warning", - "Message": "The new version is removing enum value(s) \u0027item2\u0027 from the old version.", + "Message": "The new version is removing enum value(s) 'item2' from the old version.", "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "OldJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "NewJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "Id": 1019, - "Code": "RemovedEnumValue", + "Code": "RemovedEnumResponseValue", "Mode": "Removal" }, { "Severity": "Info", - "Message": "The new version has a more constraining \u0027enum\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'enum' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "OldJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "NewJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/constrainsItems/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_weaker/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_weaker/diff.json index 1dee519..917e3f7 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_weaker/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/constraint_is_weaker/diff.json @@ -1,95 +1,95 @@ [ { "Severity": "Info", - "Message": "The new version has a less constraining \u0027maximum\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'maximum' value than the previous one in a request schema.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minLimit/maximum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minLimit/maximum", "OldJsonPath": "#/components/schemas/limitParam/properties/minLimit/maximum", "NewJsonPath": "#/components/schemas/limitParam/properties/minLimit/maximum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "RequestConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Info", - "Message": "The new version has a less constraining \u0027minimum\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'minimum' value than the previous one in a request schema.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/exclusiveMin/minimum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/exclusiveMin/minimum", "OldJsonPath": "#/components/schemas/limitParam/properties/exclusiveMin/minimum", "NewJsonPath": "#/components/schemas/limitParam/properties/exclusiveMin/minimum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "RequestConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Info", - "Message": "The new version has a less constraining \u0027maxLength\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'maxLength' value than the previous one in a request schema.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/maxTextSize/maxLength", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/maxTextSize/maxLength", "OldJsonPath": "#/components/schemas/limitParam/properties/maxTextSize/maxLength", "NewJsonPath": "#/components/schemas/limitParam/properties/maxTextSize/maxLength", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "RequestConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Info", - "Message": "The new version has a less constraining \u0027minLength\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'minLength' value than the previous one in a request schema.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minTextSize/minLength", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/minTextSize/minLength", "OldJsonPath": "#/components/schemas/limitParam/properties/minTextSize/minLength", "NewJsonPath": "#/components/schemas/limitParam/properties/minTextSize/minLength", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "RequestConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Info", - "Message": "The new version has a less constraining \u0027maxItems\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'maxItems' value than the previous one in a request schema.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/maxItems", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/maxItems", "OldJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/maxItems", "NewJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/maxItems", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "RequestConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Info", - "Message": "The new version has a less constraining \u0027minItems\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'minItems' value than the previous one in a request schema.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/minItems", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/redirectUrl/minItems", "OldJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/minItems", "NewJsonPath": "#/components/schemas/limitParam/properties/redirectUrl/minItems", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "RequestConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Warning", - "Message": "The new version is adding enum value(s) \u0027item2\u0027 from the old version.", + "Message": "The new version is adding enum value(s) 'item2' from the old version.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "OldJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "NewJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { "Severity": "Info", - "Message": "The new version has a less constraining \u0027enum\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'enum' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "NewJsonRef": "#/paths/~1pets/get/parameters/0/schema/properties/constrainsItems/enum", "OldJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "NewJsonPath": "#/components/schemas/limitParam/properties/constrainsItems/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" }, { "Severity": "Warning", - "Message": "The new version has a less constraining \u0027maximum\u0027 value than the previous one.", + "Message": "The new version has a less constraining 'maximum' value than the previous one.", "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", "OldJsonPath": "#/paths/~1pets/get/responses/200/content/application~1json/schema/properties/code/maximum", @@ -98,4 +98,4 @@ "Code": "ConstraintIsWeaker", "Mode": "Update" } -] +] \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_add/diff.json index d863fa9..12d1ca2 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_add/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_remove/diff.json index aa862e4..c6ae435 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_body_remove/diff.json @@ -1,7 +1,7 @@ [ { "Severity": "Warning", - "Message": "The new version is removing enum value(s) \u0027mno\u0027 from the old version.", + "Message": "The new version is removing enum value(s) 'mno' from the old version.", "OldJsonRef": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "NewJsonRef": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "OldJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", @@ -12,13 +12,13 @@ }, { "Severity": "Info", - "Message": "The new version has a more constraining \u0027enum\u0027 value than the previous one.", + "Message": "The new version has a more constraining 'enum' value than the previous one.", "OldJsonRef": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "NewJsonRef": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "OldJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_add/diff.json index 2d982c3..f05557d 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_add/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/RequestResponseEnum/enum", "NewJsonPath": "#/components/schemas/RequestResponseEnum/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" }, { @@ -40,7 +40,7 @@ "OldJsonPath": "#/components/schemas/RequestResponseEnum/enum", "NewJsonPath": "#/components/schemas/RequestResponseEnum/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_remove/diff.json index e26077f..43139aa 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_both_ref_remove/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/components/schemas/RequestResponseEnum/enum", "NewJsonPath": "#/components/schemas/RequestResponseEnum/enum", "Id": 1019, - "Code": "RemovedEnumValue", + "Code": "RemovedEnumResponseValue", "Mode": "Removal" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/RequestResponseEnum/enum", "NewJsonPath": "#/components/schemas/RequestResponseEnum/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" }, { @@ -40,7 +40,7 @@ "OldJsonPath": "#/components/schemas/RequestResponseEnum/enum", "NewJsonPath": "#/components/schemas/RequestResponseEnum/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_add/diff.json index 8464da8..baf61a9 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_add/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/parameters/0/schema/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/parameters/0/schema/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/parameters/0/schema/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/parameters/0/schema/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_remove/diff.json index cc4066f..73fd029 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_path_remove/diff.json @@ -29,7 +29,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/parameters/0/schema/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/parameters/0/schema/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_add/diff.json index bf82fbb..5a2a54f 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_add/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/parameters/1/schema/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/parameters/1/schema/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/parameters/1/schema/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/parameters/1/schema/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_remove/diff.json index b900be5..1f8892a 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_query_remove/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/parameters/1/schema/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/parameters/1/schema/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_add/diff.json index 9ce0600..530a4f3 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_add/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/components/schemas/RequestOnlyEnum/enum", "NewJsonPath": "#/components/schemas/RequestOnlyEnum/enum", "Id": 1020, - "Code": "AddedEnumValue", + "Code": "AddedEnumRequestValue", "Mode": "Addition" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/RequestOnlyEnum/enum", "NewJsonPath": "#/components/schemas/RequestOnlyEnum/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_remove/diff.json index ea437e7..01478ee 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_request_ref_remove/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/RequestOnlyEnum/enum", "NewJsonPath": "#/components/schemas/RequestOnlyEnum/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_add/diff.json index 0891126..3fe32fa 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_add/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_add/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_add/diff.json index 574863c..3d0c7ce 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_add/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_add/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/ResponseOnlyEnum/enum", "NewJsonPath": "#/components/schemas/ResponseOnlyEnum/enum", "Id": 1037, - "Code": "ConstraintIsWeaker", + "Code": "EnumConstraintIsWeaker", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_remove/diff.json index a3c05e8..98e04b0 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_ref_remove/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/components/schemas/ResponseOnlyEnum/enum", "NewJsonPath": "#/components/schemas/ResponseOnlyEnum/enum", "Id": 1019, - "Code": "RemovedEnumValue", + "Code": "RemovedEnumResponseValue", "Mode": "Removal" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/schemas/ResponseOnlyEnum/enum", "NewJsonPath": "#/components/schemas/ResponseOnlyEnum/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_remove/diff.json index 7dc25f1..64900d9 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction_response_remove/diff.json @@ -7,7 +7,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum", "Id": 1019, - "Code": "RemovedEnumValue", + "Code": "RemovedEnumResponseValue", "Mode": "Removal" }, { @@ -18,7 +18,7 @@ "OldJsonPath": "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum", "NewJsonPath": "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_equal_schemas/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_equal_schemas/diff.json index 5dafa11..0d4f101 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_equal_schemas/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_equal_schemas/diff.json @@ -1,13 +1,2 @@ [ - { - "Severity": "Info", - "Message": "The versions have not changed.", - "OldJsonRef": "#/info/version", - "NewJsonRef": "#/info/version", - "OldJsonPath": "#/info/version", - "NewJsonPath": "#/info/version", - "Id": 1001, - "Code": "NoVersionChange", - "Mode": "Update" - } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_out_of_order_parameters/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_out_of_order_parameters/diff.json index bcfc062..979a580 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_out_of_order_parameters/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_49_false_1015_out_of_order_parameters/diff.json @@ -1,15 +1,4 @@ [ - { - "Severity": "Info", - "Message": "The versions have not changed.", - "OldJsonRef": "#/info/version", - "NewJsonRef": "#/info/version", - "OldJsonPath": "#/info/version", - "NewJsonPath": "#/info/version", - "Id": 1001, - "Code": "NoVersionChange", - "Mode": "Update" - }, { "Severity": "Error", "Message": "The order of parameter 'access_token' was changed. ", diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case1/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case1/diff.json index 97d8115..53a653b 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case1/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case1/diff.json @@ -12,13 +12,13 @@ }, { "Severity": "Error", - "Message": "The new version has a new property 'testHiddenNotNullableProperty' in response that was not found in the old version.", + "Message": "The new version has a new property 'testHiddenNotNullableProperty' in response that was not found in the old version and additional properties are specifically forbidden.", "OldJsonRef": "#/paths/~1api~1v1~1public~1productContent/get/responses/200/content/application~1json/schema/properties/testHiddenNotNullableProperty", "NewJsonRef": "#/paths/~1api~1v1~1public~1productContent/get/responses/200/content/application~1json/schema/properties/testHiddenNotNullableProperty", "OldJsonPath": "#/components/schemas/ContentModel/properties/testHiddenNotNullableProperty", "NewJsonPath": "#/components/schemas/ContentModel/properties/testHiddenNotNullableProperty", "Id": 1041, - "Code": "AddedPropertyInResponse", + "Code": "AddedBreakingPropertyInResponse", "Mode": "Addition" }, { @@ -32,4 +32,4 @@ "Code": "AddedRequiredProperty", "Mode": "Addition" } -] +] \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case2/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case2/diff.json index 97d8115..53a653b 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case2/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/issue_59_case2/diff.json @@ -12,13 +12,13 @@ }, { "Severity": "Error", - "Message": "The new version has a new property 'testHiddenNotNullableProperty' in response that was not found in the old version.", + "Message": "The new version has a new property 'testHiddenNotNullableProperty' in response that was not found in the old version and additional properties are specifically forbidden.", "OldJsonRef": "#/paths/~1api~1v1~1public~1productContent/get/responses/200/content/application~1json/schema/properties/testHiddenNotNullableProperty", "NewJsonRef": "#/paths/~1api~1v1~1public~1productContent/get/responses/200/content/application~1json/schema/properties/testHiddenNotNullableProperty", "OldJsonPath": "#/components/schemas/ContentModel/properties/testHiddenNotNullableProperty", "NewJsonPath": "#/components/schemas/ContentModel/properties/testHiddenNotNullableProperty", "Id": 1041, - "Code": "AddedPropertyInResponse", + "Code": "AddedBreakingPropertyInResponse", "Mode": "Addition" }, { @@ -32,4 +32,4 @@ "Code": "AddedRequiredProperty", "Mode": "Addition" } -] +] \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/oneof_remove/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/oneof_remove/diff.json index 09078d2..c5a7e50 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/oneof_remove/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/oneof_remove/diff.json @@ -44,7 +44,7 @@ "Mode": "Update" }, { - "Severity": "Error", + "Severity": "Warning", "Message": "The new version has a new property 'allow_attachments' in response that was not found in the old version.", "OldJsonRef": "#/paths/~1api~1public~1form/post/responses/200/content/application~1json/schema/properties/ticket/properties/allow_attachments", "NewJsonRef": "#/paths/~1api~1public~1form/post/responses/200/content/application~1json/schema/properties/ticket/properties/allow_attachments", diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/removed_enum_value/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/removed_enum_value/diff.json index a04f3d4..de5240d 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/removed_enum_value/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/removed_enum_value/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/components/parameters/limitParam/schema/enum", "NewJsonPath": "#/components/parameters/limitParam/schema/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" }, { @@ -40,7 +40,7 @@ "OldJsonPath": "#/components/schemas/Pet/properties/petType/enum", "NewJsonPath": "#/components/schemas/Pet/properties/petType/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" }, { @@ -62,7 +62,7 @@ "OldJsonPath": "#/components/schemas/Pet/properties/petType/enum", "NewJsonPath": "#/components/schemas/Pet/properties/petType/enum", "Id": 1024, - "Code": "ConstraintIsStronger", + "Code": "EnumConstraintIsStronger", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/required_status_changed/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/required_status_changed/diff.json index afd05e0..2b36b67 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/required_status_changed/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/required_status_changed/diff.json @@ -1,25 +1,25 @@ [ { "Severity": "Info", - "Message": "The \u0027required\u0027 status changed from the old version(\u0027True\u0027) to the new version(\u0027False\u0027).", + "Message": "The \u0027required\u0027 status was removed from the old version(\u0027True\u0027) to the new version(\u0027False\u0027).", "OldJsonRef": "#/paths/~1pets/get/parameters/0/required", "NewJsonRef": "#/paths/~1pets/get/parameters/0/required", "OldJsonPath": "#/paths/~1pets/get/parameters/0/required", "NewJsonPath": "#/paths/~1pets/get/parameters/0/required", "Id": 1025, - "Code": "RequiredStatusChange", - "Mode": "Update" + "Code": "RequiredStatusRemoved", + "Mode": "Removal" }, { "Severity": "Info", - "Message": "The \u0027required\u0027 status changed from the old version(\u0027True\u0027) to the new version(\u0027False\u0027).", + "Message": "The \u0027required\u0027 status was removed from the old version(\u0027True\u0027) to the new version(\u0027False\u0027).", "OldJsonRef": "#/paths/~1pets/get/parameters/1/required", "NewJsonRef": "#/paths/~1pets/get/parameters/1/required", "OldJsonPath": "#/components/parameters/limit/required", "NewJsonPath": "#/components/parameters/limit/required", "Id": 1025, - "Code": "RequiredStatusChange", - "Mode": "Update" + "Code": "RequiredStatusRemoved", + "Mode": "Removal" }, { "Severity": "Warning", @@ -29,7 +29,7 @@ "OldJsonPath": "#/paths/~1pets/get/requestBody/required", "NewJsonPath": "#/paths/~1pets/get/requestBody/required", "Id": 1025, - "Code": "RequiredStatusChange", + "Code": "RequiredStatusAdded", "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/type_format_changed/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/type_format_changed/diff.json index d3219f6..3a99e0b 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/type_format_changed/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/type_format_changed/diff.json @@ -20,5 +20,16 @@ "Id": 1023, "Code": "TypeFormatChanged", "Mode": "Update" + }, + { + "Severity": "Info", + "Message": "The new version has a different format than the previous one.", + "OldJsonRef": "#/components/parameters/skipParam/schema/format", + "NewJsonRef": "#/components/parameters/skipParam/schema/format", + "OldJsonPath": "#/components/parameters/skipParam/schema/format", + "NewJsonPath": "#/components/parameters/skipParam/schema/format", + "Id": 1023, + "Code": "WideningTypeFormatChanged", + "Mode": "Update" } ] diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/x-ms-paths/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/x-ms-paths/diff.json index 6f4ae99..476ff39 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/Resource/x-ms-paths/diff.json +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/x-ms-paths/diff.json @@ -18,7 +18,7 @@ "OldJsonPath": "#/x-ms-paths/~1myPath~1query-drive?op=folder/get/parameters/0/required", "NewJsonPath": "#/x-ms-paths/~1myPath~1query-drive?op=folder/get/parameters/0/required", "Id": 1025, - "Code": "RequiredStatusChange", + "Code": "RequiredStatusAdded", "Mode": "Update" }, { diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/diff.json new file mode 100644 index 0000000..4e41be1 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/diff.json @@ -0,0 +1,49 @@ +{ + "Result": "Warning", + "Messages": [ + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027petType\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "OldJsonPath": "#/components/schemas/Pet/properties/petType", + "NewJsonPath": "#/components/schemas/Pet/properties/petType", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027petAge\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "OldJsonPath": "#/components/schemas/Pet/properties/petAge", + "NewJsonPath": "#/components/schemas/Pet/properties/petAge", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "OldJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Warning", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version.", + "OldJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "OldJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "NewJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedPropertyInResponse", + "Mode": "Addition" + } + ] +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/new.yaml new file mode 100644 index 0000000..42d7521 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/new.yaml @@ -0,0 +1,61 @@ +openapi: 3.0.0 +info: + version: 1.0.1 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + properties: + error: + type: string + message: + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + properties: + name: + type: string + petType: + type: string + petAge: + type: integer + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + properties: + error: + type: string + message: + type: string diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/old.yaml new file mode 100644 index 0000000..4d67371 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response/old.yaml @@ -0,0 +1,53 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + properties: + error: + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + properties: + name: + type: string + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + properties: + error: + type: string diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/diff.json new file mode 100644 index 0000000..c322517 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/diff.json @@ -0,0 +1,49 @@ +{ + "Result": "Error", + "Messages": [ + { + "Severity": "Breaking", + "Message": "The new version has a new property \u0027petType\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petType", + "OldJsonPath": "#/components/schemas/Pet/properties/petType", + "NewJsonPath": "#/components/schemas/Pet/properties/petType", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Breaking", + "Message": "The new version has a new property \u0027petAge\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "NewJsonRef": "#/paths/~1pets/get/responses/200/content/application~1json/schema/items/properties/petAge", + "OldJsonPath": "#/components/schemas/Pet/properties/petAge", + "NewJsonPath": "#/components/schemas/Pet/properties/petAge", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Breaking", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "OldJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "NewJsonPath": "#/paths/~1pets/get/responses/404/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + }, + { + "Severity": "Breaking", + "Message": "The new version has a new property \u0027message\u0027 in response that was not found in the old version and additional properties are specifically forbidden.", + "OldJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "NewJsonRef": "#/paths/~1pets/get/responses/500/content/application~1json/schema/properties/message", + "OldJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "NewJsonPath": "#/components/responses/InternalErrorResponse/content/application~1json/schema/properties/message", + "Id": 1041, + "Code": "AddedBreakingPropertyInResponse", + "Mode": "Addition" + } + ] +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/new.yaml new file mode 100644 index 0000000..01e371e --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/new.yaml @@ -0,0 +1,64 @@ +openapi: 3.0.0 +info: + version: 1.0.1 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + additionalProperties: false + properties: + error: + type: string + message: + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + properties: + name: + type: string + petType: + type: string + petAge: + type: integer + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + additionalProperties: false + properties: + error: + type: string + message: + type: string + diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/old.yaml new file mode 100644 index 0000000..cddb804 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/added_property_in_response_no_additional_properties/old.yaml @@ -0,0 +1,54 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +servers: + - url: http://petstore.swagger.io/api +paths: + /pets: + get: + description: | + Returns all pets from the system that the user has access to + Nam sed condimentum est. Maecenas tempor sagittis sapien, nec rhoncus sem sagittis sit amet. Aenean at gravida augue, ac iaculis sem. Curabitur odio lorem, ornare eget elementum nec, cursus id lectus. Duis mi turpis, pulvinar ac eros ac, tincidunt varius justo. In hac habitasse platea dictumst. Integer at adipiscing ante, a sagittis ligula. Aenean pharetra tempor ante molestie imperdiet. Vivamus id aliquam diam. Cras quis velit non tortor eleifend sagittis. Praesent at enim pharetra urna volutpat venenatis eget eget mauris. In eleifend fermentum facilisis. Praesent enim enim, gravida ac sodales sed, placerat id erat. Suspendisse lacus dolor, consectetur non augue vel, vehicula interdum libero. Morbi euismod sagittis libero sed lacinia. + + Sed tempus felis lobortis leo pulvinar rutrum. Nam mattis velit nisl, eu condimentum ligula luctus nec. Phasellus semper velit eget aliquet faucibus. In a mattis elit. Phasellus vel urna viverra, condimentum lorem id, rhoncus nibh. Ut pellentesque posuere elementum. Sed a varius odio. Morbi rhoncus ligula libero, vel eleifend nunc tristique vitae. Fusce et sem dui. Aenean nec scelerisque tortor. Fusce malesuada accumsan magna vel tempus. Quisque mollis felis eu dolor tristique, sit amet auctor felis gravida. Sed libero lorem, molestie sed nisl in, accumsan tempor nisi. Fusce sollicitudin massa ut lacinia mattis. Sed vel eleifend lorem. Pellentesque vitae felis pretium, pulvinar elit eu, euismod sapien. + operationId: findPets + responses: + "200": + description: pet response + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Pet' + "404": + description: not found response + content: + application/json: + schema: + type: object + properties: + error: + type: string + "500": + $ref: '#/components/responses/InternalErrorResponse' +components: + schemas: + Pet: + type: object + additionalProperties: false + properties: + name: + type: string + responses: + InternalErrorResponse: + description: internal error + content: + application/json: + schema: + type: object + properties: + error: + type: string diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/diff.json new file mode 100644 index 0000000..ac99a1b --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/diff.json @@ -0,0 +1,27 @@ +{ + "Result": "Error", + "Messages": [ + { + "Severity": "Error", + "Message": "Paths must be a map/object ", + "OldJsonRef": null, + "NewJsonRef": null, + "OldJsonPath": null, + "NewJsonPath": null, + "Id": 9000, + "Code": "OpenApiError", + "Mode": "Specification" + }, + { + "Severity": "Error", + "Message": "The field 'paths' in 'document' object is REQUIRED. #/paths", + "OldJsonRef": null, + "NewJsonRef": null, + "OldJsonPath": null, + "NewJsonPath": null, + "Id": 9000, + "Code": "OpenApiError", + "Mode": "Specification" + } + ] +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/new.yaml new file mode 100644 index 0000000..01ea559 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/new.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 2.0.1 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/old.yaml new file mode 100644 index 0000000..77a0c63 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/invalid_schema/old.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 2.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/diff.json new file mode 100644 index 0000000..8b268b8 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/diff.json @@ -0,0 +1,16 @@ +{ + "Result": "Error", + "Messages": [ + { + "Severity": "Breaking", + "Message": "A major version change. This signifies breaking changes may be made. Old 1.0.0, New 2.0.0.", + "OldJsonRef": "#/info/version", + "NewJsonRef": "#/info/version", + "OldJsonPath": "#/info/version", + "NewJsonPath": "#/info/version", + "Id": 1050, + "Code": "MajorVersionChange", + "Mode": "Addition" + } + ] +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/new.yaml new file mode 100644 index 0000000..77a0c63 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/new.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 2.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/old.yaml new file mode 100644 index 0000000..d22eeda --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/major_version_update/old.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/diff.json new file mode 100644 index 0000000..9702fa4 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/diff.json @@ -0,0 +1,16 @@ +{ + "Result": "Warning", + "Messages": [ + { + "Severity": "Warning", + "Message": "A minor version change. This signifies additive changes or occasionally, non-backwards compatible changes may be made in minor version where impact is believed to be low relative to the benefit provided. Old 1.0.0, New 1.1.0.", + "OldJsonRef": "#/info/version", + "NewJsonRef": "#/info/version", + "OldJsonPath": "#/info/version", + "NewJsonPath": "#/info/version", + "Id": 1051, + "Code": "MinorVersionChange", + "Mode": "Addition" + } + ] +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/new.yaml new file mode 100644 index 0000000..2b87ae6 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/new.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 1.1.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/old.yaml new file mode 100644 index 0000000..d22eeda --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/minor_version_update/old.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/diff.json b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/diff.json new file mode 100644 index 0000000..8d3260b --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/diff.json @@ -0,0 +1,16 @@ +{ + "Result": "Error", + "Messages": [ + { + "Severity": "Error", + "Message": "A version number does not follow semantic conventions Old 1.0.0, New 2e.", + "OldJsonRef": "#/info/version", + "NewJsonRef": "#/info/version", + "OldJsonPath": "#/info/version", + "NewJsonPath": "#/info/version", + "Id": 1049, + "Code": "NonSemanticVersion", + "Mode": "Specification" + } + ] +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/new.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/new.yaml new file mode 100644 index 0000000..53dea94 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/new.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 2e + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/old.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/old.yaml new file mode 100644 index 0000000..d22eeda --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource1.0/non_semantic_version_number/old.yaml @@ -0,0 +1,6 @@ +openapi: 3.0.0 +info: + version: 1.0.0 + title: Swagger Petstore + description: A sample API that uses a petstore as an example to demonstrate features in the OpenAPI 3.0 specification +paths: {} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator/Comparators/ComponentComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/ComponentComparator.cs index 70d4b3c..d595847 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/ComponentComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/ComponentComparator.cs @@ -26,7 +26,7 @@ private static void CompareReference(ComparisonContext context, { if (newReference?.ReferenceV3 != null && !newReference.ReferenceV3.Equals(oldReference?.ReferenceV3)) { - context.LogBreakingChange(ComparisonRules.ReferenceRedirection); + context.Log(ComparisonRules.ReferenceRedirection); } } } diff --git a/src/Criteo.OpenApi.Comparator/Comparators/ContentComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/ContentComparator.cs index ff69d3a..9ce589f 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/ContentComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/ContentComparator.cs @@ -35,7 +35,7 @@ internal IEnumerable Compare(ComparisonContext context, var comparisonMessage = context.Direction == DataDirection.Request ? ComparisonRules.RequestBodyFormatNoLongerSupported : ComparisonRules.ResponseBodyInOperationFormatNoLongerSupported; - context.LogBreakingChange(comparisonMessage, removedMediaType); + context.Log(comparisonMessage, removedMediaType); context.Pop(); } @@ -45,7 +45,7 @@ internal IEnumerable Compare(ComparisonContext context, var comparisonMessage = context.Direction == DataDirection.Request ? ComparisonRules.RequestBodyFormatNowSupported : ComparisonRules.ResponseBodyFormatNowSupported; - context.LogInfo(comparisonMessage, addedMediaType); + context.Log(comparisonMessage, addedMediaType); context.Pop(); } diff --git a/src/Criteo.OpenApi.Comparator/Comparators/OpenApiDocumentComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/OpenApiDocumentComparator.cs index c5636fa..147543d 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/OpenApiDocumentComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/OpenApiDocumentComparator.cs @@ -38,7 +38,7 @@ internal OpenApiDocumentComparator() /// The original document model. /// The new document model. /// A list of messages from the comparison. - internal IEnumerable Compare( + internal List Compare( ComparisonContext context, OpenApiDocument oldDocument, OpenApiDocument newDocument @@ -96,7 +96,7 @@ private static void CompareVersions(ComparisonContext context, string oldVersion var oldVersionAsList = oldVersion.Split('.'); // If the version consists only of numbers separated by '.', we'll consider it semantic versioning. - if (!context.Strict && oldVersionAsList.Length > 0 && newVersionAsList.Length > 0) + if (oldVersionAsList.Length > 0 && newVersionAsList.Length > 0) { var isVersionChanged = false; @@ -111,9 +111,14 @@ private static void CompareVersions(ComparisonContext context, string oldVersion { isVersionChanged = true; + if (newMajor > oldMajor) + { + context.Log(ComparisonRules.MajorVersionChange, oldVersion, newVersion); + } + if (oldMajor > newMajor) { - context.LogError(ComparisonRules.VersionsReversed, oldVersion, newVersion); + context.Log(ComparisonRules.VersionsReversed, oldVersion, newVersion); } } @@ -124,30 +129,28 @@ private static void CompareVersions(ComparisonContext context, string oldVersion if (areIntegers && oldMinor != newMinor) { - isVersionChanged = true; + if (newMinor > oldMinor) + { + context.Log(ComparisonRules.MinorVersionChange, oldVersion, newVersion); + } if (oldMinor > newMinor) { - context.LogError(ComparisonRules.VersionsReversed, oldVersion, newVersion); + context.Log(ComparisonRules.VersionsReversed, oldVersion, newVersion); } } } - // Situation 2: The versioning scheme is something else, maybe a date or just a label? - // Regardless of what it is, we just check whether the two strings are equal or not. - - if (!isVersionChanged && !areIntegers) + if (!areIntegers) { - isVersionChanged = !oldVersion.ToLower().Equals(newVersion.ToLower()); + context.Log(ComparisonRules.NonSemanticVersion, oldVersion, newVersion); } - - context.Strict = !isVersionChanged; } - if (context.Strict) + if (oldVersion.ToLower().Equals(newVersion.ToLower())) { // There was no version change between the documents. This is not an error, but noteworthy. - context.LogInfo(ComparisonRules.NoVersionChange); + context.Log(ComparisonRules.NoVersionChange); } context.Pop(); @@ -168,7 +171,7 @@ private static void CompareServers(ComparisonContext context, foreach (var oldServerUrl in oldServerUrls.Except(newServerUrls)) { context.PushServerByUrl(oldServerUrl); - context.LogBreakingChange(ComparisonRules.ServerNoLongerSupported, oldServerUrl); + context.Log(ComparisonRules.ServerNoLongerSupported, oldServerUrl); context.Pop(); } context.Pop(); @@ -198,14 +201,14 @@ private void ComparePaths(ComparisonContext context, context.PushPathProperty(removedPath, isFromExtension); var removedPathWithVariables = oldPathsWithVariables.First(oldPath => ObjectPath.OpenApiPathName(oldPath) == removedPath); - context.LogBreakingChange(ComparisonRules.RemovedPath, removedPathWithVariables); + context.Log(ComparisonRules.RemovedPath, removedPathWithVariables); context.Pop(); } foreach (var addedPath in newPaths.Keys.Except(commonPaths)) { context.PushPathProperty(addedPath, isFromExtension); - context.LogInfo(ComparisonRules.AddedPath); + context.Log(ComparisonRules.AddedPath); context.Pop(); } @@ -231,14 +234,14 @@ private void CompareOperations(ComparisonContext context, foreach (var removedOperationName in oldOperations.Keys.Except(commonOperations)) { context.PushProperty(removedOperationName.ToString().ToLower()); - context.LogBreakingChange(ComparisonRules.RemovedOperation, oldOperations[removedOperationName].OperationId); + context.Log(ComparisonRules.RemovedOperation, oldOperations[removedOperationName].OperationId); context.Pop(); } foreach (var addedOperationName in newOperations.Keys.Except(commonOperations)) { context.PushProperty(addedOperationName.ToString().ToLower()); - context.LogInfo(ComparisonRules.AddedOperation); + context.Log(ComparisonRules.AddedOperation); context.Pop(); } @@ -484,7 +487,7 @@ private void CompareSchemas(ComparisonContext context, if (!_isSchemaReferenced[oldSchema.Value]) { // It's only an error if the schema is referenced in the old service. - context.LogBreakingChange(ComparisonRules.RemovedDefinition, oldSchema.Key); + context.Log(ComparisonRules.RemovedDefinition, oldSchema.Key); } } else @@ -506,7 +509,7 @@ private void CompareParameters(ComparisonContext context, context.PushProperty(oldParameterName); if (!newParameters.TryGetValue(oldParameterName, out var newParameter)) { - context.LogBreakingChange(ComparisonRules.RemovedClientParameter, oldParameterName); + context.Log(ComparisonRules.RemovedClientParameter, oldParameterName); } else { @@ -526,7 +529,7 @@ private void CompareResponses(ComparisonContext context, { if (!newResponses.TryGetValue(oldDefinition, out var response)) { - context.LogBreakingChange(ComparisonRules.RemovedDefinition, oldDefinition); + context.Log(ComparisonRules.RemovedDefinition, oldDefinition); } else { diff --git a/src/Criteo.OpenApi.Comparator/Comparators/OperationComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/OperationComparator.cs index fddf7c1..10c20c3 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/OperationComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/OperationComparator.cs @@ -47,7 +47,7 @@ internal void Compare(ComparisonContext context, if (newOperation.OperationId != oldOperation.OperationId) { context.PushProperty("operationId"); - context.LogBreakingChange(ComparisonRules.ModifiedOperationId, oldOperation.OperationId, newOperation.OperationId); + context.Log(ComparisonRules.ModifiedOperationId, oldOperation.OperationId, newOperation.OperationId); context.Pop(); } @@ -113,7 +113,7 @@ private static void CompareParametersOrder(ComparisonContext context, if (OrderHasChanged(priorIndex, i)) { - context.LogBreakingChange(ComparisonRules.ChangedParameterOrder, newParameter.Name); + context.Log(ComparisonRules.ChangedParameterOrder, newParameter.Name); } } } @@ -140,7 +140,7 @@ private void CheckRequiredParametersRemoval(ComparisonContext context, else if (oldParameter.Required || oldParameter.In == ParameterLocation.Path) { // Removed required parameter - context.LogBreakingChange(ComparisonRules.RemovedRequiredParameter, oldParameter.Name); + context.Log(ComparisonRules.RemovedRequiredParameter, oldParameter.Name); } context.Pop(); @@ -172,7 +172,7 @@ private static void CheckRequiredParametersAddition(ComparisonContext context, { // Did not find required parameter in the old swagger i.e required parameter is added context.PushParameterByName(newParameter.Name); - context.LogBreakingChange( + context.Log( newParameter.Required || newParameter.In == ParameterLocation.Path ? ComparisonRules.AddingRequiredParameter : ComparisonRules.AddingOptionalParameter, newParameter.Name); @@ -194,7 +194,7 @@ private void CompareResponses(ComparisonContext context, foreach (var statusCode in addedResponseCodes) { context.PushProperty(statusCode); - context.LogBreakingChange(ComparisonRules.AddingResponseCode, statusCode); + context.Log(ComparisonRules.AddingResponseCode, statusCode); context.Pop(); } @@ -202,7 +202,7 @@ private void CompareResponses(ComparisonContext context, foreach (var statusCode in removedResponseCodes) { context.PushProperty(statusCode); - context.LogBreakingChange(ComparisonRules.RemovedResponseCode, statusCode); + context.Log(ComparisonRules.RemovedResponseCode, statusCode); context.Pop(); } @@ -236,7 +236,7 @@ private static void CompareExtensions(ComparisonContext context, return; context.PushProperty(longRunningOperationExtension); - context.LogBreakingChange(ComparisonRules.LongRunningOperationExtensionChanged); + context.Log(ComparisonRules.LongRunningOperationExtensionChanged); context.Pop(); } diff --git a/src/Criteo.OpenApi.Comparator/Comparators/ParameterComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/ParameterComparator.cs index a76a3c5..a5dd9fc 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/ParameterComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/ParameterComparator.cs @@ -76,7 +76,7 @@ private static void CompareIn(ComparisonContext context, if (oldIn != newIn) { context.PushProperty("in"); - context.LogBreakingChange(ComparisonRules.ParameterInHasChanged, + context.Log(ComparisonRules.ParameterInHasChanged, oldIn.ToString().ToLower(), newIn.ToString().ToLower() ); @@ -91,7 +91,7 @@ private static void CompareConstantStatus(ComparisonContext context, if (newParameter.IsConstant() != oldParameter.IsConstant()) { context.PushProperty("enum"); - context.LogBreakingChange(ComparisonRules.ConstantStatusHasChanged); + context.Log(ComparisonRules.ConstantStatusHasChanged); context.Pop(); } } @@ -105,11 +105,11 @@ private static void CompareRequiredStatus(ComparisonContext context, context.PushProperty("required"); if (newParameter.IsRequired()) { - context.LogBreakingChange(ComparisonRules.RequiredStatusChange, false, true); + context.Log(ComparisonRules.RequiredStatusAdded, false, true); } else { - context.LogInfo(ComparisonRules.RequiredStatusChange, true, false); + context.Log(ComparisonRules.RequiredStatusRemoved, true, false); } context.Pop(); } @@ -121,7 +121,7 @@ private static void CompareStyle(ComparisonContext context, if (oldParameter.Style != newParameter.Style) { context.PushProperty("style"); - context.LogBreakingChange(ComparisonRules.ParameterStyleChanged, oldParameter.Name); + context.Log(ComparisonRules.ParameterStyleChanged, oldParameter.Name); context.Pop(); } } diff --git a/src/Criteo.OpenApi.Comparator/Comparators/RequestBodyComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/RequestBodyComparator.cs index f120241..28c9927 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/RequestBodyComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/RequestBodyComparator.cs @@ -26,13 +26,13 @@ internal void Compare(ComparisonContext context, if (oldRequestBody == null) { - context.LogBreakingChange(ComparisonRules.AddedRequestBody); + context.Log(ComparisonRules.AddedRequestBody); return; } if (newRequestBody == null) { - context.LogBreakingChange(ComparisonRules.RemovedRequestBody); + context.Log(ComparisonRules.RemovedRequestBody); return; } @@ -64,11 +64,11 @@ private static void CompareRequired(ComparisonContext context, context.PushProperty("required"); if (newRequired) { - context.LogBreakingChange(ComparisonRules.RequiredStatusChange, oldRequired, newRequired); + context.Log(ComparisonRules.RequiredStatusAdded, oldRequired, newRequired); } else { - context.LogInfo(ComparisonRules.RequiredStatusChange, oldRequired, newRequired); + context.Log(ComparisonRules.RequiredStatusRemoved, oldRequired, newRequired); } context.Pop(); } diff --git a/src/Criteo.OpenApi.Comparator/Comparators/ResponseComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/ResponseComparator.cs index 0148f2b..0b9d066 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/ResponseComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/ResponseComparator.cs @@ -5,77 +5,84 @@ using Criteo.OpenApi.Comparator.Comparators.Extensions; using Microsoft.OpenApi.Models; -namespace Criteo.OpenApi.Comparator.Comparators +namespace Criteo.OpenApi.Comparator.Comparators; + +internal class ResponseComparator { - internal class ResponseComparator + private readonly ContentComparator _contentComparator; + + internal ResponseComparator(ContentComparator contentComparator) { - private readonly ContentComparator _contentComparator; + _contentComparator = contentComparator; + } - internal ResponseComparator(ContentComparator contentComparator) - { - _contentComparator = contentComparator; - } + internal void Compare(ComparisonContext context, + OpenApiResponse oldResponse, OpenApiResponse newResponse) + { + ComponentComparator.Compare(context, oldResponse, newResponse); - internal void Compare(ComparisonContext context, - OpenApiResponse oldResponse, OpenApiResponse newResponse) + using (context.WithDirection(DataDirection.Response)) { - ComponentComparator.Compare(context, oldResponse, newResponse); - - using (context.WithDirection(DataDirection.Response)) + if (!string.IsNullOrWhiteSpace(oldResponse.Reference?.ReferenceV3)) { - if (!string.IsNullOrWhiteSpace(oldResponse.Reference?.ReferenceV3)) - { - oldResponse = oldResponse.Reference.Resolve(context.OldOpenApiDocument.Components.Responses); - if (oldResponse == null) - return; - } + oldResponse = oldResponse.Reference.Resolve(context.OldOpenApiDocument.Components.Responses); + if (oldResponse == null) + return; + } - if (!string.IsNullOrWhiteSpace(newResponse.Reference?.ReferenceV3)) - { - newResponse = newResponse.Reference.Resolve(context.NewOpenApiDocument.Components.Responses); - if (newResponse == null) - return; - } + if (!string.IsNullOrWhiteSpace(newResponse.Reference?.ReferenceV3)) + { + newResponse = newResponse.Reference.Resolve(context.NewOpenApiDocument.Components.Responses); + if (newResponse == null) + return; + } - CompareHeaders(context, oldResponse.Headers, newResponse.Headers); + CompareHeaders(context, oldResponse.Headers, newResponse.Headers); - _contentComparator.Compare(context, oldResponse.Content, newResponse.Content); - } + _contentComparator.Compare(context, oldResponse.Content, newResponse.Content); } + } - private static void CompareHeaders(ComparisonContext context, - IDictionary oldHeaders, - IDictionary newHeaders) - { - newHeaders = newHeaders ?? new Dictionary(); - oldHeaders = oldHeaders ?? new Dictionary(); + private static void CompareHeaders(ComparisonContext context, + IDictionary oldHeaders, + IDictionary newHeaders) + { + newHeaders ??= new Dictionary(); + oldHeaders ??= new Dictionary(); - context.PushProperty("headers"); - foreach (var header in newHeaders) + context.PushProperty("headers"); + foreach (var header in newHeaders) + { + context.PushProperty(header.Key); + if (!oldHeaders.TryGetValue(header.Key, out var oldHeader)) { - context.PushProperty(header.Key); - if (!oldHeaders.TryGetValue(header.Key, out var oldHeader)) - { - context.LogInfo(ComparisonRules.AddingHeader, header.Key); - } + if (header.Value.Required && context.Direction == DataDirection.Request) + context.Log(ComparisonRules.AddingRequiredHeader, header.Key); else - { - ComponentComparator.Compare(context, oldHeader, header.Value); - } - context.Pop(); + context.Log(ComparisonRules.AddingHeader, header.Key); + } + else + { + ComponentComparator.Compare(context, oldHeader, header.Value); } - foreach (var oldHeader in oldHeaders) + context.Pop(); + } + + foreach (var oldHeader in oldHeaders) + { + context.PushProperty(oldHeader.Key); + if (!newHeaders.ContainsKey(oldHeader.Key)) { - context.PushProperty(oldHeader.Key); - if (!newHeaders.ContainsKey(oldHeader.Key)) - { - context.LogBreakingChange(ComparisonRules.RemovingHeader, oldHeader.Key); - } - context.Pop(); + if (context.Direction == DataDirection.Response) + context.Log(ComparisonRules.RemovingHeader, oldHeader.Key); + else + context.Log(ComparisonRules.RemovingRequestHeader, oldHeader.Key); } context.Pop(); } + + context.Pop(); } -} +} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs index ec7cb49..bfe8acc 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs @@ -31,20 +31,20 @@ internal void Compare(ComparisonContext context, if (oldSchema == null) { - context.LogError(ComparisonRules.AddedSchema); + context.Log(ComparisonRules.AddedSchema); return; } if (newSchema == null) { - context.LogBreakingChange(ComparisonRules.RemovedDefinition, default(string)); + context.Log(ComparisonRules.RemovedDefinition, default(string)); return; } if (newSchema.Reference?.ReferenceV3 != null && !newSchema.Reference.ReferenceV3.Equals(oldSchema.Reference?.ReferenceV3)) { - context.LogBreakingChange(ComparisonRules.ReferenceRedirection); + context.Log(ComparisonRules.ReferenceRedirection); } var areSchemasReferenced = false; @@ -107,7 +107,7 @@ internal void Compare(ComparisonContext context, CompareProperties(context, oldSchema, newSchema, isSchemaReferenced); - CompareRequired(context, oldSchema.Required, newSchema.Required); + CompareRequired(context, oldSchema, newSchema); CompareNullable(context, oldSchema.Nullable, newSchema.Nullable); } @@ -118,7 +118,7 @@ private static void CompareNullable(ComparisonContext context, { if (oldNullable == newNullable) return; context.PushProperty("nullable"); - context.LogBreakingChange( + context.Log( ComparisonRules.NullablePropertyChanged, oldNullable.ToString().ToLower(), newNullable.ToString().ToLower() @@ -133,7 +133,7 @@ private static void CompareReadOnly(ComparisonContext context, if (oldReadOnly != newReadOnly) { context.PushProperty("readOnly"); - context.LogBreakingChange( + context.Log( ComparisonRules.ReadonlyPropertyChanged, oldReadOnly.ToString().ToLower(), newReadOnly.ToString().ToLower() @@ -149,7 +149,7 @@ private static void CompareDiscriminator(ComparisonContext context, || oldDiscriminator?.PropertyName != null && !oldDiscriminator.PropertyName.Equals(newDiscriminator?.PropertyName)) { context.PushProperty("discriminator"); - context.LogBreakingChange(ComparisonRules.DifferentDiscriminator); + context.Log(ComparisonRules.DifferentDiscriminator); context.Pop(); } } @@ -165,7 +165,7 @@ private static void CompareDefault(ComparisonContext context, return; context.PushProperty("default"); - context.LogBreakingChange(ComparisonRules.DefaultValueChanged); + context.Log(ComparisonRules.DefaultValueChanged); context.Pop(); } @@ -209,21 +209,21 @@ private static void CompareConstraints(ComparisonContext context, if (oldSchema.MultipleOf.DifferFrom(newSchema.MultipleOf)) { context.PushProperty("multipleOf"); - context.LogBreakingChange(ComparisonRules.ConstraintChanged, "multipleOf"); + context.Log(ComparisonRules.MultipleOfConstraintChanged, "multipleOf"); context.Pop(); } if (oldSchema.UniqueItems != newSchema.UniqueItems) { context.PushProperty("uniqueItems"); - context.LogBreakingChange(ComparisonRules.ConstraintChanged, "uniqueItems"); + context.Log(ComparisonRules.UniqueItemsConstraintChanged, "uniqueItems"); context.Pop(); } if (oldSchema.Pattern.DifferFrom(newSchema.Pattern)) { context.PushProperty("pattern"); - context.LogBreakingChange(ComparisonRules.ConstraintChanged, "pattern"); + context.Log(ComparisonRules.PatternConstraintChanged, "pattern"); context.Pop(); } } @@ -234,21 +234,21 @@ private static void CompareConstraint(ComparisonContext context, decimal? oldCon context.PushProperty(attributeName); if (additionalCondition) { - context.LogBreakingChange(ComparisonRules.ConstraintChanged, attributeName); + context.Log(ComparisonRules.ConstraintChanged, attributeName); } else if (Narrows(oldConstraint, newConstraint, isLowerBound)) { if (context.Direction == DataDirection.Request) - context.LogBreakingChange(ComparisonRules.ConstraintIsStronger, attributeName); + context.Log(ComparisonRules.ConstraintIsStronger, attributeName); else - context.LogInfo(ComparisonRules.ConstraintIsStronger, attributeName); + context.Log(ComparisonRules.ResponseConstraintIsStronger, attributeName); } else if (Widens(oldConstraint, newConstraint, isLowerBound)) { if (context.Direction == DataDirection.Response) - context.LogBreakingChange(ComparisonRules.ConstraintIsWeaker, attributeName); + context.Log(ComparisonRules.ConstraintIsWeaker, attributeName); else - context.LogInfo(ComparisonRules.ConstraintIsWeaker, attributeName); + context.Log(ComparisonRules.RequestConstraintIsWeaker, attributeName); } context.Pop(); } @@ -297,7 +297,7 @@ private static void CompareType(ComparisonContext context, string oldType, strin var newTypeString = newType == null ? "" : newType.ToLower(); context.PushProperty("type"); - context.LogBreakingChange(ComparisonRules.TypeChanged, newTypeString, oldTypeString); + context.Log(ComparisonRules.TypeChanged, newTypeString, oldTypeString); context.Pop(); } } @@ -337,23 +337,23 @@ private static void CompareEnum(ComparisonContext context, if (constrains) { - LogAction logger = context.Direction == DataDirection.Response ? context.LogWarning : context.LogBreakingChange; - logger(ComparisonRules.RemovedEnumValue, string.Join(", ", removedEnums.Select(e => e.StringValue()))); + var rule = context.Direction == DataDirection.Response ? ComparisonRules.RemovedEnumResponseValue : ComparisonRules.RemovedEnumValue; + context.Log(rule, string.Join(", ", removedEnums.Select(e => e.StringValue()))); } if (relaxes && !IsEnumModelAsString(enumExtension)) { - LogAction logger = context.Direction == DataDirection.Request ? context.LogWarning : context.LogBreakingChange; - logger(ComparisonRules.AddedEnumValue, string.Join(", ", addedEnums.Select(e => e.StringValue()))); + var rule = context.Direction == DataDirection.Request ? ComparisonRules.AddedEnumRequestValue : ComparisonRules.AddedEnumValue; + context.Log(rule, string.Join(", ", addedEnums.Select(e => e.StringValue()))); } } if (relaxes && constrains) - context.LogInfo(ComparisonRules.ConstraintChanged, "enum"); + context.Log(ComparisonRules.EnumConstraintChanged, "enum"); else if (relaxes) - context.LogInfo(ComparisonRules.ConstraintIsWeaker, "enum"); + context.Log(ComparisonRules.EnumConstraintIsWeaker, "enum"); else if (constrains) - context.LogInfo(ComparisonRules.ConstraintIsStronger, "enum"); + context.Log(ComparisonRules.EnumConstraintIsStronger, "enum"); context.Pop(); } @@ -373,12 +373,14 @@ private static void CompareFormat(ComparisonContext context, OpenApiSchema oldSchema, OpenApiSchema newSchema) { - if (!oldSchema.Format.DifferFrom(newSchema.Format) - || IsFormatChangeAllowed(context, oldSchema, newSchema)) + if (!oldSchema.Format.DifferFrom(newSchema.Format)) return; context.PushProperty("format"); - context.LogBreakingChange(ComparisonRules.TypeFormatChanged); + if (IsFormatChangeAllowed(context, oldSchema, newSchema)) + context.Log(ComparisonRules.WideningTypeFormatChanged); + else + context.Log(ComparisonRules.TypeFormatChanged); context.Pop(); } @@ -386,7 +388,7 @@ private static bool IsFormatChangeAllowed(ComparisonContext context, OpenApiSchema oldSchema, OpenApiSchema newSchema) { - if (newSchema.Type == null || !newSchema.Type.Equals("integer") || context.Strict + if (newSchema.Type == null || !newSchema.Type.Equals("integer") || oldSchema.Format == null || newSchema.Format == null) return false; @@ -406,7 +408,7 @@ private static void CompareAllOf(ComparisonContext context, context.PushProperty("allOf"); if (oldAllOf == null || newAllOf == null) { - context.LogBreakingChange(ComparisonRules.DifferentAllOf); + context.Log(ComparisonRules.DifferentAllOf); context.Pop(); return; } @@ -421,7 +423,7 @@ private static void CompareAllOf(ComparisonContext context, if (differenceCount > 0) { - context.LogBreakingChange(ComparisonRules.DifferentAllOf); + context.Log(ComparisonRules.DifferentAllOf); } context.Pop(); } @@ -435,7 +437,7 @@ private void CompareOneOf( context.PushProperty("oneOf"); if (oldOneOf == null || newOneOf == null) { - context.LogBreakingChange(ComparisonRules.DifferentOneOf); + context.Log(ComparisonRules.DifferentOneOf); context.Pop(); return; } @@ -450,7 +452,7 @@ private void CompareOneOf( if (differenceCount > 0) { - context.LogBreakingChange(ComparisonRules.DifferentOneOf); + context.Log(ComparisonRules.DifferentOneOf); } var commonReferences = oldOneOfReferences @@ -497,7 +499,7 @@ private static void CompareRemovedProperties(ComparisonContext context, foreach (var propertyName in removedProperties) { context.PushProperty(propertyName); - context.LogBreakingChange(ComparisonRules.RemovedProperty, propertyName); + context.Log(ComparisonRules.RemovedProperty, propertyName); context.Pop(); } } @@ -517,21 +519,25 @@ private static void CompareAddedProperties(ComparisonContext context, { context.PushProperty(property.Key); - if (oldSchema.IsPropertyRequired(property.Key)) - { - context.LogBreakingChange(ComparisonRules.AddedRequiredProperty, property.Key); - } - if (context.Direction == DataDirection.Response) { if (property.Value.ReadOnly) - context.LogInfo(ComparisonRules.AddedReadOnlyPropertyInResponse, property.Key); + context.Log(ComparisonRules.AddedReadOnlyPropertyInResponse, property.Key); + else if (newSchema.AdditionalPropertiesAllowed && oldSchema.AdditionalPropertiesAllowed) + context.Log(ComparisonRules.AddedPropertyInResponse, property.Key); else - context.LogBreakingChange(ComparisonRules.AddedPropertyInResponse, property.Key); + context.Log(ComparisonRules.AddedBreakingPropertyInResponse, property.Key); } else if (isSchemaReferenced && !newSchema.IsPropertyRequired(property.Key)) { - context.LogBreakingChange(ComparisonRules.AddedOptionalProperty, property.Key); + if (oldSchema.IsPropertyRequired(property.Key)) + { + context.Log(ComparisonRules.AddedRequiredProperty, property.Key); + } + else + { + context.Log(ComparisonRules.AddedOptionalProperty, property.Key); + } } context.Pop(); @@ -560,11 +566,11 @@ private void CompareAdditionalProperties(ComparisonContext context, context.PushProperty("additionalProperties"); if (oldAdditionalProperties == null && newAdditionalProperties != null) { - context.LogBreakingChange(ComparisonRules.AddedAdditionalProperties); + context.Log(ComparisonRules.AddedAdditionalProperties); } else if (oldAdditionalProperties != null && newAdditionalProperties == null) { - context.LogBreakingChange(ComparisonRules.RemovedAdditionalProperties); + context.Log(ComparisonRules.RemovedAdditionalProperties); } else if (newAdditionalProperties != null) { @@ -580,23 +586,31 @@ private void CompareAdditionalProperties(ComparisonContext context, /// A set of old required properties /// A set of new required properties private static void CompareRequired(ComparisonContext context, - ISet oldRequired, - ISet newRequired) + OpenApiSchema oldRequired, + OpenApiSchema newRequired) { if (newRequired == null) return; if (oldRequired == null) { - context.LogBreakingChange(ComparisonRules.AddedRequiredProperty, string.Join(", ", newRequired)); + context.Log(ComparisonRules.AddedRequiredProperty, string.Join(", ", newRequired)); return; } - List addedRequiredProperties = newRequired.Except(oldRequired).ToList(); + List addedRequiredProperties = newRequired.Required.Except(oldRequired.Required).ToList(); if (addedRequiredProperties.Any()) { - context.LogBreakingChange(ComparisonRules.AddedRequiredProperty, + if (context.Direction == DataDirection.Request || !oldRequired.AdditionalPropertiesAllowed || ! newRequired.AdditionalPropertiesAllowed) + { + context.Log(ComparisonRules.AddedRequiredProperty, string.Join(", ", addedRequiredProperties)); + } + else + { + context.Log(ComparisonRules.AddedRequiredResponseProperty, + string.Join(", ", addedRequiredProperties)); + } } } } diff --git a/src/Criteo.OpenApi.Comparator/ComparisonContext.cs b/src/Criteo.OpenApi.Comparator/ComparisonContext.cs index 39864cb..7d37249 100644 --- a/src/Criteo.OpenApi.Comparator/ComparisonContext.cs +++ b/src/Criteo.OpenApi.Comparator/ComparisonContext.cs @@ -9,8 +9,6 @@ namespace Criteo.OpenApi.Comparator { - internal delegate void LogAction(ComparisonRule rule, params object[] formatArguments); - /// /// Provides context for a comparison, such as the ancestors in the validation tree, the root object /// and information about the key or index that locate this object in the parent's list or dictionary @@ -39,9 +37,6 @@ internal ComparisonContext(JsonDocument oldOpenApiDocument, Jso /// Old swagger internal OpenApiDocument NewOpenApiDocument => _newOpenApiDocument.Typed; - /// If true, then breaking changes are errors instead of warnings. - internal bool Strict { get; set; } - /// Request, Response, Both or None private readonly DisposableDataDirection _direction = new(); @@ -67,62 +62,27 @@ internal void PushPathProperty(string name, bool asProperty = false) => _path.Pu internal void Pop() => _path.Pop(); - private readonly Stack _path = new Stack(new[] { ObjectPath.Empty }); + private readonly Stack _path = new([ObjectPath.Empty]); - internal void LogInfo(ComparisonRule rule, params object[] formatArguments) => + internal void Log(ComparisonRule rule, params object[] formatArguments) => _messages.Add(new ComparisonMessage( rule, Path, _oldOpenApiDocument, _newOpenApiDocument, - Severity.Info, formatArguments )); - internal void LogWarning(ComparisonRule rule, params object[] formatArguments) => - _messages.Add(new ComparisonMessage( - rule, - Path, - _oldOpenApiDocument, - _newOpenApiDocument, - Severity.Warning, - formatArguments - )); - - internal void LogError(ComparisonRule rule, params object[] formatArguments) => - _messages.Add(new ComparisonMessage( - rule, - Path, - _oldOpenApiDocument, - _newOpenApiDocument, - Severity.Error, - formatArguments - )); - - internal void LogBreakingChange(ComparisonRule rule, params object[] formatArguments) => - _messages.Add(new ComparisonMessage( - rule, - Path, - _oldOpenApiDocument, - _newOpenApiDocument, - Strict ? Severity.Error : Severity.Warning, - formatArguments - )); /// /// Lists all the found differences /// - internal IEnumerable Messages - { - get - { - // TODO: How to eliminate duplicate messages - // Issue: https://github.com/Azure/openapi-diff/issues/48 - return _messages; //.Distinct(new CustomComparer()); - } - } + internal List Messages => + // TODO: How to eliminate duplicate messages + // Issue: https://github.com/Azure/openapi-diff/issues/48 + _messages; //.Distinct(new CustomComparer()); - private readonly IList _messages = new List(); + private readonly List _messages = []; } internal class DisposableDataDirection : IDisposable diff --git a/src/Criteo.OpenApi.Comparator/ComparisonMessage.cs b/src/Criteo.OpenApi.Comparator/ComparisonMessage.cs index 6b9a3e3..c2a9688 100644 --- a/src/Criteo.OpenApi.Comparator/ComparisonMessage.cs +++ b/src/Criteo.OpenApi.Comparator/ComparisonMessage.cs @@ -7,6 +7,8 @@ using System.Linq; using Criteo.OpenApi.Comparator.Parser; using Criteo.OpenApi.Comparator.Logging; +using Microsoft.OpenApi.Models; +using System.Data; namespace Criteo.OpenApi.Comparator { @@ -22,11 +24,10 @@ internal ComparisonMessage( ObjectPath path, IJsonDocument oldDocument, IJsonDocument newDocument, - Severity severity, params object[] formatArguments ) { - Severity = severity; + Severity = rule.Severity; Message = $"{string.Format(CultureInfo.CurrentCulture, rule.Message, formatArguments)}"; Path = path; OldDocument = oldDocument; @@ -37,12 +38,23 @@ params object[] formatArguments Mode = rule.Type; } + internal ComparisonMessage(OpenApiError item) + { + var rule = ComparisonRules.OpenApiError; + Severity = rule.Severity; + Message = item.Message + " " + item.Pointer; + Id = rule.Id; + Code = rule.Code; + DocUrl = $"{DocBaseUrl}{rule.Id}.md"; + Mode = rule.Type; + } + private IJsonDocument OldDocument { get; } private IJsonDocument NewDocument { get; } /// Info, Error, Warning - public Severity Severity { get; } + public MessageSeverity Severity { get; set; } /// /// Explicit description of the change. @@ -57,7 +69,7 @@ params object[] formatArguments /// JSON Pointer of the old JSON reference /// JSON Pointer defines a string syntax for identifying a specific value /// within a JSON document - public string OldJsonRef => Path.JsonPointer(OldDocument); + public string OldJsonRef => Path?.JsonPointer(OldDocument); /// /// A JToken from the old document that contains such information as location. @@ -71,7 +83,7 @@ public JToken OldJson() => Mode != MessageType.Addition /// JSON Pointer of the new JSON reference /// JSON Pointer defines a string syntax for identifying a specific value /// within a JSON document - public string NewJsonRef => Path.JsonPointer(NewDocument); + public string NewJsonRef => Path?.JsonPointer(NewDocument); /// /// A JToken from the new document that contains such information as location. @@ -84,12 +96,12 @@ public JToken NewJson() => Mode != MessageType.Removal /// /// JSON Pointer of the old resolved JSON reference /// - public string OldJsonPath => Path.JsonPointer(OldDocument, resolveReferences: true); + public string OldJsonPath => Path?.JsonPointer(OldDocument, resolveReferences: true); /// /// JSON Pointer of the new resolved JSON reference /// - public string NewJsonPath => Path.JsonPointer(NewDocument, resolveReferences: true); + public string NewJsonPath => Path?.JsonPointer(NewDocument, resolveReferences: true); /// /// The id of the validation message diff --git a/src/Criteo.OpenApi.Comparator/ComparisonRule.cs b/src/Criteo.OpenApi.Comparator/ComparisonRule.cs index 74df08f..338ddbb 100644 --- a/src/Criteo.OpenApi.Comparator/ComparisonRule.cs +++ b/src/Criteo.OpenApi.Comparator/ComparisonRule.cs @@ -19,5 +19,8 @@ public class ComparisonRule /// Difference type (Addition, Update, Removal) public MessageType Type { get; set; } + + /// Difference type (Info, Warning, Breading, Error) + public MessageSeverity Severity { get; set; } } } diff --git a/src/Criteo.OpenApi.Comparator/ComparisonRules.cs b/src/Criteo.OpenApi.Comparator/ComparisonRules.cs index 3320397..8a0fdcb 100644 --- a/src/Criteo.OpenApi.Comparator/ComparisonRules.cs +++ b/src/Criteo.OpenApi.Comparator/ComparisonRules.cs @@ -11,615 +11,899 @@ public static class ComparisonRules /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1000.md /// - public static ComparisonRule VersionsReversed = new ComparisonRule + public static ComparisonRule VersionsReversed = new() { Id = 1000, Code = nameof(VersionsReversed), Message = "The new version has a lower value than the old: {0} -> {1}", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Error }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1001.md /// - public static ComparisonRule NoVersionChange = new ComparisonRule + public static ComparisonRule NoVersionChange = new() { Id = 1001, Code = nameof(NoVersionChange), Message = "The versions have not changed.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Info }; /// - /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1002.md + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1049.md /// - public static ComparisonRule ProtocolNoLongerSupported = new ComparisonRule + public static ComparisonRule NonSemanticVersion = new() { - Id = 1002, - Code = nameof(ProtocolNoLongerSupported), - Message = "The new version does not support '{0}' as a protocol.", - Type = MessageType.Removal + Id = 1049, + Code = nameof(NonSemanticVersion), + Message = "A version number does not follow semantic conventions Old {0}, New {1}.", + Type = MessageType.Specification, + Severity = MessageSeverity.Error + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1050.md + /// + public static ComparisonRule MajorVersionChange = new() + { + Id = 1050, + Code = nameof(MajorVersionChange), + Message = "A major version change. This signifies breaking changes may be made. Old {0}, New {1}.", + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1051.md + /// + public static ComparisonRule MinorVersionChange = new() + { + Id = 1051, + Code = nameof(MinorVersionChange), + Message = "A minor version change. This signifies additive changes or occasionally, non-backwards compatible changes may be made in minor version where impact is believed to be low relative to the benefit provided. Old {0}, New {1}.", + Type = MessageType.Addition, + Severity = MessageSeverity.Warning }; /// /// OpenApi Specification version 3 specific /// - public static ComparisonRule ServerNoLongerSupported = new ComparisonRule + public static ComparisonRule ServerNoLongerSupported = new() { Id = 10021, Code = nameof(ServerNoLongerSupported), Message = "The new version does not support the server with url '{0}' anymore", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1003.md /// - public static ComparisonRule RequestBodyFormatNoLongerSupported = new ComparisonRule + public static ComparisonRule RequestBodyFormatNoLongerSupported = new() { Id = 1003, Code = nameof(RequestBodyFormatNoLongerSupported), Message = "The new version does not support '{0}' as a request body format.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Extension of rule 1003 /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1003.md /// - public static ComparisonRule ResponseBodyInOperationFormatNoLongerSupported = new ComparisonRule + public static ComparisonRule ResponseBodyInOperationFormatNoLongerSupported = new() { Id = 10031, Code = nameof(ResponseBodyInOperationFormatNoLongerSupported), Message = "The new version of operation does not support '{0}' as a response body format.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1004.md /// - public static ComparisonRule ResponseBodyFormatNowSupported = new ComparisonRule + public static ComparisonRule ResponseBodyFormatNowSupported = new() { Id = 1004, Code = nameof(ResponseBodyFormatNowSupported), Message = "The old version did not support '{0}' as a response body format.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Info + }; /// /// Extension of rule 1004 /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1004.md /// - public static ComparisonRule ResponseBodyInOperationFormatNowSupported = new ComparisonRule + public static ComparisonRule ResponseBodyInOperationFormatNowSupported = new() { Id = 10041, Code = nameof(ResponseBodyInOperationFormatNowSupported), Message = "The old version of operation did not support '{0}' as a response body format.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1005.md /// - public static ComparisonRule RemovedPath = new ComparisonRule + public static ComparisonRule RemovedPath = new() { Id = 1005, Code = nameof(RemovedPath), Message = "The new version is missing a path that was found in the old version. Was path '{0}' removed or restructured?", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1006.md /// - public static ComparisonRule RemovedDefinition = new ComparisonRule + public static ComparisonRule RemovedDefinition = new() { Id = 1006, Code = nameof(RemovedDefinition), Message = "The new version is missing a definition that was found in the old version. Was '{0}' removed or renamed?", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1007.md /// - public static ComparisonRule RemovedClientParameter = new ComparisonRule + public static ComparisonRule RemovedClientParameter = new() { Id = 1007, Code = nameof(RemovedClientParameter), Message = "The new version is missing a client parameter that was found in the old version. Was '{0}' removed or renamed?", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1008.md /// - public static ComparisonRule ModifiedOperationId = new ComparisonRule + public static ComparisonRule ModifiedOperationId = new() { Id = 1008, Code = nameof(ModifiedOperationId), Message = "The operation id has been changed from '{0}' to '{1}'. This will impact generated code.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1009.md /// - public static ComparisonRule RemovedRequiredParameter = new ComparisonRule + public static ComparisonRule RemovedRequiredParameter = new() { Id = 1009, Code = nameof(RemovedRequiredParameter), Message = "The required parameter '{0}' was removed in the new version.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1010.md /// - public static ComparisonRule AddingRequiredParameter = new ComparisonRule + public static ComparisonRule AddingRequiredParameter = new() { Id = 1010, Code = nameof(AddingRequiredParameter), Message = "The required parameter '{0}' was added in the new version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1011.md /// - public static ComparisonRule AddingResponseCode = new ComparisonRule + public static ComparisonRule AddingResponseCode = new() { Id = 1011, Code = nameof(AddingResponseCode), Message = "The new version adds a response code '{0}'.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1012.md /// - public static ComparisonRule RemovedResponseCode = new ComparisonRule + public static ComparisonRule RemovedResponseCode = new() { Id = 1012, Code = nameof(RemovedResponseCode), Message = "The new version removes the response code '{0}'", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1013.md /// - public static ComparisonRule AddingHeader = new ComparisonRule + public static ComparisonRule AddingHeader = new() { Id = 1013, Code = nameof(AddingHeader), + Message = "The new version adds a header '{0}'.", + Type = MessageType.Addition, + Severity = MessageSeverity.Info + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1013.md + /// + public static ComparisonRule AddingRequiredHeader = new() + { + Id = 1013, + Code = nameof(AddingRequiredHeader), Message = "The new version adds a required header '{0}'.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1014.md /// - public static ComparisonRule RemovingHeader = new ComparisonRule + public static ComparisonRule RemovingHeader = new() { Id = 1014, Code = nameof(RemovingHeader), Message = "The new version removes a required header '{0}'.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1014.md + /// + public static ComparisonRule RemovingRequestHeader = new() + { + Id = 1014, + Code = nameof(RemovingRequestHeader), + Message = "The new version removes a required header '{0}'.", + Type = MessageType.Removal, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1015.md /// - public static ComparisonRule ParameterInHasChanged = new ComparisonRule + public static ComparisonRule ParameterInHasChanged = new() { Id = 1015, Code = nameof(ParameterInHasChanged), Message = "How the parameter is passed has changed -- it used to be '{0}', now it is '{1}'.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1016.md /// - public static ComparisonRule ConstantStatusHasChanged = new ComparisonRule + public static ComparisonRule ConstantStatusHasChanged = new() { Id = 1016, Code = nameof(ConstantStatusHasChanged), Message = "The 'constant' status changed from the old version to the new.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1017.md /// - public static ComparisonRule ReferenceRedirection = new ComparisonRule + public static ComparisonRule ReferenceRedirection = new() { Id = 1017, Code = nameof(ReferenceRedirection), Message = "The '$ref' property points to different models in the old and new versions.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1018.md /// - public static ComparisonRule RequestBodyFormatNowSupported = new ComparisonRule + public static ComparisonRule RequestBodyFormatNowSupported = new() { Id = 1018, Code = nameof(RequestBodyFormatNowSupported), Message = "The old version did not support '{0}' as a request body format.", Type = MessageType.Addition, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1019.md /// - public static ComparisonRule RemovedEnumValue = new ComparisonRule + public static ComparisonRule RemovedEnumValue = new() { Id = 1019, Code = nameof(RemovedEnumValue), Message = "The new version is removing enum value(s) '{0}' from the old version.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1019.md + /// + public static ComparisonRule RemovedEnumResponseValue = new() + { + Id = 1019, + Code = nameof(RemovedEnumResponseValue), + Message = "The new version is removing enum value(s) '{0}' from the old version.", + Type = MessageType.Removal, + Severity = MessageSeverity.Warning }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1000.md /// - public static ComparisonRule AddedEnumValue = new ComparisonRule + public static ComparisonRule AddedEnumValue = new() { Id = 1020, Code = nameof(AddedEnumValue), Message = "The new version is adding enum value(s) '{0}' from the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1000.md + /// + public static ComparisonRule AddedEnumRequestValue = new() + { + Id = 1020, + Code = nameof(AddedEnumRequestValue), + Message = "The new version is adding enum value(s) '{0}' from the old version.", + Type = MessageType.Addition, + Severity = MessageSeverity.Warning }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1021.md /// - public static ComparisonRule AddedAdditionalProperties = new ComparisonRule + public static ComparisonRule AddedAdditionalProperties = new() { Id = 1021, Code = nameof(AddedAdditionalProperties), Message = "The new version adds an 'additionalProperties' element.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1022.md /// - public static ComparisonRule RemovedAdditionalProperties = new ComparisonRule + public static ComparisonRule RemovedAdditionalProperties = new() { Id = 1022, Code = nameof(RemovedAdditionalProperties), Message = "The new version removes the 'additionalProperties' element.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1023.md + /// + public static ComparisonRule WideningTypeFormatChanged = new() + { + Id = 1023, + Code = nameof(WideningTypeFormatChanged), + Message = "The new version has a different format than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1023.md /// - public static ComparisonRule TypeFormatChanged = new ComparisonRule + public static ComparisonRule TypeFormatChanged = new() { Id = 1023, Code = nameof(TypeFormatChanged), Message = "The new version has a different format than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1024.md /// - public static ComparisonRule ConstraintIsStronger = new ComparisonRule + public static ComparisonRule ConstraintIsStronger = new() { Id = 1024, Code = nameof(ConstraintIsStronger), Message = "The new version has a more constraining '{0}' value than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1024.md + /// + public static ComparisonRule ResponseConstraintIsStronger = new() + { + Id = 1024, + Code = nameof(ResponseConstraintIsStronger), + Message = "The new version has a more constraining '{0}' value than the previous one for a response schema.", + Type = MessageType.Update, + Severity = MessageSeverity.Info + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1024.md + /// + public static ComparisonRule EnumConstraintIsStronger = new() + { + Id = 1024, + Code = nameof(EnumConstraintIsStronger), + Message = "The new version has a more constraining '{0}' value than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1025.md /// - public static ComparisonRule RequiredStatusChange = new ComparisonRule + public static ComparisonRule RequiredStatusAdded = new() { Id = 1025, - Code = nameof(RequiredStatusChange), + Code = nameof(RequiredStatusAdded), Message = "The 'required' status changed from the old version('{0}') to the new version('{1}').", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1025.md + /// + public static ComparisonRule RequiredStatusRemoved = new() + { + Id = 1025, + Code = nameof(RequiredStatusRemoved), + Message = "The 'required' status was removed from the old version('{0}') to the new version('{1}').", + Type = MessageType.Removal, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1026.md /// - public static ComparisonRule TypeChanged = new ComparisonRule + public static ComparisonRule TypeChanged = new() { Id = 1026, Code = nameof(TypeChanged), Message = "The new version has a different type '{0}' than the previous one '{1}'.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1027.md /// - public static ComparisonRule DefaultValueChanged = new ComparisonRule + public static ComparisonRule DefaultValueChanged = new() { Id = 1027, Code = nameof(DefaultValueChanged), Message = "The new version has a different default value than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1028.md /// - public static ComparisonRule ArrayCollectionFormatChanged = new ComparisonRule + public static ComparisonRule ArrayCollectionFormatChanged = new() { Id = 1028, Code = nameof(ArrayCollectionFormatChanged), Message = "The new version has a different array collection format than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// OpenApi Specification version 3 specific /// - public static ComparisonRule ParameterStyleChanged = new ComparisonRule + public static ComparisonRule ParameterStyleChanged = new() { Id = 10281, Code = nameof(ParameterStyleChanged), Message = "Parameter '{0}' has a different style value in the new version.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1029.md /// - public static ComparisonRule ReadonlyPropertyChanged = new ComparisonRule + public static ComparisonRule ReadonlyPropertyChanged = new() { Id = 1029, Code = nameof(ReadonlyPropertyChanged), Message = "The read only property has changed from '{0}' to '{1}'.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1031.md /// - public static ComparisonRule DifferentDiscriminator = new ComparisonRule + public static ComparisonRule DifferentDiscriminator = new() { Id = 1030, Code = nameof(DifferentDiscriminator), Message = "The new version has a different discriminator than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1031.md /// - public static ComparisonRule DifferentExtends = new ComparisonRule + public static ComparisonRule DifferentExtends = new() { Id = 1031, Code = nameof(DifferentExtends), Message = "The new version has a different 'extends' property than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1032.md /// - public static ComparisonRule DifferentAllOf = new ComparisonRule + public static ComparisonRule DifferentAllOf = new() { Id = 1032, Code = nameof(DifferentAllOf), Message = "The new version has a different 'allOf' property than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// OpenApi Specification version 3 specific /// - public static ComparisonRule DifferentOneOf = new ComparisonRule + public static ComparisonRule DifferentOneOf = new() { Id = 10321, Code = nameof(DifferentOneOf), Message = "The new version has a different 'oneOf' property than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1033.md /// - public static ComparisonRule RemovedProperty = new ComparisonRule + public static ComparisonRule RemovedProperty = new() { Id = 1033, Code = nameof(RemovedProperty), Message = "The new version is missing a property found in the old version. Was '{0}' renamed or removed?", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1034.md /// - public static ComparisonRule AddedRequiredProperty = new ComparisonRule + public static ComparisonRule AddedRequiredProperty = new() { Id = 1034, Code = nameof(AddedRequiredProperty), Message = "The new version has new required property '{0}' that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1034.md + /// + public static ComparisonRule AddedRequiredResponseProperty = new() + { + Id = 1034, + Code = nameof(AddedRequiredResponseProperty), + Message = "The new version has new required response property '{0}' that was not found in the old version.", + Type = MessageType.Addition, + Severity = MessageSeverity.Warning }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1035.md /// - public static ComparisonRule RemovedOperation = new ComparisonRule + public static ComparisonRule RemovedOperation = new() { Id = 1035, Code = nameof(RemovedOperation), Message = "The new version is missing an operation that was found in the old version. Was operationId '{0}' removed or restructured?", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1036.md /// - public static ComparisonRule ConstraintChanged = new ComparisonRule + public static ComparisonRule ConstraintChanged = new() { Id = 1036, Code = nameof(ConstraintChanged), Message = "The new version has a different '{0}' value than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1036.md + /// + public static ComparisonRule EnumConstraintChanged = new() + { + Id = 1036, + Code = nameof(EnumConstraintChanged), + Message = "The new version has a different '{0}' value than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Info + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1036.md + /// + public static ComparisonRule MultipleOfConstraintChanged = new() + { + Id = 1036, + Code = nameof(MultipleOfConstraintChanged), + Message = "The new version has a different '{0}' value than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1036.md + /// + public static ComparisonRule UniqueItemsConstraintChanged = new() + { + Id = 1036, + Code = nameof(UniqueItemsConstraintChanged), + Message = "The new version has a different '{0}' value than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1036.md + /// + public static ComparisonRule PatternConstraintChanged = new() + { + Id = 1036, + Code = nameof(PatternConstraintChanged), + Message = "The new version has a different '{0}' value than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1037.md /// - public static ComparisonRule ConstraintIsWeaker = new ComparisonRule + public static ComparisonRule ConstraintIsWeaker = new() { Id = 1037, Code = nameof(ConstraintIsWeaker), Message = "The new version has a less constraining '{0}' value than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1037.md + /// + public static ComparisonRule RequestConstraintIsWeaker = new() + { + Id = 1037, + Code = nameof(RequestConstraintIsWeaker), + Message = "The new version has a less constraining '{0}' value than the previous one in a request schema.", + Type = MessageType.Update, + Severity = MessageSeverity.Info + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1037.md + /// + public static ComparisonRule EnumConstraintIsWeaker = new() + { + Id = 1037, + Code = nameof(EnumConstraintIsWeaker), + Message = "The new version has a less constraining '{0}' value than the previous one.", + Type = MessageType.Update, + Severity = MessageSeverity.Info }; + /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1038.md /// - public static ComparisonRule AddedPath = new ComparisonRule + public static ComparisonRule AddedPath = new() { Id = 1038, Code = nameof(AddedPath), Message = "The new version is adding a path that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1039.md /// - public static ComparisonRule AddedOperation = new ComparisonRule + public static ComparisonRule AddedOperation = new() { Id = 1039, Code = nameof(AddedOperation), Message = "The new version is adding an operation that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1040.md /// - public static ComparisonRule AddedReadOnlyPropertyInResponse = new ComparisonRule + public static ComparisonRule AddedReadOnlyPropertyInResponse = new() { Id = 1040, Code = nameof(AddedReadOnlyPropertyInResponse), Message = "The new version has a new read-only property '{0}' in response that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Info }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1041.md /// - public static ComparisonRule AddedPropertyInResponse = new ComparisonRule + public static ComparisonRule AddedPropertyInResponse = new() { Id = 1041, Code = nameof(AddedPropertyInResponse), Message = "The new version has a new property '{0}' in response that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Warning + }; + + /// + /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1041.md + /// + public static ComparisonRule AddedBreakingPropertyInResponse = new() + { + Id = 1041, + Code = nameof(AddedBreakingPropertyInResponse), + Message = "The new version has a new property '{0}' in response that was not found in the old version and additional properties are specifically forbidden.", + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1042.md /// - public static ComparisonRule ChangedParameterOrder = new ComparisonRule + public static ComparisonRule ChangedParameterOrder = new() { Id = 1042, Code = nameof(ChangedParameterOrder), Message = "The order of parameter '{0}' was changed. ", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1043.md /// - public static ComparisonRule AddingOptionalParameter = new ComparisonRule + public static ComparisonRule AddingOptionalParameter = new() { Id = 1043, Code = nameof(AddingOptionalParameter), Message = "The optional parameter '{0}' was added in the new version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1044.md /// - public static ComparisonRule LongRunningOperationExtensionChanged = new ComparisonRule + public static ComparisonRule LongRunningOperationExtensionChanged = new() { Id = 1044, Code = nameof(LongRunningOperationExtensionChanged), Message = "The new version has a different 'x-ms-long-running-operation' value than the previous one.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1045.md /// - public static ComparisonRule AddedOptionalProperty = new ComparisonRule + public static ComparisonRule AddedOptionalProperty = new() { Id = 1045, Code = nameof(AddedOptionalProperty), Message = "The new version has a new optional property '{0}' that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Warning }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1046.md /// - public static ComparisonRule AddedRequestBody = new ComparisonRule + public static ComparisonRule AddedRequestBody = new() { Id = 1046, Code = nameof(AddedRequestBody), Message = "The new version is adding a requestBody that was not found in the old version.", - Type = MessageType.Addition + Type = MessageType.Addition, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1047.md /// - public static ComparisonRule RemovedRequestBody = new ComparisonRule + public static ComparisonRule RemovedRequestBody = new() { Id = 1047, Code = nameof(RemovedRequestBody), Message = "The new version is removing a requestBody that was found in the old version.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Breaking }; /// /// Rule documentation: https://github.com/Azure/openapi-diff/blob/master/docs/rules/1048.md /// - public static ComparisonRule AddedSchema = new ComparisonRule + public static ComparisonRule AddedSchema = new() { Id = 1048, Code = nameof(AddedSchema), Message = "The new version is adding a new schema that was not found in the old version.", - Type = MessageType.Removal + Type = MessageType.Removal, + Severity = MessageSeverity.Error }; /// /// OpenApi Specification version 3 specific /// - public static ComparisonRule NullablePropertyChanged = new ComparisonRule() + public static ComparisonRule NullablePropertyChanged = new() { Id = 2000, Code = nameof(NullablePropertyChanged), Message = "The nullable property has changed from '{0}' to '{1}'.", - Type = MessageType.Update + Type = MessageType.Update, + Severity = MessageSeverity.Breaking + }; + + /// + /// OpenApi Specification version 3 specific + /// + public static ComparisonRule OpenApiError = new() + { + Id = 9000, + Code = nameof(OpenApiError), + Severity = MessageSeverity.Error, + Type = MessageType.Specification }; } } diff --git a/src/Criteo.OpenApi.Comparator/Criteo.OpenApi.Comparator.csproj b/src/Criteo.OpenApi.Comparator/Criteo.OpenApi.Comparator.csproj index 3aa0014..f10a894 100644 --- a/src/Criteo.OpenApi.Comparator/Criteo.OpenApi.Comparator.csproj +++ b/src/Criteo.OpenApi.Comparator/Criteo.OpenApi.Comparator.csproj @@ -14,7 +14,7 @@ Criteo Criteo Copyright (c) Criteo Technology. All rights reserved. - 0.8.3 + 1.0.0 https://github.com/criteo/openapi-comparator https://github.com/criteo/openapi-comparator Criteo, OpenApi, OpenApi-Comparator, OpenApi-Diff, Swagger diff --git a/src/Criteo.OpenApi.Comparator/Logging/ChangeLevel.cs b/src/Criteo.OpenApi.Comparator/Logging/ChangeLevel.cs new file mode 100644 index 0000000..124d3e7 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator/Logging/ChangeLevel.cs @@ -0,0 +1,32 @@ +// Copyright (c) Criteo Technology. All rights reserved. +// Licensed under the Apache 2.0 License. See LICENSE in the project root for license information. + +namespace Criteo.OpenApi.Comparator.Logging; + +/// +/// Used to express to severity of detected change. +/// this property is defined according to our own interpretation of the +/// comparison rules and may differ according to your own interpretation. +/// +public enum ChangeLevel +{ + /// + /// No changes detected + /// + None, + + /// + /// The change hasn't any impact on the API (i.e. the version of the OAS is updated) + /// + Info, + + /// + /// The change has an impact on the API, but should not break client's integration (i.e. adding an optional parameter) + /// + Warning, + + /// + /// The change should be considered as a breaking change (i.e. updating the format of a response body) + /// + Error +} \ No newline at end of file diff --git a/src/Criteo.OpenApi.Comparator/Logging/LogMessageSeverity.cs b/src/Criteo.OpenApi.Comparator/Logging/LogMessageSeverity.cs deleted file mode 100644 index 02c2a31..0000000 --- a/src/Criteo.OpenApi.Comparator/Logging/LogMessageSeverity.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Criteo Technology. All rights reserved. -// Licensed under the Apache 2.0 License. See LICENSE in the project root for license information. - -namespace Criteo.OpenApi.Comparator.Logging -{ - /// - /// Used to express to severity of detected change. - /// this property is defined according to our own interpretation of the - /// comparison rules and may differ according to your own interpretation. - /// - public enum Severity - { - /// - /// The change hasn't any impact on the API (i.e. the version of the OAS is updated) - /// - Info, - - /// - /// The change has an impact on the API, but should not break client's integration (i.e. adding an optional parameter) - /// - Warning, - - /// - /// The change should be considered as a breaking change (i.e. updating the format of a response body) - /// - Error - } -} diff --git a/src/Criteo.OpenApi.Comparator/Logging/ObjectPath.cs b/src/Criteo.OpenApi.Comparator/Logging/ObjectPath.cs index a25e161..78318e5 100644 --- a/src/Criteo.OpenApi.Comparator/Logging/ObjectPath.cs +++ b/src/Criteo.OpenApi.Comparator/Logging/ObjectPath.cs @@ -150,6 +150,11 @@ private static (JToken token, string reference) FindTokenFromReference(JToken to /// The json path of the json document internal string JsonPointer(IJsonDocument jsonDocument, bool resolveReferences = false) { + if (jsonDocument == null) + { + return null; + } + string path = null; foreach (var (_, reference, name) in CompletePath(jsonDocument.Token)) { diff --git a/src/Criteo.OpenApi.Comparator/MessageSeverity.cs b/src/Criteo.OpenApi.Comparator/MessageSeverity.cs new file mode 100644 index 0000000..8de60b3 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator/MessageSeverity.cs @@ -0,0 +1,35 @@ +// Copyright (c) Criteo Technology. All rights reserved. +// Licensed under the Apache 2.0 License. See LICENSE in the project root for license information. + +using Criteo.OpenApi.Comparator.Logging; + +namespace Criteo.OpenApi.Comparator +{ + /// + /// Types of differences that can be found + /// + public enum MessageSeverity + { + /// + /// The change hasn't any impact on the API (i.e. the version of the OAS is updated) + /// + Info, + + /// + /// The change has an impact on the API, but should not break client's integration (i.e. adding an optional parameter) + /// + Warning, + + /// + /// The change should be considered as a breaking change (i.e. updating the format of a response body) + /// This would be an error if in strict mode, or when comparing minor version changes. + /// When not in strict mode (major version changes) it will be considered a warning. + /// + Breaking, + + /// + /// The change should be considered as an error regardless of the version differences + /// + Error + } +} diff --git a/src/Criteo.OpenApi.Comparator/MessageType.cs b/src/Criteo.OpenApi.Comparator/MessageType.cs index 45cef10..9b130ab 100644 --- a/src/Criteo.OpenApi.Comparator/MessageType.cs +++ b/src/Criteo.OpenApi.Comparator/MessageType.cs @@ -22,5 +22,10 @@ public enum MessageType /// If an OpenAPI element is removed in the new version /// Removal, + + /// + /// An element doesn't follow the Open API specification + /// + Specification } } diff --git a/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs b/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs index 1323ffb..64231dd 100644 --- a/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs +++ b/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs @@ -1,68 +1,134 @@ // Copyright (c) Criteo Technology. All rights reserved. // Licensed under the Apache 2.0 License. See LICENSE in the project root for license information. +using System; using System.Collections.Generic; using System.Linq; using Criteo.OpenApi.Comparator.Comparators; +using Criteo.OpenApi.Comparator.Logging; using Criteo.OpenApi.Comparator.Parser; using Microsoft.OpenApi.Models; -namespace Criteo.OpenApi.Comparator +namespace Criteo.OpenApi.Comparator; + +/// +/// OpenAPI Comparator base class +/// +public static class OpenApiComparator { /// - /// OpenAPI Comparator base class + /// Compares two OpenAPI specification. /// - public static class OpenApiComparator + /// The content of the old OpenAPI Specification + /// The content of the new OpenAPI Specification + /// Result Details + /// The severity of the changes. + public static ChangeLevel Compare( + out List comparisonMessages, + string oldOpenApiSpec, + string newOpenApiSpec) { - /// - /// Compares two OpenAPI specification. - /// - /// The content of the old OpenAPI Specification - /// The content of the new OpenAPI Specification - /// Parsing errors - /// If true, then breaking changes are errors instead of warnings. - public static IEnumerable Compare( - string oldOpenApiSpec, - string newOpenApiSpec, - out IEnumerable parsingErrors, - bool strict = false) - { - var oldOpenApiDocument = OpenApiParser.Parse(oldOpenApiSpec, out var oldSpecDiagnostic); - var newOpenApiDocument = OpenApiParser.Parse(newOpenApiSpec, out var newSpecDiagnostic); + var oldOpenApiDocument = OpenApiParser.Parse(oldOpenApiSpec, out var oldSpecDiagnostic); + var newOpenApiDocument = OpenApiParser.Parse(newOpenApiSpec, out var newSpecDiagnostic); + var context = new ComparisonContext(oldOpenApiDocument, newOpenApiDocument); - parsingErrors = oldSpecDiagnostic.Errors - .Select(e => new ParsingError("old", e)) - .Concat(newSpecDiagnostic.Errors.Select(e => new ParsingError("new", e))); + var comparator = new OpenApiDocumentComparator(); + comparisonMessages = comparator.Compare(context, oldOpenApiDocument.Typed, newOpenApiDocument.Typed); + comparisonMessages.AddRange(oldSpecDiagnostic.Errors.Select(item => new ComparisonMessage(item))); + comparisonMessages.AddRange(newSpecDiagnostic.Errors.Select(item => new ComparisonMessage(item))); - var context = new ComparisonContext(oldOpenApiDocument, newOpenApiDocument) { Strict = strict }; + if (!comparisonMessages.Any()) + return ChangeLevel.None; - var comparator = new OpenApiDocumentComparator(); - var comparisonMessages = comparator.Compare(context, oldOpenApiDocument.Typed, newOpenApiDocument.Typed); + var maxSeverity = comparisonMessages.Max(s => s.Severity); - return comparisonMessages; - } + return maxSeverity switch + { + MessageSeverity.Error => ChangeLevel.Error, + MessageSeverity.Breaking => ChangeLevel.Error, + MessageSeverity.Warning => ChangeLevel.Warning, + MessageSeverity.Info => ChangeLevel.Info, + _ => ChangeLevel.Warning + }; } /// - /// Represents an error that occurred while parsing an OpenAPI document. + /// Compares two OpenAPI specification. /// - public class ParsingError + /// The content of the old OpenAPI Specification + /// The content of the new OpenAPI Specification + /// Parsing errors + /// If true, then breaking changes are errors instead of warnings. + [Obsolete( + "Use new compare which returns severity and parsing errors as a comparison message. Also strict is true by default")] + public static IEnumerable Compare( + string oldOpenApiSpec, + string newOpenApiSpec, + out IEnumerable parsingErrors, + bool strict = false) { - private readonly string _documentName; - private readonly OpenApiError _error; - - /// - /// Initializes a new instance of the class. - /// - /// - /// - public ParsingError(string documentName, OpenApiError error) + var oldOpenApiDocument = OpenApiParser.Parse(oldOpenApiSpec, out var oldSpecDiagnostic); + var newOpenApiDocument = OpenApiParser.Parse(newOpenApiSpec, out var newSpecDiagnostic); + + parsingErrors = oldSpecDiagnostic.Errors + .Select(e => new ParsingError("old", e)) + .Concat(newSpecDiagnostic.Errors.Select(e => new ParsingError("new", e))); + + var context = new ComparisonContext(oldOpenApiDocument, newOpenApiDocument); + + var comparator = new OpenApiDocumentComparator(); + var comparisonMessages = comparator.Compare(context, oldOpenApiDocument.Typed, newOpenApiDocument.Typed); + + if (comparisonMessages.Any(c => + c.Code == ComparisonRules.NoVersionChange.Code)) + strict = true; + + // If it was a patch version run in strict + if (comparisonMessages.All(c => + c.Code != ComparisonRules.MajorVersionChange.Code && + c.Code != ComparisonRules.MinorVersionChange.Code + )) + strict = true; + + comparisonMessages.RemoveAll(c => + c.Code == ComparisonRules.MajorVersionChange.Code || + c.Code == ComparisonRules.MinorVersionChange.Code + ); + + + comparisonMessages.ForEach(c => { - _documentName = documentName; - _error = error; - } + if (c.Severity == MessageSeverity.Breaking) + c.Severity = strict ? MessageSeverity.Error : MessageSeverity.Warning; + }); - /// - public override string ToString() => $"[{_documentName}] {_error}"; + return comparisonMessages; } } + +/// +/// Represents an error that occurred while parsing an OpenAPI document. +/// +[Obsolete] +public class ParsingError +{ + private readonly string _documentName; + private readonly OpenApiError _error; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public ParsingError(string documentName, OpenApiError error) + { + _documentName = documentName; + _error = error; + } + + /// + public override string ToString() + { + return $"[{_documentName}] {_error}"; + } +} \ No newline at end of file