mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-11 22:30:41 +01:00
Add UseDefinedRequestMatchers to ProxyAndRecordSettings (#821)
* . * UseDefinedRequestMatchers * ok * . * ClientIP * t * fix ut * . * cf * cf2
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
@@ -1,31 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// Cookie Model
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class CookieModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Cookie Model
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class CookieModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
public IList<MatcherModel>? Matchers { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the matchers.
|
||||
/// </summary>
|
||||
public IList<MatcherModel>? Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ignore case.
|
||||
/// </summary>
|
||||
public bool? IgnoreCase { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the ignore case.
|
||||
/// </summary>
|
||||
public bool? IgnoreCase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reject on match.
|
||||
/// </summary>
|
||||
public bool? RejectOnMatch { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Reject on match.
|
||||
/// </summary>
|
||||
public bool? RejectOnMatch { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public string? MatchOperator { get; set; }
|
||||
}
|
||||
@@ -54,5 +54,14 @@ namespace WireMock.Admin.Settings
|
||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||
/// </summary>
|
||||
// public bool PreferProxyMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||
/// - <c>false</c>, the default matchers will be used.
|
||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||
///
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// The functions
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
|
||||
public Func<IDictionary<string, string>, bool>[]? Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name
|
||||
@@ -28,7 +28,7 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public IStringMatcher[] Matchers { get; }
|
||||
public IStringMatcher[]? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||
@@ -37,15 +37,12 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
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) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -55,10 +52,10 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
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<IStringMatcher>().ToArray())
|
||||
{
|
||||
Guard.NotNull(patterns, nameof(patterns));
|
||||
Guard.NotNull(patterns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -68,14 +65,11 @@ public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
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 <see cref="RequestMessageCookieMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageCookieMatcher([NotNull] params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
public RequestMessageCookieMatcher(params Func<IDictionary<string, string>, 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.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -11,7 +11,10 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public class RequestMessageParamMatcher : IRequestMatcher
|
||||
{
|
||||
private readonly MatchBehaviour _matchBehaviour;
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
/// </summary>
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The funcs
|
||||
@@ -63,7 +66,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
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
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<string>(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);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ namespace WireMock.RequestBuilders
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
|
||||
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;
|
||||
|
||||
@@ -271,6 +271,7 @@ public partial class Response : IResponseBuilder
|
||||
var proxyHelper = new ProxyHelper(settings);
|
||||
|
||||
return await proxyHelper.SendAsync(
|
||||
mapping,
|
||||
ProxyAndRecordSettings,
|
||||
_httpClientForProxy,
|
||||
requestMessage,
|
||||
|
||||
@@ -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)
|
||||
|
||||
181
src/WireMock.Net/Serialization/ProxyMappingConverter.cs
Normal file
181
src/WireMock.Net/Serialization/ProxyMappingConverter.cs
Normal file
@@ -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<RequestMessageClientIPMatcher>();
|
||||
var pathMatcher = request?.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||
var headerMatchers = request?.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
|
||||
var cookieMatchers = request?.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
|
||||
var paramMatchers = request?.GetRequestMessageMatchers<RequestMessageParamMatcher>();
|
||||
var methodMatcher = request?.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
var bodyMatcher = request?.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
|
||||
var excludedHeaders = new List<string>(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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -236,6 +236,7 @@ public partial class WireMockServer
|
||||
var proxyHelper = new ProxyHelper(settings);
|
||||
|
||||
var (responseMessage, mapping) = await proxyHelper.SendAsync(
|
||||
null,
|
||||
_settings.ProxyAndRecordSettings!,
|
||||
_httpClientForProxy!,
|
||||
requestMessage,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -49,4 +49,13 @@ public class ProxyAndRecordSettings : HttpClientSettings
|
||||
/// </summary>
|
||||
//[PublicAPI]
|
||||
//public bool PreferProxyMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||
/// - <c>false</c>, the default matchers will be used.
|
||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||
///
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
}
|
||||
@@ -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!
|
||||
};
|
||||
|
||||
|
||||
16
src/WireMock.Net/Util/GuidUtils.cs
Normal file
16
src/WireMock.Net/Util/GuidUtils.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
internal interface IGuidUtils
|
||||
{
|
||||
Guid NewGuid();
|
||||
}
|
||||
|
||||
internal class GuidUtils : IGuidUtils
|
||||
{
|
||||
public Guid NewGuid()
|
||||
{
|
||||
return Guid.NewGuid();
|
||||
}
|
||||
}
|
||||
@@ -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<IGuidUtils>();
|
||||
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("<RequestType>Auth</RequestType"));
|
||||
|
||||
var mappingMock = new Mock<IMapping>();
|
||||
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<IRequestMessage>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
72
test/WireMock.Net.Tests/Serialization/files/proxy.json
Normal file
72
test/WireMock.Net.Tests/Serialization/files/proxy.json
Normal file
@@ -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": "<RequestType>Auth</RequestType",
|
||||
"IgnoreCase": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"Response": {}
|
||||
}
|
||||
@@ -85,6 +85,9 @@
|
||||
<None Update="responsebody.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Serialization\files\proxy.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="__admin\mappings.org\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
@@ -105,6 +108,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pact\files\" />
|
||||
<Folder Include="Serialization\files\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -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 = "<pretendXml>value</pretendXml>";
|
||||
var serverForProxyForwarding = WireMockServer.Start();
|
||||
var fileSystemHandlerMock = new Mock<IFileSystemHandler>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user