From e6535f7380e424668163f812da80d8e8c270b649 Mon Sep 17 00:00:00 2001 From: Fabian Niederer Date: Tue, 28 Apr 2020 11:09:22 +0200 Subject: [PATCH 1/6] added validation & renabled OpenApiPaths validators --- .../Properties/SRResource.Designer.cs | 11 ++++- .../Properties/SRResource.resx | 3 ++ .../Services/OpenApiWalker.cs | 2 + .../Validations/OpenApiValidator.cs | 6 +++ .../Validations/Rules/OpenApiPathsRules.cs | 26 ++++++++++ .../OpenApiPathsValidationTests.cs | 49 +++++++++++++++++++ .../Validations/ValidationRuleSetTests.cs | 2 +- 7 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 47f9493b9..69cc96223 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -19,7 +19,7 @@ namespace Microsoft.OpenApi.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class SRResource { @@ -330,6 +330,15 @@ internal static string Validation_PathItemMustBeginWithSlash { } } + /// + /// Looks up a localized string similar to The path signature '{0}' MUST begin be unique.. + /// + internal static string Validation_PathSignatureMustBeUnique { + get { + return ResourceManager.GetString("Validation_PathSignatureMustBeUnique", resourceCulture); + } + } + /// /// Looks up a localized string similar to The same rule cannot be in the same rule set twice.. /// diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index 48f910a95..5e439868b 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -207,6 +207,9 @@ The path item name '{0}' MUST begin with a slash. + + The path signature '{0}' MUST begin be unique. + The same rule cannot be in the same rule set twice. diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index dd3c09564..6167d8d24 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -219,6 +219,7 @@ internal void Walk(OpenApiPaths paths) _visitor.CurrentKeys.Path = null; } } + } /// @@ -1036,6 +1037,7 @@ internal void Walk(IOpenApiElement element) case OpenApiOAuthFlow e: Walk(e); break; case OpenApiOperation e: Walk(e); break; case OpenApiParameter e: Walk(e); break; + case OpenApiPaths e: Walk(e); break; case OpenApiRequestBody e: Walk(e); break; case OpenApiResponse e: Walk(e); break; case OpenApiSchema e: Walk(e); break; diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs index 69bcda93d..e6edd8fc7 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs @@ -131,6 +131,12 @@ public void AddError(OpenApiValidatorError error) /// The object to be validated public override void Visit(OpenApiParameter item) => Validate(item); + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiPaths item) => Validate(item); + /// /// Execute validation rules against an /// diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index 7ac374b62..5e5424870 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System.Collections.Generic; +using System.Text.RegularExpressions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; @@ -34,6 +36,30 @@ public static class OpenApiPathsRules } }); + /// + /// A relative path to an individual endpoint. The field name MUST begin with a slash. + /// + public static ValidationRule PathMustBeUnique => + new ValidationRule( + (context, item) => + { + const string regexPath = "\\{([^/]+)\\}"; + var hashSet = new HashSet(); + + foreach (var path in item.Keys) + { + context.Enter(path); + + var pathSignature = Regex.Replace(path, regexPath, "{}"); + + if (!hashSet.Add(pathSignature)) + context.CreateError(nameof(PathMustBeUnique), + string.Format(SRResource.Validation_PathSignatureMustBeUnique, pathSignature)); + + context.Exit(); + } + }); + // add more rules } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs new file mode 100644 index 000000000..23a0a3e0f --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiPathsValidationTests.cs @@ -0,0 +1,49 @@ +using System.Linq; +using FluentAssertions; +using Microsoft.OpenApi.Extensions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; +using Xunit; + +namespace Microsoft.OpenApi.Validations.Tests +{ + public class OpenApiPathsValidationTests + { + [Fact] + public void ValidatePathsMustBeginWithSlash() + { + // Arrange + var error = string.Format(SRResource.Validation_PathItemMustBeginWithSlash, "pets/{petId}"); + var paths = new OpenApiPaths + { + {"pets/{petId}",new OpenApiPathItem() } + }; + + // Act + var errors = paths.Validate(ValidationRuleSet.GetDefaultRuleSet()); + + // Assert + errors.Should().NotBeEmpty(); + errors.Select(e => e.Message).Should().BeEquivalentTo(error); + } + + [Fact] + public void ValidatePathsAreUnique() + { + // Arrange + var error = string.Format(SRResource.Validation_PathSignatureMustBeUnique, "/pets/{}"); + var paths = new OpenApiPaths + { + {"/pets/{petId}",new OpenApiPathItem() }, + {"/pets/{name}",new OpenApiPathItem() } + }; + + // Act + var errors = paths.Validate(ValidationRuleSet.GetDefaultRuleSet()); + + // Assert + errors.Should().NotBeEmpty(); + errors.Select(e => e.Message).Should().BeEquivalentTo(error); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs index af259cf1b..8153e6054 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs @@ -43,7 +43,7 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules() Assert.NotEmpty(rules); // Update the number if you add new default rule(s). - Assert.Equal(21, rules.Count); + Assert.Equal(22, rules.Count); } } } From 6bf85c5bd09203f8fcf4e6d8111b83cddeb306cc Mon Sep 17 00:00:00 2001 From: Darrel Date: Tue, 28 Jul 2020 22:59:08 -0400 Subject: [PATCH 2/6] Update src/Microsoft.OpenApi/Properties/SRResource.resx --- src/Microsoft.OpenApi/Properties/SRResource.resx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index 5e439868b..8039212f9 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -208,7 +208,7 @@ The path item name '{0}' MUST begin with a slash. - The path signature '{0}' MUST begin be unique. + The path signature '{0}' MUST be unique. The same rule cannot be in the same rule set twice. @@ -219,4 +219,4 @@ The string '{0}' MUST be in the format of an email address. - \ No newline at end of file + From 9429da4f1ff62fca498892d37f993267c916a471 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 24 Oct 2023 15:41:44 -0400 Subject: [PATCH 3/6] - cleans up duplicated method Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Validations/OpenApiValidator.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs index 6678b3282..8e9ed4fc4 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs @@ -137,12 +137,6 @@ public void AddWarning(OpenApiValidatorWarning warning) /// The object to be validated public override void Visit(OpenApiParameter item) => Validate(item); - /// - /// Execute validation rules against an - /// - /// The object to be validated - public override void Visit(OpenApiPaths item) => Validate(item); - /// /// Execute validation rules against an /// From 6bda890fc7f19db283123a7aa4327b2cc39bc99e Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 24 Oct 2023 15:44:01 -0400 Subject: [PATCH 4/6] - makes regex static for performance Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index 5fd200100..37587674b 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using System.Text.RegularExpressions; using Microsoft.OpenApi.Models; @@ -35,6 +36,7 @@ public static class OpenApiPathsRules } }); + private static readonly Regex regexPath = new Regex("\\{([^/]+)\\}", RegexOptions.Compiled, TimeSpan.FromMilliseconds(100)); /// /// A relative path to an individual endpoint. The field name MUST begin with a slash. /// @@ -42,14 +44,13 @@ public static class OpenApiPathsRules new ValidationRule( (context, item) => { - const string regexPath = "\\{([^/]+)\\}"; var hashSet = new HashSet(); foreach (var path in item.Keys) { context.Enter(path); - var pathSignature = Regex.Replace(path, regexPath, "{}"); + var pathSignature = regexPath.Replace(path, "{}"); if (!hashSet.Add(pathSignature)) context.CreateError(nameof(PathMustBeUnique), From da035fad3bd418628bab2969cb01fec280342cf8 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 24 Oct 2023 15:45:54 -0400 Subject: [PATCH 5/6] - fixes string comparison Signed-off-by: Vincent Biret --- src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index 37587674b..c25ca8aff 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -26,7 +26,7 @@ public static class OpenApiPathsRules { context.Enter(pathName); - if (pathName == null || !pathName.StartsWith("/")) + if (pathName == null || !pathName.StartsWith("/", StringComparison.OrdinalIgnoreCase)) { context.CreateError(nameof(PathNameMustBeginWithSlash), string.Format(SRResource.Validation_PathItemMustBeginWithSlash, pathName)); From 5140bafd7ebda60ad97126b66aa8df3afb9eb111 Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 24 Oct 2023 15:47:32 -0400 Subject: [PATCH 6/6] - fixes unit tests Signed-off-by: Vincent Biret --- test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt | 1 + .../Validations/ValidationRuleSetTests.cs | 2 +- 2 files 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 b6828dff7..a5309e46d 100755 --- a/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt +++ b/test/Microsoft.OpenApi.Tests/PublicApi/PublicApi.approved.txt @@ -1431,6 +1431,7 @@ namespace Microsoft.OpenApi.Validations.Rules [Microsoft.OpenApi.Validations.Rules.OpenApiRule] public static class OpenApiPathsRules { + public static Microsoft.OpenApi.Validations.ValidationRule PathMustBeUnique { get; } public static Microsoft.OpenApi.Validations.ValidationRule PathNameMustBeginWithSlash { get; } } [Microsoft.OpenApi.Validations.Rules.OpenApiRule] diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs index fcafaab21..708d6ee64 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs @@ -35,7 +35,7 @@ public void DefaultRuleSetPropertyReturnsTheCorrectRules() Assert.NotEmpty(rules); // Update the number if you add new default rule(s). - Assert.Equal(22, rules.Count); + Assert.Equal(23, rules.Count); } } }