Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix#344 report error when one of schema doesnt have the discriminator property #346

19 changes: 19 additions & 0 deletions src/Microsoft.OpenApi/Properties/SRResource.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/Microsoft.OpenApi/Properties/SRResource.resx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,12 @@
<data name="Validation_ComponentsKeyMustMatchRegularExpr" xml:space="preserve">
<value>The key '{0}' in '{1}' of components MUST match the regular expression '{2}'.</value>
</data>
<data name="Validation_CompositeSchemaMustContainPropertySpecifiedInTheDiscriminator" xml:space="preserve">
<value>Composite Schema {0} must contain property specified in the discriminator {1}.</value>
</data>
<data name="Validation_CompositeSchemaRequiedFieldMustContainThePropertySpecifiedInTheDiscriminator" xml:space="preserve">
<value>Composite schema {0} must contain property specified in the discriminator {1} in the required field list.</value>
</data>
<data name="Validation_ExtensionNameMustBeginWithXDash" xml:space="preserve">
<value>The extension name '{0}' in '{1}' object MUST begin with 'x-'.</value>
</data>
Expand Down
64 changes: 64 additions & 0 deletions src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -55,6 +57,68 @@ public static class OpenApiSchemaRules
context.Exit();
});

/// <summary>
/// Validates OneOf Discriminator
/// </summary>
public static ValidationRule<OpenApiSchema> ValidateOneOfDiscriminator =>
new ValidationRule<OpenApiSchema>(
(context, schema) =>
{
// oneOf
context.Enter("oneOf");

if (schema.OneOf != null && schema.Discriminator != null)
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
{
ValidateSchemaListDiscriminator(context, nameof(ValidateOneOfDiscriminator),
schema.OneOf, schema.Discriminator);
}

context.Exit();
});

// <summary>
/// Validates AnyOf Discriminator
/// </summary>
public static ValidationRule<OpenApiSchema> ValidateAnyOfDiscriminator =>
new ValidationRule<OpenApiSchema>(
(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.


/// <summary>
/// Checks if the schemas in the list contain a property with the property name specified by the discriminator.
/// </summary>
private static void ValidateSchemaListDiscriminator(IValidationContext context, string ruleName,
IList<OpenApiSchema> 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_CompositeSchemaRequiedFieldMustContainThePropertySpecifiedInTheDiscriminator,
schema.Reference.Id, discriminator.PropertyName));
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.

using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Properties;
using Microsoft.OpenApi.Readers.ParseNodes;
using Microsoft.OpenApi.Readers.V3;
using Microsoft.OpenApi.Validations;
using Microsoft.OpenApi.Validations.Rules;
senthilkumarmohan marked this conversation as resolved.
Show resolved Hide resolved
using SharpYaml.Serialization;
using Xunit;

Expand Down Expand Up @@ -47,5 +51,6 @@ public void ParseBasicDiscriminatorShouldSucceed()
});
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -231,5 +232,125 @@ public void ValidateDefaultShouldNotHaveDataTypeMismatchForComplexSchema()
"#/default/property4",
});
}

[Fact]
public void ValidateOneOfCompositeSchemaMustContainPropertySpecifiedInTheDiscriminator()
{
IEnumerable<OpenApiError> errors;
var schema = new OpenApiSchema
{
Type = "object",
OneOf = new List<OpenApiSchema>
{
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<OpenApiValidatorError>
{
new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateOneOfDiscriminator),"#/oneOf",
string.Format(SRResource.Validation_CompositeSchemaRequiedFieldMustContainThePropertySpecifiedInTheDiscriminator,
"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_CompositeSchemaRequiedFieldMustContainThePropertySpecifiedInTheDiscriminator,
"schema2", "property2")),

});
}

[Fact]
public void ValidateAnyOfCompositeSchemaMustContainPropertySpecifiedInTheDiscriminator()
{
IEnumerable<OpenApiError> errors;
var schema = new OpenApiSchema
{
Type = "object",
AnyOf = new List<OpenApiSchema>
{
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<OpenApiValidatorError>
{
new OpenApiValidatorError(nameof(OpenApiSchemaRules.ValidateAnyOfDiscriminator),"#/anyOf",
string.Format(SRResource.Validation_CompositeSchemaRequiedFieldMustContainThePropertySpecifiedInTheDiscriminator,
"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_CompositeSchemaRequiedFieldMustContainThePropertySpecifiedInTheDiscriminator,
"schema2", "property2")),

});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}