From 5435c53a7b3f623978083392a14587b78d1f1664 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 7 Mar 2021 16:11:16 +0100 Subject: [PATCH 01/21] wip --- .../Admin/Mappings/MappingModel.cs | 5 +++ .../Admin/Mappings/ResponseModel.cs | 1 - .../Admin/Mappings/WebhookModel.cs | 13 ++++++ .../Admin/Mappings/WebhookRequestModel.cs | 41 +++++++++++++++++++ .../{Util => Models}/IBodyData.cs | 0 .../Models/IWebhook.cs | 7 ++++ .../Models/IWebhookRequest.cs | 40 ++++++++++++++++++ src/WireMock.Net/IMapping.cs | 6 +++ src/WireMock.Net/Mapping.cs | 4 ++ src/WireMock.Net/{Util => Models}/BodyData.cs | 0 src/WireMock.Net/Owin/WireMockMiddleware.cs | 6 +++ .../Serialization/MappingConverter.cs | 31 +++++++++++--- .../Server/IRespondWithAProvider.cs | 2 + .../Server/RespondWithAProvider.cs | 5 +++ 14 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 src/WireMock.Net.Abstractions/Admin/Mappings/WebhookModel.cs create mode 100644 src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs rename src/WireMock.Net.Abstractions/{Util => Models}/IBodyData.cs (100%) create mode 100644 src/WireMock.Net.Abstractions/Models/IWebhook.cs create mode 100644 src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs rename src/WireMock.Net/{Util => Models}/BodyData.cs (100%) diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs index f5eb8afe8..b1d5274fd 100644 --- a/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs @@ -52,5 +52,10 @@ public class MappingModel /// Saves this mapping as a static mapping file. /// public bool? SaveToFile { get; set; } + + /// + /// The Webhook. + /// + public WebhookModel Webhook { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs index 28035a8d6..6aab6dc51 100644 --- a/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using WireMock.Types; namespace WireMock.Admin.Mappings { diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookModel.cs new file mode 100644 index 000000000..ab6a51bca --- /dev/null +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookModel.cs @@ -0,0 +1,13 @@ +namespace WireMock.Admin.Mappings +{ + /// + /// The Webhook + /// + public class WebhookModel + { + /// + /// The Webhook Request. + /// + public WebhookRequestModel Request { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs new file mode 100644 index 000000000..2bff90fa0 --- /dev/null +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace WireMock.Admin.Mappings +{ + /// + /// RequestModel + /// + public class WebhookRequestModel + { + /// + /// Gets or sets the Url. (Can be a string or a UrlModel) + /// + public object Url { get; set; } + + /// + /// The methods + /// + public string Method { get; set; } + + /// + /// Use BodyMessage Transformer. + /// + public bool? UseTransformer { get; set; } + + /// + /// Gets the type of the transformer. + /// + public string TransformerType { get; set; } + + /// + /// Gets or sets the headers. + /// + public IDictionary Headers { get; set; } + + /// + /// Gets or sets the body. + /// + public BodyModel Body { get; set; } + + } +} \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Util/IBodyData.cs b/src/WireMock.Net.Abstractions/Models/IBodyData.cs similarity index 100% rename from src/WireMock.Net.Abstractions/Util/IBodyData.cs rename to src/WireMock.Net.Abstractions/Models/IBodyData.cs diff --git a/src/WireMock.Net.Abstractions/Models/IWebhook.cs b/src/WireMock.Net.Abstractions/Models/IWebhook.cs new file mode 100644 index 000000000..8eed99989 --- /dev/null +++ b/src/WireMock.Net.Abstractions/Models/IWebhook.cs @@ -0,0 +1,7 @@ +namespace WireMock.Models +{ + public interface IWebhook + { + IWebhookRequest Request { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs new file mode 100644 index 000000000..b5f678f69 --- /dev/null +++ b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using WireMock.Admin.Mappings; +using WireMock.Types; +using WireMock.Util; + +namespace WireMock.Models +{ + public interface IWebhookRequest + { + /// + /// Gets or sets the Url. (Can be a string or a UrlModel) + /// + object Url { get; set; } + + /// + /// The method + /// + string Method { get; set; } + + /// + /// Gets the headers. + /// + IDictionary> Headers { get; } + + /// + /// Gets or sets the body. + /// + IBodyData BodyData { get; set; } + + /// + /// Use BodyMessage Transformer. + /// + bool? UseTransformer { get; set; } + + /// + /// The type of the transformer. + /// + TransformerType? TransformerType { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/IMapping.cs b/src/WireMock.Net/IMapping.cs index 6cdcf0e27..f7816a1e8 100644 --- a/src/WireMock.Net/IMapping.cs +++ b/src/WireMock.Net/IMapping.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using WireMock.Matchers.Request; +using WireMock.Models; using WireMock.ResponseProviders; using WireMock.Settings; @@ -93,6 +94,11 @@ public interface IMapping /// bool LogMapping { get; } + /// + /// The Webhook. + /// + IWebhook Webhook { get; } + /// /// ProvideResponseAsync /// diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index 9952f4ef6..e9ec6c539 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using JetBrains.Annotations; using WireMock.Matchers.Request; +using WireMock.Models; using WireMock.ResponseProviders; using WireMock.Settings; @@ -54,6 +55,9 @@ public class Mapping : IMapping /// public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider); + /// + public IWebhook Webhook { get; } + /// /// Initializes a new instance of the class. /// diff --git a/src/WireMock.Net/Util/BodyData.cs b/src/WireMock.Net/Models/BodyData.cs similarity index 100% rename from src/WireMock.Net/Util/BodyData.cs rename to src/WireMock.Net/Models/BodyData.cs diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 274bdd0df..17a4af188 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -9,6 +9,7 @@ using WireMock.Types; using WireMock.Validation; using WireMock.ResponseBuilders; +using WireMock.Proxy; #if !USE_ASPNETCORE using Microsoft.Owin; using IContext = Microsoft.Owin.IOwinContext; @@ -153,6 +154,11 @@ private async Task InvokeInternal(IContext ctx) { UpdateScenarioState(targetMapping); } + + if (targetMapping.Webhook != null) + { + // new ProxyHelper(null).SendAsync() + } } catch (Exception ex) { diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index 844885d2d..d80a9c958 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -2,6 +2,7 @@ using System.Linq; using WireMock.Admin.Mappings; using WireMock.Matchers.Request; +using WireMock.Models; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; using WireMock.Settings; @@ -84,7 +85,8 @@ public MappingModel ToMappingModel(IMapping mapping) Response = new ResponseModel { Delay = (int?)response.Delay?.TotalMilliseconds - } + }, + Webhook = MapWebhook(mapping.Webhook) }; if (bodyMatcher?.Matchers != null) @@ -131,11 +133,8 @@ public MappingModel ToMappingModel(IMapping mapping) mappingModel.Response.Headers = MapHeaders(response.ResponseMessage.Headers); } - if (response.UseTransformer) - { - mappingModel.Response.UseTransformer = response.UseTransformer; - mappingModel.Response.TransformerType = response.TransformerType.ToString(); - } + mappingModel.Response.UseTransformer = response.UseTransformer; + mappingModel.Response.TransformerType = mappingModel.Response.UseTransformer == true ? MapTransformerType(response.TransformerType) : null; if (response.UseTransformerForBodyAsFile) { @@ -192,6 +191,26 @@ public MappingModel ToMappingModel(IMapping mapping) return mappingModel; } + private static WebhookModel MapWebhook(IWebhook webhook) + { + return webhook?.Request == null ? null : new WebhookModel + { + Request = new WebhookRequestModel + { + Url = webhook.Request.Url, + Method = webhook.Request.Method, + Headers = webhook.Request.Headers != null ? webhook.Request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()) : null, + UseTransformer = webhook.Request.UseTransformer, + TransformerType = webhook.Request.UseTransformer == true ? MapTransformerType(webhook.Request.TransformerType) : null + } + }; + } + + private static string MapTransformerType(TransformerType? transformerType) + { + return transformerType?.ToString(); + } + private static WebProxyModel MapWebProxy(IWebProxySettings settings) { return settings != null ? new WebProxyModel diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index 50ea594db..fc22cabcc 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -97,5 +97,7 @@ public interface IRespondWithAProvider /// The number of times this match should be matched before the state will be changed to the specified one. Default value is 1. /// The . IRespondWithAProvider WillSetStateTo(int state, int? times = 1); + + IRespondWithAProvider WithWebhook(); } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index b2c303d64..e729e6f82 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -140,5 +140,10 @@ public IRespondWithAProvider WillSetStateTo(int state, int? times = 1) { return WillSetStateTo(state.ToString(), times); } + + public IRespondWithAProvider WithWebhook() + { + throw new NotImplementedException(); + } } } \ No newline at end of file From ffcde9fea2d9e115c0a67f8ac0deb932a4403679 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 7 Mar 2021 18:02:00 +0100 Subject: [PATCH 02/21] x --- .../Program.cs | 128 +++++++++--------- .../Properties/launchSettings.json | 2 +- src/WireMock.Net/Mapping.cs | 5 +- src/WireMock.Net/Models/Webhook.cs | 7 + src/WireMock.Net/Models/WebhookRequest.cs | 21 +++ src/WireMock.Net/Proxy/ProxyHelper.cs | 3 +- .../Server/IRespondWithAProvider.cs | 4 +- .../Server/RespondWithAProvider.cs | 11 +- .../Server/WireMockServer.Admin.cs | 31 +++++ 9 files changed, 140 insertions(+), 72 deletions(-) create mode 100644 src/WireMock.Net/Models/Webhook.cs create mode 100644 src/WireMock.Net/Models/WebhookRequest.cs diff --git a/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs b/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs index a39469763..395442d5e 100644 --- a/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs +++ b/examples/WireMock.Net.StandAlone.NETCoreApp/Program.cs @@ -1,70 +1,70 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; -using log4net; -using log4net.Config; -using log4net.Repository; -using WireMock.RequestBuilders; -using WireMock.ResponseBuilders; -using WireMock.Server; -using WireMock.Settings; - -namespace WireMock.Net.StandAlone.NETCoreApp -{ - static class Program - { - private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); - // private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); - - private static int sleepTime = 30000; - private static WireMockServer _server; - - static void Main(string[] args) - { - XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config")); - +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using log4net; +using log4net.Config; +using log4net.Repository; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; +using WireMock.Settings; + +namespace WireMock.Net.StandAlone.NETCoreApp +{ + static class Program + { + private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); + // private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); + + private static int sleepTime = 30000; + private static WireMockServer _server; + + static void Main(string[] args) + { + XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config")); + if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger())) { return; } - settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'"))); - - _server = WireMockServer.Start(settings); - - _server - .Given(Request.Create() - .UsingAnyMethod()) - .RespondWith(Response.Create() - .WithTransformer() - .WithBody("{{Random Type=\"Integer\" Min=100 Max=999999}} {{DateTime.Now}} {{DateTime.Now \"yyyy-MMM\"}} {{String.Format (DateTime.Now) \"MMM-dd\"}}")); - - Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down"); - - Console.CancelKeyPress += (s, e) => - { - Stop("CancelKeyPress"); - }; - - System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx => - { - Stop("AssemblyLoadContext.Default.Unloading"); - }; - - while (true) - { - Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running : {_server.IsStarted}"); - Thread.Sleep(sleepTime); - } - } - - private static void Stop(string why) - { - Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'"); - _server.Stop(); - Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped"); - } - } + settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'"))); + + _server = WireMockServer.Start(settings); + + _server + .Given(Request.Create() + .UsingAnyMethod()) + .RespondWith(Response.Create() + .WithTransformer() + .WithBody("{{Random Type=\"Integer\" Min=100 Max=999999}} {{DateTime.Now}} {{DateTime.Now \"yyyy-MMM\"}} {{String.Format (DateTime.Now) \"MMM-dd\"}}")); + + Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down"); + + Console.CancelKeyPress += (s, e) => + { + Stop("CancelKeyPress"); + }; + + System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx => + { + Stop("AssemblyLoadContext.Default.Unloading"); + }; + + while (true) + { + Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running : {_server.IsStarted}"); + Thread.Sleep(sleepTime); + } + } + + private static void Stop(string why) + { + Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'"); + _server.Stop(); + Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped"); + } + } } \ No newline at end of file diff --git a/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json b/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json index 89c5b6b82..1adeba280 100644 --- a/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json +++ b/examples/WireMock.Net.StandAlone.NETCoreApp/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "WireMock.Net.StandAlone.NETCoreApp": { "commandName": "Project", - "commandLineArgs": "--Urls https://localhost:10080 --WireMockLogger WireMockConsoleLogger" + "commandLineArgs": "--Urls http://localhost:9091 --WireMockLogger WireMockConsoleLogger" } } } \ No newline at end of file diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index e9ec6c539..49d65920f 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using JetBrains.Annotations; +using WireMock.Admin.Mappings; using WireMock.Matchers.Request; using WireMock.Models; using WireMock.ResponseProviders; @@ -83,7 +84,8 @@ public Mapping( [CanBeNull] string scenario, [CanBeNull] string executionConditionState, [CanBeNull] string nextState, - [CanBeNull] int? stateTimes) + [CanBeNull] int? stateTimes, + [CanBeNull] IWebhook webhook) { Guid = guid; Title = title; @@ -96,6 +98,7 @@ public Mapping( ExecutionConditionState = executionConditionState; NextState = nextState; StateTimes = stateTimes; + Webhook = webhook; } /// diff --git a/src/WireMock.Net/Models/Webhook.cs b/src/WireMock.Net/Models/Webhook.cs new file mode 100644 index 000000000..1facbe1b8 --- /dev/null +++ b/src/WireMock.Net/Models/Webhook.cs @@ -0,0 +1,7 @@ +namespace WireMock.Models +{ + public class Webhook : IWebhook + { + public IWebhookRequest Request { get; set ; } + } +} diff --git a/src/WireMock.Net/Models/WebhookRequest.cs b/src/WireMock.Net/Models/WebhookRequest.cs new file mode 100644 index 000000000..904e0a725 --- /dev/null +++ b/src/WireMock.Net/Models/WebhookRequest.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using WireMock.Types; +using WireMock.Util; + +namespace WireMock.Models +{ + public class WebhookRequest : IWebhookRequest + { + public object Url { get; set; } + + public string Method { get; set; } + + public IDictionary> Headers { get; set; } + + public IBodyData BodyData { get; set; } + + public bool? UseTransformer { get; set; } + + public TransformerType? TransformerType { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Proxy/ProxyHelper.cs b/src/WireMock.Net/Proxy/ProxyHelper.cs index f88852408..96d12519f 100644 --- a/src/WireMock.Net/Proxy/ProxyHelper.cs +++ b/src/WireMock.Net/Proxy/ProxyHelper.cs @@ -3,7 +3,6 @@ using System.Linq; using System.Net.Http; using System.Threading.Tasks; -using HandlebarsDotNet.Helpers.Validation; using JetBrains.Annotations; using WireMock.Http; using WireMock.Matchers; @@ -106,7 +105,7 @@ private IMapping ToMapping(IProxyAndRecordSettings proxyAndRecordSettings, Reque 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); } } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index fc22cabcc..dae748146 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -1,4 +1,6 @@ using System; +using WireMock.Admin.Mappings; +using WireMock.Models; using WireMock.ResponseProviders; namespace WireMock.Server @@ -98,6 +100,6 @@ public interface IRespondWithAProvider /// The . IRespondWithAProvider WillSetStateTo(int state, int? times = 1); - IRespondWithAProvider WithWebhook(); + IRespondWithAProvider WithWebhook(IWebhook webhook); } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index e729e6f82..7eee294ab 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -1,7 +1,9 @@ // 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. using System; +using WireMock.Admin.Mappings; using WireMock.Matchers.Request; +using WireMock.Models; using WireMock.ResponseProviders; using WireMock.Settings; @@ -26,6 +28,8 @@ internal class RespondWithAProvider : IRespondWithAProvider public Guid Guid { get; private set; } = Guid.NewGuid(); + public IWebhook Webhook { get; private set; } + /// /// Initializes a new instance of the class. /// @@ -47,7 +51,7 @@ public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestM /// The 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); } /// @@ -141,9 +145,10 @@ public IRespondWithAProvider WillSetStateTo(int state, int? times = 1) return WillSetStateTo(state.ToString(), times); } - public IRespondWithAProvider WithWebhook() + public IRespondWithAProvider WithWebhook(IWebhook webhook) { - throw new NotImplementedException(); + Webhook = webhook; + return this; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 3c9a82b51..6d45814e5 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -16,6 +16,7 @@ using WireMock.Logging; using WireMock.Matchers; using WireMock.Matchers.Request; +using WireMock.Models; using WireMock.Proxy; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; @@ -470,6 +471,11 @@ private ResponseMessage MappingsPost(RequestMessage requestMessage) respondProvider = respondProvider.WillSetStateTo(mappingModel.SetStateTo); } + if (mappingModel.Webhook?.Request != null) + { + respondProvider = respondProvider.WithWebhook(Map(mappingModel.Webhook)); + } + respondProvider.RespondWith(responseBuilder); return respondProvider.Guid; @@ -762,6 +768,31 @@ private IRequestBuilder InitRequestBuilder(RequestModel requestModel, bool pathO return requestBuilder; } + private IWebhook Map(WebhookModel model) + { + var webhook = new Webhook + { + Request = new WebhookRequest + { + Url = model.Request.Url, + Method = model.Request.Method, + Headers = model.Request.Headers != null ? model.Request.Headers.ToDictionary(x => x.Key, x => new WireMockList(x.Value)) : null, + UseTransformer = model.Request.UseTransformer + } + }; + + if (model.Request.UseTransformer == true) + { + if (!Enum.TryParse(model.Request.TransformerType, out var transformerType)) + { + transformerType = TransformerType.Handlebars; + } + webhook.Request.TransformerType = transformerType; + } + + return webhook; + } + private IResponseBuilder InitResponseBuilder(ResponseModel responseModel) { IResponseBuilder responseBuilder = Response.Create(); From 7b951f97b313d8d64aee876f8284a711cfafda75 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 7 Mar 2021 18:40:15 +0100 Subject: [PATCH 03/21] fix build --- .../WireMock.Net.Tests/Serialization/MappingConverterTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs index 319870325..0cd3c1e9d 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs @@ -25,7 +25,7 @@ public void ToMappingModel() // Assign var request = Request.Create(); var response = Response.Create(); - var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 0, null, null, null, null); + var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 0, null, null, null, null, null); // Act var model = _sut.ToMappingModel(mapping); @@ -44,7 +44,7 @@ public void ToMappingModel_WithPriority_ReturnsPriority() // Assign var request = Request.Create(); 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 var model = _sut.ToMappingModel(mapping); From 50627ff001ab1e4b9d203d60a1d0e02fab537601 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Mar 2021 15:48:49 +0000 Subject: [PATCH 04/21] . --- .../Models/IWebhookRequest.cs | 5 +-- src/WireMock.Net/Http/HttpClientBuilder.cs | 2 +- src/WireMock.Net/Http/WebhookSender.cs | 41 +++++++++++++++++++ src/WireMock.Net/Models/Webhook.cs | 4 ++ src/WireMock.Net/Models/WebhookRequest.cs | 11 ++++- .../Owin/Mappers/OwinRequestMapper.cs | 2 +- src/WireMock.Net/Owin/WireMockMiddleware.cs | 22 ++++++++-- src/WireMock.Net/RequestMessage.cs | 2 +- .../Server/WireMockServer.Admin.cs | 8 ++-- .../Settings/HttpClientSettings.cs | 17 ++++++++ .../Settings/IHttpClientSettings.cs | 24 +++++++++++ .../Settings/IProxyAndRecordSettings.cs | 18 +------- .../Settings/ProxyAndRecordSettings.cs | 2 +- 13 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 src/WireMock.Net/Http/WebhookSender.cs create mode 100644 src/WireMock.Net/Settings/HttpClientSettings.cs create mode 100644 src/WireMock.Net/Settings/IHttpClientSettings.cs diff --git a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs index b5f678f69..784dc5f5b 100644 --- a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs +++ b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using WireMock.Admin.Mappings; using WireMock.Types; using WireMock.Util; @@ -8,9 +7,9 @@ namespace WireMock.Models public interface IWebhookRequest { /// - /// Gets or sets the Url. (Can be a string or a UrlModel) + /// Gets or sets the Url. /// - object Url { get; set; } + string Url { get; set; } /// /// The method diff --git a/src/WireMock.Net/Http/HttpClientBuilder.cs b/src/WireMock.Net/Http/HttpClientBuilder.cs index 00b2dea22..67dbebf14 100644 --- a/src/WireMock.Net/Http/HttpClientBuilder.cs +++ b/src/WireMock.Net/Http/HttpClientBuilder.cs @@ -7,7 +7,7 @@ namespace WireMock.Http { internal static class HttpClientBuilder { - public static HttpClient Build(IProxyAndRecordSettings settings) + public static HttpClient Build(IHttpClientSettings settings) { #if NETSTANDARD || NETCOREAPP3_1 || NET5_0 var handler = new HttpClientHandler diff --git a/src/WireMock.Net/Http/WebhookSender.cs b/src/WireMock.Net/Http/WebhookSender.cs new file mode 100644 index 000000000..3dadee182 --- /dev/null +++ b/src/WireMock.Net/Http/WebhookSender.cs @@ -0,0 +1,41 @@ +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.Types; +using WireMock.Validation; + +namespace WireMock.Http +{ + internal class WebhookSender + { + private const string ClientIp = "::1"; + + public Task SendAsync([NotNull] HttpClient client, [NotNull] IWebhookRequest request) + { + Check.NotNull(client, nameof(client)); + Check.NotNull(request, nameof(request)); + + // Create RequestMessage + var requestMessage = new RequestMessage( + new UrlDetails(request.Url), + request.Method, + ClientIp, + request.BodyData, + request.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); + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Models/Webhook.cs b/src/WireMock.Net/Models/Webhook.cs index 1facbe1b8..99fda81f7 100644 --- a/src/WireMock.Net/Models/Webhook.cs +++ b/src/WireMock.Net/Models/Webhook.cs @@ -1,7 +1,11 @@ namespace WireMock.Models { + /// + /// Webhook + /// public class Webhook : IWebhook { + /// public IWebhookRequest Request { get; set ; } } } diff --git a/src/WireMock.Net/Models/WebhookRequest.cs b/src/WireMock.Net/Models/WebhookRequest.cs index 904e0a725..823b49681 100644 --- a/src/WireMock.Net/Models/WebhookRequest.cs +++ b/src/WireMock.Net/Models/WebhookRequest.cs @@ -4,18 +4,27 @@ namespace WireMock.Models { + /// + /// WebhookRequest + /// public class WebhookRequest : IWebhookRequest { - public object Url { get; set; } + /// + public string Url { get; set; } + /// public string Method { get; set; } + /// public IDictionary> Headers { get; set; } + /// public IBodyData BodyData { get; set; } + /// public bool? UseTransformer { get; set; } + /// public TransformerType? TransformerType { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Owin/Mappers/OwinRequestMapper.cs b/src/WireMock.Net/Owin/Mappers/OwinRequestMapper.cs index 49f8b5389..79a485dcc 100644 --- a/src/WireMock.Net/Owin/Mappers/OwinRequestMapper.cs +++ b/src/WireMock.Net/Owin/Mappers/OwinRequestMapper.cs @@ -53,7 +53,7 @@ public async Task MapAsync(IRequest request, IWireMockMiddleware } } - BodyData body = null; + IBodyData body = null; if (request.Body != null && BodyParser.ShouldParseBody(method, options.AllowBodyForAllHttpMethods == true)) { var bodyParserSettings = new BodyParserSettings diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 17a4af188..9ac0e799a 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -9,7 +9,7 @@ using WireMock.Types; using WireMock.Validation; using WireMock.ResponseBuilders; -using WireMock.Proxy; +using System.Net.Http; #if !USE_ASPNETCORE using Microsoft.Owin; using IContext = Microsoft.Owin.IOwinContext; @@ -31,6 +31,7 @@ internal class WireMockMiddleware : OwinMiddleware private readonly IOwinRequestMapper _requestMapper; private readonly IOwinResponseMapper _responseMapper; private readonly IMappingMatcher _mappingMatcher; + private readonly HttpClient _httpClientForWebhook; #if !USE_ASPNETCORE public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next) @@ -155,9 +156,9 @@ private async Task InvokeInternal(IContext ctx) UpdateScenarioState(targetMapping); } - if (targetMapping.Webhook != null) + if (!targetMapping.IsAdminInterface && targetMapping.Webhook != null) { - // new ProxyHelper(null).SendAsync() + await SendToWebhookAsync(targetMapping).ConfigureAwait(false); } } catch (Exception ex) @@ -190,6 +191,21 @@ private async Task InvokeInternal(IContext ctx) await CompletedTask; } + private async Task SendToWebhookAsync(IMapping mapping) + { + var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.ProxyAndRecordSettings); + var webhookSender = new WebhookSender(); + + try + { + await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhook.Request).ConfigureAwait(false); + } + catch (Exception ex) + { + _options.Logger.Error("Sending message to Webhook Mapping '{mapping.Guid}' failed. Exception: {ex}", mapping.Guid, ex); + } + } + private void UpdateScenarioState(IMapping mapping) { var scenario = _options.Scenarios[mapping.Scenario]; diff --git a/src/WireMock.Net/RequestMessage.cs b/src/WireMock.Net/RequestMessage.cs index 85edef65b..fe3be8ea6 100644 --- a/src/WireMock.Net/RequestMessage.cs +++ b/src/WireMock.Net/RequestMessage.cs @@ -101,7 +101,7 @@ public class RequestMessage : IRequestMessage /// The BodyData. /// The headers. /// The cookies. - public RequestMessage([NotNull] UrlDetails urlDetails, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] BodyData bodyData = null, [CanBeNull] IDictionary headers = null, [CanBeNull] IDictionary cookies = null) + public RequestMessage([NotNull] UrlDetails urlDetails, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] IBodyData bodyData = null, [CanBeNull] IDictionary headers = null, [CanBeNull] IDictionary cookies = null) { Check.NotNull(urlDetails, nameof(urlDetails)); Check.NotNull(method, nameof(method)); diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 6d45814e5..6ee092089 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -473,7 +473,7 @@ private ResponseMessage MappingsPost(RequestMessage requestMessage) if (mappingModel.Webhook?.Request != null) { - respondProvider = respondProvider.WithWebhook(Map(mappingModel.Webhook)); + respondProvider = respondProvider.WithWebhook(MapAndInitWebhook(mappingModel.Webhook)); } respondProvider.RespondWith(responseBuilder); @@ -768,15 +768,15 @@ private IRequestBuilder InitRequestBuilder(RequestModel requestModel, bool pathO return requestBuilder; } - private IWebhook Map(WebhookModel model) + private IWebhook MapAndInitWebhook(WebhookModel model) { var webhook = new Webhook { Request = new WebhookRequest { - Url = model.Request.Url, + Url = model.Request.Url as string, Method = model.Request.Method, - Headers = model.Request.Headers != null ? model.Request.Headers.ToDictionary(x => x.Key, x => new WireMockList(x.Value)) : null, + Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList(x.Value)), UseTransformer = model.Request.UseTransformer } }; diff --git a/src/WireMock.Net/Settings/HttpClientSettings.cs b/src/WireMock.Net/Settings/HttpClientSettings.cs new file mode 100644 index 000000000..ab9e20500 --- /dev/null +++ b/src/WireMock.Net/Settings/HttpClientSettings.cs @@ -0,0 +1,17 @@ +namespace WireMock.Settings +{ + /// + /// HttpClientSettings + /// + public class HttpClientSettings : IHttpClientSettings + { + /// + public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; } + + /// + public IWebProxySettings WebProxySettings { get; set; } + + /// + public bool? AllowAutoRedirect { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Settings/IHttpClientSettings.cs b/src/WireMock.Net/Settings/IHttpClientSettings.cs new file mode 100644 index 000000000..82a9d9c2f --- /dev/null +++ b/src/WireMock.Net/Settings/IHttpClientSettings.cs @@ -0,0 +1,24 @@ +namespace WireMock.Settings +{ + /// + /// IHttpClientSettings + /// + public interface IHttpClientSettings + { + /// + /// The clientCertificate thumbprint or subject name fragment to use. + /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com"" + /// + string ClientX509Certificate2ThumbprintOrSubjectName { get; set; } + + /// + /// Defines the WebProxySettings. + /// + IWebProxySettings WebProxySettings { get; set; } + + /// + /// Proxy requests should follow redirection (30x). + /// + bool? AllowAutoRedirect { get; set; } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs index 2426cf2c7..2e10128e4 100644 --- a/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs +++ b/src/WireMock.Net/Settings/IProxyAndRecordSettings.cs @@ -5,7 +5,7 @@ namespace WireMock.Settings /// /// IProxyAndRecordSettings /// - public interface IProxyAndRecordSettings + public interface IProxyAndRecordSettings : IHttpClientSettings { /// /// The URL to proxy. @@ -29,12 +29,6 @@ public interface IProxyAndRecordSettings /// bool SaveMappingToFile { get; set; } - /// - /// The clientCertificate thumbprint or subject name fragment to use. - /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com"" - /// - string ClientX509Certificate2ThumbprintOrSubjectName { get; set; } - /// /// Defines a list from headers which will be excluded from the saved mappings. /// @@ -44,15 +38,5 @@ public interface IProxyAndRecordSettings /// Defines a list of cookies which will be excluded from the saved mappings. /// string[] ExcludedCookies { get; set; } - - /// - /// Defines the WebProxySettings. - /// - IWebProxySettings WebProxySettings { get; set; } - - /// - /// Proxy requests should follow redirection (30x). - /// - bool? AllowAutoRedirect { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs index cc797b3e1..42906b88f 100644 --- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs +++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs @@ -5,7 +5,7 @@ namespace WireMock.Settings /// /// ProxyAndRecordSettings /// - public class ProxyAndRecordSettings : IProxyAndRecordSettings + public class ProxyAndRecordSettings : HttpClientSettings, IProxyAndRecordSettings { /// /// The URL to proxy. From c7e7fbf72230d439bffb081696e714bce9dac7a2 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Mar 2021 16:14:16 +0000 Subject: [PATCH 05/21] . --- src/WireMock.Net/Owin/WireMockMiddleware.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 9ac0e799a..04b9466dd 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -10,6 +10,7 @@ using WireMock.Validation; using WireMock.ResponseBuilders; using System.Net.Http; +using WireMock.Settings; #if !USE_ASPNETCORE using Microsoft.Owin; using IContext = Microsoft.Owin.IOwinContext; @@ -193,7 +194,10 @@ private async Task InvokeInternal(IContext ctx) private async Task SendToWebhookAsync(IMapping mapping) { - var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.ProxyAndRecordSettings); + var httpClientSettings = new HttpClientSettings + { + }; + var httpClientForWebhook = HttpClientBuilder.Build(httpClientSettings); var webhookSender = new WebhookSender(); try From 5f7a483af92155d9fd61262b189c308eb768b1c7 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Mar 2021 16:25:17 +0000 Subject: [PATCH 06/21] fix --- src/WireMock.Net/Owin/WireMockMiddleware.cs | 5 +---- src/WireMock.Net/Settings/IWebhookSettings.cs | 9 +++++++++ .../Settings/IWireMockServerSettings.cs | 6 ++++++ .../Settings/ProxyAndRecordSettings.cs | 15 --------------- src/WireMock.Net/Settings/WebhookSettings.cs | 9 +++++++++ .../Settings/WireMockServerSettings.cs | 4 ++++ 6 files changed, 29 insertions(+), 19 deletions(-) create mode 100644 src/WireMock.Net/Settings/IWebhookSettings.cs create mode 100644 src/WireMock.Net/Settings/WebhookSettings.cs diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 04b9466dd..a28a1f339 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -194,10 +194,7 @@ private async Task InvokeInternal(IContext ctx) private async Task SendToWebhookAsync(IMapping mapping) { - var httpClientSettings = new HttpClientSettings - { - }; - var httpClientForWebhook = HttpClientBuilder.Build(httpClientSettings); + var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings()); var webhookSender = new WebhookSender(); try diff --git a/src/WireMock.Net/Settings/IWebhookSettings.cs b/src/WireMock.Net/Settings/IWebhookSettings.cs new file mode 100644 index 000000000..ea4a5777c --- /dev/null +++ b/src/WireMock.Net/Settings/IWebhookSettings.cs @@ -0,0 +1,9 @@ +namespace WireMock.Settings +{ + /// + /// IWebhookSettings + /// + public interface IWebhookSettings : IHttpClientSettings + { + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Settings/IWireMockServerSettings.cs b/src/WireMock.Net/Settings/IWireMockServerSettings.cs index ac511676d..d70e0411e 100644 --- a/src/WireMock.Net/Settings/IWireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/IWireMockServerSettings.cs @@ -186,5 +186,11 @@ public interface IWireMockServerSettings /// [PublicAPI] bool CustomCertificateDefined { get; } + + /// + /// Defines the global IWebhookSettingsto use + /// + [PublicAPI] + IWebhookSettings WebhookSettings { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs index 42906b88f..1e3f57f93 100644 --- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs +++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs @@ -32,13 +32,6 @@ public class ProxyAndRecordSettings : HttpClientSettings, IProxyAndRecordSetting [PublicAPI] public string SaveMappingForStatusCodePattern { get; set; } = "*"; - /// - /// The clientCertificate thumbprint or subject name fragment to use. - /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com"" - /// - [PublicAPI] - public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; } - /// [PublicAPI] public string[] ExcludedHeaders { get; set; } @@ -46,13 +39,5 @@ public class ProxyAndRecordSettings : HttpClientSettings, IProxyAndRecordSetting /// [PublicAPI] public string[] ExcludedCookies { get; set; } - - /// - [PublicAPI] - public IWebProxySettings WebProxySettings { get; set; } - - /// - [PublicAPI] - public bool? AllowAutoRedirect { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Settings/WebhookSettings.cs b/src/WireMock.Net/Settings/WebhookSettings.cs new file mode 100644 index 000000000..f0689d91e --- /dev/null +++ b/src/WireMock.Net/Settings/WebhookSettings.cs @@ -0,0 +1,9 @@ +namespace WireMock.Settings +{ + /// + /// WebhookSettings + /// + public class WebhookSettings : HttpClientSettings, IWebhookSettings + { + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Settings/WireMockServerSettings.cs b/src/WireMock.Net/Settings/WireMockServerSettings.cs index 1f655c4cf..1074a64cd 100644 --- a/src/WireMock.Net/Settings/WireMockServerSettings.cs +++ b/src/WireMock.Net/Settings/WireMockServerSettings.cs @@ -129,5 +129,9 @@ public class WireMockServerSettings : IWireMockServerSettings /// [PublicAPI] public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true; + + /// + [PublicAPI] + public IWebhookSettings WebhookSettings { get; set; } } } \ No newline at end of file From dab361c56ae5676f3ac83a7fa209196afd985c57 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Mar 2021 17:46:09 +0100 Subject: [PATCH 07/21] fix ut --- Directory.Build.props | 2 +- src/WireMock.Net/Serialization/MappingConverter.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index b42e7e767..d9a88a26d 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ - 1.4.6 + 1.4.7 See CHANGELOG.md https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png https://github.com/WireMock-Net/WireMock.Net diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index d80a9c958..98b1eba45 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -133,8 +133,11 @@ public MappingModel ToMappingModel(IMapping mapping) mappingModel.Response.Headers = MapHeaders(response.ResponseMessage.Headers); } - mappingModel.Response.UseTransformer = response.UseTransformer; - mappingModel.Response.TransformerType = mappingModel.Response.UseTransformer == true ? MapTransformerType(response.TransformerType) : null; + if (response.UseTransformer) + { + mappingModel.Response.UseTransformer = response.UseTransformer; + mappingModel.Response.TransformerType = response.TransformerType.ToString(); + } if (response.UseTransformerForBodyAsFile) { From e3cd1833485294ad546850d7ad5f68fa912e54b8 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Mar 2021 20:12:13 +0100 Subject: [PATCH 08/21] . --- .../Admin/Mappings/WebhookRequestModel.cs | 20 ++++------ .../Models/IWebhookRequest.cs | 13 ++----- src/WireMock.Net/Http/WebhookSender.cs | 1 + src/WireMock.Net/Models/WebhookRequest.cs | 6 --- .../Serialization/MappingConverter.cs | 33 +++++++++++++---- .../Server/WireMockServer.Admin.cs | 37 +++++++++++++------ 6 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs index 2bff90fa0..255083910 100644 --- a/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs @@ -8,25 +8,15 @@ namespace WireMock.Admin.Mappings public class WebhookRequestModel { /// - /// Gets or sets the Url. (Can be a string or a UrlModel) + /// Gets or sets the Url. /// - public object Url { get; set; } + public string Url { get; set; } /// /// The methods /// public string Method { get; set; } - /// - /// Use BodyMessage Transformer. - /// - public bool? UseTransformer { get; set; } - - /// - /// Gets the type of the transformer. - /// - public string TransformerType { get; set; } - /// /// Gets or sets the headers. /// @@ -35,7 +25,11 @@ public class WebhookRequestModel /// /// Gets or sets the body. /// - public BodyModel Body { get; set; } + public string Body { get; set; } + /// + /// Gets or sets the body (as JSON object). + /// + public object BodyAsJson { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs index 784dc5f5b..404dbaec4 100644 --- a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs +++ b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs @@ -4,6 +4,9 @@ namespace WireMock.Models { + /// + /// IWebhookRequest + /// public interface IWebhookRequest { /// @@ -25,15 +28,5 @@ public interface IWebhookRequest /// Gets or sets the body. /// IBodyData BodyData { get; set; } - - /// - /// Use BodyMessage Transformer. - /// - bool? UseTransformer { get; set; } - - /// - /// The type of the transformer. - /// - TransformerType? TransformerType { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Http/WebhookSender.cs b/src/WireMock.Net/Http/WebhookSender.cs index 3dadee182..a5990b8ef 100644 --- a/src/WireMock.Net/Http/WebhookSender.cs +++ b/src/WireMock.Net/Http/WebhookSender.cs @@ -7,6 +7,7 @@ using WireMock.Models; using WireMock.Settings; using WireMock.Types; +using WireMock.Util; using WireMock.Validation; namespace WireMock.Http diff --git a/src/WireMock.Net/Models/WebhookRequest.cs b/src/WireMock.Net/Models/WebhookRequest.cs index 823b49681..02f31f183 100644 --- a/src/WireMock.Net/Models/WebhookRequest.cs +++ b/src/WireMock.Net/Models/WebhookRequest.cs @@ -20,11 +20,5 @@ public class WebhookRequest : IWebhookRequest /// public IBodyData BodyData { get; set; } - - /// - public bool? UseTransformer { get; set; } - - /// - public TransformerType? TransformerType { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index 98b1eba45..ed184337c 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -196,22 +196,39 @@ public MappingModel ToMappingModel(IMapping mapping) private static WebhookModel MapWebhook(IWebhook webhook) { - return webhook?.Request == null ? null : new WebhookModel + 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 != null ? webhook.Request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()) : null, - UseTransformer = webhook.Request.UseTransformer, - TransformerType = webhook.Request.UseTransformer == true ? MapTransformerType(webhook.Request.TransformerType) : null + Headers = webhook.Request.Headers != null ? webhook.Request.Headers.ToDictionary(x => x.Key, x => x.Value.ToString()) : null } }; - } - private static string MapTransformerType(TransformerType? transformerType) - { - return transformerType?.ToString(); + 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; } private static WebProxyModel MapWebProxy(IWebProxySettings settings) diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 6ee092089..1ef51023a 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -473,7 +473,7 @@ private ResponseMessage MappingsPost(RequestMessage requestMessage) if (mappingModel.Webhook?.Request != null) { - respondProvider = respondProvider.WithWebhook(MapAndInitWebhook(mappingModel.Webhook)); + respondProvider = respondProvider.WithWebhook(MapWebhook(mappingModel.Webhook)); } respondProvider.RespondWith(responseBuilder); @@ -768,26 +768,41 @@ private IRequestBuilder InitRequestBuilder(RequestModel requestModel, bool pathO return requestBuilder; } - private IWebhook MapAndInitWebhook(WebhookModel model) + private IWebhook MapWebhook(WebhookModel model) { - var webhook = new Webhook + var webhook = new Webhook { Request = new WebhookRequest { - Url = model.Request.Url as string, + Url = model.Request.Url, Method = model.Request.Method, - Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList(x.Value)), - UseTransformer = model.Request.UseTransformer + Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList(x.Value)) ?? new Dictionary>() } }; - if (model.Request.UseTransformer == true) + IEnumerable contentTypeHeader = null; + if (webhook.Request.Headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase))) { - if (!Enum.TryParse(model.Request.TransformerType, out var transformerType)) + 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 { - transformerType = TransformerType.Handlebars; - } - webhook.Request.TransformerType = transformerType; + 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; From 327f0de885ca34f52516945c94990f5a3f1af203 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Mon, 8 Mar 2021 20:20:35 +0100 Subject: [PATCH 09/21] more tests --- .../Serialization/MappingConverterTests.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs index 0cd3c1e9d..c839c6f29 100644 --- a/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs +++ b/test/WireMock.Net.Tests/Serialization/MappingConverterTests.cs @@ -1,9 +1,13 @@ using System; +using System.Collections.Generic; using FluentAssertions; +using WireMock.Models; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; using WireMock.Serialization; using WireMock.Settings; +using WireMock.Types; +using WireMock.Util; using Xunit; namespace WireMock.Net.Tests.Serialization @@ -25,7 +29,26 @@ public void ToMappingModel() // Assign var request = Request.Create(); var response = Response.Create(); - var mapping = new Mapping(Guid.NewGuid(), "", null, _settings, request, response, 0, null, null, null, null, null); + var webhook = new Webhook + { + Request = new WebhookRequest + { + Url = "https://test.com", + Headers = new Dictionary> + { + { "Single", new WireMockList("x") }, + { "Multi", new WireMockList("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 var model = _sut.ToMappingModel(mapping); @@ -33,9 +56,16 @@ public void ToMappingModel() // Assert model.Should().NotBeNull(); model.Priority.Should().BeNull(); + model.Response.BodyAsJsonIndented.Should().BeNull(); model.Response.UseTransformer.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] From 552b0c2f8aaed3143854b300865bf95386d6d28a Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 08:16:31 +0000 Subject: [PATCH 10/21] t --- .../Admin/Mappings/WebhookRequestModel.cs | 10 ++ .../Models/IWebhook.cs | 8 +- .../Models/IWebhookRequest.cs | 12 +- src/WireMock.Net/Http/WebhookSender.cs | 54 ++++++++- src/WireMock.Net/Models/WebhookRequest.cs | 48 ++++---- src/WireMock.Net/Owin/WireMockMiddleware.cs | 10 +- .../Serialization/MappingConverter.cs | 39 +------ .../Serialization/WebhookMapper.cs | 103 ++++++++++++++++++ .../Server/WireMockServer.Admin.cs | 41 +------ src/WireMock.Net/Transformers/Transformer.cs | 5 +- 10 files changed, 218 insertions(+), 112 deletions(-) create mode 100644 src/WireMock.Net/Serialization/WebhookMapper.cs diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs index 255083910..05e0f4e38 100644 --- a/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs +++ b/src/WireMock.Net.Abstractions/Admin/Mappings/WebhookRequestModel.cs @@ -31,5 +31,15 @@ public class WebhookRequestModel /// Gets or sets the body (as JSON object). /// public object BodyAsJson { get; set; } + + /// + /// Use ResponseMessage Transformer. + /// + public bool? UseTransformer { get; set; } + + /// + /// Gets the type of the transformer. + /// + public string TransformerType { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Models/IWebhook.cs b/src/WireMock.Net.Abstractions/Models/IWebhook.cs index 8eed99989..67174d9eb 100644 --- a/src/WireMock.Net.Abstractions/Models/IWebhook.cs +++ b/src/WireMock.Net.Abstractions/Models/IWebhook.cs @@ -1,7 +1,13 @@ namespace WireMock.Models { + /// + /// IWebhook + /// public interface IWebhook - { + { + /// + /// Request + /// IWebhookRequest Request { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs index 404dbaec4..6ff36a461 100644 --- a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs +++ b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs @@ -27,6 +27,16 @@ public interface IWebhookRequest /// /// Gets or sets the body. /// - IBodyData BodyData { get; set; } + IBodyData BodyData { get; set; } + + /// + /// Use ResponseMessage Transformer. + /// + bool? UseTransformer { get; set; } + + /// + /// The transformer type. + /// + TransformerType TransformerType { get; set; } } } \ No newline at end of file diff --git a/src/WireMock.Net/Http/WebhookSender.cs b/src/WireMock.Net/Http/WebhookSender.cs index a5990b8ef..40339fe8b 100644 --- a/src/WireMock.Net/Http/WebhookSender.cs +++ b/src/WireMock.Net/Http/WebhookSender.cs @@ -6,6 +6,9 @@ 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; @@ -16,18 +19,63 @@ internal class WebhookSender { private const string ClientIp = "::1"; - public Task SendAsync([NotNull] HttpClient client, [NotNull] IWebhookRequest request) + private readonly IWireMockServerSettings _settings; + + public WebhookSender(IWireMockServerSettings settings) + { + _settings = settings ?? throw new ArgumentNullException(nameof(settings)); + } + + public Task SendAsync([NotNull] HttpClient client, [NotNull] IWebhookRequest request, [NotNull] RequestMessage originalRequestMessage) { Check.NotNull(client, nameof(client)); Check.NotNull(request, nameof(request)); + IBodyData bodyData; + IDictionary> 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."); + } + + var responseMessage = new ResponseMessage + { + BodyData = request.BodyData, + Headers = request.Headers + }; + + var transformedResponseMessage = responseMessageTransformer.Transform(originalRequestMessage, responseMessage, false); + bodyData = transformedResponseMessage.BodyData; + headers = transformedResponseMessage.Headers; + } + else + { + bodyData = request.BodyData; + headers = request.Headers; + } + // Create RequestMessage var requestMessage = new RequestMessage( new UrlDetails(request.Url), request.Method, ClientIp, - request.BodyData, - request.Headers.ToDictionary(x => x.Key, x => x.Value.ToArray())) + bodyData, + headers.ToDictionary(x => x.Key, x => x.Value.ToArray())) { DateTime = DateTime.UtcNow }; diff --git a/src/WireMock.Net/Models/WebhookRequest.cs b/src/WireMock.Net/Models/WebhookRequest.cs index 02f31f183..077d84faa 100644 --- a/src/WireMock.Net/Models/WebhookRequest.cs +++ b/src/WireMock.Net/Models/WebhookRequest.cs @@ -1,24 +1,30 @@ -using System.Collections.Generic; -using WireMock.Types; -using WireMock.Util; - -namespace WireMock.Models -{ +using System.Collections.Generic; +using WireMock.Types; +using WireMock.Util; + +namespace WireMock.Models +{ /// /// WebhookRequest - /// - public class WebhookRequest : IWebhookRequest - { - /// - public string Url { get; set; } - - /// - public string Method { get; set; } - - /// - public IDictionary> Headers { get; set; } - - /// - public IBodyData BodyData { get; set; } - } + /// + public class WebhookRequest : IWebhookRequest + { + /// + public string Url { get; set; } + + /// + public string Method { get; set; } + + /// + public IDictionary> Headers { get; set; } + + /// + public IBodyData BodyData { get; set; } + + /// + public bool? UseTransformer { get; set; } + + /// + public TransformerType TransformerType { get; set; } + } } \ No newline at end of file diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index a28a1f339..2c3e98a13 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -159,7 +159,7 @@ private async Task InvokeInternal(IContext ctx) if (!targetMapping.IsAdminInterface && targetMapping.Webhook != null) { - await SendToWebhookAsync(targetMapping).ConfigureAwait(false); + await SendToWebhookAsync(targetMapping, request).ConfigureAwait(false); } } catch (Exception ex) @@ -192,18 +192,18 @@ private async Task InvokeInternal(IContext ctx) await CompletedTask; } - private async Task SendToWebhookAsync(IMapping mapping) + private async Task SendToWebhookAsync(IMapping mapping, RequestMessage requestMessage) { var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings()); - var webhookSender = new WebhookSender(); + var webhookSender = new WebhookSender(mapping.Settings); try { - await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhook.Request).ConfigureAwait(false); + await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhook.Request, requestMessage).ConfigureAwait(false); } catch (Exception ex) { - _options.Logger.Error("Sending message to Webhook Mapping '{mapping.Guid}' failed. Exception: {ex}", mapping.Guid, ex); + _options.Logger.Error($"Sending message to Webhook Mapping '{mapping.Guid}' failed. Exception: {ex}"); } } diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index ed184337c..3e30b6cb7 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -86,7 +86,7 @@ public MappingModel ToMappingModel(IMapping mapping) { Delay = (int?)response.Delay?.TotalMilliseconds }, - Webhook = MapWebhook(mapping.Webhook) + Webhook = WebhookMapper.Map(mapping.Webhook) }; if (bodyMatcher?.Matchers != null) @@ -194,42 +194,7 @@ public MappingModel ToMappingModel(IMapping mapping) return mappingModel; } - private static WebhookModel MapWebhook(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 != null ? webhook.Request.Headers.ToDictionary(x => x.Key, x => x.Value.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; - } + private static WebProxyModel MapWebProxy(IWebProxySettings settings) { diff --git a/src/WireMock.Net/Serialization/WebhookMapper.cs b/src/WireMock.Net/Serialization/WebhookMapper.cs new file mode 100644 index 000000000..08f19f144 --- /dev/null +++ b/src/WireMock.Net/Serialization/WebhookMapper.cs @@ -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(x.Value)) ?? new Dictionary>() + } + }; + + if (model.Request.UseTransformer == true) + { + webhook.Request.UseTransformer = true; + if (!Enum.TryParse(model.Request.TransformerType, out var transformerType)) + { + transformerType = TransformerType.Handlebars; + } + webhook.Request.TransformerType = transformerType; + } + + IEnumerable 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; + } + } +} \ No newline at end of file diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index 1ef51023a..ce76ed370 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -16,7 +16,6 @@ using WireMock.Logging; using WireMock.Matchers; using WireMock.Matchers.Request; -using WireMock.Models; using WireMock.Proxy; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; @@ -473,7 +472,7 @@ private ResponseMessage MappingsPost(RequestMessage requestMessage) if (mappingModel.Webhook?.Request != null) { - respondProvider = respondProvider.WithWebhook(MapWebhook(mappingModel.Webhook)); + respondProvider = respondProvider.WithWebhook(WebhookMapper.Map(mappingModel.Webhook)); } respondProvider.RespondWith(responseBuilder); @@ -768,45 +767,7 @@ private IRequestBuilder InitRequestBuilder(RequestModel requestModel, bool pathO return requestBuilder; } - private IWebhook MapWebhook(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(x.Value)) ?? new Dictionary>() - } - }; - IEnumerable 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; - } private IResponseBuilder InitResponseBuilder(ResponseModel responseModel) { diff --git a/src/WireMock.Net/Transformers/Transformer.cs b/src/WireMock.Net/Transformers/Transformer.cs index 0acedc2d2..46a32843c 100644 --- a/src/WireMock.Net/Transformers/Transformer.cs +++ b/src/WireMock.Net/Transformers/Transformer.cs @@ -6,7 +6,6 @@ using Newtonsoft.Json.Linq; using WireMock.Types; using WireMock.Util; -using WireMock.Validation; namespace WireMock.Transformers.Handlebars { @@ -16,9 +15,7 @@ internal class Transformer : ITransformer public Transformer([NotNull] ITransformerContextFactory factory) { - Check.NotNull(factory, nameof(factory)); - - _factory = factory; + _factory = factory ?? throw new ArgumentNullException(nameof(factory)); } public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile) From 63ed0138ab04d5b4a917bfbb8d30154d92573894 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 10:11:18 +0000 Subject: [PATCH 11/21] sc --- src/WireMock.Net/Mapping.cs | 1 + src/WireMock.Net/Models/BodyData.cs | 2 +- src/WireMock.Net/Models/Webhook.cs | 18 +++++++++--------- src/WireMock.Net/Owin/WireMockMiddleware.cs | 1 - .../Server/IRespondWithAProvider.cs | 5 +++++ .../Server/RespondWithAProvider.cs | 1 + 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/WireMock.Net/Mapping.cs b/src/WireMock.Net/Mapping.cs index 49d65920f..031c027a3 100644 --- a/src/WireMock.Net/Mapping.cs +++ b/src/WireMock.Net/Mapping.cs @@ -73,6 +73,7 @@ public class Mapping : IMapping /// State in which the current mapping can occur. [Optional] /// The next state which will occur after the current mapping execution. [Optional] /// Only when the current state is executed this number, the next state which will occur. [Optional] + /// The Webhook. [Optional] public Mapping( Guid guid, [CanBeNull] string title, diff --git a/src/WireMock.Net/Models/BodyData.cs b/src/WireMock.Net/Models/BodyData.cs index 1e7d54e00..494465e50 100644 --- a/src/WireMock.Net/Models/BodyData.cs +++ b/src/WireMock.Net/Models/BodyData.cs @@ -6,7 +6,7 @@ namespace WireMock.Util /// /// BodyData /// - public class BodyData : IBodyData + internal class BodyData : IBodyData { /// public Encoding Encoding { get; set; } diff --git a/src/WireMock.Net/Models/Webhook.cs b/src/WireMock.Net/Models/Webhook.cs index 99fda81f7..6178b4e58 100644 --- a/src/WireMock.Net/Models/Webhook.cs +++ b/src/WireMock.Net/Models/Webhook.cs @@ -1,11 +1,11 @@ -namespace WireMock.Models -{ +namespace WireMock.Models +{ /// /// Webhook - /// - public class Webhook : IWebhook - { - /// - public IWebhookRequest Request { get; set ; } - } -} + /// + public class Webhook : IWebhook + { + /// + public IWebhookRequest Request { get; set ; } + } +} diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index 2c3e98a13..a2ebb2977 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -32,7 +32,6 @@ internal class WireMockMiddleware : OwinMiddleware private readonly IOwinRequestMapper _requestMapper; private readonly IOwinResponseMapper _responseMapper; private readonly IMappingMatcher _mappingMatcher; - private readonly HttpClient _httpClientForWebhook; #if !USE_ASPNETCORE public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next) diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index dae748146..3659eabab 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -100,6 +100,11 @@ public interface IRespondWithAProvider /// The . IRespondWithAProvider WillSetStateTo(int state, int? times = 1); + /// + /// Add a Wehbook to exceute after the response has been generated. + /// + /// The Webhook + /// The . IRespondWithAProvider WithWebhook(IWebhook webhook); } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index 7eee294ab..556852d0b 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -145,6 +145,7 @@ public IRespondWithAProvider WillSetStateTo(int state, int? times = 1) return WillSetStateTo(state.ToString(), times); } + /// public IRespondWithAProvider WithWebhook(IWebhook webhook) { Webhook = webhook; From 38c4d3dc6389f964ae743be432aebdee39c6368d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 10:14:25 +0000 Subject: [PATCH 12/21] 2 --- src/WireMock.Net/Models/Webhook.cs | 4 ++-- src/WireMock.Net/Serialization/MappingConverter.cs | 3 --- src/WireMock.Net/Server/WireMockServer.Admin.cs | 2 -- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/src/WireMock.Net/Models/Webhook.cs b/src/WireMock.Net/Models/Webhook.cs index 6178b4e58..7e2fef8b9 100644 --- a/src/WireMock.Net/Models/Webhook.cs +++ b/src/WireMock.Net/Models/Webhook.cs @@ -6,6 +6,6 @@ public class Webhook : IWebhook { /// - public IWebhookRequest Request { get; set ; } + public IWebhookRequest Request { get; set; } } -} +} \ No newline at end of file diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs index 3e30b6cb7..cc08c1614 100644 --- a/src/WireMock.Net/Serialization/MappingConverter.cs +++ b/src/WireMock.Net/Serialization/MappingConverter.cs @@ -2,7 +2,6 @@ using System.Linq; using WireMock.Admin.Mappings; using WireMock.Matchers.Request; -using WireMock.Models; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; using WireMock.Settings; @@ -194,8 +193,6 @@ public MappingModel ToMappingModel(IMapping mapping) return mappingModel; } - - private static WebProxyModel MapWebProxy(IWebProxySettings settings) { return settings != null ? new WebProxyModel diff --git a/src/WireMock.Net/Server/WireMockServer.Admin.cs b/src/WireMock.Net/Server/WireMockServer.Admin.cs index ce76ed370..9087a4ed8 100644 --- a/src/WireMock.Net/Server/WireMockServer.Admin.cs +++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs @@ -767,8 +767,6 @@ private IRequestBuilder InitRequestBuilder(RequestModel requestModel, bool pathO return requestBuilder; } - - private IResponseBuilder InitResponseBuilder(ResponseModel responseModel) { IResponseBuilder responseBuilder = Response.Create(); From 9dd85eafe08d28d6a2e73ddf929c3c60f31d1deb Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 12:11:14 +0000 Subject: [PATCH 13/21] wh test --- src/WireMock.Net/Http/WebhookSender.cs | 3 +- .../WireMockServer.Webhook.cs | 66 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 test/WireMock.Net.Tests/WireMockServer.Webhook.cs diff --git a/src/WireMock.Net/Http/WebhookSender.cs b/src/WireMock.Net/Http/WebhookSender.cs index 40339fe8b..94910d3ab 100644 --- a/src/WireMock.Net/Http/WebhookSender.cs +++ b/src/WireMock.Net/Http/WebhookSender.cs @@ -75,7 +75,8 @@ public Task SendAsync([NotNull] HttpClient client, [NotNull request.Method, ClientIp, bodyData, - headers.ToDictionary(x => x.Key, x => x.Value.ToArray())) + headers?.ToDictionary(x => x.Key, x => x.Value.ToArray()) + ) { DateTime = DateTime.UtcNow }; diff --git a/test/WireMock.Net.Tests/WireMockServer.Webhook.cs b/test/WireMock.Net.Tests/WireMockServer.Webhook.cs new file mode 100644 index 000000000..5c590ba85 --- /dev/null +++ b/test/WireMock.Net.Tests/WireMockServer.Webhook.cs @@ -0,0 +1,66 @@ +using FluentAssertions; +using NFluent; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading.Tasks; +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(); + } + } +} \ No newline at end of file From 1ca6561492a36b3525d4020c10a0986165da6a65 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 13:37:40 +0000 Subject: [PATCH 14/21] x --- .../Models/IWebhookRequest.cs | 10 +-- src/WireMock.Net/Models/Webhook.cs | 6 +- .../Server/IRespondWithAProvider.cs | 44 ++++++++++- .../Server/RespondWithAProvider.cs | 77 ++++++++++++++++++- 4 files changed, 128 insertions(+), 9 deletions(-) diff --git a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs index 6ff36a461..bfd4f07fd 100644 --- a/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs +++ b/src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs @@ -10,27 +10,27 @@ namespace WireMock.Models public interface IWebhookRequest { /// - /// Gets or sets the Url. + /// The Webhook Url. /// string Url { get; set; } /// - /// The method + /// The method to use. /// string Method { get; set; } /// - /// Gets the headers. + /// The Headers to send. /// IDictionary> Headers { get; } /// - /// Gets or sets the body. + /// The body to send. /// IBodyData BodyData { get; set; } /// - /// Use ResponseMessage Transformer. + /// Use Transformer. /// bool? UseTransformer { get; set; } diff --git a/src/WireMock.Net/Models/Webhook.cs b/src/WireMock.Net/Models/Webhook.cs index 7e2fef8b9..b5583d24a 100644 --- a/src/WireMock.Net/Models/Webhook.cs +++ b/src/WireMock.Net/Models/Webhook.cs @@ -1,4 +1,8 @@ -namespace WireMock.Models +using System.Collections.Generic; +using JetBrains.Annotations; +using WireMock.Types; + +namespace WireMock.Models { /// /// Webhook diff --git a/src/WireMock.Net/Server/IRespondWithAProvider.cs b/src/WireMock.Net/Server/IRespondWithAProvider.cs index 3659eabab..560b3562c 100644 --- a/src/WireMock.Net/Server/IRespondWithAProvider.cs +++ b/src/WireMock.Net/Server/IRespondWithAProvider.cs @@ -1,7 +1,9 @@ using System; -using WireMock.Admin.Mappings; +using System.Collections.Generic; +using JetBrains.Annotations; using WireMock.Models; using WireMock.ResponseProviders; +using WireMock.Types; namespace WireMock.Server { @@ -101,10 +103,48 @@ public interface IRespondWithAProvider IRespondWithAProvider WillSetStateTo(int state, int? times = 1); /// - /// Add a Wehbook to exceute after the response has been generated. + /// Add a Webbook to call after the response has been generated. /// /// The Webhook /// The . IRespondWithAProvider WithWebhook(IWebhook webhook); + + /// + /// Add a Webbook to call after the response has been generated. + /// + /// The Webhook Url + /// The method to use. [optional] + /// The Headers to send. [optional] + /// The body (as string) to send. [optional] + /// Use Transformer. [optional] + /// The transformer type. [optional] + /// The . + IRespondWithAProvider WithWebhook( + [NotNull] string url, + [CanBeNull] string method = "post", + [CanBeNull] IDictionary> headers = null, + [CanBeNull] string body = null, + bool useTransformer = true, + TransformerType transformerType = TransformerType.Handlebars + ); + + /// + /// Add a Webbook to call after the response has been generated. + /// + /// The Webhook Url + /// The method to use. [optional] + /// The Headers to send. [optional] + /// The body (as json) to send. [optional] + /// Use Transformer. [optional] + /// The transformer type. [optional] + /// The . + IRespondWithAProvider WithWebhook( + [NotNull] string url, + [CanBeNull] string method = "post", + [CanBeNull] IDictionary> headers = null, + [CanBeNull] object body = null, + bool useTransformer = true, + TransformerType transformerType = TransformerType.Handlebars + ); } } \ No newline at end of file diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index 556852d0b..828fc3033 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -1,12 +1,18 @@ // 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. using System; +using JetBrains.Annotations; +using System.Collections.Generic; using WireMock.Admin.Mappings; using WireMock.Matchers.Request; using WireMock.Models; using WireMock.ResponseProviders; using WireMock.Settings; - +using WireMock.Types; +using WireMock.Util; +using System.Reflection; +using WireMock.RequestBuilders; + namespace WireMock.Server { /// @@ -149,7 +155,76 @@ public IRespondWithAProvider WillSetStateTo(int state, int? times = 1) public IRespondWithAProvider WithWebhook(IWebhook webhook) { Webhook = webhook; + + return this; + } + + /// + public IRespondWithAProvider WithWebhook( + [NotNull] string url, + [CanBeNull] string method = "post", + [CanBeNull] IDictionary> 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; + } + + /// + public IRespondWithAProvider WithWebhook( + [NotNull] string url, + [CanBeNull] string method = "post", + [CanBeNull] IDictionary> 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 = "post", + IDictionary> headers = null, + bool useTransformer = true, + TransformerType transformerType = TransformerType.Handlebars) + { + return new Webhook + { + Request = new WebhookRequest + { + Url = url, + Method = method ?? "post", + Headers = headers, + UseTransformer = useTransformer, + TransformerType = transformerType + } + }; } } } \ No newline at end of file From f7b3f63cf53d124743d652fec3ae54d70924cc2a Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 15:33:20 +0000 Subject: [PATCH 15/21] . --- test/WireMock.Net.Tests/WireMockServer.Webhook.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/test/WireMock.Net.Tests/WireMockServer.Webhook.cs b/test/WireMock.Net.Tests/WireMockServer.Webhook.cs index 5c590ba85..778a2b5d6 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Webhook.cs +++ b/test/WireMock.Net.Tests/WireMockServer.Webhook.cs @@ -1,10 +1,8 @@ -using FluentAssertions; -using NFluent; -using System; -using System.Linq; +using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; +using FluentAssertions; using WireMock.Models; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; From 0215cacf58168c2f8a17f5e45df09542d593f3fb Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 15:45:47 +0000 Subject: [PATCH 16/21] f --- .../Server/RespondWithAProvider.cs | 15 ++++----- src/WireMock.Net/Transformers/Transformer.cs | 23 ++++++------- ...hook.cs => WireMockServer.WebhookTests.cs} | 33 +++++++++++++++++++ 3 files changed, 51 insertions(+), 20 deletions(-) rename test/WireMock.Net.Tests/{WireMockServer.Webhook.cs => WireMockServer.WebhookTests.cs} (61%) diff --git a/src/WireMock.Net/Server/RespondWithAProvider.cs b/src/WireMock.Net/Server/RespondWithAProvider.cs index 828fc3033..59837f8b7 100644 --- a/src/WireMock.Net/Server/RespondWithAProvider.cs +++ b/src/WireMock.Net/Server/RespondWithAProvider.cs @@ -1,17 +1,14 @@ // 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. using System; -using JetBrains.Annotations; using System.Collections.Generic; -using WireMock.Admin.Mappings; +using JetBrains.Annotations; using WireMock.Matchers.Request; using WireMock.Models; using WireMock.ResponseProviders; using WireMock.Settings; using WireMock.Types; using WireMock.Util; -using System.Reflection; -using WireMock.RequestBuilders; namespace WireMock.Server { @@ -183,7 +180,7 @@ public IRespondWithAProvider WithWebhook( return this; } - /// + /// public IRespondWithAProvider WithWebhook( [NotNull] string url, [CanBeNull] string method = "post", @@ -209,10 +206,10 @@ public IRespondWithAProvider WithWebhook( private IWebhook InitWebhook( string url, - string method = "post", - IDictionary> headers = null, - bool useTransformer = true, - TransformerType transformerType = TransformerType.Handlebars) + string method, + IDictionary> headers, + bool useTransformer, + TransformerType transformerType) { return new Webhook { diff --git a/src/WireMock.Net/Transformers/Transformer.cs b/src/WireMock.Net/Transformers/Transformer.cs index 46a32843c..0eded1e17 100644 --- a/src/WireMock.Net/Transformers/Transformer.cs +++ b/src/WireMock.Net/Transformers/Transformer.cs @@ -46,19 +46,20 @@ public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage responseMessage.FaultPercentage = original.FaultPercentage; // Headers - var newHeaders = new Dictionary>(); - 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(templateHeaderValues)); + if (original.Headers != null) + { + var newHeaders = new Dictionary>(); + 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(templateHeaderValues)); + } + + responseMessage.Headers = newHeaders; } - responseMessage.Headers = newHeaders; - switch (original.StatusCode) { case int statusCodeAsInteger: diff --git a/test/WireMock.Net.Tests/WireMockServer.Webhook.cs b/test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs similarity index 61% rename from test/WireMock.Net.Tests/WireMockServer.Webhook.cs rename to test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs index 778a2b5d6..eff53efd1 100644 --- a/test/WireMock.Net.Tests/WireMockServer.Webhook.cs +++ b/test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs @@ -60,5 +60,38 @@ public async Task WireMockServer_WithWebhook_Should_Send_Message_To_Webhook() server.Dispose(); serverReceivingTheWebhook.Dispose(); } + + [Fact] + public async Task WireMockServer_WithWebhookArgs_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(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); + + server.Dispose(); + serverReceivingTheWebhook.Dispose(); + } } } \ No newline at end of file From 67c57a785fcebdade82928d290637d5d8623d768 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Tue, 9 Mar 2021 16:27:20 +0000 Subject: [PATCH 17/21] . --- src/WireMock.Net/Http/WebhookSender.cs | 14 +- src/WireMock.Net/Owin/WireMockMiddleware.cs | 7 +- src/WireMock.Net/Transformers/ITransformer.cs | 8 +- src/WireMock.Net/Transformers/Transformer.cs | 144 +++++++++++------- 4 files changed, 106 insertions(+), 67 deletions(-) diff --git a/src/WireMock.Net/Http/WebhookSender.cs b/src/WireMock.Net/Http/WebhookSender.cs index 94910d3ab..bcd065983 100644 --- a/src/WireMock.Net/Http/WebhookSender.cs +++ b/src/WireMock.Net/Http/WebhookSender.cs @@ -26,10 +26,12 @@ public WebhookSender(IWireMockServerSettings settings) _settings = settings ?? throw new ArgumentNullException(nameof(settings)); } - public Task SendAsync([NotNull] HttpClient client, [NotNull] IWebhookRequest request, [NotNull] RequestMessage originalRequestMessage) + public Task 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> headers; @@ -53,15 +55,7 @@ public Task SendAsync([NotNull] HttpClient client, [NotNull throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported."); } - var responseMessage = new ResponseMessage - { - BodyData = request.BodyData, - Headers = request.Headers - }; - - var transformedResponseMessage = responseMessageTransformer.Transform(originalRequestMessage, responseMessage, false); - bodyData = transformedResponseMessage.BodyData; - headers = transformedResponseMessage.Headers; + (bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers); } else { diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs index a2ebb2977..e5b9eb925 100644 --- a/src/WireMock.Net/Owin/WireMockMiddleware.cs +++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs @@ -9,7 +9,6 @@ using WireMock.Types; using WireMock.Validation; using WireMock.ResponseBuilders; -using System.Net.Http; using WireMock.Settings; #if !USE_ASPNETCORE using Microsoft.Owin; @@ -158,7 +157,7 @@ private async Task InvokeInternal(IContext ctx) if (!targetMapping.IsAdminInterface && targetMapping.Webhook != null) { - await SendToWebhookAsync(targetMapping, request).ConfigureAwait(false); + await SendToWebhookAsync(targetMapping, request, response).ConfigureAwait(false); } } catch (Exception ex) @@ -191,14 +190,14 @@ private async Task InvokeInternal(IContext ctx) await CompletedTask; } - private async Task SendToWebhookAsync(IMapping mapping, RequestMessage requestMessage) + 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, requestMessage).ConfigureAwait(false); + await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhook.Request, request, response).ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/WireMock.Net/Transformers/ITransformer.cs b/src/WireMock.Net/Transformers/ITransformer.cs index e6e4d5408..ebcd3336a 100644 --- a/src/WireMock.Net/Transformers/ITransformer.cs +++ b/src/WireMock.Net/Transformers/ITransformer.cs @@ -1,7 +1,13 @@ -namespace WireMock.Transformers +using System.Collections.Generic; +using WireMock.Types; +using WireMock.Util; + +namespace WireMock.Transformers { interface ITransformer { ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile); + + (IBodyData BodyData, IDictionary> Headers) Transform(RequestMessage originalRequestMessage, ResponseMessage originalResponseMessage, IBodyData bodyData, IDictionary> headers); } } \ No newline at end of file diff --git a/src/WireMock.Net/Transformers/Transformer.cs b/src/WireMock.Net/Transformers/Transformer.cs index 0eded1e17..04e112c0e 100644 --- a/src/WireMock.Net/Transformers/Transformer.cs +++ b/src/WireMock.Net/Transformers/Transformer.cs @@ -18,47 +18,50 @@ public Transformer([NotNull] ITransformerContextFactory factory) _factory = factory ?? throw new ArgumentNullException(nameof(factory)); } + public (IBodyData BodyData, IDictionary> Headers) Transform(RequestMessage originalRequestMessage, ResponseMessage originalResponseMessage, IBodyData bodyData, IDictionary> 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) { - var handlebarsContext = _factory.Create(); + var transformerContext = _factory.Create(); var responseMessage = new ResponseMessage(); - var model = new { request = requestMessage }; - - switch (original.BodyData?.DetectedBodyType) - { - case BodyType.Json: - TransformBodyAsJson(handlebarsContext, model, original, responseMessage); - break; - - case BodyType.File: - TransformBodyAsFile(handlebarsContext, model, original, responseMessage, useTransformerForBodyAsFile); - break; + var model = new + { + request = requestMessage + }; - case BodyType.String: - responseMessage.BodyOriginal = original.BodyData.BodyAsString; - TransformBodyAsString(handlebarsContext, model, original, responseMessage); - break; + if (original.BodyData?.DetectedBodyType != null) + { + responseMessage.BodyData = TransformBodyData(transformerContext, model, original.BodyData, useTransformerForBodyAsFile); + + if (original.BodyData.DetectedBodyType == BodyType.String) + { + responseMessage.BodyOriginal = original.BodyData.BodyAsString; + } } responseMessage.FaultType = original.FaultType; responseMessage.FaultPercentage = original.FaultPercentage; - // Headers - if (original.Headers != null) - { - var newHeaders = new Dictionary>(); - 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(templateHeaderValues)); - } - - responseMessage.Headers = newHeaders; - } + responseMessage.Headers = TransformHeaders(transformerContext, model, original.Headers); switch (original.StatusCode) { @@ -67,17 +70,54 @@ public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage break; case string statusCodeAsString: - responseMessage.StatusCode = handlebarsContext.ParseAndRender(statusCodeAsString, model); + responseMessage.StatusCode = transformerContext.ParseAndRender(statusCodeAsString, model); break; } 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> TransformHeaders(ITransformerContext transformerContext, object model, IDictionary> original) + { + if (original == null) + { + return null; + } + + var newHeaders = new Dictionary>(); + 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(templateHeaderValues)); + } + + return newHeaders; + } + + private static IBodyData TransformBodyAsJson(ITransformerContext handlebarsContext, object model, IBodyData original) { JToken jToken; - switch (original.BodyData.BodyAsJson) + switch (original.BodyAsJson) { case JObject bodyAsJObject: jToken = bodyAsJObject.DeepClone(); @@ -94,23 +134,23 @@ private static void TransformBodyAsJson(ITransformerContext handlebarsContext, o break; default: - jToken = JObject.FromObject(original.BodyData.BodyAsJson); + jToken = JObject.FromObject(original.BodyAsJson); WalkNode(handlebarsContext, jToken, model); break; } - responseMessage.BodyData = new BodyData + return new BodyData { - Encoding = original.BodyData.Encoding, - DetectedBodyType = original.BodyData.DetectedBodyType, - DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, + Encoding = original.Encoding, + DetectedBodyType = original.DetectedBodyType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, BodyAsJson = jToken }; } 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)) { @@ -184,27 +224,27 @@ private static void ReplaceNodeValue(JToken node, string stringValue) 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, - DetectedBodyType = original.BodyData.DetectedBodyType, - DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, - BodyAsString = handlebarsContext.ParseAndRender(original.BodyData.BodyAsString, model) + Encoding = original.Encoding, + DetectedBodyType = original.DetectedBodyType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, + 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) { - responseMessage.BodyData = new BodyData + return new BodyData { - DetectedBodyType = original.BodyData.DetectedBodyType, - DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, + DetectedBodyType = original.DetectedBodyType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, BodyAsFile = transformedBodyAsFilename }; } @@ -212,10 +252,10 @@ private void TransformBodyAsFile(ITransformerContext handlebarsContext, object m { string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename); - responseMessage.BodyData = new BodyData + return new BodyData { DetectedBodyType = BodyType.String, - DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, BodyAsString = handlebarsContext.ParseAndRender(text, model), BodyAsFile = transformedBodyAsFilename }; From 2e471f28a5ec442580bb84bb9052be75a87bb123 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Mar 2021 08:25:00 +0000 Subject: [PATCH 18/21] more tests --- .../WireMockServer.WebhookTests.cs | 200 +++++++++++------- 1 file changed, 118 insertions(+), 82 deletions(-) diff --git a/test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs b/test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs index eff53efd1..7e6ae43ef 100644 --- a/test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs +++ b/test/WireMock.Net.Tests/WireMockServer.WebhookTests.cs @@ -1,32 +1,33 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Threading.Tasks; +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.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 - { +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], @@ -37,61 +38,96 @@ public async Task WireMockServer_WithWebhook_Should_Send_Message_To_Webhook() 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_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(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); - - server.Dispose(); - serverReceivingTheWebhook.Dispose(); - } - } + } + }) + .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(); + } + } } \ No newline at end of file From 007fb5674c64771eafea0df6640d43bb5865932d Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Mar 2021 11:10:56 +0000 Subject: [PATCH 19/21] return new Dictionary>(); --- src/WireMock.Net/Transformers/Transformer.cs | 444 +++++++++---------- 1 file changed, 222 insertions(+), 222 deletions(-) diff --git a/src/WireMock.Net/Transformers/Transformer.cs b/src/WireMock.Net/Transformers/Transformer.cs index 04e112c0e..901590f2d 100644 --- a/src/WireMock.Net/Transformers/Transformer.cs +++ b/src/WireMock.Net/Transformers/Transformer.cs @@ -1,31 +1,31 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using WireMock.Types; -using WireMock.Util; - -namespace WireMock.Transformers.Handlebars -{ - internal class Transformer : ITransformer - { - private readonly ITransformerContextFactory _factory; - - public Transformer([NotNull] ITransformerContextFactory factory) - { - _factory = factory ?? throw new ArgumentNullException(nameof(factory)); - } - +using System; +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using WireMock.Types; +using WireMock.Util; + +namespace WireMock.Transformers.Handlebars +{ + internal class Transformer : ITransformer + { + private readonly ITransformerContextFactory _factory; + + public Transformer([NotNull] ITransformerContextFactory factory) + { + _factory = factory ?? throw new ArgumentNullException(nameof(factory)); + } + public (IBodyData BodyData, IDictionary> Headers) Transform(RequestMessage originalRequestMessage, ResponseMessage originalResponseMessage, IBodyData bodyData, IDictionary> headers) { - var transformerContext = _factory.Create(); - - var model = new + var transformerContext = _factory.Create(); + + var model = new { - request = originalRequestMessage, - response = originalResponseMessage + request = originalRequestMessage, + response = originalResponseMessage }; IBodyData newBodyData = null; @@ -35,19 +35,19 @@ public Transformer([NotNull] ITransformerContextFactory factory) } return (newBodyData, TransformHeaders(transformerContext, model, headers)); - } - - public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile) - { - var transformerContext = _factory.Create(); - - var responseMessage = new ResponseMessage(); - - var model = new + } + + public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original, bool useTransformerForBodyAsFile) + { + var transformerContext = _factory.Create(); + + var responseMessage = new ResponseMessage(); + + var model = new { - request = requestMessage - }; - + request = requestMessage + }; + if (original.BodyData?.DetectedBodyType != null) { responseMessage.BodyData = TransformBodyData(transformerContext, model, original.BodyData, useTransformerForBodyAsFile); @@ -56,50 +56,50 @@ public ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage { responseMessage.BodyOriginal = original.BodyData.BodyAsString; } - } - - responseMessage.FaultType = original.FaultType; - responseMessage.FaultPercentage = original.FaultPercentage; - - responseMessage.Headers = TransformHeaders(transformerContext, model, original.Headers); - - switch (original.StatusCode) - { - case int statusCodeAsInteger: - responseMessage.StatusCode = statusCodeAsInteger; - break; - - case string statusCodeAsString: - responseMessage.StatusCode = transformerContext.ParseAndRender(statusCodeAsString, model); - break; - } - - return responseMessage; - } - + } + + responseMessage.FaultType = original.FaultType; + responseMessage.FaultPercentage = original.FaultPercentage; + + responseMessage.Headers = TransformHeaders(transformerContext, model, original.Headers); + + switch (original.StatusCode) + { + case int statusCodeAsInteger: + responseMessage.StatusCode = statusCodeAsInteger; + break; + + case string statusCodeAsString: + responseMessage.StatusCode = transformerContext.ParseAndRender(statusCodeAsString, model); + break; + } + + return 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: + 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; + default: + return null; } - } - + } + private static IDictionary> TransformHeaders(ITransformerContext transformerContext, object model, IDictionary> original) { if (original == null) { - return null; + return new Dictionary>(); } var newHeaders = new Dictionary>(); @@ -112,154 +112,154 @@ private static IDictionary> TransformHeaders(ITrans } return newHeaders; - } - - private static IBodyData TransformBodyAsJson(ITransformerContext handlebarsContext, object model, IBodyData original) - { - JToken jToken; - switch (original.BodyAsJson) - { - case JObject bodyAsJObject: - jToken = bodyAsJObject.DeepClone(); - WalkNode(handlebarsContext, jToken, model); - break; - - case Array bodyAsArray: - jToken = JArray.FromObject(bodyAsArray); - WalkNode(handlebarsContext, jToken, model); - break; - - case string bodyAsString: - jToken = ReplaceSingleNode(handlebarsContext, bodyAsString, model); - break; - - default: - jToken = JObject.FromObject(original.BodyAsJson); - WalkNode(handlebarsContext, jToken, model); - break; - } - - return new BodyData - { - Encoding = original.Encoding, - DetectedBodyType = original.DetectedBodyType, - DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, - BodyAsJson = jToken - }; - } - - private static JToken ReplaceSingleNode(ITransformerContext handlebarsContext, string stringValue, object model) - { - string transformedString = handlebarsContext.ParseAndRender(stringValue, model); - - if (!string.Equals(stringValue, transformedString)) - { - const string property = "_"; - JObject dummy = JObject.Parse($"{{ \"{property}\": null }}"); - JToken node = dummy[property]; - - ReplaceNodeValue(node, transformedString); - - return dummy[property]; - } - - return stringValue; - } - - private static void WalkNode(ITransformerContext handlebarsContext, JToken node, object model) - { - if (node.Type == JTokenType.Object) - { - // In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions. - foreach (JProperty child in node.Children().ToArray()) - { - WalkNode(handlebarsContext, child.Value, model); - } - } - else if (node.Type == JTokenType.Array) - { - // In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions. - foreach (JToken child in node.Children().ToArray()) - { - WalkNode(handlebarsContext, child, model); - } - } - else if (node.Type == JTokenType.String) - { - // In case of string, try to transform the value. - string stringValue = node.Value(); - if (string.IsNullOrEmpty(stringValue)) - { - return; - } - - string transformedString = handlebarsContext.ParseAndRender(stringValue, model); - if (!string.Equals(stringValue, transformedString)) - { - ReplaceNodeValue(node, transformedString); - } - } - } - - private static void ReplaceNodeValue(JToken node, string stringValue) - { - if (bool.TryParse(stringValue, out bool valueAsBoolean)) - { - node.Replace(valueAsBoolean); - return; - } - - JToken value; - try - { - // Try to convert this string into a JsonObject - value = JToken.Parse(stringValue); - } - catch (JsonException) - { - // Ignore JsonException and just keep string value and convert to JToken - value = stringValue; - } - - node.Replace(value); - } - - private static IBodyData TransformBodyAsString(ITransformerContext handlebarsContext, object model, IBodyData original) - { - return new BodyData - { - Encoding = original.Encoding, - DetectedBodyType = original.DetectedBodyType, - DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, - BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString, model) - }; - } - - private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile) - { - string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile, model); - - if (!useTransformerForBodyAsFile) - { - return new BodyData - { - DetectedBodyType = original.DetectedBodyType, - DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, - BodyAsFile = transformedBodyAsFilename - }; - } - else - { - string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename); - - return new BodyData - { - DetectedBodyType = BodyType.String, - DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, - BodyAsString = handlebarsContext.ParseAndRender(text, model), - BodyAsFile = transformedBodyAsFilename - }; - } - } - } + } + + private static IBodyData TransformBodyAsJson(ITransformerContext handlebarsContext, object model, IBodyData original) + { + JToken jToken; + switch (original.BodyAsJson) + { + case JObject bodyAsJObject: + jToken = bodyAsJObject.DeepClone(); + WalkNode(handlebarsContext, jToken, model); + break; + + case Array bodyAsArray: + jToken = JArray.FromObject(bodyAsArray); + WalkNode(handlebarsContext, jToken, model); + break; + + case string bodyAsString: + jToken = ReplaceSingleNode(handlebarsContext, bodyAsString, model); + break; + + default: + jToken = JObject.FromObject(original.BodyAsJson); + WalkNode(handlebarsContext, jToken, model); + break; + } + + return new BodyData + { + Encoding = original.Encoding, + DetectedBodyType = original.DetectedBodyType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, + BodyAsJson = jToken + }; + } + + private static JToken ReplaceSingleNode(ITransformerContext handlebarsContext, string stringValue, object model) + { + string transformedString = handlebarsContext.ParseAndRender(stringValue, model); + + if (!string.Equals(stringValue, transformedString)) + { + const string property = "_"; + JObject dummy = JObject.Parse($"{{ \"{property}\": null }}"); + JToken node = dummy[property]; + + ReplaceNodeValue(node, transformedString); + + return dummy[property]; + } + + return stringValue; + } + + private static void WalkNode(ITransformerContext handlebarsContext, JToken node, object model) + { + if (node.Type == JTokenType.Object) + { + // In case of Object, loop all children. Do a ToArray() to avoid `Collection was modified` exceptions. + foreach (JProperty child in node.Children().ToArray()) + { + WalkNode(handlebarsContext, child.Value, model); + } + } + else if (node.Type == JTokenType.Array) + { + // In case of Array, loop all items. Do a ToArray() to avoid `Collection was modified` exceptions. + foreach (JToken child in node.Children().ToArray()) + { + WalkNode(handlebarsContext, child, model); + } + } + else if (node.Type == JTokenType.String) + { + // In case of string, try to transform the value. + string stringValue = node.Value(); + if (string.IsNullOrEmpty(stringValue)) + { + return; + } + + string transformedString = handlebarsContext.ParseAndRender(stringValue, model); + if (!string.Equals(stringValue, transformedString)) + { + ReplaceNodeValue(node, transformedString); + } + } + } + + private static void ReplaceNodeValue(JToken node, string stringValue) + { + if (bool.TryParse(stringValue, out bool valueAsBoolean)) + { + node.Replace(valueAsBoolean); + return; + } + + JToken value; + try + { + // Try to convert this string into a JsonObject + value = JToken.Parse(stringValue); + } + catch (JsonException) + { + // Ignore JsonException and just keep string value and convert to JToken + value = stringValue; + } + + node.Replace(value); + } + + private static IBodyData TransformBodyAsString(ITransformerContext handlebarsContext, object model, IBodyData original) + { + return new BodyData + { + Encoding = original.Encoding, + DetectedBodyType = original.DetectedBodyType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, + BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString, model) + }; + } + + private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile) + { + string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile, model); + + if (!useTransformerForBodyAsFile) + { + return new BodyData + { + DetectedBodyType = original.DetectedBodyType, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, + BodyAsFile = transformedBodyAsFilename + }; + } + else + { + string text = handlebarsContext.FileSystemHandler.ReadResponseBodyAsString(transformedBodyAsFilename); + + return new BodyData + { + DetectedBodyType = BodyType.String, + DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, + BodyAsString = handlebarsContext.ParseAndRender(text, model), + BodyAsFile = transformedBodyAsFilename + }; + } + } + } } \ No newline at end of file From 523864e48364e7b340c9bed7cf6d8e6e48576965 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Wed, 10 Mar 2021 11:22:01 +0000 Subject: [PATCH 20/21] more tests --- .../Serialization/WebhookMapperTests.cs | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 test/WireMock.Net.Tests/Serialization/WebhookMapperTests.cs diff --git a/test/WireMock.Net.Tests/Serialization/WebhookMapperTests.cs b/test/WireMock.Net.Tests/Serialization/WebhookMapperTests.cs new file mode 100644 index 000000000..dc6df4e13 --- /dev/null +++ b/test/WireMock.Net.Tests/Serialization/WebhookMapperTests.cs @@ -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 + { + { "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 + { + { "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 + { + { "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); + } + } +} From 819629125ce3f34e71a31ba6a8825a13b0b8adb1 Mon Sep 17 00:00:00 2001 From: Stef Heyenrath Date: Sun, 21 Mar 2021 09:18:26 +0100 Subject: [PATCH 21/21] 1.4.7 --- CHANGELOG.md | 9 ++++++--- GitHubReleaseNotes.txt | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1c4c866..ac3505b19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 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) +- [#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) +- [#597](https://github.com/WireMock-Net/WireMock.Net/pull/597) - Remove 2 second delay from first response and add IPv6 address support [bug, feature] contributed by [benagain](https://github.com/benagain) + # 1.4.6 (26 February 2021) - [#587](https://github.com/WireMock-Net/WireMock.Net/pull/587) - Fix WithCallback logic when using other fluent builder statements [bug] contributed by [StefH](https://github.com/StefH) - [#569](https://github.com/WireMock-Net/WireMock.Net/issues/569) - WithCallback circumvent the rest of the builder [bug] @@ -43,6 +48,7 @@ - [#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) +- [#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) - [#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) @@ -50,9 +56,6 @@ - [#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] -# 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) - [#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) diff --git a/GitHubReleaseNotes.txt b/GitHubReleaseNotes.txt index b56e91c33..9d4181ab7 100644 --- a/GitHubReleaseNotes.txt +++ b/GitHubReleaseNotes.txt @@ -1,3 +1,3 @@ https://github.com/StefH/GitHubReleaseNotes -GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.4.6 \ No newline at end of file +GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.4.7 \ No newline at end of file