Skip to content

Commit

Permalink
WebHook : UseFireAndForget + Delay (#803)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* small update

* Tweak middleware and fix bug in example (#806)

Co-authored-by: Matt Philmon <[email protected]>

* .ConfigureAwait(false)

Co-authored-by: mattisking <[email protected]>
Co-authored-by: Matt Philmon <[email protected]>
  • Loading branch information
3 people authored Sep 12, 2022
1 parent 13a06b9 commit 98a0f2f
Show file tree
Hide file tree
Showing 22 changed files with 634 additions and 424 deletions.
35 changes: 35 additions & 0 deletions examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Newtonsoft.Json;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
Expand Down Expand Up @@ -576,6 +577,40 @@ public static void Run()
};
}));

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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
3 changes: 0 additions & 3 deletions examples/WireMock.Net.Console.Net472.Classic/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -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>
5 changes: 5 additions & 0 deletions src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
}
19 changes: 9 additions & 10 deletions src/WireMock.Net.Abstractions/Admin/Mappings/WebhookModel.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
namespace WireMock.Admin.Mappings
namespace WireMock.Admin.Mappings;

/// <summary>
/// The Webhook
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookModel
{
/// <summary>
/// The Webhook
/// The Webhook Request.
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookModel
{
/// <summary>
/// The Webhook Request.
/// </summary>
public WebhookRequestModel Request { get; set; }
}
public WebhookRequestModel Request { get; set; } = null!;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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; }
}
94 changes: 54 additions & 40 deletions src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,60 @@
using WireMock.Types;
using WireMock.Util;

namespace WireMock.Models
namespace WireMock.Models;

/// <summary>
/// IWebhookRequest
/// </summary>
public interface IWebhookRequest
{
/// <summary>
/// IWebhookRequest
/// </summary>
public interface IWebhookRequest
{
/// <summary>
/// The Webhook Url.
/// </summary>
string Url { get; set; }

/// <summary>
/// The method to use.
/// </summary>
string Method { get; set; }

/// <summary>
/// The Headers to send.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }

/// <summary>
/// The body to send.
/// </summary>
IBodyData? BodyData { get; set; }

/// <summary>
/// Use Transformer.
/// </summary>
bool? UseTransformer { get; set; }

/// <summary>
/// The transformer type.
/// </summary>
TransformerType TransformerType { get; set; }

/// <summary>
/// The ReplaceNodeOptions to use when transforming a JSON node.
/// </summary>
ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
}
/// The Webhook Url.
/// </summary>
string Url { get; set; }

/// <summary>
/// The method to use.
/// </summary>
string Method { get; set; }

/// <summary>
/// The Headers to send.
/// </summary>
IDictionary<string, WireMockList<string>>? Headers { get; }

/// <summary>
/// The body to send.
/// </summary>
IBodyData? BodyData { get; set; }

/// <summary>
/// Use Transformer.
/// </summary>
bool? UseTransformer { get; set; }

/// <summary>
/// The transformer type.
/// </summary>
TransformerType TransformerType { get; set; }

/// <summary>
/// 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; }
}
56 changes: 43 additions & 13 deletions src/WireMock.Net/Http/WebhookSender.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;

Expand All @@ -25,20 +28,26 @@ public WebhookSender(WireMockServerSettings settings)
_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);
Expand All @@ -47,26 +56,26 @@ public Task<HttpResponseMessage> SendAsync(HttpClient client, IMapping mapping,

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())
Expand All @@ -76,9 +85,30 @@ public Task<HttpResponseMessage> SendAsync(HttpClient client, IMapping mapping,
};

// 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;
}
}
5 changes: 5 additions & 0 deletions src/WireMock.Net/IMapping.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit 98a0f2f

Please sign in to comment.