Skip to content

Commit

Permalink
Fix bug in implied "object" validation in OpenAPI schema. (#38)
Browse files Browse the repository at this point in the history
* Added support for validating "oneOf" and "anyOf" without specifiying the top-level object type.

Added specs that demonstrate that behaviour.

Added specs that demonstrate that the schema is not broken for implied "object" types.

* Added validation for the "allOf" case too.

* Added an ADR for the "allow implict object type" scenario.
  • Loading branch information
mwadams authored May 5, 2020
1 parent 72ee5de commit 3a8304f
Show file tree
Hide file tree
Showing 6 changed files with 392 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,30 +170,20 @@ private void ValidateType(JToken token, OpenApiSchema schema, string? propertyNa

private JsonObjectType ToJsonObjectType(string type)
{
switch (type)
{
case "none":
return JsonObjectType.None;
case "array":
return JsonObjectType.Array;
case "boolean":
return JsonObjectType.Boolean;
case "integer":
return JsonObjectType.Integer;
case "null":
return JsonObjectType.Null;
case "number":
return JsonObjectType.Number;
case "object":
case null: // Lazy people don't say that they are an object
return JsonObjectType.Object;
case "string":
return JsonObjectType.String;
case "File":
return JsonObjectType.File;
default:
throw new ArgumentOutOfRangeException(nameof(type));
}
return type switch
{
"none" => JsonObjectType.None,
"array" => JsonObjectType.Array,
"boolean" => JsonObjectType.Boolean,
"integer" => JsonObjectType.Integer,
"null" => JsonObjectType.Null,
"number" => JsonObjectType.Number,
"object" => JsonObjectType.Object,
null => JsonObjectType.None,
"string" => JsonObjectType.String,
"File" => JsonObjectType.File,
_ => throw new ArgumentOutOfRangeException(nameof(type)),
};
}

private IEnumerable<JsonObjectType> GetTypes(OpenApiSchema schema)
Expand Down
44 changes: 44 additions & 0 deletions Solutions/Menes.Specs/Features/OpenApiValidation.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
Feature: OpenApiValidation
In order to diagnose bad requests or responses
As a developer
I want to be able to define openapi validation rules in my schema

@perScenarioContainer
Scenario Outline: Validate object definition
Given the schema '<Schema Fragment>'
And the payload '<Payload JSON>'
When I validate the payload against the schema
Then the result should be <Result>

Examples:
| ID | Schema Fragment | Payload JSON | Result |
| 1 | { "properties": {"foo": {type: "string"}, "bar": { type: "number" } } } | { "foo": "something", "bar": 14 } | valid |
| 2 | { "type": "object", "properties": {"foo": {type: "string"}, "bar": { type: "number" } } } | { "foo": "something", "bar": 14 } | valid |

@perScenarioContainer
Scenario Outline: Validate anyOf allOf and oneOf
Given the schema '<Schema Fragment>'
And the payload '<Payload JSON>'
When I validate the payload against the schema
Then the result should be <Result>

Examples:
| ID | Schema Fragment | Payload JSON | Result |
| 1 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | 3 | valid |
| 2 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | 3.3 | valid |
| 3 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | [3.3, "henry"] | valid |
| 4 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | true | valid |
| 5 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | false | valid |
| 6 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | { "foo": "something", "bar": 14 } | valid |
| 7 | { "anyOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | "A string" | invalid |
# Item 8 is *invalid* because "3" matches BOTH a number AND an integer (i.e. two of, not one of)
| 8 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | 3 | invalid |
| 9 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | 3.3 | valid |
| 10 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | [3.3, "henry"] | valid |
| 11 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | true | valid |
| 12 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | false | valid |
| 13 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | { "foo": "something", "bar": 14 } | valid |
| 14 | { "oneOf": [{"type": "object"}, {"type": "array"}, {"type": "boolean"}, {"type": "integer"}, {"type": "number"}] } | "A string" | invalid |
| 15 | { "allOf": [{"type": "integer"}, {"type": "number"}] } | 3 | valid |
| 16 | { "allOf": [{"type": "integer"}, {"type": "number"}] } | 3.3 | invalid |
| 17 | { "allOf":[{"type":"object","required":["petType"],"properties":{"petType":{"type":"string"}},"discriminator":{"propertyName":"petType"}},{"type":"object","properties":{"name":{"type":"string"}}}]} | { "petType": "cat", "name": "misty" } | valid |
213 changes: 213 additions & 0 deletions Solutions/Menes.Specs/Features/OpenApiValidation.feature.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 0 additions & 11 deletions Solutions/Menes.Specs/Menes.Specs.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,6 @@
<RootNamespace>Menes.Specs</RootNamespace>
</PropertyGroup>

<ItemGroup>
<SpecFlowObsoleteCodeBehindFiles Remove="Features\AccessControlPolicy.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\ExemptOperationIdsAccessPolicy.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\HalDocument{T}.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\OpenApiAccessCheckerExtensions.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\OpenApiMisconfigurationDetection.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\OpenApiOperationInvoker.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\OpenApiWebLinkResolver.feature.cs" />
<SpecFlowObsoleteCodeBehindFiles Remove="Features\ShortCircuitingAccessControlPolicyAdapter.feature.cs" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Corvus.SpecFlow.Extensions" Version="0.7.0-preview.3" />
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">
Expand Down
Loading

0 comments on commit 3a8304f

Please sign in to comment.