Add support to use 'mapping' object in in reponse templating (#798)

* mapping

* .

* .
This commit is contained in:
Stef Heyenrath
2022-09-03 08:52:05 +02:00
committed by GitHub
parent 862c04e722
commit 74480c8ba9
37 changed files with 2114 additions and 2055 deletions

View File

@@ -25,9 +25,10 @@ internal class WebhookSender
_settings = Guard.NotNull(settings);
}
public Task<HttpResponseMessage> SendAsync(HttpClient client, IWebhookRequest request, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage)
public Task<HttpResponseMessage> SendAsync(HttpClient client, IMapping mapping, IWebhookRequest request, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage)
{
Guard.NotNull(client);
Guard.NotNull(mapping);
Guard.NotNull(request);
Guard.NotNull(originalRequestMessage);
Guard.NotNull(originalResponseMessage);
@@ -54,7 +55,7 @@ internal class WebhookSender
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
}
(bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers, request.TransformerReplaceNodeOptions);
(bodyData, headers) = responseMessageTransformer.Transform(mapping, originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers, request.TransformerReplaceNodeOptions);
}
else
{

View File

@@ -15,14 +15,14 @@ public class Mapping : IMapping
/// <inheritdoc />
public Guid Guid { get; }
/// <inheritdoc />
public string? Title { get; }
/// <inheritdoc />
public string? Title { get; }
/// <inheritdoc />
public string? Description { get; }
/// <inheritdoc />
public string? Description { get; }
/// <inheritdoc />
public string? Path { get; set; }
/// <inheritdoc />
public string? Path { get; set; }
/// <inheritdoc />
public int Priority { get; }
@@ -115,16 +115,16 @@ public class Mapping : IMapping
TimeSettings = timeSettings;
}
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
{
return Provider.ProvideResponseAsync(requestMessage, Settings);
}
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
{
return Provider.ProvideResponseAsync(this, requestMessage, Settings);
}
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
{
var result = new RequestMatchResult();
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
{
var result = new RequestMatchResult();
RequestMatcher.GetMatchingScore(requestMessage, result);

View File

@@ -4,12 +4,12 @@ using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Util;
using WireMock.Types;
#if !USE_ASPNETCORE
using Owin;
#else
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
using WireMock.Types;
#endif
namespace WireMock.Owin;

View File

@@ -26,7 +26,7 @@ namespace WireMock.Owin
{
internal class WireMockMiddleware : OwinMiddleware
{
private readonly object _lock = new object();
private readonly object _lock = new();
private static readonly Task CompletedTask = Task.FromResult(false);
private readonly IWireMockMiddlewareOptions _options;
private readonly IOwinRequestMapper _requestMapper;
@@ -108,10 +108,10 @@ namespace WireMock.Owin
logRequest = targetMapping.LogMapping;
if (targetMapping.IsAdminInterface && _options.AuthenticationMatcher != null)
if (targetMapping.IsAdminInterface && _options.AuthenticationMatcher != null && request.Headers != null)
{
bool present = request.Headers.TryGetValue(HttpKnownHeaderNames.Authorization, out WireMockList<string> authorization);
if (!present || _options.AuthenticationMatcher.IsMatch(authorization.ToString()) < MatchScores.Perfect)
if (!present || _options.AuthenticationMatcher.IsMatch(authorization!.ToString()) < MatchScores.Perfect)
{
_options.Logger.Error("HttpStatusCode set to 401");
response = ResponseMessageBuilder.Create(null, HttpStatusCode.Unauthorized);
@@ -208,7 +208,7 @@ namespace WireMock.Owin
try
{
await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhooks[index].Request, request, response).ConfigureAwait(false);
await webhookSender.SendAsync(httpClientForWebhook, mapping, mapping.Webhooks[index].Request, request, response).ConfigureAwait(false);
}
catch (Exception ex)
{

View File

@@ -244,7 +244,7 @@ public partial class Response : IResponseBuilder
}
/// <inheritdoc />
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings)
{
Guard.NotNull(requestMessage);
Guard.NotNull(settings);
@@ -327,7 +327,7 @@ public partial class Response : IResponseBuilder
throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported.");
}
return (responseMessageTransformer.Transform(requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
return (responseMessageTransformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
}
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)

View File

@@ -13,7 +13,7 @@ internal class DynamicAsyncResponseProvider : IResponseProvider
_responseMessageFunc = responseMessageFunc;
}
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings)
{
return (await _responseMessageFunc(requestMessage).ConfigureAwait(false), null);
}

View File

@@ -13,7 +13,7 @@ internal class DynamicResponseProvider : IResponseProvider
_responseMessageFunc = responseMessageFunc;
}
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings)
{
(IResponseMessage responseMessage, IMapping? mapping) result = (_responseMessageFunc(requestMessage), null);
return Task.FromResult(result);

View File

@@ -13,8 +13,9 @@ public interface IResponseProvider
/// <summary>
/// The provide response.
/// </summary>
/// <param name="mapping">The used mapping.</param>
/// <param name="requestMessage">The request.</param>
/// <param name="settings">The WireMockServerSettings.</param>
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings);
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings);
}

