Skip to content
Merged
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
67 changes: 62 additions & 5 deletions src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Properties;
using System.Collections.Generic;
Expand All @@ -11,7 +10,6 @@ namespace Microsoft.OpenApi.Validations.Rules
/// <summary>
/// The validation rules for <see cref="OpenApiSchema"/>.
/// </summary>

[OpenApiRule]
public static class OpenApiSchemaRules
{
Expand Down Expand Up @@ -70,15 +68,74 @@ public static class OpenApiSchemaRules

if (schema.Reference != null && schema.Discriminator != null)
{
if (!schema.Required.Contains(schema.Discriminator?.PropertyName))
var discriminatorName = schema.Discriminator?.PropertyName;

if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminatorName))
{
context.CreateError(nameof(ValidateSchemaDiscriminator),
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
schema.Reference.Id, schema.Discriminator.PropertyName));
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
schema.Reference.Id, discriminatorName));
}
}

context.Exit();
});

/// <summary>
/// Validates the property name in the discriminator against the ones present in the children schema
/// </summary>
/// <param name="schema">The parent schema.</param>
/// <param name="discriminatorName">Adds support for polymorphism. The discriminator is an object name that is used to differentiate
/// between other schemas which may satisfy the payload description.</param>
public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminatorName)
{
if (!schema.Required?.Contains(discriminatorName) ?? false)
{
// recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator
if (schema.OneOf.Count != 0)
{
return TraverseSchemaElements(discriminatorName, schema.OneOf);
}
if (schema.AnyOf.Count != 0)
{
return TraverseSchemaElements(discriminatorName, schema.AnyOf);
}
if (schema.AllOf.Count != 0)
{
return TraverseSchemaElements(discriminatorName, schema.AllOf);
}
}
else
{
return true;
}

return false;
}

/// <summary>
/// Traverses the schema elements and checks whether the schema contains the discriminator.
/// </summary>
/// <param name="discriminatorName">Adds support for polymorphism. The discriminator is an object name that is used to differentiate
/// between other schemas which may satisfy the payload description.</param>
/// <param name="childSchema">The child schema.</param>
/// <returns></returns>
public static bool TraverseSchemaElements(string discriminatorName, IList<OpenApiSchema> childSchema)
{
foreach (var childItem in childSchema)
{
if ((!childItem.Properties?.ContainsKey(discriminatorName) ?? false) &&
(!childItem.Required?.Contains(discriminatorName) ?? false))
{
return ValidateChildSchemaAgainstDiscriminator(childItem, discriminatorName);
}
else
{
return true;
}
}

return false;
}
}
}
2 changes: 2 additions & 0 deletions test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1283,6 +1283,8 @@ namespace Microsoft.OpenApi.Validations.Rules
{
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiSchema> SchemaMismatchedDataType { get; }
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiSchema> ValidateSchemaDiscriminator { get; }
public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList<Microsoft.OpenApi.Models.OpenApiSchema> childSchema) { }
public static bool ValidateChildSchemaAgainstDiscriminator(Microsoft.OpenApi.Models.OpenApiSchema schema, string discriminatorName) { }
}
[Microsoft.OpenApi.Validations.Rules.OpenApiRule]
public static class OpenApiServerRules
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,5 +268,60 @@ public void ValidateSchemaRequiredFieldListMustContainThePropertySpecifiedInTheD
"schema1", "property1"))
});
}

[Fact]
public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminator()
{
// Arrange
var components = new OpenApiComponents
{
Schemas =
{
{
"Person",
new OpenApiSchema
{
Type = "array",
Discriminator = new OpenApiDiscriminator
{
PropertyName = "type"
},
OneOf = new List<OpenApiSchema>
{
new OpenApiSchema
{
Properties =
{
{
"type",
new OpenApiSchema
{
Type = "array"
}
}
},
Reference = new OpenApiReference
{
Type = ReferenceType.Schema,
Id = "Person"
}
}
},
Reference = new OpenApiReference { Id = "Person" }
}
}
}
};

// Act
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
var walker = new OpenApiWalker(validator);
walker.Walk(components);

var errors = validator.Errors;

//Assert
errors.Should().BeEmpty();
}
}
}