Compare commits

..

2 Commits
1.4.7 ... 1.4.8

Author SHA1 Message Date
Stef Heyenrath
2ad060bbd4 1.4.8 2021-03-24 17:20:31 +00:00
Stef Heyenrath
d758301e4f Webhook (#591)
Webhook
2021-03-24 18:15:31 +01:00
41 changed files with 1215 additions and 341 deletions

View File

@@ -1,3 +1,7 @@
# 1.4.8 (24 March 2021)
- [#591](https://github.com/WireMock-Net/WireMock.Net/pull/591) - Webhook [feature] contributed by [StefH](https://github.com/StefH)
- [#589](https://github.com/WireMock-Net/WireMock.Net/issues/589) - How to send a request to a specific URL after sending response [feature]
# 1.4.7 (21 March 2021) # 1.4.7 (21 March 2021)
- [#594](https://github.com/WireMock-Net/WireMock.Net/pull/594) - Add possibility to the WithBody() to use IBodyData [feature] contributed by [StefH](https://github.com/StefH) - [#594](https://github.com/WireMock-Net/WireMock.Net/pull/594) - Add possibility to the WithBody() to use IBodyData [feature] contributed by [StefH](https://github.com/StefH)
- [#595](https://github.com/WireMock-Net/WireMock.Net/pull/595) - Use Handlebars.Net.Helpers Version="2.1.2" [feature] contributed by [StefH](https://github.com/StefH) - [#595](https://github.com/WireMock-Net/WireMock.Net/pull/595) - Use Handlebars.Net.Helpers Version="2.1.2" [feature] contributed by [StefH](https://github.com/StefH)
@@ -48,7 +52,6 @@
- [#549](https://github.com/WireMock-Net/WireMock.Net/issues/549) - WithProxy(...) does not save the mappings to file [bug] - [#549](https://github.com/WireMock-Net/WireMock.Net/issues/549) - WithProxy(...) does not save the mappings to file [bug]
# 1.3.8 (03 December 2020) # 1.3.8 (03 December 2020)
- [#539](https://github.com/WireMock-Net/WireMock.Net/pull/539) - Support for partial JSON matching contributed by [gleb-osokin](https://github.com/gleb-osokin)
- [#542](https://github.com/WireMock-Net/WireMock.Net/pull/542) - Create dotnet-wiremock tool [feature] contributed by [StefH](https://github.com/StefH) - [#542](https://github.com/WireMock-Net/WireMock.Net/pull/542) - Create dotnet-wiremock tool [feature] contributed by [StefH](https://github.com/StefH)
- [#543](https://github.com/WireMock-Net/WireMock.Net/pull/543) - Add support for .NET 5 [feature] contributed by [StefH](https://github.com/StefH) - [#543](https://github.com/WireMock-Net/WireMock.Net/pull/543) - Add support for .NET 5 [feature] contributed by [StefH](https://github.com/StefH)
- [#544](https://github.com/WireMock-Net/WireMock.Net/pull/544) - Use Java 11 in Azure Pipelines (needed for SonarCloud) [feature] contributed by [StefH](https://github.com/StefH) - [#544](https://github.com/WireMock-Net/WireMock.Net/pull/544) - Use Java 11 in Azure Pipelines (needed for SonarCloud) [feature] contributed by [StefH](https://github.com/StefH)
@@ -56,6 +59,9 @@
- [#547](https://github.com/WireMock-Net/WireMock.Net/pull/547) - Fix Proxying with SSL and NetCoreApp3.1 [bug] contributed by [StefH](https://github.com/StefH) - [#547](https://github.com/WireMock-Net/WireMock.Net/pull/547) - Fix Proxying with SSL and NetCoreApp3.1 [bug] contributed by [StefH](https://github.com/StefH)
- [#524](https://github.com/WireMock-Net/WireMock.Net/issues/524) - Proxying with SSL Not Working in .NET Core 3.1 [bug] - [#524](https://github.com/WireMock-Net/WireMock.Net/issues/524) - Proxying with SSL Not Working in .NET Core 3.1 [bug]
# 1.3.7 (17 November 2020)
- [#539](https://github.com/WireMock-Net/WireMock.Net/pull/539) - Support for partial JSON matching contributed by [gleb-osokin](https://github.com/gleb-osokin)
# 1.3.6 (10 November 2020) # 1.3.6 (10 November 2020)
- [#529](https://github.com/WireMock-Net/WireMock.Net/pull/529) - Add assertions for ClientIP, Url and ProxyUrl [feature] contributed by [akamud](https://github.com/akamud) - [#529](https://github.com/WireMock-Net/WireMock.Net/pull/529) - Add assertions for ClientIP, Url and ProxyUrl [feature] contributed by [akamud](https://github.com/akamud)
- [#535](https://github.com/WireMock-Net/WireMock.Net/pull/535) - WithCallback should use also use enum HttpStatusCode [bug] contributed by [StefH](https://github.com/StefH) - [#535](https://github.com/WireMock-Net/WireMock.Net/pull/535) - WithCallback should use also use enum HttpStatusCode [bug] contributed by [StefH](https://github.com/StefH)

View File

@@ -4,7 +4,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<VersionPrefix>1.4.7</VersionPrefix> <VersionPrefix>1.4.8</VersionPrefix>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes> <PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl> <PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl> <PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>

View File

@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes https://github.com/StefH/GitHubReleaseNotes
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.4.7 GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.4.8

View File

@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"WireMock.Net.StandAlone.NETCoreApp": { "WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "--Urls https://localhost:10080 --WireMockLogger WireMockConsoleLogger" "commandLineArgs": "--Urls http://localhost:9091 --WireMockLogger WireMockConsoleLogger"
} }
} }
} }

View File

@@ -52,5 +52,10 @@ namespace WireMock.Admin.Mappings
/// Saves this mapping as a static mapping file. /// Saves this mapping as a static mapping file.
/// </summary> /// </summary>
public bool? SaveToFile { get; set; } public bool? SaveToFile { get; set; }
/// <summary>
/// The Webhook.
/// </summary>
public WebhookModel Webhook { get; set; }
} }
} }

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Types;
namespace WireMock.Admin.Mappings namespace WireMock.Admin.Mappings
{ {

View File

@@ -0,0 +1,13 @@
namespace WireMock.Admin.Mappings
{
/// <summary>
/// The Webhook
/// </summary>
public class WebhookModel
{
/// <summary>
/// The Webhook Request.
/// </summary>
public WebhookRequestModel Request { get; set; }
}
}

View File

@@ -0,0 +1,45 @@
using System.Collections.Generic;
namespace WireMock.Admin.Mappings
{
/// <summary>
/// RequestModel
/// </summary>
public class WebhookRequestModel
{
/// <summary>
/// Gets or sets the Url.
/// </summary>
public string Url { get; set; }
/// <summary>
/// The methods
/// </summary>
public string Method { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// Use ResponseMessage Transformer.
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Gets the type of the transformer.
/// </summary>
public string TransformerType { get; set; }
}
}

View File

@@ -0,0 +1,13 @@
namespace WireMock.Models
{
/// <summary>
/// IWebhook
/// </summary>
public interface IWebhook
{
/// <summary>
/// Request
/// </summary>
IWebhookRequest Request { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Models
{
/// <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; }
}
}

View File

@@ -7,7 +7,7 @@ namespace WireMock.Http
{ {
internal static class HttpClientBuilder internal static class HttpClientBuilder
{ {
public static HttpClient Build(IProxyAndRecordSettings settings) public static HttpClient Build(IHttpClientSettings settings)
{ {
#if NETSTANDARD || NETCOREAPP3_1 || NET5_0 #if NETSTANDARD || NETCOREAPP3_1 || NET5_0
var handler = new HttpClientHandler var handler = new HttpClientHandler

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Models;
using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Transformers.Handlebars;
using WireMock.Transformers.Scriban;
using WireMock.Types;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Http
{
internal class WebhookSender
{
private const string ClientIp = "::1";
private readonly IWireMockServerSettings _settings;
public WebhookSender(IWireMockServerSettings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public Task<HttpResponseMessage> SendAsync([NotNull] HttpClient client, [NotNull] IWebhookRequest request, [NotNull] RequestMessage originalRequestMessage, [NotNull] ResponseMessage originalResponseMessage)
{
Check.NotNull(client, nameof(client));
Check.NotNull(request, nameof(request));
Check.NotNull(originalRequestMessage, nameof(originalRequestMessage));
Check.NotNull(originalResponseMessage, nameof(originalResponseMessage));
IBodyData bodyData;
IDictionary<string, WireMockList<string>> headers;
if (request.UseTransformer == true)
{
ITransformer responseMessageTransformer;
switch (request.TransformerType)
{
case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
responseMessageTransformer = new Transformer(factoryHandlebars);
break;
case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, request.TransformerType);
responseMessageTransformer = new Transformer(factoryDotLiquid);
break;
default:
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
}
(bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers);
}
else
{
bodyData = request.BodyData;
headers = request.Headers;
}
// Create RequestMessage
var requestMessage = new RequestMessage(
new UrlDetails(request.Url),
request.Method,
ClientIp,
bodyData,
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
)
{
DateTime = DateTime.UtcNow
};
// Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, request.Url);
// Call the URL
return client.SendAsync(httpRequestMessage);
}
}
}

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Settings; using WireMock.Settings;
@@ -93,6 +94,11 @@ namespace WireMock
/// </value> /// </value>
bool LogMapping { get; } bool LogMapping { get; }
/// <summary>
/// The Webhook.
/// </summary>
IWebhook Webhook { get; }
/// <summary> /// <summary>
/// ProvideResponseAsync /// ProvideResponseAsync
/// </summary> /// </summary>

View File

@@ -1,7 +1,9 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Admin.Mappings;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Settings; using WireMock.Settings;
@@ -54,6 +56,9 @@ namespace WireMock
/// <inheritdoc cref="IMapping.LogMapping" /> /// <inheritdoc cref="IMapping.LogMapping" />
public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider); public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider);
/// <inheritdoc cref="IMapping.Webhook" />
public IWebhook Webhook { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class. /// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary> /// </summary>
@@ -68,6 +73,7 @@ namespace WireMock
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param> /// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param> /// <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="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
/// <param name="webhook">The Webhook. [Optional]</param>
public Mapping( public Mapping(
Guid guid, Guid guid,
[CanBeNull] string title, [CanBeNull] string title,
@@ -79,7 +85,8 @@ namespace WireMock
[CanBeNull] string scenario, [CanBeNull] string scenario,
[CanBeNull] string executionConditionState, [CanBeNull] string executionConditionState,
[CanBeNull] string nextState, [CanBeNull] string nextState,
[CanBeNull] int? stateTimes) [CanBeNull] int? stateTimes,
[CanBeNull] IWebhook webhook)
{ {
Guid = guid; Guid = guid;
Title = title; Title = title;
@@ -92,6 +99,7 @@ namespace WireMock
ExecutionConditionState = executionConditionState; ExecutionConditionState = executionConditionState;
NextState = nextState; NextState = nextState;
StateTimes = stateTimes; StateTimes = stateTimes;
Webhook = webhook;
} }
/// <inheritdoc cref="IMapping.ProvideResponseAsync" /> /// <inheritdoc cref="IMapping.ProvideResponseAsync" />

View File

@@ -6,7 +6,7 @@ namespace WireMock.Util
/// <summary> /// <summary>
/// BodyData /// BodyData
/// </summary> /// </summary>
public class BodyData : IBodyData internal class BodyData : IBodyData
{ {
/// <inheritdoc cref="IBodyData.Encoding" /> /// <inheritdoc cref="IBodyData.Encoding" />
public Encoding Encoding { get; set; } public Encoding Encoding { get; set; }

View File

@@ -0,0 +1,15 @@
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Types;
namespace WireMock.Models
{
/// <summary>
/// Webhook
/// </summary>
public class Webhook : IWebhook
{
/// <inheritdoc cref="IWebhook.Request"/>
public IWebhookRequest Request { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Models
{
/// <summary>
/// WebhookRequest
/// </summary>
public class WebhookRequest : IWebhookRequest
{
/// <inheritdoc cref="IWebhookRequest.Url"/>
public string Url { get; set; }
/// <inheritdoc cref="IWebhookRequest.Method"/>
public string Method { get; set; }
/// <inheritdoc cref="IWebhookRequest.Headers"/>
public IDictionary<string, WireMockList<string>> Headers { get; set; }
/// <inheritdoc cref="IWebhookRequest.BodyData"/>
public IBodyData BodyData { get; set; }
/// <inheritdoc cref="IWebhookRequest.UseTransformer"/>
public bool? UseTransformer { get; set; }
/// <inheritdoc cref="IWebhookRequest.TransformerType"/>
public TransformerType TransformerType { get; set; }
}
}

View File

@@ -53,7 +53,7 @@ namespace WireMock.Owin.Mappers
} }
} }
BodyData body = null; IBodyData body = null;
if (request.Body != null && BodyParser.ShouldParseBody(method, options.AllowBodyForAllHttpMethods == true)) if (request.Body != null && BodyParser.ShouldParseBody(method, options.AllowBodyForAllHttpMethods == true))
{ {
var bodyParserSettings = new BodyParserSettings var bodyParserSettings = new BodyParserSettings

View File

@@ -9,6 +9,7 @@ using WireMock.Serialization;
using WireMock.Types; using WireMock.Types;
using WireMock.Validation; using WireMock.Validation;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Settings;
#if !USE_ASPNETCORE #if !USE_ASPNETCORE
using Microsoft.Owin; using Microsoft.Owin;
using IContext = Microsoft.Owin.IOwinContext; using IContext = Microsoft.Owin.IOwinContext;
@@ -153,6 +154,11 @@ namespace WireMock.Owin
{ {
UpdateScenarioState(targetMapping); UpdateScenarioState(targetMapping);
} }
if (!targetMapping.IsAdminInterface && targetMapping.Webhook != null)
{
await SendToWebhookAsync(targetMapping, request, response).ConfigureAwait(false);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -184,6 +190,21 @@ namespace WireMock.Owin
await CompletedTask; await CompletedTask;
} }
private async Task SendToWebhookAsync(IMapping mapping, RequestMessage request, ResponseMessage response)
{
var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings());
var webhookSender = new WebhookSender(mapping.Settings);
try
{
await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhook.Request, request, response).ConfigureAwait(false);
}
catch (Exception ex)
{
_options.Logger.Error($"Sending message to Webhook Mapping '{mapping.Guid}' failed. Exception: {ex}");
}
}
private void UpdateScenarioState(IMapping mapping) private void UpdateScenarioState(IMapping mapping)
{ {
var scenario = _options.Scenarios[mapping.Scenario]; var scenario = _options.Scenarios[mapping.Scenario];

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using HandlebarsDotNet.Helpers.Validation;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Http; using WireMock.Http;
using WireMock.Matchers; using WireMock.Matchers;
@@ -106,7 +105,7 @@ namespace WireMock.Proxy
var response = Response.Create(responseMessage); var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null, null); return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null, null, null);
} }
} }
} }

View File

@@ -101,7 +101,7 @@ namespace WireMock
/// <param name="bodyData">The BodyData.</param> /// <param name="bodyData">The BodyData.</param>
/// <param name="headers">The headers.</param> /// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param> /// <param name="cookies">The cookies.</param>
public RequestMessage([NotNull] UrlDetails urlDetails, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] BodyData bodyData = null, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null) public RequestMessage([NotNull] UrlDetails urlDetails, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] IBodyData bodyData = null, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{ {
Check.NotNull(urlDetails, nameof(urlDetails)); Check.NotNull(urlDetails, nameof(urlDetails));
Check.NotNull(method, nameof(method)); Check.NotNull(method, nameof(method));

View File

@@ -84,7 +84,8 @@ namespace WireMock.Serialization
Response = new ResponseModel Response = new ResponseModel
{ {
Delay = (int?)response.Delay?.TotalMilliseconds Delay = (int?)response.Delay?.TotalMilliseconds
} },
Webhook = WebhookMapper.Map(mapping.Webhook)
}; };
if (bodyMatcher?.Matchers != null) if (bodyMatcher?.Matchers != null)

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Admin.Mappings;
using WireMock.Http;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Serialization
{
internal static class WebhookMapper
{
public static IWebhook Map(WebhookModel model)
{
var webhook = new Webhook
{
Request = new WebhookRequest
{
Url = model.Request.Url,
Method = model.Request.Method,
Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList<string>(x.Value)) ?? new Dictionary<string, WireMockList<string>>()
}
};
if (model.Request.UseTransformer == true)
{
webhook.Request.UseTransformer = true;
if (!Enum.TryParse<TransformerType>(model.Request.TransformerType, out var transformerType))
{
transformerType = TransformerType.Handlebars;
}
webhook.Request.TransformerType = transformerType;
}
IEnumerable<string> contentTypeHeader = null;
if (webhook.Request.Headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)))
{
contentTypeHeader = webhook.Request.Headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)).Value;
}
if (model.Request.Body != null)
{
webhook.Request.BodyData = new BodyData
{
BodyAsString = model.Request.Body,
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
};
}
else if (model.Request.BodyAsJson != null)
{
webhook.Request.BodyData = new BodyData
{
BodyAsJson = model.Request.BodyAsJson,
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
};
}
return webhook;
}
public static WebhookModel Map(IWebhook webhook)
{
if (webhook?.Request == null)
{
return null;
}
var model = new WebhookModel
{
Request = new WebhookRequestModel
{
Url = webhook.Request.Url,
Method = webhook.Request.Method,
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
}
};
if (webhook.Request.BodyData != null)
{
switch (webhook.Request.BodyData.DetectedBodyType)
{
case BodyType.String:
model.Request.Body = webhook.Request.BodyData.BodyAsString;
break;
case BodyType.Json:
model.Request.BodyAsJson = webhook.Request.BodyData.BodyAsJson;
break;
default:
break;
}
}
return model;
}
}
}

View File

@@ -1,5 +1,9 @@
using System; using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Types;
namespace WireMock.Server namespace WireMock.Server
{ {
@@ -97,5 +101,50 @@ namespace WireMock.Server
/// <param name="times">The number of times this match should be matched before the state will be changed to the specified one. Default value is 1.</param> /// <param name="times">The number of times this match should be matched before the state will be changed to the specified one. Default value is 1.</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns> /// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WillSetStateTo(int state, int? times = 1); IRespondWithAProvider WillSetStateTo(int state, int? times = 1);
/// <summary>
/// Add a Webbook to call after the response has been generated.
/// </summary>
/// <param name="webhook">The Webhook</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook(IWebhook webhook);
/// <summary>
/// Add a Webbook to call after the response has been generated.
/// </summary>
/// <param name="url">The Webhook Url</param>
/// <param name="method">The method to use. [optional]</param>
/// <param name="headers">The Headers to send. [optional]</param>
/// <param name="body">The body (as string) to send. [optional]</param>
/// <param name="useTransformer">Use Transformer. [optional]</param>
/// <param name="transformerType">The transformer type. [optional]</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] string body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars
);
/// <summary>
/// Add a Webbook to call after the response has been generated.
/// </summary>
/// <param name="url">The Webhook Url</param>
/// <param name="method">The method to use. [optional]</param>
/// <param name="headers">The Headers to send. [optional]</param>
/// <param name="body">The body (as json) to send. [optional]</param>
/// <param name="useTransformer">Use Transformer. [optional]</param>
/// <param name="transformerType">The transformer type. [optional]</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] object body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars
);
} }
} }

