mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-25 17:58:33 +02:00
WebHook : UseFireAndForget + Delay (#803)
* UseFireAndForget * ... * delay * async * updated code accorsing to proposal * Change nuget to package reference for WireMock.Net.Console.Net472.Classic, move the new FireAndForget into the main mapping, out of individual webhook mappings making it all or nothing, update tests, change Middleware to await or not the firing of all webhooks. Update models as needed. (#804) Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com> * small update * Tweak middleware and fix bug in example (#806) Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com> * .ConfigureAwait(false) Co-authored-by: mattisking <mattisking@gmail.com> Co-authored-by: Matt Philmon <Matt_Philmon@carmax.com>
This commit is contained in:
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Models;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
@@ -576,6 +577,40 @@ namespace WireMock.Net.ConsoleApplication
|
||||
};
|
||||
}));
|
||||
|
||||
server.Given(Request.Create().WithPath(new WildcardMatcher("/multi-webhook", true)).UsingPost())
|
||||
.WithWebhook(new[] {
|
||||
new Webhook()
|
||||
{
|
||||
Request = new WebhookRequest
|
||||
{
|
||||
Url = "http://localhost:12345/foo1",
|
||||
Method = "post",
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "OK 1!", DetectedBodyType = BodyType.String
|
||||
},
|
||||
Delay = 1000
|
||||
}
|
||||
},
|
||||
new Webhook()
|
||||
{
|
||||
Request = new WebhookRequest
|
||||
{
|
||||
Url = "http://localhost:12345/foo2",
|
||||
Method = "post",
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "OK 2!",
|
||||
DetectedBodyType = BodyType.String
|
||||
},
|
||||
MinimumRandomDelay = 3000,
|
||||
MaximumRandomDelay = 7000
|
||||
}
|
||||
}
|
||||
})
|
||||
.WithWebhookFireAndForget(true)
|
||||
.RespondWith(Response.Create().WithBody("a-response"));
|
||||
|
||||
System.Console.WriteLine(JsonConvert.SerializeObject(server.MappingModels, Formatting.Indented));
|
||||
|
||||
System.Console.WriteLine("Press any key to stop the server");
|
||||
|
||||
@@ -342,15 +342,6 @@
|
||||
<Reference Include="TinyMapper, Version=3.0.1.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\TinyMapper.3.0.3\lib\net40\TinyMapper.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WireMock.Net, Version=1.5.3.0, Culture=neutral, PublicKeyToken=c8d65537854e1f03, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WireMock.Net.1.5.3\lib\net461\WireMock.Net.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WireMock.Net.Abstractions, Version=1.5.3.0, Culture=neutral, PublicKeyToken=c8d65537854e1f03, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WireMock.Net.Abstractions.1.5.3\lib\net451\WireMock.Net.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="WireMock.Org.Abstractions, Version=1.5.3.0, Culture=neutral, PublicKeyToken=c8d65537854e1f03, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\WireMock.Org.Abstractions.1.5.3\lib\net45\WireMock.Org.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="XPath2, Version=1.1.3.0, Culture=neutral, PublicKeyToken=463c6d7fb740c7e5, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\XPath2.1.1.3\lib\net452\XPath2.dll</HintPath>
|
||||
</Reference>
|
||||
@@ -388,6 +379,16 @@
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj">
|
||||
<Project>{b6269aac-170a-4346-8b9a-579ded3d9a94}</Project>
|
||||
<Name>WireMock.Net.Abstractions</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj">
|
||||
<Project>{d3804228-91f4-4502-9595-39584e5a01ad}</Project>
|
||||
<Name>WireMock.Net</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -147,9 +147,6 @@
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net472" />
|
||||
<package id="TinyMapper" version="3.0.3" targetFramework="net472" />
|
||||
<package id="WireMock.Net" version="1.5.3" targetFramework="net472" />
|
||||
<package id="WireMock.Net.Abstractions" version="1.5.3" targetFramework="net472" />
|
||||
<package id="WireMock.Org.Abstractions" version="1.5.3" targetFramework="net472" />
|
||||
<package id="XPath2" version="1.1.3" targetFramework="net472" />
|
||||
<package id="XPath2.Extensions" version="1.1.3" targetFramework="net472" />
|
||||
</packages>
|
||||
@@ -74,4 +74,9 @@ public class MappingModel
|
||||
/// The Webhooks.
|
||||
/// </summary>
|
||||
public WebhookModel[]? Webhooks { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fire and forget for webhooks.
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
{
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// The Webhook
|
||||
/// </summary>
|
||||
@@ -9,6 +9,5 @@
|
||||
/// <summary>
|
||||
/// The Webhook Request.
|
||||
/// </summary>
|
||||
public WebhookRequestModel Request { get; set; }
|
||||
}
|
||||
public WebhookRequestModel Request { get; set; } = null!;
|
||||
}
|
||||
@@ -11,12 +11,12 @@ public class WebhookRequestModel
|
||||
/// <summary>
|
||||
/// Gets or sets the Url.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
public string Url { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The method
|
||||
/// </summary>
|
||||
public string Method { get; set; }
|
||||
public string Method { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
@@ -47,4 +47,19 @@ public class WebhookRequestModel
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
}
|
||||
@@ -2,8 +2,8 @@ using System.Collections.Generic;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Models
|
||||
{
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// IWebhookRequest
|
||||
/// </summary>
|
||||
@@ -43,5 +43,19 @@ namespace WireMock.Models
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
int? Delay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
int? MaximumRandomDelay { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
@@ -17,6 +19,7 @@ namespace WireMock.Http;
|
||||
internal class WebhookSender
|
||||
{
|
||||
private const string ClientIp = "::1";
|
||||
private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond));
|
||||
|
||||
private readonly WireMockServerSettings _settings;
|
||||
|
||||
@@ -25,20 +28,26 @@ internal class WebhookSender
|
||||
_settings = Guard.NotNull(settings);
|
||||
}
|
||||
|
||||
public Task<HttpResponseMessage> SendAsync(HttpClient client, IMapping mapping, IWebhookRequest request, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage)
|
||||
public async Task<HttpResponseMessage> SendAsync(
|
||||
HttpClient client,
|
||||
IMapping mapping,
|
||||
IWebhookRequest webhookRequest,
|
||||
IRequestMessage originalRequestMessage,
|
||||
IResponseMessage originalResponseMessage
|
||||
)
|
||||
{
|
||||
Guard.NotNull(client);
|
||||
Guard.NotNull(mapping);
|
||||
Guard.NotNull(request);
|
||||
Guard.NotNull(webhookRequest);
|
||||
Guard.NotNull(originalRequestMessage);
|
||||
Guard.NotNull(originalResponseMessage);
|
||||
|
||||
IBodyData? bodyData;
|
||||
IDictionary<string, WireMockList<string>>? headers;
|
||||
if (request.UseTransformer == true)
|
||||
if (webhookRequest.UseTransformer == true)
|
||||
{
|
||||
ITransformer responseMessageTransformer;
|
||||
switch (request.TransformerType)
|
||||
switch (webhookRequest.TransformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
|
||||
@@ -47,26 +56,26 @@ internal class WebhookSender
|
||||
|
||||
case TransformerType.Scriban:
|
||||
case TransformerType.ScribanDotLiquid:
|
||||
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, request.TransformerType);
|
||||
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
|
||||
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
|
||||
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
|
||||
}
|
||||
|
||||
(bodyData, headers) = responseMessageTransformer.Transform(mapping, originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers, request.TransformerReplaceNodeOptions);
|
||||
(bodyData, headers) = responseMessageTransformer.Transform(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.Headers, webhookRequest.TransformerReplaceNodeOptions);
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyData = request.BodyData;
|
||||
headers = request.Headers;
|
||||
bodyData = webhookRequest.BodyData;
|
||||
headers = webhookRequest.Headers;
|
||||
}
|
||||
|
||||
// Create RequestMessage
|
||||
var requestMessage = new RequestMessage(
|
||||
new UrlDetails(request.Url),
|
||||
request.Method,
|
||||
new UrlDetails(webhookRequest.Url),
|
||||
webhookRequest.Method,
|
||||
ClientIp,
|
||||
bodyData,
|
||||
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
|
||||
@@ -76,9 +85,30 @@ internal class WebhookSender
|
||||
};
|
||||
|
||||
// Create HttpRequestMessage
|
||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, request.Url);
|
||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequest.Url);
|
||||
|
||||
// Delay (if required)
|
||||
if (TryGetDelay(webhookRequest, out var delay))
|
||||
{
|
||||
await Task.Delay(delay.Value).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Call the URL
|
||||
return client.SendAsync(httpRequestMessage);
|
||||
return await client.SendAsync(httpRequestMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static bool TryGetDelay(IWebhookRequest webhookRequest, [NotNullWhen(true)] out int? delay)
|
||||
{
|
||||
delay = webhookRequest.Delay;
|
||||
var minimumDelay = webhookRequest.MinimumRandomDelay;
|
||||
var maximumDelay = webhookRequest.MaximumRandomDelay;
|
||||
|
||||
if (minimumDelay is not null && maximumDelay is not null && maximumDelay >= minimumDelay)
|
||||
{
|
||||
delay = Random.Value!.Next(minimumDelay.Value, maximumDelay.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return delay is not null;
|
||||
}
|
||||
}
|
||||
@@ -112,6 +112,11 @@ public interface IMapping
|
||||
/// </summary>
|
||||
IWebhook[]? Webhooks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ProvideResponseAsync
|
||||
/// </summary>
|
||||
|
||||
@@ -63,6 +63,9 @@ public class Mapping : IMapping
|
||||
/// <inheritdoc />
|
||||
public IWebhook[]? Webhooks { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITimeSettings? TimeSettings { get; }
|
||||
|
||||
@@ -82,6 +85,7 @@ public class Mapping : IMapping
|
||||
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
|
||||
/// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
|
||||
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||
public Mapping(
|
||||
Guid guid,
|
||||
@@ -97,6 +101,7 @@ public class Mapping : IMapping
|
||||
string? nextState,
|
||||
int? stateTimes,
|
||||
IWebhook[]? webhooks,
|
||||
bool? useWebhooksFireAndForget,
|
||||
ITimeSettings? timeSettings)
|
||||
{
|
||||
Guid = guid;
|
||||
@@ -112,6 +117,7 @@ public class Mapping : IMapping
|
||||
NextState = nextState;
|
||||
StateTimes = stateTimes;
|
||||
Webhooks = webhooks;
|
||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||
TimeSettings = timeSettings;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
||||
/// <summary>
|
||||
/// The key
|
||||
/// </summary>
|
||||
public string? Key { get; }
|
||||
public string Key { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the key should be matched using case-ignore.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
namespace WireMock.Models
|
||||
{
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Webhook
|
||||
/// </summary>
|
||||
public class Webhook : IWebhook
|
||||
{
|
||||
/// <inheritdoc cref="IWebhook.Request"/>
|
||||
public IWebhookRequest Request { get; set; }
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public IWebhookRequest Request { get; set; } = null!;
|
||||
}
|
||||
@@ -2,32 +2,40 @@ using System.Collections.Generic;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Models
|
||||
{
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// WebhookRequest
|
||||
/// </summary>
|
||||
public class WebhookRequest : IWebhookRequest
|
||||
{
|
||||
/// <inheritdoc cref="IWebhookRequest.Url"/>
|
||||
public string Url { get; set; }
|
||||
/// <inheritdoc />
|
||||
public string Url { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="IWebhookRequest.Method"/>
|
||||
public string Method { get; set; }
|
||||
/// <inheritdoc />
|
||||
public string Method { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="IWebhookRequest.Headers"/>
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, WireMockList<string>>? Headers { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IWebhookRequest.BodyData"/>
|
||||
/// <inheritdoc />
|
||||
public IBodyData? BodyData { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IWebhookRequest.UseTransformer"/>
|
||||
/// <inheritdoc />
|
||||
public bool? UseTransformer { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IWebhookRequest.TransformerType"/>
|
||||
/// <inheritdoc />
|
||||
public TransformerType TransformerType { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IWebhookRequest.TransformerReplaceNodeOptions"/>
|
||||
/// <inheritdoc />
|
||||
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
}
|
||||
@@ -11,8 +11,8 @@ using WireMock.Serialization;
|
||||
using WireMock.Types;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Settings;
|
||||
using System.Collections.Generic;
|
||||
#if !USE_ASPNETCORE
|
||||
using Microsoft.Owin;
|
||||
using IContext = Microsoft.Owin.IOwinContext;
|
||||
using OwinMiddleware = Microsoft.Owin.OwinMiddleware;
|
||||
using Next = Microsoft.Owin.OwinMiddleware;
|
||||
@@ -161,6 +161,7 @@ namespace WireMock.Owin
|
||||
_options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping?.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
|
||||
response = ResponseMessageBuilder.Create(ex.Message, 500);
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
var log = new LogEntry
|
||||
@@ -201,19 +202,45 @@ namespace WireMock.Owin
|
||||
|
||||
private async Task SendToWebhooksAsync(IMapping mapping, IRequestMessage request, IResponseMessage response)
|
||||
{
|
||||
var tasks = new List<Func<Task>>();
|
||||
for (int index = 0; index < mapping.Webhooks?.Length; index++)
|
||||
{
|
||||
var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings());
|
||||
var webhookSender = new WebhookSender(mapping.Settings);
|
||||
var webhookRequest = mapping.Webhooks[index].Request;
|
||||
var webHookIndex = index;
|
||||
|
||||
tasks.Add(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await webhookSender.SendAsync(httpClientForWebhook, mapping, mapping.Webhooks[index].Request, request, response).ConfigureAwait(false);
|
||||
await webhookSender.SendAsync(httpClientForWebhook, mapping, webhookRequest, request, response).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_options.Logger.Error($"Sending message to Webhook [{index}] from Mapping '{mapping.Guid}' failed. Exception: {ex}");
|
||||
_options.Logger.Error($"Sending message to Webhook [{webHookIndex}] from Mapping '{mapping.Guid}' failed. Exception: {ex}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (mapping.UseWebhooksFireAndForget == true)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Do not wait
|
||||
await Task.Run(() =>
|
||||
{
|
||||
Task.WhenAll(tasks.Select(async task => await task.Invoke())).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await Task.WhenAll(tasks.Select(async task => await task.Invoke())).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -119,6 +119,7 @@ internal class ProxyHelper
|
||||
nextState: null,
|
||||
stateTimes: null,
|
||||
webhooks: null,
|
||||
useWebhooksFireAndForget: null,
|
||||
timeSettings: null
|
||||
);
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ internal class MappingConverter
|
||||
TimeSettings = TimeSettingsMapper.Map(mapping.TimeSettings),
|
||||
Title = mapping.Title,
|
||||
Description = mapping.Description,
|
||||
UseWebhooksFireAndForget = mapping.UseWebhooksFireAndForget,
|
||||
Priority = mapping.Priority != 0 ? mapping.Priority : null,
|
||||
Scenario = mapping.Scenario,
|
||||
WhenStateIs = mapping.ExecutionConditionState,
|
||||
@@ -168,7 +169,7 @@ internal class MappingConverter
|
||||
mappingModel.Response.BodyDestination = response.ResponseMessage.BodyDestination;
|
||||
mappingModel.Response.StatusCode = response.ResponseMessage.StatusCode;
|
||||
|
||||
if (response.ResponseMessage.Headers != null && response.ResponseMessage.Headers.Count > 0)
|
||||
if (response.ResponseMessage.Headers is { Count: > 0 })
|
||||
{
|
||||
mappingModel.Response.Headers = MapHeaders(response.ResponseMessage.Headers);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@ internal static class WebhookMapper
|
||||
{
|
||||
Url = model.Request.Url,
|
||||
Method = model.Request.Method,
|
||||
Delay = model.Request.Delay,
|
||||
MinimumRandomDelay = model.Request.MinimumRandomDelay,
|
||||
MaximumRandomDelay = model.Request.MaximumRandomDelay,
|
||||
Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList<string>(x.Value)) ?? new Dictionary<string, WireMockList<string>>()
|
||||
}
|
||||
};
|
||||
@@ -27,6 +30,7 @@ internal static class WebhookMapper
|
||||
if (model.Request.UseTransformer == true)
|
||||
{
|
||||
webhook.Request.UseTransformer = true;
|
||||
|
||||
if (!Enum.TryParse<TransformerType>(model.Request.TransformerType, out var transformerType))
|
||||
{
|
||||
transformerType = TransformerType.Handlebars;
|
||||
@@ -37,7 +41,6 @@ internal static class WebhookMapper
|
||||
{
|
||||
option = ReplaceNodeOptions.None;
|
||||
}
|
||||
|
||||
webhook.Request.TransformerReplaceNodeOptions = option;
|
||||
}
|
||||
|
||||
@@ -82,7 +85,10 @@ internal static class WebhookMapper
|
||||
Headers = webhook.Request.Headers?.ToDictionary(x => x.Key, x => x.Value.ToString()),
|
||||
UseTransformer = webhook.Request.UseTransformer,
|
||||
TransformerType = webhook.Request.UseTransformer == true ? webhook.Request.TransformerType.ToString() : null,
|
||||
TransformerReplaceNodeOptions = webhook.Request.TransformerReplaceNodeOptions.ToString()
|
||||
TransformerReplaceNodeOptions = webhook.Request.TransformerReplaceNodeOptions.ToString(),
|
||||
Delay = webhook.Request.Delay,
|
||||
MinimumRandomDelay = webhook.Request.MinimumRandomDelay,
|
||||
MaximumRandomDelay = webhook.Request.MaximumRandomDelay,
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -123,6 +123,13 @@ namespace WireMock.Server
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithWebhook(params IWebhook[] webhooks);
|
||||
|
||||
/// <summary>
|
||||
/// Support FireAndForget for any configured Webhooks
|
||||
/// </summary>
|
||||
/// <param name="UseWebhooksFireAndForget"></param>
|
||||
/// <returns></returns>
|
||||
IRespondWithAProvider WithWebhookFireAndForget(bool UseWebhooksFireAndForget);
|
||||
|
||||
/// <summary>
|
||||
/// Add a Webhook to call after the response has been generated.
|
||||
/// </summary>
|
||||
|
||||
@@ -30,6 +30,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly bool _saveToFile;
|
||||
|
||||
private bool _useWebhookFireAndForget = false;
|
||||
|
||||
public Guid Guid { get; private set; } = Guid.NewGuid();
|
||||
|
||||
public IWebhook[]? Webhooks { get; private set; }
|
||||
@@ -57,7 +59,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
/// <param name="provider">The provider.</param>
|
||||
public void RespondWith(IResponseProvider provider)
|
||||
{
|
||||
_registrationCallback(new Mapping(Guid, _title, _description, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks, TimeSettings), _saveToFile);
|
||||
_registrationCallback(new Mapping(Guid, _title, _description, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks, _useWebhookFireAndForget, TimeSettings), _saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -233,6 +235,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
return this;
|
||||
}
|
||||
|
||||
public IRespondWithAProvider WithWebhookFireAndForget(bool useWebhooksFireAndForget)
|
||||
{
|
||||
_useWebhookFireAndForget = useWebhooksFireAndForget;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static IWebhook InitWebhook(
|
||||
string url,
|
||||
string method,
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace WireMock.Net.Tests.Owin
|
||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, null);
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null);
|
||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||
|
||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||
@@ -230,7 +230,7 @@ namespace WireMock.Net.Tests.Owin
|
||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, null);
|
||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null);
|
||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||
|
||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||
|
||||
@@ -11,11 +11,11 @@ using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests.Serialization
|
||||
{
|
||||
namespace WireMock.Net.Tests.Serialization;
|
||||
|
||||
public class MappingConverterTests
|
||||
{
|
||||
private readonly WireMockServerSettings _settings = new WireMockServerSettings();
|
||||
private readonly WireMockServerSettings _settings = new();
|
||||
|
||||
private readonly MappingConverter _sut;
|
||||
|
||||
@@ -52,7 +52,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
}
|
||||
}
|
||||
};
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -64,10 +64,11 @@ namespace WireMock.Net.Tests.Serialization
|
||||
model.Response.BodyAsJsonIndented.Should().BeNull();
|
||||
model.Response.UseTransformer.Should().BeNull();
|
||||
model.Response.Headers.Should().BeNull();
|
||||
model.UseWebhooksFireAndForget.Should().BeFalse();
|
||||
|
||||
model.Webhooks.Should().BeNull();
|
||||
|
||||
model.Webhook.Request.Method.Should().Be("post");
|
||||
model.Webhook!.Request.Method.Should().Be("post");
|
||||
model.Webhook.Request.Url.Should().Be("https://test.com");
|
||||
model.Webhook.Request.Headers.Should().HaveCount(2);
|
||||
model.Webhook.Request.Body.Should().Be("b");
|
||||
@@ -121,7 +122,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
}
|
||||
}
|
||||
};
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -133,10 +134,11 @@ namespace WireMock.Net.Tests.Serialization
|
||||
model.Response.BodyAsJsonIndented.Should().BeNull();
|
||||
model.Response.UseTransformer.Should().BeNull();
|
||||
model.Response.Headers.Should().BeNull();
|
||||
model.UseWebhooksFireAndForget.Should().BeTrue();
|
||||
|
||||
model.Webhook.Should().BeNull();
|
||||
|
||||
model.Webhooks[0].Request.Method.Should().Be("post");
|
||||
model.Webhooks![0].Request.Method.Should().Be("post");
|
||||
model.Webhooks[0].Request.Url.Should().Be("https://test1.com");
|
||||
model.Webhooks[0].Request.Headers.Should().HaveCount(1);
|
||||
model.Webhooks[0].Request.Body.Should().Be("1");
|
||||
@@ -155,7 +157,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
var description = "my-description";
|
||||
var request = Request.Create();
|
||||
var response = Response.Create();
|
||||
var mapping = new Mapping(Guid.NewGuid(), title, description, null, _settings, request, response, 0, null, null, null, null, null, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -172,7 +174,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
// Assign
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -198,15 +200,15 @@ namespace WireMock.Net.Tests.Serialization
|
||||
End = end,
|
||||
TTL = ttl
|
||||
};
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, timeSettings);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
|
||||
// Assert
|
||||
model.Should().NotBeNull();
|
||||
model.TimeSettings.Should().NotBeNull();
|
||||
model.TimeSettings.Start.Should().Be(start);
|
||||
model.TimeSettings!.Should().NotBeNull();
|
||||
model.TimeSettings!.Start.Should().Be(start);
|
||||
model.TimeSettings.End.Should().Be(end);
|
||||
model.TimeSettings.TTL.Should().Be(ttl);
|
||||
}
|
||||
@@ -226,7 +228,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
{
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithDelay(test.Delay);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -244,7 +246,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
var delay = 1000;
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithDelay(delay);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -261,7 +263,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
int minimumDelay = 1000;
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithRandomDelay(minimumDelay);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -281,7 +283,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
int maximumDelay = 2000;
|
||||
var request = Request.Create();
|
||||
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, null);
|
||||
var mapping = new Mapping(Guid.NewGuid(), string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null);
|
||||
|
||||
// Act
|
||||
var model = _sut.ToMappingModel(mapping);
|
||||
@@ -293,4 +295,3 @@ namespace WireMock.Net.Tests.Serialization
|
||||
model.Response.MaximumRandomDelay.Should().Be(maximumDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Models;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests.Serialization
|
||||
{
|
||||
namespace WireMock.Net.Tests.Serialization;
|
||||
|
||||
public class WebhookMapperTests
|
||||
{
|
||||
[Fact]
|
||||
public void WebhookMapper_Map_Model_BodyAsString_And_UseTransformerIsFalse()
|
||||
public void WebhookMapper_Map_WebhookModel_BodyAsString_And_UseTransformerIsFalse()
|
||||
{
|
||||
// Assign
|
||||
var model = new WebhookModel
|
||||
@@ -33,14 +35,14 @@ namespace WireMock.Net.Tests.Serialization
|
||||
result.Request.Url.Should().Be("https://localhost");
|
||||
result.Request.Method.Should().Be("get");
|
||||
result.Request.Headers.Should().HaveCount(1);
|
||||
result.Request.BodyData.BodyAsJson.Should().BeNull();
|
||||
result.Request.BodyData!.BodyAsJson.Should().BeNull();
|
||||
result.Request.BodyData.BodyAsString.Should().Be("test");
|
||||
result.Request.BodyData.DetectedBodyType.Should().Be(BodyType.String);
|
||||
result.Request.UseTransformer.Should().BeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WebhookMapper_Map_Model_BodyAsString_And_UseTransformerIsTrue()
|
||||
public void WebhookMapper_Map_WebhookModel_BodyAsString_And_UseTransformerIsTrue()
|
||||
{
|
||||
// Assign
|
||||
var model = new WebhookModel
|
||||
@@ -63,7 +65,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
result.Request.Url.Should().Be("https://localhost");
|
||||
result.Request.Method.Should().Be("get");
|
||||
result.Request.Headers.Should().HaveCount(1);
|
||||
result.Request.BodyData.BodyAsJson.Should().BeNull();
|
||||
result.Request.BodyData!.BodyAsJson.Should().BeNull();
|
||||
result.Request.BodyData.BodyAsString.Should().Be("test");
|
||||
result.Request.BodyData.DetectedBodyType.Should().Be(BodyType.String);
|
||||
result.Request.UseTransformer.Should().BeTrue();
|
||||
@@ -71,7 +73,7 @@ namespace WireMock.Net.Tests.Serialization
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WebhookMapper_Map_Model_BodyAsJson()
|
||||
public void WebhookMapper_Map_WebhookModel_BodyAsJson()
|
||||
{
|
||||
// Assign
|
||||
var model = new WebhookModel
|
||||
@@ -84,8 +86,11 @@ namespace WireMock.Net.Tests.Serialization
|
||||
{
|
||||
{ "x", "y" }
|
||||
},
|
||||
BodyAsJson = new { n = 12345 }
|
||||
}
|
||||
BodyAsJson = new { n = 12345 },
|
||||
Delay = 4,
|
||||
MinimumRandomDelay = 5,
|
||||
MaximumRandomDelay = 6
|
||||
},
|
||||
};
|
||||
|
||||
var result = WebhookMapper.Map(model);
|
||||
@@ -93,9 +98,48 @@ namespace WireMock.Net.Tests.Serialization
|
||||
result.Request.Url.Should().Be("https://localhost");
|
||||
result.Request.Method.Should().Be("get");
|
||||
result.Request.Headers.Should().HaveCount(1);
|
||||
result.Request.BodyData.BodyAsString.Should().BeNull();
|
||||
result.Request.BodyData!.BodyAsString.Should().BeNull();
|
||||
result.Request.BodyData.BodyAsJson.Should().NotBeNull();
|
||||
result.Request.BodyData.DetectedBodyType.Should().Be(BodyType.Json);
|
||||
}
|
||||
result.Request.Delay.Should().Be(4);
|
||||
result.Request.MinimumRandomDelay.Should().Be(5);
|
||||
result.Request.MaximumRandomDelay.Should().Be(6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WebhookMapper_Map_Webhook_To_Model()
|
||||
{
|
||||
// Assign
|
||||
var webhook = new Webhook
|
||||
{
|
||||
Request = new WebhookRequest
|
||||
{
|
||||
Url = "https://localhost",
|
||||
Method = "get",
|
||||
Headers = new Dictionary<string, WireMockList<string>>
|
||||
{
|
||||
{ "x", new WireMockList<string>("y") }
|
||||
},
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsJson = new { n = 12345 },
|
||||
DetectedBodyType = BodyType.Json,
|
||||
DetectedBodyTypeFromContentType = BodyType.Json
|
||||
},
|
||||
Delay = 4,
|
||||
MinimumRandomDelay = 5,
|
||||
MaximumRandomDelay = 6
|
||||
}
|
||||
};
|
||||
|
||||
var result = WebhookMapper.Map(webhook);
|
||||
|
||||
result.Request.Url.Should().Be("https://localhost");
|
||||
result.Request.Method.Should().Be("get");
|
||||
result.Request.Headers.Should().HaveCount(1);
|
||||
result.Request.BodyAsJson.Should().NotBeNull();
|
||||
result.Request.Delay.Should().Be(4);
|
||||
result.Request.MinimumRandomDelay.Should().Be(5);
|
||||
result.Request.MaximumRandomDelay.Should().Be(6);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user