From 35ffbbc7d9bdefffd9fa4803d0e759824b588eb7 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 28 May 2023 23:10:09 +0200 Subject: [PATCH] RegexExampleValueGenerator --- .../Program.cs | 2 +- .../Run.cs | 4 +- .../Mappers/OpenApiPathsMapper.cs | 101 +++++++++--------- .../Types/ExampleValueType.cs | 7 +- .../Utils/RegexExampleValueGenerator.cs | 53 +++++++++ .../Server/WireMockServer.OpenApiParser.cs | 24 ++++- 6 files changed, 133 insertions(+), 58 deletions(-) create mode 100644 src/WireMock.Net.OpenApiParser/Utils/RegexExampleValueGenerator.cs diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs index 30b8b23c..fa4e9293 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs @@ -18,7 +18,7 @@ class Program private static void RunMockServerWithDynamicExampleGeneration() { // Run your mocking framework specifying your Example Values generator class. - var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), Types.ExampleValueType.Value, Types.ExampleValueType.Value); + var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), Types.PatternType.Value, Types.PatternType.Value); Console.WriteLine("Press any key to stop the servers"); Console.ReadKey(); diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs index 4ff3f775..f8eb7c81 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs @@ -18,8 +18,8 @@ public static class Run string url, bool dynamicExamples = true, IWireMockOpenApiParserExampleValues? examplesValuesGenerator = null, - ExampleValueType pathPatternToUse = ExampleValueType.Wildcard, - ExampleValueType headerPatternToUse = ExampleValueType.Wildcard + PatternType pathPatternToUse = PatternType.Wildcard, + PatternType headerPatternToUse = PatternType.Wildcard ) { var server = WireMockServer.Start(new WireMockServerSettings diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs index 09a5ae4c..fdb403cf 100644 --- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs +++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs @@ -37,18 +37,7 @@ internal class OpenApiPathsMapper .OrderBy(p => p.Key) .Select(p => MapPath(p.Key, p.Value, servers)) .SelectMany(x => x) - .ToArray() ?? - Array.Empty(); - } - - private IReadOnlyList MapPaths(OpenApiPaths? paths, IList servers) - { - return paths? - .OrderBy(p => p.Key) - .Select(p => MapPath(p.Key, p.Value, servers)) - .SelectMany(x => x) - .ToArray() ?? - Array.Empty(); + .ToArray() ?? Array.Empty(); } private IReadOnlyList MapPath(string path, OpenApiPathItem pathItem, IList servers) @@ -280,41 +269,6 @@ internal class OpenApiPathsMapper return newPath; } - private string MapBasePath(IList? servers) - { - if (servers == null || servers.Count == 0) - { - return string.Empty; - } - - OpenApiServer server = servers.First(); - if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult)) - { - return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString(); - } - - return string.Empty; - } - - private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any) - { - if (any == null) - { - return null; - } - - using var outputString = new StringWriter(); - var writer = new OpenApiJsonWriter(outputString); - any.Write(writer, OpenApiSpecVersion.OpenApi3_0); - - if (any.AnyType == AnyType.Array) - { - return JArray.Parse(outputString.ToString()); - } - - return JObject.Parse(outputString.ToString()); - } - private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers) { var mappedHeaders = headers?.ToDictionary( @@ -366,26 +320,34 @@ internal class OpenApiPathsMapper return list.Any() ? list : null; } - private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type) + private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType exampleValueType) { - return type switch + return exampleValueType switch { ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", - Pattern = GetExampleValueAsStringForSchemaType(schema), + Pattern = GetExampleValueAsStringForSchemaType(schema, exampleValueType), + IgnoreCase = _settings.IgnoreCaseExampleValues + }, + + ExampleValueType.Regex => new MatcherModel + { + Name = "RegexMatcher", + Pattern = GetExampleValueAsStringForSchemaType(schema, exampleValueType), IgnoreCase = _settings.IgnoreCaseExampleValues }, _ => new MatcherModel { Name = "WildcardMatcher", - Pattern = "*" + Pattern = "*", + IgnoreCase = _settings.IgnoreCaseExampleValues } }; } - private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema) + private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema, ExampleValueType exampleValueType) { var value = _exampleValueGenerator.GetExampleValue(schema); @@ -396,4 +358,39 @@ internal class OpenApiPathsMapper _ => value.ToString(), }; } + + private static string MapBasePath(IList? servers) + { + if (servers == null || servers.Count == 0) + { + return string.Empty; + } + + var server = servers.First(); + if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult)) + { + return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString(); + } + + return string.Empty; + } + + private static JToken? MapOpenApiAnyToJToken(IOpenApiAny? any) + { + if (any == null) + { + return null; + } + + using var outputString = new StringWriter(); + var writer = new OpenApiJsonWriter(outputString); + any.Write(writer, OpenApiSpecVersion.OpenApi3_0); + + if (any.AnyType == AnyType.Array) + { + return JArray.Parse(outputString.ToString()); + } + + return JObject.Parse(outputString.ToString()); + } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs b/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs index aabbfca9..7dbd8fde 100644 --- a/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs +++ b/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs @@ -1,7 +1,7 @@ namespace WireMock.Net.OpenApiParser.Types; /// -/// The example value to use +/// The (example) value pattern to use. /// public enum ExampleValueType { @@ -12,6 +12,11 @@ public enum ExampleValueType /// Value, + /// + /// Build a Regex based on the SchemaType. + /// + Regex, + /// /// Just use a Wildcard (*) character. /// diff --git a/src/WireMock.Net.OpenApiParser/Utils/RegexExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser/Utils/RegexExampleValueGenerator.cs new file mode 100644 index 00000000..a21b6def --- /dev/null +++ b/src/WireMock.Net.OpenApiParser/Utils/RegexExampleValueGenerator.cs @@ -0,0 +1,53 @@ +using System.Linq; +using Microsoft.OpenApi.Models; +using Stef.Validation; +using WireMock.Net.OpenApiParser.Extensions; +using WireMock.Net.OpenApiParser.Settings; +using WireMock.Net.OpenApiParser.Types; + +namespace WireMock.Net.OpenApiParser.Utils; + +internal class RegexExampleValueGenerator +{ + public RegexExampleValueGenerator(WireMockOpenApiParserSettings settings) + { + Guard.NotNull(settings); + } + + public string GetExampleValue(OpenApiSchema? schema) + { + var schemaExample = schema?.Example; + var schemaEnum = schema?.Enum?.FirstOrDefault(); + + switch (schema?.GetSchemaType()) + { + case SchemaType.Boolean: + return @"^(true|false)$"; + + case SchemaType.Integer: + return @"^-?\d+$"; + + case SchemaType.Number: + return @"^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$"; + + default: + switch (schema?.GetSchemaFormat()) + { + case SchemaFormat.Date: + return @"^(\d{4})-([01]\d)-([0-3]\d)$"; + + case SchemaFormat.DateTime: + return @"^(\d{4})-([01]\d)-([0-3]\d)T([0-2]\d):([0-5]\d):([0-5]\d)(\.\d+)?(Z|[+-][0-2]\d:[0-5]\d)$"; + + case SchemaFormat.Byte: + return @"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + + case SchemaFormat.Binary: + return @"^[a-zA-Z0-9\+/]*={0,3}$"; + + default: + return ".*"; + } + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs index 38284c61..a6da70dd 100644 --- a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -2,20 +2,39 @@ using System; using System.Linq; using System.Net; +using Newtonsoft.Json; using WireMock.Net.OpenApiParser; +using WireMock.Net.OpenApiParser.Settings; +using WireMock.Net.OpenApiParser.Types; +using WireMock.Serialization; #endif namespace WireMock.Server; public partial class WireMockServer { +#if OPENAPIPARSER + private readonly WireMockOpenApiParserSettings _openApiParserSettings = new WireMockOpenApiParserSettings + { + PathPatternToUse = ExampleValueType.Regex, + HeaderPatternToUse = ExampleValueType.Regex, + QueryParameterPatternToUse = ExampleValueType.Regex + }; +#endif + private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage) { #if OPENAPIPARSER try { var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); - return diagnostic.Errors.Any() ? ToJson(diagnostic, false, HttpStatusCode.BadRequest) : ToJson(mappingModels); + if (diagnostic.Errors.Any()) + { + var diagnosticAsJson = JsonConvert.SerializeObject(diagnostic, JsonSerializationConstants.JsonSerializerSettingsDefault); + _settings.Logger.Warn("OpenApiError(s) while converting OpenAPI specification to MappingModel(s) : {0}", diagnosticAsJson); + } + + return ToJson(mappingModels); } catch (Exception e) { @@ -35,7 +54,8 @@ public partial class WireMockServer var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); if (diagnostic.Errors.Any()) { - return ToJson(diagnostic, false, HttpStatusCode.BadRequest); + var diagnosticAsJson = JsonConvert.SerializeObject(diagnostic, JsonSerializationConstants.JsonSerializerSettingsDefault); + _settings.Logger.Warn("OpenApiError(s) while converting OpenAPI specification to MappingModel(s) : {0}", diagnosticAsJson); } ConvertMappingsAndRegisterAsRespondProvider(mappingModels);