View File

@@ -1,9 +1,14 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License. // This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root. // For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System; using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Server namespace WireMock.Server
{ {
@@ -26,6 +31,8 @@ namespace WireMock.Server
public Guid Guid { get; private set; } = Guid.NewGuid(); public Guid Guid { get; private set; } = Guid.NewGuid();
public IWebhook Webhook { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class. /// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
/// </summary> /// </summary>
@@ -47,7 +54,7 @@ namespace WireMock.Server
/// <param name="provider">The provider.</param> /// <param name="provider">The provider.</param>
public void RespondWith(IResponseProvider provider) public void RespondWith(IResponseProvider provider)
{ {
_registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState), _saveToFile); _registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhook), _saveToFile);
} }
/// <see cref="IRespondWithAProvider.WithGuid(string)"/> /// <see cref="IRespondWithAProvider.WithGuid(string)"/>
@@ -140,5 +147,81 @@ namespace WireMock.Server
{ {
return WillSetStateTo(state.ToString(), times); return WillSetStateTo(state.ToString(), times);
} }
/// <see cref="IRespondWithAProvider.WithWebhook(IWebhook)"/>
public IRespondWithAProvider WithWebhook(IWebhook webhook)
{
Webhook = webhook;
return this;
}
/// <see cref="IRespondWithAProvider.WithWebhook(string,string, IDictionary{string, WireMockList{string}}, string, bool, TransformerType)"/>
public IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] string body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Webhook = InitWebhook(url, method, headers, useTransformer, transformerType);
if (body != null)
{
Webhook.Request.BodyData = new BodyData
{
BodyAsString = body,
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyType.String
};
}
return this;
}
/// <see cref="IRespondWithAProvider.WithWebhook(string, string, IDictionary{string, WireMockList{string}}, object, bool, TransformerType)"/>
public IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] object body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Webhook = InitWebhook(url, method, headers, useTransformer, transformerType);
if (body != null)
{
Webhook.Request.BodyData = new BodyData
{
BodyAsJson = body,
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyType.Json
};
}
return this;
}
private IWebhook InitWebhook(
string url,
string method,
IDictionary<string, WireMockList<string>> headers,
bool useTransformer,
TransformerType transformerType)
{
return new Webhook
{
Request = new WebhookRequest
{
Url = url,
Method = method ?? "post",
Headers = headers,
UseTransformer = useTransformer,
TransformerType = transformerType
}
};
}
} }
} }

