diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs
index 03e493776..add3f1c14 100644
--- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs
+++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs
@@ -303,6 +303,24 @@ internal class SRResource {
}
}
+ ///
+ /// Looks up a localized string similar to Composite Schema {0} must contain property specified in the discriminator {1}..
+ ///
+ internal static string Validation_CompositeSchemaMustContainPropertySpecifiedInTheDiscriminator {
+ get {
+ return ResourceManager.GetString("Validation_CompositeSchemaMustContainPropertySpecifiedInTheDiscriminator", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Composite schema {0} must contain property specified in the discriminator {1} in the required field list..
+ ///
+ internal static string Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator {
+ get {
+ return ResourceManager.GetString("Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to The extension name '{0}' in '{1}' object MUST begin with 'x-'..
///
diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx
index 6d9ffc112..ffff42905 100644
--- a/src/Microsoft.OpenApi/Properties/SRResource.resx
+++ b/src/Microsoft.OpenApi/Properties/SRResource.resx
@@ -198,6 +198,12 @@
The key '{0}' in '{1}' of components MUST match the regular expression '{2}'.
+
+ Composite Schema {0} must contain property specified in the discriminator {1}.
+
+
+ Composite schema {0} must contain property specified in the discriminator {1} in the required field list.
+
The extension name '{0}' in '{1}' object MUST begin with 'x-'.
diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
index 86336bbc5..6fafa53b1 100644
--- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
+++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
@@ -3,6 +3,8 @@
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Properties;
+using System.Collections.Generic;
namespace Microsoft.OpenApi.Validations.Rules
{
@@ -55,6 +57,68 @@ public static class OpenApiSchemaRules
context.Exit();
});
+ ///
+ /// Validates OneOf Discriminator
+ ///
+ public static ValidationRule ValidateOneOfDiscriminator =>
+ new ValidationRule(
+ (context, schema) =>
+ {
+ // oneOf
+ context.Enter("oneOf");
+
+ if (schema.OneOf != null && schema.Discriminator != null)
+ {
+ ValidateSchemaListDiscriminator(context, nameof(ValidateOneOfDiscriminator),
+ schema.OneOf, schema.Discriminator);
+ }
+
+ context.Exit();
+ });
+
+ //
+ /// Validates AnyOf Discriminator
+ ///
+ public static ValidationRule ValidateAnyOfDiscriminator =>
+ new ValidationRule(
+ (context, schema) =>
+ {
+ // oneOf
+ context.Enter("anyOf");
+
+ if (schema.AnyOf != null && schema.Discriminator != null)
+ {
+ ValidateSchemaListDiscriminator(context, nameof(ValidateAnyOfDiscriminator),
+ schema.AnyOf, schema.Discriminator);
+ }
+
+ context.Exit();
+ });
+
// add more rule.
+
+
+ ///
+ /// Checks if the schemas in the list contain a property with the property name specified by the discriminator.
+ ///
+ private static void ValidateSchemaListDiscriminator(IValidationContext context, string ruleName,
+ IList schemas, OpenApiDiscriminator discriminator)
+ {
+ foreach (var schema in schemas)
+ {
+ if (schema.Reference != null && !schema.Properties.ContainsKey(discriminator.PropertyName))
+ {
+ context.CreateError(ruleName,
+ string.Format(SRResource.Validation_CompositeSchemaMustContainPropertySpecifiedInTheDiscriminator,
+ schema.Reference.Id, discriminator.PropertyName));
+ }
+ if (schema.Reference != null && !schema.Required.Contains(discriminator.PropertyName))
+ {
+ context.CreateError(ruleName,
+ string.Format(SRResource.Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
+ schema.Reference.Id, discriminator.PropertyName));
+ }
+ }
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs
index 0fd0685d4..8168f2e86 100644
--- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs
@@ -7,6 +7,7 @@
using FluentAssertions;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Properties;
using Microsoft.OpenApi.Services;
using Microsoft.OpenApi.Validations.Rules;
using Xunit;
@@ -231,5 +232,125 @@ public void ValidateDefaultShouldNotHaveDataTypeMismatchForComplexSchema()
"#/default/property4",
});
}
+
+ [Fact]
+ public void ValidateOneOfCompositeSchemaMustContainPropertySpecifiedInTheDiscriminator()
+ {
+ IEnumerable errors;
+ var schema = new OpenApiSchema
+ {
+ Type = "object",
+ OneOf = new List
+ {
+ new OpenApiSchema
+ {
+ Type = "object",
+ Properties = {
+ ["property1"] = new OpenApiSchema() { Type = "integer", Format ="int64" },
+ ["property2"] = new OpenApiSchema() { Type = "string" }
+ },
+ Reference = new OpenApiReference{ Id = "schema1" }
+ },
+ new OpenApiSchema
+ {
+ Type = "object",
+ Properties = {
+ ["property1"] = new OpenApiSchema() { Type = "integer", Format ="int64" },
+ },
+ Reference = new OpenApiReference{ Id = "schema2" }
+ }
+ },
+ Discriminator = new OpenApiDiscriminator
+ {
+ PropertyName = "property2"
+ }
+ };
+
+ // Act
+ var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
+ var walker = new OpenApiWalker(validator);
+ walker.Walk(schema);
+
+ errors = validator.Errors;
+ bool result = !errors.Any();
+
+ // Assert
+ result.Should().BeFalse();
+ errors.ShouldAllBeEquivalentTo(new List
+ {
+ new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),"#/oneOf",
+ string.Format(SRResource.Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
+ "schema1", "property2")),
+
+ new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),"#/oneOf",
+ string.Format(SRResource.Validation_CompositeSchemaMustContainPropertySpecifiedInTheDiscriminator,
+ "schema2", "property2")),
+
+ new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),"#/oneOf",
+ string.Format(SRResource.Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
+ "schema2", "property2")),
+
+ });
+ }
+
+ [Fact]
+ public void ValidateAnyOfCompositeSchemaMustContainPropertySpecifiedInTheDiscriminator()
+ {
+ IEnumerable errors;
+ var schema = new OpenApiSchema
+ {
+ Type = "object",
+ AnyOf = new List
+ {
+ new OpenApiSchema
+ {
+ Type = "object",
+ Properties = {
+ ["property1"] = new OpenApiSchema() { Type = "integer", Format ="int64" },
+ ["property2"] = new OpenApiSchema() { Type = "string" }
+ },
+ Reference = new OpenApiReference{ Id = "schema1" }
+ },
+ new OpenApiSchema
+ {
+ Type = "object",
+ Properties = {
+ ["property1"] = new OpenApiSchema() { Type = "integer", Format ="int64" },
+ },
+ Reference = new OpenApiReference{ Id = "schema2" }
+ }
+ },
+ Discriminator = new OpenApiDiscriminator
+ {
+ PropertyName = "property2"
+ }
+ };
+
+ // Act
+ var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet());
+ var walker = new OpenApiWalker(validator);
+ walker.Walk(schema);
+
+ errors = validator.Errors;
+ bool result = !errors.Any();
+
+ // Assert
+ result.Should().BeFalse();
+ errors.ShouldAllBeEquivalentTo(new List
+ {
+ new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateAnyOfDiscriminator),"#/anyOf",
+ string.Format(SRResource.Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
+ "schema1", "property2")),
+
+ new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateAnyOfDiscriminator),"#/anyOf",
+ string.Format(SRResource.Validation_CompositeSchemaMustContainPropertySpecifiedInTheDiscriminator,
+ "schema2", "property2")),
+
+ new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateAnyOfDiscriminator),"#/anyOf",
+ string.Format(SRResource.Validation_CompositeSchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator,
+ "schema2", "property2")),
+
+ });
+ }
}
}
diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
index f300761b4..8153e6054 100644
--- a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
+++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs
@@ -43,7 +43,7 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules()
Assert.NotEmpty(rules);
// Update the number if you add new default rule(s).
- Assert.Equal(20, rules.Count);
+ Assert.Equal(22, rules.Count);
}
}
}