Add MatchOperator "Or", "And" and "Average" for patterns (#755)

* wip

* ...

* .

* ...

* ...

* path

* url

* b

* t

* client

* .

* RequestMessageMethodMatcherTests

* .

* h

* .

* fix tests

* .
This commit is contained in:
Stef Heyenrath
2022-06-09 21:31:54 +02:00
committed by GitHub
parent 1f23022460
commit 0441c1d85e
95 changed files with 5736 additions and 5111 deletions

View File

@@ -2,251 +2,254 @@
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Stef.Validation;
using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
using Stef.Validation;
using WireMock.Constants;
namespace WireMock.Server
namespace WireMock.Server;
/// <summary>
/// The respond with a provider.
/// </summary>
internal class RespondWithAProvider : IRespondWithAProvider
{
private int _priority;
private string? _title;
private string? _description;
private string? _path;
private string? _executionConditionState;
private string? _nextState;
private string? _scenario;
private int _timesInSameState = 1;
private readonly RegistrationCallback _registrationCallback;
private readonly IRequestMatcher _requestMatcher;
private readonly WireMockServerSettings _settings;
private readonly bool _saveToFile;
public Guid Guid { get; private set; } = Guid.NewGuid();
public IWebhook[]? Webhooks { get; private set; }
public ITimeSettings? TimeSettings { get; private set; }
/// <summary>
/// The respond with a provider.
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
/// </summary>
internal class RespondWithAProvider : IRespondWithAProvider
/// <param name="registrationCallback">The registration callback.</param>
/// <param name="requestMatcher">The request matcher.</param>
/// <param name="settings">The WireMockServerSettings.</param>
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher, WireMockServerSettings settings, bool saveToFile = false)
{
private int _priority;
private string _title;
private string _description;
private string _path;
private string _executionConditionState;
private string _nextState;
private string _scenario;
private int _timesInSameState = 1;
private readonly RegistrationCallback _registrationCallback;
private readonly IRequestMatcher _requestMatcher;
private readonly WireMockServerSettings _settings;
private readonly bool _saveToFile;
_registrationCallback = registrationCallback;
_requestMatcher = requestMatcher;
_settings = settings;
_saveToFile = saveToFile;
}
public Guid Guid { get; private set; } = Guid.NewGuid();
/// <summary>
/// The respond with.
/// </summary>
/// <param name="provider">The provider.</param>
public void RespondWith(IResponseProvider provider)
{
_registrationCallback(new Mapping(Guid, _title, _description, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks, TimeSettings), _saveToFile);
}
public IWebhook[] Webhooks { get; private set; }
/// <inheritdoc />
public IRespondWithAProvider WithGuid(string guid)
{
return WithGuid(Guid.Parse(guid));
}
public ITimeSettings TimeSettings { get; private set; }
/// <inheritdoc />
public IRespondWithAProvider WithGuid(Guid guid)
{
Guid = guid;
/// <summary>
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
/// </summary>
/// <param name="registrationCallback">The registration callback.</param>
/// <param name="requestMatcher">The request matcher.</param>
/// <param name="settings">The WireMockServerSettings.</param>
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher, WireMockServerSettings settings, bool saveToFile = false)
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithTitle(string title)
{
_title = title;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithDescription(string description)
{
_description = description;
return this;
}
/// <see cref="IRespondWithAProvider.WithPath"/>
public IRespondWithAProvider WithPath(string path)
{
_path = path;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider AtPriority(int priority)
{
_priority = priority;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider InScenario(string scenario)
{
_scenario = scenario;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider InScenario(int scenario)
{
return InScenario(scenario.ToString());
}
/// <inheritdoc />
public IRespondWithAProvider WhenStateIs(string state)
{
if (string.IsNullOrEmpty(_scenario))
{
_registrationCallback = registrationCallback;
_requestMatcher = requestMatcher;
_settings = settings;
_saveToFile = saveToFile;
throw new NotSupportedException("Unable to set state condition when no scenario is defined.");
}
/// <summary>
/// The respond with.
/// </summary>
/// <param name="provider">The provider.</param>
public void RespondWith(IResponseProvider provider)
_executionConditionState = state;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WhenStateIs(int state)
{
return WhenStateIs(state.ToString());
}
/// <inheritdoc />
public IRespondWithAProvider WillSetStateTo(string state, int? times = 1)
{
if (string.IsNullOrEmpty(_scenario))
{
_registrationCallback(new Mapping(Guid, _title, _description, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks, TimeSettings), _saveToFile);
throw new NotSupportedException("Unable to set next state when no scenario is defined.");
}
/// <inheritdoc />
public IRespondWithAProvider WithGuid(string guid)
_nextState = state;
_timesInSameState = times ?? 1;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WillSetStateTo(int state, int? times = 1)
{
return WillSetStateTo(state.ToString(), times);
}
/// <inheritdoc />
public IRespondWithAProvider WithTimeSettings(ITimeSettings timeSettings)
{
Guard.NotNull(timeSettings, nameof(timeSettings));
TimeSettings = timeSettings;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithWebhook(params IWebhook[] webhooks)
{
Guard.HasNoNulls(webhooks, nameof(webhooks));
Webhooks = webhooks;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithWebhook(
string url,
string method = "post",
IDictionary<string, WireMockList<string>>? headers = null,
string? body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Guard.NotNull(url);
Guard.NotNull(method);
Webhooks = new[] { InitWebhook(url, method, headers, useTransformer, transformerType) };
if (body != null)
{
return WithGuid(Guid.Parse(guid));
}
/// <inheritdoc />
public IRespondWithAProvider WithGuid(Guid guid)
{
Guid = guid;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithTitle(string title)
{
_title = title;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithDescription(string description)
{
_description = description;
return this;
}
/// <see cref="IRespondWithAProvider.WithPath"/>
public IRespondWithAProvider WithPath(string path)
{
_path = path;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider AtPriority(int priority)
{
_priority = priority;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider InScenario(string scenario)
{
_scenario = scenario;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider InScenario(int scenario)
{
return InScenario(scenario.ToString());
}
/// <inheritdoc />
public IRespondWithAProvider WhenStateIs(string state)
{
if (string.IsNullOrEmpty(_scenario))
Webhooks[0].Request.BodyData = new BodyData
{
throw new NotSupportedException("Unable to set state condition when no scenario is defined.");
}
_executionConditionState = state;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WhenStateIs(int state)
{
return WhenStateIs(state.ToString());
}
/// <inheritdoc />
public IRespondWithAProvider WillSetStateTo(string state, int? times = 1)
{
if (string.IsNullOrEmpty(_scenario))
{
throw new NotSupportedException("Unable to set next state when no scenario is defined.");
}
_nextState = state;
_timesInSameState = times ?? 1;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WillSetStateTo(int state, int? times = 1)
{
return WillSetStateTo(state.ToString(), times);
}
/// <inheritdoc />
public IRespondWithAProvider WithTimeSettings(ITimeSettings timeSettings)
{
Guard.NotNull(timeSettings, nameof(timeSettings));
TimeSettings = timeSettings;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithWebhook(params IWebhook[] webhooks)
{
Guard.HasNoNulls(webhooks, nameof(webhooks));
Webhooks = webhooks;
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] string body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Webhooks = new[] { InitWebhook(url, method, headers, useTransformer, transformerType) };
if (body != null)
{
Webhooks[0].Request.BodyData = new BodyData
{
BodyAsString = body,
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyType.String
};
}
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] object body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Webhooks = new[] { InitWebhook(url, method, headers, useTransformer, transformerType) };
if (body != null)
{
Webhooks[0].Request.BodyData = new BodyData
{
BodyAsJson = body,
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyType.Json
};
}
return this;
}
private static IWebhook InitWebhook(
string url,
string method,
IDictionary<string, WireMockList<string>> headers,
bool useTransformer,
TransformerType transformerType)
{
return new Webhook
{
Request = new WebhookRequest
{
Url = url,
Method = method ?? "post",
Headers = headers,
UseTransformer = useTransformer,
TransformerType = transformerType
}
BodyAsString = body,
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyType.String
};
}
return this;
}
/// <inheritdoc />
public IRespondWithAProvider WithWebhook(
string url,
string method = "post",
IDictionary<string, WireMockList<string>>? headers = null,
object? body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Guard.NotNull(url);
Guard.NotNull(method);
Webhooks = new[] { InitWebhook(url, method, headers, useTransformer, transformerType) };
if (body != null)
{
Webhooks[0].Request.BodyData = new BodyData
{
BodyAsJson = body,
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyType.Json
};
}
return this;
}
private static IWebhook InitWebhook(
string url,
string method,
IDictionary<string, WireMockList<string>>? headers,
bool useTransformer,
TransformerType transformerType)
{
return new Webhook
{
Request = new WebhookRequest
{
Url = url,
Method = method,
Headers = headers,
UseTransformer = useTransformer,
TransformerType = transformerType
}
};
}
}

View File

@@ -234,14 +234,14 @@ public partial class WireMockServer
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var proxyHelper = new ProxyHelper(settings);
var (responseMessage, mapping) = await proxyHelper.SendAsync(
_settings.ProxyAndRecordSettings,
_httpClientForProxy,
_settings.ProxyAndRecordSettings!,
_httpClientForProxy!,
requestMessage,
proxyUriWithRequestPathAndQuery.AbsoluteUri
).ConfigureAwait(false);
@@ -442,73 +442,6 @@ public partial class WireMockServer
}
}
private Guid? ConvertMappingAndRegisterAsRespondProvider(MappingModel mappingModel, Guid? guid = null, string? path = null)
{
Guard.NotNull(mappingModel, nameof(mappingModel));
Guard.NotNull(mappingModel.Request, nameof(mappingModel.Request));
Guard.NotNull(mappingModel.Response, nameof(mappingModel.Response));
var requestBuilder = InitRequestBuilder(mappingModel.Request, true);
if (requestBuilder == null)
{
return null;
}
var responseBuilder = InitResponseBuilder(mappingModel.Response);
var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true);
if (guid != null)
{
respondProvider = respondProvider.WithGuid(guid.Value);
}
else if (mappingModel.Guid != null && mappingModel.Guid != Guid.Empty)
{
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
}
if (mappingModel.TimeSettings != null)
{
respondProvider = respondProvider.WithTimeSettings(TimeSettingsMapper.Map(mappingModel.TimeSettings));
}
if (path != null)
{
respondProvider = respondProvider.WithPath(path);
}
if (!string.IsNullOrEmpty(mappingModel.Title))
{
respondProvider = respondProvider.WithTitle(mappingModel.Title);
}
if (mappingModel.Priority != null)
{
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
}
if (mappingModel.Scenario != null)
{
respondProvider = respondProvider.InScenario(mappingModel.Scenario);
respondProvider = respondProvider.WhenStateIs(mappingModel.WhenStateIs);
respondProvider = respondProvider.WillSetStateTo(mappingModel.SetStateTo);
}
if (mappingModel.Webhook != null)
{
respondProvider = respondProvider.WithWebhook(WebhookMapper.Map(mappingModel.Webhook));
}
else if (mappingModel.Webhooks?.Length > 1)
{
var webhooks = mappingModel.Webhooks.Select(WebhookMapper.Map).ToArray();
respondProvider = respondProvider.WithWebhook(webhooks);
}
respondProvider.RespondWith(responseBuilder);
return respondProvider.Guid;
}
private IResponseMessage MappingsDelete(IRequestMessage requestMessage)
{
if (!string.IsNullOrEmpty(requestMessage.Body))
@@ -518,23 +451,19 @@ public partial class WireMockServer
{
return ResponseMessageBuilder.Create($"Mappings deleted. Affected GUIDs: [{string.Join(", ", deletedGuids.ToArray())}]");
}
else
{
// return bad request
return ResponseMessageBuilder.Create("Poorly formed mapping JSON.", 400);
}
}
else
{
ResetMappings();
ResetScenarios();
return ResponseMessageBuilder.Create("Mappings deleted");
// return bad request
return ResponseMessageBuilder.Create("Poorly formed mapping JSON.", 400);
}
ResetMappings();
ResetScenarios();
return ResponseMessageBuilder.Create("Mappings deleted");
}
private IEnumerable<Guid> MappingsDeleteMappingFromBody(IRequestMessage requestMessage)
private IEnumerable<Guid>? MappingsDeleteMappingFromBody(IRequestMessage requestMessage)
{
var deletedGuids = new List<Guid>();
@@ -577,9 +506,10 @@ public partial class WireMockServer
ResetScenarios();
string message = "Mappings reset";
if (requestMessage.Query.ContainsKey(QueryParamReloadStaticMappings) &&
bool.TryParse(requestMessage.Query[QueryParamReloadStaticMappings].ToString(), out bool reloadStaticMappings)
&& reloadStaticMappings)
if (requestMessage.Query != null &&
requestMessage.Query.ContainsKey(QueryParamReloadStaticMappings) &&
bool.TryParse(requestMessage.Query[QueryParamReloadStaticMappings].ToString(), out bool reloadStaticMappings) &&
reloadStaticMappings)
{
ReadStaticMappings();
message = $"{message} and static mappings reloaded";
@@ -718,220 +648,6 @@ public partial class WireMockServer
return this;
}
#endregion
private IRequestBuilder? InitRequestBuilder(RequestModel requestModel, bool pathOrUrlRequired)
{
IRequestBuilder requestBuilder = Request.Create();
if (requestModel.ClientIP != null)
{
if (requestModel.ClientIP is string clientIP)
{
requestBuilder = requestBuilder.WithClientIP(clientIP);
}
else
{
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
if (clientIPModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
}
bool pathOrUrlMatchersValid = false;
if (requestModel.Path != null)
{
if (requestModel.Path is string path)
{
requestBuilder = requestBuilder.WithPath(path);
pathOrUrlMatchersValid = true;
}
else
{
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(pathModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
pathOrUrlMatchersValid = true;
}
}
}
else if (requestModel.Url != null)
{
if (requestModel.Url is string url)
{
requestBuilder = requestBuilder.WithUrl(url);
pathOrUrlMatchersValid = true;
}
else
{
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithUrl(urlModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
pathOrUrlMatchersValid = true;
}
}
}
if (pathOrUrlRequired && !pathOrUrlMatchersValid)
{
_settings.Logger.Error("Path or Url matcher is missing for this mapping, this mapping will not be added.");
return null;
}
if (requestModel.Methods != null)
{
requestBuilder = requestBuilder.UsingMethod(requestModel.Methods);
}
if (requestModel.Headers != null)
{
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
{
requestBuilder = requestBuilder.WithHeader(
headerModel.Name,
headerModel.IgnoreCase == true,
headerModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
headerModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray()
);
}
}
if (requestModel.Cookies != null)
{
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
{
requestBuilder = requestBuilder.WithCookie(
cookieModel.Name,
cookieModel.IgnoreCase == true,
cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
cookieModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
if (requestModel.Params != null)
{
foreach (var paramModel in requestModel.Params.Where(p => p is { Matchers: { } }))
{
bool ignoreCase = paramModel.IgnoreCase == true;
requestBuilder = requestBuilder.WithParam(paramModel.Name, ignoreCase, paramModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
if (requestModel.Body?.Matcher != null)
{
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matcher));
}
else if (requestModel.Body?.Matchers != null)
{
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers));
}
return requestBuilder;
}
private IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
{
IResponseBuilder responseBuilder = Response.Create();
if (responseModel.Delay > 0)
{
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
}
else if (responseModel.MinimumRandomDelay >= 0 || responseModel.MaximumRandomDelay > 0)
{
responseBuilder = responseBuilder.WithRandomDelay(responseModel.MinimumRandomDelay ?? 0, responseModel.MaximumRandomDelay ?? 60_000);
}
if (responseModel.UseTransformer == true)
{
if (!Enum.TryParse<TransformerType>(responseModel.TransformerType, out var transformerType))
{
transformerType = TransformerType.Handlebars;
}
if (!Enum.TryParse<ReplaceNodeOptions>(responseModel.TransformerReplaceNodeOptions, out var option))
{
option = ReplaceNodeOptions.None;
}
responseBuilder = responseBuilder.WithTransformer(
transformerType,
responseModel.UseTransformerForBodyAsFile == true,
option);
}
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
var proxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = responseModel.ProxyUrl,
ClientX509Certificate2ThumbprintOrSubjectName = responseModel.X509Certificate2ThumbprintOrSubjectName,
WebProxySettings = responseModel.WebProxy != null ? new WebProxySettings
{
Address = responseModel.WebProxy.Address,
UserName = responseModel.WebProxy.UserName,
Password = responseModel.WebProxy.Password
} : null
};
return responseBuilder.WithProxy(proxyAndRecordSettings);
}
if (responseModel.StatusCode is string statusCodeAsString)
{
responseBuilder = responseBuilder.WithStatusCode(statusCodeAsString);
}
else if (responseModel.StatusCode != null)
{
// Convert to Int32 because Newtonsoft deserializes an 'object' with a number value to a long.
responseBuilder = responseBuilder.WithStatusCode(Convert.ToInt32(responseModel.StatusCode));
}
if (responseModel.Headers != null)
{
foreach (var entry in responseModel.Headers)
{
responseBuilder = entry.Value is string value ?
responseBuilder.WithHeader(entry.Key, value) :
responseBuilder.WithHeader(entry.Key, JsonUtils.ParseJTokenToObject<string[]>(entry.Value));
}
}
else if (responseModel.HeadersRaw != null)
{
foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
{
int indexColon = headerLine.IndexOf(":", StringComparison.Ordinal);
string key = headerLine.Substring(0, indexColon).TrimStart(' ', '\t');
string value = headerLine.Substring(indexColon + 1).TrimStart(' ', '\t');
responseBuilder = responseBuilder.WithHeader(key, value);
}
}
if (responseModel.BodyAsBytes != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.BodyAsBytes, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.Body != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.Body, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.BodyAsJson != null)
{
responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding), responseModel.BodyAsJsonIndented == true);
}
else if (responseModel.BodyAsFile != null)
{
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile, responseModel.BodyAsFileIsCached == true);
}
if (responseModel.Fault != null && Enum.TryParse(responseModel.Fault.Type, out FaultType faultType))
{
responseBuilder.WithFault(faultType, responseModel.Fault.Percentage);
}
return responseBuilder;
}
private void DisposeEnhancedFileSystemWatcher()
{

View File

@@ -0,0 +1,318 @@
using System;
using System.Linq;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Server;
public partial class WireMockServer
{
private Guid? ConvertMappingAndRegisterAsRespondProvider(MappingModel mappingModel, Guid? guid = null, string? path = null)
{
Guard.NotNull(mappingModel);
Guard.NotNull(mappingModel.Request, nameof(mappingModel.Request));
Guard.NotNull(mappingModel.Response, nameof(mappingModel.Response));
var requestBuilder = InitRequestBuilder(mappingModel.Request, true);
if (requestBuilder == null)
{
return null;
}
var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true);
if (guid != null)
{
respondProvider = respondProvider.WithGuid(guid.Value);
}
else if (mappingModel.Guid != null && mappingModel.Guid != Guid.Empty)
{
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
}
if (mappingModel.TimeSettings != null)
{
respondProvider = respondProvider.WithTimeSettings(TimeSettingsMapper.Map(mappingModel.TimeSettings));
}
if (path != null)
{
respondProvider = respondProvider.WithPath(path);
}
if (!string.IsNullOrEmpty(mappingModel.Title))
{
respondProvider = respondProvider.WithTitle(mappingModel.Title!);
}
if (mappingModel.Priority != null)
{
respondProvider = respondProvider.AtPriority(mappingModel.Priority.Value);
}
if (mappingModel.Scenario != null)
{
respondProvider = respondProvider.InScenario(mappingModel.Scenario);
if (!string.IsNullOrEmpty(mappingModel.WhenStateIs))
{
respondProvider = respondProvider.WhenStateIs(mappingModel.WhenStateIs!);
}
if (!string.IsNullOrEmpty(mappingModel.SetStateTo))
{
respondProvider = respondProvider.WillSetStateTo(mappingModel.SetStateTo!);
}
}
if (mappingModel.Webhook != null)
{
respondProvider = respondProvider.WithWebhook(WebhookMapper.Map(mappingModel.Webhook));
}
else if (mappingModel.Webhooks?.Length > 1)
{
var webhooks = mappingModel.Webhooks.Select(WebhookMapper.Map).ToArray();
respondProvider = respondProvider.WithWebhook(webhooks);
}
var responseBuilder = InitResponseBuilder(mappingModel.Response);
respondProvider.RespondWith(responseBuilder);
return respondProvider.Guid;
}
private IRequestBuilder? InitRequestBuilder(RequestModel requestModel, bool pathOrUrlRequired)
{
IRequestBuilder requestBuilder = Request.Create();
if (requestModel.ClientIP != null)
{
if (requestModel.ClientIP is string clientIP)
{
requestBuilder = requestBuilder.WithClientIP(clientIP);
}
else
{
var clientIPModel = JsonUtils.ParseJTokenToObject<ClientIPModel>(requestModel.ClientIP);
if (clientIPModel?.Matchers != null)
{
requestBuilder = requestBuilder.WithPath(clientIPModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
}
bool pathOrUrlMatchersValid = false;
if (requestModel.Path != null)
{
if (requestModel.Path is string path)
{
requestBuilder = requestBuilder.WithPath(path);
pathOrUrlMatchersValid = true;
}
else
{
var pathModel = JsonUtils.ParseJTokenToObject<PathModel>(requestModel.Path);
if (pathModel?.Matchers != null)
{
var matchOperator = StringUtils.ParseMatchOperator(pathModel.MatchOperator);
requestBuilder = requestBuilder.WithPath(matchOperator, pathModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
pathOrUrlMatchersValid = true;
}
}
}
else if (requestModel.Url != null)
{
if (requestModel.Url is string url)
{
requestBuilder = requestBuilder.WithUrl(url);
pathOrUrlMatchersValid = true;
}
else
{
var urlModel = JsonUtils.ParseJTokenToObject<UrlModel>(requestModel.Url);
if (urlModel?.Matchers != null)
{
var matchOperator = StringUtils.ParseMatchOperator(urlModel.MatchOperator);
requestBuilder = requestBuilder.WithUrl(matchOperator, urlModel.Matchers.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
pathOrUrlMatchersValid = true;
}
}
}
if (pathOrUrlRequired && !pathOrUrlMatchersValid)
{
_settings.Logger.Error("Path or Url matcher is missing for this mapping, this mapping will not be added.");
return null;
}
if (requestModel.Methods != null)
{
var matchBehaviour = requestModel.MethodsRejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
var matchOperator = StringUtils.ParseMatchOperator(requestModel.MethodsMatchOperator);
requestBuilder = requestBuilder.UsingMethod(matchBehaviour, matchOperator, requestModel.Methods);
}
if (requestModel.Headers != null)
{
foreach (var headerModel in requestModel.Headers.Where(h => h.Matchers != null))
{
var matchOperator = StringUtils.ParseMatchOperator(headerModel.MatchOperator);
requestBuilder = requestBuilder.WithHeader(
headerModel.Name,
headerModel.IgnoreCase == true,
headerModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
matchOperator,
headerModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray()
);
}
}
if (requestModel.Cookies != null)
{
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
{
requestBuilder = requestBuilder.WithCookie(
cookieModel.Name,
cookieModel.IgnoreCase == true,
cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
cookieModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
if (requestModel.Params != null)
{
foreach (var paramModel in requestModel.Params.Where(p => p is { Matchers: { } }))
{
bool ignoreCase = paramModel.IgnoreCase == true;
requestBuilder = requestBuilder.WithParam(paramModel.Name, ignoreCase, paramModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
}
}
if (requestModel.Body?.Matcher != null)
{
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matcher)!);
}
else if (requestModel.Body?.Matchers != null)
{
var matchOperator = StringUtils.ParseMatchOperator(requestModel.Body.MatchOperator);
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers)!, matchOperator);
}
return requestBuilder;
}
private static IResponseBuilder InitResponseBuilder(ResponseModel responseModel)
{
IResponseBuilder responseBuilder = Response.Create();
if (responseModel.Delay > 0)
{
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
}
else if (responseModel.MinimumRandomDelay >= 0 || responseModel.MaximumRandomDelay > 0)
{
responseBuilder = responseBuilder.WithRandomDelay(responseModel.MinimumRandomDelay ?? 0, responseModel.MaximumRandomDelay ?? 60_000);
}
if (responseModel.UseTransformer == true)
{
if (!Enum.TryParse<TransformerType>(responseModel.TransformerType, out var transformerType))
{
transformerType = TransformerType.Handlebars;
}
if (!Enum.TryParse<ReplaceNodeOptions>(responseModel.TransformerReplaceNodeOptions, out var option))
{
option = ReplaceNodeOptions.None;
}
responseBuilder = responseBuilder.WithTransformer(
transformerType,
responseModel.UseTransformerForBodyAsFile == true,
option);
}
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
var proxyAndRecordSettings = new ProxyAndRecordSettings
{
Url = responseModel.ProxyUrl!,
ClientX509Certificate2ThumbprintOrSubjectName = responseModel.X509Certificate2ThumbprintOrSubjectName,
WebProxySettings = responseModel.WebProxy != null ? new WebProxySettings
{
Address = responseModel.WebProxy.Address,
UserName = responseModel.WebProxy.UserName,
Password = responseModel.WebProxy.Password
} : null
};
return responseBuilder.WithProxy(proxyAndRecordSettings);
}
if (responseModel.StatusCode is string statusCodeAsString)
{
responseBuilder = responseBuilder.WithStatusCode(statusCodeAsString);
}
else if (responseModel.StatusCode != null)
{
// Convert to Int32 because Newtonsoft deserializes an 'object' with a number value to a long.
responseBuilder = responseBuilder.WithStatusCode(Convert.ToInt32(responseModel.StatusCode));
}
if (responseModel.Headers != null)
{
foreach (var entry in responseModel.Headers)
{
if (entry.Value is string value)
{
responseBuilder.WithHeader(entry.Key, value);
}
else
{
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value) ?? new string[0];
responseBuilder.WithHeader(entry.Key, headers);
}
}
}
else if (responseModel.HeadersRaw != null)
{
foreach (string headerLine in responseModel.HeadersRaw.Split(new[] { "\n", "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
{
int indexColon = headerLine.IndexOf(":", StringComparison.Ordinal);
string key = headerLine.Substring(0, indexColon).TrimStart(' ', '\t');
string value = headerLine.Substring(indexColon + 1).TrimStart(' ', '\t');
responseBuilder = responseBuilder.WithHeader(key, value);
}
}
if (responseModel.BodyAsBytes != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.BodyAsBytes, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.Body != null)
{
responseBuilder = responseBuilder.WithBody(responseModel.Body, responseModel.BodyDestination, ToEncoding(responseModel.BodyEncoding));
}
else if (responseModel.BodyAsJson != null)
{
responseBuilder = responseBuilder.WithBodyAsJson(responseModel.BodyAsJson, ToEncoding(responseModel.BodyEncoding), responseModel.BodyAsJsonIndented == true);
}
else if (responseModel.BodyAsFile != null)
{
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile, responseModel.BodyAsFileIsCached == true);
}
if (responseModel.Fault != null && Enum.TryParse(responseModel.Fault.Type, out FaultType faultType))
{
responseBuilder.WithFault(faultType, responseModel.Fault.Percentage);
}
return responseBuilder;
}
}

View File

@@ -4,6 +4,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using JetBrains.Annotations;
using Newtonsoft.Json;
@@ -12,6 +13,7 @@ using WireMock.Admin.Mappings;
using WireMock.Authentication;
using WireMock.Exceptions;
using WireMock.Handlers;
using WireMock.Http;
using WireMock.Logging;
using WireMock.Matchers.Request;
using WireMock.Owin;
@@ -101,6 +103,43 @@ public partial class WireMockServer : IWireMockServer
}
#endregion
#region HttpClient
/// <summary>
/// Create a <see cref="HttpClient"/> which can be used to call this instance.
/// </summary>
[PublicAPI]
public HttpClient CreateClient()
{
if (!IsStarted)
{
throw new InvalidOperationException("Unable to create HttpClient because the service is not started.");
}
var client = HttpClientFactory2.Create();
client.BaseAddress = new Uri(Url!);
return client;
}
/// <summary>
/// Create <see cref="HttpClient"/>s (one for each URL) which can be used to call this instance.
/// </summary>
[PublicAPI]
public HttpClient[] CreateClients()
{
if (!IsStarted)
{
throw new InvalidOperationException("Unable to create HttpClients because the service is not started.");
}
return Urls.Select(url =>
{
var client = HttpClientFactory2.Create();
client.BaseAddress = new Uri(url);
return client;
}).ToArray();
}
#endregion
#region Start/Stop
/// <summary>
/// Starts this WireMockServer with the specified settings.