From 1dd6695ede0d6e9cfcbe194182f122f38686e78f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 26 Mar 2023 17:49:30 +0200 Subject: [PATCH 01/21] Fix some nullability warnings for WireMockOpenApiParser --- .../IWireMockOpenApiParser.cs | 83 +++++++++---------- .../Mappers/OpenApiPathsMapper.cs | 25 ++++-- .../WireMockOpenApiParser.cs | 14 ++-- 3 files changed, 65 insertions(+), 57 deletions(-) diff --git a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs index a22e144bf..422bb1c0c 100644 --- a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs @@ -5,53 +5,52 @@ using WireMock.Admin.Mappings; using WireMock.Net.OpenApiParser.Settings; -namespace WireMock.Net.OpenApiParser +namespace WireMock.Net.OpenApiParser; + +/// +/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels. +/// +public interface IWireMockOpenApiParser { /// - /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels. + /// Generate from a file-path. /// - public interface IWireMockOpenApiParser - { - /// - /// Generate from a file-path. - /// - /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic); + /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic); - /// - /// Generate from a file-path. - /// - /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. - /// Additional settings - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); + /// + /// Generate from a file-path. + /// + /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. + /// Additional settings + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); - /// - /// Generate from an . - /// - /// The source OpenApiDocument - /// Additional settings [optional] - /// MappingModel - IEnumerable FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null); + /// + /// Generate from an . + /// + /// The source OpenApiDocument + /// Additional settings [optional] + /// MappingModel + IEnumerable FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null); - /// - /// Generate from a . - /// - /// The source stream - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic); + /// + /// Generate from a . + /// + /// The source stream + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic); - /// - /// Generate from a . - /// - /// The source stream - /// Additional settings - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); - } + /// + /// Generate from a . + /// + /// The source stream + /// Additional settings + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs index ae1701bc7..c5a919ef6 100644 --- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs +++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs @@ -123,7 +123,7 @@ private MappingModel MapOperationToMappingModel(string path, string httpMethod, }; } - private bool TryGetContent(IDictionary? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType) + private static bool TryGetContent(IDictionary? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType) { openApiMediaType = null; contentType = null; @@ -305,19 +305,19 @@ private string MapBasePath(IList? servers) return JObject.Parse(outputString.ToString()); } - private IDictionary? MapHeaders(string responseContentType, IDictionary headers) + private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers) { - var mappedHeaders = headers.ToDictionary( + var mappedHeaders = headers?.ToDictionary( item => item.Key, - _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern + _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern! ); if (!string.IsNullOrEmpty(responseContentType)) { - mappedHeaders.TryAdd(HeaderContentType, responseContentType); + mappedHeaders.TryAdd(HeaderContentType, responseContentType!); } - return mappedHeaders.Keys.Any() ? mappedHeaders : null; + return mappedHeaders?.Keys.Any() == true ? mappedHeaders : null; } private IList? MapQueryParameters(IEnumerable queryParameters) @@ -360,9 +360,18 @@ private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueT { return type switch { - ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues }, + ExampleValueType.Value => new MatcherModel + { + Name = "ExactMatcher", + Pattern = GetExampleValueAsStringForSchemaType(schema), + IgnoreCase = _settings.IgnoreCaseExampleValues + }, - _ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" } + _ => new MatcherModel + { + Name = "WildcardMatcher", + Pattern = "*" + } }; } diff --git a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs index e9ba071c8..4600ef9f5 100644 --- a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs @@ -16,16 +16,16 @@ namespace WireMock.Net.OpenApiParser; /// public class WireMockOpenApiParser : IWireMockOpenApiParser { - private readonly OpenApiStreamReader _reader = new OpenApiStreamReader(); + private readonly OpenApiStreamReader _reader = new(); - /// + /// [PublicAPI] public IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic) { return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic); } - /// + /// [PublicAPI] public IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { @@ -44,24 +44,24 @@ public IEnumerable FromFile(string path, WireMockOpenApiParserSett return FromDocument(document, settings); } - /// + /// [PublicAPI] public IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic) { return FromDocument(_reader.Read(stream, out diagnostic)); } - /// + /// [PublicAPI] public IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { return FromDocument(_reader.Read(stream, out diagnostic), settings); } - /// + /// [PublicAPI] public IEnumerable FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null) { - return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); + return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); } } \ No newline at end of file From 7d89aa3323b4df9b92ca3ed90d21be989fa52096 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 26 Mar 2023 18:00:24 +0200 Subject: [PATCH 02/21] . --- .../Utils/ExampleValueGenerator.cs | 90 ++++++++++--------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs index 2bc15d1ad..fc25c8986 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs @@ -11,24 +11,28 @@ namespace WireMock.Net.OpenApiParser.Utils; internal class ExampleValueGenerator { - private readonly WireMockOpenApiParserSettings _settings; + private readonly IWireMockOpenApiParserExampleValues _exampleValues; public ExampleValueGenerator(WireMockOpenApiParserSettings settings) { - _settings = Guard.NotNull(settings); + Guard.NotNull(settings); // Check if user provided an own implementation if (settings.ExampleValues is null) { - if (_settings.DynamicExamples) + if (settings.DynamicExamples) { - _settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues(); + _exampleValues = new WireMockOpenApiParserDynamicExampleValues(); } else { - _settings.ExampleValues = new WireMockOpenApiParserExampleValues(); + _exampleValues = new WireMockOpenApiParserExampleValues(); } } + else + { + _exampleValues = settings.ExampleValues; + } } public object GetExampleValue(OpenApiSchema? schema) @@ -36,78 +40,78 @@ public object GetExampleValue(OpenApiSchema? schema) var schemaExample = schema?.Example; var schemaEnum = GetRandomEnumValue(schema?.Enum); - _settings.ExampleValues.Schema = schema; + _exampleValues.Schema = schema; switch (schema?.GetSchemaType()) { case SchemaType.Boolean: var exampleBoolean = schemaExample as OpenApiBoolean; - return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value; + return exampleBoolean?.Value ?? _exampleValues.Boolean; case SchemaType.Integer: switch (schema?.GetSchemaFormat()) { case SchemaFormat.Int64: - var exampleLong = (OpenApiLong)schemaExample; - var enumLong = (OpenApiLong)schemaEnum; - var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value; - return valueLongEnumOrExample ?? _settings.ExampleValues.Integer; + var exampleLong = schemaExample as OpenApiLong; + var enumLong = schemaEnum as OpenApiLong; + var valueLongEnumOrExample = enumLong?.Value ?? exampleLong?.Value; + return valueLongEnumOrExample ?? _exampleValues.Integer; default: - var exampleInteger = (OpenApiInteger)schemaExample; - var enumInteger = (OpenApiInteger)schemaEnum; - var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value; - return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer; + var exampleInteger = schemaExample as OpenApiInteger; + var enumInteger = schemaEnum as OpenApiInteger; + var valueIntegerEnumOrExample = enumInteger?.Value ?? exampleInteger?.Value; + return valueIntegerEnumOrExample ?? _exampleValues.Integer; } case SchemaType.Number: switch (schema?.GetSchemaFormat()) { case SchemaFormat.Float: - var exampleFloat = (OpenApiFloat)schemaExample; - var enumFloat = (OpenApiFloat)schemaEnum; - var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value; - return valueFloatEnumOrExample ?? _settings.ExampleValues.Float; + var exampleFloat = schemaExample as OpenApiFloat; + var enumFloat = schemaEnum as OpenApiFloat; + var valueFloatEnumOrExample = enumFloat?.Value ?? exampleFloat?.Value; + return valueFloatEnumOrExample ?? _exampleValues.Float; default: - var exampleDouble = (OpenApiDouble)schemaExample; - var enumDouble = (OpenApiDouble)schemaEnum; - var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value; - return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double; + var exampleDouble = schemaExample as OpenApiDouble; + var enumDouble = schemaEnum as OpenApiDouble; + var valueDoubleEnumOrExample = enumDouble?.Value ?? exampleDouble?.Value; + return valueDoubleEnumOrExample ?? _exampleValues.Double; } default: switch (schema?.GetSchemaFormat()) { case SchemaFormat.Date: - var exampleDate = (OpenApiDate)schemaExample; - var enumDate = (OpenApiDate)schemaEnum; - var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value; - return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date()); + var exampleDate = schemaExample as OpenApiDate; + var enumDate = schemaEnum as OpenApiDate; + var valueDateEnumOrExample = enumDate?.Value ?? exampleDate?.Value; + return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _exampleValues.Date()); case SchemaFormat.DateTime: - var exampleDateTime = (OpenApiDateTime)schemaExample; - var enumDateTime = (OpenApiDateTime)schemaEnum; - var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value; - return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime()); + var exampleDateTime = schemaExample as OpenApiDateTime; + var enumDateTime = schemaEnum as OpenApiDateTime; + var valueDateTimeEnumOrExample = enumDateTime?.Value ?? exampleDateTime?.Value; + return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _exampleValues.DateTime()); case SchemaFormat.Byte: - var exampleByte = (OpenApiByte)schemaExample; - var enumByte = (OpenApiByte)schemaEnum; - var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value; - return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes; + var exampleByte = schemaExample as OpenApiByte; + var enumByte = schemaEnum as OpenApiByte; + var valueByteEnumOrExample = enumByte?.Value ?? exampleByte?.Value; + return valueByteEnumOrExample ?? _exampleValues.Bytes; case SchemaFormat.Binary: - var exampleBinary = (OpenApiBinary)schemaExample; - var enumBinary = (OpenApiBinary)schemaEnum; - var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value; - return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object; + var exampleBinary = schemaExample as OpenApiBinary; + var enumBinary = schemaEnum as OpenApiBinary; + var valueBinaryEnumOrExample = enumBinary?.Value ?? exampleBinary?.Value; + return valueBinaryEnumOrExample ?? _exampleValues.Object; default: - var exampleString = (OpenApiString)schemaExample; - var enumString = (OpenApiString)schemaEnum; - var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value; - return valueStringEnumOrExample ?? _settings.ExampleValues.String; + var exampleString = schemaExample as OpenApiString; + var enumString = schemaEnum as OpenApiString; + var valueStringEnumOrExample = enumString?.Value ?? exampleString?.Value; + return valueStringEnumOrExample ?? _exampleValues.String; } } } From ee59c2bb99064eae72ddb72a1e447b3772b222f3 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 26 Mar 2023 18:12:39 +0200 Subject: [PATCH 03/21] . --- .../DynamicDataGeneration.cs | 2 +- .../Program.cs | 20 ++-- .../Run.cs | 98 ++++++++++--------- 3 files changed, 63 insertions(+), 57 deletions(-) diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs index 7d51dc739..d22fe8b3f 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs @@ -11,7 +11,7 @@ public override string String get { // Since you have your Schema, you can get if max-length is set. You can generate accurate examples with this settings - var maxLength = Schema.MaxLength ?? 9; + var maxLength = Schema?.MaxLength ?? 9; return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs index c959d548c..30b8b23c5 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs @@ -17,7 +17,7 @@ static void Main(string[] args) private static void RunMockServerWithDynamicExampleGeneration() { - //Run your mocking framework specifieing youur 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); Console.WriteLine("Press any key to stop the servers"); @@ -27,15 +27,15 @@ private static void RunMockServerWithDynamicExampleGeneration() private static void RunOthersOpenApiParserExample() { - var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "https://localhost:9091/"); - var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "https://localhost:9092/"); - var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "https://localhost:9093/"); - var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "https://localhost:9094/"); - var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "https://localhost:9095/"); - var testopenapifile_json = Run.RunServer(Path.Combine(Folder, "testopenapifile.json"), "https://localhost:9096/"); - var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "https://localhost:9097/"); - var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "https://localhost:9098/"); - var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "https://localhost:9099/"); + var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "http://localhost:9091/"); + var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "http://localhost:9092/"); + var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "http://localhost:9093/"); + var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "http://localhost:9094/"); + var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "http://localhost:9095/"); + var testopenapifile_json = Run.RunServer(Path.Combine(Folder, "testopenapifile.json"), "http://localhost:9096/"); + var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "http://localhost:9097/"); + var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "http://localhost:9098/"); + var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "http://localhost:9099/"); testopenapifile_json .Given(Request.Create().WithPath("/x").UsingGet()) diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs index ae8fc9ec3..49704f0e3 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs @@ -9,64 +9,70 @@ using WireMock.Server; using WireMock.Settings; -namespace WireMock.Net.OpenApiParser.ConsoleApp +namespace WireMock.Net.OpenApiParser.ConsoleApp; + +public static class Run { - public static class Run + public static WireMockServer RunServer( + string path, + string url, + bool dynamicExamples = true, + IWireMockOpenApiParserExampleValues? examplesValuesGenerator = null, + ExampleValueType pathPatternToUse = ExampleValueType.Wildcard, + ExampleValueType headerPatternToUse = ExampleValueType.Wildcard + ) { - public static WireMockServer RunServer(string path, string url, bool dynamicExamples = true, IWireMockOpenApiParserExampleValues examplesValuesGenerator = null, ExampleValueType pathPatternToUse = ExampleValueType.Wildcard, ExampleValueType headerPatternToUse = ExampleValueType.Wildcard) + var server = WireMockServer.Start(new WireMockServerSettings { - var server = WireMockServer.Start(new WireMockServerSettings - { - AllowCSharpCodeMatcher = true, - Urls = new[] { url }, - StartAdminInterface = true, - ReadStaticMappings = true, - WatchStaticMappings = false, - WatchStaticMappingsInSubdirectories = false, - Logger = new WireMockConsoleLogger(), - SaveUnmatchedRequests = true - }); + AllowCSharpCodeMatcher = true, + Urls = new[] { url }, + StartAdminInterface = true, + ReadStaticMappings = true, + WatchStaticMappings = false, + WatchStaticMappingsInSubdirectories = false, + Logger = new WireMockConsoleLogger(), + SaveUnmatchedRequests = true + }); + + Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); - Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); + //server.SetBasicAuthentication("a", "b"); - //server.SetBasicAuthentication("a", "b"); + var settings = new WireMockOpenApiParserSettings + { + DynamicExamples = dynamicExamples, + ExampleValues = examplesValuesGenerator, + PathPatternToUse = pathPatternToUse, + HeaderPatternToUse = headerPatternToUse, + }; - var settings = new WireMockOpenApiParserSettings - { - DynamicExamples = dynamicExamples, - ExampleValues = examplesValuesGenerator, - PathPatternToUse = pathPatternToUse, - HeaderPatternToUse = headerPatternToUse, - }; + server.WithMappingFromOpenApiFile(path, settings, out var diag); - server.WithMappingFromOpenApiFile(path, settings, out var diag); + return server; + } - return server; - } + public static void RunServer(IEnumerable mappings) + { + string url1 = "http://localhost:9091/"; - public static void RunServer(IEnumerable mappings) + var server = WireMockServer.Start(new WireMockServerSettings { - string url1 = "http://localhost:9091/"; - - var server = WireMockServer.Start(new WireMockServerSettings - { - AllowCSharpCodeMatcher = true, - Urls = new[] { url1 }, - StartAdminInterface = true, - ReadStaticMappings = false, - WatchStaticMappings = false, - WatchStaticMappingsInSubdirectories = false, - Logger = new WireMockConsoleLogger(), - }); - Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); + AllowCSharpCodeMatcher = true, + Urls = new[] { url1 }, + StartAdminInterface = true, + ReadStaticMappings = false, + WatchStaticMappings = false, + WatchStaticMappingsInSubdirectories = false, + Logger = new WireMockConsoleLogger(), + }); + Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); - server.SetBasicAuthentication("a", "b"); + server.SetBasicAuthentication("a", "b"); - server.WithMapping(mappings.ToArray()); + server.WithMapping(mappings.ToArray()); - Console.WriteLine("Press any key to stop the server"); - System.Console.ReadKey(); - server.Stop(); - } + Console.WriteLine("Press any key to stop the server"); + System.Console.ReadKey(); + server.Stop(); } } \ No newline at end of file From 23c3263e4398d5526b7c8e25dd6b7f52a7150f0f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 26 Mar 2023 18:13:31 +0200 Subject: [PATCH 04/21] . --- examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs index 49704f0e3..4ff3f7758 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs @@ -17,7 +17,7 @@ public static WireMockServer RunServer( string path, string url, bool dynamicExamples = true, - IWireMockOpenApiParserExampleValues? examplesValuesGenerator = null, + IWireMockOpenApiParserExampleValues? examplesValuesGenerator = null, ExampleValueType pathPatternToUse = ExampleValueType.Wildcard, ExampleValueType headerPatternToUse = ExampleValueType.Wildcard ) From 3232be8a2640b35ad544a6c6f19922c8e397728c Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 26 Mar 2023 18:17:00 +0200 Subject: [PATCH 05/21] opt --- .../Extensions/WireMockServerExtensions.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs index f174ca18e..cba9daec0 100644 --- a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs +++ b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs @@ -31,13 +31,13 @@ public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer se /// /// The WireMockServer instance /// Path containing OpenAPI file to parse and use the mappings. - /// Returns diagnostic object containing errors detected during parsing /// Additional settings + /// Returns diagnostic object containing errors detected during parsing [PublicAPI] public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { - Guard.NotNull(server, nameof(server)); - Guard.NotNullOrEmpty(path, nameof(path)); + Guard.NotNull(server); + Guard.NotNullOrEmpty(path); var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic); @@ -80,9 +80,9 @@ public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer /// /// The WireMockServer instance /// The OpenAPI document to use as mappings. - /// Additional settings [optional] + /// Additional settings [optional]. [PublicAPI] - public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings) + public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings? settings = null) { Guard.NotNull(server); Guard.NotNull(document); From b0201be5a601f7e784b6f79c0ba145ba6cd63bbc Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 28 Mar 2023 17:54:23 +0200 Subject: [PATCH 06/21] FromText --- .../IWireMockOpenApiParser.cs | 17 +++++++++++++++++ .../WireMockOpenApiParser.cs | 19 +++++++++++++++++-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs index 422bb1c0c..e63c9deb8 100644 --- a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs @@ -53,4 +53,21 @@ public interface IWireMockOpenApiParser /// OpenApiDiagnostic output /// MappingModel IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); + + /// + /// Generate from a . + /// + /// The source text + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromText(string text, out OpenApiDiagnostic diagnostic); + + /// + /// Generate from a . + /// + /// The source text + /// Additional settings + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs index 4600ef9f5..dbe5b78a9 100644 --- a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using JetBrains.Annotations; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Readers; @@ -44,6 +45,13 @@ public IEnumerable FromFile(string path, WireMockOpenApiParserSett return FromDocument(document, settings); } + /// + [PublicAPI] + public IEnumerable FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null) + { + return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); + } + /// [PublicAPI] public IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic) @@ -58,10 +66,17 @@ public IEnumerable FromStream(Stream stream, WireMockOpenApiParser return FromDocument(_reader.Read(stream, out diagnostic), settings); } + /// + [PublicAPI] + public IEnumerable FromText(string text, out OpenApiDiagnostic diagnostic) + { + return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), out diagnostic); + } + /// [PublicAPI] - public IEnumerable FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null) + public IEnumerable FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { - return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); + return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic); } } \ No newline at end of file From b5e6d7803e348db113ebd4c72075eeec8e468860 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 28 Mar 2023 18:30:55 +0200 Subject: [PATCH 07/21] ab --- WireMock.Net Solution.sln | 7 +++ .../IWireMockOpenApiParser.cs | 56 +++++++++++++++++ .../IWireMockOpenApiParserExampleValues.cs | 54 ++++++++++++++++ ...reMockOpenApiParserDynamicExampleValues.cs | 38 +++++++++++ .../WireMockOpenApiParserExampleValues.cs | 36 +++++++++++ .../Settings/WireMockOpenApiParserSettings.cs | 63 +++++++++++++++++++ .../Types/ExampleValueType.cs | 21 +++++++ .../Types/SchemaFormat.cs | 24 +++++++ .../Types/SchemaType.cs | 20 ++++++ .../Types/WireMockOpenApiDiagnostic.cs | 19 ++++++ .../Types/WireMockOpenApiError.cs | 34 ++++++++++ ...Mock.Net.OpenApiParser.Abstractions.csproj | 41 ++++++++++++ 12 files changed, 413 insertions(+) create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs create mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln index 497727018..fc6050aa3 100644 --- a/WireMock.Net Solution.sln +++ b/WireMock.Net Solution.sln @@ -110,6 +110,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser.Abstractions", "src\WireMock.Net.OpenApiParser.Abstractions\WireMock.Net.OpenApiParser.Abstractions.csproj", "{129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -264,6 +266,10 @@ Global {ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU + {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -308,6 +314,7 @@ Global {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946} + {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs new file mode 100644 index 000000000..2c4d96df0 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.IO; +using WireMock.Admin.Mappings; +using WireMock.Net.OpenApiParser.Abstractions.Settings; +using WireMock.Net.OpenApiParser.Abstractions.Types; + +namespace WireMock.Net.OpenApiParser.Abstractions; + +/// +/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels. +/// +public interface IWireMockOpenApiParser +{ + /// + /// Generate from a file-path. + /// + /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. + /// Additional settings + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out WireMockOpenApiDiagnostic diagnostic); + + /// + /// Generate from a . + /// + /// The source stream + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromStream(Stream stream, out WireMockOpenApiDiagnostic diagnostic); + + /// + /// Generate from a . + /// + /// The source stream + /// Additional settings + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out WireMockOpenApiDiagnostic diagnostic); + + /// + /// Generate from a . + /// + /// The source text + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromText(string text, out WireMockOpenApiDiagnostic diagnostic); + + /// + /// Generate from a . + /// + /// The source text + /// Additional settings + /// OpenApiDiagnostic output + /// MappingModel + IEnumerable FromText(string text, WireMockOpenApiParserSettings settings, out WireMockOpenApiDiagnostic diagnostic); +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs new file mode 100644 index 000000000..4bc80c3db --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs @@ -0,0 +1,54 @@ +using System; + +namespace WireMock.Net.OpenApiParser.Abstractions.Settings; + +/// +/// A interface defining the example values to use for the different types. +/// +public interface IWireMockOpenApiParserExampleValues +{ + /// + /// An example value for a Boolean. + /// + bool Boolean { get; set; } + + /// + /// An example value for an Integer. + /// + int Integer { get; set; } + + /// + /// An example value for a Float. + /// + float Float { get; set; } + + /// + /// An example value for a Double. + /// + double Double { get; set; } + + /// + /// An example value for a Date. + /// + Func Date { get; set; } + + /// + /// An example value for a DateTime. + /// + Func DateTime { get; set; } + + /// + /// An example value for Bytes. + /// + byte[] Bytes { get; set; } + + /// + /// An example value for a Object. + /// + object Object { get; set; } + + /// + /// An example value for a String. + /// + string String { get; set; } +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs new file mode 100644 index 000000000..e76c23307 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs @@ -0,0 +1,38 @@ +using System; +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; + +namespace WireMock.Net.OpenApiParser.Abstractions.Settings; + +/// +/// A class defining the random example values to use for the different types. +/// +public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues +{ + /// + public virtual bool Boolean { get => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; set { } } + + /// + public virtual int Integer { get => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; set { } } + + /// + public virtual float Float { get => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; set { } } + + /// + public virtual double Double { get => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; set { } } + + /// + public virtual Func Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } } + + /// + public virtual Func DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } } + + /// + public virtual byte[] Bytes { get => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); set { } } + + /// + public virtual object Object { get; set; } = "example-object"; + + /// + public virtual string String { get => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; set { } } +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs new file mode 100644 index 000000000..e329d8aef --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs @@ -0,0 +1,36 @@ +using System; + +namespace WireMock.Net.OpenApiParser.Abstractions.Settings; + +/// +/// A class defining the example values to use for the different types. +/// +public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues +{ + /// + public virtual bool Boolean { get; set; } = true; + + /// + public virtual int Integer { get; set; } = 42; + + /// + public virtual float Float { get; set; } = 4.2f; + + /// + public virtual double Double { get; set; } = 4.2d; + + /// + public virtual Func Date { get; set; } = () => System.DateTime.UtcNow.Date; + + /// + public virtual Func DateTime { get; set; } = () => System.DateTime.UtcNow; + + /// + public virtual byte[] Bytes { get; set; } = { 48, 49, 50 }; + + /// + public virtual object Object { get; set; } = "example-object"; + + /// + public virtual string String { get; set; } = "example-string"; +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs new file mode 100644 index 000000000..9e86ac752 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs @@ -0,0 +1,63 @@ +using WireMock.Net.OpenApiParser.Abstractions.Types; + +namespace WireMock.Net.OpenApiParser.Abstractions.Settings; + +/// +/// The WireMockOpenApiParser Settings +/// +public class WireMockOpenApiParserSettings +{ + /// + /// The number of array items to generate (default is 3). + /// + public int NumberOfArrayItems { get; set; } = 3; + + /// + /// The example value type to use when generating a Path + /// + public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value; + + /// + /// The example value type to use when generating a Header + /// + public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value; + + /// + /// The example value type to use when generating a Query Parameter + /// + public ExampleValueType QueryParameterPatternToUse { get; set; } = ExampleValueType.Value; + + /// + /// The example values to use. + /// + /// Default implementations are: + /// - + /// - + /// + public IWireMockOpenApiParserExampleValues? ExampleValues { get; set; } + + /// + /// Is a Header match case insensitive? (default is true). + /// + public bool HeaderPatternIgnoreCase { get; set; } = true; + + /// + /// Is a Query Parameter match case insensitive? (default is true). + /// + public bool QueryParameterPatternIgnoreCase { get; set; } = true; + + /// + /// Is a Request Body match case insensitive? (default is true). + /// + public bool RequestBodyIgnoreCase { get; set; } = true; + + /// + /// Is a ExampleValue match case insensitive? (default is true). + /// + public bool IgnoreCaseExampleValues { get; set; } = true; + + /// + /// Are examples generated dynamically? (default is false). + /// + public bool DynamicExamples { get; set; } = false; +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs new file mode 100644 index 000000000..8eb1f85b3 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs @@ -0,0 +1,21 @@ +using WireMock.Net.OpenApiParser.Abstractions.Settings; + +namespace WireMock.Net.OpenApiParser.Abstractions.Types; + +/// +/// The example value to use +/// +public enum ExampleValueType +{ + /// + /// 1. Use a generated example value based on the SchemaType (default). + /// 2. If there is no example value defined in the schema, + /// then the will be used (custom, fixed or dynamic). + /// + Value, + + /// + /// Just use a Wildcard (*) character. + /// + Wildcard +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs new file mode 100644 index 000000000..08673a90e --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs @@ -0,0 +1,24 @@ +namespace WireMock.Net.OpenApiParser.Abstractions.Types; + +public enum OpenApiSchemaFormat +{ + Float, + + Double, + + Int32, + + Int64, + + Date, + + DateTime, + + Password, + + Byte, + + Binary, + + Undefined +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs new file mode 100644 index 000000000..29f950a56 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs @@ -0,0 +1,20 @@ +namespace WireMock.Net.OpenApiParser.Abstractions.Types; + +internal enum OpenApiSchemaSchemaType +{ + Object, + + Array, + + String, + + Integer, + + Number, + + Boolean, + + File, + + Unknown +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs new file mode 100644 index 000000000..1603e3e27 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace WireMock.Net.OpenApiParser.Abstractions.Types; + +/// +/// Object containing all diagnostic information related to Open API parsing. +/// +public class WireMockOpenApiDiagnostic +{ + /// + /// List of all errors. + /// + public IReadOnlyList Errors { get; set; } = new List(); + + /// + /// Open API specification version of the document parsed. + /// + public string SpecificationVersion { get; set; } = "undefined"; +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs new file mode 100644 index 000000000..b07e3f2be --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs @@ -0,0 +1,34 @@ +namespace WireMock.Net.OpenApiParser.Abstractions.Types; + +/// +/// Error related to the Open API Document. +/// +public class WireMockOpenApiError +{ + /// + /// Initializes the class. + /// + public WireMockOpenApiError(string pointer, string message) + { + Pointer = pointer; + Message = message; + } + + /// + /// Message explaining the error. + /// + public string Message { get; set; } + + /// + /// Pointer to the location of the error. + /// + public string Pointer { get; set; } + + /// + /// Gets the string representation of . + /// + public override string ToString() + { + return Message + (!string.IsNullOrEmpty(Pointer) ? " [" + Pointer + "]" : ""); + } +} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj b/src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj new file mode 100644 index 000000000..7c38f36b6 --- /dev/null +++ b/src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj @@ -0,0 +1,41 @@ + + + + An OpenApi (swagger) parser to generate MappingModel or mapping.json file. + net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1 + + wiremock;openapi;OAS;converter;parser;openapiparser + + {130CBFE3-32EE-4EB2-BE3F-281E0EEF2A2D} + true + ../WireMock.Net/WireMock.Net.ruleset + true + ../WireMock.Net/WireMock.Net.snk + true + MIT + 10 + + + + true + + + + + + + + + + + + + \ No newline at end of file From 975105066aa8d1d5367e97fcd319b607316aeded Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 28 Mar 2023 19:09:41 +0200 Subject: [PATCH 08/21] . --- WireMock.Net Solution.sln | 7 --- .../IWireMockOpenApiParser.cs | 56 ----------------- .../IWireMockOpenApiParserExampleValues.cs | 54 ---------------- ...reMockOpenApiParserDynamicExampleValues.cs | 38 ----------- .../WireMockOpenApiParserExampleValues.cs | 36 ----------- .../Settings/WireMockOpenApiParserSettings.cs | 63 ------------------- .../Types/ExampleValueType.cs | 21 ------- .../Types/SchemaFormat.cs | 24 ------- .../Types/SchemaType.cs | 20 ------ .../Types/WireMockOpenApiDiagnostic.cs | 19 ------ .../Types/WireMockOpenApiError.cs | 34 ---------- ...Mock.Net.OpenApiParser.Abstractions.csproj | 41 ------------ .../IWireMockOpenApiParser.cs | 28 ++++----- .../Mappers/OpenApiPathsMapper.cs | 12 ++-- .../WireMockOpenApiParser.cs | 14 ++--- .../Server/WireMockServer.Admin.cs | 8 ++- .../Server/WireMockServer.ConvertMapping.cs | 15 ++--- .../Server/WireMockServer.OpenApiParser.cs | 59 +++++++++++++++++ src/WireMock.Net/WireMock.Net.csproj | 8 +++ 19 files changed, 108 insertions(+), 449 deletions(-) delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs delete mode 100644 src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj create mode 100644 src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln index fc6050aa3..497727018 100644 --- a/WireMock.Net Solution.sln +++ b/WireMock.Net Solution.sln @@ -110,8 +110,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser.Abstractions", "src\WireMock.Net.OpenApiParser.Abstractions\WireMock.Net.OpenApiParser.Abstractions.csproj", "{129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -266,10 +264,6 @@ Global {ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU {ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU {ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU - {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -314,7 +308,6 @@ Global {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946} - {129ABFE3-32EE-4EB2-BE3F-281E0EEF2A2D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs deleted file mode 100644 index 2c4d96df0..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/IWireMockOpenApiParser.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using WireMock.Admin.Mappings; -using WireMock.Net.OpenApiParser.Abstractions.Settings; -using WireMock.Net.OpenApiParser.Abstractions.Types; - -namespace WireMock.Net.OpenApiParser.Abstractions; - -/// -/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels. -/// -public interface IWireMockOpenApiParser -{ - /// - /// Generate from a file-path. - /// - /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. - /// Additional settings - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out WireMockOpenApiDiagnostic diagnostic); - - /// - /// Generate from a . - /// - /// The source stream - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromStream(Stream stream, out WireMockOpenApiDiagnostic diagnostic); - - /// - /// Generate from a . - /// - /// The source stream - /// Additional settings - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out WireMockOpenApiDiagnostic diagnostic); - - /// - /// Generate from a . - /// - /// The source text - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromText(string text, out WireMockOpenApiDiagnostic diagnostic); - - /// - /// Generate from a . - /// - /// The source text - /// Additional settings - /// OpenApiDiagnostic output - /// MappingModel - IEnumerable FromText(string text, WireMockOpenApiParserSettings settings, out WireMockOpenApiDiagnostic diagnostic); -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs deleted file mode 100644 index 4bc80c3db..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/IWireMockOpenApiParserExampleValues.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; - -namespace WireMock.Net.OpenApiParser.Abstractions.Settings; - -/// -/// A interface defining the example values to use for the different types. -/// -public interface IWireMockOpenApiParserExampleValues -{ - /// - /// An example value for a Boolean. - /// - bool Boolean { get; set; } - - /// - /// An example value for an Integer. - /// - int Integer { get; set; } - - /// - /// An example value for a Float. - /// - float Float { get; set; } - - /// - /// An example value for a Double. - /// - double Double { get; set; } - - /// - /// An example value for a Date. - /// - Func Date { get; set; } - - /// - /// An example value for a DateTime. - /// - Func DateTime { get; set; } - - /// - /// An example value for Bytes. - /// - byte[] Bytes { get; set; } - - /// - /// An example value for a Object. - /// - object Object { get; set; } - - /// - /// An example value for a String. - /// - string String { get; set; } -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs deleted file mode 100644 index e76c23307..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserDynamicExampleValues.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using RandomDataGenerator.FieldOptions; -using RandomDataGenerator.Randomizers; - -namespace WireMock.Net.OpenApiParser.Abstractions.Settings; - -/// -/// A class defining the random example values to use for the different types. -/// -public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues -{ - /// - public virtual bool Boolean { get => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; set { } } - - /// - public virtual int Integer { get => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; set { } } - - /// - public virtual float Float { get => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; set { } } - - /// - public virtual double Double { get => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; set { } } - - /// - public virtual Func Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } } - - /// - public virtual Func DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } } - - /// - public virtual byte[] Bytes { get => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); set { } } - - /// - public virtual object Object { get; set; } = "example-object"; - - /// - public virtual string String { get => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; set { } } -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs deleted file mode 100644 index e329d8aef..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserExampleValues.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace WireMock.Net.OpenApiParser.Abstractions.Settings; - -/// -/// A class defining the example values to use for the different types. -/// -public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues -{ - /// - public virtual bool Boolean { get; set; } = true; - - /// - public virtual int Integer { get; set; } = 42; - - /// - public virtual float Float { get; set; } = 4.2f; - - /// - public virtual double Double { get; set; } = 4.2d; - - /// - public virtual Func Date { get; set; } = () => System.DateTime.UtcNow.Date; - - /// - public virtual Func DateTime { get; set; } = () => System.DateTime.UtcNow; - - /// - public virtual byte[] Bytes { get; set; } = { 48, 49, 50 }; - - /// - public virtual object Object { get; set; } = "example-object"; - - /// - public virtual string String { get; set; } = "example-string"; -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs deleted file mode 100644 index 9e86ac752..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Settings/WireMockOpenApiParserSettings.cs +++ /dev/null @@ -1,63 +0,0 @@ -using WireMock.Net.OpenApiParser.Abstractions.Types; - -namespace WireMock.Net.OpenApiParser.Abstractions.Settings; - -/// -/// The WireMockOpenApiParser Settings -/// -public class WireMockOpenApiParserSettings -{ - /// - /// The number of array items to generate (default is 3). - /// - public int NumberOfArrayItems { get; set; } = 3; - - /// - /// The example value type to use when generating a Path - /// - public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value; - - /// - /// The example value type to use when generating a Header - /// - public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value; - - /// - /// The example value type to use when generating a Query Parameter - /// - public ExampleValueType QueryParameterPatternToUse { get; set; } = ExampleValueType.Value; - - /// - /// The example values to use. - /// - /// Default implementations are: - /// - - /// - - /// - public IWireMockOpenApiParserExampleValues? ExampleValues { get; set; } - - /// - /// Is a Header match case insensitive? (default is true). - /// - public bool HeaderPatternIgnoreCase { get; set; } = true; - - /// - /// Is a Query Parameter match case insensitive? (default is true). - /// - public bool QueryParameterPatternIgnoreCase { get; set; } = true; - - /// - /// Is a Request Body match case insensitive? (default is true). - /// - public bool RequestBodyIgnoreCase { get; set; } = true; - - /// - /// Is a ExampleValue match case insensitive? (default is true). - /// - public bool IgnoreCaseExampleValues { get; set; } = true; - - /// - /// Are examples generated dynamically? (default is false). - /// - public bool DynamicExamples { get; set; } = false; -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs deleted file mode 100644 index 8eb1f85b3..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Types/ExampleValueType.cs +++ /dev/null @@ -1,21 +0,0 @@ -using WireMock.Net.OpenApiParser.Abstractions.Settings; - -namespace WireMock.Net.OpenApiParser.Abstractions.Types; - -/// -/// The example value to use -/// -public enum ExampleValueType -{ - /// - /// 1. Use a generated example value based on the SchemaType (default). - /// 2. If there is no example value defined in the schema, - /// then the will be used (custom, fixed or dynamic). - /// - Value, - - /// - /// Just use a Wildcard (*) character. - /// - Wildcard -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs deleted file mode 100644 index 08673a90e..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaFormat.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace WireMock.Net.OpenApiParser.Abstractions.Types; - -public enum OpenApiSchemaFormat -{ - Float, - - Double, - - Int32, - - Int64, - - Date, - - DateTime, - - Password, - - Byte, - - Binary, - - Undefined -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs deleted file mode 100644 index 29f950a56..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Types/SchemaType.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace WireMock.Net.OpenApiParser.Abstractions.Types; - -internal enum OpenApiSchemaSchemaType -{ - Object, - - Array, - - String, - - Integer, - - Number, - - Boolean, - - File, - - Unknown -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs deleted file mode 100644 index 1603e3e27..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiDiagnostic.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; - -namespace WireMock.Net.OpenApiParser.Abstractions.Types; - -/// -/// Object containing all diagnostic information related to Open API parsing. -/// -public class WireMockOpenApiDiagnostic -{ - /// - /// List of all errors. - /// - public IReadOnlyList Errors { get; set; } = new List(); - - /// - /// Open API specification version of the document parsed. - /// - public string SpecificationVersion { get; set; } = "undefined"; -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs b/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs deleted file mode 100644 index b07e3f2be..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/Types/WireMockOpenApiError.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace WireMock.Net.OpenApiParser.Abstractions.Types; - -/// -/// Error related to the Open API Document. -/// -public class WireMockOpenApiError -{ - /// - /// Initializes the class. - /// - public WireMockOpenApiError(string pointer, string message) - { - Pointer = pointer; - Message = message; - } - - /// - /// Message explaining the error. - /// - public string Message { get; set; } - - /// - /// Pointer to the location of the error. - /// - public string Pointer { get; set; } - - /// - /// Gets the string representation of . - /// - public override string ToString() - { - return Message + (!string.IsNullOrEmpty(Pointer) ? " [" + Pointer + "]" : ""); - } -} \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj b/src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj deleted file mode 100644 index 7c38f36b6..000000000 --- a/src/WireMock.Net.OpenApiParser.Abstractions/WireMock.Net.OpenApiParser.Abstractions.csproj +++ /dev/null @@ -1,41 +0,0 @@ - - - - An OpenApi (swagger) parser to generate MappingModel or mapping.json file. - net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1 - - wiremock;openapi;OAS;converter;parser;openapiparser - - {130CBFE3-32EE-4EB2-BE3F-281E0EEF2A2D} - true - ../WireMock.Net/WireMock.Net.ruleset - true - ../WireMock.Net/WireMock.Net.snk - true - MIT - 10 - - - - true - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs index e63c9deb8..c85304d83 100644 --- a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs @@ -13,61 +13,61 @@ namespace WireMock.Net.OpenApiParser; public interface IWireMockOpenApiParser { /// - /// Generate from a file-path. + /// Generate from a file-path. /// /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. /// OpenApiDiagnostic output /// MappingModel - IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic); + IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic); /// - /// Generate from a file-path. + /// Generate from a file-path. /// /// The path to read the OpenApi/Swagger/V2/V3 or Raml file. /// Additional settings /// OpenApiDiagnostic output /// MappingModel - IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); + IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); /// - /// Generate from an . + /// Generate from an . /// /// The source OpenApiDocument /// Additional settings [optional] /// MappingModel - IEnumerable FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null); + IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null); /// - /// Generate from a . + /// Generate from a . /// /// The source stream /// OpenApiDiagnostic output /// MappingModel - IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic); + IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic); /// - /// Generate from a . + /// Generate from a . /// /// The source stream /// Additional settings /// OpenApiDiagnostic output /// MappingModel - IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); + IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); /// - /// Generate from a . + /// Generate from a . /// /// The source text /// OpenApiDiagnostic output /// MappingModel - IEnumerable FromText(string text, out OpenApiDiagnostic diagnostic); + IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic); /// - /// Generate from a . + /// Generate from a . /// /// The source text /// Additional settings /// OpenApiDiagnostic output /// MappingModel - IEnumerable FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); + IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic); } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs index c5a919ef6..e88e31234 100644 --- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs +++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs @@ -31,19 +31,19 @@ public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) _exampleValueGenerator = new ExampleValueGenerator(settings); } - public IEnumerable ToMappingModels(OpenApiPaths paths, IList servers) + public IReadOnlyList ToMappingModels(OpenApiPaths paths, IList servers) { - return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x); + return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray(); } - private IEnumerable MapPaths(OpenApiPaths paths, IList servers) + private IReadOnlyList MapPaths(OpenApiPaths paths, IList servers) { - return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x); + return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray(); } - private IEnumerable MapPath(string path, OpenApiPathItem pathItem, IList servers) + private IReadOnlyList MapPath(string path, OpenApiPathItem pathItem, IList servers) { - return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)); + return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray(); } private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList servers) diff --git a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs index dbe5b78a9..d690f8cb7 100644 --- a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs +++ b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs @@ -21,14 +21,14 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser /// [PublicAPI] - public IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic) + public IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic) { return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic); } /// [PublicAPI] - public IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) + public IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { OpenApiDocument document; if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase)) @@ -47,35 +47,35 @@ public IEnumerable FromFile(string path, WireMockOpenApiParserSett /// [PublicAPI] - public IEnumerable FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null) + public IReadOnlyList FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null) { return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); } /// [PublicAPI] - public IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic) + public IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic) { return FromDocument(_reader.Read(stream, out diagnostic)); } /// [PublicAPI] - public IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) + public IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { return FromDocument(_reader.Read(stream, out diagnostic), settings); } /// [PublicAPI] - public IEnumerable FromText(string text, out OpenApiDiagnostic diagnostic) + public IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic) { return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), out diagnostic); } /// [PublicAPI] - public IEnumerable FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) + public IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) { return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic); } diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 90294f682..e6fc65117 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -113,6 +113,10 @@ private void InitAdmin() Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileGet)); Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingHead()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileHead)); Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete)); + +#if OPENAPIPARSER + InitOpenApiParserAdmin(); +#endif } #endregion @@ -737,7 +741,7 @@ private void EnhancedFileSystemWatcherDeleted(object sender, FileSystemEventArgs return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null; } - private static ResponseMessage ToJson(T result, bool keepNullValues = false) + private static ResponseMessage ToJson(T result, bool keepNullValues = false, object? statusCode = null) { return new ResponseMessage { @@ -746,7 +750,7 @@ private static ResponseMessage ToJson(T result, bool keepNullValues = false) DetectedBodyType = BodyType.String, BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues : JsonSerializationConstants.JsonSerializerSettingsDefault) }, - StatusCode = (int)HttpStatusCode.OK, + StatusCode = statusCode ?? (int)HttpStatusCode.OK, Headers = new Dictionary> { { HttpKnownHeaderNames.ContentType, new WireMockList(WireMockConstants.ContentTypeJson) } } }; } diff --git a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs index 052440ad9..89886b404 100644 --- a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs +++ b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using Stef.Validation; using WireMock.Admin.Mappings; @@ -14,7 +15,7 @@ namespace WireMock.Server; public partial class WireMockServer { - private void ConvertMappingsAndRegisterAsRespondProvider(MappingModel[] mappingModels, string? path = null) + private void ConvertMappingsAndRegisterAsRespondProvider(IReadOnlyList mappingModels, string? path = null) { var duplicateGuids = mappingModels .Where(m => m.Guid != null) @@ -46,7 +47,7 @@ private void ConvertMappingsAndRegisterAsRespondProvider(MappingModel[] mappingM } var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true); - + if (guid != null) { respondProvider = respondProvider.WithGuid(guid.Value); @@ -205,11 +206,11 @@ private void ConvertMappingsAndRegisterAsRespondProvider(MappingModel[] mappingM { foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null)) { - requestBuilder = requestBuilder.WithCookie( - cookieModel.Name, - cookieModel.IgnoreCase == true, - cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch, - cookieModel.Matchers!.Select(_matcherMapper.Map).OfType().ToArray()); + requestBuilder = requestBuilder.WithCookie( + cookieModel.Name, + cookieModel.IgnoreCase == true, + cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch, + cookieModel.Matchers!.Select(_matcherMapper.Map).OfType().ToArray()); } } diff --git a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs new file mode 100644 index 000000000..7b7f4145d --- /dev/null +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -0,0 +1,59 @@ +#if OPENAPIPARSER +using System; +using System.Linq; +using System.Net; +using WireMock.Constants; +using WireMock.Net.OpenApiParser; +using WireMock.RequestBuilders; +using WireMock.ResponseProviders; + +namespace WireMock.Server; + +public partial class WireMockServer +{ + private const string AdminOpenApi = "/__admin/openapi"; + + private void InitOpenApiParserAdmin() + { + Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost().WithBody(b => b != null)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ConvertOpenApiToMappings)); + + Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost().WithBody(b => b != null)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SaveOpenApiToMappings)); + + } + + private IResponseMessage ConvertOpenApiToMappings(IRequestMessage requestMessage) + { + try + { + var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); + return diagnostic.Errors.Any() ? ToJson(diagnostic, false, HttpStatusCode.BadRequest) : ToJson(mappingModels); + } + catch (Exception e) + { + _settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e); + return ResponseMessageBuilder.Create(e.ToString(), HttpStatusCode.BadRequest); + } + } + + private IResponseMessage SaveOpenApiToMappings(IRequestMessage requestMessage) + { + try + { + var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); + if (diagnostic.Errors.Any()) + { + return ToJson(diagnostic, false, HttpStatusCode.BadRequest); + } + + ConvertMappingsAndRegisterAsRespondProvider(mappingModels); + + return ResponseMessageBuilder.Create("OpenApi document converted to Mappings", HttpStatusCode.Created); + } + catch (Exception e) + { + _settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e); + return ResponseMessageBuilder.Create(e.ToString(), HttpStatusCode.BadRequest); + } + } +} +#endif \ No newline at end of file diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index fb0d137c8..8eafb108e 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -46,6 +46,10 @@ USE_ASPNETCORE;NET46 + + OPENAPIPARSER + + @@ -189,4 +193,8 @@ + + + + \ No newline at end of file From 6589eb133557efc43636c214b9c43e0d35e5d39f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 28 Mar 2023 19:23:48 +0200 Subject: [PATCH 09/21] private const string AdminOpenApi = "/__admin/openapi"; --- .../ResponseBuilders/IProxyResponseBuilder.cs | 2 +- .../Server/WireMockServer.OpenApiParser.cs | 4 ++-- src/WireMock.Net/WireMock.Net.csproj | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/WireMock.Net/ResponseBuilders/IProxyResponseBuilder.cs b/src/WireMock.Net/ResponseBuilders/IProxyResponseBuilder.cs index 716cdd0dc..e5ff1d01a 100644 --- a/src/WireMock.Net/ResponseBuilders/IProxyResponseBuilder.cs +++ b/src/WireMock.Net/ResponseBuilders/IProxyResponseBuilder.cs @@ -27,7 +27,7 @@ public interface IProxyResponseBuilder : IStatusCodeResponseBuilder /// WithProxy using . /// /// The proxy url. - /// The X509Certificate2. + /// The X509Certificate2. /// A . IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate); } \ 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 7b7f4145d..4e36a1324 100644 --- a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -15,9 +15,9 @@ public partial class WireMockServer private void InitOpenApiParserAdmin() { - Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost().WithBody(b => b != null)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ConvertOpenApiToMappings)); + Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ConvertOpenApiToMappings)); - Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost().WithBody(b => b != null)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SaveOpenApiToMappings)); + Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SaveOpenApiToMappings)); } diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj index 8eafb108e..2759924ec 100644 --- a/src/WireMock.Net/WireMock.Net.csproj +++ b/src/WireMock.Net/WireMock.Net.csproj @@ -35,19 +35,19 @@ - NETSTANDARD;USE_ASPNETCORE + $(DefineConstants);NETSTANDARD;USE_ASPNETCORE - USE_ASPNETCORE + $(DefineConstants);USE_ASPNETCORE - USE_ASPNETCORE;NET46 + $(DefineConstants);USE_ASPNETCORE;NET46 - - OPENAPIPARSER + + $(DefineConstants);OPENAPIPARSER @@ -194,7 +194,7 @@ - + \ No newline at end of file From 81a776587a3def483d671be82720ff0583e56e78 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 28 Mar 2023 19:41:05 +0200 Subject: [PATCH 10/21] fix test --- .../Server/WireMockServer.Admin.cs | 8 +++--- .../Server/WireMockServer.OpenApiParser.cs | 25 ++++++++----------- .../WireMockServer.Proxy.cs | 2 +- .../WireMockServer.Settings.cs | 6 ++--- 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index e6fc65117..d74c2e6b4 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -37,6 +37,8 @@ public partial class WireMockServer private const string AdminRequests = "/__admin/requests"; private const string AdminSettings = "/__admin/settings"; private const string AdminScenarios = "/__admin/scenarios"; + private const string AdminOpenApi = "/__admin/openapi"; + private const string QueryParamReloadStaticMappings = "reloadStaticMappings"; private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567"); @@ -114,9 +116,9 @@ private void InitAdmin() Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingHead()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileHead)); Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete)); -#if OPENAPIPARSER - InitOpenApiParserAdmin(); -#endif + // __admin/openapi + Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ConvertOpenApiToMappings)); + Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SaveOpenApiToMappings)); } #endregion diff --git a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs index 4e36a1324..e9c96fe64 100644 --- a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -2,27 +2,16 @@ using System; using System.Linq; using System.Net; -using WireMock.Constants; using WireMock.Net.OpenApiParser; -using WireMock.RequestBuilders; -using WireMock.ResponseProviders; +#endif namespace WireMock.Server; public partial class WireMockServer { - private const string AdminOpenApi = "/__admin/openapi"; - - private void InitOpenApiParserAdmin() - { - Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ConvertOpenApiToMappings)); - - Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SaveOpenApiToMappings)); - - } - private IResponseMessage ConvertOpenApiToMappings(IRequestMessage requestMessage) { +#if OPENAPIPARSER try { var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); @@ -33,10 +22,14 @@ private IResponseMessage ConvertOpenApiToMappings(IRequestMessage requestMessage _settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e); return ResponseMessageBuilder.Create(e.ToString(), HttpStatusCode.BadRequest); } +#else + return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400); +#endif } private IResponseMessage SaveOpenApiToMappings(IRequestMessage requestMessage) { +#if OPENAPIPARSER try { var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic); @@ -54,6 +47,8 @@ private IResponseMessage SaveOpenApiToMappings(IRequestMessage requestMessage) _settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e); return ResponseMessageBuilder.Create(e.ToString(), HttpStatusCode.BadRequest); } +#else + return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400); +#endif } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs index 7955222b3..25cc876c8 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs @@ -117,7 +117,7 @@ public async Task WireMockServer_Proxy_AdminTrue_With_SaveMapping_Is_True_And_Sa } // Assert - server.Mappings.Should().HaveCount(32); + server.Mappings.Should().HaveCount(34); } [Fact] diff --git a/test/WireMock.Net.Tests/WireMockServer.Settings.cs b/test/WireMock.Net.Tests/WireMockServer.Settings.cs index 83413879e..e0974cb25 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Settings.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Settings.cs @@ -81,7 +81,7 @@ public void WireMockServer_WireMockServerSettings_PriorityFromAllAdminMappingsIs // Assert server.Mappings.Should().NotBeNull(); - server.Mappings.Should().HaveCount(30); + server.Mappings.Should().HaveCount(32); server.Mappings.All(m => m.Priority == WireMockConstants.AdminPriority).Should().BeTrue(); } @@ -100,9 +100,9 @@ public void WireMockServer_WireMockServerSettings_ProxyAndRecordSettings_ProxyPr // Assert server.Mappings.Should().NotBeNull(); - server.Mappings.Should().HaveCount(31); + server.Mappings.Should().HaveCount(33); - server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(30); + server.Mappings.Count(m => m.Priority == WireMockConstants.AdminPriority).Should().Be(32); server.Mappings.Count(m => m.Priority == WireMockConstants.ProxyPriority).Should().Be(1); } From e9bf7379fac40dd7868c4f85c51fab9591586d09 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 28 Mar 2023 20:09:05 +0200 Subject: [PATCH 11/21] rnd --- .../Utils/ExampleValueGenerator.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs index fc25c8986..469f32b68 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs @@ -1,7 +1,8 @@ -using System; using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; using Stef.Validation; using WireMock.Net.OpenApiParser.Extensions; using WireMock.Net.OpenApiParser.Settings; @@ -120,8 +121,7 @@ public object GetExampleValue(OpenApiSchema? schema) { if (schemaEnum?.Count > 0) { - int maxValue = schemaEnum.Count - 1; - int randomEnum = new Random().Next(0, maxValue); + int randomEnum = RandomizerFactory.GetRandomizer(new FieldOptionsInteger { Min = 0, Max = schemaEnum.Count - 1 }).Generate() ?? 42; return schemaEnum[randomEnum]; } From 3edfce45cad7dea5e418fe63a04ae9f1007e785b Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 29 Mar 2023 17:20:25 +0200 Subject: [PATCH 12/21] . --- .../Mappers/OpenApiPathsMapper.cs | 8 ++++---- src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs index e88e31234..49d4b5995 100644 --- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs +++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs @@ -31,14 +31,14 @@ public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) _exampleValueGenerator = new ExampleValueGenerator(settings); } - public IReadOnlyList ToMappingModels(OpenApiPaths paths, IList servers) + public IReadOnlyList ToMappingModels(OpenApiPaths? paths, IList servers) { - return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray(); + return paths?.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray() ?? Array.Empty(); } - private IReadOnlyList MapPaths(OpenApiPaths paths, IList servers) + private IReadOnlyList MapPaths(OpenApiPaths? paths, IList servers) { - return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray(); + return paths?.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray() ?? Array.Empty(); } private IReadOnlyList MapPath(string path, OpenApiPathItem pathItem, IList servers) diff --git a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs index e9c96fe64..42a31cfb4 100644 --- a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -20,7 +20,7 @@ private IResponseMessage ConvertOpenApiToMappings(IRequestMessage requestMessage catch (Exception e) { _settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e); - return ResponseMessageBuilder.Create(e.ToString(), HttpStatusCode.BadRequest); + return ResponseMessageBuilder.Create(e.Message, HttpStatusCode.BadRequest); } #else return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400); @@ -45,7 +45,7 @@ private IResponseMessage SaveOpenApiToMappings(IRequestMessage requestMessage) catch (Exception e) { _settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e); - return ResponseMessageBuilder.Create(e.ToString(), HttpStatusCode.BadRequest); + return ResponseMessageBuilder.Create(e.Message, HttpStatusCode.BadRequest); } #else return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400); From 810ed22764588d5def98bd745fd049311b7d091f Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 29 Mar 2023 17:26:30 +0200 Subject: [PATCH 13/21] urldetails --- src/WireMock.Net/Models/UrlDetails.cs | 76 +++++++++++++-------------- 1 file changed, 36 insertions(+), 40 deletions(-) diff --git a/src/WireMock.Net/Models/UrlDetails.cs b/src/WireMock.Net/Models/UrlDetails.cs index 9459a0c19..7f227fbd5 100644 --- a/src/WireMock.Net/Models/UrlDetails.cs +++ b/src/WireMock.Net/Models/UrlDetails.cs @@ -1,51 +1,47 @@ -using System; +using System; using Stef.Validation; -namespace WireMock.Models +namespace WireMock.Models; + +/// +/// UrlDetails +/// +public class UrlDetails { /// - /// UrlDetails + /// Gets the url (relative). /// - public class UrlDetails - { - /// - /// Gets the url (relative). - /// - public Uri Url { get; } + public Uri Url { get; } - /// - /// Gets the AbsoluteUrl. - /// - public Uri AbsoluteUrl { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The URL. - public UrlDetails(string url) : this(new Uri(url)) - { - } + /// + /// Gets the AbsoluteUrl. + /// + public Uri AbsoluteUrl { get; } - /// - /// Initializes a new instance of the class. - /// - /// The URL. - public UrlDetails(Uri url) : this(url, url) - { - } + /// + /// Initializes a new instance of the class. + /// + /// The URL. + public UrlDetails(string url) : this(new Uri(url)) + { + } - /// - /// Initializes a new instance of the class. - /// - /// The absolute URL. - /// The URL (relative). - public UrlDetails(Uri absoluteUrl, Uri url) - { - Guard.NotNull(absoluteUrl, nameof(absoluteUrl)); - Guard.NotNull(url, nameof(url)); + /// + /// Initializes a new instance of the class. + /// + /// The URL. + public UrlDetails(Uri url) : this(url, url) + { + } - AbsoluteUrl = absoluteUrl; - Url = url; - } + /// + /// Initializes a new instance of the class. + /// + /// The absolute URL. + /// The URL (relative). + public UrlDetails(Uri absoluteUrl, Uri url) + { + AbsoluteUrl = Guard.NotNull(absoluteUrl); + Url = Guard.NotNull(url); } } \ No newline at end of file From 1c21f07f085142cbe87562bd7f4b197dfa8964e2 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 29 Mar 2023 17:27:35 +0200 Subject: [PATCH 14/21] 0 --- src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs index 469f32b68..5da407ac7 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs @@ -121,7 +121,7 @@ public object GetExampleValue(OpenApiSchema? schema) { if (schemaEnum?.Count > 0) { - int randomEnum = RandomizerFactory.GetRandomizer(new FieldOptionsInteger { Min = 0, Max = schemaEnum.Count - 1 }).Generate() ?? 42; + var randomEnum = RandomizerFactory.GetRandomizer(new FieldOptionsInteger { Min = 0, Max = schemaEnum.Count - 1 }).Generate() ?? 0; return schemaEnum[randomEnum]; } From 63a2c18da636b3b03d001aa4c3276f849ac81c89 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 29 Mar 2023 22:04:13 +0200 Subject: [PATCH 15/21] , --- examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs | 4 ++-- src/WireMock.Net/IMapping.cs | 4 ++-- src/WireMock.Net/Mapping.cs | 4 ++-- src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs | 5 ++++- ...thoutMethods_ShouldReturnCorrectMappingModel.verified.txt | 3 +-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs b/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs index ff7105953..48f1577a1 100644 --- a/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs +++ b/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs @@ -24,8 +24,8 @@ static class Program static async Task Main(string[] args) { - await TestAsync().ConfigureAwait(false); - return; + //await TestAsync().ConfigureAwait(false); + //return; XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config")); diff --git a/src/WireMock.Net/IMapping.cs b/src/WireMock.Net/IMapping.cs index 86f35dd50..b53caa0a0 100644 --- a/src/WireMock.Net/IMapping.cs +++ b/src/WireMock.Net/IMapping.cs @@ -121,7 +121,7 @@ public interface IMapping /// /// Use Fire and Forget for the defined webhook(s). [Optional] /// - bool? UseWebhooksFireAndForget { get; set; } + bool? UseWebhooksFireAndForget { get; } /// /// Data Object which can be used when WithTransformer is used. @@ -130,7 +130,7 @@ public interface IMapping /// lookup data "1" /// /// - object? Data { get; set; } + object? Data { get; } /// /// ProvideResponseAsync diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index e5823a1e8..6323fbe22 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -67,13 +67,13 @@ public class Mapping : IMapping public IWebhook[]? Webhooks { get; } /// - public bool? UseWebhooksFireAndForget { get; set; } + public bool? UseWebhooksFireAndForget { get; } /// public ITimeSettings? TimeSettings { get; } /// - public object? Data { get; set; } + public object? Data { get; } /// /// Initializes a new instance of the class. diff --git a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs index 89886b404..3786d839a 100644 --- a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs +++ b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs @@ -108,7 +108,10 @@ private void ConvertMappingsAndRegisterAsRespondProvider(IReadOnlyList Date: Fri, 31 Mar 2023 14:04:44 +0200 Subject: [PATCH 16/21] . --- .../WireMock.Net.OpenApiParser.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj index 12636783c..ee1972f46 100644 --- a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj +++ b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj @@ -4,7 +4,7 @@ An OpenApi (swagger) parser to generate MappingModel or mapping.json file. net46;netstandard2.0;netstandard2.1 true - wiremock;openapi;OAS;converter;parser;openapiparser + wiremock;openapi;OAS;raml;converter;parser;openapiparser {D3804228-91F4-4502-9595-39584E5AADAD} true ../WireMock.Net/WireMock.Net.ruleset From 7299c721660ee818ee9db522eb3538242cd52778 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 1 Apr 2023 12:12:32 +0200 Subject: [PATCH 17/21] tests --- WireMock.Net Solution.sln.DotSettings | 1 + .../Mappers/OpenApiPathsMapper.cs | 14 +- .../WireMockOpenApiParserExampleValues.cs | 2 +- .../Utils/ExampleValueGenerator.cs | 17 +- .../IWireMockAdminApi.cs | 16 + .../Server/WireMockServer.Admin.cs | 4 +- .../Server/WireMockServer.OpenApiParser.cs | 4 +- .../OpenApiParser/petstore-openapi3.json | 840 ++++++++++++++++++ .../OpenApiParser/petstore.yml | 730 +++++++++++++++ .../WireMock.Net.Tests.csproj | 6 + .../WireMockAdminApiTests.cs | 77 ++ 11 files changed, 1689 insertions(+), 22 deletions(-) create mode 100644 test/WireMock.Net.Tests/OpenApiParser/petstore-openapi3.json create mode 100644 test/WireMock.Net.Tests/OpenApiParser/petstore.yml diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings index 92eaabab2..6f5a8ca10 100644 --- a/WireMock.Net Solution.sln.DotSettings +++ b/WireMock.Net Solution.sln.DotSettings @@ -27,6 +27,7 @@ True True True + True True True True diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs index 49d4b5995..09a5ae4ca 100644 --- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs +++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs @@ -33,12 +33,22 @@ public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) public IReadOnlyList ToMappingModels(OpenApiPaths? paths, IList servers) { - return paths?.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray() ?? Array.Empty(); + return paths? + .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?.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x).ToArray() ?? Array.Empty(); + return paths? + .OrderBy(p => p.Key) + .Select(p => MapPath(p.Key, p.Value, servers)) + .SelectMany(x => x) + .ToArray() ?? + Array.Empty(); } private IReadOnlyList MapPath(string path, OpenApiPathItem pathItem, IList servers) diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs index 6b3af5096..5f550d195 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs @@ -36,5 +36,5 @@ public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleV public virtual string String { get; set; } = "example-string"; /// - public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema(); + public virtual OpenApiSchema? Schema { get; set; } = new(); } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs index 5da407ac7..9edde43d3 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs @@ -1,8 +1,6 @@ -using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; -using RandomDataGenerator.FieldOptions; -using RandomDataGenerator.Randomizers; using Stef.Validation; using WireMock.Net.OpenApiParser.Extensions; using WireMock.Net.OpenApiParser.Settings; @@ -39,7 +37,7 @@ public ExampleValueGenerator(WireMockOpenApiParserSettings settings) public object GetExampleValue(OpenApiSchema? schema) { var schemaExample = schema?.Example; - var schemaEnum = GetRandomEnumValue(schema?.Enum); + var schemaEnum = schema?.Enum?.FirstOrDefault(); _exampleValues.Schema = schema; @@ -116,15 +114,4 @@ public object GetExampleValue(OpenApiSchema? schema) } } } - - private static IOpenApiAny? GetRandomEnumValue(IList? schemaEnum) - { - if (schemaEnum?.Count > 0) - { - var randomEnum = RandomizerFactory.GetRandomizer(new FieldOptionsInteger { Min = 0, Max = schemaEnum.Count - 1 }).Generate() ?? 0; - return schemaEnum[randomEnum]; - } - - return null; - } } \ No newline at end of file diff --git a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs index d304e9db5..92fdcb709 100644 --- a/src/WireMock.Net.RestClient/IWireMockAdminApi.cs +++ b/src/WireMock.Net.RestClient/IWireMockAdminApi.cs @@ -280,4 +280,20 @@ public interface IWireMockAdminApi /// The optional cancellationToken. [Head("files/{filename}")] Task FileExistsAsync([Path] string filename, CancellationToken cancellationToken = default); + + /// + /// Convert an OpenApi / RAML document to mappings. + /// + /// The OpenApi or RAML document as text. + /// The optional cancellationToken. + [Post("openapi/convert")] + Task> OpenApiConvertAsync([Body] string text, CancellationToken cancellationToken = default); + + /// + /// Convert an OpenApi / RAML document to mappings and save these. + /// + /// The OpenApi or RAML document as text. + /// The optional cancellationToken. + [Post("openapi/save")] + Task OpenApiSaveAsync([Body] string text, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index d74c2e6b4..1864cc11d 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -117,8 +117,8 @@ private void InitAdmin() Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete)); // __admin/openapi - Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ConvertOpenApiToMappings)); - Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(SaveOpenApiToMappings)); + Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiConvertToMappings)); + Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiSaveToMappings)); } #endregion diff --git a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs index 42a31cfb4..38284c611 100644 --- a/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs +++ b/src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs @@ -9,7 +9,7 @@ namespace WireMock.Server; public partial class WireMockServer { - private IResponseMessage ConvertOpenApiToMappings(IRequestMessage requestMessage) + private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage) { #if OPENAPIPARSER try @@ -27,7 +27,7 @@ private IResponseMessage ConvertOpenApiToMappings(IRequestMessage requestMessage #endif } - private IResponseMessage SaveOpenApiToMappings(IRequestMessage requestMessage) + private IResponseMessage OpenApiSaveToMappings(IRequestMessage requestMessage) { #if OPENAPIPARSER try diff --git a/test/WireMock.Net.Tests/OpenApiParser/petstore-openapi3.json b/test/WireMock.Net.Tests/OpenApiParser/petstore-openapi3.json new file mode 100644 index 000000000..9f4c45960 --- /dev/null +++ b/test/WireMock.Net.Tests/OpenApiParser/petstore-openapi3.json @@ -0,0 +1,840 @@ +{ + "openapi": "3.0.2", + "info": { + "title": "Swagger Petstore - OpenAPI 3.0", + "description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) ", + "termsOfService": "http://swagger.io/terms/", + "contact": { "email": "apiteam@swagger.io" }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0.4" + }, + "externalDocs": { + "description": "Find out more about Swagger", + "url": "http://swagger.io" + }, + "servers": [ { "url": "/api/v3" } ], + "tags": [ + { + "name": "pet", + "description": "Everything about your Pets", + "externalDocs": { + "description": "Find out more", + "url": "http://swagger.io" + } + }, + { + "name": "store", + "description": "Operations about user" + }, + { + "name": "user", + "description": "Access to Petstore orders", + "externalDocs": { + "description": "Find out more about our store", + "url": "http://swagger.io" + } + } + ], + "paths": { + "/pet": { + "put": { + "tags": [ "pet" ], + "summary": "Update an existing pet", + "description": "Update an existing pet by Id", + "operationId": "updatePet", + "requestBody": { + "description": "Update an existent pet in the store", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } + } + }, + "400": { "description": "Invalid ID supplied" }, + "404": { "description": "Pet not found" }, + "405": { "description": "Validation exception" } + }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + }, + "post": { + "tags": [ "pet" ], + "summary": "Add a new pet to the store", + "description": "Add a new pet to the store", + "operationId": "addPet", + "requestBody": { + "description": "Create a new pet in the store", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } } + }, + "required": true + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } + } + }, + "405": { "description": "Invalid input" } + }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + } + }, + "/pet/findByStatus": { + "get": { + "tags": [ "pet" ], + "summary": "Finds Pets by status", + "description": "Multiple status values can be provided with comma separated strings", + "operationId": "findPetsByStatus", + "parameters": [ + { + "name": "status", + "in": "query", + "description": "Status values that need to be considered for filter", + "required": false, + "explode": true, + "schema": { + "type": "string", + "default": "available", + "enum": [ "available", "pending", "sold" ] + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/Pet" } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/Pet" } + } + } + } + }, + "400": { "description": "Invalid status value" } + }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + } + }, + "/pet/findByTags": { + "get": { + "tags": [ "pet" ], + "summary": "Finds Pets by tags", + "description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.", + "operationId": "findPetsByTags", + "parameters": [ + { + "name": "tags", + "in": "query", + "description": "Tags to filter by", + "required": false, + "explode": true, + "schema": { + "type": "array", + "items": { "type": "string" } + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/Pet" } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/Pet" } + } + } + } + }, + "400": { "description": "Invalid tag value" } + }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + } + }, + "/pet/{petId}": { + "get": { + "tags": [ "pet" ], + "summary": "Find pet by ID", + "description": "Returns a single pet", + "operationId": "getPetById", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to return", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } } + } + }, + "400": { "description": "Invalid ID supplied" }, + "404": { "description": "Pet not found" } + }, + "security": [ + { "api_key": [] }, + { "petstore_auth": [ "write:pets", "read:pets" ] } + ] + }, + "post": { + "tags": [ "pet" ], + "summary": "Updates a pet in the store with form data", + "description": "", + "operationId": "updatePetWithForm", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet that needs to be updated", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "name", + "in": "query", + "description": "Name of pet that needs to be updated", + "schema": { "type": "string" } + }, + { + "name": "status", + "in": "query", + "description": "Status of pet that needs to be updated", + "schema": { "type": "string" } + } + ], + "responses": { "405": { "description": "Invalid input" } }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + }, + "delete": { + "tags": [ "pet" ], + "summary": "Deletes a pet", + "description": "", + "operationId": "deletePet", + "parameters": [ + { + "name": "api_key", + "in": "header", + "description": "", + "required": false, + "schema": { "type": "string" } + }, + { + "name": "petId", + "in": "path", + "description": "Pet id to delete", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { "400": { "description": "Invalid pet value" } }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + } + }, + "/pet/{petId}/uploadImage": { + "post": { + "tags": [ "pet" ], + "summary": "uploads an image", + "description": "", + "operationId": "uploadFile", + "parameters": [ + { + "name": "petId", + "in": "path", + "description": "ID of pet to update", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + }, + { + "name": "additionalMetadata", + "in": "query", + "description": "Additional Metadata", + "required": false, + "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } } + } + }, + "security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ] + } + }, + "/store/inventory": { + "get": { + "tags": [ "store" ], + "summary": "Returns pet inventories by status", + "description": "Returns a map of status codes to quantities", + "operationId": "getInventory", + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": { + "type": "integer", + "format": "int32" + } + } + } + } + } + }, + "security": [ { "api_key": [] } ] + } + }, + "/store/order": { + "post": { + "tags": [ "store" ], + "summary": "Place an order for a pet", + "description": "Place a new order in the store", + "operationId": "placeOrder", + "requestBody": { + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/Order" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/Order" } }, + "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Order" } } + } + }, + "responses": { + "200": { + "description": "successful operation", + "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } } + }, + "405": { "description": "Invalid input" } + } + } + }, + "/store/order/{orderId}": { + "get": { + "tags": [ "store" ], + "summary": "Find purchase order by ID", + "description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions", + "operationId": "getOrderById", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of order that needs to be fetched", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { "schema": { "$ref": "#/components/schemas/Order" } }, + "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } + } + }, + "400": { "description": "Invalid ID supplied" }, + "404": { "description": "Order not found" } + } + }, + "delete": { + "tags": [ "store" ], + "summary": "Delete purchase order by ID", + "description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "operationId": "deleteOrder", + "parameters": [ + { + "name": "orderId", + "in": "path", + "description": "ID of the order that needs to be deleted", + "required": true, + "schema": { + "type": "integer", + "format": "int64" + } + } + ], + "responses": { + "400": { "description": "Invalid ID supplied" }, + "404": { "description": "Order not found" } + } + } + }, + "/user": { + "post": { + "tags": [ "user" ], + "summary": "Create user", + "description": "This can only be done by the logged in user.", + "operationId": "createUser", + "requestBody": { + "description": "Created user object", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/User" } } + } + }, + "responses": { + "default": { + "description": "successful operation", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/User" } } + } + } + } + } + }, + "/user/createWithList": { + "post": { + "tags": [ "user" ], + "summary": "Creates list of users with given input array", + "description": "Creates list of users with given input array", + "operationId": "createUsersWithListInput", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/User" } + } + } + } + }, + "responses": { + "200": { + "description": "Successful operation", + "content": { + "application/xml": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/json": { "schema": { "$ref": "#/components/schemas/User" } } + } + }, + "default": { "description": "successful operation" } + } + } + }, + "/user/login": { + "get": { + "tags": [ "user" ], + "summary": "Logs user into the system", + "description": "", + "operationId": "loginUser", + "parameters": [ + { + "name": "username", + "in": "query", + "description": "The user name for login", + "required": false, + "schema": { "type": "string" } + }, + { + "name": "password", + "in": "query", + "description": "The password for login in clear text", + "required": false, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "successful operation", + "headers": { + "X-Rate-Limit": { + "description": "calls per hour allowed by the user", + "schema": { + "type": "integer", + "format": "int32" + } + }, + "X-Expires-After": { + "description": "date in UTC when toekn expires", + "schema": { + "type": "string", + "format": "date-time" + } + } + }, + "content": { + "application/xml": { "schema": { "type": "string" } }, + "application/json": { "schema": { "type": "string" } } + } + }, + "400": { "description": "Invalid username/password supplied" } + } + } + }, + "/user/logout": { + "get": { + "tags": [ "user" ], + "summary": "Logs out current logged in user session", + "description": "", + "operationId": "logoutUser", + "parameters": [], + "responses": { "default": { "description": "successful operation" } } + } + }, + "/user/{username}": { + "get": { + "tags": [ "user" ], + "summary": "Get user by user name", + "description": "", + "operationId": "getUserByName", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be fetched. Use user1 for testing. ", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "200": { + "description": "successful operation", + "content": { + "application/xml": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/json": { "schema": { "$ref": "#/components/schemas/User" } } + } + }, + "400": { "description": "Invalid username supplied" }, + "404": { "description": "User not found" } + } + }, + "put": { + "tags": [ "user" ], + "summary": "Update user", + "description": "This can only be done by the logged in user.", + "operationId": "updateUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "name that need to be deleted", + "required": true, + "schema": { "type": "string" } + } + ], + "requestBody": { + "description": "Update an existent user in the store", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/User" } }, + "application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/User" } } + } + }, + "responses": { "default": { "description": "successful operation" } } + }, + "delete": { + "tags": [ "user" ], + "summary": "Delete user", + "description": "This can only be done by the logged in user.", + "operationId": "deleteUser", + "parameters": [ + { + "name": "username", + "in": "path", + "description": "The name that needs to be deleted", + "required": true, + "schema": { "type": "string" } + } + ], + "responses": { + "400": { "description": "Invalid username supplied" }, + "404": { "description": "User not found" } + } + } + } + }, + "components": { + "schemas": { + "Order": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "petId": { + "type": "integer", + "format": "int64", + "example": 198772 + }, + "quantity": { + "type": "integer", + "format": "int32", + "example": 7 + }, + "shipDate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "description": "Order Status", + "example": "approved", + "enum": [ "placed", "approved", "delivered" ] + }, + "complete": { "type": "boolean" } + }, + "xml": { "name": "order" } + }, + "Customer": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 100000 + }, + "username": { + "type": "string", + "example": "fehguy" + }, + "address": { + "type": "array", + "xml": { + "name": "addresses", + "wrapped": true + }, + "items": { "$ref": "#/components/schemas/Address" } + } + }, + "xml": { "name": "customer" } + }, + "Address": { + "type": "object", + "properties": { + "street": { + "type": "string", + "example": "437 Lytton" + }, + "city": { + "type": "string", + "example": "Palo Alto" + }, + "state": { + "type": "string", + "example": "CA" + }, + "zip": { + "type": "string", + "example": "94301" + } + }, + "xml": { "name": "address" } + }, + "Category": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 1 + }, + "name": { + "type": "string", + "example": "Dogs" + } + }, + "xml": { "name": "category" } + }, + "User": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "username": { + "type": "string", + "example": "theUser" + }, + "firstName": { + "type": "string", + "example": "John" + }, + "lastName": { + "type": "string", + "example": "James" + }, + "email": { + "type": "string", + "example": "john@email.com" + }, + "password": { + "type": "string", + "example": "12345" + }, + "phone": { + "type": "string", + "example": "12345" + }, + "userStatus": { + "type": "integer", + "description": "User Status", + "format": "int32", + "example": 1 + } + }, + "xml": { "name": "user" } + }, + "Tag": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { "type": "string" } + }, + "xml": { "name": "tag" } + }, + "Pet": { + "required": [ "name", "photoUrls" ], + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64", + "example": 10 + }, + "name": { + "type": "string", + "example": "doggie" + }, + "category": { "$ref": "#/components/schemas/Category" }, + "photoUrls": { + "type": "array", + "xml": { "wrapped": true }, + "items": { + "type": "string", + "xml": { "name": "photoUrl" } + } + }, + "tags": { + "type": "array", + "xml": { "wrapped": true }, + "items": { "$ref": "#/components/schemas/Tag" } + }, + "status": { + "type": "string", + "description": "pet status in the store", + "enum": [ "available", "pending", "sold" ] + } + }, + "xml": { "name": "pet" } + }, + "ApiResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int32" + }, + "type": { "type": "string" }, + "message": { "type": "string" } + }, + "xml": { "name": "##default" } + } + }, + "requestBodies": { + "Pet": { + "description": "Pet object that needs to be added to the store", + "content": { + "application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }, + "application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } } + } + }, + "UserArray": { + "description": "List of user object", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { "$ref": "#/components/schemas/User" } + } + } + } + } + }, + "securitySchemes": { + "petstore_auth": { + "type": "oauth2", + "flows": { + "implicit": { + "authorizationUrl": "https://petstore.swagger.io/oauth/authorize", + "scopes": { + "write:pets": "modify pets in your account", + "read:pets": "read your pets" + } + } + } + }, + "api_key": { + "type": "apiKey", + "name": "api_key", + "in": "header" + } + } + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/OpenApiParser/petstore.yml b/test/WireMock.Net.Tests/OpenApiParser/petstore.yml new file mode 100644 index 000000000..9383878a5 --- /dev/null +++ b/test/WireMock.Net.Tests/OpenApiParser/petstore.yml @@ -0,0 +1,730 @@ +swagger: '2.0' +info: + description: 'This is a sample server Petstore server. Copied from https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/src/test/resources/2_0/petstore.yaml.' + version: 1.0.0 + title: Swagger Petstore + termsOfService: 'http://swagger.io/terms/' + contact: + email: apiteam@swagger.io + license: + name: Apache-2.0 + url: 'http://www.apache.org/licenses/LICENSE-2.0.html' +host: petstore.swagger.io +basePath: /v2 +tags: + - name: pet + description: Everything about your Pets + externalDocs: + description: Find out more + url: 'http://swagger.io' + - name: store + description: Access to Petstore orders + - name: user + description: Operations about user + externalDocs: + description: Find out more about our store + url: 'http://swagger.io' +schemes: + - http +paths: + /pet: + post: + tags: + - pet + summary: Add a new pet to the store + description: '' + operationId: addPet + consumes: + - application/json + - application/xml + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + $ref: '#/definitions/Pet' + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + put: + tags: + - pet + summary: Update an existing pet + description: '' + operationId: updatePet + consumes: + - application/json + - application/xml + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: Pet object that needs to be added to the store + required: true + schema: + $ref: '#/definitions/Pet' + responses: + '400': + description: Invalid ID supplied + '404': + description: Pet not found + '405': + description: Validation exception + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /pet/findByStatus: + get: + tags: + - pet + summary: Finds Pets by status + description: Multiple status values can be provided with comma separated strings + operationId: findPetsByStatus + produces: + - application/xml + - application/json + parameters: + - name: status + in: query + description: Status values that need to be considered for filter + required: true + type: array + items: + type: string + enum: + - available + - pending + - sold + default: available + collectionFormat: csv + responses: + '200': + description: successful operation + schema: + type: array + items: + $ref: '#/definitions/Pet' + '400': + description: Invalid status value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /pet/findByTags: + get: + tags: + - pet + summary: Finds Pets by tags + description: 'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.' + operationId: findPetsByTags + produces: + - application/xml + - application/json + parameters: + - name: tags + in: query + description: Tags to filter by + required: true + type: array + items: + type: string + collectionFormat: csv + responses: + '200': + description: successful operation + schema: + type: array + items: + $ref: '#/definitions/Pet' + '400': + description: Invalid tag value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + deprecated: true + '/pet/{petId}': + get: + tags: + - pet + summary: Find pet by ID + description: Returns a single pet + operationId: getPetById + produces: + - application/xml + - application/json + parameters: + - name: petId + in: path + description: ID of pet to return + required: true + type: integer + format: int64 + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Pet' + '400': + description: Invalid ID supplied + '404': + description: Pet not found + security: + - api_key: [] + post: + tags: + - pet + summary: Updates a pet in the store with form data + description: '' + operationId: updatePetWithForm + consumes: + - application/x-www-form-urlencoded + produces: + - application/xml + - application/json + parameters: + - name: petId + in: path + description: ID of pet that needs to be updated + required: true + type: integer + format: int64 + - name: name + in: formData + description: Updated name of the pet + required: false + type: string + - name: status + in: formData + description: Updated status of the pet + required: false + type: string + responses: + '405': + description: Invalid input + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + delete: + tags: + - pet + summary: Deletes a pet + description: '' + operationId: deletePet + produces: + - application/xml + - application/json + parameters: + - name: api_key + in: header + required: false + type: string + - name: petId + in: path + description: Pet id to delete + required: true + type: integer + format: int64 + responses: + '400': + description: Invalid pet value + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + '/pet/{petId}/uploadImage': + post: + tags: + - pet + summary: uploads an image + description: '' + operationId: uploadFile + consumes: + - multipart/form-data + produces: + - application/json + parameters: + - name: petId + in: path + description: ID of pet to update + required: true + type: integer + format: int64 + - name: additionalMetadata + in: formData + description: Additional data to pass to server + required: false + type: string + - name: file + in: formData + description: file to upload + required: false + type: file + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/ApiResponse' + security: + - petstore_auth: + - 'write:pets' + - 'read:pets' + /store/inventory: + get: + tags: + - store + summary: Returns pet inventories by status + description: Returns a map of status codes to quantities + operationId: getInventory + produces: + - application/json + parameters: [] + responses: + '200': + description: successful operation + schema: + type: object + additionalProperties: + type: integer + format: int32 + security: + - api_key: [] + /store/order: + post: + tags: + - store + summary: Place an order for a pet + description: '' + operationId: placeOrder + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: order placed for purchasing the pet + required: true + schema: + $ref: '#/definitions/Order' + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Order' + '400': + description: Invalid Order + '/store/order/{orderId}': + get: + tags: + - store + summary: Find purchase order by ID + description: 'For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions' + operationId: getOrderById + produces: + - application/xml + - application/json + parameters: + - name: orderId + in: path + description: ID of pet that needs to be fetched + required: true + type: integer + maximum: 5 + minimum: 1 + format: int64 + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/Order' + '400': + description: Invalid ID supplied + '404': + description: Order not found + delete: + tags: + - store + summary: Delete purchase order by ID + description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors + operationId: deleteOrder + produces: + - application/xml + - application/json + parameters: + - name: orderId + in: path + description: ID of the order that needs to be deleted + required: true + type: string + responses: + '400': + description: Invalid ID supplied + '404': + description: Order not found + /user: + post: + tags: + - user + summary: Create user + description: This can only be done by the logged in user. + operationId: createUser + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: Created user object + required: true + schema: + $ref: '#/definitions/User' + responses: + default: + description: successful operation + /user/createWithArray: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithArrayInput + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: List of user object + required: true + schema: + type: array + items: + $ref: '#/definitions/User' + responses: + default: + description: successful operation + /user/createWithList: + post: + tags: + - user + summary: Creates list of users with given input array + description: '' + operationId: createUsersWithListInput + produces: + - application/xml + - application/json + parameters: + - in: body + name: body + description: List of user object + required: true + schema: + type: array + items: + $ref: '#/definitions/User' + responses: + default: + description: successful operation + /user/login: + get: + tags: + - user + summary: Logs user into the system + description: '' + operationId: loginUser + produces: + - application/xml + - application/json + parameters: + - name: username + in: query + description: The user name for login + required: true + type: string + - name: password + in: query + description: The password for login in clear text + required: true + type: string + responses: + '200': + description: successful operation + schema: + type: string + headers: + X-Rate-Limit: + type: integer + format: int32 + description: calls per hour allowed by the user + X-Expires-After: + type: string + format: date-time + description: date in UTC when toekn expires + '400': + description: Invalid username/password supplied + /user/logout: + get: + tags: + - user + summary: Logs out current logged in user session + description: '' + operationId: logoutUser + produces: + - application/xml + - application/json + parameters: [] + responses: + default: + description: successful operation + '/user/{username}': + get: + tags: + - user + summary: Get user by user name + description: '' + operationId: getUserByName + produces: + - application/xml + - application/json + parameters: + - name: username + in: path + description: 'The name that needs to be fetched. Use user1 for testing.' + required: true + type: string + responses: + '200': + description: successful operation + schema: + $ref: '#/definitions/User' + '400': + description: Invalid username supplied + '404': + description: User not found + put: + tags: + - user + summary: Updated user + description: This can only be done by the logged in user. + operationId: updateUser + produces: + - application/xml + - application/json + parameters: + - name: username + in: path + description: name that need to be deleted + required: true + type: string + - in: body + name: body + description: Updated user object + required: true + schema: + $ref: '#/definitions/User' + responses: + '400': + description: Invalid user supplied + '404': + description: User not found + delete: + tags: + - user + summary: Delete user + description: This can only be done by the logged in user. + operationId: deleteUser + produces: + - application/xml + - application/json + parameters: + - name: username + in: path + description: The name that needs to be deleted + required: true + type: string + responses: + '400': + description: Invalid username supplied + '404': + description: User not found +securityDefinitions: + petstore_auth: + type: oauth2 + authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog' + flow: implicit + scopes: + 'write:pets': modify pets in your account + 'read:pets': read your pets + api_key: + type: apiKey + name: api_key + in: header +definitions: + Order: + title: Pet Order + description: An order for a pets from the pet store + type: object + properties: + id: + type: integer + format: int64 + petId: + type: integer + format: int64 + quantity: + type: integer + format: int32 + shipDate: + type: string + format: date-time + status: + type: string + description: Order Status + enum: + - placed + - approved + - delivered + complete: + type: boolean + default: false + xml: + name: Order + Category: + title: Pet category + description: A category for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Category + User: + title: a User + description: A User who is purchasing from the pet store + type: object + properties: + id: + type: integer + format: int64 + username: + type: string + firstName: + type: string + lastName: + type: string + email: + type: string + password: + type: string + phone: + type: string + userStatus: + type: integer + format: int32 + description: User Status + xml: + name: User + Tag: + title: Pet Tag + description: A tag for a pet + type: object + properties: + id: + type: integer + format: int64 + name: + type: string + xml: + name: Tag + Pet: + title: a Pet + description: A pet for sale in the pet store + type: object + required: + - name + - photoUrls + properties: + id: + type: integer + format: int64 + category: + $ref: '#/definitions/Category' + name: + type: string + example: doggie + photoUrls: + type: array + xml: + name: photoUrl + wrapped: true + items: + type: string + tags: + type: array + xml: + name: tag + wrapped: true + items: + $ref: '#/definitions/Tag' + status: + type: string + description: pet status in the store + enum: + - available + - pending + - sold + xml: + name: Pet + ApiResponse: + title: An uploaded response + description: Describes the result of uploading an image resource + type: object + properties: + code: + type: integer + format: int32 + type: + type: string + message: + type: string + #issue: https://github.com/swagger-api/swagger-codegen/issues/7980 + Amount: + type: object + description: > + some description + properties: + value: + format: double + type: number + minimum: 0.01 + maximum: 1000000000000000 + description: > + some description + currency: + $ref: '#/definitions/Currency' + required: + - value + - currency + Currency: + type: string + pattern: '^[A-Z]{3,3}$' + description: > + some description +externalDocs: + description: Find out more about Swagger + url: 'http://swagger.io' \ No newline at end of file diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index 32c944099..006f88685 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -103,6 +103,12 @@ + + PreserveNewest + + + PreserveNewest + PreserveNewest diff --git a/test/WireMock.Net.Tests/WireMockAdminApiTests.cs b/test/WireMock.Net.Tests/WireMockAdminApiTests.cs index 55a60812c..8d794a4bf 100644 --- a/test/WireMock.Net.Tests/WireMockAdminApiTests.cs +++ b/test/WireMock.Net.Tests/WireMockAdminApiTests.cs @@ -1,5 +1,6 @@ #if !(NET452 || NET461 || NETCOREAPP3_1) using System; +using System.IO; using System.Linq; using System.Net.Http; using System.Net.Http.Headers; @@ -760,5 +761,81 @@ public async Task IWireMockAdminApi_GetMappingsCode() server.Stop(); } + + [Fact] + public async Task IWireMockAdminApi_OpenApiConvert_Yml() + { + // Arrange + var openApiDocument = await File.ReadAllTextAsync(Path.Combine("OpenApiParser", "petstore.yml")); + + var server = WireMockServer.StartWithAdminInterface(); + var api = RestClient.For(server.Url); + + // Act + var mappings = await api.OpenApiConvertAsync(openApiDocument).ConfigureAwait(false); + + // Assert + server.MappingModels.Should().BeEmpty(); + mappings.Should().HaveCount(20); + + server.Stop(); + } + + [Fact] + public async Task IWireMockAdminApi_OpenApiConvert_Json() + { + // Arrange + var openApiDocument = await File.ReadAllTextAsync(Path.Combine("OpenApiParser", "petstore-openapi3.json")); + + var server = WireMockServer.StartWithAdminInterface(); + var api = RestClient.For(server.Url); + + // Act + var mappings = await api.OpenApiConvertAsync(openApiDocument).ConfigureAwait(false); + + // Assert + server.MappingModels.Should().BeEmpty(); + mappings.Should().HaveCount(19); + + server.Stop(); + } + + [Fact] + public async Task IWireMockAdminApi_OpenApiSave_Json() + { + // Arrange + var openApiDocument = await File.ReadAllTextAsync(Path.Combine("OpenApiParser", "petstore-openapi3.json")); + + var server = WireMockServer.StartWithAdminInterface(); + var api = RestClient.For(server.Url); + + // Act + var statusModel = await api.OpenApiSaveAsync(openApiDocument).ConfigureAwait(false); + + // Assert + statusModel.Status.Should().Be("OpenApi document converted to Mappings"); + server.MappingModels.Should().HaveCount(19); + + server.Stop(); + } + + [Fact] + public async Task IWireMockAdminApi_OpenApiSave_Yml() + { + // Arrange + var openApiDocument = await File.ReadAllTextAsync(Path.Combine("OpenApiParser", "petstore.yml")); + + var server = WireMockServer.StartWithAdminInterface(); + var api = RestClient.For(server.Url); + + // Act + var mappings = await api.OpenApiConvertAsync(openApiDocument).ConfigureAwait(false); + + // Assert + server.MappingModels.Should().BeEmpty(); + mappings.Should().HaveCount(20); + + server.Stop(); + } } #endif \ No newline at end of file From 6b80d92ee38dca434568151633996dac5109287e Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 1 Apr 2023 18:12:55 +0200 Subject: [PATCH 18/21] . --- .../WireMockLog4NetLogger.cs | 69 ++++++++------- .../Serialization/SwaggerMapper.cs | 6 +- .../Util/CultureInfoUtilsTests.cs | 83 +++++++++++++++++++ 3 files changed, 121 insertions(+), 37 deletions(-) create mode 100644 test/WireMock.Net.Tests/Util/CultureInfoUtilsTests.cs diff --git a/examples/WireMock.Net.StandAlone.NETCoreApp/WireMockLog4NetLogger.cs b/examples/WireMock.Net.StandAlone.NETCoreApp/WireMockLog4NetLogger.cs index 27add78ac..f95113a45 100644 --- a/examples/WireMock.Net.StandAlone.NETCoreApp/WireMockLog4NetLogger.cs +++ b/examples/WireMock.Net.StandAlone.NETCoreApp/WireMockLog4NetLogger.cs @@ -1,44 +1,43 @@ -using System; +using System; using log4net; using Newtonsoft.Json; using WireMock.Admin.Requests; using WireMock.Logging; -namespace WireMock.Net.StandAlone.NETCoreApp +namespace WireMock.Net.StandAlone.NETCoreApp; + +internal class WireMockLog4NetLogger : IWireMockLogger { - internal class WireMockLog4NetLogger : IWireMockLogger + private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); + + public void Debug(string formatString, params object[] args) + { + Log.DebugFormat(formatString, args); + } + + public void Info(string formatString, params object[] args) + { + Log.InfoFormat(formatString, args); + } + + public void Warn(string formatString, params object[] args) + { + Log.WarnFormat(formatString, args); + } + + public void Error(string formatString, params object[] args) + { + Log.ErrorFormat(formatString, args); + } + + public void Error(string message, Exception exception) + { + Log.Error(message, exception); + } + + public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest) { - private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); - - public void Debug(string formatString, params object[] args) - { - Log.DebugFormat(formatString, args); - } - - public void Info(string formatString, params object[] args) - { - Log.InfoFormat(formatString, args); - } - - public void Warn(string formatString, params object[] args) - { - Log.WarnFormat(formatString, args); - } - - public void Error(string formatString, params object[] args) - { - Log.ErrorFormat(formatString, args); - } - - public void Error(string message, Exception exception) - { - Log.Error(message, exception); - } - - public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest) - { - string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented); - Log.DebugFormat("Admin[{0}] {1}", isAdminRequest, message); - } + string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented); + Log.DebugFormat("Admin[{0}] {1}", isAdminRequest, message); } } \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/SwaggerMapper.cs b/src/WireMock.Net/Serialization/SwaggerMapper.cs index 9439974d2..5cbdf502d 100644 --- a/src/WireMock.Net/Serialization/SwaggerMapper.cs +++ b/src/WireMock.Net/Serialization/SwaggerMapper.cs @@ -54,10 +54,12 @@ public static string ToSwagger(WireMockServer server) { operation.Parameters.Add(openApiParameter); } + foreach (var openApiParameter in MapRequestHeaders(mapping.Request.Headers)) { operation.Parameters.Add(openApiParameter); } + foreach (var openApiParameter in MapRequestCookies(mapping.Request.Cookies)) { operation.Parameters.Add(openApiParameter); @@ -94,7 +96,7 @@ public static string ToSwagger(WireMockServer server) return openApiDocument.ToJson(SchemaType.OpenApi3, Formatting.Indented); } - private static IEnumerable MapRequestQueryParameters(IList? queryParameters) + private static IReadOnlyList MapRequestQueryParameters(IList? queryParameters) { if (queryParameters == null) { @@ -146,7 +148,7 @@ private static IEnumerable MapRequestHeaders(IList MapRequestCookies(IList? cookies) + private static IReadOnlyList MapRequestCookies(IList? cookies) { if (cookies == null) { diff --git a/test/WireMock.Net.Tests/Util/CultureInfoUtilsTests.cs b/test/WireMock.Net.Tests/Util/CultureInfoUtilsTests.cs new file mode 100644 index 000000000..2dc1e0937 --- /dev/null +++ b/test/WireMock.Net.Tests/Util/CultureInfoUtilsTests.cs @@ -0,0 +1,83 @@ +using System; +using System.Globalization; +using FluentAssertions; +using WireMock.Util; +using Xunit; + +namespace WireMock.Net.Tests.Util; + +public class CultureInfoUtilsTests +{ + [Theory] + [InlineData(null, typeof(CultureInfo))] + [InlineData("en-US", typeof(CultureInfo))] + [InlineData("fr-FR", typeof(CultureInfo))] + [InlineData("es-ES", typeof(CultureInfo))] + public void Parse_ValidInputs_ReturnsExpectedCultureInfo(string? value, Type expectedType) + { + // Act + var result = CultureInfoUtils.Parse(value); + + // Assert + result.Should().BeOfType(expectedType); + } + + [Theory] + [InlineData("InvalidCulture")] + [InlineData("123456")] + public void Parse_InvalidInputs_ReturnsCurrentCulture(string? value) + { + // Arrange + var expectedCulture = CultureInfo.CurrentCulture; + + // Act + var result = CultureInfoUtils.Parse(value); + + // Assert + result.Should().Be(expectedCulture); + } + +#if !NETSTANDARD1_3 + [Fact] + public void Parse_IntegerInput_ReturnsExpectedCultureInfo() + { + // Arrange + string value = "1033"; // en-US culture identifier + var expectedCulture = new CultureInfo(1033); + + // Act + var result = CultureInfoUtils.Parse(value); + + // Assert + result.Should().Be(expectedCulture); + } +#endif + + [Fact] + public void Parse_CurrentCultureInput_ReturnsCurrentCulture() + { + // Arrange + string value = nameof(CultureInfo.CurrentCulture); + var expectedCulture = CultureInfo.CurrentCulture; + + // Act + var result = CultureInfoUtils.Parse(value); + + // Assert + result.Should().Be(expectedCulture); + } + + [Fact] + public void Parse_InvariantCultureInput_ReturnsInvariantCulture() + { + // Arrange + string value = nameof(CultureInfo.InvariantCulture); + var expectedCulture = CultureInfo.InvariantCulture; + + // Act + var result = CultureInfoUtils.Parse(value); + + // Assert + result.Should().Be(expectedCulture); + } +} \ No newline at end of file From d911bb17ed56f2fe829a291aa3efaf97310ce8ee Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 1 Apr 2023 19:26:38 +0200 Subject: [PATCH 19/21] CompressionUtilsTests --- .../Util/CompressionUtilsTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 test/WireMock.Net.Tests/Util/CompressionUtilsTests.cs diff --git a/test/WireMock.Net.Tests/Util/CompressionUtilsTests.cs b/test/WireMock.Net.Tests/Util/CompressionUtilsTests.cs new file mode 100644 index 000000000..09a89c7ee --- /dev/null +++ b/test/WireMock.Net.Tests/Util/CompressionUtilsTests.cs @@ -0,0 +1,62 @@ +using System; +using System.Text; +using FluentAssertions; +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; +using WireMock.Util; +using Xunit; + +namespace WireMock.Net.Tests.Util; + +public class CompressionUtilsTests +{ + [Theory] + [InlineData("gzip")] + [InlineData("deflate")] + public void CompressDecompress_ValidInput_ReturnsOriginalData(string contentEncoding) + { + // Arrange + byte[] data = Encoding.UTF8.GetBytes("Test data for compression"); + + // Act + byte[] compressedData = CompressionUtils.Compress(contentEncoding, data); + byte[] decompressedData = CompressionUtils.Decompress(contentEncoding, compressedData); + + // Assert + decompressedData.Should().BeEquivalentTo(data); + } + + [Theory] + [InlineData("gzip")] + [InlineData("deflate")] + public void Compress_ValidInput_ReturnsCompressedData(string contentEncoding) + { + // Arrange + var text = RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = "[0-9A-Z]{1000}" }).Generate()!; + byte[] data = Encoding.UTF8.GetBytes(text); + + // Act + byte[] compressedData = CompressionUtils.Compress(contentEncoding, data); + + // Assert + compressedData.Length.Should().BeLessThan(data.Length); + } + + [Fact] + public void CompressDecompress_InvalidContentEncoding_ThrowsNotSupportedException() + { + // Arrange + byte[] data = Encoding.UTF8.GetBytes("Test data for compression"); + string contentEncoding = "invalid"; + + // Act + Action compressAction = () => CompressionUtils.Compress(contentEncoding, data); + Action decompressAction = () => CompressionUtils.Decompress(contentEncoding, data); + + // Assert + compressAction.Should().Throw() + .WithMessage($"ContentEncoding '{contentEncoding}' is not supported."); + decompressAction.Should().Throw() + .WithMessage($"ContentEncoding '{contentEncoding}' is not supported."); + } +} \ No newline at end of file From ad5b96491839f293a1cb01f2a1f6469266551671 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sat, 1 Apr 2023 19:44:36 +0200 Subject: [PATCH 20/21] ut --- src/WireMock.Net/Util/RegexUtils.cs | 2 +- .../Util/RegexUtilsTests.cs | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test/WireMock.Net.Tests/Util/RegexUtilsTests.cs diff --git a/src/WireMock.Net/Util/RegexUtils.cs b/src/WireMock.Net/Util/RegexUtils.cs index 9b33aff81..3d1e3742d 100644 --- a/src/WireMock.Net/Util/RegexUtils.cs +++ b/src/WireMock.Net/Util/RegexUtils.cs @@ -25,7 +25,7 @@ public static Dictionary GetNamedGroups(Regex regex, string inpu return namedGroupsDictionary; } - public static (bool IsValid, bool Result) MatchRegex(string pattern, string input, bool useRegexExtended = true) + public static (bool IsValid, bool Result) MatchRegex(string? pattern, string input, bool useRegexExtended = true) { if (string.IsNullOrEmpty(pattern)) { diff --git a/test/WireMock.Net.Tests/Util/RegexUtilsTests.cs b/test/WireMock.Net.Tests/Util/RegexUtilsTests.cs new file mode 100644 index 000000000..d00da00ed --- /dev/null +++ b/test/WireMock.Net.Tests/Util/RegexUtilsTests.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Text.RegularExpressions; +using FluentAssertions; +using WireMock.Util; +using Xunit; + +namespace WireMock.Net.Tests.Util; + +public class RegexUtilsTests +{ + [Fact] + public void GetNamedGroups_ValidRegexWithNamedGroups_ReturnsNamedGroupsDictionary() + { + // Arrange + var pattern = @"^(?\w+)\s(?\d+)$"; + var input = "MainStreet 123"; + var regex = new Regex(pattern); + + // Act + var namedGroupsDictionary = RegexUtils.GetNamedGroups(regex, input); + + // Assert + namedGroupsDictionary.Should().NotBeEmpty() + .And.Contain(new KeyValuePair("street", "MainStreet")) + .And.Contain(new KeyValuePair("number", "123")); + } + + [Theory] + [InlineData("", "test", false, false)] + [InlineData(null, "test", false, false)] + [InlineData(".*", "test", true, true)] + [InlineData("invalid[", "test", false, false)] + public void MatchRegex_WithVariousPatterns_ReturnsExpectedResults( + string? pattern, string input, bool expectedIsValid, bool expectedResult) + { + // Act + var (isValidResult, matchResult) = RegexUtils.MatchRegex(pattern, input); + + // Assert + isValidResult.Should().Be(expectedIsValid); + matchResult.Should().Be(expectedResult); + } + + [Theory] + [InlineData("", "test", false, false)] + [InlineData(null, "test", false, false)] + [InlineData(".*", "test", true, true)] + [InlineData("invalid[", "test", false, false)] + public void MatchRegex_WithVariousPatternsAndExtendedRegex_ReturnsExpectedResults( + string? pattern, string input, bool expectedIsValid, bool expectedResult) + { + // Act + var (isValidResult, matchResult) = RegexUtils.MatchRegex(pattern, input, useRegexExtended: true); + + // Assert + isValidResult.Should().Be(expectedIsValid); + matchResult.Should().Be(expectedResult); + } +} \ No newline at end of file From dd8370f13bd13a86844bdc6e28acd7bbb2f819d0 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 2 Apr 2023 11:01:48 +0200 Subject: [PATCH 21/21] . --- src/WireMock.Net/Util/BytesEncodingUtils.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/WireMock.Net/Util/BytesEncodingUtils.cs b/src/WireMock.Net/Util/BytesEncodingUtils.cs index 44af91932..9dc854fd2 100644 --- a/src/WireMock.Net/Util/BytesEncodingUtils.cs +++ b/src/WireMock.Net/Util/BytesEncodingUtils.cs @@ -108,7 +108,7 @@ private static bool IsValid(IReadOnlyList buffer, int position, int length return true; } - if (ch >= 0xc2 && ch <= 0xdf) + if (ch is >= 0xc2 and <= 0xdf) { if (position >= length - 2) { @@ -145,7 +145,7 @@ private static bool IsValid(IReadOnlyList buffer, int position, int length return true; } - if (ch >= 0xe1 && ch <= 0xef) + if (ch is >= 0xe1 and <= 0xef) { if (position >= length - 3) { @@ -204,7 +204,7 @@ private static bool IsValid(IReadOnlyList buffer, int position, int length return true; } - if (ch >= 0xf1 && ch <= 0xf3) + if (ch is >= 0xf1 and <= 0xf3) { if (position >= length - 4) {