Skip to content

Commit a9e91fe

Browse files
authored
Merge pull request #951 from microsoft/mk/fix-parent-schema-discriminator-issue
Fix parent schema discriminator validation
2 parents c43f024 + 1b55a1d commit a9e91fe

File tree

3 files changed

+119
-5
lines changed

3 files changed

+119
-5
lines changed

src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33

4-
using Microsoft.OpenApi.Any;
54
using Microsoft.OpenApi.Models;
65
using Microsoft.OpenApi.Properties;
76
using System.Collections.Generic;
@@ -11,7 +10,6 @@ namespace Microsoft.OpenApi.Validations.Rules
1110
/// <summary>
1211
/// The validation rules for <see cref="OpenApiSchema"/>.
1312
/// </summary>
14-
1513
[OpenApiRule]
1614
public static class OpenApiSchemaRules
1715
{
@@ -70,15 +68,74 @@ public static class OpenApiSchemaRules
7068

7169
if (schema.Reference != null && schema.Discriminator != null)
7270
{
73-
if (!schema.Required.Contains(schema.Discriminator?.PropertyName))
71+
var discriminatorName = schema.Discriminator?.PropertyName;
72+
73+
if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminatorName))
7474
{
7575
context.CreateError(nameof(ValidateSchemaDiscriminator),
76-
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
77-
schema.Reference.Id, schema.Discriminator.PropertyName));
76+
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
77+
schema.Reference.Id, discriminatorName));
7878
}
7979
}
8080

8181
context.Exit();
8282
});
83+
84+
/// <summary>
85+
/// Validates the property name in the discriminator against the ones present in the children schema
86+
/// </summary>
87+
/// <param name="schema">The parent schema.</param>
88+
/// <param name="discriminatorName">Adds support for polymorphism. The discriminator is an object name that is used to differentiate
89+
/// between other schemas which may satisfy the payload description.</param>
90+
public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminatorName)
91+
{
92+
if (!schema.Required?.Contains(discriminatorName) ?? false)
93+
{
94+
// recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator
95+
if (schema.OneOf.Count != 0)
96+
{
97+
return TraverseSchemaElements(discriminatorName, schema.OneOf);
98+
}
99+
if (schema.AnyOf.Count != 0)
100+
{
101+
return TraverseSchemaElements(discriminatorName, schema.AnyOf);
102+
}
103+
if (schema.AllOf.Count != 0)
104+
{
105+
return TraverseSchemaElements(discriminatorName, schema.AllOf);
106+
}
107+
}
108+
else
109+
{
110+
return true;
111+
}
112+
113+
return false;
114+
}
115+
116+
/// <summary>
117+
/// Traverses the schema elements and checks whether the schema contains the discriminator.
118+
/// </summary>
119+
/// <param name="discriminatorName">Adds support for polymorphism. The discriminator is an object name that is used to differentiate
120+
/// between other schemas which may satisfy the payload description.</param>
121+
/// <param name="childSchema">The child schema.</param>
122+
/// <returns></returns>
123+
public static bool TraverseSchemaElements(string discriminatorName, IList<OpenApiSchema> childSchema)
124+
{
125+
foreach (var childItem in childSchema)
126+
{
127+
if ((!childItem.Properties?.ContainsKey(discriminatorName) ?? false) &&
128+
(!childItem.Required?.Contains(discriminatorName) ?? false))
129+
{
130+
return ValidateChildSchemaAgainstDiscriminator(childItem, discriminatorName);
131+
}
132+
else
133+
{
134+
return true;
135+
}
136+
}
137+
138+
return false;
139+
}
83140
}
84141
}

test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,8 @@ namespace Microsoft.OpenApi.Validations.Rules
13261326
{
13271327
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiSchema> SchemaMismatchedDataType { get; }
13281328
public static Microsoft.OpenApi.Validations.ValidationRule<Microsoft.OpenApi.Models.OpenApiSchema> ValidateSchemaDiscriminator { get; }
1329+
public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList<Microsoft.OpenApi.Models.OpenApiSchema> childSchema) { }
1330+
public static bool ValidateChildSchemaAgainstDiscriminator(Microsoft.OpenApi.Models.OpenApiSchema schema, string discriminatorName) { }
13291331
}
13301332
[Microsoft.OpenApi.Validations.Rules.OpenApiRule]
13311333
public static class OpenApiServerRules

test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,5 +268,60 @@ public void ValidateSchemaRequiredFieldListMustContainThePropertySpecifiedInTheD
268268
"schema1", "property1"))
269269
});
270270
}
271+
272+
[Fact]
273+
public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminator()
274+
{
275+
// Arrange
276+
var components = new OpenApiComponents
277+
{
278+
Schemas =
279+
{
280+
{
281+
"Person",
282+
new OpenApiSchema
283+
{
284+
Type = "array",
285+
Discriminator = new OpenApiDiscriminator
286+
{
287+
PropertyName = "type"
288+
},
289+
OneOf = new List<OpenApiSchema>
290+
{
291+
new OpenApiSchema
292+
{
293+
Properties =
294+
{
295+
{
296+
"type",
297+
new OpenApiSchema
298+
{
299+
Type = "array"
300+
}
301+
}
302+
},
303+
Reference = new OpenApiReference
304+
{
305+
Type = ReferenceType.Schema,
306+
Id = "Person"
307+
}
308+
}
309+
},
310+
Reference = new OpenApiReference { Id = "Person" }
311+
}
312+
}
313+
}
314+
};
315+
316+
// Act
317+
var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
318+
var walker = new OpenApiWalker(validator);
319+
walker.Walk(components);
320+
321+
var errors = validator.Errors;
322+
323+
//Assert
324+
errors.Should().BeEmpty();
325+
}
271326
}
272327
}

0 commit comments

Comments
 (0)