11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT license.
33
4- using Microsoft . OpenApi . Any ;
54using Microsoft . OpenApi . Models ;
65using Microsoft . OpenApi . Properties ;
76using 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