From fd1f4968b4f0d4b18cface3c0ca6afa2b029b1cd Mon Sep 17 00:00:00 2001 From: Bruno Targhetta Date: Tue, 28 Dec 2021 13:38:42 -0300 Subject: [PATCH] Provide open api schema to dynamic examples generator so you can generate accurate data (#706) * Provide open api schema to dynamic examples generator so you can generate accurate data using settings like max-length in case of a string * Rename Schema Property and add a dynamic examples generator with properties from settings like max-length * Remove blank lines * Add virtual to all public method in WireMockOpenApiParserExampleValues and ireMockOpenApiParserDynamicExampleValues to extend and overrides examples values --- .../DynamicDataGeneration.cs | 26 +++++++ .../OpenApiFiles/Swagger_Customer_V2.0.json | 73 +++++++++++++++++ .../Program.cs | 78 ++++++++++++------- .../Run.cs | 12 +-- ...reMock.Net.OpenApiParser.ConsoleApp.csproj | 3 + .../IWireMockOpenApiParserExampleValues.cs | 8 +- ...reMockOpenApiParserDynamicExampleValues.cs | 21 ++--- .../WireMockOpenApiParserExampleValues.cs | 23 +++--- .../Utils/ExampleValueGenerator.cs | 2 + 9 files changed, 191 insertions(+), 55 deletions(-) create mode 100644 examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs create mode 100644 examples/WireMock.Net.OpenApiParser.ConsoleApp/OpenApiFiles/Swagger_Customer_V2.0.json diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs new file mode 100644 index 00000000..e2fe4afd --- /dev/null +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/DynamicDataGeneration.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.OpenApi.Models; +using RandomDataGenerator.FieldOptions; +using RandomDataGenerator.Randomizers; +using WireMock.Net.OpenApiParser.Settings; + +namespace WireMock.Net.OpenApiParser.ConsoleApp +{ + public class DynamicDataGeneration : WireMockOpenApiParserDynamicExampleValues + { + public override string String + { + get + { + //Since you have your Schema, you can get if max-lenght is set. You can generate accurate examples with this settings + var maxLength = this.Schema.MaxLength ?? 9; + + return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex + { + Pattern = $"[0-9A-Z]{{{maxLength}}}" + }).Generate() ?? "example-string"; + } + set { } + } + } +} diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/OpenApiFiles/Swagger_Customer_V2.0.json b/examples/WireMock.Net.OpenApiParser.ConsoleApp/OpenApiFiles/Swagger_Customer_V2.0.json new file mode 100644 index 00000000..6b5ba8d9 --- /dev/null +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/OpenApiFiles/Swagger_Customer_V2.0.json @@ -0,0 +1,73 @@ +{ + "swagger": "2.0", + "info": { + "title": "customer", + "description": "It contains basic customer actions.", + "version": "v1" + }, + "host": "localhost", + "basePath": "/v1/customer/", + "schemes": [ + "https" + ], + "paths": { + "/": { + "get": { + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "Correlation-Id", + "required": true, + "in": "header", + "type": "string", + "maxLength": 3 + } + ], + "responses": { + "200": { + "description": "", + "x-amf-mediaType": "application/json", + "schema": { + "$ref": "#/definitions/ResponseCustmer" + } + } + } + } + } + }, + "definitions": { + "ResponseCustmer": { + + "type": "object", + "additionalProperties": true, + "required": [ + "first-name", + "last-name", + "status", + "interest" + ], + "properties": { + "first-name": { + "type": "string" + }, + "last-name": { + "type": "string" + }, + "status": { + "type": "string", + "maxLength": 2 + }, + "interest": { + "type": "string", + "maxLength": 45 + } + } + } + } +} + diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs index e9b5fcd8..37e183b4 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Program.cs @@ -1,41 +1,59 @@ using System; using System.IO; - +using Microsoft.OpenApi.Readers; +using Newtonsoft.Json; + namespace WireMock.Net.OpenApiParser.ConsoleApp { class Program { private const string Folder = "OpenApiFiles"; static void Main(string[] args) - { - 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/"); - - Console.WriteLine("Press any key to stop the servers"); - Console.ReadKey(); - - serverOpenAPIExamples.Stop(); - serverPetstore_V2_json.Stop(); - serverPetstore_V2_yaml.Stop(); - serverPetstore_V300_yaml.Stop(); - serverPetstore_V302_json.Stop(); - - //IWireMockOpenApiParser parser = new WireMockOpenApiParser(); - - //var petStoreModels = parser.FromStream(File.OpenRead("petstore-openapi3.json"), out OpenApiDiagnostic diagnostic1); - //string petStoreJson = JsonConvert.SerializeObject(petStoreModels, Settings); - // File.WriteAllText("../../../wiremock-petstore-openapi3.json", petStoreJson); - - //Run.RunServer(petStoreModels); - - //var mappingModels2 = parser.FromStream(File.OpenRead("infura.yaml"), out OpenApiDiagnostic diagnostic2); - //Console.WriteLine(JsonConvert.SerializeObject(diagnostic2, Settings)); - - //string json2 = JsonConvert.SerializeObject(mappingModels2, Settings); - //Console.WriteLine(json2); + { + //RunOthersOpenApiParserExample(); + + RunMockServerWithDynamicExampleGeneration(); } + + private static void RunMockServerWithDynamicExampleGeneration() { + //Run your mocking framework specifieing youur 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"); + + Console.ReadKey(); + serverCustomer_V2_json.Stop(); + } + + 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/"); + + Console.WriteLine("Press any key to stop the servers"); + Console.ReadKey(); + + serverOpenAPIExamples.Stop(); + serverPetstore_V2_json.Stop(); + serverPetstore_V2_yaml.Stop(); + serverPetstore_V300_yaml.Stop(); + serverPetstore_V302_json.Stop(); + + //IWireMockOpenApiParser parser = new WireMockOpenApiParser(); + + //var petStoreModels = parser.FromStream(File.OpenRead("petstore-openapi3.json"), out OpenApiDiagnostic diagnostic1); + //string petStoreJson = JsonConvert.SerializeObject(petStoreModels, Settings); + // File.WriteAllText("../../../wiremock-petstore-openapi3.json", petStoreJson); + + //Run.RunServer(petStoreModels); + + //var mappingModels2 = parser.FromStream(File.OpenRead("infura.yaml"), out OpenApiDiagnostic diagnostic2); + //Console.WriteLine(JsonConvert.SerializeObject(diagnostic2, Settings)); + + //string json2 = JsonConvert.SerializeObject(mappingModels2, Settings); + //Console.WriteLine(json2); + } } } \ No newline at end of file diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs index 207d6188..ae8fc9ec 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs @@ -13,28 +13,30 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp { public static class Run { - public static WireMockServer RunServer(string path, string url, bool dynamicExamples = true) + 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 { AllowCSharpCodeMatcher = true, Urls = new[] { url }, StartAdminInterface = true, - ReadStaticMappings = false, + ReadStaticMappings = true, WatchStaticMappings = false, WatchStaticMappingsInSubdirectories = false, Logger = new WireMockConsoleLogger(), SaveUnmatchedRequests = true }); + Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); - server.SetBasicAuthentication("a", "b"); + //server.SetBasicAuthentication("a", "b"); var settings = new WireMockOpenApiParserSettings { DynamicExamples = dynamicExamples, - PathPatternToUse = ExampleValueType.Wildcard, - HeaderPatternToUse = ExampleValueType.Wildcard + ExampleValues = examplesValuesGenerator, + PathPatternToUse = pathPatternToUse, + HeaderPatternToUse = headerPatternToUse, }; server.WithMappingFromOpenApiFile(path, settings, out var diag); diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/WireMock.Net.OpenApiParser.ConsoleApp.csproj b/examples/WireMock.Net.OpenApiParser.ConsoleApp/WireMock.Net.OpenApiParser.ConsoleApp.csproj index 4e8a2d10..a6ea5167 100644 --- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/WireMock.Net.OpenApiParser.ConsoleApp.csproj +++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/WireMock.Net.OpenApiParser.ConsoleApp.csproj @@ -36,6 +36,9 @@ PreserveNewest + + PreserveNewest + \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs index 6778794f..cef841f1 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs @@ -1,5 +1,6 @@ using System; - +using Microsoft.OpenApi.Models; + namespace WireMock.Net.OpenApiParser.Settings { /// @@ -48,5 +49,10 @@ namespace WireMock.Net.OpenApiParser.Settings /// An example value for a String. /// string String { get; set; } + + /// + /// OpenApi Schema to generate dynamic examples more accurate + /// + OpenApiSchema Schema { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs index 988a9065..7856b887 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs @@ -1,4 +1,5 @@ using System; +using Microsoft.OpenApi.Models; using RandomDataGenerator.FieldOptions; using RandomDataGenerator.Randomizers; @@ -10,22 +11,24 @@ namespace WireMock.Net.OpenApiParser.Settings public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues { /// - public bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } } + public virtual bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } } /// - public int Integer { get { return RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; } set { } } + public virtual int Integer { get { return RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; } set { } } /// - public float Float { get { return RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; } set { } } + public virtual float Float { get { return RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; } set { } } /// - public double Double { get { return RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; } set { } } + public virtual double Double { get { return RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; } set { } } /// - public Func Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } } + public virtual Func Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } } /// - public Func DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } } + public virtual Func DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } } /// - public byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } } + public virtual byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } } /// - public object Object { get; set; } = "example-object"; + public virtual object Object { get; set; } = "example-object"; /// - public string String { get { return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; } set { } } + public virtual string String { get { return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; } set { } } + /// + public virtual OpenApiSchema Schema { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs index 58c95a1a..ece47d02 100644 --- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs +++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs @@ -1,5 +1,6 @@ using System; - +using Microsoft.OpenApi.Models; + namespace WireMock.Net.OpenApiParser.Settings { /// @@ -8,22 +9,24 @@ namespace WireMock.Net.OpenApiParser.Settings public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues { /// - public bool Boolean { get; set; } = true; + public virtual bool Boolean { get; set; } = true; /// - public int Integer { get; set; } = 42; + public virtual int Integer { get; set; } = 42; /// - public float Float { get; set; } = 4.2f; + public virtual float Float { get; set; } = 4.2f; /// - public double Double { get; set; } = 4.2d; + public virtual double Double { get; set; } = 4.2d; /// - public Func Date { get; set; } = () => System.DateTime.UtcNow.Date; + public virtual Func Date { get; set; } = () => System.DateTime.UtcNow.Date; /// - public Func DateTime { get; set; } = () => System.DateTime.UtcNow; + public virtual Func DateTime { get; set; } = () => System.DateTime.UtcNow; /// - public byte[] Bytes { get; set; } = { 48, 49, 50 }; + public virtual byte[] Bytes { get; set; } = { 48, 49, 50 }; /// - public object Object { get; set; } = "example-object"; + public virtual object Object { get; set; } = "example-object"; /// - public string String { get; set; } = "example-string"; + public virtual string String { get; set; } = "example-string"; + /// + public virtual OpenApiSchema Schema { get; set; } = new OpenApiSchema(); } } \ 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 b9e7f409..55aad19c 100644 --- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs +++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs @@ -36,6 +36,8 @@ namespace WireMock.Net.OpenApiParser.Utils var schemaExample = schema?.Example; var schemaEnum = GetRandomEnumValue(schema?.Enum); + _settings.ExampleValues.Schema = schema; + switch (schema?.GetSchemaType()) { case SchemaType.Boolean: