mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-30 14:22:20 +02:00
Add possibility to use settings to generate MappingModel models with wildcard path parameters. (#609)
* Add optional settings for WithMappingFromOpenApi * . * . * cleanup
This commit is contained in:
@@ -13,6 +13,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Raml/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
|
||||||
</wpf:ResourceDictionary>
|
</wpf:ResourceDictionary>
|
||||||
@@ -3,9 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
|
using WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Net.OpenApiParser.Extensions;
|
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.ConsoleApp
|
namespace WireMock.Net.OpenApiParser.ConsoleApp
|
||||||
{
|
{
|
||||||
@@ -23,13 +25,18 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
|
|||||||
ReadStaticMappings = false,
|
ReadStaticMappings = false,
|
||||||
WatchStaticMappings = false,
|
WatchStaticMappings = false,
|
||||||
WatchStaticMappingsInSubdirectories = false,
|
WatchStaticMappingsInSubdirectories = false,
|
||||||
Logger = new WireMockConsoleLogger(),
|
Logger = new WireMockConsoleLogger()
|
||||||
});
|
});
|
||||||
Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
|
Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
|
||||||
|
|
||||||
server.SetBasicAuthentication("a", "b");
|
server.SetBasicAuthentication("a", "b");
|
||||||
|
|
||||||
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");
|
Console.WriteLine("Press any key to stop the server");
|
||||||
System.Console.ReadKey();
|
System.Console.ReadKey();
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
using System;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices.ComTypes;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Microsoft.OpenApi.Readers;
|
using Microsoft.OpenApi.Readers;
|
||||||
|
using SharpYaml.Model;
|
||||||
|
using Stef.Validation;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Extensions
|
namespace WireMock.Net.OpenApiParser.Extensions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Some extension methods for <see cref="IWireMockServer"/>.
|
||||||
|
/// </summary>
|
||||||
public static class WireMockServerExtensions
|
public static class WireMockServerExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -15,18 +22,26 @@ namespace WireMock.Net.OpenApiParser.Extensions
|
|||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
|
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
if (server == null)
|
return WithMappingFromOpenApiFile(server, path, null, out diagnostic);
|
||||||
{
|
}
|
||||||
throw new ArgumentNullException(nameof(server));
|
|
||||||
}
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
var mappings = new WireMockOpenApiParser().FromFile(path, out diagnostic);
|
/// <summary>
|
||||||
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
|
/// <param name="settings">Additional settings</param>
|
||||||
|
[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());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
@@ -37,9 +52,27 @@ namespace WireMock.Net.OpenApiParser.Extensions
|
|||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
|
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
||||||
|
/// <param name="settings">Additional settings</param>
|
||||||
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
|
[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());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
@@ -49,9 +82,14 @@ namespace WireMock.Net.OpenApiParser.Extensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
||||||
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document)
|
/// <param name="settings">Additional settings [optional]</param>
|
||||||
|
[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());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,37 +1,57 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Microsoft.OpenApi.Readers;
|
using Microsoft.OpenApi.Readers;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
namespace WireMock.Net.OpenApiParser
|
|
||||||
{
|
namespace WireMock.Net.OpenApiParser
|
||||||
/// <summary>
|
{
|
||||||
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
|
/// <summary>
|
||||||
/// </summary>
|
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
|
||||||
public interface IWireMockOpenApiParser
|
/// </summary>
|
||||||
{
|
public interface IWireMockOpenApiParser
|
||||||
/// <summary>
|
{
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="document">The source OpenApiDocument</param>
|
|
||||||
/// <returns>MappingModel</returns>
|
|
||||||
IEnumerable<MappingModel> FromDocument(OpenApiDocument document);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">The source stream</param>
|
|
||||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
|
||||||
/// <returns>MappingModel</returns>
|
|
||||||
IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
|
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
||||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic);
|
IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic);
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
||||||
|
/// <param name="settings">Additional settings</param>
|
||||||
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
|
/// <returns>MappingModel</returns>
|
||||||
|
IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate <see cref="IEnumerable{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="document">The source OpenApiDocument</param>
|
||||||
|
/// <param name="settings">Additional settings [optional]</param>
|
||||||
|
/// <returns>MappingModel</returns>
|
||||||
|
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings settings = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The source stream</param>
|
||||||
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
|
/// <returns>MappingModel</returns>
|
||||||
|
IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The source stream</param>
|
||||||
|
/// <param name="settings">Additional settings</param>
|
||||||
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
|
/// <returns>MappingModel</returns>
|
||||||
|
IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
276
src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs
Normal file
276
src/WireMock.Net.OpenApiParser/Mappers/OpenApiPathsMapper.cs
Normal file
@@ -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<MappingModel> ToMappingModels(OpenApiPaths paths)
|
||||||
|
{
|
||||||
|
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths)
|
||||||
|
{
|
||||||
|
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<MappingModel> 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<string, OpenApiMediaType> 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<OpenApiParameter> 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<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> 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<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> 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 "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WireMock.Net.OpenApiParser.Settings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A class defining the example values to use for the different types.
|
||||||
|
/// </summary>
|
||||||
|
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<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
|
||||||
|
|
||||||
|
public Func<DateTime> 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
namespace WireMock.Net.OpenApiParser.Settings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The WireMockOpenApiParser Settings
|
||||||
|
/// </summary>
|
||||||
|
public class WireMockOpenApiParserSettings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The number of array items to generate (default is 3).
|
||||||
|
/// </summary>
|
||||||
|
public int NumberOfArrayItems { get; set; } = 3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The example value type to use when generating a Path
|
||||||
|
/// </summary>
|
||||||
|
public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The example value type to use when generating a Header
|
||||||
|
/// </summary>
|
||||||
|
public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The example values to use
|
||||||
|
/// </summary>
|
||||||
|
public WireMockOpenApiParserExampleValues ExampleValues { get; } = new WireMockOpenApiParserExampleValues();
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs
Normal file
18
src/WireMock.Net.OpenApiParser/Types/ExampleValueType.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace WireMock.Net.OpenApiParser.Types
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The example value to use
|
||||||
|
/// </summary>
|
||||||
|
public enum ExampleValueType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Use a generated example value based on the SchemaType (default).
|
||||||
|
/// </summary>
|
||||||
|
Value,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Just use a Wildcard (*) character.
|
||||||
|
/// </summary>
|
||||||
|
Wildcard
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +1,57 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using WireMock.Net.OpenApiParser.Extensions;
|
using WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Utils
|
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())
|
switch (schema?.GetSchemaType())
|
||||||
{
|
{
|
||||||
case SchemaType.Boolean:
|
case SchemaType.Boolean:
|
||||||
return true;
|
return _settings.ExampleValues.Boolean;
|
||||||
|
|
||||||
case SchemaType.Integer:
|
case SchemaType.Integer:
|
||||||
return 42;
|
return _settings.ExampleValues.Integer;
|
||||||
|
|
||||||
case SchemaType.Number:
|
case SchemaType.Number:
|
||||||
switch (schema?.GetSchemaFormat())
|
switch (schema?.GetSchemaFormat())
|
||||||
{
|
{
|
||||||
case SchemaFormat.Float:
|
case SchemaFormat.Float:
|
||||||
return 4.2f;
|
return _settings.ExampleValues.Float;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 4.2d;
|
return _settings.ExampleValues.Double;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
switch (schema?.GetSchemaFormat())
|
switch (schema?.GetSchemaFormat())
|
||||||
{
|
{
|
||||||
case SchemaFormat.Date:
|
case SchemaFormat.Date:
|
||||||
return DateTimeUtils.ToRfc3339Date(DateTime.UtcNow);
|
return DateTimeUtils.ToRfc3339Date(_settings.ExampleValues.Date());
|
||||||
|
|
||||||
case SchemaFormat.DateTime:
|
case SchemaFormat.DateTime:
|
||||||
return DateTimeUtils.ToRfc3339DateTime(DateTime.UtcNow);
|
return DateTimeUtils.ToRfc3339DateTime(_settings.ExampleValues.DateTime());
|
||||||
|
|
||||||
case SchemaFormat.Byte:
|
case SchemaFormat.Byte:
|
||||||
return new byte[] { 48, 49, 50 };
|
return _settings.ExampleValues.Bytes;
|
||||||
|
|
||||||
case SchemaFormat.Binary:
|
case SchemaFormat.Binary:
|
||||||
return "example-object";
|
return _settings.ExampleValues.Object;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return "example-string";
|
return _settings.ExampleValues.String;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<LangVersion>8.0</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
@@ -24,14 +25,11 @@
|
|||||||
<PackageReference Include="RamlToOpenApiConverter" Version="0.1.1" />
|
<PackageReference Include="RamlToOpenApiConverter" Version="0.1.1" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
|
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||||
|
<PackageReference Include="Stef.Validation" Version="0.0.3" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Options\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,34 +1,33 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.OpenApi;
|
|
||||||
using Microsoft.OpenApi.Any;
|
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Microsoft.OpenApi.Readers;
|
using Microsoft.OpenApi.Readers;
|
||||||
using Microsoft.OpenApi.Writers;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using RamlToOpenApiConverter;
|
using RamlToOpenApiConverter;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Net.OpenApiParser.Extensions;
|
using WireMock.Net.OpenApiParser.Mappers;
|
||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
using WireMock.Net.OpenApiParser.Utils;
|
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser
|
namespace WireMock.Net.OpenApiParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
|
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
||||||
{
|
{
|
||||||
private const int ArrayItems = 3;
|
|
||||||
|
|
||||||
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||||
|
[PublicAPI]
|
||||||
|
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
OpenApiDocument document;
|
OpenApiDocument document;
|
||||||
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -42,248 +41,28 @@ namespace WireMock.Net.OpenApiParser
|
|||||||
document = reader.Read(File.OpenRead(path), out diagnostic);
|
document = reader.Read(File.OpenRead(path), out diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FromDocument(document);
|
return FromDocument(document, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return FromDocument(_reader.Read(stream, out diagnostic));
|
return FromDocument(_reader.Read(stream, out diagnostic));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument)
|
public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return MapPaths(openApiDocument.Paths);
|
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<MappingModel> MapPaths(OpenApiPaths paths)
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
||||||
|
[PublicAPI]
|
||||||
|
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null)
|
||||||
{
|
{
|
||||||
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
|
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths);
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<MappingModel> 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<string, OpenApiMediaType> 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<OpenApiParameter> 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<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> 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<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user