/// <summary> /// Reads the stream input and parses it into an Open API document. /// </summary> /// <param name="input">Stream containing OpenAPI description to parse.</param> /// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param> /// <returns>Instance of newly created OpenApiDocument</returns> public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { ParsingContext context; YamlDocument yamlDocument; diagnostic = new OpenApiDiagnostic(); // Parse the YAML/JSON try { yamlDocument = LoadYamlDocument(input); } catch (SyntaxErrorException ex) { diagnostic.Errors.Add(new OpenApiReaderError(ex)); return(new OpenApiDocument()); } context = new ParsingContext { ExtensionParsers = _settings.ExtensionParsers }; OpenApiDocument document = null; try { // Parse the OpenAPI Document document = context.Parse(yamlDocument, diagnostic); // Resolve References if requested switch (_settings.ReferenceResolution) { case ReferenceResolutionSetting.ResolveAllReferences: throw new ArgumentException(Properties.SRResource.CannotResolveRemoteReferencesSynchronously); case ReferenceResolutionSetting.ResolveLocalReferences: var resolver = new OpenApiReferenceResolver(document); var walker = new OpenApiWalker(resolver); walker.Walk(document); foreach (var item in resolver.Errors) { diagnostic.Errors.Add(item); } break; case ReferenceResolutionSetting.DoNotResolveReferences: break; } } catch (OpenApiException ex) { diagnostic.Errors.Add(new OpenApiError(ex)); } // Validate the document var errors = document.Validate(_settings.RuleSet); foreach (var item in errors) { diagnostic.Errors.Add(item); } return(document); }
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_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")), }); }
public void ValidateExamplesShouldNotHaveDataTypeMismatchForSimpleSchema() { // Arrange IEnumerable <OpenApiError> errors; var parameter = new OpenApiParameter() { Name = "parameter1", In = ParameterLocation.Path, Required = true, Schema = new OpenApiSchema() { Type = "object", AdditionalProperties = new OpenApiSchema() { Type = "integer", } }, Examples = { ["example0"] = new OpenApiExample() { Value = new OpenApiString("1"), }, ["example1"] = new OpenApiExample() { Value = new OpenApiObject() { ["x"] = new OpenApiInteger(2), ["y"] = new OpenApiString("20"), ["z"] = new OpenApiString("200") } }, ["example2"] = new OpenApiExample() { Value = new OpenApiArray() { new OpenApiInteger(3) } }, ["example3"] = new OpenApiExample() { Value = new OpenApiObject() { ["x"] = new OpenApiInteger(4), ["y"] = new OpenApiInteger(40), } }, } }; // Act var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); var walker = new OpenApiWalker(validator); walker.Walk(parameter); errors = validator.Errors; bool result = !errors.Any(); // Assert result.Should().BeFalse(); errors.Select(e => e.Message).Should().BeEquivalentTo(new[] { RuleHelpers.DataTypeMismatchedErrorMessage, RuleHelpers.DataTypeMismatchedErrorMessage, RuleHelpers.DataTypeMismatchedErrorMessage, }); errors.Select(e => e.Pointer).Should().BeEquivalentTo(new[] { // #enum/0 is not an error since the spec allows // representing an object using a string. "#/examples/example1/value/y", "#/examples/example1/value/z", "#/examples/example2/value" }); }
public void ValidateDefaultShouldNotHaveDataTypeMismatchForComplexSchema() { // Arrange IEnumerable <OpenApiError> errors; var schema = new OpenApiSchema() { Type = "object", Properties = { ["property1"] = new OpenApiSchema() { Type = "array", Items = new OpenApiSchema() { Type = "integer", Format = "int64" } }, ["property2"] = new OpenApiSchema() { Type = "array", Items = new OpenApiSchema() { Type = "object", AdditionalProperties = new OpenApiSchema() { Type = "boolean" } } }, ["property3"] = new OpenApiSchema() { Type = "string", Format = "password" }, ["property4"] = new OpenApiSchema() { Type = "string" } }, Default = new OpenApiObject() { ["property1"] = new OpenApiArray() { new OpenApiInteger(12), new OpenApiLong(13), new OpenApiString("1"), }, ["property2"] = new OpenApiArray() { new OpenApiInteger(2), new OpenApiObject() { ["x"] = new OpenApiBoolean(true), ["y"] = new OpenApiBoolean(false), ["z"] = new OpenApiString("1234"), } }, ["property3"] = new OpenApiPassword("123"), ["property4"] = new OpenApiDateTime(DateTime.UtcNow) } }; // 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.Select(e => e.Message).Should().BeEquivalentTo(new[] { RuleHelpers.DataTypeMismatchedErrorMessage, RuleHelpers.DataTypeMismatchedErrorMessage, RuleHelpers.DataTypeMismatchedErrorMessage, RuleHelpers.DataTypeMismatchedErrorMessage, RuleHelpers.DataTypeMismatchedErrorMessage, }); errors.Select(e => e.Pointer).Should().BeEquivalentTo(new[] { "#/default/property1/0", "#/default/property1/2", "#/default/property2/0", "#/default/property2/1/z", "#/default/property4", }); }
public void LocateReferences() { var baseSchema = new OpenApiSchema() { Reference = new OpenApiReference() { Id = "base", Type = ReferenceType.Schema }, UnresolvedReference = false }; var derivedSchema = new OpenApiSchema { AnyOf = new List <OpenApiSchema>() { baseSchema }, Reference = new OpenApiReference() { Id = "derived", Type = ReferenceType.Schema }, UnresolvedReference = false }; var testHeader = new OpenApiHeader() { Schema = derivedSchema, Reference = new OpenApiReference() { Id = "test-header", Type = ReferenceType.Header }, UnresolvedReference = false }; var doc = new OpenApiDocument { Paths = new OpenApiPaths() { ["/"] = new OpenApiPathItem() { Operations = new Dictionary <OperationType, OpenApiOperation>() { [OperationType.Get] = new OpenApiOperation() { Responses = new OpenApiResponses() { ["200"] = new OpenApiResponse() { Content = new Dictionary <string, OpenApiMediaType>() { ["application/json"] = new OpenApiMediaType() { Schema = derivedSchema } }, Headers = new Dictionary <string, OpenApiHeader>() { ["test-header"] = testHeader } } } } } } }, Components = new OpenApiComponents() { Schemas = new Dictionary <string, OpenApiSchema>() { ["derived"] = derivedSchema, ["base"] = baseSchema, }, Headers = new Dictionary <string, OpenApiHeader>() { ["test-header"] = testHeader } } }; var locator = new LocatorVisitor(); var walker = new OpenApiWalker(locator); walker.Walk(doc); locator.Locations.Where(l => l.StartsWith("referenceAt:")).Should().BeEquivalentTo(new List <string> { "referenceAt: #/paths/~1/get/responses/200/content/application~1json/schema", "referenceAt: #/paths/~1/get/responses/200/headers/test-header", "referenceAt: #/components/schemas/derived/anyOf/0", "referenceAt: #/components/headers/test-header/schema" }); }