Compare commits

...

5 Commits

Author SHA1 Message Date
Stef Heyenrath
dabe3a2a10 Merge branch 'master' into stef-IgnoreOpenApiErrors 2023-06-03 17:01:15 +02:00
Stef Heyenrath
b609191095 . 2023-06-01 21:14:13 +02:00
Stef Heyenrath
b1ae9aaf46 Merge branch 'master' into stef-IgnoreOpenApiErrors 2023-06-01 21:04:21 +02:00
Stef Heyenrath
ad3ef83c5e . 2023-05-30 21:41:30 +02:00
Stef Heyenrath
35ffbbc7d9 RegexExampleValueGenerator 2023-05-28 23:10:09 +02:00
8 changed files with 144 additions and 61 deletions

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using WireMock.Net.OpenApiParser.Types;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
@@ -18,7 +19,7 @@ class Program
private static void RunMockServerWithDynamicExampleGeneration() private static void RunMockServerWithDynamicExampleGeneration()
{ {
// Run your mocking framework specifying your Example Values generator class. // 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(), ExampleValueType.Value, ExampleValueType.Value);
Console.WriteLine("Press any key to stop the servers"); Console.WriteLine("Press any key to stop the servers");
Console.ReadKey(); Console.ReadKey();

View File

@@ -23,12 +23,14 @@ internal class OpenApiPathsMapper
private const string HeaderContentType = "Content-Type"; private const string HeaderContentType = "Content-Type";
private readonly WireMockOpenApiParserSettings _settings; private readonly WireMockOpenApiParserSettings _settings;
private readonly ExampleValueGenerator _exampleValueGenerator; private readonly IExampleValueGenerator _exampleValueGenerator;
private readonly IExampleValueGenerator _regexExampleValueGenerator;
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings); _settings = Guard.NotNull(settings);
_exampleValueGenerator = new ExampleValueGenerator(settings); _exampleValueGenerator = new ExampleValueGenerator(settings);
_regexExampleValueGenerator = new RegexExampleValueGenerator(settings);
} }
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers) public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
@@ -37,18 +39,7 @@ internal class OpenApiPathsMapper
.OrderBy(p => p.Key) .OrderBy(p => p.Key)
.Select(p => MapPath(p.Key, p.Value, servers)) .Select(p => MapPath(p.Key, p.Value, servers))
.SelectMany(x => x) .SelectMany(x => x)
.ToArray() ?? .ToArray() ?? Array.Empty<MappingModel>();
Array.Empty<MappingModel>();
}
private IReadOnlyList<MappingModel> MapPaths(OpenApiPaths? paths, IList<OpenApiServer> servers)
{
return paths?
.OrderBy(p => p.Key)
.Select(p => MapPath(p.Key, p.Value, servers))
.SelectMany(x => x)
.ToArray() ??
Array.Empty<MappingModel>();
} }
private IReadOnlyList<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers) private IReadOnlyList<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
@@ -280,41 +271,6 @@ internal class OpenApiPathsMapper
return newPath; return newPath;
} }
private string MapBasePath(IList<OpenApiServer>? 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<string, object>? MapHeaders(string? responseContentType, IDictionary<string, OpenApiHeader>? headers) private IDictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, OpenApiHeader>? headers)
{ {
var mappedHeaders = headers?.ToDictionary( var mappedHeaders = headers?.ToDictionary(
@@ -366,28 +322,38 @@ internal class OpenApiPathsMapper
return list.Any() ? list : null; 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 ExampleValueType.Value => new MatcherModel
{ {
Name = "ExactMatcher", 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 IgnoreCase = _settings.IgnoreCaseExampleValues
}, },
_ => new MatcherModel _ => new MatcherModel
{ {
Name = "WildcardMatcher", Name = "WildcardMatcher",
Pattern = "*" Pattern = "*",
IgnoreCase = _settings.IgnoreCaseExampleValues
} }
}; };
} }
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema) private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema, ExampleValueType exampleValueType)
{ {
var value = _exampleValueGenerator.GetExampleValue(schema); var value = exampleValueType == ExampleValueType.Regex ?
_regexExampleValueGenerator.GetExampleValue(schema) :
_exampleValueGenerator.GetExampleValue(schema);
return value switch return value switch
{ {
@@ -396,4 +362,39 @@ internal class OpenApiPathsMapper
_ => value.ToString(), _ => value.ToString(),
}; };
} }
private static string MapBasePath(IList<OpenApiServer>? 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());
}
} }

