diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index 70d558a13..4f2a122a9 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -5,6 +5,8 @@ namespace Microsoft.OpenApi { + using System; + using System.ComponentModel; using System.Linq; /// @@ -48,6 +50,7 @@ public static class OpenApiSchemaRules { var discriminatorName = schema.Discriminator?.PropertyName; +#pragma warning disable CS0618 // Type or member is obsolete if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminatorName)) { context.Enter("discriminator"); @@ -56,6 +59,7 @@ public static class OpenApiSchemaRules schema is OpenApiSchemaReference { Reference: not null} schemaReference ? schemaReference.Reference.Id : string.Empty, discriminatorName)); context.Exit(); } +#pragma warning restore CS0618 // Type or member is obsolete } }); @@ -65,6 +69,8 @@ public static class OpenApiSchemaRules /// 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. + [Obsolete("This method will be made private in future versions.")] + [Browsable(false)] public static bool ValidateChildSchemaAgainstDiscriminator(IOpenApiSchema schema, string? discriminatorName) { if (discriminatorName is not null) @@ -72,15 +78,15 @@ public static bool ValidateChildSchemaAgainstDiscriminator(IOpenApiSchema schema if (schema.Required is null || !schema.Required.Contains(discriminatorName)) { // recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator - if (schema.OneOf?.Count != 0) + if (schema.OneOf is { Count: > 0}) { return TraverseSchemaElements(discriminatorName, schema.OneOf); } - if (schema.AnyOf?.Count != 0) + if (schema.AnyOf is { Count: > 0}) { return TraverseSchemaElements(discriminatorName, schema.AnyOf); } - if (schema.AllOf?.Count != 0) + if (schema.AllOf is { Count: > 0}) { return TraverseSchemaElements(discriminatorName, schema.AllOf); } @@ -102,25 +108,26 @@ public static bool ValidateChildSchemaAgainstDiscriminator(IOpenApiSchema schema /// between other schemas which may satisfy the payload description. /// The child schema. /// + [Obsolete("This method will be made private in future versions.")] + [Browsable(false)] public static bool TraverseSchemaElements(string discriminatorName, IList? childSchema) { - if (childSchema is not null) + if (childSchema is null) { - foreach (var childItem in childSchema) + return false; + } + foreach (var childItem in childSchema) + { + if ((!childItem.Properties?.ContainsKey(discriminatorName) ?? false) && + (!childItem.Required?.Contains(discriminatorName) ?? false)) { - if ((!childItem.Properties?.ContainsKey(discriminatorName) ?? false) && - (!childItem.Required?.Contains(discriminatorName) ?? false)) - { - return ValidateChildSchemaAgainstDiscriminator(childItem, discriminatorName); - } - else - { - return true; - } + return ValidateChildSchemaAgainstDiscriminator(childItem, discriminatorName); } - return false; - } - + 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 39cfb8ccc..d0b8989ef 100644 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1261,7 +1261,11 @@ namespace Microsoft.OpenApi { public static Microsoft.OpenApi.ValidationRule ValidateSchemaDiscriminator { get; } public static Microsoft.OpenApi.ValidationRule ValidateSchemaPropertyHasValue { get; } + [System.ComponentModel.Browsable(false)] + [System.Obsolete("This method will be made private in future versions.")] public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList? childSchema) { } + [System.ComponentModel.Browsable(false)] + [System.Obsolete("This method will be made private in future versions.")] public static bool ValidateChildSchemaAgainstDiscriminator(Microsoft.OpenApi.IOpenApiSchema schema, string? discriminatorName) { } } public class OpenApiSecurityRequirement : System.Collections.Generic.Dictionary>, Microsoft.OpenApi.IOpenApiElement, Microsoft.OpenApi.IOpenApiSerializable diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs index f22806825..fdf36e0b4 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs @@ -246,7 +246,7 @@ public void ValidateSchemaRequiredFieldListMustContainThePropertySpecifiedInTheD } [Fact] - public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminator() + public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminatorOneOf() { // Arrange var components = new OpenApiComponents @@ -293,5 +293,102 @@ public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscrim //Assert Assert.Empty(errors); } + + [Fact] + public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminatorAnyOf() + { + // Arrange + var components = new OpenApiComponents + { + Schemas = new Dictionary + { + { + "Person", + new OpenApiSchema + { + Type = JsonSchemaType.Array, + Discriminator = new() + { + PropertyName = "type" + }, + AnyOf = + [ + new OpenApiSchema() + { + Properties = new Dictionary + { + { + "type", + new OpenApiSchema + { + Type = JsonSchemaType.Array + } + } + }, + } + ], + } + } + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + var walker = new OpenApiWalker(validator); + walker.Walk(components); + + var errors = validator.Errors; + + //Assert + Assert.Empty(errors); + } + [Fact] + public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminatorAllOf() + { + // Arrange + var components = new OpenApiComponents + { + Schemas = new Dictionary + { + { + "Person", + new OpenApiSchema + { + Type = JsonSchemaType.Array, + Discriminator = new() + { + PropertyName = "type" + }, + AllOf = + [ + new OpenApiSchema() + { + Properties = new Dictionary + { + { + "type", + new OpenApiSchema + { + Type = JsonSchemaType.Array + } + } + }, + } + ], + } + } + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + var walker = new OpenApiWalker(validator); + walker.Walk(components); + + var errors = validator.Errors; + + //Assert + Assert.Empty(errors); + } } }