Skip to content

Commit 5fd6039

Browse files
committed
Update the validation process to be recursive
1 parent 6af10c2 commit 5fd6039

File tree

1 file changed

+56
-31
lines changed

1 file changed

+56
-31
lines changed

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

Lines changed: 56 additions & 31 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,50 +68,77 @@ public static class OpenApiSchemaRules
7068

7169
if (schema.Reference != null && schema.Discriminator != null)
7270
{
73-
if (!schema.Required.Contains(schema.Discriminator?.PropertyName))
71+
var discriminator = schema.Discriminator?.PropertyName;
72+
var schemaReferenceId = schema.Reference.Id;
73+
74+
if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminator, schemaReferenceId, context))
7475
{
75-
// check schema.OneOf, schema.AnyOf or schema.AllOf
76-
if(schema.OneOf.Count != 0)
77-
{
78-
ValidateDiscriminatorAgainstChildSchema(schema.OneOf, schema, context);
79-
}
80-
else if (schema.AnyOf.Count != 0)
81-
{
82-
ValidateDiscriminatorAgainstChildSchema(schema.AnyOf, schema, context);
83-
}
84-
else if (schema.AllOf.Count != 0)
85-
{
86-
ValidateDiscriminatorAgainstChildSchema(schema.AllOf, schema, context);
87-
}
88-
else
89-
{
90-
context.CreateError(nameof(ValidateSchemaDiscriminator),
91-
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
92-
schema.Reference.Id, schema.Discriminator.PropertyName));
93-
}
76+
context.CreateError(nameof(ValidateSchemaDiscriminator),
77+
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
78+
schemaReferenceId, discriminator));
9479
}
9580
}
96-
81+
9782
context.Exit();
9883
});
9984

10085
/// <summary>
10186
/// Validates the property name in the discriminator against the ones present in the children schema
10287
/// </summary>
103-
/// <param name="childSchema">The derived schema.</param>
10488
/// <param name="schema">The parent schema.</param>
89+
/// <param name="discriminator">Adds support for polymorphism. The discriminator is an object name that is used to differentiate
90+
/// between other schemas which may satisfy the payload description.</param>
91+
/// <param name="schemaReferenceId"></param>
10592
/// <param name="context">A validation context.</param>
106-
public static void ValidateDiscriminatorAgainstChildSchema(IList<OpenApiSchema> childSchema, OpenApiSchema schema, IValidationContext context)
93+
public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminator, string schemaReferenceId, IValidationContext context)
10794
{
108-
foreach (var schemaItem in childSchema)
95+
bool containsDiscriminator = false;
96+
97+
if (!schema.Required.Contains(discriminator))
10998
{
110-
if (!schemaItem.Properties.Keys.Contains(schema.Discriminator?.PropertyName))
99+
// recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator
100+
if (schema.OneOf.Count != 0)
101+
{
102+
return TraverseSchemaElements(discriminator, schema.OneOf, schemaReferenceId, context, containsDiscriminator);
103+
}
104+
if (schema.AnyOf.Count != 0)
111105
{
112-
context.CreateError(nameof(ValidateSchemaDiscriminator),
113-
string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
114-
schema.Reference.Id, schema.Discriminator.PropertyName));
106+
return TraverseSchemaElements(discriminator, schema.AnyOf, schemaReferenceId, context, containsDiscriminator);
115107
}
116-
}
108+
if (schema.AllOf.Count != 0)
109+
{
110+
return TraverseSchemaElements(discriminator, schema.AllOf, schemaReferenceId, context, containsDiscriminator);
111+
}
112+
}
113+
114+
return containsDiscriminator;
115+
}
116+
117+
/// <summary>
118+
/// Traverses the schema elements and checks whether the schema contains the discriminator.
119+
/// </summary>
120+
/// <param name="discriminator">Adds support for polymorphism. The discriminator is an object name that is used to differentiate
121+
/// between other schemas which may satisfy the payload description.</param>
122+
/// <param name="childSchema">The child schema.</param>
123+
/// <param name="schemaReferenceId"> The schema reference Id.</param>
124+
/// <param name="context"> A validation context.</param>
125+
/// <param name="containsDiscriminator">Tracks whether the discriminator is present.</param>
126+
/// <returns></returns>
127+
public static bool TraverseSchemaElements(string discriminator, IList<OpenApiSchema> childSchema, string schemaReferenceId, IValidationContext context, bool containsDiscriminator)
128+
{
129+
foreach (var childItem in childSchema)
130+
{
131+
if (!childItem.Properties.ContainsKey(discriminator) && !childItem.Required.Contains(discriminator))
132+
{
133+
return ValidateChildSchemaAgainstDiscriminator(childItem, discriminator, schemaReferenceId, context);
134+
}
135+
else
136+
{
137+
return containsDiscriminator = true;
138+
}
139+
}
140+
141+
return containsDiscriminator;
117142
}
118143
}
119144
}

0 commit comments

Comments
 (0)