View File

@@ -470,6 +470,11 @@ namespace WireMock.Server
respondProvider = respondProvider.WillSetStateTo(mappingModel.SetStateTo); respondProvider = respondProvider.WillSetStateTo(mappingModel.SetStateTo);
} }
if (mappingModel.Webhook?.Request != null)
{
respondProvider = respondProvider.WithWebhook(WebhookMapper.Map(mappingModel.Webhook));
}
respondProvider.RespondWith(responseBuilder); respondProvider.RespondWith(responseBuilder);
return respondProvider.Guid; return respondProvider.Guid;

View File

@@ -0,0 +1,17 @@
namespace WireMock.Settings
{
/// <summary>
/// HttpClientSettings
/// </summary>
public class HttpClientSettings : IHttpClientSettings
{
/// <inheritdoc cref="IHttpClientSettings.ClientX509Certificate2ThumbprintOrSubjectName"/>
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IHttpClientSettings.WebProxySettings"/>
public IWebProxySettings WebProxySettings { get; set; }
/// <inheritdoc cref="IHttpClientSettings.AllowAutoRedirect"/>
public bool? AllowAutoRedirect { get; set; }
}
}

View File

@@ -0,0 +1,24 @@
namespace WireMock.Settings
{
/// <summary>
/// IHttpClientSettings
/// </summary>
public interface IHttpClientSettings
{
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// Defines the WebProxySettings.
/// </summary>
IWebProxySettings WebProxySettings { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
bool? AllowAutoRedirect { get; set; }
}
}

View File

@@ -5,7 +5,7 @@ namespace WireMock.Settings
/// <summary> /// <summary>
/// IProxyAndRecordSettings /// IProxyAndRecordSettings
/// </summary> /// </summary>
public interface IProxyAndRecordSettings public interface IProxyAndRecordSettings : IHttpClientSettings
{ {
/// <summary> /// <summary>
/// The URL to proxy. /// The URL to proxy.
@@ -29,12 +29,6 @@ namespace WireMock.Settings
/// </summary> /// </summary>
bool SaveMappingToFile { get; set; } bool SaveMappingToFile { get; set; }
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary> /// <summary>
/// Defines a list from headers which will be excluded from the saved mappings. /// Defines a list from headers which will be excluded from the saved mappings.
/// </summary> /// </summary>
@@ -44,15 +38,5 @@ namespace WireMock.Settings
/// Defines a list of cookies which will be excluded from the saved mappings. /// Defines a list of cookies which will be excluded from the saved mappings.
/// </summary> /// </summary>
string[] ExcludedCookies { get; set; } string[] ExcludedCookies { get; set; }
/// <summary>
/// Defines the WebProxySettings.
/// </summary>
IWebProxySettings WebProxySettings { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
bool? AllowAutoRedirect { get; set; }
} }
} }

