diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index 1639e6248..a8ed2e93c 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -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; @@ -11,7 +10,6 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// - [OpenApiRule] public static class OpenApiSchemaRules { @@ -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(); }); + + /// + /// Validates the property name in the discriminator against the ones present in the children schema + /// + /// The parent schema. + /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate + /// between other schemas which may satisfy the payload description. + 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; + } + + /// + /// Traverses the schema elements and checks whether the schema contains the discriminator. + /// + /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate + /// between other schemas which may satisfy the payload description. + /// The child schema. + /// + public static bool TraverseSchemaElements(string discriminatorName, IList 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; + } } } diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index e9d78acb2..c8930e9fb 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1283,6 +1283,8 @@ namespace Microsoft.OpenApi.Validations.Rules { public static Microsoft.OpenApi.Validations.ValidationRule SchemaMismatchedDataType { get; } public static Microsoft.OpenApi.Validations.ValidationRule ValidateSchemaDiscriminator { get; } + public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList childSchema) { } + public static bool ValidateChildSchemaAgainstDiscriminator(Microsoft.OpenApi.Models.OpenApiSchema schema, string discriminatorName) { } } [Microsoft.OpenApi.Validations.Rules.OpenApiRule] public static class OpenApiServerRules diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs index d239e15a1..04acf7737 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs @@ -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 + { + 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(); + } } }