diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings
index f55a13e7..79eca48e 100644
--- a/WireMock.Net Solution.sln.DotSettings
+++ b/WireMock.Net Solution.sln.DotSettings
@@ -13,6 +13,7 @@
True
True
True
+ True
True
True
\ No newline at end of file
diff --git a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs
index 84a9ab4b..92cc0cf8 100644
--- a/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs
+++ b/examples/WireMock.Net.OpenApiParser.ConsoleApp/Run.cs
@@ -3,9 +3,11 @@ using System.Collections.Generic;
using System.Linq;
using WireMock.Admin.Mappings;
using WireMock.Logging;
+using WireMock.Net.OpenApiParser.Extensions;
+using WireMock.Net.OpenApiParser.Settings;
+using WireMock.Net.OpenApiParser.Types;
using WireMock.Server;
using WireMock.Settings;
-using WireMock.Net.OpenApiParser.Extensions;
namespace WireMock.Net.OpenApiParser.ConsoleApp
{
@@ -23,13 +25,18 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
ReadStaticMappings = false,
WatchStaticMappings = false,
WatchStaticMappingsInSubdirectories = false,
- Logger = new WireMockConsoleLogger(),
+ Logger = new WireMockConsoleLogger()
});
Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b");
- server.WithMappingFromOpenApiFile(path, out var diag);
+ var settings = new WireMockOpenApiParserSettings
+ {
+ PathPatternToUse = ExampleValueType.Wildcard
+ };
+
+ server.WithMappingFromOpenApiFile(path, settings, out var diag);
Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
diff --git a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs
index eb3eedd0..d195c36c 100644
--- a/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs
+++ b/src/WireMock.Net.OpenApiParser/Extensions/WireMockServerExtensions.cs
@@ -1,12 +1,19 @@
-using System;
-using System.IO;
+using System.IO;
using System.Linq;
+using System.Runtime.InteropServices.ComTypes;
+using JetBrains.Annotations;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
+using SharpYaml.Model;
+using Stef.Validation;
+using WireMock.Net.OpenApiParser.Settings;
using WireMock.Server;
namespace WireMock.Net.OpenApiParser.Extensions
{
+ ///
+ /// Some extension methods for .
+ ///
public static class WireMockServerExtensions
{
///
@@ -15,18 +22,26 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// 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)
{
- if (server == null)
- {
- throw new ArgumentNullException(nameof(server));
- }
- if (string.IsNullOrEmpty(path))
- {
- throw new ArgumentNullException(nameof(path));
- }
+ return WithMappingFromOpenApiFile(server, path, null, out diagnostic);
+ }
- var mappings = new WireMockOpenApiParser().FromFile(path, 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.
+ /// Returns diagnostic object containing errors detected during parsing
+ /// Additional settings
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
+ {
+ Guard.NotNull(server, nameof(server));
+ Guard.NotNullOrEmpty(path, nameof(path));
+
+ var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
return server.WithMapping(mappings.ToArray());
}
@@ -37,9 +52,27 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// 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)
{
- var mappings = new WireMockOpenApiParser().FromStream(stream, out diagnostic);
+ return WithMappingFromOpenApiStream(server, stream, null, 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, nameof(server));
+ Guard.NotNull(stream, nameof(stream));
+ Guard.NotNull(settings, nameof(settings));
+
+ var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
return server.WithMapping(mappings.ToArray());
}
@@ -49,9 +82,14 @@ namespace WireMock.Net.OpenApiParser.Extensions
///
/// The WireMockServer instance
/// The OpenAPI document to use as mappings.
- public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document)
+ /// Additional settings [optional]
+ [PublicAPI]
+ public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings = null)
{
- var mappings = new WireMockOpenApiParser().FromDocument(document);
+ Guard.NotNull(server, nameof(server));
+ Guard.NotNull(document, nameof(document));
+
+ var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
return server.WithMapping(mappings.ToArray());
}
diff --git a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs
index 9cc3cb40..660f890d 100644
--- a/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs
+++ b/src/WireMock.Net.OpenApiParser/IWireMockOpenApiParser.cs
@@ -1,37 +1,57 @@
-using System.Collections.Generic;
-using System.IO;
-using Microsoft.OpenApi.Models;
-using Microsoft.OpenApi.Readers;
-using WireMock.Admin.Mappings;
-
-namespace WireMock.Net.OpenApiParser
-{
- ///
- /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
- ///
- public interface IWireMockOpenApiParser
- {
- ///
- /// Generate from an .
- ///
- /// The source OpenApiDocument
- /// MappingModel
- IEnumerable FromDocument(OpenApiDocument document);
-
- ///
- /// Generate from a .
- ///
- /// The source stream
- /// OpenApiDiagnostic output
- /// MappingModel
- IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
-
+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
- IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic);
- }
+ /// MappingModel
+ IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a file-path.
+ ///
+ /// The path to read the OpenApi/Swagger/V2/V3 or Raml file.
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from an .
+ ///
+ /// The source OpenApiDocument
+ /// Additional settings [optional]
+ /// MappingModel
+ IEnumerable FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings settings = null);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source stream
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
+
+ ///
+ /// Generate from a .
+ ///
+ /// The source stream
+ /// Additional settings
+ /// OpenApiDiagnostic output
+ /// MappingModel
+ IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
+ }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs
new file mode 100644
index 00000000..5a9b4c38
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs
@@ -0,0 +1,276 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Microsoft.OpenApi;
+using Microsoft.OpenApi.Any;
+using Microsoft.OpenApi.Models;
+using Microsoft.OpenApi.Writers;
+using Newtonsoft.Json.Linq;
+using WireMock.Admin.Mappings;
+using WireMock.Net.OpenApiParser.Extensions;
+using WireMock.Net.OpenApiParser.Settings;
+using WireMock.Net.OpenApiParser.Types;
+using WireMock.Net.OpenApiParser.Utils;
+
+namespace WireMock.Net.OpenApiParser.Mappers
+{
+ internal class OpenApiPathsMapper
+ {
+ private readonly WireMockOpenApiParserSettings _settings;
+ private readonly ExampleValueGenerator _exampleValueGenerator;
+
+ public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
+ {
+ _settings = settings ?? throw new ArgumentNullException(nameof(settings));
+ _exampleValueGenerator = new ExampleValueGenerator(settings);
+ }
+
+ public IEnumerable ToMappingModels(OpenApiPaths paths)
+ {
+ return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
+ }
+
+ private IEnumerable MapPaths(OpenApiPaths paths)
+ {
+ return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
+ }
+
+ private IEnumerable MapPath(string path, OpenApiPathItem pathItem)
+ {
+ return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value));
+ }
+
+ private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation)
+ {
+ var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
+ var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
+ var response = operation.Responses.FirstOrDefault();
+
+ TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType);
+ var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
+ var responseExample = responseContent?.Example;
+
+ var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) : MapSchemaToObject(responseSchema);
+
+ if (int.TryParse(response.Key, out var httpStatusCode))
+ {
+ httpStatusCode = 200;
+ }
+
+ return new MappingModel
+ {
+ Guid = Guid.NewGuid(),
+ Request = new RequestModel
+ {
+ Methods = new[] { httpMethod },
+ Path = MapPathWithParameters(path, pathParameters),
+ Params = MapQueryParameters(queryParameters)
+ },
+ Response = new ResponseModel
+ {
+ StatusCode = httpStatusCode,
+ Headers = MapHeaders(responseContentType, response.Value?.Headers),
+ BodyAsJson = body
+ }
+ };
+ }
+
+ private bool TryGetContent(IDictionary contents, out OpenApiMediaType openApiMediaType, 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 object MapSchemaToObject(OpenApiSchema schema, string name = null)
+ {
+ if (schema == null)
+ {
+ return null;
+ }
+
+ switch (schema.GetSchemaType())
+ {
+ case SchemaType.Array:
+ var jArray = new JArray();
+ for (int i = 0; i < _settings.NumberOfArrayItems; i++)
+ {
+ if (schema.Items.Properties.Count > 0)
+ {
+ var arrayItem = new JObject();
+ foreach (var property in schema.Items.Properties)
+ {
+ var objectValue = MapSchemaToObject(property.Value, property.Key);
+ if (objectValue is JProperty jp)
+ {
+ arrayItem.Add(jp);
+ }
+ else
+ {
+ arrayItem.Add(new JProperty(property.Key, objectValue));
+ }
+ }
+
+ jArray.Add(arrayItem);
+ }
+ else
+ {
+ jArray.Add(MapSchemaToObject(schema.Items, name));
+ }
+ }
+
+ return jArray;
+
+ case SchemaType.Boolean:
+ case SchemaType.Integer:
+ case SchemaType.Number:
+ case SchemaType.String:
+ return _exampleValueGenerator.GetExampleValue(schema);
+
+ case SchemaType.Object:
+ var propertyAsJObject = new JObject();
+ foreach (var schemaProperty in schema.Properties)
+ {
+ string propertyName = schemaProperty.Key;
+ var openApiSchema = schemaProperty.Value;
+ if (openApiSchema.GetSchemaType() == SchemaType.Object)
+ {
+ var mapped = MapSchemaToObject(schemaProperty.Value, schemaProperty.Key);
+ if (mapped is JProperty jp)
+ {
+ propertyAsJObject.Add(jp);
+ }
+ }
+ else
+ {
+ bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
+
+ propertyAsJObject.Add(new JProperty(propertyName, _exampleValueGenerator.GetExampleValue(openApiSchema)));
+ }
+ }
+
+ return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
+
+ default:
+ return null;
+ }
+ }
+
+ private string MapPathWithParameters(string path, IEnumerable parameters)
+ {
+ if (parameters == null)
+ {
+ return path;
+ }
+
+ string newPath = path;
+ foreach (var parameter in parameters)
+ {
+ newPath = newPath.Replace($"{{{parameter.Name}}}", GetExampleValue(parameter.Schema, _settings.PathPatternToUse));
+ }
+
+ return newPath;
+ }
+
+ 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);
+
+ return JObject.Parse(outputString.ToString());
+ }
+
+ private IDictionary MapHeaders(string responseContentType, IDictionary headers)
+ {
+ var mappedHeaders = headers.ToDictionary(
+ item => item.Key,
+ item => GetExampleValue(null, _settings.HeaderPatternToUse) as object
+ );
+
+ if (!string.IsNullOrEmpty(responseContentType))
+ {
+ if (!mappedHeaders.ContainsKey("Content-Type"))
+ {
+ mappedHeaders.Add("Content-Type", responseContentType);
+ }
+ else
+ {
+ mappedHeaders["Content-Type"] = responseContentType;
+ }
+ }
+
+ return mappedHeaders.Keys.Any() ? mappedHeaders : null;
+ }
+
+ private IList MapQueryParameters(IEnumerable queryParameters)
+ {
+ var list = queryParameters
+ .Select(qp => new ParamModel
+ {
+ Name = qp.Name,
+ Matchers = new[]
+ {
+ new MatcherModel
+ {
+ Name = "ExactMatcher",
+ Pattern = GetDefaultValueAsStringForSchemaType(qp.Schema)
+ }
+ }
+ })
+ .ToList();
+
+ return list.Any() ? list : null;
+ }
+
+ private string GetDefaultValueAsStringForSchemaType(OpenApiSchema schema)
+ {
+ var value = _exampleValueGenerator.GetExampleValue(schema);
+
+ switch (value)
+ {
+ case string valueAsString:
+ return valueAsString;
+
+ default:
+ return value.ToString();
+ }
+ }
+
+ private string GetExampleValue(OpenApiSchema schema, ExampleValueType type)
+ {
+ switch (type)
+ {
+ case ExampleValueType.Value:
+ return GetDefaultValueAsStringForSchemaType(schema);
+
+ default:
+ return "*";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs
new file mode 100644
index 00000000..c4d43ee9
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserExampleValues.cs
@@ -0,0 +1,30 @@
+using System;
+
+namespace WireMock.Net.OpenApiParser.Settings
+{
+ ///
+ /// A class defining the example values to use for the different types.
+ ///
+ public class WireMockOpenApiParserExampleValues
+ {
+#pragma warning disable 1591
+ public bool Boolean { get; set; } = true;
+
+ public int Integer { get; set; } = 42;
+
+ public float Float { get; set; } = 4.2f;
+
+ public double Double { get; set; } = 4.2d;
+
+ public Func Date { get; set; } = () => System.DateTime.UtcNow.Date;
+
+ public Func DateTime { get; set; } = () => System.DateTime.UtcNow;
+
+ public byte[] Bytes { get; set; } = { 48, 49, 50 };
+
+ public object Object { get; set; } = "example-object";
+
+ public string String { get; set; } = "example-string";
+#pragma warning restore 1591
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserSettings.cs b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserSettings.cs
new file mode 100644
index 00000000..f02c9813
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser/Settings/WireMockOpenApiParserSettings.cs
@@ -0,0 +1,30 @@
+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 values to use
+ ///
+ public WireMockOpenApiParserExampleValues ExampleValues { get; } = new WireMockOpenApiParserExampleValues();
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs b/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs
new file mode 100644
index 00000000..698a114d
--- /dev/null
+++ b/src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs
@@ -0,0 +1,18 @@
+namespace WireMock.Net.OpenApiParser.Types
+{
+ ///
+ /// The example value to use
+ ///
+ public enum ExampleValueType
+ {
+ ///
+ /// Use a generated example value based on the SchemaType (default).
+ ///
+ Value,
+
+ ///
+ /// Just use a Wildcard (*) character.
+ ///
+ Wildcard
+ }
+}
\ 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 87f1b25b..1e03dc06 100644
--- a/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs
+++ b/src/WireMock.Net.OpenApiParser/Utils/ExampleValueGenerator.cs
@@ -1,49 +1,57 @@
using System;
using Microsoft.OpenApi.Models;
using WireMock.Net.OpenApiParser.Extensions;
+using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Utils
{
- internal static class ExampleValueGenerator
+ internal class ExampleValueGenerator
{
- public static object GetExampleValue(OpenApiSchema schema)
+ private readonly WireMockOpenApiParserSettings _settings;
+
+ public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
+ {
+ _settings = settings ?? throw new ArgumentNullException(nameof(settings));
+ }
+
+ public object GetExampleValue(OpenApiSchema schema)
{
switch (schema?.GetSchemaType())
{
case SchemaType.Boolean:
- return true;
+ return _settings.ExampleValues.Boolean;
case SchemaType.Integer:
- return 42;
+ return _settings.ExampleValues.Integer;
case SchemaType.Number:
switch (schema?.GetSchemaFormat())
{
case SchemaFormat.Float:
- return 4.2f;
+ return _settings.ExampleValues.Float;
default:
- return 4.2d;
+ return _settings.ExampleValues.Double;
}
default:
switch (schema?.GetSchemaFormat())
{
case SchemaFormat.Date:
- return DateTimeUtils.ToRfc3339Date(DateTime.UtcNow);
+ return DateTimeUtils.ToRfc3339Date(_settings.ExampleValues.Date());
case SchemaFormat.DateTime:
- return DateTimeUtils.ToRfc3339DateTime(DateTime.UtcNow);
+ return DateTimeUtils.ToRfc3339DateTime(_settings.ExampleValues.DateTime());
case SchemaFormat.Byte:
- return new byte[] { 48, 49, 50 };
+ return _settings.ExampleValues.Bytes;
case SchemaFormat.Binary:
- return "example-object";
+ return _settings.ExampleValues.Object;
default:
- return "example-string";
+ return _settings.ExampleValues.String;
}
}
}
diff --git a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj
index e3d507ba..45f88755 100644
--- a/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj
+++ b/src/WireMock.Net.OpenApiParser/WireMock.Net.OpenApiParser.csproj
@@ -12,6 +12,7 @@
../WireMock.Net/WireMock.Net.snk
true
MIT
+ 8.0
@@ -24,14 +25,11 @@
+
-
-
-
-
\ No newline at end of file
diff --git a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs
index 44fed945..d84672a8 100644
--- a/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs
+++ b/src/WireMock.Net.OpenApiParser/WireMockOpenApiParser.cs
@@ -1,34 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using JetBrains.Annotations;
-using Microsoft.OpenApi;
-using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers;
-using Microsoft.OpenApi.Writers;
-using Newtonsoft.Json.Linq;
using RamlToOpenApiConverter;
using WireMock.Admin.Mappings;
-using WireMock.Net.OpenApiParser.Extensions;
-using WireMock.Net.OpenApiParser.Types;
-using WireMock.Net.OpenApiParser.Utils;
+using WireMock.Net.OpenApiParser.Mappers;
+using WireMock.Net.OpenApiParser.Settings;
namespace WireMock.Net.OpenApiParser
{
///
- /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
+ /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
///
public class WireMockOpenApiParser : IWireMockOpenApiParser
{
- private const int ArrayItems = 3;
-
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
- ///
+ ///
[PublicAPI]
public IEnumerable FromFile(string path, out OpenApiDiagnostic diagnostic)
+ {
+ return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
+ }
+
+ ///
+ [PublicAPI]
+ public IEnumerable FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{
OpenApiDocument document;
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
@@ -42,248 +41,28 @@ namespace WireMock.Net.OpenApiParser
document = reader.Read(File.OpenRead(path), out diagnostic);
}
- return FromDocument(document);
+ return FromDocument(document, settings);
}
- ///
+ ///
[PublicAPI]
public IEnumerable FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
{
return FromDocument(_reader.Read(stream, out diagnostic));
}
- ///
+ ///
[PublicAPI]
- public IEnumerable FromDocument(OpenApiDocument openApiDocument)
+ public IEnumerable FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{
- return MapPaths(openApiDocument.Paths);
+ return FromDocument(_reader.Read(stream, out diagnostic), settings);
}
- private static IEnumerable MapPaths(OpenApiPaths paths)
+ ///
+ [PublicAPI]
+ public IEnumerable FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null)
{
- return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
- }
-
- private static IEnumerable MapPath(string path, OpenApiPathItem pathItem)
- {
- return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value));
- }
-
- private static MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation)
- {
- var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
- var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
- var response = operation.Responses.FirstOrDefault();
- TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType);
- var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
- var responseExample = responseContent?.Example;
-
- var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) : MapSchemaToObject(responseSchema);
-
- if (int.TryParse(response.Key, out var httpStatusCode))
- {
- httpStatusCode = 200;
- }
-
- return new MappingModel
- {
- Guid = Guid.NewGuid(),
- Request = new RequestModel
- {
- Methods = new[] { httpMethod },
- Path = MapPathWithParameters(path, pathParameters),
- Params = MapQueryParameters(queryParameters)
- },
- Response = new ResponseModel
- {
- StatusCode = httpStatusCode,
- Headers = MapHeaders(responseContentType, response.Value?.Headers),
- BodyAsJson = body
- }
- };
- }
-
- private static bool TryGetContent(IDictionary contents, out OpenApiMediaType openApiMediaType, 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 static object MapSchemaToObject(OpenApiSchema schema, string name = null)
- {
- if (schema == null)
- {
- return null;
- }
-
- switch (schema.GetSchemaType())
- {
- case SchemaType.Array:
- var jArray = new JArray();
- for (int i = 0; i < ArrayItems; i++)
- {
- if (schema.Items.Properties.Count > 0)
- {
- var arrayItem = new JObject();
- foreach (var property in schema.Items.Properties)
- {
- var objectValue = MapSchemaToObject(property.Value, property.Key);
- if (objectValue is JProperty jp)
- {
- arrayItem.Add(jp);
- }
- else
- {
- arrayItem.Add(new JProperty(property.Key, objectValue));
- }
- }
-
- jArray.Add(arrayItem);
- }
- else
- {
- jArray.Add(MapSchemaToObject(schema.Items, name));
- }
- }
-
- return jArray;
-
- case SchemaType.Boolean:
- case SchemaType.Integer:
- case SchemaType.Number:
- case SchemaType.String:
- return ExampleValueGenerator.GetExampleValue(schema);
-
- case SchemaType.Object:
- var propertyAsJObject = new JObject();
- foreach (var schemaProperty in schema.Properties)
- {
- string propertyName = schemaProperty.Key;
- var openApiSchema = schemaProperty.Value;
- if (openApiSchema.GetSchemaType() == SchemaType.Object)
- {
- var mapped = MapSchemaToObject(schemaProperty.Value, schemaProperty.Key);
- if (mapped is JProperty jp)
- {
- propertyAsJObject.Add(jp);
- }
- }
- else
- {
- bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
-
- propertyAsJObject.Add(new JProperty(propertyName, ExampleValueGenerator.GetExampleValue(openApiSchema)));
- }
- }
-
- return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
-
- default:
- return null;
- }
- }
-
- private static string MapPathWithParameters(string path, IEnumerable parameters)
- {
- if (parameters == null)
- {
- return path;
- }
-
- string newPath = path;
- foreach (var parameter in parameters)
- {
- newPath = newPath.Replace($"{{{parameter.Name}}}", ExampleValueGenerator.GetExampleValue(parameter.Schema).ToString());
- }
-
- return newPath;
- }
-
- private static JToken MapOpenApiAnyToJToken(IOpenApiAny any)
- {
- if (any == null)
- {
- return null;
- }
-
- using (var outputString = new StringWriter())
- {
- var writer = new OpenApiJsonWriter(outputString);
- any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
-
- return JObject.Parse(outputString.ToString());
- }
- }
-
- private static IDictionary MapHeaders(string responseContentType, IDictionary headers)
- {
- var mappedHeaders = headers.ToDictionary(item => item.Key, item => ExampleValueGenerator.GetExampleValue(null));
- if (!string.IsNullOrEmpty(responseContentType))
- {
- if (!mappedHeaders.ContainsKey("Content-Type"))
- {
- mappedHeaders.Add("Content-Type", responseContentType);
- }
- else
- {
- mappedHeaders["Content-Type"] = responseContentType;
- }
- }
-
- return mappedHeaders.Keys.Any() ? mappedHeaders : null;
- }
-
- private static IList MapQueryParameters(IEnumerable queryParameters)
- {
- var list = queryParameters
- .Select(qp => new ParamModel
- {
- Name = qp.Name,
- Matchers = new[]
- {
- new MatcherModel
- {
- Name = "ExactMatcher",
- Pattern = GetDefaultValueAsStringForSchemaType(qp.Schema)
- }
- }
- })
- .ToList();
-
- return list.Any() ? list : null;
- }
-
- private static string GetDefaultValueAsStringForSchemaType(OpenApiSchema schema)
- {
- var value = ExampleValueGenerator.GetExampleValue(schema);
-
- switch (value)
- {
- case string valueAsString:
- return valueAsString;
-
- default:
- return value.ToString();
- }
+ return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths);
}
}
}
\ No newline at end of file