Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions documentation/rules/2001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
### 2001 - RemovedRequiredProperty

**Description**: Checks whether an existing required property is removed from the previous specification.

**Cause**: This is considered a breaking change when the property is used in responses.

**Example**: Property `name` of schema `Person` is removed in the new version.

Old specification
```json5
{
"Person": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32",
"xml": {
"attribute": true
}
},
"name": {
"type": "string",
"xml": {
"namespace": "http://example.com/schema/sample",
"prefix": "sample"
}
}
},
"required": [
"id",
"name"
]
}
}
```

New specification
```json5
{
"Person": {
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32",
"xml": {
"attribute": true
}
},
"name": {
"type": "string",
"xml": {
"namespace": "http://example.com/schema/sample",
"prefix": "sample"
}
}
},
"required": [
"id"
]
}
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,21 @@ public void Compare_OAS_Should_Return_Nullable_Property_Changed_When_Nullable_Pr
});
}

[Test]
public void CompareOAS_ShouldReturn_RemovedRequiredPropertyDifferences()
{
var differences = CompareSpecifications("removed_required_property.json");

var expectedDifference = new ExpectedDifference
{
Rule = ComparisonRules.RemovedRequiredProperty,
Severity = Severity.Warning,
NewJsonRef = "#/paths/~1pets/put/responses/200/content/text~1plain/schema"
};
differences.AssertContains(expectedDifference, 1);
differences.AssertContainsOnly(expectedDifference);
}

/// <summary>
/// Helper method -- load two Open Api Specification documents and invoke the comparison logic.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
{
"openapi": "3.0.0",
"info": {
"title": "Removed_required_property",
"version": "2.0"
},
"paths": {
"/pets": {
"put": {
"description": "Update a pet",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PetWrite"
}
}
}
},
"responses": {
"200": {
"description": "Request successful",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/PetRead"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"PetRead": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"petType": {
"type": "string",
"enum": [
"cat",
"dog"
]
}
},
"required": [
"petType"
]
},
"PetWrite": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"petType": {
"type": "string",
"enum": [
"cat",
"dog"
]
}
},
"required": [
"petType"
]
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,6 @@
"schemas": {
"Pet": {
"type": "object",
"required": [
"name"
],
"discriminator": {
"propertyName": "petType"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"openapi": "3.0.0",
"info": {
"title": "Removed_required_property",
"version": "1.0"
},
"paths": {
"/pets": {
"put": {
"description": "Update a pet",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PetWrite"
}
}
}
},
"responses": {
"200": {
"description": "Request successful",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/PetRead"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"PetRead": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"petType": {
"type": "string",
"enum": [
"cat",
"dog"
]
}
},
"required": [
"name",
"petType"
]
},
"PetWrite": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"petType": {
"type": "string",
"enum": [
"cat",
"dog"
]
}
},
"required": [
"name",
"petType"
]
}
}
}
}
19 changes: 18 additions & 1 deletion src/Criteo.OpenApi.Comparator/Comparators/SchemaComparator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ private static void CompareRequired(ComparisonContext context,
ISet<string> oldRequired,
ISet<string> newRequired)
{
if (newRequired == null)
if (newRequired == null && (oldRequired == null || context.Direction == DataDirection.Request))
return;

if (oldRequired == null)
Expand All @@ -556,12 +556,29 @@ private static void CompareRequired(ComparisonContext context,
return;
}

if (newRequired == null)
{
context.LogBreakingChange(ComparisonRules.RemovedRequiredProperty, string.Join(", ", oldRequired));
return;
}

List<string> addedRequiredProperties = newRequired.Except(oldRequired).ToList();
if (addedRequiredProperties.Any())
{
context.LogBreakingChange(ComparisonRules.AddedRequiredProperty,
string.Join(", ", addedRequiredProperties));
}

if (context.Direction == DataDirection.Request)
{
return;
}
List<string> removedRequiredProperties = oldRequired.Except(newRequired).ToList();
if (removedRequiredProperties.Any())
{
context.LogBreakingChange(ComparisonRules.RemovedRequiredProperty,
string.Join(", ", addedRequiredProperties));
}
}
}
}
11 changes: 11 additions & 0 deletions src/Criteo.OpenApi.Comparator/ComparisonRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -609,5 +609,16 @@ public static class ComparisonRules
Message = "The nullable property has changed from '{0}' to '{1}'.",
Type = MessageType.Update
};

/// <summary>
/// OpenApi Specification version 3 specific
/// </summary>
public static ComparisonRule RemovedRequiredProperty = new ComparisonRule()
{
Id = 2001,
Code = nameof(RemovedRequiredProperty),
Message = "The required property '{0}' was removed in the new version.",
Type = MessageType.Removal
};
}
}