View File

@@ -15,7 +15,7 @@ internal class ProxyAsyncResponseProvider : IResponseProvider
_settings = settings;
}
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings)
{
return (await _responseMessageFunc(requestMessage, _settings).ConfigureAwait(false), null);
}

View File

@@ -1,33 +1,32 @@
namespace WireMock
namespace WireMock;
/// <summary>
/// The ScenarioState
/// </summary>
public class ScenarioState
{
/// <summary>
/// The ScenarioState
/// Gets or sets the Name (from the Scenario).
/// </summary>
public class ScenarioState
{
/// <summary>
/// Gets or sets the Name (from the Scenario).
/// </summary>
public string Name { get; set; }
public string? Name { get; set; }
/// <summary>
/// Gets or sets the NextState.
/// </summary>
public string NextState { get; set; }
/// <summary>
/// Gets or sets the NextState.
/// </summary>
public string? NextState { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ScenarioState"/> is started.
/// </summary>
public bool Started { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ScenarioState"/> is started.
/// </summary>
public bool Started { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ScenarioState"/> is finished.
/// </summary>
public bool Finished { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this <see cref="ScenarioState"/> is finished.
/// </summary>
public bool Finished { get; set; }
/// <summary>
/// Gets or sets the state counter.
/// </summary>
public int Counter { get; set; }
}
/// <summary>
/// Gets or sets the state counter.
/// </summary>
public int Counter { get; set; }
}

View File

@@ -21,7 +21,7 @@ public static class WireMockServerSettingsParser
[PublicAPI]
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
{
Guard.HasNoNulls(args, nameof(args));
Guard.HasNoNulls(args);
var parser = new SimpleCommandLineParser();
parser.Parse(args);
@@ -68,8 +68,7 @@ public static class WireMockServerSettingsParser
{
settings.Logger = logger;
}
if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
else if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
{
settings.Logger = new WireMockConsoleLogger();
}

View File

@@ -6,7 +6,7 @@ namespace WireMock.Transformers;
interface ITransformer
{
ResponseMessage Transform(IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options);
ResponseMessage Transform(IMapping mapping, IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options);
(IBodyData? BodyData, IDictionary<string, WireMockList<string>>? Headers) Transform(IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, IDictionary<string, WireMockList<string>>? headers, ReplaceNodeOptions options);
(IBodyData? BodyData, IDictionary<string, WireMockList<string>>? Headers) Transform(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, IDictionary<string, WireMockList<string>>? headers, ReplaceNodeOptions options);
}

View File

@@ -19,6 +19,7 @@ internal class Transformer : ITransformer
}
public (IBodyData? BodyData, IDictionary<string, WireMockList<string>>? Headers) Transform(
IMapping mapping,
IRequestMessage originalRequestMessage,
IResponseMessage originalResponseMessage,
IBodyData? bodyData,
@@ -29,6 +30,7 @@ internal class Transformer : ITransformer
var model = new
{
mapping,
request = originalRequestMessage,
response = originalResponseMessage
};
@@ -42,7 +44,7 @@ internal class Transformer : ITransformer
return (newBodyData, TransformHeaders(transformerContext, model, headers));
}
public ResponseMessage Transform(IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options)
public ResponseMessage Transform(IMapping mapping, IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options)
{
var transformerContext = _factory.Create();
@@ -50,6 +52,7 @@ internal class Transformer : ITransformer
var model = new
{
mapping,
request = requestMessage
};
@@ -82,22 +85,15 @@ internal class Transformer : ITransformer
return responseMessage;
}
private static IBodyData TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, object model, IBodyData original, bool useTransformerForBodyAsFile)
private static IBodyData? TransformBodyData(ITransformerContext transformerContext, ReplaceNodeOptions options, object model, IBodyData original, bool useTransformerForBodyAsFile)
{
switch (original?.DetectedBodyType)
return original.DetectedBodyType switch
{
case BodyType.Json:
return TransformBodyAsJson(transformerContext, options, model, original);
case BodyType.File:
return TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile);
case BodyType.String:
return TransformBodyAsString(transformerContext, model, original);
default:
return null;
}
BodyType.Json => TransformBodyAsJson(transformerContext, options, model, original),
BodyType.File => TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile),
BodyType.String => TransformBodyAsString(transformerContext, model, original),
_ => null
};
}
private static IDictionary<string, WireMockList<string>> TransformHeaders(ITransformerContext transformerContext, object model, IDictionary<string, WireMockList<string>>? original)
@@ -166,11 +162,17 @@ internal class Transformer : ITransformer
{
const string property = "_";
JObject dummy = JObject.Parse($"{{ \"{property}\": null }}");
JToken node = dummy[property];
if (dummy[property] == null)
{
// TODO: check if just returning null is fine
return string.Empty;
}
JToken node = dummy[property]!;
ReplaceNodeValue(options, node, transformedString);
return dummy[property];
return dummy[property]!;
}
return stringValue;