diff --git a/resources/WireMock.Net-Logo.png b/resources/WireMock.Net-Logo.png
index 7325bd13..6e2bcf39 100644
Binary files a/resources/WireMock.Net-Logo.png and b/resources/WireMock.Net-Logo.png differ
diff --git a/src/WireMock.Net.Abstractions/Admin/Mappings/CookieModel.cs b/src/WireMock.Net.Abstractions/Admin/Mappings/CookieModel.cs
index 5b88bb42..e38c60f1 100644
--- a/src/WireMock.Net.Abstractions/Admin/Mappings/CookieModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Mappings/CookieModel.cs
@@ -1,31 +1,39 @@
using System.Collections.Generic;
-namespace WireMock.Admin.Mappings
+namespace WireMock.Admin.Mappings;
+
+///
+/// Cookie Model
+///
+[FluentBuilder.AutoGenerateBuilder]
+public class CookieModel
{
///
- /// Cookie Model
+ /// Gets or sets the name.
///
- [FluentBuilder.AutoGenerateBuilder]
- public class CookieModel
- {
- ///
- /// Gets or sets the name.
- ///
- public string Name { get; set; } = null!;
+ public string Name { get; set; } = null!;
- ///
- /// Gets or sets the matchers.
- ///
- public IList? Matchers { get; set; }
+ ///
+ /// Gets or sets the matchers.
+ ///
+ public IList? Matchers { get; set; }
- ///
- /// Gets or sets the ignore case.
- ///
- public bool? IgnoreCase { get; set; }
+ ///
+ /// Gets or sets the ignore case.
+ ///
+ public bool? IgnoreCase { get; set; }
- ///
- /// Reject on match.
- ///
- public bool? RejectOnMatch { get; set; }
- }
+ ///
+ /// Reject on match.
+ ///
+ public bool? RejectOnMatch { get; set; }
+
+ ///
+ /// The Operator to use when matchers are defined. [Optional]
+ /// - null = Same as "or".
+ /// - "or" = Only one pattern should match.
+ /// - "and" = All patterns should match.
+ /// - "average" = The average value from all patterns.
+ ///
+ public string? MatchOperator { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
index 96ca4181..36d4b03e 100644
--- a/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
+++ b/src/WireMock.Net.Abstractions/Admin/Settings/ProxyAndRecordSettingsModel.cs
@@ -54,5 +54,14 @@ namespace WireMock.Admin.Settings
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to true).
///
// public bool PreferProxyMapping { get; set; }
+
+ ///
+ /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
+ /// - false, the default matchers will be used.
+ /// - true, the defined mappings in the request wil be used for the new mapping.
+ ///
+ /// Default value is false.
+ ///
+ public bool UseDefinedRequestMatchers { get; set; }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageCookieMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageCookieMatcher.cs
index f4af9a44..576927de 100644
--- a/src/WireMock.Net/Matchers/Request/RequestMessageCookieMatcher.cs
+++ b/src/WireMock.Net/Matchers/Request/RequestMessageCookieMatcher.cs
@@ -1,8 +1,7 @@
+using Stef.Validation;
using System;
using System.Collections.Generic;
using System.Linq;
-using JetBrains.Annotations;
-using Stef.Validation;
namespace WireMock.Matchers.Request;
@@ -13,12 +12,13 @@ namespace WireMock.Matchers.Request;
public class RequestMessageCookieMatcher : IRequestMatcher
{
private readonly MatchBehaviour _matchBehaviour;
+
private readonly bool _ignoreCase;
///
/// The functions
///
- public Func, bool>[] Funcs { get; }
+ public Func, bool>[]? Funcs { get; }
///
/// The name
@@ -28,7 +28,7 @@ public class RequestMessageCookieMatcher : IRequestMatcher
///
/// The matchers.
///
- public IStringMatcher[] Matchers { get; }
+ public IStringMatcher[]? Matchers { get; }
///
/// Initializes a new instance of the class.
@@ -37,15 +37,12 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// The pattern.
/// Ignore the case from the pattern.
/// The match behaviour.
- public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, [NotNull] string pattern, bool ignoreCase)
+ public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, string pattern, bool ignoreCase)
{
- Guard.NotNull(name, nameof(name));
- Guard.NotNull(pattern, nameof(pattern));
-
_matchBehaviour = matchBehaviour;
_ignoreCase = ignoreCase;
- Name = name;
- Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, pattern, ignoreCase) };
+ Name = Guard.NotNull(name);
+ Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, Guard.NotNull(pattern), ignoreCase) };
}
///
@@ -55,10 +52,10 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// The name.
/// The patterns.
/// Ignore the case from the pattern.
- public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, bool ignoreCase, [NotNull] params string[] patterns) :
+ public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, bool ignoreCase, params string[] patterns) :
this(matchBehaviour, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast().ToArray())
{
- Guard.NotNull(patterns, nameof(patterns));
+ Guard.NotNull(patterns);
}
///
@@ -68,14 +65,11 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// The name.
/// The matchers.
/// Ignore the case from the pattern.
- public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, bool ignoreCase, [NotNull] params IStringMatcher[] matchers)
+ public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, bool ignoreCase, params IStringMatcher[] matchers)
{
- Guard.NotNull(name, nameof(name));
- Guard.NotNull(matchers, nameof(matchers));
-
_matchBehaviour = matchBehaviour;
- Name = name;
- Matchers = matchers;
+ Name = Guard.NotNull(name);
+ Matchers = Guard.NotNull(matchers);
_ignoreCase = ignoreCase;
}
@@ -83,11 +77,12 @@ public class RequestMessageCookieMatcher : IRequestMatcher
/// Initializes a new instance of the class.
///
/// The funcs.
- public RequestMessageCookieMatcher([NotNull] params Func, bool>[] funcs)
+ public RequestMessageCookieMatcher(params Func, bool>[] funcs)
{
- Guard.NotNull(funcs, nameof(funcs));
+ Guard.NotNull(funcs);
Funcs = funcs;
+ Name = string.Empty; // Not used when Func, but set to a non-null valid value.
}
///
diff --git a/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs b/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs
index dc4e34f8..8ac113d7 100644
--- a/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs
+++ b/src/WireMock.Net/Matchers/Request/RequestMessageParamMatcher.cs
@@ -11,7 +11,10 @@ namespace WireMock.Matchers.Request;
///
public class RequestMessageParamMatcher : IRequestMatcher
{
- private readonly MatchBehaviour _matchBehaviour;
+ ///
+ /// MatchBehaviour
+ ///
+ public MatchBehaviour MatchBehaviour { get; }
///
/// The funcs
@@ -63,7 +66,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
/// The matchers.
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, IStringMatcher[]? matchers)
{
- _matchBehaviour = matchBehaviour;
+ MatchBehaviour = matchBehaviour;
Key = Guard.NotNull(key);
IgnoreCase = ignoreCase;
Matchers = matchers;
@@ -81,7 +84,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
///
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
- double score = MatchBehaviourHelper.Convert(_matchBehaviour, IsMatch(requestMessage));
+ double score = MatchBehaviourHelper.Convert(MatchBehaviour, IsMatch(requestMessage));
return requestMatchResult.AddScore(GetType(), score);
}
diff --git a/src/WireMock.Net/Owin/WireMockMiddleware.cs b/src/WireMock.Net/Owin/WireMockMiddleware.cs
index e0e088bf..23445942 100644
--- a/src/WireMock.Net/Owin/WireMockMiddleware.cs
+++ b/src/WireMock.Net/Owin/WireMockMiddleware.cs
@@ -161,7 +161,6 @@ namespace WireMock.Owin
_options.Logger.Error($"Providing a Response for Mapping '{result.Match?.Mapping?.Guid}' failed. HttpStatusCode set to 500. Exception: {ex}");
response = ResponseMessageBuilder.Create(ex.Message, 500);
}
-
finally
{
var log = new LogEntry
diff --git a/src/WireMock.Net/Proxy/ProxyHelper.cs b/src/WireMock.Net/Proxy/ProxyHelper.cs
index 600d628a..6d6178a4 100644
--- a/src/WireMock.Net/Proxy/ProxyHelper.cs
+++ b/src/WireMock.Net/Proxy/ProxyHelper.cs
@@ -1,16 +1,10 @@
+using Stef.Validation;
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
-using Stef.Validation;
-using WireMock.Constants;
using WireMock.Http;
-using WireMock.Matchers;
-using WireMock.RequestBuilders;
-using WireMock.ResponseBuilders;
+using WireMock.Serialization;
using WireMock.Settings;
-using WireMock.Types;
using WireMock.Util;
namespace WireMock.Proxy;
@@ -18,13 +12,16 @@ namespace WireMock.Proxy;
internal class ProxyHelper
{
private readonly WireMockServerSettings _settings;
+ private readonly ProxyMappingConverter _proxyMappingConverter;
public ProxyHelper(WireMockServerSettings settings)
{
_settings = Guard.NotNull(settings);
+ _proxyMappingConverter = new ProxyMappingConverter(settings, new GuidUtils());
}
public async Task<(IResponseMessage Message, IMapping? Mapping)> SendAsync(
+ IMapping? mapping,
ProxyAndRecordSettings proxyAndRecordSettings,
HttpClient client,
IRequestMessage requestMessage,
@@ -49,78 +46,13 @@ internal class ProxyHelper
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false);
- IMapping? mapping = null;
+ IMapping? newMapping = null;
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
{
- mapping = ToMapping(proxyAndRecordSettings, requestMessage, responseMessage);
+ newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage);
}
- return (responseMessage, mapping);
- }
-
- private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
- {
- var excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { };
- var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
-
- var request = Request.Create();
- request.WithPath(requestMessage.Path);
- request.UsingMethod(requestMessage.Method);
-
- requestMessage.Query?.Loop((key, value) => request.WithParam(key, false, value.ToArray()));
- requestMessage.Cookies?.Loop((key, value) =>
- {
- if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase))
- {
- request.WithCookie(key, value);
- }
- });
-
- var allExcludedHeaders = new List(excludedHeaders) { "Cookie" };
- requestMessage.Headers?.Loop((key, value) =>
- {
- if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase))
- {
- request.WithHeader(key, value.ToArray());
- }
- });
-
- bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
- switch (requestMessage.BodyData?.DetectedBodyType)
- {
- case BodyType.Json:
- request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails));
- break;
-
- case BodyType.String:
- request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString));
- break;
-
- case BodyType.Bytes:
- request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
- break;
- }
-
- var response = Response.Create(responseMessage);
-
- return new Mapping
- (
- guid: Guid.NewGuid(),
- title: $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}",
- description: string.Empty,
- path: null,
- settings: _settings,
- request,
- response,
- priority: WireMockConstants.ProxyPriority, // This was 0
- scenario: null,
- executionConditionState: null,
- nextState: null,
- stateTimes: null,
- webhooks: null,
- useWebhooksFireAndForget: null,
- timeSettings: null
- );
+ return (responseMessage, newMapping);
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/RequestBuilders/Request.WithParam.cs b/src/WireMock.Net/RequestBuilders/Request.WithParam.cs
index 0b300f22..06a925e6 100644
--- a/src/WireMock.Net/RequestBuilders/Request.WithParam.cs
+++ b/src/WireMock.Net/RequestBuilders/Request.WithParam.cs
@@ -20,7 +20,7 @@ namespace WireMock.RequestBuilders
///
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
- Guard.NotNull(key, nameof(key));
+ Guard.NotNull(key);
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
return this;
diff --git a/src/WireMock.Net/ResponseBuilders/Response.cs b/src/WireMock.Net/ResponseBuilders/Response.cs
index 194e1e69..536feb87 100644
--- a/src/WireMock.Net/ResponseBuilders/Response.cs
+++ b/src/WireMock.Net/ResponseBuilders/Response.cs
@@ -271,6 +271,7 @@ public partial class Response : IResponseBuilder
var proxyHelper = new ProxyHelper(settings);
return await proxyHelper.SendAsync(
+ mapping,
ProxyAndRecordSettings,
_httpClientForProxy,
requestMessage,
diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs
index 96dcf870..5f2460a6 100644
--- a/src/WireMock.Net/Serialization/MappingConverter.cs
+++ b/src/WireMock.Net/Serialization/MappingConverter.cs
@@ -20,7 +20,7 @@ internal class MappingConverter
public MappingConverter(MatcherMapper mapper)
{
- _mapper = Guard.NotNull(mapper, nameof(mapper));
+ _mapper = Guard.NotNull(mapper);
}
public MappingModel ToMappingModel(IMapping mapping)
diff --git a/src/WireMock.Net/Serialization/ProxyMappingConverter.cs b/src/WireMock.Net/Serialization/ProxyMappingConverter.cs
new file mode 100644
index 00000000..67446ad5
--- /dev/null
+++ b/src/WireMock.Net/Serialization/ProxyMappingConverter.cs
@@ -0,0 +1,181 @@
+using Stef.Validation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using WireMock.Constants;
+using WireMock.Matchers;
+using WireMock.Matchers.Request;
+using WireMock.RequestBuilders;
+using WireMock.ResponseBuilders;
+using WireMock.Settings;
+using WireMock.Types;
+using WireMock.Util;
+
+namespace WireMock.Serialization;
+
+internal class ProxyMappingConverter
+{
+ private readonly WireMockServerSettings _settings;
+ private readonly IGuidUtils _guidUtils;
+
+ public ProxyMappingConverter(WireMockServerSettings settings, IGuidUtils guidUtils)
+ {
+ _settings = Guard.NotNull(settings);
+ _guidUtils = Guard.NotNull(guidUtils);
+ }
+
+ public IMapping ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
+ {
+ var request = (Request?)mapping?.RequestMatcher;
+ var clientIPMatcher = request?.GetRequestMessageMatcher();
+ var pathMatcher = request?.GetRequestMessageMatcher();
+ var headerMatchers = request?.GetRequestMessageMatchers();
+ var cookieMatchers = request?.GetRequestMessageMatchers();
+ var paramMatchers = request?.GetRequestMessageMatchers();
+ var methodMatcher = request?.GetRequestMessageMatcher();
+ var bodyMatcher = request?.GetRequestMessageMatcher();
+
+ var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
+
+ var excludedHeaders = new List(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
+ var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
+
+ var newRequest = Request.Create();
+
+ // ClientIP
+ if (useDefinedRequestMatchers && clientIPMatcher?.Matchers is not null)
+ {
+ newRequest.WithClientIP(clientIPMatcher.MatchOperator, clientIPMatcher.Matchers.ToArray());
+ }
+
+ // Path
+ if (useDefinedRequestMatchers && pathMatcher?.Matchers is not null)
+ {
+ newRequest.WithPath(pathMatcher.MatchOperator, pathMatcher.Matchers.ToArray());
+ }
+ else
+ {
+ newRequest.WithPath(requestMessage.Path);
+ }
+
+ // Method
+ if (useDefinedRequestMatchers && methodMatcher is not null)
+ {
+ newRequest.UsingMethod(methodMatcher.Methods);
+ }
+ else
+ {
+ newRequest.UsingMethod(requestMessage.Method);
+ }
+
+ // QueryParams
+ if (useDefinedRequestMatchers && paramMatchers is not null)
+ {
+ foreach (var paramMatcher in paramMatchers)
+ {
+ newRequest.WithParam(paramMatcher.Key, paramMatcher.MatchBehaviour, paramMatcher.Matchers!.ToArray());
+ }
+ }
+ else
+ {
+ requestMessage.Query?.Loop((key, value) => newRequest.WithParam(key, false, value.ToArray()));
+ }
+
+ // Cookies
+ if (useDefinedRequestMatchers && cookieMatchers is not null)
+ {
+ foreach (var cookieMatcher in cookieMatchers.Where(hm => hm.Matchers is not null))
+ {
+ if (!excludedCookies.Contains(cookieMatcher.Name, StringComparer.OrdinalIgnoreCase))
+ {
+ newRequest.WithCookie(cookieMatcher.Name, cookieMatcher.Matchers!);
+ }
+ }
+ }
+ else
+ {
+ requestMessage.Cookies?.Loop((key, value) =>
+ {
+ if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase))
+ {
+ newRequest.WithCookie(key, value);
+ }
+ });
+ }
+
+ // Headers
+ if (useDefinedRequestMatchers && headerMatchers is not null)
+ {
+ foreach (var headerMatcher in headerMatchers.Where(hm => hm.Matchers is not null))
+ {
+ if (!excludedHeaders.Contains(headerMatcher.Name, StringComparer.OrdinalIgnoreCase))
+ {
+ newRequest.WithHeader(headerMatcher.Name, headerMatcher.Matchers!);
+ }
+ }
+ }
+ else
+ {
+ requestMessage.Headers?.Loop((key, value) =>
+ {
+ if (!excludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase))
+ {
+ newRequest.WithHeader(key, value.ToArray());
+ }
+ });
+ }
+
+ // Body
+ bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
+ if (useDefinedRequestMatchers && bodyMatcher?.Matchers is not null)
+ {
+ newRequest.WithBody(bodyMatcher.Matchers);
+ }
+ else
+ {
+ switch (requestMessage.BodyData?.DetectedBodyType)
+ {
+ case BodyType.Json:
+ newRequest.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails));
+ break;
+
+ case BodyType.String:
+ newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString));
+ break;
+
+ case BodyType.Bytes:
+ newRequest.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
+ break;
+ }
+ }
+
+ // Title
+ var title = useDefinedRequestMatchers && !string.IsNullOrEmpty(mapping?.Title) ?
+ mapping!.Title :
+ $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}";
+
+ // Description
+ var description = useDefinedRequestMatchers && !string.IsNullOrEmpty(mapping?.Description) ?
+ mapping!.Description :
+ $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}";
+
+ return new Mapping
+ (
+ guid: _guidUtils.NewGuid(),
+ title: title,
+ description: description,
+ path: null,
+ settings: _settings,
+ requestMatcher: newRequest,
+ provider: Response.Create(responseMessage),
+ priority: WireMockConstants.ProxyPriority, // This was 0
+ scenario: null,
+ executionConditionState: null,
+ nextState: null,
+ stateTimes: null,
+ webhooks: null,
+ useWebhooksFireAndForget: null,
+ timeSettings: null
+ );
+ }
+}
\ 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 52a58d3f..2609a554 100644
--- a/src/WireMock.Net/Server/WireMockServer.Admin.cs
+++ b/src/WireMock.Net/Server/WireMockServer.Admin.cs
@@ -236,6 +236,7 @@ public partial class WireMockServer
var proxyHelper = new ProxyHelper(settings);
var (responseMessage, mapping) = await proxyHelper.SendAsync(
+ null,
_settings.ProxyAndRecordSettings!,
_httpClientForProxy!,
requestMessage,
diff --git a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs
index 3c732ff6..1368b06d 100644
--- a/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs
+++ b/src/WireMock.Net/Server/WireMockServer.ConvertMapping.cs
@@ -198,7 +198,7 @@ public partial class WireMockServer
{
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
{
- requestBuilder = requestBuilder.WithCookie(
+ requestBuilder = requestBuilder.WithCookie(
cookieModel.Name,
cookieModel.IgnoreCase == true,
cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
diff --git a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
index 851eba80..3ae30763 100644
--- a/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
+++ b/src/WireMock.Net/Settings/ProxyAndRecordSettings.cs
@@ -49,4 +49,13 @@ public class ProxyAndRecordSettings : HttpClientSettings
///
//[PublicAPI]
//public bool PreferProxyMapping { get; set; }
+
+ ///
+ /// When SaveMapping is set to true, this setting can be used to control the behavior of the generated request matchers for the new mapping.
+ /// - false, the default matchers will be used.
+ /// - true, the defined mappings in the request wil be used for the new mapping.
+ ///
+ /// Default value is false.
+ ///
+ public bool UseDefinedRequestMatchers { get; set; }
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
index 2caca070..32fecedb 100644
--- a/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
+++ b/src/WireMock.Net/Settings/WireMockServerSettingsParser.cs
@@ -92,6 +92,7 @@ public static class WireMockServerSettingsParser
SaveMapping = parser.GetBoolValue("SaveMapping"),
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
+ UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
Url = proxyUrl!
};
diff --git a/src/WireMock.Net/Util/GuidUtils.cs b/src/WireMock.Net/Util/GuidUtils.cs
new file mode 100644
index 00000000..53892407
--- /dev/null
+++ b/src/WireMock.Net/Util/GuidUtils.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace WireMock.Util;
+
+internal interface IGuidUtils
+{
+ Guid NewGuid();
+}
+
+internal class GuidUtils : IGuidUtils
+{
+ public Guid NewGuid()
+ {
+ return Guid.NewGuid();
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs b/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs
new file mode 100644
index 00000000..228d63b3
--- /dev/null
+++ b/test/WireMock.Net.Tests/Serialization/ProxyMappingConverterTests.cs
@@ -0,0 +1,70 @@
+using System;
+using Moq;
+using Newtonsoft.Json;
+using System.IO;
+using FluentAssertions;
+using WireMock.Matchers;
+using WireMock.RequestBuilders;
+using WireMock.Serialization;
+using WireMock.Settings;
+using WireMock.Util;
+using Xunit;
+
+namespace WireMock.Net.Tests.Serialization;
+
+public class ProxyMappingConverterTests
+{
+ private readonly WireMockServerSettings _settings = new();
+
+ private readonly MappingConverter _mappingConverter;
+
+ private readonly ProxyMappingConverter _sut;
+
+ public ProxyMappingConverterTests()
+ {
+ var guidUtilsMock = new Mock();
+ guidUtilsMock.Setup(g => g.NewGuid()).Returns(Guid.Parse("ff55ac0a-fea9-4d7b-be74-5e483a2c1305"));
+
+ _mappingConverter = new MappingConverter(new MatcherMapper(_settings));
+
+ _sut = new ProxyMappingConverter(_settings, guidUtilsMock.Object);
+ }
+
+ [Fact]
+ public void ToMapping_UseDefinedRequestMatchers_True()
+ {
+ // Arrange
+ var proxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ UseDefinedRequestMatchers = true
+ };
+
+ var request = Request.Create()
+ .UsingPost()
+ .WithPath("x")
+ .WithParam("p1", "p1-v")
+ .WithParam("p2", "p2-v")
+ .WithHeader("Content-Type", new ContentTypeMatcher("text/plain"))
+ .WithCookie("c", "x")
+ .WithBody(new RegexMatcher("Auth();
+ mappingMock.SetupGet(m => m.RequestMatcher).Returns(request);
+ mappingMock.SetupGet(m => m.Title).Returns("my title");
+ mappingMock.SetupGet(m => m.Description).Returns("my description");
+
+ var requestMessageMock = new Mock();
+
+ var responseMessage = new ResponseMessage();
+
+ // Act
+ var proxyMapping = _sut.ToMapping(mappingMock.Object, proxyAndRecordSettings, requestMessageMock.Object, responseMessage);
+
+ // Assert
+ var model = _mappingConverter.ToMappingModel(proxyMapping);
+ var json = JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault);
+ var expected = File.ReadAllText(Path.Combine("../../../", "Serialization", "files", "proxy.json"));
+
+ json.Should().Be(expected);
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Serialization/files/proxy.json b/test/WireMock.Net.Tests/Serialization/files/proxy.json
new file mode 100644
index 00000000..a045bc7a
--- /dev/null
+++ b/test/WireMock.Net.Tests/Serialization/files/proxy.json
@@ -0,0 +1,72 @@
+{
+ "Guid": "ff55ac0a-fea9-4d7b-be74-5e483a2c1305",
+ "Title": "my title",
+ "Description": "my description",
+ "Priority": -2000000,
+ "Request": {
+ "Path": {
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "x",
+ "IgnoreCase": false
+ }
+ ]
+ },
+ "Methods": [
+ "POST"
+ ],
+ "Headers": [
+ {
+ "Name": "Content-Type",
+ "Matchers": [
+ {
+ "Name": "ContentTypeMatcher",
+ "Pattern": "text/plain",
+ "IgnoreCase": false
+ }
+ ]
+ }
+ ],
+ "Cookies": [
+ {
+ "Name": "c",
+ "Matchers": [
+ {
+ "Name": "WildcardMatcher",
+ "Pattern": "x",
+ "IgnoreCase": true
+ }
+ ]
+ }
+ ],
+ "Params": [
+ {
+ "Name": "p1",
+ "Matchers": [
+ {
+ "Name": "ExactMatcher",
+ "Pattern": "p1-v"
+ }
+ ]
+ },
+ {
+ "Name": "p2",
+ "Matchers": [
+ {
+ "Name": "ExactMatcher",
+ "Pattern": "p2-v"
+ }
+ ]
+ }
+ ],
+ "Body": {
+ "Matcher": {
+ "Name": "RegexMatcher",
+ "Pattern": "Auth
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
@@ -105,6 +108,7 @@
+
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
index def80c60..aa2a03a7 100644
--- a/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
+++ b/test/WireMock.Net.Tests/WireMockServer.Proxy.cs
@@ -1,3 +1,6 @@
+using FluentAssertions;
+using Moq;
+using NFluent;
using System;
using System.Linq;
using System.Net;
@@ -5,11 +8,9 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
-using FluentAssertions;
-using Moq;
-using NFluent;
-using WireMock.Admin.Mappings;
+using WireMock.Constants;
using WireMock.Handlers;
+using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
@@ -119,6 +120,61 @@ public class WireMockServerProxyTests
server.Mappings.Should().HaveCount(28);
}
+ [Fact]
+ public async Task WireMockServer_Proxy_With_SaveMappingToFile_Is_True_ShouldSaveMappingToFile()
+ {
+ // Assign
+ string path = $"/prx_{Guid.NewGuid()}";
+ var title = "IndexFile";
+ var description = "IndexFile_Test";
+ var stringBody = "value";
+ var serverForProxyForwarding = WireMockServer.Start();
+ var fileSystemHandlerMock = new Mock();
+ fileSystemHandlerMock.Setup(f => f.GetMappingFolder()).Returns("m");
+
+ var settings = new WireMockServerSettings
+ {
+ ProxyAndRecordSettings = new ProxyAndRecordSettings
+ {
+ Url = serverForProxyForwarding.Urls[0],
+ SaveMapping = false,
+ SaveMappingToFile = true
+ },
+ FileSystemHandler = fileSystemHandlerMock.Object
+ };
+
+ var server = WireMockServer.Start(settings);
+ server.Given(Request.Create()
+ .WithPath("/*")
+ .WithBody(new RegexMatcher(stringBody)))
+ .WithTitle(title)
+ .WithDescription(description)
+ .AtPriority(WireMockConstants.ProxyPriority)
+ .RespondWith(Response.Create().WithProxy(new ProxyAndRecordSettings
+ {
+ Url = serverForProxyForwarding.Urls[0],
+ SaveMapping = false,
+ SaveMappingToFile = true,
+ UseDefinedRequestMatchers = true,
+ }));
+
+ // Act
+ var requestMessage = new HttpRequestMessage
+ {
+ Method = HttpMethod.Post,
+ RequestUri = new Uri($"{server.Urls[0]}{path}"),
+ Content = new StringContent(stringBody)
+ };
+ var httpClientHandler = new HttpClientHandler { AllowAutoRedirect = false };
+ await new HttpClient(httpClientHandler).SendAsync(requestMessage).ConfigureAwait(false);
+
+ // Assert
+ server.Mappings.Should().HaveCount(2);
+
+ // Verify
+ fileSystemHandlerMock.Verify(f => f.WriteMappingFile($"m{System.IO.Path.DirectorySeparatorChar}{title}.json", It.IsRegex(stringBody)), Times.Once);
+ }
+
[Fact]
public async Task WireMockServer_Proxy_With_SaveMapping_Is_False_And_SaveMappingToFile_Is_True_ShouldSaveMappingToFile()
{
@@ -735,8 +791,6 @@ public class WireMockServerProxyTests
content.Should().NotBeEmpty();
server.LogEntries.Should().HaveCount(1);
- var status = ((StatusModel)server.LogEntries.First().ResponseMessage.BodyData.BodyAsJson).Status;
-
server.Stop();
}
}
\ No newline at end of file