Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix#344 report error when one of schema doesnt have the discriminator property #346

18 changes: 18 additions & 0 deletions src/Microsoft.OpenApi/Properties/SRResource.Designer.cs

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

6 changes: 6 additions & 0 deletions src/Microsoft.OpenApi/Properties/SRResource.resx
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,12 @@
<data name="Validation_RuleAddTwice" xml:space="preserve">
<value>The same rule cannot be in the same rule set twice.</value>
</data>
<data name="Validation_SchemaDoesntContainDiscriminatorProperty" xml:space="preserve">
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
<value>Schema {0} doesn't contain discriminator property {1}.</value>
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="Validation_SchemaDoesntContainDiscriminatorPropertyInRequiredFieldList" xml:space="preserve">
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
<value>Schema {0} doesn't contain discriminator property {1} in the required field list.</value>
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
</data>
<data name="Validation_StringMustBeEmailAddress" xml:space="preserve">
<value>The string '{0}' MUST be in the format of an email address.</value>
</data>
Expand Down
68 changes: 68 additions & 0 deletions src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Properties;
using System.Collections.Generic;

namespace Microsoft.OpenApi.Validations.Rules
{
Expand Down Expand Up @@ -55,6 +57,72 @@ public static class OpenApiSchemaRules
context.Exit();
});

/// <summary>
/// Validates OneOf Discriminator
/// </summary>
public static ValidationRule<OpenApiSchema> ValidateOneOfDiscriminator =>
new ValidationRule<OpenApiSchema>(
(context, schema) =>
{
// oneOf
context.Enter("oneOf");

if (schema.OneOf != null && schema.Discriminator != null)
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
{
ValidateSchemaListDiscriminator(context, nameof(ValidateOneOfDiscriminator),
schema.OneOf, schema.Discriminator);
}

context.Exit();
});

// <summary>
/// Validates AnyOf Discriminator
/// </summary>
public static ValidationRule<OpenApiSchema> ValidateAnyOfDiscriminator =>
new ValidationRule<OpenApiSchema>(
(context, schema) =>
{
// oneOf
context.Enter("anyOf");

if (schema.AnyOf != null && schema.Discriminator != null)
{
ValidateSchemaListDiscriminator(context, nameof(ValidateAnyOfDiscriminator),
schema.AnyOf, schema.Discriminator);
}

context.Exit();
});

// add more rule.


/// <summary>
/// Checks if the schemas in the list contain a property with the property name specified by the discriminator.
/// </summary>
/// <param name="context"></param>
/// <param name="ruleName"></param>
/// <param name="schemas">OneOf/AnyOf schemas</param>
/// <param name="discriminator">discriminator</param>
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
private static void ValidateSchemaListDiscriminator(IValidationContext context, string ruleName,
IList<OpenApiSchema> schemas, OpenApiDiscriminator discriminator)
{
foreach (var schema in schemas)
{
if (!schema.Properties.ContainsKey(discriminator.PropertyName))
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
{
context.AddError(new OpenApiValidatorError(ruleName, context.PathString,
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorProperty,
schema.Reference.Id, discriminator.PropertyName)));
}
if (!schema.Required.Contains(discriminator.PropertyName))
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
{
context.AddError(new OpenApiValidatorError(ruleName, context.PathString,
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorPropertyInRequiredFieldList,
schema.Reference.Id, discriminator.PropertyName)));
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@
<EmbeddedResource Include="V3Tests\Samples\OpenApiDiscriminator\basicDiscriminator.yaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
<EmbeddedResource Include="V3Tests\Samples\OpenApiDiscriminator\invalidAnyOfDiscriminator.yaml" />
<EmbeddedResource Include="V3Tests\Samples\OpenApiDiscriminator\invalidOneOfDiscriminator.yaml" />
<EmbeddedResource Include="V3Tests\Samples\OpenApiDocument\basicDocumentWithMultipleServers.yaml">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Properties;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Readers.V3;
using Microsoft.OpenApi.Validations;
using Microsoft.OpenApi.Validations.Rules;
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
using SharpYaml.Serialization;
using Xunit;

Expand Down Expand Up @@ -47,5 +51,51 @@ public void ParseBasicDiscriminatorShouldSucceed()
});
}
}

[Fact]
public void OneOfSchemasWithoutDiscriminatorPropertyShouldYieldError()
{
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "invalidOneOfDiscriminator.yaml")))
{
var diagnostic = new OpenApiDiagnostic();
var reader = new OpenApiStreamReader();
var doc = reader.Read(stream, out diagnostic);
diagnostic.Errors.ShouldAllBeEquivalentTo(new List<OpenApiError>
{
new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),
"#/paths/~1pets~1{id}/get/responses/200/content/application~1json/schema/oneOf",
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorPropertyInRequiredFieldList, "pet1", "petType")),

new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),
"#/paths/~1pets~1{id}/get/responses/200/content/application~1json/schema/oneOf",
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorProperty, "pet2", "petType")),

new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),
"#/paths/~1pets~1{id}/get/responses/200/content/application~1json/schema/oneOf",
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorPropertyInRequiredFieldList, "pet2", "petType"))
});
}
}

[Fact]
public void AnyOfSchemasWithoutDiscriminatorPropertyShouldYieldError()
{
using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "invalidAnyOfDiscriminator.yaml")))
{
var diagnostic = new OpenApiDiagnostic();
var reader = new OpenApiStreamReader();
var doc = reader.Read(stream, out diagnostic);
diagnostic.Errors.ShouldAllBeEquivalentTo(new List<OpenApiError>
{
new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateAnyOfDiscriminator),
"#/paths/~1pets~1{id}/get/responses/200/content/application~1json/schema/anyOf",
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorProperty, "pet2", "petType")),

new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateAnyOfDiscriminator),
"#/paths/~1pets~1{id}/get/responses/200/content/application~1json/schema/anyOf",
string.Format(SRResource.Validation_SchemaDoesntContainDiscriminatorPropertyInRequiredFieldList, "pet2", "petType"))
});
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
openapi: '3.0.0'
info:
version: '1.0.0'
title: Petstore - Invalid 'OneOf' discriminator
servers:
- url: http://petstore.swagger.io/api
paths:
/pets/{id}:
get:
description: Returns a pet based on a single ID,
operationId: findPetById
parameters:
- name: id
in: path
description: ID of pet to fetch
required: true
schema:
type: integer
format: int64
responses:
'200':
description: pet response
content:
application/json:
schema:
anyOf:
- "$ref": '#/components/schemas/pet1'
- "$ref": '#/components/schemas/pet2'
discriminator:
propertyName: petType
components:
schemas:
pet1:
type: object
required:
- id
- name
- petType
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
petType:
type: string
pet2:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
openapi: '3.0.0'
info:
version: '1.0.0'
title: Petstore - Invalid 'OneOf' discriminator
servers:
- url: http://petstore.swagger.io/api
paths:
/pets/{id}:
get:
description: Returns a pet based on a single ID,
operationId: findPetById
parameters:
- name: id
in: path
description: ID of pet to fetch
required: true
schema:
type: integer
format: int64
responses:
'200':
description: pet response
content:
application/json:
schema:
oneOf:
- "$ref": '#/components/schemas/pet1'
- "$ref": '#/components/schemas/pet2'
discriminator:
propertyName: petType
components:
schemas:
pet1:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
petType:
type: string
pet2:
type: object
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules()
Assert.NotEmpty(rules);

// Update the number if you add new default rule(s).
Assert.Equal(20, rules.Count);
Assert.Equal(22, rules.Count);
}
}
}