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
41 changes: 24 additions & 17 deletions src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

namespace Microsoft.OpenApi
{
using System;
using System.ComponentModel;
using System.Linq;

/// <summary>
Expand Down Expand Up @@ -48,6 +50,7 @@
{
var discriminatorName = schema.Discriminator?.PropertyName;

#pragma warning disable CS0618 // Type or member is obsolete
if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminatorName))
{
context.Enter("discriminator");
Expand All @@ -56,6 +59,7 @@
schema is OpenApiSchemaReference { Reference: not null} schemaReference ? schemaReference.Reference.Id : string.Empty, discriminatorName));
context.Exit();
}
#pragma warning restore CS0618 // Type or member is obsolete
}
});

Expand All @@ -65,27 +69,29 @@
/// <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>
[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)
{
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);
}
}
else

Check notice

Code scanning / CodeQL

Missed ternary opportunity Note

Both branches of this 'if' statement return - consider using '?' to express intent better.
{
return true;
}
Expand All @@ -102,25 +108,26 @@
/// between other schemas which may satisfy the payload description.</param>
/// <param name="childSchema">The child schema.</param>
/// <returns></returns>
[Obsolete("This method will be made private in future versions.")]
[Browsable(false)]
public static bool TraverseSchemaElements(string discriminatorName, IList<IOpenApiSchema>? 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;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,11 @@ namespace Microsoft.OpenApi
{
public static Microsoft.OpenApi.ValidationRule<Microsoft.OpenApi.IOpenApiSchema> ValidateSchemaDiscriminator { get; }
public static Microsoft.OpenApi.ValidationRule<Microsoft.OpenApi.IOpenApiSchema> 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<Microsoft.OpenApi.IOpenApiSchema>? 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.OpenApiSecuritySchemeReference, System.Collections.Generic.List<string>>, Microsoft.OpenApi.IOpenApiElement, Microsoft.OpenApi.IOpenApiSerializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ public void ValidateSchemaRequiredFieldListMustContainThePropertySpecifiedInTheD
}

[Fact]
public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminator()
public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminatorOneOf()
{
// Arrange
var components = new OpenApiComponents
Expand Down Expand Up @@ -293,5 +293,102 @@ public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscrim
//Assert
Assert.Empty(errors);
}

[Fact]
public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminatorAnyOf()
{
// Arrange
var components = new OpenApiComponents
{
Schemas = new Dictionary<string, IOpenApiSchema>
{
{
"Person",
new OpenApiSchema
{
Type = JsonSchemaType.Array,
Discriminator = new()
{
PropertyName = "type"
},
AnyOf =
[
new OpenApiSchema()
{
Properties = new Dictionary<string, IOpenApiSchema>
{
{
"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<string, IOpenApiSchema>
{
{
"Person",
new OpenApiSchema
{
Type = JsonSchemaType.Array,
Discriminator = new()
{
PropertyName = "type"
},
AllOf =
[
new OpenApiSchema()
{
Properties = new Dictionary<string, IOpenApiSchema>
{
{
"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);
}
}
}
Loading