diff --git a/WireMock.Net Solution.sln b/WireMock.Net Solution.sln
index 0438f830..d7c71918 100644
--- a/WireMock.Net Solution.sln
+++ b/WireMock.Net Solution.sln
@@ -46,8 +46,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net452
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.RequestLogTest", "examples\WireMock.Net.Console.RequestLogTest\WireMock.Net.Console.RequestLogTest.csproj", "{A9D039B9-7509-4CF1-9EFD-87EB82998575}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser", "src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj", "{D3804228-91F4-4502-9595-39584E5AADAD}"
-EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.OpenApiParser.ConsoleApp", "examples\WireMock.Net.OpenApiParser.ConsoleApp\WireMock.Net.OpenApiParser.ConsoleApp.csproj", "{5C09FB93-1535-4F92-AF26-21E8A061EE4A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.FluentAssertions", "src\WireMock.Net.FluentAssertions\WireMock.Net.FluentAssertions.csproj", "{B6269AAC-170A-4346-8B9A-579DED3D9A95}"
@@ -130,6 +128,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Middleware.Tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.AwesomeAssertions", "src\WireMock.Net.AwesomeAssertions\WireMock.Net.AwesomeAssertions.csproj", "{7753670F-7C7F-44BF-8BC7-08325588E60C}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenApiParser", "src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj", "{D3804228-91F4-4502-9595-39584E5AADAD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -176,10 +176,6 @@ Global
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A9D039B9-7509-4CF1-9EFD-87EB82998575}.Release|Any CPU.Build.0 = Release|Any CPU
- {D3804228-91F4-4502-9595-39584E5AADAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {D3804228-91F4-4502-9595-39584E5AADAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {D3804228-91F4-4502-9595-39584E5AADAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {D3804228-91F4-4502-9595-39584E5AADAD}.Release|Any CPU.Build.0 = Release|Any CPU
{5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5C09FB93-1535-4F92-AF26-21E8A061EE4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -308,6 +304,10 @@ Global
{7753670F-7C7F-44BF-8BC7-08325588E60C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7753670F-7C7F-44BF-8BC7-08325588E60C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D3804228-91F4-4502-9595-39584E5AADAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D3804228-91F4-4502-9595-39584E5AADAD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D3804228-91F4-4502-9595-39584E5AADAD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D3804228-91F4-4502-9595-39584E5AADAD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -323,7 +323,6 @@ Global
{7F0B2446-0363-4720-AF46-F47F83B557DC} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{668F689E-57B4-422E-8846-C0FF643CA268} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{A9D039B9-7509-4CF1-9EFD-87EB82998575} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
- {D3804228-91F4-4502-9595-39584E5AADAD} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{5C09FB93-1535-4F92-AF26-21E8A061EE4A} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{B6269AAC-170A-4346-8B9A-579DED3D9A95} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{40BF24B5-12E6-4610-9489-138798632E28} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
@@ -358,6 +357,7 @@ Global
{6B30AA9F-DA04-4EB5-B03C-45A8EF272ECE} = {0BB8B634-407A-4610-A91F-11586990767A}
{A5FEF4F7-7DA2-4962-89A8-16BA942886E5} = {0BB8B634-407A-4610-A91F-11586990767A}
{7753670F-7C7F-44BF-8BC7-08325588E60C} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
+ {D3804228-91F4-4502-9595-39584E5AADAD} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
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 b63d3343..ddc6cf32 100644
--- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/WireMock.Net.OpenApiParser.ConsoleApp.csproj
+++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/WireMock.Net.OpenApiParser.ConsoleApp.csproj
@@ -2,7 +2,7 @@
Exe
- net6.0
+ net8.0
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Extensions/DictionaryExtensions.cs b/src/WireMock.Net.OpenApiParser.Preview/Extensions/DictionaryExtensions.cs
new file mode 100644
index 00000000..a819a1f9
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Extensions/DictionaryExtensions.cs
@@ -0,0 +1,22 @@
+// Copyright © WireMock.Net
+
+#if NET46 || NET47 || NETSTANDARD2_0
+using System.Collections.Generic;
+
+namespace WireMock.Net.OpenApiParser.Extensions;
+
+internal static class DictionaryExtensions
+{
+ public static bool TryAdd(this Dictionary? dictionary, TKey key, TValue value)
+ {
+ if (dictionary is null || dictionary.ContainsKey(key))
+ {
+ return false;
+ }
+
+ dictionary[key] = value;
+
+ return true;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Extensions/OpenApiSchemaExtensions.cs b/src/WireMock.Net.OpenApiParser.Preview/Extensions/OpenApiSchemaExtensions.cs
new file mode 100644
index 00000000..02f7a49b
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Extensions/OpenApiSchemaExtensions.cs
@@ -0,0 +1,85 @@
+// Copyright © WireMock.Net
+
+using System.Linq;
+using System.Text.Json;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
+using WireMock.Net.OpenApiParser.Types;
+
+namespace WireMock.Net.OpenApiParser.Extensions;
+
+internal static class OpenApiSchemaExtensions
+{
+ public static bool TryGetXNullable(this IOpenApiSchema schema, out bool value)
+ {
+ value = false;
+
+ if (schema.Extensions != null && schema.Extensions.TryGetValue(OpenApiConstants.NullableExtension, out var nullExtRawValue) && nullExtRawValue is OpenApiAny { Node: { } jsonNode })
+ {
+ value = jsonNode.GetValueKind() == JsonValueKind.True;
+ return true;
+ }
+
+ return false;
+ }
+
+ public static JsonSchemaType? GetSchemaType(this IOpenApiSchema? schema, out bool isNullable)
+ {
+ isNullable = false;
+
+ if (schema == null)
+ {
+ return null;
+ }
+
+ if (schema.Type == null)
+ {
+ if (schema.AllOf?.Any() == true || schema.AnyOf?.Any() == true)
+ {
+ return JsonSchemaType.Object;
+ }
+ }
+
+ isNullable = (schema.Type | JsonSchemaType.Null) == JsonSchemaType.Null || (schema.TryGetXNullable(out var xNullable) && xNullable);
+
+ // Removes the Null flag from the schema.Type, ensuring the returned value represents a non-nullable type.
+ return schema.Type & ~JsonSchemaType.Null;
+ }
+
+ public static SchemaFormat GetSchemaFormat(this IOpenApiSchema? schema)
+ {
+ switch (schema?.Format)
+ {
+ case "float":
+ return SchemaFormat.Float;
+
+ case "double":
+ return SchemaFormat.Double;
+
+ case "int32":
+ return SchemaFormat.Int32;
+
+ case "int64":
+ return SchemaFormat.Int64;
+
+ case "date":
+ return SchemaFormat.Date;
+
+ case "date-time":
+ return SchemaFormat.DateTime;
+
+ case "password":
+ return SchemaFormat.Password;
+
+ case "byte":
+ return SchemaFormat.Byte;
+
+ case "binary":
+ return SchemaFormat.Binary;
+
+ default:
+ return SchemaFormat.Undefined;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Extensions/WireMockServerExtensions.cs b/src/WireMock.Net.OpenApiParser.Preview/Extensions/WireMockServerExtensions.cs
new file mode 100644
index 00000000..8d8aee7f
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Extensions/WireMockServerExtensions.cs
@@ -0,0 +1,96 @@
+// Copyright © WireMock.Net
+
+using System.IO;
+using System.Linq;
+using JetBrains.Annotations;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Reader;
+using Stef.Validation;
+using WireMock.Net.OpenApiParser.Settings;
+using WireMock.Server;
+
+namespace WireMock.Net.OpenApiParser.Extensions;
+
+///
+/// Some extension methods for .
+///
+public static class WireMockServerExtensions
+{
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file.
+ ///
+ /// The WireMockServer instance
+ /// Path containing OpenAPI file to parse and use the mappings.
+ /// Returns diagnostic object containing errors detected during parsing
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
+ {
+ return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file.
+ ///
+ /// The WireMockServer instance
+ /// Path containing OpenAPI file to parse and use the mappings.
+ /// 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);
+ Guard.NotNullOrEmpty(path);
+
+ var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
+
+ return server.WithMapping(mappings.ToArray());
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream.
+ ///
+ /// The WireMockServer instance
+ /// Stream containing OpenAPI description to parse and use the mappings.
+ /// Returns diagnostic object containing errors detected during parsing
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
+ {
+ return WithMappingFromOpenApiStream(server, stream, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream.
+ ///
+ /// The WireMockServer instance
+ /// Stream containing OpenAPI description to parse and use the mappings.
+ /// Additional settings
+ /// Returns diagnostic object containing errors detected during parsing
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ Guard.NotNull(server);
+ Guard.NotNull(stream);
+ Guard.NotNull(settings);
+
+ var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
+
+ return server.WithMapping(mappings.ToArray());
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 document.
+ ///
+ /// The WireMockServer instance
+ /// The OpenAPI document to use as mappings.
+ /// Additional settings [optional].
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
+ {
+ Guard.NotNull(server);
+ Guard.NotNull(document);
+
+ var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
+
+ return server.WithMapping(mappings.ToArray());
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser.Preview/IWireMockOpenApiParser.cs
new file mode 100644
index 00000000..4e978739
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/IWireMockOpenApiParser.cs
@@ -0,0 +1,75 @@
+// Copyright © WireMock.Net
+
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Reader;
+using WireMock.Admin.Mappings;
+using WireMock.Net.OpenApiParser.Settings;
+
+namespace WireMock.Net.OpenApiParser;
+
+///
+/// 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/V31 or Raml file.
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a file-path.
+ ///
+ /// The path to read the OpenApi/Swagger/V2/V3/V31 or Raml file.
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from an .
+ ///
+ /// The source OpenApiDocument
+ /// Additional settings [optional]
+ /// MappingModel
+ IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source stream
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source stream
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source text
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source text
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser.Preview/Mappers/OpenApiPathsMapper.cs
new file mode 100644
index 00000000..e48766e0
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Mappers/OpenApiPathsMapper.cs
@@ -0,0 +1,347 @@
+// Copyright © WireMock.Net
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text.Json;
+using System.Text.Json.Nodes;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
+using Newtonsoft.Json;
+using Stef.Validation;
+using WireMock.Admin.Mappings;
+using WireMock.Net.OpenApiParser.Extensions;
+using WireMock.Net.OpenApiParser.Settings;
+using WireMock.Net.OpenApiParser.Types;
+using WireMock.Net.OpenApiParser.Utils;
+using SystemTextJsonSerializer = System.Text.Json.JsonSerializer;
+
+namespace WireMock.Net.OpenApiParser.Mappers;
+
+internal class OpenApiPathsMapper
+{
+ private const string HeaderContentType = "Content-Type";
+
+ private readonly WireMockOpenApiParserSettings _settings;
+ private readonly ExampleValueGenerator _exampleValueGenerator;
+
+ public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
+ {
+ _settings = Guard.NotNull(settings);
+ _exampleValueGenerator = new ExampleValueGenerator(settings);
+ }
+
+ public IReadOnlyList ToMappingModels(OpenApiPaths? paths, IList servers)
+ {
+ return paths?
+ .OrderBy(p => p.Key)
+ .Select(p => MapPath(p.Key, p.Value, servers))
+ .SelectMany(x => x)
+ .ToArray() ?? [];
+ }
+
+ private IReadOnlyList MapPath(string path, IOpenApiPathItem pathItem, IList 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)
+ {
+ var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? [];
+ var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? [];
+ var headers = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? [];
+
+ var response = operation?.Responses?.FirstOrDefault() ?? new KeyValuePair();
+
+ TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out var responseContentType);
+ var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
+ var responseExample = responseContent?.Example;
+ var responseSchemaExample = responseContent?.Schema?.Example;
+
+ var responseBody = responseExample ?? responseSchemaExample ?? MapSchemaToObject(responseSchema);
+
+ var requestBodyModel = new BodyModel();
+ if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
+ {
+ var request = operation.RequestBody.Content;
+ TryGetContent(request, out var requestContent, out _);
+
+ var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
+ var requestBodyExample = requestContent!.Example;
+ var requestBodySchemaExample = requestContent.Schema?.Example;
+
+ var requestBodyMapped = requestBodyExample ?? requestBodySchemaExample ?? MapSchemaToObject(requestBodySchema);
+ requestBodyModel = MapRequestBody(requestBodyMapped);
+ }
+
+ if (!int.TryParse(response.Key, out var httpStatusCode))
+ {
+ httpStatusCode = 200;
+ }
+
+ return new MappingModel
+ {
+ Guid = Guid.NewGuid(),
+ Request = new RequestModel
+ {
+ Methods = [httpMethod],
+ Path = PathUtils.Combine(MapBasePath(servers), MapPathWithParameters(path, pathParameters)),
+ Params = MapQueryParameters(queryParameters),
+ Headers = MapRequestHeaders(headers),
+ Body = requestBodyModel
+ },
+ Response = new ResponseModel
+ {
+ StatusCode = httpStatusCode,
+ Headers = MapHeaders(responseContentType, response.Value?.Headers),
+ BodyAsJson = responseBody != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(responseBody)) : null
+ }
+ };
+ }
+
+ private BodyModel? MapRequestBody(JsonNode? requestBody)
+ {
+ if (requestBody == null)
+ {
+ return null;
+ }
+
+ return new BodyModel
+ {
+ Matcher = new MatcherModel
+ {
+ Name = "JsonMatcher",
+ Pattern = SystemTextJsonSerializer.Serialize(requestBody, new JsonSerializerOptions { WriteIndented = true }),
+ IgnoreCase = _settings.RequestBodyIgnoreCase
+ }
+ };
+ }
+
+ private static bool TryGetContent(IDictionary? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
+ {
+ openApiMediaType = null;
+ contentType = null;
+
+ if (contents == null || contents.Values.Count == 0)
+ {
+ return false;
+ }
+
+ if (contents.TryGetValue("application/json", out var content))
+ {
+ openApiMediaType = content;
+ contentType = "application/json";
+ }
+ else
+ {
+ var first = contents.FirstOrDefault();
+ openApiMediaType = first.Value;
+ contentType = first.Key;
+ }
+
+ return true;
+ }
+
+ private JsonNode? MapSchemaToObject(IOpenApiSchema? schema)
+ {
+ if (schema == null)
+ {
+ return null;
+ }
+
+ switch (schema.GetSchemaType(out _))
+ {
+ case JsonSchemaType.Array:
+ var array = new JsonArray();
+ for (var i = 0; i < _settings.NumberOfArrayItems; i++)
+ {
+ if (schema.Items?.Properties?.Count > 0)
+ {
+ var item = new JsonObject();
+ foreach (var property in schema.Items.Properties)
+ {
+ item[property.Key] = MapSchemaToObject(property.Value);
+ }
+
+ array.Add(item);
+ }
+ else
+ {
+ var arrayItem = MapSchemaToObject(schema.Items);
+ array.Add(arrayItem);
+ }
+ }
+
+ if (schema.AllOf?.Count > 0)
+ {
+ array.Add(MapSchemaAllOfToObject(schema));
+ }
+
+ return array;
+
+ case JsonSchemaType.Boolean:
+ case JsonSchemaType.Integer:
+ case JsonSchemaType.Number:
+ case JsonSchemaType.String:
+ return _exampleValueGenerator.GetExampleValue(schema);
+
+ case JsonSchemaType.Object:
+ var propertyAsJsonObject = new JsonObject();
+ foreach (var schemaProperty in schema.Properties ?? new Dictionary())
+ {
+ propertyAsJsonObject[schemaProperty.Key] = MapPropertyAsJsonNode(schemaProperty.Value);
+ }
+
+ if (schema.AllOf?.Count > 0)
+ {
+ foreach (var group in schema.AllOf.SelectMany(p => p.Properties ?? new Dictionary()).GroupBy(x => x.Key))
+ {
+ propertyAsJsonObject[group.Key] = MapPropertyAsJsonNode(group.First().Value);
+ }
+ }
+
+ return propertyAsJsonObject;
+
+ default:
+ return null;
+ }
+ }
+
+ private JsonObject MapSchemaAllOfToObject(IOpenApiSchema schema)
+ {
+ var arrayItem = new JsonObject();
+ foreach (var property in schema.AllOf ?? [])
+ {
+ foreach (var item in property.Properties ?? new Dictionary())
+ {
+ arrayItem[item.Key] = MapPropertyAsJsonNode(item.Value);
+ }
+ }
+ return arrayItem;
+ }
+
+ private JsonNode? MapPropertyAsJsonNode(IOpenApiSchema openApiSchema)
+ {
+ var schemaType = openApiSchema.GetSchemaType(out _);
+ if (schemaType is JsonSchemaType.Object or JsonSchemaType.Array)
+ {
+ return MapSchemaToObject(openApiSchema);
+ }
+
+ return _exampleValueGenerator.GetExampleValue(openApiSchema);
+ }
+
+ private string MapPathWithParameters(string path, IEnumerable? parameters)
+ {
+ if (parameters == null)
+ {
+ return path;
+ }
+
+ var newPath = path;
+ foreach (var parameter in parameters)
+ {
+ var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse);
+ newPath = newPath.Replace($"{{{parameter.Name}}}", exampleMatcherModel.Pattern as string);
+ }
+
+ return newPath;
+ }
+
+ private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers)
+ {
+ var mappedHeaders = headers?
+ .ToDictionary(item => item.Key, _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!) ?? new Dictionary();
+
+ if (!string.IsNullOrEmpty(responseContentType))
+ {
+ mappedHeaders.TryAdd(HeaderContentType, responseContentType);
+ }
+
+ return mappedHeaders.Keys.Any() ? mappedHeaders : null;
+ }
+
+ private IList? MapQueryParameters(IEnumerable queryParameters)
+ {
+ var list = queryParameters
+ .Where(req => req.Required)
+ .Select(qp => new ParamModel
+ {
+ Name = qp.Name ?? string.Empty,
+ IgnoreCase = _settings.QueryParameterPatternIgnoreCase,
+ Matchers =
+ [
+ GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse)
+ ]
+ })
+ .ToList();
+
+ return list.Any() ? list : null;
+ }
+
+ private IList? MapRequestHeaders(IEnumerable headers)
+ {
+ var list = headers
+ .Where(req => req.Required)
+ .Select(qp => new HeaderModel
+ {
+ Name = qp.Name ?? string.Empty,
+ IgnoreCase = _settings.HeaderPatternIgnoreCase,
+ Matchers =
+ [
+ GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse)
+ ]
+ })
+ .ToList();
+
+ return list.Any() ? list : null;
+ }
+
+ private MatcherModel GetExampleMatcherModel(IOpenApiSchema? schema, ExampleValueType type)
+ {
+ return type switch
+ {
+ ExampleValueType.Value => new MatcherModel
+ {
+ Name = "ExactMatcher",
+ Pattern = GetExampleValueAsStringForSchemaType(schema),
+ IgnoreCase = _settings.IgnoreCaseExampleValues
+ },
+
+ _ => new MatcherModel
+ {
+ Name = "WildcardMatcher",
+ Pattern = "*"
+ }
+ };
+ }
+
+ private string GetExampleValueAsStringForSchemaType(IOpenApiSchema? schema)
+ {
+ var value = _exampleValueGenerator.GetExampleValue(schema);
+
+ if (value.GetValueKind() == JsonValueKind.String)
+ {
+ return value.GetValue();
+ }
+
+ return value.ToString();
+ }
+
+ private static string MapBasePath(IList? servers)
+ {
+ var server = servers?.FirstOrDefault();
+ if (server == null)
+ {
+ return string.Empty;
+ }
+
+ if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out var uriResult))
+ {
+ return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
+ }
+
+ return string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Properties/AssemblyInfo.cs b/src/WireMock.Net.OpenApiParser.Preview/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..fb354ea5
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Properties/AssemblyInfo.cs
@@ -0,0 +1,5 @@
+// Copyright © WireMock.Net
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Settings/IWireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser.Preview/Settings/IWireMockOpenApiParserExampleValues.cs
new file mode 100644
index 00000000..5d335144
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Settings/IWireMockOpenApiParserExampleValues.cs
@@ -0,0 +1,62 @@
+// Copyright © WireMock.Net
+
+using System;
+using Microsoft.OpenApi.Models.Interfaces;
+
+namespace WireMock.Net.OpenApiParser.Settings;
+
+///
+/// An interface defining the example values to use for the different types.
+///
+public interface IWireMockOpenApiParserExampleValues
+{
+ ///
+ /// An example value for a Boolean.
+ ///
+ bool Boolean { get; }
+
+ ///
+ /// An example value for an Integer.
+ ///
+ int Integer { get; }
+
+ ///
+ /// An example value for a Float.
+ ///
+ float Float { get; }
+
+ ///
+ /// An example value for a Decimal.
+ ///
+ decimal Decimal { get; }
+
+ ///
+ /// An example value for a Date.
+ ///
+ Func Date { get; }
+
+ ///
+ /// An example value for a DateTime.
+ ///
+ Func DateTime { get; }
+
+ ///
+ /// An example value for Bytes.
+ ///
+ byte[] Bytes { get; }
+
+ ///
+ /// An example value for a Object.
+ ///
+ object Object { get; }
+
+ ///
+ /// An example value for a String.
+ ///
+ string String { get; }
+
+ ///
+ /// OpenApi Schema to generate dynamic examples more accurate
+ ///
+ IOpenApiSchema? Schema { get; set; }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserDynamicExampleValues.cs b/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserDynamicExampleValues.cs
new file mode 100644
index 00000000..194762c5
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserDynamicExampleValues.cs
@@ -0,0 +1,59 @@
+// Copyright © WireMock.Net
+
+using System;
+using Microsoft.OpenApi.Models.Interfaces;
+using RandomDataGenerator.FieldOptions;
+using RandomDataGenerator.Randomizers;
+
+namespace WireMock.Net.OpenApiParser.Settings;
+
+///
+/// A class defining the random example values to use for the different types.
+///
+public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
+{
+ ///
+ public virtual bool Boolean => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true;
+
+ ///
+ public virtual int Integer => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42;
+
+ ///
+ public virtual float Float => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f;
+
+ ///
+ public virtual decimal Decimal => SafeConvertFloatToDecimal(RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f);
+
+ ///
+ public virtual Func Date => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date;
+
+ ///
+ public virtual Func DateTime => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow;
+
+ ///
+ public virtual byte[] Bytes => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate();
+
+ ///
+ public virtual object Object => "example-object";
+
+ ///
+ public virtual string String => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string";
+
+ ///
+ public virtual IOpenApiSchema? Schema { get; set; }
+
+ ///
+ /// Safely converts a float to a decimal, ensuring the value stays within the bounds of a decimal.
+ ///
+ /// The float value to convert.
+ /// A decimal value within the valid range of a decimal.
+ private static decimal SafeConvertFloatToDecimal(float value)
+ {
+ return value switch
+ {
+ < (float)decimal.MinValue => decimal.MinValue,
+ > (float)decimal.MaxValue => decimal.MaxValue,
+ _ => (decimal)value
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserExampleValues.cs
new file mode 100644
index 00000000..3b3c6fea
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserExampleValues.cs
@@ -0,0 +1,43 @@
+// Copyright © WireMock.Net
+
+using System;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
+
+namespace WireMock.Net.OpenApiParser.Settings;
+
+///
+/// A class defining the example values to use for the different types.
+///
+public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
+{
+ ///
+ public virtual bool Boolean => true;
+
+ ///
+ public virtual int Integer => 42;
+
+ ///
+ public virtual float Float => 4.2f;
+
+ ///
+ public virtual decimal Decimal => 4.2m;
+
+ ///
+ public virtual Func Date { get; } = () => System.DateTime.UtcNow.Date;
+
+ ///
+ public virtual Func DateTime { get; } = () => System.DateTime.UtcNow;
+
+ ///
+ public virtual byte[] Bytes { get; } = [48, 49, 50];
+
+ ///
+ public virtual object Object => "example-object";
+
+ ///
+ public virtual string String => "example-string";
+
+ ///
+ public virtual IOpenApiSchema? Schema { get; set; } = new OpenApiSchema();
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserSettings.cs b/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserSettings.cs
new file mode 100644
index 00000000..ea3cc600
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Settings/WireMockOpenApiParserSettings.cs
@@ -0,0 +1,73 @@
+// Copyright © WireMock.Net
+
+using WireMock.Net.OpenApiParser.Types;
+
+namespace WireMock.Net.OpenApiParser.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?
+ ///
+ public bool DynamicExamples { get; set; }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Types/ExampleValueType.cs b/src/WireMock.Net.OpenApiParser.Preview/Types/ExampleValueType.cs
new file mode 100644
index 00000000..98856b3a
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Types/ExampleValueType.cs
@@ -0,0 +1,21 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.OpenApiParser.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.Preview/Types/SchemaFormat.cs b/src/WireMock.Net.OpenApiParser.Preview/Types/SchemaFormat.cs
new file mode 100644
index 00000000..c5370238
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Types/SchemaFormat.cs
@@ -0,0 +1,26 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.OpenApiParser.Types;
+
+internal enum SchemaFormat
+{
+ Float,
+
+ Double,
+
+ Int32,
+
+ Int64,
+
+ Date,
+
+ DateTime,
+
+ Password,
+
+ Byte,
+
+ Binary,
+
+ Undefined
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Utils/DateTimeUtils.cs b/src/WireMock.Net.OpenApiParser.Preview/Utils/DateTimeUtils.cs
new file mode 100644
index 00000000..e3178549
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Utils/DateTimeUtils.cs
@@ -0,0 +1,22 @@
+// Copyright © WireMock.Net
+
+using System;
+using System.Globalization;
+
+namespace WireMock.Net.OpenApiParser.Utils;
+
+internal static class DateTimeUtils
+{
+ private const string DateFormat = "yyyy-MM-dd";
+ private const string DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fffzzz";
+
+ public static string ToRfc3339DateTime(DateTime dateTime)
+ {
+ return dateTime.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo);
+ }
+
+ public static string ToRfc3339Date(DateTime dateTime)
+ {
+ return dateTime.ToString(DateFormat, DateTimeFormatInfo.InvariantInfo);
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Utils/ExampleValueGenerator.cs b/src/WireMock.Net.OpenApiParser.Preview/Utils/ExampleValueGenerator.cs
new file mode 100644
index 00000000..153d4c67
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Utils/ExampleValueGenerator.cs
@@ -0,0 +1,105 @@
+// Copyright © WireMock.Net
+
+using System;
+using System.Linq;
+using System.Text.Json.Nodes;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Models.Interfaces;
+using Stef.Validation;
+using WireMock.Net.OpenApiParser.Extensions;
+using WireMock.Net.OpenApiParser.Settings;
+using WireMock.Net.OpenApiParser.Types;
+
+namespace WireMock.Net.OpenApiParser.Utils;
+
+internal class ExampleValueGenerator
+{
+ private readonly IWireMockOpenApiParserExampleValues _exampleValues;
+
+ public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
+ {
+ Guard.NotNull(settings);
+
+ // Check if user provided an own implementation
+ if (settings.ExampleValues is null)
+ {
+ if (settings.DynamicExamples)
+ {
+ _exampleValues = new WireMockOpenApiParserDynamicExampleValues();
+ }
+ else
+ {
+ _exampleValues = new WireMockOpenApiParserExampleValues();
+ }
+ }
+ else
+ {
+ _exampleValues = settings.ExampleValues;
+ }
+ }
+
+ public JsonNode GetExampleValue(IOpenApiSchema? schema)
+ {
+ var schemaExample = schema?.Example;
+ var schemaEnum = schema?.Enum?.FirstOrDefault();
+
+ _exampleValues.Schema = schema;
+
+ switch (schema?.GetSchemaType(out _))
+ {
+ case JsonSchemaType.Boolean:
+ var exampleBoolean = schemaExample?.GetValue();
+ return exampleBoolean ?? _exampleValues.Boolean;
+
+ case JsonSchemaType.Integer:
+ var exampleInteger = schemaExample?.GetValue();
+ var enumInteger = schemaEnum?.GetValue();
+ var valueIntegerEnumOrExample = enumInteger ?? exampleInteger;
+ return valueIntegerEnumOrExample ?? _exampleValues.Integer;
+
+ case JsonSchemaType.Number:
+ switch (schema.GetSchemaFormat())
+ {
+ case SchemaFormat.Float:
+ var exampleFloat = schemaExample?.GetValue();
+ var enumFloat = schemaEnum?.GetValue();
+ var valueFloatEnumOrExample = enumFloat ?? exampleFloat;
+ return valueFloatEnumOrExample ?? _exampleValues.Float;
+
+ default:
+ var exampleDecimal = schemaExample?.GetValue();
+ var enumDecimal = schemaEnum?.GetValue();
+ var valueDecimalEnumOrExample = enumDecimal ?? exampleDecimal;
+ return valueDecimalEnumOrExample ?? _exampleValues.Decimal;
+ }
+
+ default:
+ switch (schema?.GetSchemaFormat())
+ {
+ case SchemaFormat.Date:
+ var exampleDate = schemaExample?.GetValue();
+ var enumDate = schemaEnum?.GetValue();
+ var valueDateEnumOrExample = enumDate ?? exampleDate;
+ return valueDateEnumOrExample ?? DateTimeUtils.ToRfc3339Date(_exampleValues.Date());
+
+ case SchemaFormat.DateTime:
+ var exampleDateTime = schemaExample?.GetValue();
+ var enumDateTime = schemaEnum?.GetValue();
+ var valueDateTimeEnumOrExample = enumDateTime ?? exampleDateTime;
+ return valueDateTimeEnumOrExample ?? DateTimeUtils.ToRfc3339DateTime(_exampleValues.DateTime());
+
+ case SchemaFormat.Byte:
+ var exampleByte = schemaExample?.GetValue();
+ var enumByte = schemaEnum?.GetValue();
+ var valueByteEnumOrExample = enumByte ?? exampleByte;
+ return Convert.ToBase64String(valueByteEnumOrExample ?? _exampleValues.Bytes);
+
+ default:
+ var exampleString = schemaExample?.GetValue();
+ var enumString = schemaEnum?.GetValue();
+ var valueStringEnumOrExample = enumString ?? exampleString;
+ return valueStringEnumOrExample ?? _exampleValues.String;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/Utils/PathUtils.cs b/src/WireMock.Net.OpenApiParser.Preview/Utils/PathUtils.cs
new file mode 100644
index 00000000..c46b3923
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/Utils/PathUtils.cs
@@ -0,0 +1,27 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.OpenApiParser.Utils;
+
+internal static class PathUtils
+{
+ internal static string Combine(params string[] paths)
+ {
+ if (paths.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ var result = paths[0].Trim().TrimEnd('/');
+
+ for (int i = 1; i < paths.Length; i++)
+ {
+ var nextPath = paths[i].Trim().TrimStart('/').TrimEnd('/');
+ if (!string.IsNullOrEmpty(nextPath))
+ {
+ result += '/' + nextPath;
+ }
+ }
+
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/WireMock.Net.OpenApiParser.Preview.csproj b/src/WireMock.Net.OpenApiParser.Preview/WireMock.Net.OpenApiParser.Preview.csproj
new file mode 100644
index 00000000..41fdaecf
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/WireMock.Net.OpenApiParser.Preview.csproj
@@ -0,0 +1,36 @@
+
+
+
+ An OpenApi (swagger) parser to generate MappingModel or mapping.json file.
+ net47;netstandard2.0;netstandard2.1;net8.0
+ true
+ wiremock;openapi;OAS;raml;converter;parser;openapiparser
+ {E5B03EEF-822C-4295-952B-4479AD30082B}
+ true
+ ../WireMock.Net/WireMock.Net.ruleset
+ true
+ ../WireMock.Net/WireMock.Net.snk
+ true
+ MIT
+
+
+
+ true
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser.Preview/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser.Preview/WireMockOpenApiParser.cs
new file mode 100644
index 00000000..849da722
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser.Preview/WireMockOpenApiParser.cs
@@ -0,0 +1,107 @@
+// Copyright © WireMock.Net
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using JetBrains.Annotations;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Reader;
+using Microsoft.OpenApi.YamlReader;
+using RamlToOpenApiConverter;
+using WireMock.Admin.Mappings;
+using WireMock.Net.OpenApiParser.Mappers;
+using WireMock.Net.OpenApiParser.Settings;
+
+namespace WireMock.Net.OpenApiParser;
+
+///
+/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
+///
+public class WireMockOpenApiParser : IWireMockOpenApiParser
+{
+ private static readonly OpenApiReaderSettings ReaderSettings = new();
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic)
+ {
+ return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ OpenApiDocument document;
+ if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
+ {
+ diagnostic = new OpenApiDiagnostic();
+ document = new RamlConverter().ConvertToOpenApiDocument(path);
+ }
+ else
+ {
+ document = Read(File.OpenRead(path), out diagnostic);
+ }
+
+ return FromDocument(document, settings);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
+ {
+ return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(document.Paths, document.Servers ?? []);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
+ {
+ return FromDocument(Read(stream, out diagnostic));
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ return FromDocument(Read(stream, out diagnostic), settings);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic)
+ {
+ return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), out diagnostic);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic);
+ }
+
+ private static OpenApiDocument Read(Stream stream, out OpenApiDiagnostic diagnostic)
+ {
+ var reader = new OpenApiYamlReader();
+
+ if (stream is not MemoryStream memoryStream)
+ {
+ memoryStream = ReadStreamIntoMemoryStream(stream);
+ }
+
+ var result = reader.Read(memoryStream, ReaderSettings);
+
+ diagnostic = result.Diagnostic ?? new OpenApiDiagnostic();
+ return result.Document ?? throw new InvalidOperationException("The document is null.");
+ }
+
+ private static MemoryStream ReadStreamIntoMemoryStream(Stream stream)
+ {
+ var memoryStream = new MemoryStream();
+ stream.CopyTo(memoryStream);
+ memoryStream.Position = 0;
+ return memoryStream;
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs
index a819a1f9..8034318e 100644
--- a/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs
+++ b/src/WireMock.Net.OpenApiParser/Extensions/DictionaryExtensions.cs
@@ -1,6 +1,6 @@
// Copyright © WireMock.Net
-#if NET46 || NET47 || NETSTANDARD2_0
+#if NET46 || NETSTANDARD2_0
using System.Collections.Generic;
namespace WireMock.Net.OpenApiParser.Extensions;
diff --git a/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs
index 02f7a49b..efd4bfcd 100644
--- a/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs
+++ b/src/WireMock.Net.OpenApiParser/Extensions/OpenApiSchemaExtensions.cs
@@ -1,53 +1,60 @@
// Copyright © WireMock.Net
using System.Linq;
-using System.Text.Json;
using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Models.Interfaces;
using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Extensions;
internal static class OpenApiSchemaExtensions
{
- public static bool TryGetXNullable(this IOpenApiSchema schema, out bool value)
+ ///
+ /// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
+ ///
+ public static bool TryGetXNullable(this OpenApiSchema schema, out bool value)
{
value = false;
- if (schema.Extensions != null && schema.Extensions.TryGetValue(OpenApiConstants.NullableExtension, out var nullExtRawValue) && nullExtRawValue is OpenApiAny { Node: { } jsonNode })
+ if (schema.Extensions.TryGetValue("x-nullable", out var e) && e is OpenApiBoolean openApiBoolean)
{
- value = jsonNode.GetValueKind() == JsonValueKind.True;
+ value = openApiBoolean.Value;
return true;
}
return false;
}
- public static JsonSchemaType? GetSchemaType(this IOpenApiSchema? schema, out bool isNullable)
+ public static SchemaType GetSchemaType(this OpenApiSchema? schema)
{
- isNullable = false;
-
if (schema == null)
{
- return null;
+ return SchemaType.Unknown;
}
if (schema.Type == null)
{
- if (schema.AllOf?.Any() == true || schema.AnyOf?.Any() == true)
+ if (schema.AllOf.Any() || schema.AnyOf.Any())
{
- return JsonSchemaType.Object;
+ return SchemaType.Object;
}
}
- isNullable = (schema.Type | JsonSchemaType.Null) == JsonSchemaType.Null || (schema.TryGetXNullable(out var xNullable) && xNullable);
-
- // Removes the Null flag from the schema.Type, ensuring the returned value represents a non-nullable type.
- return schema.Type & ~JsonSchemaType.Null;
+ return schema.Type switch
+ {
+ "object" => SchemaType.Object,
+ "array" => SchemaType.Array,
+ "integer" => SchemaType.Integer,
+ "number" => SchemaType.Number,
+ "boolean" => SchemaType.Boolean,
+ "string" => SchemaType.String,
+ "file" => SchemaType.File,
+ _ => SchemaType.Unknown
+ };
}
- public static SchemaFormat GetSchemaFormat(this IOpenApiSchema? schema)
+ public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema)
{
switch (schema?.Format)
{
diff --git a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs
index bd20f521..494d4f37 100644
--- a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs
+++ b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs
@@ -1,96 +1,96 @@
-// Copyright © WireMock.Net
-
-using System.IO;
-using System.Linq;
-using JetBrains.Annotations;
-using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Reader;
-using Stef.Validation;
-using WireMock.Net.OpenApiParser.Settings;
-using WireMock.Server;
-
-namespace WireMock.Net.OpenApiParser.Extensions;
-
-///
-/// Some extension methods for .
-///
-public static class WireMockServerExtensions
-{
- ///
- /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file.
- ///
- /// The WireMockServer instance
- /// Path containing OpenAPI file to parse and use the mappings.
- /// Returns diagnostic object containing errors detected during parsing
- [PublicAPI]
- public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
- {
- return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
- }
-
- ///
- /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 file.
- ///
- /// The WireMockServer instance
- /// Path containing OpenAPI file to parse and use the mappings.
- /// 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);
- Guard.NotNullOrEmpty(path);
-
- var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
-
- return server.WithMapping(mappings.ToArray());
- }
-
- ///
- /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream.
- ///
- /// The WireMockServer instance
- /// Stream containing OpenAPI description to parse and use the mappings.
- /// Returns diagnostic object containing errors detected during parsing
- [PublicAPI]
- public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
- {
- return WithMappingFromOpenApiStream(server, stream, new WireMockOpenApiParserSettings(), out diagnostic);
- }
-
- ///
- /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 stream.
- ///
- /// The WireMockServer instance
- /// Stream containing OpenAPI description to parse and use the mappings.
- /// Additional settings
- /// Returns diagnostic object containing errors detected during parsing
- [PublicAPI]
- public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
- {
- Guard.NotNull(server);
- Guard.NotNull(stream);
- Guard.NotNull(settings);
-
- var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
-
- return server.WithMapping(mappings.ToArray());
- }
-
- ///
- /// Register the mappings via an OpenAPI (swagger) V2/V3/V3.1 document.
- ///
- /// The WireMockServer instance
- /// The OpenAPI document to use as mappings.
- /// Additional settings [optional].
- [PublicAPI]
- public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
- {
- Guard.NotNull(server);
- Guard.NotNull(document);
-
- var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
-
- return server.WithMapping(mappings.ToArray());
- }
+// Copyright © WireMock.Net
+
+using System.IO;
+using System.Linq;
+using JetBrains.Annotations;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers;
+using Stef.Validation;
+using WireMock.Net.OpenApiParser.Settings;
+using WireMock.Server;
+
+namespace WireMock.Net.OpenApiParser.Extensions;
+
+///
+/// Some extension methods for .
+///
+public static class WireMockServerExtensions
+{
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
+ ///
+ /// The WireMockServer instance
+ /// Path containing OpenAPI file to parse and use the mappings.
+ /// Returns diagnostic object containing errors detected during parsing
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
+ {
+ return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
+ ///
+ /// The WireMockServer instance
+ /// Path containing OpenAPI file to parse and use the mappings.
+ /// 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);
+ Guard.NotNullOrEmpty(path);
+
+ var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
+
+ return server.WithMapping(mappings.ToArray());
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
+ ///
+ /// The WireMockServer instance
+ /// Stream containing OpenAPI description to parse and use the mappings.
+ /// Returns diagnostic object containing errors detected during parsing
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
+ {
+ return WithMappingFromOpenApiStream(server, stream, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
+ ///
+ /// The WireMockServer instance
+ /// Stream containing OpenAPI description to parse and use the mappings.
+ /// Additional settings
+ /// Returns diagnostic object containing errors detected during parsing
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ Guard.NotNull(server);
+ Guard.NotNull(stream);
+ Guard.NotNull(settings);
+
+ var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
+
+ return server.WithMapping(mappings.ToArray());
+ }
+
+ ///
+ /// Register the mappings via an OpenAPI (swagger) V2 or V3 document.
+ ///
+ /// The WireMockServer instance
+ /// The OpenAPI document to use as mappings.
+ /// Additional settings [optional].
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
+ {
+ Guard.NotNull(server);
+ Guard.NotNull(document);
+
+ var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
+
+ return server.WithMapping(mappings.ToArray());
+ }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs
index 4118414c..e3b2b728 100644
--- a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs
+++ b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs
@@ -1,75 +1,75 @@
-// Copyright © WireMock.Net
-
-using System.Collections.Generic;
-using System.IO;
-using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Reader;
-using WireMock.Admin.Mappings;
-using WireMock.Net.OpenApiParser.Settings;
-
-namespace WireMock.Net.OpenApiParser;
-
-///
-/// 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/V31 or Raml file.
- /// OpenApiDiagnostic output
- /// MappingModel
- IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic);
-
- ///
- /// Generate from a file-path.
- ///
- /// The path to read the OpenApi/Swagger/V2/V3/V31 or Raml file.
- /// Additional settings
- /// OpenApiDiagnostic output
- /// MappingModel
- IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
-
- ///
- /// Generate from an .
- ///
- /// The source OpenApiDocument
- /// Additional settings [optional]
- /// MappingModel
- IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
-
- ///
- /// Generate from a .
- ///
- /// The source stream
- /// OpenApiDiagnostic output
- /// MappingModel
- IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
-
- ///
- /// Generate from a .
- ///
- /// The source stream
- /// Additional settings
- /// OpenApiDiagnostic output
- /// MappingModel
- IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
-
- ///
- /// Generate from a .
- ///
- /// The source text
- /// OpenApiDiagnostic output
- /// MappingModel
- IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic);
-
- ///
- /// Generate from a .
- ///
- /// The source text
- /// Additional settings
- /// OpenApiDiagnostic output
- /// MappingModel
- IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+// Copyright © WireMock.Net
+
+using System.Collections.Generic;
+using System.IO;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers;
+using WireMock.Admin.Mappings;
+using WireMock.Net.OpenApiParser.Settings;
+
+namespace WireMock.Net.OpenApiParser;
+
+///
+/// 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.
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList 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
+ IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from an .
+ ///
+ /// The source OpenApiDocument
+ /// Additional settings [optional]
+ /// MappingModel
+ IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source stream
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source stream
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source text
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source text
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ 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 e48766e0..6056aecb 100644
--- a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs
+++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs
@@ -3,19 +3,20 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.IO;
using System.Linq;
-using System.Text.Json;
-using System.Text.Json.Nodes;
+using Microsoft.OpenApi;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Models.Interfaces;
+using Microsoft.OpenApi.Writers;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
using WireMock.Net.OpenApiParser.Utils;
-using SystemTextJsonSerializer = System.Text.Json.JsonSerializer;
namespace WireMock.Net.OpenApiParser.Mappers;
@@ -38,40 +39,56 @@ internal class OpenApiPathsMapper
.OrderBy(p => p.Key)
.Select(p => MapPath(p.Key, p.Value, servers))
.SelectMany(x => x)
- .ToArray() ?? [];
+ .ToArray() ??
+ Array.Empty();
}
- private IReadOnlyList MapPath(string path, IOpenApiPathItem pathItem, IList servers)
+ private IReadOnlyList MapPaths(OpenApiPaths? paths, IList servers)
{
- return pathItem.Operations?.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray() ?? [];
+ 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)
+ {
+ 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)
{
- var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? [];
- var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? [];
- var headers = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? [];
+ var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
+ var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
+ var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header);
- var response = operation?.Responses?.FirstOrDefault() ?? new KeyValuePair();
+ var response = operation.Responses.FirstOrDefault();
- TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out var responseContentType);
+ TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType);
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
var responseExample = responseContent?.Example;
var responseSchemaExample = responseContent?.Schema?.Example;
- var responseBody = responseExample ?? responseSchemaExample ?? MapSchemaToObject(responseSchema);
+ var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) :
+ responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) :
+ MapSchemaToObject(responseSchema);
var requestBodyModel = new BodyModel();
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
{
var request = operation.RequestBody.Content;
- TryGetContent(request, out var requestContent, out _);
+ TryGetContent(request, out OpenApiMediaType? requestContent, out _);
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
var requestBodyExample = requestContent!.Example;
var requestBodySchemaExample = requestContent.Schema?.Example;
- var requestBodyMapped = requestBodyExample ?? requestBodySchemaExample ?? MapSchemaToObject(requestBodySchema);
+ var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
+ requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) :
+ MapSchemaToObject(requestBodySchema);
+
requestBodyModel = MapRequestBody(requestBodyMapped);
}
@@ -95,12 +112,12 @@ internal class OpenApiPathsMapper
{
StatusCode = httpStatusCode,
Headers = MapHeaders(responseContentType, response.Value?.Headers),
- BodyAsJson = responseBody != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(responseBody)) : null
+ BodyAsJson = body
}
};
}
- private BodyModel? MapRequestBody(JsonNode? requestBody)
+ private BodyModel? MapRequestBody(object? requestBody)
{
if (requestBody == null)
{
@@ -112,7 +129,7 @@ internal class OpenApiPathsMapper
Matcher = new MatcherModel
{
Name = "JsonMatcher",
- Pattern = SystemTextJsonSerializer.Serialize(requestBody, new JsonSerializerOptions { WriteIndented = true }),
+ Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented),
IgnoreCase = _settings.RequestBodyIgnoreCase
}
};
@@ -143,103 +160,117 @@ internal class OpenApiPathsMapper
return true;
}
- private JsonNode? MapSchemaToObject(IOpenApiSchema? schema)
+ private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null)
{
if (schema == null)
{
return null;
}
- switch (schema.GetSchemaType(out _))
+ switch (schema.GetSchemaType())
{
- case JsonSchemaType.Array:
- var array = new JsonArray();
- for (var i = 0; i < _settings.NumberOfArrayItems; i++)
+ case SchemaType.Array:
+ var jArray = new JArray();
+ for (int i = 0; i < _settings.NumberOfArrayItems; i++)
{
- if (schema.Items?.Properties?.Count > 0)
+ if (schema.Items.Properties.Count > 0)
{
- var item = new JsonObject();
+ var arrayItem = new JObject();
foreach (var property in schema.Items.Properties)
{
- item[property.Key] = MapSchemaToObject(property.Value);
+ var objectValue = MapSchemaToObject(property.Value, property.Key);
+ if (objectValue is JProperty jp)
+ {
+ arrayItem.Add(jp);
+ }
+ else
+ {
+ arrayItem.Add(new JProperty(property.Key, objectValue));
+ }
}
- array.Add(item);
+ jArray.Add(arrayItem);
}
else
{
- var arrayItem = MapSchemaToObject(schema.Items);
- array.Add(arrayItem);
+ var arrayItem = MapSchemaToObject(schema.Items, name: null); // Set name to null to force JObject instead of JProperty
+ jArray.Add(arrayItem);
}
}
- if (schema.AllOf?.Count > 0)
+ if (schema.AllOf.Count > 0)
{
- array.Add(MapSchemaAllOfToObject(schema));
+ jArray.Add(MapSchemaAllOfToObject(schema));
}
- return array;
+ return jArray;
- case JsonSchemaType.Boolean:
- case JsonSchemaType.Integer:
- case JsonSchemaType.Number:
- case JsonSchemaType.String:
+ case SchemaType.Boolean:
+ case SchemaType.Integer:
+ case SchemaType.Number:
+ case SchemaType.String:
return _exampleValueGenerator.GetExampleValue(schema);
- case JsonSchemaType.Object:
- var propertyAsJsonObject = new JsonObject();
- foreach (var schemaProperty in schema.Properties ?? new Dictionary())
+ case SchemaType.Object:
+ var propertyAsJObject = new JObject();
+ foreach (var schemaProperty in schema.Properties)
{
- propertyAsJsonObject[schemaProperty.Key] = MapPropertyAsJsonNode(schemaProperty.Value);
+ propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
}
- if (schema.AllOf?.Count > 0)
+ if (schema.AllOf.Count > 0)
{
- foreach (var group in schema.AllOf.SelectMany(p => p.Properties ?? new Dictionary()).GroupBy(x => x.Key))
+ foreach (var group in schema.AllOf.SelectMany(p => p.Properties).GroupBy(x => x.Key))
{
- propertyAsJsonObject[group.Key] = MapPropertyAsJsonNode(group.First().Value);
+ propertyAsJObject.Add(MapPropertyAsJObject(group.First().Value, group.Key));
}
}
- return propertyAsJsonObject;
+ return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject;
default:
return null;
}
}
- private JsonObject MapSchemaAllOfToObject(IOpenApiSchema schema)
+ private JObject MapSchemaAllOfToObject(OpenApiSchema schema)
{
- var arrayItem = new JsonObject();
- foreach (var property in schema.AllOf ?? [])
+ var arrayItem = new JObject();
+ foreach (var property in schema.AllOf)
{
- foreach (var item in property.Properties ?? new Dictionary())
+ foreach (var item in property.Properties)
{
- arrayItem[item.Key] = MapPropertyAsJsonNode(item.Value);
+ arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key));
}
}
return arrayItem;
}
- private JsonNode? MapPropertyAsJsonNode(IOpenApiSchema openApiSchema)
+ private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key)
{
- var schemaType = openApiSchema.GetSchemaType(out _);
- if (schemaType is JsonSchemaType.Object or JsonSchemaType.Array)
+ if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array)
{
- return MapSchemaToObject(openApiSchema);
+ var mapped = MapSchemaToObject(openApiSchema, key);
+ if (mapped is JProperty jp)
+ {
+ return jp;
+ }
+
+ return new JProperty(key, mapped);
}
- return _exampleValueGenerator.GetExampleValue(openApiSchema);
+ // bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
+ return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
}
- private string MapPathWithParameters(string path, IEnumerable? parameters)
+ private string MapPathWithParameters(string path, IEnumerable? parameters)
{
if (parameters == null)
{
return path;
}
- var newPath = path;
+ string newPath = path;
foreach (var parameter in parameters)
{
var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse);
@@ -249,56 +280,93 @@ internal class OpenApiPathsMapper
return newPath;
}
- private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers)
+ private string MapBasePath(IList? servers)
{
- var mappedHeaders = headers?
- .ToDictionary(item => item.Key, _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!) ?? new Dictionary();
+ if (servers == null || servers.Count == 0)
+ {
+ return string.Empty;
+ }
+
+ OpenApiServer server = servers.First();
+ if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
+ {
+ return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
+ }
+
+ return string.Empty;
+ }
+
+ private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
+ {
+ if (any == null)
+ {
+ return null;
+ }
+
+ using var outputString = new StringWriter();
+ var writer = new OpenApiJsonWriter(outputString);
+ any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
+
+ if (any.AnyType == AnyType.Array)
+ {
+ return JArray.Parse(outputString.ToString());
+ }
+
+ return JObject.Parse(outputString.ToString());
+ }
+
+ private IDictionary? MapHeaders(string? responseContentType, IDictionary? headers)
+ {
+ var mappedHeaders = headers?.ToDictionary(
+ item => item.Key,
+ _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!
+ ) ?? new Dictionary();
if (!string.IsNullOrEmpty(responseContentType))
{
- mappedHeaders.TryAdd(HeaderContentType, responseContentType);
+ mappedHeaders.TryAdd(HeaderContentType, responseContentType!);
}
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
}
- private IList? MapQueryParameters(IEnumerable queryParameters)
+ private IList? MapQueryParameters(IEnumerable queryParameters)
{
var list = queryParameters
.Where(req => req.Required)
.Select(qp => new ParamModel
{
- Name = qp.Name ?? string.Empty,
+ Name = qp.Name,
IgnoreCase = _settings.QueryParameterPatternIgnoreCase,
- Matchers =
- [
+ Matchers = new[]
+ {
GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse)
- ]
+ }
})
.ToList();
return list.Any() ? list : null;
}
- private IList? MapRequestHeaders(IEnumerable headers)
+ private IList? MapRequestHeaders(IEnumerable headers)
{
var list = headers
.Where(req => req.Required)
.Select(qp => new HeaderModel
{
- Name = qp.Name ?? string.Empty,
+ Name = qp.Name,
IgnoreCase = _settings.HeaderPatternIgnoreCase,
- Matchers =
- [
+ Matchers = new[]
+ {
GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse)
- ]
+ }
})
.ToList();
return list.Any() ? list : null;
}
- private MatcherModel GetExampleMatcherModel(IOpenApiSchema? schema, ExampleValueType type)
+ private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type)
{
return type switch
{
@@ -317,31 +385,15 @@ internal class OpenApiPathsMapper
};
}
- private string GetExampleValueAsStringForSchemaType(IOpenApiSchema? schema)
+ private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
{
var value = _exampleValueGenerator.GetExampleValue(schema);
- if (value.GetValueKind() == JsonValueKind.String)
+ return value switch
{
- return value.GetValue();
- }
+ string valueAsString => valueAsString,
- return value.ToString();
- }
-
- private static string MapBasePath(IList? servers)
- {
- var server = servers?.FirstOrDefault();
- if (server == null)
- {
- return string.Empty;
- }
-
- if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out var uriResult))
- {
- return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
- }
-
- return string.Empty;
+ _ => value.ToString(),
+ };
}
}
\ 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 7c52339d..eecfed7e 100644
--- a/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs
+++ b/src/WireMock.Net.OpenApiParser/Settings/IWireMockOpenApiParserExampleValues.cs
@@ -1,62 +1,62 @@
-// Copyright © WireMock.Net
-
-using System;
-using Microsoft.OpenApi.Models.Interfaces;
-
-namespace WireMock.Net.OpenApiParser.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; }
-
- ///
- /// An example value for an Integer.
- ///
- int Integer { get; }
-
- ///
- /// An example value for a Float.
- ///
- float Float { get; }
-
- ///
- /// An example value for a Decimal.
- ///
- decimal Decimal { get; }
-
- ///
- /// An example value for a Date.
- ///
- Func Date { get; }
-
- ///
- /// An example value for a DateTime.
- ///
- Func DateTime { get; }
-
- ///
- /// An example value for Bytes.
- ///
- byte[] Bytes { get; }
-
- ///
- /// An example value for a Object.
- ///
- object Object { get; }
-
- ///
- /// An example value for a String.
- ///
- string String { get; }
-
- ///
- /// OpenApi Schema to generate dynamic examples more accurate
- ///
- IOpenApiSchema? Schema { get; set; }
+// Copyright © WireMock.Net
+
+using System;
+using Microsoft.OpenApi.Models;
+
+namespace WireMock.Net.OpenApiParser.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; }
+
+ ///
+ /// An example value for an Integer.
+ ///
+ int Integer { get; }
+
+ ///
+ /// An example value for a Float.
+ ///
+ float Float { get; }
+
+ ///
+ /// An example value for a Double.
+ ///
+ double Double { get; }
+
+ ///
+ /// An example value for a Date.
+ ///
+ Func Date { get; }
+
+ ///
+ /// An example value for a DateTime.
+ ///
+ Func DateTime { get; }
+
+ ///
+ /// An example value for Bytes.
+ ///
+ byte[] Bytes { get; }
+
+ ///
+ /// An example value for a Object.
+ ///
+ object Object { get; }
+
+ ///
+ /// An example value for a String.
+ ///
+ string String { get; }
+
+ ///
+ /// 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 c28e7016..46b2c766 100644
--- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs
+++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserDynamicExampleValues.cs
@@ -1,59 +1,44 @@
-// Copyright © WireMock.Net
-
-using System;
-using Microsoft.OpenApi.Models.Interfaces;
-using RandomDataGenerator.FieldOptions;
-using RandomDataGenerator.Randomizers;
-
-namespace WireMock.Net.OpenApiParser.Settings;
-
-///
-/// A class defining the random example values to use for the different types.
-///
-public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
-{
- ///
- public virtual bool Boolean => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true;
-
- ///
- public virtual int Integer => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42;
-
- ///
- public virtual float Float => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f;
-
- ///
- public virtual decimal Decimal => SafeConvertFloatToDecimal(RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f);
-
- ///
- public virtual Func Date => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date;
-
- ///
- public virtual Func DateTime => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow;
-
- ///
- public virtual byte[] Bytes => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate();
-
- ///
- public virtual object Object => "example-object";
-
- ///
- public virtual string String => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string";
-
- ///
- public virtual IOpenApiSchema? Schema { get; set; }
-
- ///
- /// Safely converts a float to a decimal, ensuring the value stays within the bounds of a decimal.
- ///
- /// The float value to convert.
- /// A decimal value within the valid range of a decimal.
- private static decimal SafeConvertFloatToDecimal(float value)
- {
- return value switch
- {
- < (float)decimal.MinValue => decimal.MinValue,
- > (float)decimal.MaxValue => decimal.MaxValue,
- _ => (decimal)value
- };
- }
+// Copyright © WireMock.Net
+
+using System;
+using Microsoft.OpenApi.Models;
+using RandomDataGenerator.FieldOptions;
+using RandomDataGenerator.Randomizers;
+
+namespace WireMock.Net.OpenApiParser.Settings;
+
+///
+/// A class defining the random example values to use for the different types.
+///
+public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
+{
+ ///
+ public virtual bool Boolean => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true;
+
+ ///
+ public virtual int Integer => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42;
+
+ ///
+ public virtual float Float => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f;
+
+ ///
+ public virtual double Double => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d;
+
+ ///
+ public virtual Func Date => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date;
+
+ ///
+ public virtual Func DateTime => () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow;
+
+ ///
+ public virtual byte[] Bytes => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate();
+
+ ///
+ public virtual object Object => "example-object";
+
+ ///
+ public virtual string String => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string";
+
+ ///
+ 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 4a43b424..99665cf0 100644
--- a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs
+++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs
@@ -1,43 +1,42 @@
-// Copyright © WireMock.Net
-
-using System;
-using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Models.Interfaces;
-
-namespace WireMock.Net.OpenApiParser.Settings;
-
-///
-/// A class defining the example values to use for the different types.
-///
-public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
-{
- ///
- public virtual bool Boolean => true;
-
- ///
- public virtual int Integer => 42;
-
- ///
- public virtual float Float => 4.2f;
-
- ///
- public virtual decimal Decimal => 4.2m;
-
- ///
- public virtual Func Date { get; } = () => System.DateTime.UtcNow.Date;
-
- ///
- public virtual Func DateTime { get; } = () => System.DateTime.UtcNow;
-
- ///
- public virtual byte[] Bytes { get; } = [48, 49, 50];
-
- ///
- public virtual object Object => "example-object";
-
- ///
- public virtual string String => "example-string";
-
- ///
- public virtual IOpenApiSchema? Schema { get; set; } = new OpenApiSchema();
+// Copyright © WireMock.Net
+
+using System;
+using Microsoft.OpenApi.Models;
+
+namespace WireMock.Net.OpenApiParser.Settings;
+
+///
+/// A class defining the example values to use for the different types.
+///
+public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
+{
+ ///
+ public virtual bool Boolean => true;
+
+ ///
+ public virtual int Integer => 42;
+
+ ///
+ public virtual float Float => 4.2f;
+
+ ///
+ public virtual double Double => 4.2d;
+
+ ///
+ public virtual Func Date { get; } = () => System.DateTime.UtcNow.Date;
+
+ ///
+ public virtual Func DateTime { get; } = () => System.DateTime.UtcNow;
+
+ ///
+ public virtual byte[] Bytes { get; } = { 48, 49, 50 };
+
+ ///
+ public virtual object Object => "example-object";
+
+ ///
+ public virtual string String => "example-string";
+
+ ///
+ public virtual OpenApiSchema? Schema { get; set; } = new();
}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Types/SchemaType.cs b/src/WireMock.Net.OpenApiParser/Types/SchemaType.cs
new file mode 100644
index 00000000..4b5df64d
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser/Types/SchemaType.cs
@@ -0,0 +1,22 @@
+// Copyright © WireMock.Net
+
+namespace WireMock.Net.OpenApiParser.Types;
+
+internal enum SchemaType
+{
+ Object,
+
+ Array,
+
+ String,
+
+ Integer,
+
+ Number,
+
+ Boolean,
+
+ File,
+
+ Unknown
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs b/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs
index e3178549..c0108295 100644
--- a/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs
+++ b/src/WireMock.Net.OpenApiParser/Utils/DateTimeUtils.cs
@@ -7,16 +7,13 @@ namespace WireMock.Net.OpenApiParser.Utils;
internal static class DateTimeUtils
{
- private const string DateFormat = "yyyy-MM-dd";
- private const string DateTimeFormat = "yyyy-MM-dd'T'HH:mm:ss.fffzzz";
-
public static string ToRfc3339DateTime(DateTime dateTime)
{
- return dateTime.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo);
+ return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
}
public static string ToRfc3339Date(DateTime dateTime)
{
- return dateTime.ToString(DateFormat, DateTimeFormatInfo.InvariantInfo);
+ return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
}
}
\ 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 153d4c67..4039a358 100644
--- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs
+++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs
@@ -1,10 +1,8 @@
// Copyright © WireMock.Net
-using System;
using System.Linq;
-using System.Text.Json.Nodes;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Models.Interfaces;
using Stef.Validation;
using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings;
@@ -38,66 +36,82 @@ internal class ExampleValueGenerator
}
}
- public JsonNode GetExampleValue(IOpenApiSchema? schema)
+ public object GetExampleValue(OpenApiSchema? schema)
{
var schemaExample = schema?.Example;
var schemaEnum = schema?.Enum?.FirstOrDefault();
_exampleValues.Schema = schema;
- switch (schema?.GetSchemaType(out _))
+ switch (schema?.GetSchemaType())
{
- case JsonSchemaType.Boolean:
- var exampleBoolean = schemaExample?.GetValue();
- return exampleBoolean ?? _exampleValues.Boolean;
+ case SchemaType.Boolean:
+ var exampleBoolean = schemaExample as OpenApiBoolean;
+ return exampleBoolean?.Value ?? _exampleValues.Boolean;
- case JsonSchemaType.Integer:
- var exampleInteger = schemaExample?.GetValue();
- var enumInteger = schemaEnum?.GetValue();
- var valueIntegerEnumOrExample = enumInteger ?? exampleInteger;
- return valueIntegerEnumOrExample ?? _exampleValues.Integer;
+ case SchemaType.Integer:
+ switch (schema?.GetSchemaFormat())
+ {
+ case SchemaFormat.Int64:
+ var exampleLong = schemaExample as OpenApiLong;
+ var enumLong = schemaEnum as OpenApiLong;
+ var valueLongEnumOrExample = enumLong?.Value ?? exampleLong?.Value;
+ return valueLongEnumOrExample ?? _exampleValues.Integer;
- case JsonSchemaType.Number:
- switch (schema.GetSchemaFormat())
+ default:
+ 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 = schemaExample?.GetValue();
- var enumFloat = schemaEnum?.GetValue();
- var valueFloatEnumOrExample = enumFloat ?? exampleFloat;
+ var exampleFloat = schemaExample as OpenApiFloat;
+ var enumFloat = schemaEnum as OpenApiFloat;
+ var valueFloatEnumOrExample = enumFloat?.Value ?? exampleFloat?.Value;
return valueFloatEnumOrExample ?? _exampleValues.Float;
default:
- var exampleDecimal = schemaExample?.GetValue();
- var enumDecimal = schemaEnum?.GetValue();
- var valueDecimalEnumOrExample = enumDecimal ?? exampleDecimal;
- return valueDecimalEnumOrExample ?? _exampleValues.Decimal;
+ 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 = schemaExample?.GetValue();
- var enumDate = schemaEnum?.GetValue();
- var valueDateEnumOrExample = enumDate ?? exampleDate;
- return valueDateEnumOrExample ?? DateTimeUtils.ToRfc3339Date(_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 = schemaExample?.GetValue();
- var enumDateTime = schemaEnum?.GetValue();
- var valueDateTimeEnumOrExample = enumDateTime ?? exampleDateTime;
- return valueDateTimeEnumOrExample ?? DateTimeUtils.ToRfc3339DateTime(_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 = schemaExample?.GetValue();
- var enumByte = schemaEnum?.GetValue();
- var valueByteEnumOrExample = enumByte ?? exampleByte;
- return Convert.ToBase64String(valueByteEnumOrExample ?? _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 = schemaExample as OpenApiBinary;
+ var enumBinary = schemaEnum as OpenApiBinary;
+ var valueBinaryEnumOrExample = enumBinary?.Value ?? exampleBinary?.Value;
+ return valueBinaryEnumOrExample ?? _exampleValues.Object;
default:
- var exampleString = schemaExample?.GetValue();
- var enumString = schemaEnum?.GetValue();
- var valueStringEnumOrExample = enumString ?? exampleString;
+ var exampleString = schemaExample as OpenApiString;
+ var enumString = schemaEnum as OpenApiString;
+ var valueStringEnumOrExample = enumString?.Value ?? exampleString?.Value;
return valueStringEnumOrExample ?? _exampleValues.String;
}
}
diff --git a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj
index d6053b02..9fac3c76 100644
--- a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj
+++ b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj
@@ -1,36 +1,37 @@
-
-
-
- An OpenApi (swagger) parser to generate MappingModel or mapping.json file.
- net47;netstandard2.0;netstandard2.1;net8.0
- true
- wiremock;openapi;OAS;raml;converter;parser;openapiparser
- {D3804228-91F4-4502-9595-39584E5AADAD}
- true
- ../WireMock.Net/WireMock.Net.ruleset
- true
- ../WireMock.Net/WireMock.Net.snk
- true
- MIT
-
-
-
- true
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
+
+
+
+ An OpenApi (swagger) parser to generate MappingModel or mapping.json file.
+ net46;netstandard2.0;netstandard2.1
+ true
+ wiremock;openapi;OAS;raml;converter;parser;openapiparser
+ {D3804228-91F4-4502-9595-39584E5AADAD}
+ true
+ ../WireMock.Net/WireMock.Net.ruleset
+ true
+ ../WireMock.Net/WireMock.Net.snk
+ true
+ MIT
+
+
+
+ true
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs
index 9db653d8..c027eed9 100644
--- a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs
+++ b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs
@@ -1,107 +1,84 @@
-// Copyright © WireMock.Net
-
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using JetBrains.Annotations;
-using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Reader;
-using Microsoft.OpenApi.YamlReader;
-using RamlToOpenApiConverter;
-using WireMock.Admin.Mappings;
-using WireMock.Net.OpenApiParser.Mappers;
-using WireMock.Net.OpenApiParser.Settings;
-
-namespace WireMock.Net.OpenApiParser;
-
-///
-/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
-///
-public class WireMockOpenApiParser : IWireMockOpenApiParser
-{
- private static readonly OpenApiReaderSettings ReaderSettings = new();
-
- ///
- [PublicAPI]
- public IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic)
- {
- return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
- }
-
- ///
- [PublicAPI]
- public IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
- {
- OpenApiDocument document;
- if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
- {
- diagnostic = new OpenApiDiagnostic();
- document = new RamlConverter().ConvertToOpenApiDocument(path);
- }
- else
- {
- document = Read(File.OpenRead(path), out diagnostic);
- }
-
- return FromDocument(document, settings);
- }
-
- ///
- [PublicAPI]
- public IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
- {
- return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(document.Paths, document.Servers ?? []);
- }
-
- ///
- [PublicAPI]
- public IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
- {
- return FromDocument(Read(stream, out diagnostic));
- }
-
- ///
- [PublicAPI]
- public IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
- {
- return FromDocument(Read(stream, out diagnostic), settings);
- }
-
- ///
- [PublicAPI]
- public IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic)
- {
- return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), out diagnostic);
- }
-
- ///
- [PublicAPI]
- public IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
- {
- return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic);
- }
-
- private static OpenApiDocument Read(Stream stream, out OpenApiDiagnostic diagnostic)
- {
- var reader = new OpenApiYamlReader();
-
- if (stream is not MemoryStream memoryStream)
- {
- memoryStream = ReadStreamIntoMemoryStream(stream);
- }
-
- var result = reader.Read(memoryStream, ReaderSettings);
-
- diagnostic = result.Diagnostic ?? new OpenApiDiagnostic();
- return result.Document ?? throw new InvalidOperationException("The document is null.");
- }
-
- private static MemoryStream ReadStreamIntoMemoryStream(Stream stream)
- {
- var memoryStream = new MemoryStream();
- stream.CopyTo(memoryStream);
- memoryStream.Position = 0;
- return memoryStream;
- }
+// Copyright © WireMock.Net
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using JetBrains.Annotations;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Readers;
+using RamlToOpenApiConverter;
+using WireMock.Admin.Mappings;
+using WireMock.Net.OpenApiParser.Mappers;
+using WireMock.Net.OpenApiParser.Settings;
+
+namespace WireMock.Net.OpenApiParser;
+
+///
+/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
+///
+public class WireMockOpenApiParser : IWireMockOpenApiParser
+{
+ private readonly OpenApiStreamReader _reader = new();
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromFile(string path, out OpenApiDiagnostic diagnostic)
+ {
+ return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ OpenApiDocument document;
+ if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
+ {
+ diagnostic = new OpenApiDiagnostic();
+ document = new RamlConverter().ConvertToOpenApiDocument(path);
+ }
+ else
+ {
+ var reader = new OpenApiStreamReader();
+ document = reader.Read(File.OpenRead(path), out diagnostic);
+ }
+
+ return FromDocument(document, settings);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
+ {
+ return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(document.Paths, document.Servers);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
+ {
+ return FromDocument(_reader.Read(stream, out diagnostic));
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ return FromDocument(_reader.Read(stream, out diagnostic), settings);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromText(string text, out OpenApiDiagnostic diagnostic)
+ {
+ return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), out diagnostic);
+ }
+
+ ///
+ [PublicAPI]
+ public IReadOnlyList FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic);
+ }
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Util/TypeLoader.cs b/src/WireMock.Net/Util/TypeLoader.cs
index 23e8e633..2be7e020 100644
--- a/src/WireMock.Net/Util/TypeLoader.cs
+++ b/src/WireMock.Net/Util/TypeLoader.cs
@@ -25,7 +25,7 @@ internal static class TypeLoader
return foundType;
}
- throw new DllNotFoundException($"No dll found which implements Interface '{key}'.");
+ throw new DllNotFoundException($"No dll found which implements interface '{key}'.");
});
return (TInterface)Activator.CreateInstance(pluginType, args)!;
diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj
index 168787c3..0edb2b2f 100644
--- a/src/WireMock.Net/WireMock.Net.csproj
+++ b/src/WireMock.Net/WireMock.Net.csproj
@@ -46,7 +46,7 @@
$(DefineConstants);USE_ASPNETCORE;NET46
-
+
$(DefineConstants);OPENAPIPARSER
@@ -205,7 +205,7 @@
-
+
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt
index 382d8455..7190423d 100644
--- a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt
+++ b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.FromText_ShouldReturnMappings.verified.txt
@@ -334,7 +334,7 @@
"processingTerminalId": "example-string",
"order": {
"orderId": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00",
+ "dateTime": "2024-06-19T12:34:56.000+00:00",
"description": "example-string",
"amount": 42,
"currency": "AED",
@@ -1787,7 +1787,7 @@
"processingTerminalId": "example-string",
"order": {
"orderId": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00",
+ "dateTime": "2024-06-19T12:34:56.000+00:00",
"description": "example-string",
"amount": 42,
"currency": "AED"
@@ -2714,7 +2714,7 @@
"processingTerminalId": "example-string",
"order": {
"orderId": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00",
+ "dateTime": "2024-06-19T12:34:56.000+00:00",
"description": "example-string",
"amount": 42,
"currency": "AED",
@@ -2863,7 +2863,7 @@
"processingTerminalId": "example-string",
"order": {
"orderId": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00",
+ "dateTime": "2024-06-19T12:34:56.000+00:00",
"description": "example-string",
"amount": 42,
"currency": "AED"
@@ -6893,7 +6893,7 @@
"operator": "example-string",
"order": {
"orderId": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00",
+ "dateTime": "2024-06-19T12:34:56.000+00:00",
"description": "example-string",
"amount": 42,
"currency": "AED",
@@ -7093,7 +7093,7 @@
"offlineProcessing": {
"operation": "offlineDecline",
"approvalCode": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00"
+ "dateTime": "2024-06-19T12:34:56.000+00:00"
},
"autoCapture": true,
"processAsSale": true
@@ -11349,7 +11349,7 @@
{
"op": "replace",
"path": "/a/b/c",
- "value": 420
+ "value": "420"
},
{
"op": "move",
@@ -11827,7 +11827,7 @@
{
"op": "replace",
"path": "/a/b/c",
- "value": 420
+ "value": "420"
},
{
"op": "move",
@@ -12541,7 +12541,7 @@
{
"op": "replace",
"path": "/a/b/c",
- "value": 420
+ "value": "420"
},
{
"op": "move",
@@ -12986,7 +12986,7 @@
"operator": "example-string",
"order": {
"orderId": "example-string",
- "dateTime": "2024-06-19T12:34:56.000\u002B00:00",
+ "dateTime": "2024-06-19T12:34:56.000+00:00",
"description": "example-string",
"amount": 42,
"currency": "AED",
diff --git a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs
index 2afddcaa..59da2970 100644
--- a/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs
+++ b/test/WireMock.Net.Tests/OpenApiParser/WireMockOpenApiParserTests.cs
@@ -23,7 +23,7 @@ public class WireMockOpenApiParserTests
_exampleValuesMock.SetupGet(e => e.Boolean).Returns(true);
_exampleValuesMock.SetupGet(e => e.Integer).Returns(42);
_exampleValuesMock.SetupGet(e => e.Float).Returns(1.1f);
- _exampleValuesMock.SetupGet(e => e.Decimal).Returns(2.2m);
+ _exampleValuesMock.SetupGet(e => e.Double).Returns(2.2);
_exampleValuesMock.SetupGet(e => e.String).Returns("example-string");
_exampleValuesMock.SetupGet(e => e.Object).Returns("example-object");
_exampleValuesMock.SetupGet(e => e.Bytes).Returns("Stef"u8.ToArray());