View File

@@ -0,0 +1,9 @@
namespace WireMock.Settings
{
/// <summary>
/// IWebhookSettings
/// </summary>
public interface IWebhookSettings : IHttpClientSettings
{
}
}

View File

@@ -186,5 +186,11 @@ namespace WireMock.Settings
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
bool CustomCertificateDefined { get; } bool CustomCertificateDefined { get; }
/// <summary>
/// Defines the global IWebhookSettingsto use
/// </summary>
[PublicAPI]
IWebhookSettings WebhookSettings { get; set; }
} }
} }

View File

@@ -5,7 +5,7 @@ namespace WireMock.Settings
/// <summary> /// <summary>
/// ProxyAndRecordSettings /// ProxyAndRecordSettings
/// </summary> /// </summary>
public class ProxyAndRecordSettings : IProxyAndRecordSettings public class ProxyAndRecordSettings : HttpClientSettings, IProxyAndRecordSettings
{ {
/// <summary> /// <summary>
/// The URL to proxy. /// The URL to proxy.
@@ -32,13 +32,6 @@ namespace WireMock.Settings
[PublicAPI] [PublicAPI]
public string SaveMappingForStatusCodePattern { get; set; } = "*"; public string SaveMappingForStatusCodePattern { get; set; } = "*";
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
[PublicAPI]
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.ExcludedHeaders"/> /// <inheritdoc cref="IProxyAndRecordSettings.ExcludedHeaders"/>
[PublicAPI] [PublicAPI]
public string[] ExcludedHeaders { get; set; } public string[] ExcludedHeaders { get; set; }
@@ -46,13 +39,5 @@ namespace WireMock.Settings
/// <inheritdoc cref="IProxyAndRecordSettings.ExcludedCookies"/> /// <inheritdoc cref="IProxyAndRecordSettings.ExcludedCookies"/>
[PublicAPI] [PublicAPI]
public string[] ExcludedCookies { get; set; } public string[] ExcludedCookies { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.WebProxySettings"/>
[PublicAPI]
public IWebProxySettings WebProxySettings { get; set; }
/// <inheritdoc cref="IProxyAndRecordSettings.AllowAutoRedirect"/>
[PublicAPI]
public bool? AllowAutoRedirect { get; set; }
} }
} }

