From 254b70f04e6daca40898fdc8046ba02c09a094b3 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 21 Jul 2022 17:14:08 +0300 Subject: [PATCH 01/44] Add logic to check whether the properties of anyOf/oneOf/allOf match the property name in the dicriminator --- .../Validations/Rules/OpenApiSchemaRules.cs | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index 1639e6248..aadaac4ec 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -72,13 +72,48 @@ public static class OpenApiSchemaRules { if (!schema.Required.Contains(schema.Discriminator?.PropertyName)) { - context.CreateError(nameof(ValidateSchemaDiscriminator), - string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, - schema.Reference.Id, schema.Discriminator.PropertyName)); + // check schema.OneOf, schema.AnyOf or schema.AllOf + if(schema.OneOf.Count != 0) + { + ValidateDiscriminatorAgainstChildSchema(schema.OneOf, schema, context); + } + else if (schema.AnyOf.Count != 0) + { + ValidateDiscriminatorAgainstChildSchema(schema.AnyOf, schema, context); + } + else if (schema.AllOf.Count != 0) + { + ValidateDiscriminatorAgainstChildSchema(schema.AllOf, schema, context); + } + else + { + context.CreateError(nameof(ValidateSchemaDiscriminator), + string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, + schema.Reference.Id, schema.Discriminator.PropertyName)); + } } - } + } context.Exit(); }); + + /// + /// Validates the property name in the discriminator against the ones present in the children schema + /// + /// The derived schema. + /// The parent schema. + /// A validation context. + public static void ValidateDiscriminatorAgainstChildSchema(IList childSchema, OpenApiSchema schema, IValidationContext context) + { + foreach (var schemaItem in childSchema) + { + if (!schemaItem.Properties.Keys.Contains(schema.Discriminator?.PropertyName)) + { + context.CreateError(nameof(ValidateSchemaDiscriminator), + string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, + schema.Reference.Id, schema.Discriminator.PropertyName)); + } + } + } } } From 96cfc45bbb7a7c66afbe6d30b672b636fbec4c09 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 21 Jul 2022 17:14:28 +0300 Subject: [PATCH 02/44] Add test case --- .../OpenApiSchemaValidationTests.cs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs index d239e15a1..04acf7737 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiSchemaValidationTests.cs @@ -268,5 +268,60 @@ public void ValidateSchemaRequiredFieldListMustContainThePropertySpecifiedInTheD "schema1", "property1")) }); } + + [Fact] + public void ValidateOneOfSchemaPropertyNameContainsPropertySpecifiedInTheDiscriminator() + { + // Arrange + var components = new OpenApiComponents + { + Schemas = + { + { + "Person", + new OpenApiSchema + { + Type = "array", + Discriminator = new OpenApiDiscriminator + { + PropertyName = "type" + }, + OneOf = new List + { + new OpenApiSchema + { + Properties = + { + { + "type", + new OpenApiSchema + { + Type = "array" + } + } + }, + Reference = new OpenApiReference + { + Type = ReferenceType.Schema, + Id = "Person" + } + } + }, + Reference = new OpenApiReference { Id = "Person" } + } + } + } + }; + + // Act + var validator = new OpenApiValidator(ValidationRuleSet.GetDefaultRuleSet()); + var walker = new OpenApiWalker(validator); + walker.Walk(components); + + var errors = validator.Errors; + + //Assert + errors.Should().BeEmpty(); + } } } From 2b34d2dad6c7fcdb657437a5c320a0c655cdcd9e Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 21 Jul 2022 17:22:09 +0300 Subject: [PATCH 03/44] Update public Api --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index e9d78acb2..dc42f5485 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1283,6 +1283,7 @@ namespace Microsoft.OpenApi.Validations.Rules { public static Microsoft.OpenApi.Validations.ValidationRule SchemaMismatchedDataType { get; } public static Microsoft.OpenApi.Validations.ValidationRule ValidateSchemaDiscriminator { get; } + public static void ValidateDiscriminatorAgainstChildSchema(System.Collections.Generic.IList childSchema, Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Validations.IValidationContext context) { } } [Microsoft.OpenApi.Validations.Rules.OpenApiRule] public static class OpenApiServerRules From 6af10c23e2c68f5f14c7c9d3dcb49400fd0cbb43 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 21 Jul 2022 17:27:13 +0300 Subject: [PATCH 04/44] Remove whitespace --- src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index aadaac4ec..0b72ebc52 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -92,8 +92,8 @@ public static class OpenApiSchemaRules schema.Reference.Id, schema.Discriminator.PropertyName)); } } - } - + } + context.Exit(); }); From 5fd60398c3347ec814f9f65b38fbc164055601fe Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Aug 2022 12:13:07 +0300 Subject: [PATCH 05/44] Update the validation process to be recursive --- .../Validations/Rules/OpenApiSchemaRules.cs | 87 ++++++++++++------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index 0b72ebc52..e6316588c 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; using System.Collections.Generic; @@ -11,7 +10,6 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// - [OpenApiRule] public static class OpenApiSchemaRules { @@ -70,50 +68,77 @@ public static class OpenApiSchemaRules if (schema.Reference != null && schema.Discriminator != null) { - if (!schema.Required.Contains(schema.Discriminator?.PropertyName)) + var discriminator = schema.Discriminator?.PropertyName; + var schemaReferenceId = schema.Reference.Id; + + if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminator, schemaReferenceId, context)) { - // check schema.OneOf, schema.AnyOf or schema.AllOf - if(schema.OneOf.Count != 0) - { - ValidateDiscriminatorAgainstChildSchema(schema.OneOf, schema, context); - } - else if (schema.AnyOf.Count != 0) - { - ValidateDiscriminatorAgainstChildSchema(schema.AnyOf, schema, context); - } - else if (schema.AllOf.Count != 0) - { - ValidateDiscriminatorAgainstChildSchema(schema.AllOf, schema, context); - } - else - { - context.CreateError(nameof(ValidateSchemaDiscriminator), - string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, - schema.Reference.Id, schema.Discriminator.PropertyName)); - } + context.CreateError(nameof(ValidateSchemaDiscriminator), + string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, + schemaReferenceId, discriminator)); } } - + context.Exit(); }); /// /// Validates the property name in the discriminator against the ones present in the children schema /// - /// The derived schema. /// The parent schema. + /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate + /// between other schemas which may satisfy the payload description. + /// /// A validation context. - public static void ValidateDiscriminatorAgainstChildSchema(IList childSchema, OpenApiSchema schema, IValidationContext context) + public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminator, string schemaReferenceId, IValidationContext context) { - foreach (var schemaItem in childSchema) + bool containsDiscriminator = false; + + if (!schema.Required.Contains(discriminator)) { - if (!schemaItem.Properties.Keys.Contains(schema.Discriminator?.PropertyName)) + // recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator + if (schema.OneOf.Count != 0) + { + return TraverseSchemaElements(discriminator, schema.OneOf, schemaReferenceId, context, containsDiscriminator); + } + if (schema.AnyOf.Count != 0) { - context.CreateError(nameof(ValidateSchemaDiscriminator), - string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, - schema.Reference.Id, schema.Discriminator.PropertyName)); + return TraverseSchemaElements(discriminator, schema.AnyOf, schemaReferenceId, context, containsDiscriminator); } - } + if (schema.AllOf.Count != 0) + { + return TraverseSchemaElements(discriminator, schema.AllOf, schemaReferenceId, context, containsDiscriminator); + } + } + + return containsDiscriminator; + } + + /// + /// Traverses the schema elements and checks whether the schema contains the discriminator. + /// + /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate + /// between other schemas which may satisfy the payload description. + /// The child schema. + /// The schema reference Id. + /// A validation context. + /// Tracks whether the discriminator is present. + /// + public static bool TraverseSchemaElements(string discriminator, IList childSchema, string schemaReferenceId, IValidationContext context, bool containsDiscriminator) + { + foreach (var childItem in childSchema) + { + if (!childItem.Properties.ContainsKey(discriminator) && !childItem.Required.Contains(discriminator)) + { + return ValidateChildSchemaAgainstDiscriminator(childItem, discriminator, schemaReferenceId, context); + } + else + { + return containsDiscriminator = true; + } + } + + return containsDiscriminator; } } } From 2707493e77b3dcac79e1531bd46fe550e2f36d9b Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Aug 2022 15:39:51 +0300 Subject: [PATCH 06/44] Code cleanup --- .../Validations/Rules/OpenApiSchemaRules.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index e6316588c..3b274232e 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -68,14 +68,13 @@ public static class OpenApiSchemaRules if (schema.Reference != null && schema.Discriminator != null) { - var discriminator = schema.Discriminator?.PropertyName; - var schemaReferenceId = schema.Reference.Id; + var discriminatorName = schema.Discriminator?.PropertyName; - if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminator, schemaReferenceId, context)) + if (!ValidateChildSchemaAgainstDiscriminator(schema, discriminatorName)) { context.CreateError(nameof(ValidateSchemaDiscriminator), string.Format(SRResource.Validation_SchemaRequiredFieldListMustContainThePropertySpecifiedInTheDiscriminator, - schemaReferenceId, discriminator)); + schema.Reference.Id, discriminatorName)); } } @@ -86,30 +85,32 @@ public static class OpenApiSchemaRules /// Validates the property name in the discriminator against the ones present in the children schema /// /// The parent schema. - /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate + /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate /// between other schemas which may satisfy the payload description. - /// - /// A validation context. - public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminator, string schemaReferenceId, IValidationContext context) + public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminatorName) { bool containsDiscriminator = false; - if (!schema.Required.Contains(discriminator)) + if (!schema.Required?.Contains(discriminatorName) ?? false) { // recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator if (schema.OneOf.Count != 0) { - return TraverseSchemaElements(discriminator, schema.OneOf, schemaReferenceId, context, containsDiscriminator); + return TraverseSchemaElements(discriminatorName, schema.OneOf, ref containsDiscriminator); } if (schema.AnyOf.Count != 0) { - return TraverseSchemaElements(discriminator, schema.AnyOf, schemaReferenceId, context, containsDiscriminator); + return TraverseSchemaElements(discriminatorName, schema.AnyOf, ref containsDiscriminator); } if (schema.AllOf.Count != 0) { - return TraverseSchemaElements(discriminator, schema.AllOf, schemaReferenceId, context, containsDiscriminator); + return TraverseSchemaElements(discriminatorName, schema.AllOf, ref containsDiscriminator); } } + else + { + return true; + } return containsDiscriminator; } @@ -117,20 +118,19 @@ public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, /// /// Traverses the schema elements and checks whether the schema contains the discriminator. /// - /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate + /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate /// between other schemas which may satisfy the payload description. /// The child schema. - /// The schema reference Id. - /// A validation context. /// Tracks whether the discriminator is present. /// - public static bool TraverseSchemaElements(string discriminator, IList childSchema, string schemaReferenceId, IValidationContext context, bool containsDiscriminator) + public static bool TraverseSchemaElements(string discriminatorName, IList childSchema, ref bool containsDiscriminator) { foreach (var childItem in childSchema) { - if (!childItem.Properties.ContainsKey(discriminator) && !childItem.Required.Contains(discriminator)) + if ((!childItem.Properties?.ContainsKey(discriminatorName) ?? false) && + (!childItem.Required?.Contains(discriminatorName) ?? false)) { - return ValidateChildSchemaAgainstDiscriminator(childItem, discriminator, schemaReferenceId, context); + return ValidateChildSchemaAgainstDiscriminator(childItem, discriminatorName); } else { From 13fa9e82cd09007f416f2ae117f58594bfca6864 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Tue, 2 Aug 2022 15:44:13 +0300 Subject: [PATCH 07/44] Update public API interface --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index dc42f5485..87300f76d 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1283,7 +1283,8 @@ namespace Microsoft.OpenApi.Validations.Rules { public static Microsoft.OpenApi.Validations.ValidationRule SchemaMismatchedDataType { get; } public static Microsoft.OpenApi.Validations.ValidationRule ValidateSchemaDiscriminator { get; } - public static void ValidateDiscriminatorAgainstChildSchema(System.Collections.Generic.IList childSchema, Microsoft.OpenApi.Models.OpenApiSchema schema, Microsoft.OpenApi.Validations.IValidationContext context) { } + public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList childSchema, ref bool containsDiscriminator) { } + public static bool ValidateChildSchemaAgainstDiscriminator(Microsoft.OpenApi.Models.OpenApiSchema schema, string discriminatorName) { } } [Microsoft.OpenApi.Validations.Rules.OpenApiRule] public static class OpenApiServerRules From 3682ceaea538e6026c9e81edb78fa52c6c88bc3e Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 4 Aug 2022 15:18:13 +0300 Subject: [PATCH 08/44] Clean up code --- .../Validations/Rules/OpenApiSchemaRules.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs index 3b274232e..a8ed2e93c 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiSchemaRules.cs @@ -89,22 +89,20 @@ public static class OpenApiSchemaRules /// between other schemas which may satisfy the payload description. public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, string discriminatorName) { - bool containsDiscriminator = false; - if (!schema.Required?.Contains(discriminatorName) ?? false) { // recursively check nested schema.OneOf, schema.AnyOf or schema.AllOf and their required fields for the discriminator if (schema.OneOf.Count != 0) { - return TraverseSchemaElements(discriminatorName, schema.OneOf, ref containsDiscriminator); + return TraverseSchemaElements(discriminatorName, schema.OneOf); } if (schema.AnyOf.Count != 0) { - return TraverseSchemaElements(discriminatorName, schema.AnyOf, ref containsDiscriminator); + return TraverseSchemaElements(discriminatorName, schema.AnyOf); } if (schema.AllOf.Count != 0) { - return TraverseSchemaElements(discriminatorName, schema.AllOf, ref containsDiscriminator); + return TraverseSchemaElements(discriminatorName, schema.AllOf); } } else @@ -112,7 +110,7 @@ public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, return true; } - return containsDiscriminator; + return false; } /// @@ -121,9 +119,8 @@ public static bool ValidateChildSchemaAgainstDiscriminator(OpenApiSchema schema, /// Adds support for polymorphism. The discriminator is an object name that is used to differentiate /// between other schemas which may satisfy the payload description. /// The child schema. - /// Tracks whether the discriminator is present. /// - public static bool TraverseSchemaElements(string discriminatorName, IList childSchema, ref bool containsDiscriminator) + public static bool TraverseSchemaElements(string discriminatorName, IList childSchema) { foreach (var childItem in childSchema) { @@ -134,11 +131,11 @@ public static bool TraverseSchemaElements(string discriminatorName, IList Date: Thu, 4 Aug 2022 15:36:40 +0300 Subject: [PATCH 09/44] Remove param --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 87300f76d..c8930e9fb 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1283,7 +1283,7 @@ namespace Microsoft.OpenApi.Validations.Rules { public static Microsoft.OpenApi.Validations.ValidationRule SchemaMismatchedDataType { get; } public static Microsoft.OpenApi.Validations.ValidationRule ValidateSchemaDiscriminator { get; } - public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList childSchema, ref bool containsDiscriminator) { } + public static bool TraverseSchemaElements(string discriminatorName, System.Collections.Generic.IList childSchema) { } public static bool ValidateChildSchemaAgainstDiscriminator(Microsoft.OpenApi.Models.OpenApiSchema schema, string discriminatorName) { } } [Microsoft.OpenApi.Validations.Rules.OpenApiRule] From 50610bef8a1e43ea6d67cab95bfd20318e05cb93 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 11 Aug 2022 09:17:40 +0300 Subject: [PATCH 10/44] Adds an issue template for a unified and concise bug report --- .github/ISSUE_TEMPLATE/bug_report.md | 30 ++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..a7e4e9837 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,30 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the current behavior: + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots/Code Snippets** +If applicable, add screenshots or code snippets to help explain your problem. + +**Your environment:** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + - Desktop or mobile + - If applicable, Link to your project + +**Additional context** +Add any other context about the problem here. From 9cbac86671e48f46d9ee2e109848f4b47daecb78 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 11 Aug 2022 09:24:17 +0300 Subject: [PATCH 11/44] Adds a feature template --- .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..021458556 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From aba7694d96acb84e65c9f15fc5446584ec50e8ec Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 11 Aug 2022 10:03:06 +0300 Subject: [PATCH 12/44] Edit bug template --- .github/ISSUE_TEMPLATE/bug_report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a7e4e9837..2c10124b0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -17,7 +17,7 @@ Steps to reproduce the current behavior: A clear and concise description of what you expected to happen. **Screenshots/Code Snippets** -If applicable, add screenshots or code snippets to help explain your problem. +If applicable, add screenshots of the stack trace or a code snippet to help explain your problem. **Your environment:** - OS: [e.g. iOS] @@ -25,6 +25,6 @@ If applicable, add screenshots or code snippets to help explain your problem. - Version [e.g. 22] - Desktop or mobile - If applicable, Link to your project - + **Additional context** Add any other context about the problem here. From c2a23a0561b873272230eaf4faaa0d1f050920fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 Aug 2022 22:15:59 +0000 Subject: [PATCH 13/44] Bump SharpYaml from 1.9.2 to 2.1.0 Bumps [SharpYaml](https://github.com/xoofx/SharpYaml) from 1.9.2 to 2.1.0. - [Release notes](https://github.com/xoofx/SharpYaml/releases) - [Changelog](https://github.com/xoofx/SharpYaml/blob/master/changelog.md) - [Commits](https://github.com/xoofx/SharpYaml/compare/1.9.2...2.1.0) --- updated-dependencies: - dependency-name: SharpYaml dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj | 2 +- .../Microsoft.OpenApi.Readers.Tests.csproj | 2 +- test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index 1ff5b99e9..94a5b7335 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -34,7 +34,7 @@ - + diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index 83e5482a7..4faadc3f6 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -255,7 +255,7 @@ - + diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 022b0e5dc..6cac42c5d 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -19,7 +19,7 @@ - + From ffc7c1b823c846c15fb04d417d870968200c777e Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Sun, 14 Aug 2022 01:21:01 +0300 Subject: [PATCH 14/44] Remove unnecessary info --- .github/ISSUE_TEMPLATE/bug_report.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2c10124b0..c5b4cab90 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -18,13 +18,7 @@ A clear and concise description of what you expected to happen. **Screenshots/Code Snippets** If applicable, add screenshots of the stack trace or a code snippet to help explain your problem. - -**Your environment:** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - - Desktop or mobile - - If applicable, Link to your project +If applicable, add a link to your project **Additional context** Add any other context about the problem here. From aa503ff00e8a75f12ecc8628162744887424245a Mon Sep 17 00:00:00 2001 From: Millicent Achieng Date: Sun, 14 Aug 2022 02:20:59 +0300 Subject: [PATCH 15/44] Fix anyOf and oneOf serialization by checking for null first before writing either as allOf --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index d43756887..e50c49d1c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -700,12 +700,12 @@ internal void WriteAsSchemaProperties( if (AllOf == null || AllOf.Count == 0) { // anyOf (Not Supported in V2) - Write the first schema only as an allOf. - writer.WriteOptionalCollection(OpenApiConstants.AllOf, AnyOf.Take(1), (w, s) => s.SerializeAsV2(w)); + writer.WriteOptionalCollection(OpenApiConstants.AllOf, AnyOf?.Take(1), (w, s) => s.SerializeAsV2(w)); if (AnyOf == null || AnyOf.Count == 0) { // oneOf (Not Supported in V2) - Write the first schema only as an allOf. - writer.WriteOptionalCollection(OpenApiConstants.AllOf, OneOf.Take(1), (w, s) => s.SerializeAsV2(w)); + writer.WriteOptionalCollection(OpenApiConstants.AllOf, OneOf?.Take(1), (w, s) => s.SerializeAsV2(w)); } } From 19d9edc216236dba862158f9ffb107bd872146ea Mon Sep 17 00:00:00 2001 From: Millicent Achieng Date: Sun, 14 Aug 2022 03:51:07 +0300 Subject: [PATCH 16/44] Remove unused method --- .../V2/OpenApiOperationDeserializer.cs | 2 +- .../V2/OpenApiV2VersionService.cs | 24 ------------------- 2 files changed, 1 insertion(+), 25 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs index a3bda05e1..1cf5b7ae8 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiOperationDeserializer.cs @@ -60,7 +60,7 @@ internal static partial class OpenApiV2Deserializer "consumes", (o, n) => { var consumes = n.CreateSimpleList(s => s.GetScalarValue()); if (consumes.Count > 0) { - n.Context.SetTempStorage(TempStorageKeys.OperationConsumes,consumes); + n.Context.SetTempStorage(TempStorageKeys.OperationConsumes,consumes); } } }, diff --git a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs index 718dcec04..33c9d7c6f 100644 --- a/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs +++ b/src/Microsoft.OpenApi.Readers/V2/OpenApiV2VersionService.cs @@ -107,30 +107,6 @@ private static ReferenceType ParseReferenceType(string referenceTypeName) } } - private static string GetReferenceTypeV2Name(ReferenceType referenceType) - { - switch (referenceType) - { - case ReferenceType.Schema: - return "definitions"; - - case ReferenceType.Parameter: - return "parameters"; - - case ReferenceType.Response: - return "responses"; - - case ReferenceType.Tag: - return "tags"; - - case ReferenceType.SecurityScheme: - return "securityDefinitions"; - - default: - throw new ArgumentException(); - } - } - private static ReferenceType GetReferenceTypeV2FromName(string referenceType) { switch (referenceType) From e0b30619c15a02d6d009a9a095df891dcc3e961e Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 15 Aug 2022 20:03:04 +0300 Subject: [PATCH 17/44] Publish release notes and .exe before pushing to Nuget --- .azure-pipelines/ci-build.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 5e8ddbd3b..44606bf01 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -266,14 +266,6 @@ stages: echo "$artifactName" echo "$artifactVersion" displayName: 'Fetch Artifact Name' - - - task: NuGetCommand@2 - displayName: 'NuGet push' - inputs: - command: push - packagesToPush: '$(Pipeline.Workspace)/Nugets/Microsoft.OpenApi.Hidi.*.nupkg' - nuGetFeedType: external - publishFeedCredentials: 'OpenAPI Nuget Connection' - task: GitHubRelease@1 displayName: 'GitHub release (edit)' inputs: @@ -285,6 +277,13 @@ stages: releaseNotesSource: inline assets: '$(Pipeline.Workspace)\**\*.exe' changeLogType: issueBased + - task: NuGetCommand@2 + displayName: 'NuGet push' + inputs: + command: push + packagesToPush: '$(Pipeline.Workspace)/Nugets/Microsoft.OpenApi.Hidi.*.nupkg' + nuGetFeedType: external + publishFeedCredentials: 'OpenAPI Nuget Connection' - deployment: deploy_lib dependsOn: [] From 5938d425c55ad4580e16fdba23ac12fbdca344e7 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 15 Aug 2022 20:14:10 +0300 Subject: [PATCH 18/44] Add a condition that triggers this task to run whether or not the preceding step has failed --- .azure-pipelines/ci-build.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 44606bf01..3e1b04fca 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -266,8 +266,16 @@ stages: echo "$artifactName" echo "$artifactVersion" displayName: 'Fetch Artifact Name' + - task: NuGetCommand@2 + displayName: 'NuGet push' + inputs: + command: push + packagesToPush: '$(Pipeline.Workspace)/Nugets/Microsoft.OpenApi.Hidi.*.nupkg' + nuGetFeedType: external + publishFeedCredentials: 'OpenAPI Nuget Connection' - task: GitHubRelease@1 displayName: 'GitHub release (edit)' + condition: succeededOrFailed() inputs: gitHubConnection: 'Github-MaggieKimani1' action: edit @@ -277,13 +285,6 @@ stages: releaseNotesSource: inline assets: '$(Pipeline.Workspace)\**\*.exe' changeLogType: issueBased - - task: NuGetCommand@2 - displayName: 'NuGet push' - inputs: - command: push - packagesToPush: '$(Pipeline.Workspace)/Nugets/Microsoft.OpenApi.Hidi.*.nupkg' - nuGetFeedType: external - publishFeedCredentials: 'OpenAPI Nuget Connection' - deployment: deploy_lib dependsOn: [] From 3b260ad3c8e3a03dc258b3495bfa747dc380d67a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Aug 2022 21:10:39 +0000 Subject: [PATCH 19/44] Bump Verify.Xunit from 17.9.0 to 17.10.0 Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 17.9.0 to 17.10.0. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/17.9.0...17.10.0) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 6cac42c5d..4ef308a15 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -20,7 +20,7 @@ - + all From 529400d0216d7147142dfbbcc6d5dc947e4d5bb3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 16 Aug 2022 21:07:14 +0000 Subject: [PATCH 20/44] Bump Verify.Xunit from 17.10.0 to 17.10.2 Bumps [Verify.Xunit](https://github.com/VerifyTests/Verify) from 17.10.0 to 17.10.2. - [Release notes](https://github.com/VerifyTests/Verify/releases) - [Commits](https://github.com/VerifyTests/Verify/compare/17.10.0...17.10.2) --- updated-dependencies: - dependency-name: Verify.Xunit dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 4ef308a15..a6ba76259 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -20,7 +20,7 @@ - + all From 0636e4a7e60f1bd8244333b756fb0a85a428e847 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 17 Aug 2022 13:18:26 +0300 Subject: [PATCH 21/44] Add a hashCode field to the diagnostics object to keep track of the hash value --- src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs | 5 +++++ src/Microsoft.OpenApi.Readers/ParsingContext.cs | 1 + 2 files changed, 6 insertions(+) diff --git a/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs b/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs index c3178ccfb..937a13891 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs @@ -26,5 +26,10 @@ public class OpenApiDiagnostic : IDiagnostic /// Open API specification version of the document parsed. /// public OpenApiSpecVersion SpecificationVersion { get; set; } + + /// + /// The unique hash code of the generated OpenAPI document + /// + public int HashCode { get; set; } } } diff --git a/src/Microsoft.OpenApi.Readers/ParsingContext.cs b/src/Microsoft.OpenApi.Readers/ParsingContext.cs index 6c4dece2f..537a981b8 100644 --- a/src/Microsoft.OpenApi.Readers/ParsingContext.cs +++ b/src/Microsoft.OpenApi.Readers/ParsingContext.cs @@ -75,6 +75,7 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument) throw new OpenApiUnsupportedSpecVersionException(inputVersion); } + Diagnostic.HashCode = doc.GetHashCode(); return doc; } From 500f0c7f45a81a13d51cafc9a74208d0280585a0 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 17 Aug 2022 13:19:21 +0300 Subject: [PATCH 22/44] Override the base GetHashCode() to compute the hash value for an OpenApi document and its property values --- .../Models/OpenApiDocument.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 44cbc71ab..9058dc7ba 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -375,6 +375,27 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference) return ResolveReference(reference, false); } + /// + /// Computes the hash code for an OpenApiDocument and its property values. + /// + /// The hash code. + public override int GetHashCode() + { + // select two random prime numbers e.g 1 and 3 and use them to compute hash codes + int hash = 1; + hash = hash * 3 + (Workspace == null ? 0 : Workspace.GetHashCode()); + hash = hash * 3 + (Info == null ? 0 : Info.GetHashCode()); + hash = hash * 3 + (Servers == null ? 0 : Servers.GetHashCode()); + hash = hash * 3 + (Paths == null ? 0 : Paths.GetHashCode()); + hash = hash * 3 + (Components == null ? 0 : Components.GetHashCode()); + hash = hash * 3 + (SecurityRequirements == null ? 0 : SecurityRequirements.GetHashCode()); + hash = hash * 3 + (Tags == null ? 0 : Tags.GetHashCode()); + hash = hash * 3 + (ExternalDocs == null ? 0 : ExternalDocs.GetHashCode()); + hash = hash * 3 + (Extensions == null ? 0 : Extensions.GetHashCode()); + + return hash; + } + /// /// Load the referenced object from a object /// From 7a58221b10c43bb7f42a01f679b12c46c5866cc9 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 17 Aug 2022 13:20:44 +0300 Subject: [PATCH 23/44] Simplify using statement, add tests, test file and cleanup --- .../OpenApiYamlDocumentReader.cs | 1 - .../Microsoft.OpenApi.Readers.Tests.csproj | 5 +- .../V3Tests/OpenApiDocumentTests.cs | 59 ++++++++++++++----- .../minimalDocumentWithWhitespace.yaml | 9 +++ 4 files changed, 57 insertions(+), 17 deletions(-) create mode 100644 test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml diff --git a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs index aae09ec86..3aedafbf1 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiYamlDocumentReader.cs @@ -79,7 +79,6 @@ public OpenApiDocument Read(YamlDocument input, out OpenApiDiagnostic diagnostic { diagnostic.Warnings.Add(item); } - } return document; diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index e89e47745..98f228850 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -1,4 +1,4 @@ - + net6.0 false @@ -137,6 +137,9 @@ Never + + Never + Never diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index f1d8b805f..b77fe8537 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -243,26 +243,55 @@ public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic() [Fact] public void ParseMinimalDocumentShouldSucceed() { - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml"))) - { - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - openApiDoc.Should().BeEquivalentTo( - new OpenApiDocument + openApiDoc.Should().BeEquivalentTo( + new OpenApiDocument + { + Info = new OpenApiInfo { - Info = new OpenApiInfo - { - Title = "Simple Document", - Version = "0.9.1" - }, - Paths = new OpenApiPaths() - }); + Title = "Simple Document", + Version = "0.9.1" + }, + Paths = new OpenApiPaths() + }); - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); - } + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); } + [Fact] + public void TestHashCodesForSimilarOpenApiDocuments() + { + // Arrange + using var stream1 = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); + using var stream2 = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); + using var stream3 = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocumentWithWhitespace.yaml")); + + // Act + /* + Test whether reading in the same document twice yields the same hash code, + And reading in similar documents but one has a whitespace yields the same hash code + */ + var openApiDoc1 = new OpenApiStreamReader().Read(stream1, out var diagnostic1); + var openApiDoc2 = new OpenApiStreamReader().Read(stream2, out var diagnostic2); + var openApiDoc3 = new OpenApiStreamReader().Read(stream3, out var diagnostic3); + + // Assert + /* The assumption is, if doc1.Equals(doc2), then doc1.GetHashCode().Equals(doc2.GetHashCode())*/ + if (openApiDoc1.Equals(openApiDoc2) && openApiDoc2.Equals(openApiDoc3)) + { + Assert.Equal(diagnostic1.HashCode, diagnostic2.HashCode); + Assert.Equal(diagnostic2.HashCode, diagnostic3.HashCode); + } + + /*Adding a server object to the original doc to check whether the hash code changes*/ + openApiDoc1.Servers = new List(); + var hash = openApiDoc1.GetHashCode(); + Assert.NotEqual(diagnostic1.HashCode, hash); + } + [Fact] public void ParseStandardPetStoreDocumentShouldSucceed() { diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml new file mode 100644 index 000000000..a68eb2fee --- /dev/null +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml @@ -0,0 +1,9 @@ +openapi : 3.0.0 +info: + title: Simple Document + version: 0.9.1 + +paths: {} + + + From f9a32fa9233a982d690520d6cf2a4d5319fded9e Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 17 Aug 2022 14:24:12 +0300 Subject: [PATCH 24/44] Refactor failing tests --- .../V2Tests/OpenApiDocumentTests.cs | 4 +- .../V2Tests/OpenApiServerTests.cs | 12 +- .../V3Tests/OpenApiCallbackTests.cs | 155 +++++++++--------- .../V3Tests/OpenApiDocumentTests.cs | 101 ++++++------ .../V3Tests/OpenApiSchemaTests.cs | 15 +- 5 files changed, 141 insertions(+), 146 deletions(-) diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs index 39bc0db80..b3f3033ac 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs @@ -150,9 +150,9 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture) }, Paths = new OpenApiPaths() }); + var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 }; - context.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 }); + Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); } [Fact] diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs index c87b491ab..3e2cde88d 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs @@ -280,14 +280,12 @@ public void InvalidHostShouldYieldError() var doc = reader.Read(input, out var diagnostic); doc.Servers.Count.Should().Be(0); - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic + + Assert.Equal(OpenApiSpecVersion.OpenApi2_0, diagnostic.SpecificationVersion); + diagnostic.Errors.Should().BeEquivalentTo( + new List { - Errors = - { - new OpenApiError("#/", "Invalid host") - }, - SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 + new OpenApiError("#/", "Invalid host") }); } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs index 320f01fae..a89c087ef 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs @@ -21,29 +21,28 @@ public class OpenApiCallbackTests [Fact] public void ParseBasicCallbackShouldSucceed() { - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicCallback.yaml"))) - { - // Arrange - var yamlStream = new YamlStream(); - yamlStream.Load(new StreamReader(stream)); - var yamlNode = yamlStream.Documents.First().RootNode; + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicCallback.yaml")); + // Arrange + var yamlStream = new YamlStream(); + yamlStream.Load(new StreamReader(stream)); + var yamlNode = yamlStream.Documents.First().RootNode; - var diagnostic = new OpenApiDiagnostic(); - var context = new ParsingContext(diagnostic); + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); - var node = new MapNode(context, (YamlMappingNode)yamlNode); + var node = new MapNode(context, (YamlMappingNode)yamlNode); - // Act - var callback = OpenApiV3Deserializer.LoadCallback(node); + // Act + var callback = OpenApiV3Deserializer.LoadCallback(node); - // Assert - diagnostic.Should().BeEquivalentTo(new OpenApiDiagnostic()); + // Assert + diagnostic.Should().BeEquivalentTo(new OpenApiDiagnostic()); - callback.Should().BeEquivalentTo( - new OpenApiCallback + callback.Should().BeEquivalentTo( + new OpenApiCallback + { + PathItems = { - PathItems = - { [RuntimeExpression.Build("$request.body#/url")] = new OpenApiPathItem { @@ -69,33 +68,31 @@ public void ParseBasicCallbackShouldSucceed() } } } - } - }); - } + } + }); } [Fact] public void ParseCallbackWithReferenceShouldSucceed() { - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "callbackWithReference.yaml"))) - { - // Act - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "callbackWithReference.yaml")); + // Act + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - // Assert - var path = openApiDoc.Paths.First().Value; - var subscribeOperation = path.Operations[OperationType.Post]; + // Assert + var path = openApiDoc.Paths.First().Value; + var subscribeOperation = path.Operations[OperationType.Post]; - var callback = subscribeOperation.Callbacks["simpleHook"]; + var callback = subscribeOperation.Callbacks["simpleHook"]; + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); - callback.Should().BeEquivalentTo( - new OpenApiCallback + callback.Should().BeEquivalentTo( + new OpenApiCallback + { + PathItems = { - PathItems = - { [RuntimeExpression.Build("$request.body#/url")]= new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -122,39 +119,38 @@ public void ParseCallbackWithReferenceShouldSucceed() } } } - }, - Reference = new OpenApiReference - { - Type = ReferenceType.Callback, - Id = "simpleHook", - HostDocument = openApiDoc - } - }); - } + }, + Reference = new OpenApiReference + { + Type = ReferenceType.Callback, + Id = "simpleHook", + HostDocument = openApiDoc + } + }); } [Fact] public void ParseMultipleCallbacksWithReferenceShouldSucceed() { - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "multipleCallbacksWithReference.yaml"))) - { - // Act - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "multipleCallbacksWithReference.yaml")); + // Act + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + + // Assert + var path = openApiDoc.Paths.First().Value; + var subscribeOperation = path.Operations[OperationType.Post]; - // Assert - var path = openApiDoc.Paths.First().Value; - var subscribeOperation = path.Operations[OperationType.Post]; + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); - var callback1 = subscribeOperation.Callbacks["simpleHook"]; + var callback1 = subscribeOperation.Callbacks["simpleHook"]; - callback1.Should().BeEquivalentTo( - new OpenApiCallback + callback1.Should().BeEquivalentTo( + new OpenApiCallback + { + PathItems = { - PathItems = - { [RuntimeExpression.Build("$request.body#/url")]= new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -181,21 +177,21 @@ public void ParseMultipleCallbacksWithReferenceShouldSucceed() } } } - }, - Reference = new OpenApiReference - { - Type = ReferenceType.Callback, - Id = "simpleHook", - HostDocument = openApiDoc - } - }); + }, + Reference = new OpenApiReference + { + Type = ReferenceType.Callback, + Id = "simpleHook", + HostDocument = openApiDoc + } + }); - var callback2 = subscribeOperation.Callbacks["callback2"]; - callback2.Should().BeEquivalentTo( - new OpenApiCallback + var callback2 = subscribeOperation.Callbacks["callback2"]; + callback2.Should().BeEquivalentTo( + new OpenApiCallback + { + PathItems = { - PathItems = - { [RuntimeExpression.Build("/simplePath")]= new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -223,15 +219,15 @@ public void ParseMultipleCallbacksWithReferenceShouldSucceed() } }, } - } - }); + } + }); - var callback3 = subscribeOperation.Callbacks["callback3"]; - callback3.Should().BeEquivalentTo( - new OpenApiCallback + var callback3 = subscribeOperation.Callbacks["callback3"]; + callback3.Should().BeEquivalentTo( + new OpenApiCallback + { + PathItems = { - PathItems = - { [RuntimeExpression.Build(@"http://example.com?transactionId={$request.body#/id}&email={$request.body#/email}")] = new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -266,9 +262,8 @@ public void ParseMultipleCallbacksWithReferenceShouldSucceed() } } } - } - }); - } + } + }); } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index b77fe8537..9ea3cfa4f 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -99,8 +99,9 @@ public void ParseDocumentFromInlineStringShouldSucceed() Paths = new OpenApiPaths() }); - context.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); } [Theory] @@ -170,31 +171,30 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture) }, Paths = new OpenApiPaths() }); + var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - context.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); } [Fact] public void ParseBasicDocumentWithMultipleServersShouldSucceed() { - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml"))) - { - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml")); + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); - openApiDoc.Should().BeEquivalentTo( - new OpenApiDocument + openApiDoc.Should().BeEquivalentTo( + new OpenApiDocument + { + Info = new OpenApiInfo + { + Title = "The API", + Version = "0.9.1", + }, + Servers = { - Info = new OpenApiInfo - { - Title = "The API", - Version = "0.9.1", - }, - Servers = - { new OpenApiServer { Url = new Uri("http://www.example.org/api").ToString(), @@ -205,39 +205,34 @@ public void ParseBasicDocumentWithMultipleServersShouldSucceed() Url = new Uri("https://www.example.org/api").ToString(), Description = "The https endpoint" } - }, - Paths = new OpenApiPaths() - }); - } + }, + Paths = new OpenApiPaths() + }); } [Fact] public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic() { - using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml"))) - { - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml")); + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - openApiDoc.Should().BeEquivalentTo( - new OpenApiDocument + openApiDoc.Should().BeEquivalentTo( + new OpenApiDocument + { + Info = new OpenApiInfo { - Info = new OpenApiInfo - { - Version = "0.9" - }, - Paths = new OpenApiPaths() - }); + Version = "0.9" + }, + Paths = new OpenApiPaths() + }); - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic - { - Errors = - { - new OpenApiValidatorError(nameof(OpenApiInfoRules.InfoRequiredFields),"#/info/title", "The field 'title' in 'info' object is REQUIRED.") - }, - SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 - }); - } + diagnostic.Errors.Should().BeEquivalentTo( + new List + { + new OpenApiValidatorError(nameof(OpenApiInfoRules.InfoRequiredFields),"#/info/title", "The field 'title' in 'info' object is REQUIRED.") + }); + + Assert.Equal(OpenApiSpecVersion.OpenApi3_0, diagnostic.SpecificationVersion); } [Fact] @@ -257,8 +252,9 @@ public void ParseMinimalDocumentShouldSucceed() Paths = new OpenApiPaths() }); - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); } [Fact] @@ -718,8 +714,9 @@ public void ParseStandardPetStoreDocumentShouldSucceed() actual.Should().BeEquivalentTo(expected); } - context.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); } [Fact] @@ -1251,8 +1248,9 @@ public void ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() actual.Should().BeEquivalentTo(expected, options => options.Excluding(m => m.Name == "HostDocument")); } - context.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); } [Fact] @@ -1267,8 +1265,9 @@ public void ParsePetStoreExpandedShouldSucceed() // TODO: Create the object in memory and compare with the one read from YAML file. } - context.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); } [Fact] diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs index 9bdafeba6..63d7894c5 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs @@ -332,8 +332,9 @@ public void ParseBasicSchemaWithReferenceShouldSucceed() // Assert var components = openApiDoc.Components; - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); components.Should().BeEquivalentTo( new OpenApiComponents @@ -438,8 +439,9 @@ public void ParseAdvancedSchemaWithReferenceShouldSucceed() // Assert var components = openApiDoc.Components; - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); components.Should().BeEquivalentTo( new OpenApiComponents @@ -619,8 +621,9 @@ public void ParseSelfReferencingSchemaShouldNotStackOverflow() // Assert var components = openApiDoc.Components; - diagnostic.Should().BeEquivalentTo( - new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + + Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); var schemaExtension = new OpenApiSchema() { From c7e3ed81e1bce61a19b2ca7fa236f162adeaf5b9 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 22 Aug 2022 14:01:30 +0300 Subject: [PATCH 25/44] Compute hash value using hashing algorithm during serialization --- src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs | 2 +- .../OpenApiStreamReader.cs | 13 +++++++++++++ src/Microsoft.OpenApi.Readers/ParsingContext.cs | 1 - 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs b/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs index 937a13891..d634fe804 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs @@ -30,6 +30,6 @@ public class OpenApiDiagnostic : IDiagnostic /// /// The unique hash code of the generated OpenAPI document /// - public int HashCode { get; set; } + public string HashCode { get; set; } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index 13bdbdef8..d4dc709cf 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -3,6 +3,8 @@ using System; using System.IO; +using System.Security.Cryptography; +using System.Text; using System.Threading.Tasks; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; @@ -42,6 +44,17 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { var reader = new StreamReader(input); var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); + + HashAlgorithm sha = SHA512.Create(); + byte[] data = sha.ComputeHash(input); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < data.Length; i++) + { + sb.Append(data[i].ToString("X2")); + } + + diagnostic.HashCode = sb.ToString(); + if (!_settings.LeaveStreamOpen) { reader.Dispose(); diff --git a/src/Microsoft.OpenApi.Readers/ParsingContext.cs b/src/Microsoft.OpenApi.Readers/ParsingContext.cs index 537a981b8..6c4dece2f 100644 --- a/src/Microsoft.OpenApi.Readers/ParsingContext.cs +++ b/src/Microsoft.OpenApi.Readers/ParsingContext.cs @@ -75,7 +75,6 @@ internal OpenApiDocument Parse(YamlDocument yamlDocument) throw new OpenApiUnsupportedSpecVersionException(inputVersion); } - Diagnostic.HashCode = doc.GetHashCode(); return doc; } From 8d20075393b9c46e686580cead3b685c6d7027f2 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 22 Aug 2022 14:21:51 +0300 Subject: [PATCH 26/44] Clean up test --- .../Models/OpenApiDocument.cs | 60 +++++++++++++------ .../V3Tests/OpenApiDocumentTests.cs | 15 +---- 2 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 6ad301cc0..6b9d3af59 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -3,7 +3,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Runtime.Serialization.Formatters.Binary; +using System.Security.Cryptography; +using System.Text; using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Services; @@ -62,6 +66,8 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible /// public IDictionary Extensions { get; set; } = new Dictionary(); + private static readonly object locker = new(); + /// /// Parameter-less constructor /// @@ -375,26 +381,42 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference) return ResolveReference(reference, false); } - /// - /// Computes the hash code for an OpenApiDocument and its property values. - /// - /// The hash code. - public override int GetHashCode() + ///// + ///// Computes the hash code for an OpenApiDocument and its property values. + ///// + ///// The hash code. + //public override int GetHashCode() + //{ + // // select two random prime numbers e.g 1 and 3 and use them to compute hash codes + // int hash = 1; + // hash = hash * 3 + (Workspace == null ? 0 : Workspace.GetHashCode()); + // hash = hash * 3 + (Info == null ? 0 : Info.GetHashCode()); + // hash = hash * 3 + (Servers == null ? 0 : Servers.GetHashCode()); + // hash = hash * 3 + (Paths == null ? 0 : Paths.GetHashCode()); + // hash = hash * 3 + (Components == null ? 0 : Components.GetHashCode()); + // hash = hash * 3 + (SecurityRequirements == null ? 0 : SecurityRequirements.GetHashCode()); + // hash = hash * 3 + (Tags == null ? 0 : Tags.GetHashCode()); + // hash = hash * 3 + (ExternalDocs == null ? 0 : ExternalDocs.GetHashCode()); + // hash = hash * 3 + (Extensions == null ? 0 : Extensions.GetHashCode()); + + // return hash; + //} + + public static string GenerateHashValue(Stream input) { - // select two random prime numbers e.g 1 and 3 and use them to compute hash codes - int hash = 1; - hash = hash * 3 + (Workspace == null ? 0 : Workspace.GetHashCode()); - hash = hash * 3 + (Info == null ? 0 : Info.GetHashCode()); - hash = hash * 3 + (Servers == null ? 0 : Servers.GetHashCode()); - hash = hash * 3 + (Paths == null ? 0 : Paths.GetHashCode()); - hash = hash * 3 + (Components == null ? 0 : Components.GetHashCode()); - hash = hash * 3 + (SecurityRequirements == null ? 0 : SecurityRequirements.GetHashCode()); - hash = hash * 3 + (Tags == null ? 0 : Tags.GetHashCode()); - hash = hash * 3 + (ExternalDocs == null ? 0 : ExternalDocs.GetHashCode()); - hash = hash * 3 + (Extensions == null ? 0 : Extensions.GetHashCode()); - - return hash; - } + HashAlgorithm sha = SHA512.Create(); + byte[] result = sha.ComputeHash(input); + + // Build the final string by converting each byte + // into hex and appending it to a StringBuilder + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < result.Length; i++) + { + sb.Append(result[i].ToString("X2")); + } + + return sb.ToString(); + } /// /// Load the referenced object from a object diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index 9ea3cfa4f..242dc6b74 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -9,13 +9,11 @@ using System.Threading; using FluentAssertions; using Microsoft.OpenApi.Any; -using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Validations; using Microsoft.OpenApi.Validations.Rules; using Microsoft.OpenApi.Writers; -using Newtonsoft.Json; using Xunit; using Xunit.Abstractions; @@ -275,17 +273,8 @@ And reading in similar documents but one has a whitespace yields the same hash c var openApiDoc3 = new OpenApiStreamReader().Read(stream3, out var diagnostic3); // Assert - /* The assumption is, if doc1.Equals(doc2), then doc1.GetHashCode().Equals(doc2.GetHashCode())*/ - if (openApiDoc1.Equals(openApiDoc2) && openApiDoc2.Equals(openApiDoc3)) - { - Assert.Equal(diagnostic1.HashCode, diagnostic2.HashCode); - Assert.Equal(diagnostic2.HashCode, diagnostic3.HashCode); - } - - /*Adding a server object to the original doc to check whether the hash code changes*/ - openApiDoc1.Servers = new List(); - var hash = openApiDoc1.GetHashCode(); - Assert.NotEqual(diagnostic1.HashCode, hash); + Assert.Equal(diagnostic1.HashCode, diagnostic2.HashCode); + Assert.Equal(diagnostic2.HashCode, diagnostic3.HashCode); } [Fact] From a676e37ede211ea94fe521cd56373d28e2386c68 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 22 Aug 2022 14:22:33 +0300 Subject: [PATCH 27/44] Code cleanup --- .../OpenApiStreamReader.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index d4dc709cf..0554108b2 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -45,15 +45,17 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) var reader = new StreamReader(input); var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); - HashAlgorithm sha = SHA512.Create(); - byte[] data = sha.ComputeHash(input); - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < data.Length; i++) - { - sb.Append(data[i].ToString("X2")); - } + //HashAlgorithm sha = SHA512.Create(); + //byte[] data = sha.ComputeHash(input); + //StringBuilder sb = new StringBuilder(); + //for (int i = 0; i < data.Length; i++) + //{ + // sb.Append(data[i].ToString("X2")); + //} + - diagnostic.HashCode = sb.ToString(); + //diagnostic.HashCode = sb.ToString(); + diagnostic.HashCode = OpenApiDocument.GenerateHashValue(input); if (!_settings.LeaveStreamOpen) { From 96f060bce9cea7867bdc3d8bcd014bdeae49b5dc Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 22 Aug 2022 15:33:33 +0300 Subject: [PATCH 28/44] Code cleanup --- src/Microsoft.OpenApi.Hidi/OpenApiService.cs | 80 +------------------ .../OpenApiStreamReader.cs | 10 --- .../Models/OpenApiDocument.cs | 29 ++----- 3 files changed, 6 insertions(+), 113 deletions(-) diff --git a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs index c37c9479d..461ca50be 100644 --- a/src/Microsoft.OpenApi.Hidi/OpenApiService.cs +++ b/src/Microsoft.OpenApi.Hidi/OpenApiService.cs @@ -356,57 +356,7 @@ public static OpenApiDocument FixReferences(OpenApiDocument document) return doc; } - - private static async Task GetStream(string input, ILogger logger) - { - var stopwatch = new Stopwatch(); - stopwatch.Start(); - - Stream stream; - if (input.StartsWith("http")) - { - try - { - var httpClientHandler = new HttpClientHandler() - { - SslProtocols = System.Security.Authentication.SslProtocols.Tls12, - }; - using var httpClient = new HttpClient(httpClientHandler) - { - DefaultRequestVersion = HttpVersion.Version20 - }; - stream = await httpClient.GetStreamAsync(input); - } - catch (HttpRequestException ex) - { - logger.LogError($"Could not download the file at {input}, reason{ex}"); - return null; - } - } - else - { - try - { - var fileInput = new FileInfo(input); - stream = fileInput.OpenRead(); - } - catch (Exception ex) when (ex is FileNotFoundException || - ex is PathTooLongException || - ex is DirectoryNotFoundException || - ex is IOException || - ex is UnauthorizedAccessException || - ex is SecurityException || - ex is NotSupportedException) - { - logger.LogError($"Could not open the file at {input}, reason: {ex.Message}"); - return null; - } - } - stopwatch.Stop(); - logger.LogTrace("{timestamp}ms: Read file {input}", stopwatch.ElapsedMilliseconds, input); - return stream; - } - + /// /// Takes in a file stream, parses the stream into a JsonDocument and gets a list of paths and Http methods /// @@ -462,34 +412,6 @@ private static Dictionary> EnumerateJsonDocument(JsonElemen return paths; } - /// - /// Fixes the references in the resulting OpenApiDocument. - /// - /// The converted OpenApiDocument. - /// A valid OpenApiDocument instance. - // private static OpenApiDocument FixReferences2(OpenApiDocument document) - // { - // // This method is only needed because the output of ConvertToOpenApi isn't quite a valid OpenApiDocument instance. - // // So we write it out, and read it back in again to fix it up. - - // OpenApiDocument document; - // logger.LogTrace("Parsing the OpenApi file"); - // var result = await new OpenApiStreamReader(new OpenApiReaderSettings - // { - // RuleSet = ValidationRuleSet.GetDefaultRuleSet(), - // BaseUrl = new Uri(openapi) - // } - // ).ReadAsync(stream); - - // document = result.OpenApiDocument; - // var context = result.OpenApiDiagnostic; - // var sb = new StringBuilder(); - // document.SerializeAsV3(new OpenApiYamlWriter(new StringWriter(sb))); - // var doc = new OpenApiStringReader().Read(sb.ToString(), out _); - - // return doc; - // } - /// /// Reads stream from file system or makes HTTP request depending on the input string /// diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index 0554108b2..6d21a692f 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -45,16 +45,6 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) var reader = new StreamReader(input); var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); - //HashAlgorithm sha = SHA512.Create(); - //byte[] data = sha.ComputeHash(input); - //StringBuilder sb = new StringBuilder(); - //for (int i = 0; i < data.Length; i++) - //{ - // sb.Append(data[i].ToString("X2")); - //} - - - //diagnostic.HashCode = sb.ToString(); diagnostic.HashCode = OpenApiDocument.GenerateHashValue(input); if (!_settings.LeaveStreamOpen) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 6b9d3af59..ccdb65092 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; using System.Text; using Microsoft.OpenApi.Exceptions; @@ -66,8 +65,6 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible /// public IDictionary Extensions { get; set; } = new Dictionary(); - private static readonly object locker = new(); - /// /// Parameter-less constructor /// @@ -381,27 +378,11 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference) return ResolveReference(reference, false); } - ///// - ///// Computes the hash code for an OpenApiDocument and its property values. - ///// - ///// The hash code. - //public override int GetHashCode() - //{ - // // select two random prime numbers e.g 1 and 3 and use them to compute hash codes - // int hash = 1; - // hash = hash * 3 + (Workspace == null ? 0 : Workspace.GetHashCode()); - // hash = hash * 3 + (Info == null ? 0 : Info.GetHashCode()); - // hash = hash * 3 + (Servers == null ? 0 : Servers.GetHashCode()); - // hash = hash * 3 + (Paths == null ? 0 : Paths.GetHashCode()); - // hash = hash * 3 + (Components == null ? 0 : Components.GetHashCode()); - // hash = hash * 3 + (SecurityRequirements == null ? 0 : SecurityRequirements.GetHashCode()); - // hash = hash * 3 + (Tags == null ? 0 : Tags.GetHashCode()); - // hash = hash * 3 + (ExternalDocs == null ? 0 : ExternalDocs.GetHashCode()); - // hash = hash * 3 + (Extensions == null ? 0 : Extensions.GetHashCode()); - - // return hash; - //} - + /// + /// Uses the stream input to generate the hash value of an OpenApi document + /// + /// Stream containing OpenAPI description to hash. + /// The hash value. public static string GenerateHashValue(Stream input) { HashAlgorithm sha = SHA512.Create(); From 9772ad07a467e365f925b2fbcf5d921e9b1cb637 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 22 Aug 2022 16:12:41 +0300 Subject: [PATCH 29/44] Update public Api interface --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index b320f8b10..11ed58e88 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1,7 +1,7 @@ [assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Microsoft/OpenAPI.NET")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Readers.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName=".NET Standard 2.0")] namespace Microsoft.OpenApi.Any { public enum AnyType @@ -535,6 +535,7 @@ namespace Microsoft.OpenApi.Models public System.Collections.Generic.IEnumerable ResolveReferences() { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } + public static string GenerateHashValue(System.IO.Stream input) { } } public class OpenApiEncoding : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { From d50f8577ccb2f2b54683a9c6026f73037f5905b1 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 22 Aug 2022 16:12:41 +0300 Subject: [PATCH 30/44] Revert "Update public Api interface" This reverts commit 9772ad07a467e365f925b2fbcf5d921e9b1cb637. --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 11ed58e88..b320f8b10 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1,7 +1,7 @@ [assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Microsoft/OpenAPI.NET")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Readers.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName=".NET Standard 2.0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")] namespace Microsoft.OpenApi.Any { public enum AnyType @@ -535,7 +535,6 @@ namespace Microsoft.OpenApi.Models public System.Collections.Generic.IEnumerable ResolveReferences() { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } - public static string GenerateHashValue(System.IO.Stream input) { } } public class OpenApiEncoding : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { From df65299b34981e45798160cb0fcbca413c243898 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 24 Aug 2022 12:05:08 +0300 Subject: [PATCH 31/44] Cater for file content transferred with base64 encoding --- src/Microsoft.OpenApi/Models/OpenApiOperation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index ba0af7317..9b32934c2 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -255,7 +255,7 @@ public void SerializeAsV2(IOpenApiWriter writer) { var paramName = property.Key; var paramSchema = property.Value; - if (paramSchema.Type == "string" && paramSchema.Format == "binary") { + if (paramSchema.Type == "string" && paramSchema.Format == "binary" || paramSchema.Format == "base64") { paramSchema.Type = "file"; paramSchema.Format = null; } From 9b8e16b5d19f8fae7fb7b08b953276a0e6dfba4f Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Wed, 24 Aug 2022 14:58:02 +0300 Subject: [PATCH 32/44] Add braces to the conditional statement --- src/Microsoft.OpenApi/Models/OpenApiOperation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index 9b32934c2..d2f8dec07 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -255,7 +255,7 @@ public void SerializeAsV2(IOpenApiWriter writer) { var paramName = property.Key; var paramSchema = property.Value; - if (paramSchema.Type == "string" && paramSchema.Format == "binary" || paramSchema.Format == "base64") { + if (paramSchema.Type == "string" && (paramSchema.Format == "binary" || paramSchema.Format == "base64")) { paramSchema.Type = "file"; paramSchema.Format = null; } From 8674fe332aa83d44c25427e3d047bc9d3fd9a2dd Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 26 Aug 2022 12:32:08 +0300 Subject: [PATCH 33/44] Revert previous changes --- .../OpenApiDiagnostic.cs | 5 - .../OpenApiStreamReader.cs | 2 - .../Microsoft.OpenApi.Readers.Tests.csproj | 3 - .../V2Tests/OpenApiDocumentTests.cs | 8 +- .../V2Tests/OpenApiServerTests.cs | 12 +- .../V3Tests/OpenApiCallbackTests.cs | 155 ++++++++--------- .../V3Tests/OpenApiDocumentTests.cs | 157 ++++++++---------- .../V3Tests/OpenApiSchemaTests.cs | 17 +- 8 files changed, 169 insertions(+), 190 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs b/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs index d634fe804..c3178ccfb 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiDiagnostic.cs @@ -26,10 +26,5 @@ public class OpenApiDiagnostic : IDiagnostic /// Open API specification version of the document parsed. /// public OpenApiSpecVersion SpecificationVersion { get; set; } - - /// - /// The unique hash code of the generated OpenAPI document - /// - public string HashCode { get; set; } } } diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index 6d21a692f..f6c9e7613 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -45,8 +45,6 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) var reader = new StreamReader(input); var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); - diagnostic.HashCode = OpenApiDocument.GenerateHashValue(input); - if (!_settings.LeaveStreamOpen) { reader.Dispose(); diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index 597302062..94432db9a 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -137,9 +137,6 @@ Never - - Never - Never diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs index b3f3033ac..fcf0471ea 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiDocumentTests.cs @@ -150,9 +150,9 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture) }, Paths = new OpenApiPaths() }); - var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 }; - Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); + context.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 }); } [Fact] @@ -208,7 +208,7 @@ public void ShouldParseProducesInAnyOrder() { Type = ReferenceType.Schema, Id = "Error", - HostDocument= doc + HostDocument = doc }, Properties = new Dictionary() { @@ -407,7 +407,7 @@ public void ShouldAssignSchemaToAllResponses() { Id = "Error", Type = ReferenceType.Schema, - HostDocument= document + HostDocument = document } }; var responses = document.Paths["/items"].Operations[OperationType.Get].Responses; diff --git a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs index 3e2cde88d..c87b491ab 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V2Tests/OpenApiServerTests.cs @@ -280,12 +280,14 @@ public void InvalidHostShouldYieldError() var doc = reader.Read(input, out var diagnostic); doc.Servers.Count.Should().Be(0); - - Assert.Equal(OpenApiSpecVersion.OpenApi2_0, diagnostic.SpecificationVersion); - diagnostic.Errors.Should().BeEquivalentTo( - new List + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic { - new OpenApiError("#/", "Invalid host") + Errors = + { + new OpenApiError("#/", "Invalid host") + }, + SpecificationVersion = OpenApiSpecVersion.OpenApi2_0 }); } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs index a89c087ef..320f01fae 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiCallbackTests.cs @@ -21,28 +21,29 @@ public class OpenApiCallbackTests [Fact] public void ParseBasicCallbackShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicCallback.yaml")); - // Arrange - var yamlStream = new YamlStream(); - yamlStream.Load(new StreamReader(stream)); - var yamlNode = yamlStream.Documents.First().RootNode; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicCallback.yaml"))) + { + // Arrange + var yamlStream = new YamlStream(); + yamlStream.Load(new StreamReader(stream)); + var yamlNode = yamlStream.Documents.First().RootNode; - var diagnostic = new OpenApiDiagnostic(); - var context = new ParsingContext(diagnostic); + var diagnostic = new OpenApiDiagnostic(); + var context = new ParsingContext(diagnostic); - var node = new MapNode(context, (YamlMappingNode)yamlNode); + var node = new MapNode(context, (YamlMappingNode)yamlNode); - // Act - var callback = OpenApiV3Deserializer.LoadCallback(node); + // Act + var callback = OpenApiV3Deserializer.LoadCallback(node); - // Assert - diagnostic.Should().BeEquivalentTo(new OpenApiDiagnostic()); + // Assert + diagnostic.Should().BeEquivalentTo(new OpenApiDiagnostic()); - callback.Should().BeEquivalentTo( - new OpenApiCallback - { - PathItems = + callback.Should().BeEquivalentTo( + new OpenApiCallback { + PathItems = + { [RuntimeExpression.Build("$request.body#/url")] = new OpenApiPathItem { @@ -68,31 +69,33 @@ public void ParseBasicCallbackShouldSucceed() } } } - } - }); + } + }); + } } [Fact] public void ParseCallbackWithReferenceShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "callbackWithReference.yaml")); - // Act - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "callbackWithReference.yaml"))) + { + // Act + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - // Assert - var path = openApiDoc.Paths.First().Value; - var subscribeOperation = path.Operations[OperationType.Post]; + // Assert + var path = openApiDoc.Paths.First().Value; + var subscribeOperation = path.Operations[OperationType.Post]; - var callback = subscribeOperation.Callbacks["simpleHook"]; - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + var callback = subscribeOperation.Callbacks["simpleHook"]; - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); - callback.Should().BeEquivalentTo( - new OpenApiCallback - { - PathItems = + callback.Should().BeEquivalentTo( + new OpenApiCallback { + PathItems = + { [RuntimeExpression.Build("$request.body#/url")]= new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -119,38 +122,39 @@ public void ParseCallbackWithReferenceShouldSucceed() } } } - }, - Reference = new OpenApiReference - { - Type = ReferenceType.Callback, - Id = "simpleHook", - HostDocument = openApiDoc - } - }); + }, + Reference = new OpenApiReference + { + Type = ReferenceType.Callback, + Id = "simpleHook", + HostDocument = openApiDoc + } + }); + } } [Fact] public void ParseMultipleCallbacksWithReferenceShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "multipleCallbacksWithReference.yaml")); - // Act - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - - // Assert - var path = openApiDoc.Paths.First().Value; - var subscribeOperation = path.Operations[OperationType.Post]; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "multipleCallbacksWithReference.yaml"))) + { + // Act + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + // Assert + var path = openApiDoc.Paths.First().Value; + var subscribeOperation = path.Operations[OperationType.Post]; - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); - var callback1 = subscribeOperation.Callbacks["simpleHook"]; + var callback1 = subscribeOperation.Callbacks["simpleHook"]; - callback1.Should().BeEquivalentTo( - new OpenApiCallback - { - PathItems = + callback1.Should().BeEquivalentTo( + new OpenApiCallback { + PathItems = + { [RuntimeExpression.Build("$request.body#/url")]= new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -177,21 +181,21 @@ public void ParseMultipleCallbacksWithReferenceShouldSucceed() } } } - }, - Reference = new OpenApiReference - { - Type = ReferenceType.Callback, - Id = "simpleHook", - HostDocument = openApiDoc - } - }); + }, + Reference = new OpenApiReference + { + Type = ReferenceType.Callback, + Id = "simpleHook", + HostDocument = openApiDoc + } + }); - var callback2 = subscribeOperation.Callbacks["callback2"]; - callback2.Should().BeEquivalentTo( - new OpenApiCallback - { - PathItems = + var callback2 = subscribeOperation.Callbacks["callback2"]; + callback2.Should().BeEquivalentTo( + new OpenApiCallback { + PathItems = + { [RuntimeExpression.Build("/simplePath")]= new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -219,15 +223,15 @@ public void ParseMultipleCallbacksWithReferenceShouldSucceed() } }, } - } - }); + } + }); - var callback3 = subscribeOperation.Callbacks["callback3"]; - callback3.Should().BeEquivalentTo( - new OpenApiCallback - { - PathItems = + var callback3 = subscribeOperation.Callbacks["callback3"]; + callback3.Should().BeEquivalentTo( + new OpenApiCallback { + PathItems = + { [RuntimeExpression.Build(@"http://example.com?transactionId={$request.body#/id}&email={$request.body#/email}")] = new OpenApiPathItem { Operations = { [OperationType.Post] = new OpenApiOperation() @@ -262,8 +266,9 @@ public void ParseMultipleCallbacksWithReferenceShouldSucceed() } } } - } - }); + } + }); + } } } } diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs index 242dc6b74..6fbb7065a 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiDocumentTests.cs @@ -9,11 +9,13 @@ using System.Threading; using FluentAssertions; using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Validations; using Microsoft.OpenApi.Validations.Rules; using Microsoft.OpenApi.Writers; +using Newtonsoft.Json; using Xunit; using Xunit.Abstractions; @@ -32,8 +34,10 @@ public T Clone(T element) where T : IOpenApiSerializable { IOpenApiWriter writer; var streamWriter = new FormattingStreamWriter(stream, CultureInfo.InvariantCulture); - writer = new OpenApiJsonWriter(streamWriter, new OpenApiJsonWriterSettings() { - InlineLocalReferences = true}); + writer = new OpenApiJsonWriter(streamWriter, new OpenApiJsonWriterSettings() + { + InlineLocalReferences = true + }); element.SerializeAsV3(writer); writer.Flush(); stream.Position = 0; @@ -46,7 +50,7 @@ public T Clone(T element) where T : IOpenApiSerializable } } - public OpenApiSecurityScheme CloneSecurityScheme(OpenApiSecurityScheme element) + public OpenApiSecurityScheme CloneSecurityScheme(OpenApiSecurityScheme element) { using (var stream = new MemoryStream()) { @@ -97,9 +101,8 @@ public void ParseDocumentFromInlineStringShouldSucceed() Paths = new OpenApiPaths() }); - var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); + context.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); } [Theory] @@ -169,30 +172,31 @@ public void ParseDocumentWithDifferentCultureShouldSucceed(string culture) }, Paths = new OpenApiPaths() }); - var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); + context.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); } [Fact] public void ParseBasicDocumentWithMultipleServersShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml")); - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "basicDocumentWithMultipleServers.yaml"))) + { + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); - openApiDoc.Should().BeEquivalentTo( - new OpenApiDocument - { - Info = new OpenApiInfo - { - Title = "The API", - Version = "0.9.1", - }, - Servers = + openApiDoc.Should().BeEquivalentTo( + new OpenApiDocument { + Info = new OpenApiInfo + { + Title = "The API", + Version = "0.9.1", + }, + Servers = + { new OpenApiServer { Url = new Uri("http://www.example.org/api").ToString(), @@ -203,80 +207,64 @@ public void ParseBasicDocumentWithMultipleServersShouldSucceed() Url = new Uri("https://www.example.org/api").ToString(), Description = "The https endpoint" } - }, - Paths = new OpenApiPaths() - }); + }, + Paths = new OpenApiPaths() + }); + } } [Fact] public void ParseBrokenMinimalDocumentShouldYieldExpectedDiagnostic() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml")); - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "brokenMinimalDocument.yaml"))) + { + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - openApiDoc.Should().BeEquivalentTo( - new OpenApiDocument - { - Info = new OpenApiInfo + openApiDoc.Should().BeEquivalentTo( + new OpenApiDocument { - Version = "0.9" - }, - Paths = new OpenApiPaths() - }); - - diagnostic.Errors.Should().BeEquivalentTo( - new List - { - new OpenApiValidatorError(nameof(OpenApiInfoRules.InfoRequiredFields),"#/info/title", "The field 'title' in 'info' object is REQUIRED.") - }); + Info = new OpenApiInfo + { + Version = "0.9" + }, + Paths = new OpenApiPaths() + }); - Assert.Equal(OpenApiSpecVersion.OpenApi3_0, diagnostic.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic + { + Errors = + { + new OpenApiValidatorError(nameof(OpenApiInfoRules.InfoRequiredFields),"#/info/title", "The field 'title' in 'info' object is REQUIRED.") + }, + SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 + }); + } } [Fact] public void ParseMinimalDocumentShouldSucceed() { - using var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); - var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + using (var stream = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml"))) + { + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); - openApiDoc.Should().BeEquivalentTo( - new OpenApiDocument - { - Info = new OpenApiInfo + openApiDoc.Should().BeEquivalentTo( + new OpenApiDocument { - Title = "Simple Document", - Version = "0.9.1" - }, - Paths = new OpenApiPaths() - }); - - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; + Info = new OpenApiInfo + { + Title = "Simple Document", + Version = "0.9.1" + }, + Paths = new OpenApiPaths() + }); - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); + } } - [Fact] - public void TestHashCodesForSimilarOpenApiDocuments() - { - // Arrange - using var stream1 = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); - using var stream2 = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocument.yaml")); - using var stream3 = Resources.GetStream(Path.Combine(SampleFolderPath, "minimalDocumentWithWhitespace.yaml")); - - // Act - /* - Test whether reading in the same document twice yields the same hash code, - And reading in similar documents but one has a whitespace yields the same hash code - */ - var openApiDoc1 = new OpenApiStreamReader().Read(stream1, out var diagnostic1); - var openApiDoc2 = new OpenApiStreamReader().Read(stream2, out var diagnostic2); - var openApiDoc3 = new OpenApiStreamReader().Read(stream3, out var diagnostic3); - - // Assert - Assert.Equal(diagnostic1.HashCode, diagnostic2.HashCode); - Assert.Equal(diagnostic2.HashCode, diagnostic3.HashCode); - } - [Fact] public void ParseStandardPetStoreDocumentShouldSucceed() { @@ -703,9 +691,8 @@ public void ParseStandardPetStoreDocumentShouldSucceed() actual.Should().BeEquivalentTo(expected); } - var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); + context.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); } [Fact] @@ -1237,9 +1224,8 @@ public void ParseModifiedPetStoreDocumentWithTagAndSecurityShouldSucceed() actual.Should().BeEquivalentTo(expected, options => options.Excluding(m => m.Name == "HostDocument")); } - var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); + context.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); } [Fact] @@ -1254,9 +1240,8 @@ public void ParsePetStoreExpandedShouldSucceed() // TODO: Create the object in memory and compare with the one read from YAML file. } - var context2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(context.SpecificationVersion, context2.SpecificationVersion); + context.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); } [Fact] diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs index 63d7894c5..0101d9c6e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Readers.Tests/V3Tests/OpenApiSchemaTests.cs @@ -332,9 +332,8 @@ public void ParseBasicSchemaWithReferenceShouldSucceed() // Assert var components = openApiDoc.Components; - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); components.Should().BeEquivalentTo( new OpenApiComponents @@ -424,7 +423,7 @@ public void ParseBasicSchemaWithReferenceShouldSucceed() } } } - },options => options.Excluding(m => m.Name == "HostDocument")); + }, options => options.Excluding(m => m.Name == "HostDocument")); } } @@ -439,9 +438,8 @@ public void ParseAdvancedSchemaWithReferenceShouldSucceed() // Assert var components = openApiDoc.Components; - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); components.Should().BeEquivalentTo( new OpenApiComponents @@ -621,9 +619,8 @@ public void ParseSelfReferencingSchemaShouldNotStackOverflow() // Assert var components = openApiDoc.Components; - var diagnostic2 = new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }; - - Assert.Equal(diagnostic.SpecificationVersion, diagnostic2.SpecificationVersion); + diagnostic.Should().BeEquivalentTo( + new OpenApiDiagnostic() { SpecificationVersion = OpenApiSpecVersion.OpenApi3_0 }); var schemaExtension = new OpenApiSchema() { From ba7377748d9560b6fd8160ebfbb0bd66ed6d3db2 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 26 Aug 2022 12:33:05 +0300 Subject: [PATCH 34/44] Refactor hashing logic and add test cases --- .../Models/OpenApiDocument.cs | 33 +++++++++++++++---- .../Microsoft.OpenApi.Tests.csproj | 14 ++++++++ .../Models/OpenApiDocumentTests.cs | 30 ++++++++++++++++- .../Models/Samples/sampleDocument.yaml | 5 +++ .../sampleDocumentWithWhiteSpaces.yaml} | 6 ++-- 5 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocument.yaml rename test/{Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml => Microsoft.OpenApi.Tests/Models/Samples/sampleDocumentWithWhiteSpaces.yaml} (100%) diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index ccdb65092..09a183811 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -65,6 +65,11 @@ public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible /// public IDictionary Extensions { get; set; } = new Dictionary(); + /// + /// The unique hash code of the generated OpenAPI document + /// + public string HashCode => GenerateHashValue(this); + /// /// Parameter-less constructor /// @@ -379,21 +384,35 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference) } /// - /// Uses the stream input to generate the hash value of an OpenApi document + /// Takes in an OpenApi document instance and generates its hash value /// - /// Stream containing OpenAPI description to hash. + /// The OpenAPI description to hash. /// The hash value. - public static string GenerateHashValue(Stream input) + public static string GenerateHashValue(OpenApiDocument doc) { HashAlgorithm sha = SHA512.Create(); - byte[] result = sha.ComputeHash(input); + using var memoryStream = new MemoryStream(); + + using var cryptoStream = new CryptoStream(memoryStream, sha, CryptoStreamMode.Write); + using var streamWriter = new StreamWriter(cryptoStream); + + var openApiJsonWriter = new OpenApiJsonWriter(streamWriter, new OpenApiJsonWriterSettings { Terse = true }); + doc.SerializeAsV3(openApiJsonWriter); + openApiJsonWriter.Flush(); + var hash = memoryStream.ToArray(); + + return ConvertByteArrayToString(hash); + } + + private static string ConvertByteArrayToString(byte[] hash) + { // Build the final string by converting each byte // into hex and appending it to a StringBuilder StringBuilder sb = new StringBuilder(); - for (int i = 0; i < result.Length; i++) + for (int i = 0; i < hash.Length; i++) { - sb.Append(result[i].ToString("X2")); + sb.Append(hash[i].ToString("X2")); } return sb.ToString(); diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index a6ba76259..872447cc9 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -36,6 +36,20 @@ + + Always + + + Always + + + + + + + + + \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs index 10cadd597..cd4cc2b5a 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiDocumentTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System; @@ -10,6 +10,7 @@ using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Writers; using VerifyXunit; using Xunit; @@ -1314,5 +1315,32 @@ public void SerializeRelativeRootPathWithHostAsV2JsonWorks() actual.Should().Be(expected); } + [Fact] + public void TestHashCodesForSimilarOpenApiDocuments() + { + // Arrange + var sampleFolderPath = "Models/Samples/"; + + var doc1 = ParseInputFile(Path.Combine(sampleFolderPath, "sampleDocument.yaml")); + var doc2 = ParseInputFile(Path.Combine(sampleFolderPath, "sampleDocument.yaml")); + var doc3 = ParseInputFile(Path.Combine(sampleFolderPath, "sampleDocumentWithWhiteSpaces.yaml")); + + // Act && Assert + /* + Test whether reading in two similar documents yield the same hash code, + And reading in similar documents(one has a whitespace) yields the same hash code as the result is terse + */ + Assert.True(doc1.HashCode != null && doc2.HashCode != null && doc1.HashCode.Equals(doc2.HashCode)); + Assert.Equal(doc1.HashCode, doc3.HashCode); + } + + private static OpenApiDocument ParseInputFile(string filePath) + { + // Read in the input yaml file + using FileStream stream = File.OpenRead(filePath); + var openApiDoc = new OpenApiStreamReader().Read(stream, out var diagnostic); + + return openApiDoc; + } } } diff --git a/test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocument.yaml b/test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocument.yaml new file mode 100644 index 000000000..34153a5f5 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocument.yaml @@ -0,0 +1,5 @@ +openapi : 3.0.0 +info: + title: Simple Document + version: 0.9.1 +paths: {} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml b/test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocumentWithWhiteSpaces.yaml similarity index 100% rename from test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml rename to test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocumentWithWhiteSpaces.yaml index a68eb2fee..5f31baa0e 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/V3Tests/Samples/OpenApiDocument/minimalDocumentWithWhitespace.yaml +++ b/test/Microsoft.OpenApi.Tests/Models/Samples/sampleDocumentWithWhiteSpaces.yaml @@ -1,9 +1,9 @@ openapi : 3.0.0 + info: title: Simple Document - version: 0.9.1 -paths: {} - + version: 0.9.1 +paths: {} From 2bb383cd0630fd86967fb08aa48238a7c6ecd59a Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 26 Aug 2022 12:54:26 +0300 Subject: [PATCH 35/44] Update public API interface --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index b320f8b10..823ddab81 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1,7 +1,7 @@ [assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Microsoft/OpenAPI.NET")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Readers.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName=".NET Standard 2.0")] namespace Microsoft.OpenApi.Any { public enum AnyType @@ -525,6 +525,7 @@ namespace Microsoft.OpenApi.Models public Microsoft.OpenApi.Models.OpenApiComponents Components { get; set; } public System.Collections.Generic.IDictionary Extensions { get; set; } public Microsoft.OpenApi.Models.OpenApiExternalDocs ExternalDocs { get; set; } + public string HashCode { get; } public Microsoft.OpenApi.Models.OpenApiInfo Info { get; set; } public Microsoft.OpenApi.Models.OpenApiPaths Paths { get; set; } public System.Collections.Generic.IList SecurityRequirements { get; set; } @@ -535,6 +536,7 @@ namespace Microsoft.OpenApi.Models public System.Collections.Generic.IEnumerable ResolveReferences() { } public void SerializeAsV2(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } public void SerializeAsV3(Microsoft.OpenApi.Writers.IOpenApiWriter writer) { } + public static string GenerateHashValue(Microsoft.OpenApi.Models.OpenApiDocument doc) { } } public class OpenApiEncoding : Microsoft.OpenApi.Interfaces.IOpenApiElement, Microsoft.OpenApi.Interfaces.IOpenApiExtensible, Microsoft.OpenApi.Interfaces.IOpenApiSerializable { From feebffc73295355c80588e4d805a20fb4d1ce5ff Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 26 Aug 2022 13:04:57 +0300 Subject: [PATCH 36/44] Remove framework display name from interface --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt index 823ddab81..745d91d43 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1,7 +1,7 @@ [assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/Microsoft/OpenAPI.NET")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Readers.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Microsoft.OpenApi.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100957cb48387b2a5f54f5ce39255f18f26d32a39990db27cf48737afc6bc62759ba996b8a2bfb675d4e39f3d06ecb55a178b1b4031dcb2a767e29977d88cce864a0d16bfc1b3bebb0edf9fe285f10fffc0a85f93d664fa05af07faa3aad2e545182dbf787e3fd32b56aca95df1a3c4e75dec164a3f1a4c653d971b01ffc39eb3c4")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName=".NET Standard 2.0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName="")] namespace Microsoft.OpenApi.Any { public enum AnyType From 25395760922737cef9efd8c54f55af26bb16ca67 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 26 Aug 2022 16:28:10 +0300 Subject: [PATCH 37/44] Address PR feedback --- src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs | 3 --- src/Microsoft.OpenApi/Models/OpenApiDocument.cs | 9 ++++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs index f6c9e7613..13bdbdef8 100644 --- a/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs +++ b/src/Microsoft.OpenApi.Readers/OpenApiStreamReader.cs @@ -3,8 +3,6 @@ using System; using System.IO; -using System.Security.Cryptography; -using System.Text; using System.Threading.Tasks; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; @@ -44,7 +42,6 @@ public OpenApiDocument Read(Stream input, out OpenApiDiagnostic diagnostic) { var reader = new StreamReader(input); var result = new OpenApiTextReaderReader(_settings).Read(reader, out diagnostic); - if (!_settings.LeaveStreamOpen) { reader.Dispose(); diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 09a183811..836e45dd8 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -390,17 +390,16 @@ public IOpenApiReferenceable ResolveReference(OpenApiReference reference) /// The hash value. public static string GenerateHashValue(OpenApiDocument doc) { - HashAlgorithm sha = SHA512.Create(); - using var memoryStream = new MemoryStream(); - - using var cryptoStream = new CryptoStream(memoryStream, sha, CryptoStreamMode.Write); + using HashAlgorithm sha = SHA512.Create(); + using var cryptoStream = new CryptoStream(Stream.Null, sha, CryptoStreamMode.Write); using var streamWriter = new StreamWriter(cryptoStream); var openApiJsonWriter = new OpenApiJsonWriter(streamWriter, new OpenApiJsonWriterSettings { Terse = true }); doc.SerializeAsV3(openApiJsonWriter); openApiJsonWriter.Flush(); - var hash = memoryStream.ToArray(); + cryptoStream.FlushFinalBlock(); + var hash = sha.Hash; return ConvertByteArrayToString(hash); } From 6d7f73592af60e598a322d37db07259691986954 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 29 Aug 2022 10:40:52 +0300 Subject: [PATCH 38/44] Update string comparison logic --- src/Microsoft.OpenApi/Models/OpenApiOperation.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index d2f8dec07..7983a243e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -253,9 +253,11 @@ public void SerializeAsV2(IOpenApiWriter writer) { foreach (var property in RequestBody.Content.First().Value.Schema.Properties) { - var paramName = property.Key; var paramSchema = property.Value; - if (paramSchema.Type == "string" && (paramSchema.Format == "binary" || paramSchema.Format == "base64")) { + if ("string".Equals(paramSchema.Type, StringComparison.OrdinalIgnoreCase) + && ("binary".Equals(paramSchema.Format, StringComparison.OrdinalIgnoreCase) + || "base64".Equals(paramSchema.Format, StringComparison.OrdinalIgnoreCase))) + { paramSchema.Type = "file"; paramSchema.Format = null; } From 5c6d17f7e2835c2a31aea9c550bd2a1462937e6f Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Mon, 29 Aug 2022 17:00:10 +0300 Subject: [PATCH 39/44] Attach changelog labels for improved release notes experience --- .azure-pipelines/ci-build.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 3e1b04fca..2f1b6b9b5 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -285,6 +285,12 @@ stages: releaseNotesSource: inline assets: '$(Pipeline.Workspace)\**\*.exe' changeLogType: issueBased + changeLogLabels: '[ + { "label" : "feature-work", "feature", "displayName" : "New Features", "state" : "closed" }, + { "label" : "enhancement", "V2-Enhancement", "displayName" : "Enhancements", "state" : "closed" }, + { "label" : "bug", "bug-fix", "displayName" : "Bugs", "state" : "closed" }, + { "label" : "documentation", "doc", "displayName" : "Documentation", "state" : "closed"}, + { "label" : "dependencies", "displayName" : "Package Updates", "state" : "closed" }]' - deployment: deploy_lib dependsOn: [] From f0a3fde93ebe7c3e23c6aba41fabb7c1ef3665f7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Aug 2022 21:14:34 +0000 Subject: [PATCH 40/44] Bump Microsoft.NET.Test.Sdk from 17.3.0 to 17.3.1 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.3.0 to 17.3.1. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v17.3.0...v17.3.1) --- updated-dependencies: - dependency-name: Microsoft.NET.Test.Sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../Microsoft.OpenApi.Hidi.Tests.csproj | 2 +- .../Microsoft.OpenApi.Readers.Tests.csproj | 2 +- .../Microsoft.OpenApi.SmokeTests.csproj | 2 +- test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj b/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj index 6045d85b6..084738bac 100644 --- a/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj +++ b/Microsoft.OpenApi.Hidi.Tests/Microsoft.OpenApi.Hidi.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj index 94432db9a..63b7a2a1f 100644 --- a/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj +++ b/test/Microsoft.OpenApi.Readers.Tests/Microsoft.OpenApi.Readers.Tests.csproj @@ -250,7 +250,7 @@ - + diff --git a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj index ef9886bb9..114ba749c 100644 --- a/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj +++ b/test/Microsoft.OpenApi.SmokeTests/Microsoft.OpenApi.SmokeTests.csproj @@ -8,7 +8,7 @@ - + diff --git a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj index 872447cc9..5b476500c 100644 --- a/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj +++ b/test/Microsoft.OpenApi.Tests/Microsoft.OpenApi.Tests.csproj @@ -16,7 +16,7 @@ - + From 4ecd027f3ffffdbd224c5a99268a4483ff48ad4f Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 1 Sep 2022 20:12:38 +0300 Subject: [PATCH 41/44] Add child schema format to parent schema when serializing as V2 --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 6 ++ .../Models/OpenApiSchemaTests.cs | 63 +++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 7482b6baf..95fe873ab 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -707,6 +707,12 @@ internal void WriteAsSchemaProperties( // oneOf (Not Supported in V2) - Write the first schema only as an allOf. writer.WriteOptionalCollection(OpenApiConstants.AllOf, OneOf?.Take(1), (w, s) => s.SerializeAsV2(w)); } + if (OneOf?.Count > 0) + { + // Take the format and set it at the root + var oneOfFormat = OneOf.Select(x => x.Format.ToString()).FirstOrDefault(); + this.Format = oneOfFormat; + } } // properties diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index ae13944e6..26e8df5f8 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -422,5 +422,68 @@ public async Task SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync(bool prod // Assert await Verifier.Verify(actual).UseParameters(produceTerseOutput); } + + [Fact] + public void SerializeSchemaPrimitiveTypeShouldRemoveFormatInRootIfPresentInChildrenSchema() + { + // Arrange + var schema = new OpenApiSchema() + { + OneOf = new List + { + new OpenApiSchema + { + Type = "number", + Format = "decimal" + }, + new OpenApiSchema { Type = "string" }, + } + }; + + var outputStringWriter = new StringWriter(CultureInfo.InvariantCulture); + var openApiJsonWriter = new OpenApiJsonWriter(outputStringWriter, new OpenApiJsonWriterSettings { Terse = false }); + + // Act + // Serialize as V2 + schema.SerializeAsV2(openApiJsonWriter); + openApiJsonWriter.Flush(); + + var v2Schema = outputStringWriter.GetStringBuilder().ToString();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n",""); + + // Serialize as V3 + //schema.SerializeAsV3(openApiJsonWriter); + //openApiJsonWriter.Flush(); + + //var v3Schema = outputStringWriter.GetStringBuilder().ToString();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n", ""); + + var expectedV2Schema = @"{ +""allOf"": [ +{ + ""format"": ""decimal"", + ""type"": ""number"" +}], +""format"": ""decimal"" +}".Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n",""); + + + var expectedV3Schema = @"{ +""allOf"": [ +{ + ""format"": ""decimal"", + ""type"": ""number"" +}]} +{""oneOf"": [ +{ + ""type"": ""number"", + ""format"": ""decimal"" +}, +{""type"" : ""string""} + +]}}";//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n", ""); + + // Assert + Assert.Equal(expectedV2Schema, v2Schema); // Assert that v2 schema has the root schema Format defined + //Assert.Equal(expectedV3Schema, v3Schema); + } } } From 00fe8db93bce4e449af3b505dfeaf8d2cd0d05ee Mon Sep 17 00:00:00 2001 From: Irvine Date: Thu, 1 Sep 2022 21:21:40 +0300 Subject: [PATCH 42/44] Retrieve Format property --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 14 ++++++-------- .../Models/OpenApiSchemaTests.cs | 17 +++++++++-------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index 95fe873ab..c3b36c4f2 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. using System.Collections.Generic; @@ -630,6 +630,10 @@ internal void WriteAsSchemaProperties( } // format + Format ??= AllOf?.FirstOrDefault(x => x.Format != null)?.Format ?? + AnyOf?.FirstOrDefault(x => x.Format != null)?.Format ?? + OneOf?.FirstOrDefault(x => x.Format != null)?.Format; + writer.WriteProperty(OpenApiConstants.Format, Format); // title @@ -695,7 +699,7 @@ internal void WriteAsSchemaProperties( // allOf writer.WriteOptionalCollection(OpenApiConstants.AllOf, AllOf, (w, s) => s.SerializeAsV2(w)); - // If there isn't already an AllOf, and the schema contains a oneOf or anyOf write an allOf with the first + // If there isn't already an allOf, and the schema contains a oneOf or anyOf write an allOf with the first // schema in the list as an attempt to guess at a graceful downgrade situation. if (AllOf == null || AllOf.Count == 0) { @@ -707,12 +711,6 @@ internal void WriteAsSchemaProperties( // oneOf (Not Supported in V2) - Write the first schema only as an allOf. writer.WriteOptionalCollection(OpenApiConstants.AllOf, OneOf?.Take(1), (w, s) => s.SerializeAsV2(w)); } - if (OneOf?.Count > 0) - { - // Take the format and set it at the root - var oneOfFormat = OneOf.Select(x => x.Format.ToString()).FirstOrDefault(); - this.Format = oneOfFormat; - } } // properties diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index 26e8df5f8..a2e991e75 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -448,7 +448,7 @@ public void SerializeSchemaPrimitiveTypeShouldRemoveFormatInRootIfPresentInChild schema.SerializeAsV2(openApiJsonWriter); openApiJsonWriter.Flush(); - var v2Schema = outputStringWriter.GetStringBuilder().ToString();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n",""); + var v2Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n",""); // Serialize as V3 //schema.SerializeAsV3(openApiJsonWriter); @@ -457,13 +457,14 @@ public void SerializeSchemaPrimitiveTypeShouldRemoveFormatInRootIfPresentInChild //var v3Schema = outputStringWriter.GetStringBuilder().ToString();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n", ""); var expectedV2Schema = @"{ -""allOf"": [ -{ - ""format"": ""decimal"", - ""type"": ""number"" -}], -""format"": ""decimal"" -}".Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n",""); + ""format"": ""decimal"", + ""allOf"": [ + { + ""format"": ""decimal"", + ""type"": ""number"" + } + ] +}".MakeLineBreaksEnvironmentNeutral(); var expectedV3Schema = @"{ From 0818fb742bca9c330dcd066b37aed8c0288b7ac7 Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Thu, 1 Sep 2022 23:27:34 +0300 Subject: [PATCH 43/44] Clean up code --- src/Microsoft.OpenApi/Models/OpenApiSchema.cs | 6 ++-- .../Models/OpenApiSchemaTests.cs | 29 ++----------------- 2 files changed, 6 insertions(+), 29 deletions(-) diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index c3b36c4f2..8734c19a2 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -630,9 +630,9 @@ internal void WriteAsSchemaProperties( } // format - Format ??= AllOf?.FirstOrDefault(x => x.Format != null)?.Format ?? - AnyOf?.FirstOrDefault(x => x.Format != null)?.Format ?? - OneOf?.FirstOrDefault(x => x.Format != null)?.Format; + Format ??= AllOf?.FirstOrDefault(static x => x.Format != null)?.Format ?? + AnyOf?.FirstOrDefault(static x => x.Format != null)?.Format ?? + OneOf?.FirstOrDefault(static x => x.Format != null)?.Format; writer.WriteProperty(OpenApiConstants.Format, Format); diff --git a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs index a2e991e75..429129c1e 100644 --- a/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs +++ b/test/Microsoft.OpenApi.Tests/Models/OpenApiSchemaTests.cs @@ -424,7 +424,7 @@ public async Task SerializeSchemaWRequiredPropertiesAsV2JsonWorksAsync(bool prod } [Fact] - public void SerializeSchemaPrimitiveTypeShouldRemoveFormatInRootIfPresentInChildrenSchema() + public void SerializeAsV2ShouldSetFormatPropertyInParentSchemaIfPresentInChildrenSchema() { // Arrange var schema = new OpenApiSchema() @@ -448,13 +448,7 @@ public void SerializeSchemaPrimitiveTypeShouldRemoveFormatInRootIfPresentInChild schema.SerializeAsV2(openApiJsonWriter); openApiJsonWriter.Flush(); - var v2Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n",""); - - // Serialize as V3 - //schema.SerializeAsV3(openApiJsonWriter); - //openApiJsonWriter.Flush(); - - //var v3Schema = outputStringWriter.GetStringBuilder().ToString();//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n", ""); + var v2Schema = outputStringWriter.GetStringBuilder().ToString().MakeLineBreaksEnvironmentNeutral(); var expectedV2Schema = @"{ ""format"": ""decimal"", @@ -466,25 +460,8 @@ public void SerializeSchemaPrimitiveTypeShouldRemoveFormatInRootIfPresentInChild ] }".MakeLineBreaksEnvironmentNeutral(); - - var expectedV3Schema = @"{ -""allOf"": [ -{ - ""format"": ""decimal"", - ""type"": ""number"" -}]} -{""oneOf"": [ -{ - ""type"": ""number"", - ""format"": ""decimal"" -}, -{""type"" : ""string""} - -]}}";//.Replace(Environment.NewLine, "").Replace(" ", "").Replace("\n", ""); - // Assert - Assert.Equal(expectedV2Schema, v2Schema); // Assert that v2 schema has the root schema Format defined - //Assert.Equal(expectedV3Schema, v3Schema); + Assert.Equal(expectedV2Schema, v2Schema); } } } From 8cec362ceeb7f59aaaffc0d7ca70f08cc0ed806d Mon Sep 17 00:00:00 2001 From: Maggie Kimani Date: Fri, 2 Sep 2022 14:57:01 +0300 Subject: [PATCH 44/44] Bump lib versions --- src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj | 2 +- src/Microsoft.OpenApi/Microsoft.OpenApi.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj index 94d74046d..9ca5f6cfe 100644 --- a/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj +++ b/src/Microsoft.OpenApi.Readers/Microsoft.OpenApi.Readers.csproj @@ -10,7 +10,7 @@ Microsoft Microsoft.OpenApi.Readers Microsoft.OpenApi.Readers - 1.4.0-preview2 + 1.4.0-preview3 OpenAPI.NET Readers for JSON and YAML documents © Microsoft Corporation. All rights reserved. OpenAPI .NET diff --git a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj index a768312e6..1bbfcb987 100644 --- a/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj +++ b/src/Microsoft.OpenApi/Microsoft.OpenApi.csproj @@ -11,7 +11,7 @@ Microsoft Microsoft.OpenApi Microsoft.OpenApi - 1.4.0-preview2 + 1.4.0-preview3 .NET models with JSON and YAML writers for OpenAPI specification © Microsoft Corporation. All rights reserved. OpenAPI .NET