From 399e62f2b8064e5f6a3f7d11b975db7b17d2f60a Mon Sep 17 00:00:00 2001 From: Nam Vu Date: Tue, 9 Jul 2024 17:15:32 +0200 Subject: [PATCH] feat: better handling of enum changes based on direction | Enum Change | In Request (path/query/body) | In Response (body) | In Both | |---------------|------------------------------|--------------------|----------| | Value Added | OK | Breaking | Breaking | | Value Removed | Breaking | OK | Breaking | --- .../OpenApiEnumDirectionTests.cs | 82 +++++++++++++++++++ .../OpenApiSpecificationsCompareTests.cs | 2 +- .../Resource/enum_direction/body_add.yaml | 68 +++++++++++++++ .../Resource/enum_direction/body_remove.yaml | 66 +++++++++++++++ .../Resource/enum_direction/path_add.yaml | 68 +++++++++++++++ .../Resource/enum_direction/path_remove.yaml | 66 +++++++++++++++ .../Resource/enum_direction/query_add.yaml | 68 +++++++++++++++ .../Resource/enum_direction/query_remove.yaml | 66 +++++++++++++++ .../Resource/enum_direction/reference.json | 72 ++++++++++++++++ .../Resource/enum_direction/response_add.yaml | 68 +++++++++++++++ .../enum_direction/response_remove.yaml | 66 +++++++++++++++ .../Comparators/SchemaComparator.cs | 63 +++++++------- .../ComparisonContext.cs | 12 +++ .../OpenApiComparator.cs | 5 +- 14 files changed, 736 insertions(+), 36 deletions(-) create mode 100644 src/Criteo.OpenApi.Comparator.UTest/OpenApiEnumDirectionTests.cs create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_add.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_remove.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_add.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_remove.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_add.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_remove.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/reference.json create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_add.yaml create mode 100644 src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_remove.yaml diff --git a/src/Criteo.OpenApi.Comparator.UTest/OpenApiEnumDirectionTests.cs b/src/Criteo.OpenApi.Comparator.UTest/OpenApiEnumDirectionTests.cs new file mode 100644 index 0000000..eb9228c --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/OpenApiEnumDirectionTests.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +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 OpenApiEnumDirectionTests +{ + [TestCase("path_add", "#/paths/~1order~1{path}/post/parameters/0/schema/enum")] + [TestCase("query_add", "#/paths/~1order~1{path}/post/parameters/1/schema/enum")] + [TestCase("body_add", "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum")] + public void CompareOAS_ShouldReturn_NonBreakingEnumChange_InRequest(string oasName, string jsonRef) + { + var differences = CompareSpecifications(oasName); + differences.AssertContains(new ExpectedDifference + { + Rule = ComparisonRules.AddedEnumValue, + Severity = Severity.Warning, + NewJsonRef = jsonRef + }, 1); + } + + [TestCase("response_remove", "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum")] + public void CompareOAS_ShouldReturn_NonBreakingEnumChange_InResponse(string oasName, string jsonRef) + { + var differences = CompareSpecifications(oasName); + differences.AssertContains(new ExpectedDifference + { + Rule = ComparisonRules.RemovedEnumValue, + Severity = Severity.Warning, + NewJsonRef = jsonRef + }, 1); + } + + [TestCase("path_remove", "#/paths/~1order~1{path}/post/parameters/0/schema/enum")] + [TestCase("query_remove", "#/paths/~1order~1{path}/post/parameters/1/schema/enum")] + [TestCase("body_remove", "#/paths/~1order~1{path}/post/requestBody/content/application~1json/schema/properties/foo/enum")] + public void CompareOAS_ShouldReturn_BreakingEnumChange_InRequest(string oasName, string jsonRef) + { + var differences = CompareSpecifications(oasName); + differences.AssertContains(new ExpectedDifference + { + Rule = ComparisonRules.RemovedEnumValue, + Severity = Severity.Error, + NewJsonRef = jsonRef, + }, 1); + } + + [TestCase("response_add", "#/paths/~1order~1{path}/post/responses/200/content/application~1json/schema/properties/bar/enum")] + public void CompareOAS_ShouldReturn_BreakingEnumChange_InResponse(string oasName, string jsonRef) + { + var differences = CompareSpecifications(oasName); + differences.AssertContains(new ExpectedDifference + { + Rule = ComparisonRules.AddedEnumValue, + Severity = Severity.Error, + NewJsonRef = jsonRef + }, 1); + } + + private static IList CompareSpecifications(string oasName) + { + var baseDirectory = Directory + .GetParent(typeof(OpenApiSpecificationsCompareTests).GetTypeInfo().Assembly.Location) + .ToString(); + + var referenceFileName = Path.Combine(baseDirectory, "Resource", "enum_direction", "reference.json"); + var newFileName = Path.Combine(baseDirectory, "Resource", "enum_direction", oasName + ".yaml"); + + var differences = OpenApiComparator + .Compare(File.ReadAllText(referenceFileName), File.ReadAllText(newFileName), strict: true) + .ToList(); + + OpenApiSpecificationsCompareTests.ValidateDifferences(differences); + + return differences; + } +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs b/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs index 7c265d8..5b48070 100644 --- a/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs +++ b/src/Criteo.OpenApi.Comparator.UTest/OpenApiSpecificationsCompareTests.cs @@ -859,7 +859,7 @@ private static void ValidateMessage(ComparisonMessage message) } } - private static void ValidateDifferences(IEnumerable differences) + internal static void ValidateDifferences(IEnumerable differences) { foreach (var message in differences) { diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_add.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_add.yaml new file mode 100644 index 0000000..3ba1844 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_add.yaml @@ -0,0 +1,68 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - 123 + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_remove.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_remove.yaml new file mode 100644 index 0000000..b791384 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/body_remove.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_add.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_add.yaml new file mode 100644 index 0000000..c038a4e --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_add.yaml @@ -0,0 +1,68 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - 123 + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_remove.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_remove.yaml new file mode 100644 index 0000000..6ccfcc1 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/path_remove.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_add.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_add.yaml new file mode 100644 index 0000000..415179a --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_add.yaml @@ -0,0 +1,68 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + - 123 + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_remove.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_remove.yaml new file mode 100644 index 0000000..c503594 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/query_remove.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/reference.json b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/reference.json new file mode 100644 index 0000000..3e20f51 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/reference.json @@ -0,0 +1,72 @@ +{ + "openapi": "3.0.0", + "info": { "title": "My API", "version": "0.1.0" }, + "paths": { + "/order/{path}": { + "post": { + "summary": "New Order", + "operationId": "new_order_order__path__post", + "parameters": [ + { + "name": "path", + "in": "path", + "required": true, + "schema": { + "enum": ["abc", "def"], + "type": "string", + "title": "Path" + } + }, + { + "name": "query", + "in": "query", + "required": true, + "schema": { + "enum": ["ghi", "jkl"], + "type": "string", + "title": "Query" + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/RequestModel" } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ResponseModel" } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "RequestModel": { + "properties": { + "foo": { "type": "string", "enum": ["mno", "pqr"], "title": "Foo" } + }, + "type": "object", + "required": ["foo"], + "title": "RequestModel" + }, + "ResponseModel": { + "properties": { + "bar": { "type": "string", "enum": ["stu", "vwx"], "title": "Bar" } + }, + "type": "object", + "required": ["bar"], + "title": "ResponseModel" + } + } + } +} diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_add.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_add.yaml new file mode 100644 index 0000000..715c974 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_add.yaml @@ -0,0 +1,68 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + - vwx + - 123 + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_remove.yaml b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_remove.yaml new file mode 100644 index 0000000..c3fcd81 --- /dev/null +++ b/src/Criteo.OpenApi.Comparator.UTest/Resource/enum_direction/response_remove.yaml @@ -0,0 +1,66 @@ +openapi: 3.0.0 +info: + title: My API + version: 0.2.0 +paths: + /order/{path}: + post: + summary: New Order + operationId: new_order_order__path__post + parameters: + - name: path + in: path + required: true + schema: + enum: + - abc + - def + type: string + title: Path + - name: query + in: query + required: true + schema: + enum: + - ghi + - jkl + type: string + title: Query + requestBody: + required: true + content: + application/json: + schema: + $ref: "#/components/schemas/RequestModel" + responses: + "200": + description: Successful Response + content: + application/json: + schema: + $ref: "#/components/schemas/ResponseModel" +components: + schemas: + RequestModel: + properties: + foo: + type: string + enum: + - mno + - pqr + title: Foo + type: object + required: + - foo + title: RequestModel + ResponseModel: + properties: + bar: + type: string + enum: + - stu + title: Bar + type: object + required: + - bar + title: ResponseModel diff --git a/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs b/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs index 0442ebb..06c55d6 100644 --- a/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs +++ b/src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs @@ -110,10 +110,10 @@ internal void Compare(ComparisonContext context, CompareProperties(context, oldSchema, newSchema, isSchemaReferenced); CompareRequired(context, oldSchema.Required, newSchema.Required); - + CompareNullable(context, oldSchema.Nullable, newSchema.Nullable); } - + private static void CompareNullable(ComparisonContext context, bool oldNullable, bool newNullable) @@ -171,8 +171,8 @@ private static void CompareDefault(ComparisonContext context, context.Pop(); } - private static void CompareConstraints(ComparisonContext context, - OpenApiSchema oldSchema, OpenApiSchema newSchema) + private static void CompareConstraints(ComparisonContext context, + OpenApiSchema oldSchema, OpenApiSchema newSchema) { if (oldSchema.Maximum.DifferFrom(newSchema.Maximum) || oldSchema.ExclusiveMaximum != newSchema.ExclusiveMaximum) @@ -230,30 +230,30 @@ private static void CompareConstraints(ComparisonContext context, } } - private static void CompareConstraint(ComparisonContext context, decimal? oldConstraint, - decimal? newConstraint, string attributeName, bool isLowerBound, bool additionalCondition = false) - { - context.PushProperty(attributeName); - if (additionalCondition) - { - context.LogBreakingChange(ComparisonRules.ConstraintChanged, attributeName); - } - else if (Narrows(oldConstraint, newConstraint, isLowerBound)) - { - if (context.Direction == DataDirection.Request) + private static void CompareConstraint(ComparisonContext context, decimal? oldConstraint, + decimal? newConstraint, string attributeName, bool isLowerBound, bool additionalCondition = false) + { + context.PushProperty(attributeName); + if (additionalCondition) + { + context.LogBreakingChange(ComparisonRules.ConstraintChanged, attributeName); + } + else if (Narrows(oldConstraint, newConstraint, isLowerBound)) + { + if (context.Direction == DataDirection.Request) context.LogBreakingChange(ComparisonRules.ConstraintIsStronger, attributeName); - else + else context.LogInfo(ComparisonRules.ConstraintIsStronger, attributeName); - } - else if (Widens(oldConstraint, newConstraint, isLowerBound)) - { - if (context.Direction == DataDirection.Response) + } + else if (Widens(oldConstraint, newConstraint, isLowerBound)) + { + if (context.Direction == DataDirection.Response) context.LogBreakingChange(ComparisonRules.ConstraintIsWeaker, attributeName); - else + else context.LogInfo(ComparisonRules.ConstraintIsWeaker, attributeName); - } - context.Pop(); - } + } + context.Pop(); + } private static bool Narrows(decimal? oldConstraint, decimal? newConstraint, bool isLowerBound) { @@ -337,19 +337,16 @@ private static void CompareEnum(ComparisonContext context, var addedEnums = newEnum.Where(newEnumElement => oldEnum.All(newEnumElement.DifferFrom)).ToList(); relaxes = addedEnums.Any(); - if (context.Direction == DataDirection.Request && constrains) + if (constrains) { - context.LogBreakingChange(ComparisonRules.RemovedEnumValue, - string.Join(", ", removedEnums)); + LogAction logger = context.Direction == DataDirection.Request ? context.LogBreakingChange : context.LogWarning; + logger(ComparisonRules.RemovedEnumValue, string.Join(", ", removedEnums)); } - if (context.Direction == DataDirection.Response && relaxes) + if (relaxes && !IsEnumModelAsString(enumExtension)) { - if (!IsEnumModelAsString(enumExtension)) - { - context.LogBreakingChange(ComparisonRules.AddedEnumValue, - string.Join(", ", addedEnums)); - } + LogAction logger = context.Direction == DataDirection.Response ? context.LogBreakingChange : context.LogWarning; + logger(ComparisonRules.AddedEnumValue, string.Join(", ", addedEnums)); } } diff --git a/src/Criteo.OpenApi.Comparator/ComparisonContext.cs b/src/Criteo.OpenApi.Comparator/ComparisonContext.cs index 619ce1b..7ad0f72 100644 --- a/src/Criteo.OpenApi.Comparator/ComparisonContext.cs +++ b/src/Criteo.OpenApi.Comparator/ComparisonContext.cs @@ -9,6 +9,8 @@ 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 @@ -69,6 +71,16 @@ internal void LogInfo(ComparisonRule rule, params object[] formatArguments) => 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, diff --git a/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs b/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs index 8d4c669..1f6e546 100644 --- a/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs +++ b/src/Criteo.OpenApi.Comparator/OpenApiComparator.cs @@ -17,12 +17,13 @@ public static class OpenApiComparator /// /// The content of the old OpenAPI Specification /// The content of the new OpenAPI Specification - public static IEnumerable Compare(string oldOpenApiSpec, string newOpenApiSpec) + /// If true, then breaking changes are errors instead of warnings. + public static IEnumerable Compare(string oldOpenApiSpec, string newOpenApiSpec, bool strict = false) { var oldOpenApiDocument = OpenApiParser.Parse(oldOpenApiSpec); var newOpenApiDocument = OpenApiParser.Parse(newOpenApiSpec); - var context = new ComparisonContext(oldOpenApiDocument, newOpenApiDocument); + var context = new ComparisonContext(oldOpenApiDocument, newOpenApiDocument) { Strict = strict }; var comparator = new OpenApiDocumentComparator(); var comparisonMessages = comparator.Compare(context, oldOpenApiDocument.Typed, newOpenApiDocument.Typed);