mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-08-13 23:22:02 +02:00
* Upgrade Microsoft.OpenApi to 3.7.0 and YamlDotNet to 18.1.0 * fix
388 lines
13 KiB
C#
388 lines
13 KiB
C#
// Copyright © WireMock.Net
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Nodes;
|
|
using Microsoft.OpenApi;
|
|
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(WireMockOpenApiParserSettings settings)
|
|
{
|
|
private const string HeaderContentType = "Content-Type";
|
|
|
|
private readonly WireMockOpenApiParserSettings _settings = Guard.NotNull(settings);
|
|
private readonly ExampleValueGenerator _exampleValueGenerator = new(settings);
|
|
|
|
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
|
{
|
|
return paths?
|
|
.OrderBy(p => p.Key)
|
|
.Select(p => MapPath(p.Key, p.Value, servers))
|
|
.SelectMany(x => x)
|
|
.ToArray() ?? [];
|
|
}
|
|
|
|
private MappingModel[] MapPath(string path, IOpenApiPathItem pathItem, IList<OpenApiServer> 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<OpenApiServer> servers)
|
|
{
|
|
var queryParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Query) ?? [];
|
|
var pathParameters = operation.Parameters?.Where(p => p.In == ParameterLocation.Path) ?? [];
|
|
var requestHeaders = operation.Parameters?.Where(p => p.In == ParameterLocation.Header) ?? [];
|
|
|
|
return new MappingModel
|
|
{
|
|
Guid = Guid.NewGuid(),
|
|
Request = new RequestModel
|
|
{
|
|
Methods = [httpMethod],
|
|
Path = PathUtils.Combine(MapBasePath(servers), MapPathWithParameters(path, pathParameters)),
|
|
Params = MapQueryParameters(queryParameters),
|
|
Headers = MapRequestHeaders(requestHeaders),
|
|
Body = GetRequestBodyModel(operation.RequestBody)
|
|
},
|
|
Response = GetResponseModel(operation.Responses?.FirstOrDefault())
|
|
};
|
|
}
|
|
|
|
private BodyModel GetRequestBodyModel(IOpenApiRequestBody? openApiRequestBody)
|
|
{
|
|
if (openApiRequestBody is not { Content: not null, Required: true })
|
|
{
|
|
return new BodyModel();
|
|
}
|
|
|
|
var content = openApiRequestBody.Content;
|
|
|
|
TryGetContent(content, out var requestContent, out _);
|
|
|
|
var requestExample = requestContent?.Example;
|
|
var requestExamples = requestContent?.Examples;
|
|
var requestSchemaExample = requestContent?.Schema?.Example;
|
|
var requestSchemaExamples = requestContent?.Schema?.Examples;
|
|
|
|
JsonNode? request;
|
|
if (requestExample != null)
|
|
{
|
|
request = requestExample;
|
|
}
|
|
else if (requestSchemaExample != null)
|
|
{
|
|
request = requestSchemaExample;
|
|
}
|
|
else if (requestExamples != null)
|
|
{
|
|
request = requestExamples.FirstOrDefault().Value.Value;
|
|
}
|
|
else if (requestSchemaExamples != null)
|
|
{
|
|
request = requestSchemaExamples.FirstOrDefault();
|
|
}
|
|
else
|
|
{
|
|
var requestSchema = content?.FirstOrDefault().Value.Schema;
|
|
request = MapSchemaToObject(requestSchema);
|
|
}
|
|
|
|
return MapRequestBody(request) ?? new BodyModel();
|
|
}
|
|
|
|
private ResponseModel GetResponseModel(KeyValuePair<string, IOpenApiResponse>? openApiResponse)
|
|
{
|
|
var content = openApiResponse?.Value.Content;
|
|
|
|
TryGetContent(content, out var responseContent, out var contentType);
|
|
|
|
var responseExample = responseContent?.Example;
|
|
var responseExamples = responseContent?.Examples;
|
|
var responseSchemaExample = responseContent?.Schema?.Example;
|
|
var responseSchemaExamples = responseContent?.Schema?.Examples;
|
|
|
|
JsonNode? response;
|
|
if (responseExample != null)
|
|
{
|
|
response = responseExample;
|
|
}
|
|
else if (responseSchemaExample != null)
|
|
{
|
|
response = responseSchemaExample;
|
|
}
|
|
else if (responseExamples != null)
|
|
{
|
|
response = responseExamples.FirstOrDefault().Value.Value;
|
|
}
|
|
else if (responseSchemaExamples != null)
|
|
{
|
|
response = responseSchemaExamples.FirstOrDefault();
|
|
}
|
|
else
|
|
{
|
|
var responseSchema = content?.FirstOrDefault().Value?.Schema;
|
|
response = MapSchemaToObject(responseSchema);
|
|
}
|
|
|
|
return new ResponseModel
|
|
{
|
|
StatusCode = int.TryParse(openApiResponse?.Key, out var httpStatusCode) ? httpStatusCode : 200,
|
|
Headers = MapHeaders(contentType, openApiResponse?.Value.Headers),
|
|
BodyAsJson = response != null ? JsonConvert.DeserializeObject(SystemTextJsonSerializer.Serialize(response)) : 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<string, IOpenApiMediaType>? contents, [NotNullWhen(true)] out IOpenApiMediaType? 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<string, IOpenApiSchema>())
|
|
{
|
|
propertyAsJsonObject[schemaProperty.Key] = MapPropertyAsJsonNode(schemaProperty.Value);
|
|
}
|
|
|
|
if (schema.AllOf?.Count > 0)
|
|
{
|
|
foreach (var group in schema.AllOf.SelectMany(p => p.Properties ?? new Dictionary<string, IOpenApiSchema>()).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<string, IOpenApiSchema>())
|
|
{
|
|
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<IOpenApiParameter>? 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 Dictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, IOpenApiHeader>? headers)
|
|
{
|
|
var mappedHeaders = headers?
|
|
.ToDictionary(item => item.Key, _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!) ?? [];
|
|
|
|
if (responseContentType != null)
|
|
{
|
|
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
|
}
|
|
|
|
return mappedHeaders.Count > 0 ? mappedHeaders : null;
|
|
}
|
|
|
|
private IList<ParamModel>? MapQueryParameters(IEnumerable<IOpenApiParameter> 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<HeaderModel>? MapRequestHeaders(IEnumerable<IOpenApiParameter> 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<string>();
|
|
}
|
|
|
|
return value.ToString();
|
|
}
|
|
|
|
private static string MapBasePath(IList<OpenApiServer>? 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;
|
|
}
|
|
} |