View File

@@ -0,0 +1,9 @@
namespace WireMock.Settings
{
/// <summary>
/// WebhookSettings
/// </summary>
public class WebhookSettings : HttpClientSettings, IWebhookSettings
{
}
}

View File

@@ -129,5 +129,9 @@ namespace WireMock.Settings
/// <inheritdoc cref="IWireMockServerSettings.CustomCertificateDefined"/> /// <inheritdoc cref="IWireMockServerSettings.CustomCertificateDefined"/>
[PublicAPI] [PublicAPI]
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true; public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
/// <inheritdoc cref="IWireMockServerSettings.WebhookSettings"/>
[PublicAPI]
public IWebhookSettings WebhookSettings { get; set; }
} }
} }

View File

@@ -1,7 +1,13 @@
namespace WireMock.Transformers using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Transformers
{ {
interface ITransformer interface ITransformer
{ {
ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile); ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile);
(IBodyData BodyData, IDictionary<string, WireMockList<string>> Headers) Transform(RequestMessage originalRequestMessage, ResponseMessage originalResponseMessage, IBodyData bodyData, IDictionary<string, WireMockList<string>> headers);
} }
} }

View File

@@ -6,7 +6,6 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Transformers.Handlebars namespace WireMock.Transformers.Handlebars
{ {
@@ -16,51 +15,53 @@ namespace WireMock.Transformers.Handlebars
public Transformer([NotNull] ITransformerContextFactory factory) public Transformer([NotNull] ITransformerContextFactory factory)
{ {
Check.NotNull(factory, nameof(factory)); _factory = factory ?? throw new ArgumentNullException(nameof(factory));
}
_factory = factory; public (IBodyData BodyData, IDictionary<string, WireMockList<string>> Headers) Transform(RequestMessage originalRequestMessage, ResponseMessage originalResponseMessage, IBodyData bodyData, IDictionary<string, WireMockList<string>> headers)
{
var transformerContext = _factory.Create();
var model = new
{
request = originalRequestMessage,
response = originalResponseMessage
};
IBodyData newBodyData = null;
if (bodyData?.DetectedBodyType != null)
{
newBodyData = TransformBodyData(transformerContext, model, bodyData, false);
}
return (newBodyData, TransformHeaders(transformerContext, model, headers));
} }
public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile) public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile)
{ {
var handlebarsContext = _factory.Create(); var transformerContext = _factory.Create();
var responseMessage = new ResponseMessage(); var responseMessage = new ResponseMessage();
var model = new { request = requestMessage }; var model = new
switch (original.BodyData?.DetectedBodyType)
{ {
case BodyType.Json: request = requestMessage
TransformBodyAsJson(handlebarsContext, model, original, responseMessage); };
break;
case BodyType.File: if (original.BodyData?.DetectedBodyType != null)
TransformBodyAsFile(handlebarsContext, model, original, responseMessage, useTransformerForBodyAsFile); {
break; responseMessage.BodyData = TransformBodyData(transformerContext, model, original.BodyData, useTransformerForBodyAsFile);
case BodyType.String: if (original.BodyData.DetectedBodyType == BodyType.String)
{
responseMessage.BodyOriginal = original.BodyData.BodyAsString; responseMessage.BodyOriginal = original.BodyData.BodyAsString;
TransformBodyAsString(handlebarsContext, model, original, responseMessage); }
break;
} }
responseMessage.FaultType = original.FaultType; responseMessage.FaultType = original.FaultType;
responseMessage.FaultPercentage = original.FaultPercentage; responseMessage.FaultPercentage = original.FaultPercentage;
// Headers responseMessage.Headers = TransformHeaders(transformerContext, model, original.Headers);
var newHeaders = new Dictionary<string, WireMockList<string>>();
foreach (var header in original.Headers)
{
var headerKey = handlebarsContext.ParseAndRender(header.Key, model);
var templateHeaderValues = header.Value
.Select(text => handlebarsContext.ParseAndRender(text, model))
.ToArray();
newHeaders.Add(headerKey, new WireMockList<string>(templateHeaderValues));
}
responseMessage.Headers = newHeaders;
switch (original.StatusCode) switch (original.StatusCode)
{ {
@@ -69,17 +70,54 @@ namespace WireMock.Transformers.Handlebars
break; break;
case string statusCodeAsString: case string statusCodeAsString:
responseMessage.StatusCode = handlebarsContext.ParseAndRender(statusCodeAsString, model); responseMessage.StatusCode = transformerContext.ParseAndRender(statusCodeAsString, model);
break; break;
} }
return responseMessage; return responseMessage;
} }
private static void TransformBodyAsJson(ITransformerContext handlebarsContext, object model, ResponseMessage original, ResponseMessage responseMessage) private static IBodyData TransformBodyData(ITransformerContext transformerContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
{
switch (original?.DetectedBodyType)
{
case BodyType.Json:
return TransformBodyAsJson(transformerContext, model, original);
case BodyType.File:
return TransformBodyAsFile(transformerContext, model, original, useTransformerForBodyAsFile);
case BodyType.String:
return TransformBodyAsString(transformerContext, model, original);
default:
return null;
}
}
private static IDictionary<string, WireMockList<string>> TransformHeaders(ITransformerContext transformerContext, object model, IDictionary<string, WireMockList<string>> original)
{
if (original == null)
{
return new Dictionary<string, WireMockList<string>>();
}
var newHeaders = new Dictionary<string, WireMockList<string>>();
foreach (var header in original)
{
var headerKey = transformerContext.ParseAndRender(header.Key, model);
var templateHeaderValues = header.Value.Select(text => transformerContext.ParseAndRender(text, model)).ToArray();
newHeaders.Add(headerKey, new WireMockList<string>(templateHeaderValues));
}
return newHeaders;
}
private static IBodyData TransformBodyAsJson(ITransformerContext handlebarsContext, object model, IBodyData original)
{ {
JToken jToken; JToken jToken;
switch (original.BodyData.BodyAsJson) switch (original.BodyAsJson)
{ {
case JObject bodyAsJObject: case JObject bodyAsJObject:
jToken = bodyAsJObject.DeepClone(); jToken = bodyAsJObject.DeepClone();
@@ -96,23 +134,23 @@ namespace WireMock.Transformers.Handlebars
break; break;
default: default:
jToken = JObject.FromObject(original.BodyData.BodyAsJson); jToken = JObject.FromObject(original.BodyAsJson);
WalkNode(handlebarsContext, jToken, model); WalkNode(handlebarsContext, jToken, model);
break; break;
} }
responseMessage.BodyData = new BodyData return new BodyData
{ {
Encoding = original.BodyData.Encoding, Encoding = original.Encoding,
DetectedBodyType = original.BodyData.DetectedBodyType, DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsJson = jToken BodyAsJson = jToken
}; };
} }
private static JToken ReplaceSingleNode(ITransformerContext handlebarsContext, string stringValue, object model) private static JToken ReplaceSingleNode(ITransformerContext handlebarsContext, string stringValue, object model)
{ {
string transformedString = handlebarsContext.ParseAndRender(stringValue, model) as string; string transformedString = handlebarsContext.ParseAndRender(stringValue, model);
if (!string.Equals(stringValue, transformedString)) if (!string.Equals(stringValue, transformedString))
{ {
@@ -186,27 +224,27 @@ namespace WireMock.Transformers.Handlebars
node.Replace(value); node.Replace(value);
} }
private static void TransformBodyAsString(ITransformerContext handlebarsContext, object model, ResponseMessage original, ResponseMessage responseMessage) private static IBodyData TransformBodyAsString(ITransformerContext handlebarsContext, object model, IBodyData original)
{ {
responseMessage.BodyData = new BodyData return new BodyData
{ {
Encoding = original.BodyData.Encoding, Encoding = original.Encoding,
DetectedBodyType = original.BodyData.DetectedBodyType, DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsString = handlebarsContext.ParseAndRender(original.BodyData.BodyAsString, model) BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString, model)
}; };
} }
private void TransformBodyAsFile(ITransformerContext handlebarsContext, object model, ResponseMessage original, ResponseMessage responseMessage, bool useTransformerForBodyAsFile) private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
{ {
string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyData.BodyAsFile, model); string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile, model);
if (!useTransformerForBodyAsFile) if (!useTransformerForBodyAsFile)
{ {
responseMessage.BodyData = new BodyData return new BodyData
{ {
DetectedBodyType = original.BodyData.DetectedBodyType, DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsFile = transformedBodyAsFilename BodyAsFile = transformedBodyAsFilename
}; };
} }
@@ -214,10 +252,10 @@ namespace WireMock.Transformers.Handlebars
{ {
string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename); string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename);
responseMessage.BodyData = new BodyData return new BodyData
{ {
DetectedBodyType = BodyType.String, DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsString = handlebarsContext.ParseAndRender(text, model), BodyAsString = handlebarsContext.ParseAndRender(text, model),
BodyAsFile = transformedBodyAsFilename BodyAsFile = transformedBodyAsFilename
}; };

View File

@@ -1,9 +1,13 @@
using System; using System;
using System.Collections.Generic;
using FluentAssertions; using FluentAssertions;
using WireMock.Models;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Serialization; using WireMock.Serialization;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
using Xunit; using Xunit;
namespace WireMock.Net.Tests.Serialization namespace WireMock.Net.Tests.Serialization
@@ -25,7 +29,26 @@ namespace WireMock.Net.Tests.Serialization
// Assign // Assign
var request = Request.Create(); var request = Request.Create();
var response = Response.Create(); var response = Response.Create();
var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 0, null, null, null, null); var webhook = new Webhook
{
Request = new WebhookRequest
{
Url = "https://test.com",
Headers = new Dictionary<string, WireMockList<string>>
{
{ "Single", new WireMockList<string>("x") },
{ "Multi", new WireMockList<string>("a", "b") }
},
Method = "post",
BodyData = new BodyData
{
BodyAsString = "b",
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyType.String
}
}
};
var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 0, null, null, null, null, webhook);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);
@@ -33,9 +56,16 @@ namespace WireMock.Net.Tests.Serialization
// Assert // Assert
model.Should().NotBeNull(); model.Should().NotBeNull();
model.Priority.Should().BeNull(); model.Priority.Should().BeNull();
model.Response.BodyAsJsonIndented.Should().BeNull(); model.Response.BodyAsJsonIndented.Should().BeNull();
model.Response.UseTransformer.Should().BeNull(); model.Response.UseTransformer.Should().BeNull();
model.Response.Headers.Should().BeNull(); model.Response.Headers.Should().BeNull();
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");
model.Webhook.Request.BodyAsJson.Should().BeNull();
} }
[Fact] [Fact]
@@ -44,7 +74,7 @@ namespace WireMock.Net.Tests.Serialization
// Assign // Assign
var request = Request.Create(); var request = Request.Create();
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer(); var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 42, null, null, null, null); var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 42, null, null, null, null, null);
// Act // Act
var model = _sut.ToMappingModel(mapping); var model = _sut.ToMappingModel(mapping);