View File

@@ -1,7 +1,7 @@
namespace WireMock.Net.OpenApiParser.Types; namespace WireMock.Net.OpenApiParser.Types;
/// <summary> /// <summary>
/// The example value to use /// The (example) value pattern to use.
/// </summary> /// </summary>
public enum ExampleValueType public enum ExampleValueType
{ {
@@ -12,6 +12,11 @@ public enum ExampleValueType
/// </summary> /// </summary>
Value, Value,
/// <summary>
/// Build a Regex based on the SchemaType.
/// </summary>
Regex,
/// <summary> /// <summary>
/// Just use a Wildcard (*) character. /// Just use a Wildcard (*) character.
/// </summary> /// </summary>

View File

@@ -8,7 +8,7 @@ using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Utils; namespace WireMock.Net.OpenApiParser.Utils;
internal class ExampleValueGenerator internal class ExampleValueGenerator : IExampleValueGenerator
{ {
private readonly IWireMockOpenApiParserExampleValues _exampleValues; private readonly IWireMockOpenApiParserExampleValues _exampleValues;

View File

@@ -0,0 +1,8 @@
using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Utils;
internal interface IExampleValueGenerator
{
object GetExampleValue(OpenApiSchema? schema);
}

View File

@@ -0,0 +1,40 @@
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 : IExampleValueGenerator
{
public RegexExampleValueGenerator(WireMockOpenApiParserSettings settings)
{
Guard.NotNull(settings);
}
public object GetExampleValue(OpenApiSchema? schema)
{
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:
return schema?.GetSchemaFormat() switch
{
SchemaFormat.Date => @"(\d{4})-([01]\d)-([0-3]\d)",
SchemaFormat.DateTime => @"(\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)",
SchemaFormat.Byte => @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",
SchemaFormat.Binary => @"[a-zA-Z0-9\+/]*={0,3}",
_ => ".*"
};
}
}
}

View File

@@ -9,6 +9,7 @@ using RamlToOpenApiConverter;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Mappers; using WireMock.Net.OpenApiParser.Mappers;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser; namespace WireMock.Net.OpenApiParser;
@@ -17,13 +18,20 @@ namespace WireMock.Net.OpenApiParser;
/// </summary> /// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser public class WireMockOpenApiParser : IWireMockOpenApiParser
{ {
private readonly WireMockOpenApiParserSettings _wireMockOpenApiParserSettings = new WireMockOpenApiParserSettings
{
HeaderPatternToUse = ExampleValueType.Regex,
QueryParameterPatternToUse = ExampleValueType.Regex,
PathPatternToUse = ExampleValueType.Regex
};
private readonly OpenApiStreamReader _reader = new(); private readonly OpenApiStreamReader _reader = new();
/// <inheritdoc /> /// <inheritdoc />
[PublicAPI] [PublicAPI]
public IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic) public IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
{ {
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic); return FromFile(path, _wireMockOpenApiParserSettings, out diagnostic);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -49,7 +57,7 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
[PublicAPI] [PublicAPI]
public IReadOnlyList<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null) public IReadOnlyList<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
{ {
return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); return new OpenApiPathsMapper(settings ?? _wireMockOpenApiParserSettings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@@ -2,20 +2,39 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using Newtonsoft.Json;
using WireMock.Net.OpenApiParser; using WireMock.Net.OpenApiParser;
using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
using WireMock.Serialization;
#endif #endif
namespace WireMock.Server; namespace WireMock.Server;
public partial class WireMockServer 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) private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage)
{ {
#if OPENAPIPARSER #if OPENAPIPARSER
try try
{ {
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); 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) catch (Exception e)
{ {
@@ -35,7 +54,8 @@ public partial class WireMockServer
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
if (diagnostic.Errors.Any()) 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); ConvertMappingsAndRegisterAsRespondProvider(mappingModels);