View File

@@ -0,0 +1,101 @@
using System.Collections.Generic;
using FluentAssertions;
using WireMock.Admin.Mappings;
using WireMock.Serialization;
using WireMock.Types;
using Xunit;
namespace WireMock.Net.Tests.Serialization
{
public class WebhookMapperTests
{
[Fact]
public void WebhookMapper_Map_Model_BodyAsString_And_UseTransformerIsFalse()
{
// Assign
var model = new WebhookModel
{
Request = new WebhookRequestModel
{
Url = "https://localhost",
Method = "get",
Headers = new Dictionary<string, string>
{
{ "x", "y" }
},
Body = "test",
UseTransformer = false
}
};
var result = WebhookMapper.Map(model);
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.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()
{
// Assign
var model = new WebhookModel
{
Request = new WebhookRequestModel
{
Url = "https://localhost",
Method = "get",
Headers = new Dictionary<string, string>
{
{ "x", "y" }
},
Body = "test",
UseTransformer = true
}
};
var result = WebhookMapper.Map(model);
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.BodyAsString.Should().Be("test");
result.Request.BodyData.DetectedBodyType.Should().Be(BodyType.String);
result.Request.UseTransformer.Should().BeTrue();
result.Request.TransformerType.Should().Be(TransformerType.Handlebars);
}
[Fact]
public void WebhookMapper_Map_Model_BodyAsJson()
{
// Assign
var model = new WebhookModel
{
Request = new WebhookRequestModel
{
Url = "https://localhost",
Method = "get",
Headers = new Dictionary<string, string>
{
{ "x", "y" }
},
BodyAsJson = new { n = 12345 }
}
};
var result = WebhookMapper.Map(model);
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.BodyAsJson.Should().NotBeNull();
result.Request.BodyData.DetectedBodyType.Should().Be(BodyType.Json);
}
}
}

View File

@@ -0,0 +1,133 @@
using System;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using FluentAssertions;
using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Types;
using WireMock.Util;
using Xunit;
namespace WireMock.Net.Tests
{
public class WireMockServerWebhookTests
{
[Fact]
public async Task WireMockServer_WithWebhook_Should_Send_Message_To_Webhook()
{
// Assign
var serverReceivingTheWebhook = WireMockServer.Start();
serverReceivingTheWebhook.Given(Request.Create().UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
// Act
var server = WireMockServer.Start();
server.Given(Request.Create().UsingPost())
.WithWebhook(new Webhook
{
Request = new WebhookRequest
{
Url = serverReceivingTheWebhook.Urls[0],
Method = "post",
BodyData = new BodyData
{
BodyAsString = "abc",
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyType.String
}
}
})
.RespondWith(Response.Create().WithBody("a-response"));
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{server.Urls[0]}/TST"),
Content = new StringContent("test")
};
// Assert
var response = await new HttpClient().SendAsync(request);
string content = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(HttpStatusCode.OK);
content.Should().Be("a-response");
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
server.Dispose();
serverReceivingTheWebhook.Dispose();
}
[Fact]
public async Task WireMockServer_WithWebhookArgs_Should_Send_StringMessage_To_Webhook()
{
// Assign
var serverReceivingTheWebhook = WireMockServer.Start();
serverReceivingTheWebhook.Given(Request.Create().UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
// Act
var server = WireMockServer.Start();
server.Given(Request.Create().UsingPost())
.WithWebhook(serverReceivingTheWebhook.Urls[0], "post", null, "OK !", true, TransformerType.Handlebars)
.RespondWith(Response.Create().WithBody("a-response"));
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{server.Urls[0]}/TST"),
Content = new StringContent("test")
};
// Assert
var response = await new HttpClient().SendAsync(request);
string content = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(HttpStatusCode.OK);
content.Should().Be("a-response");
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
serverReceivingTheWebhook.LogEntries.First().RequestMessage.Body.Should().Be("OK !");
server.Dispose();
serverReceivingTheWebhook.Dispose();
}
[Fact]
public async Task WireMockServer_WithWebhookArgs_Should_Send_JsonMessage_To_Webhook()
{
// Assign
var serverReceivingTheWebhook = WireMockServer.Start();
serverReceivingTheWebhook.Given(Request.Create().UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
// Act
var server = WireMockServer.Start();
server.Given(Request.Create().UsingPost())
.WithWebhook(serverReceivingTheWebhook.Urls[0], "post", null, new { Status = "OK" }, true, TransformerType.Handlebars)
.RespondWith(Response.Create().WithBody("a-response"));
var request = new HttpRequestMessage
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{server.Urls[0]}/TST"),
Content = new StringContent("test")
};
// Assert
var response = await new HttpClient().SendAsync(request);
string content = await response.Content.ReadAsStringAsync();
response.StatusCode.Should().Be(HttpStatusCode.OK);
content.Should().Be("a-response");
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
serverReceivingTheWebhook.LogEntries.First().RequestMessage.Body.Should().Be("{\"Status\":\"OK\"}");
server.Dispose();
serverReceivingTheWebhook.Dispose();
}
}
}