mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-07-09 06:15:18 +02:00
Fix WithBody when using Pact and added more nullable annotations (#783)
* More nullable annotations * . * . * FIX * pact * . * p * xxx * ... * auth * array * ...
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=wiremock/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=wiremockserver/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=wiremockserver/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=xunit/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=xunit/@EntryIndexedValue">True</s:Boolean>
|
||||||
</wpf:ResourceDictionary>
|
</wpf:ResourceDictionary>
|
||||||
@@ -1,24 +1,23 @@
|
|||||||
namespace WireMock.Admin.Mappings
|
namespace WireMock.Admin.Mappings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WebProxy settings
|
||||||
|
/// </summary>
|
||||||
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
|
public class WebProxyModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WebProxy settings
|
/// A string instance that contains the address of the proxy server.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
public string Address { get; set; } = null!;
|
||||||
public class WebProxyModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A string instance that contains the address of the proxy server.
|
|
||||||
/// </summary>
|
|
||||||
public string Address { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The user name associated with the credentials.
|
/// The user name associated with the credentials. [optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string UserName { get; set; }
|
public string? UserName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The password for the user name associated with the credentials.
|
/// The password for the user name associated with the credentials. [optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Password { get; set; }
|
public string? Password { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,50 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Types;
|
|
||||||
|
|
||||||
namespace WireMock.Admin.Mappings
|
namespace WireMock.Admin.Mappings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RequestModel
|
||||||
|
/// </summary>
|
||||||
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
|
public class WebhookRequestModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RequestModel
|
/// Gets or sets the Url.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
public string Url { get; set; }
|
||||||
public class WebhookRequestModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the Url.
|
|
||||||
/// </summary>
|
|
||||||
public string Url { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The method
|
/// The method
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Method { get; set; }
|
public string Method { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the headers.
|
/// Gets or sets the headers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IDictionary<string, string>? Headers { get; set; }
|
public IDictionary<string, string>? Headers { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the body.
|
/// Gets or sets the body.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? Body { get; set; }
|
public string? Body { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the body (as JSON object).
|
/// Gets or sets the body (as JSON object).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? BodyAsJson { get; set; }
|
public object? BodyAsJson { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use ResponseMessage Transformer.
|
/// Use ResponseMessage Transformer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? UseTransformer { get; set; }
|
public bool? UseTransformer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the transformer.
|
/// Gets the type of the transformer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TransformerType { get; set; }
|
public string? TransformerType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TransformerReplaceNodeOptions { get; set; }
|
public string? TransformerReplaceNodeOptions { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -63,12 +63,12 @@ public interface IRequestMessage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the headers.
|
/// Gets the headers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, WireMockList<string>> Headers { get; }
|
IDictionary<string, WireMockList<string>>? Headers { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the cookies.
|
/// Gets the cookies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, string> Cookies { get; }
|
IDictionary<string, string>? Cookies { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the query.
|
/// Gets the query.
|
||||||
|
|||||||
@@ -3,60 +3,59 @@ using WireMock.ResponseBuilders;
|
|||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock
|
namespace WireMock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IResponseMessage
|
||||||
|
/// </summary>
|
||||||
|
public interface IResponseMessage
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IResponseMessage
|
/// The Body.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IResponseMessage
|
IBodyData? BodyData { get; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The Body.
|
|
||||||
/// </summary>
|
|
||||||
IBodyData? BodyData { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the body destination (SameAsSource, String or Bytes).
|
/// Gets the body destination (Null, SameAsSource, String or Bytes).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string BodyDestination { get; }
|
string? BodyDestination { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the body.
|
/// Gets or sets the body.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string BodyOriginal { get; }
|
string? BodyOriginal { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Fault percentage.
|
/// Gets the Fault percentage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
double? FaultPercentage { get; }
|
double? FaultPercentage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The FaultType.
|
/// The FaultType.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
FaultType FaultType { get; }
|
FaultType FaultType { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the headers.
|
/// Gets the headers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IDictionary<string, WireMockList<string>>? Headers { get; }
|
IDictionary<string, WireMockList<string>>? Headers { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the status code.
|
/// Gets or sets the status code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object StatusCode { get; }
|
object? StatusCode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the header.
|
/// Adds the header.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <param name="value">The value.</param>
|
/// <param name="value">The value.</param>
|
||||||
void AddHeader(string name, string value);
|
void AddHeader(string name, string value);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the header.
|
/// Adds the header.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name">The name.</param>
|
/// <param name="name">The name.</param>
|
||||||
/// <param name="values">The values.</param>
|
/// <param name="values">The values.</param>
|
||||||
void AddHeader(string name, params string[] values);
|
void AddHeader(string name, params string[] values);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,56 +1,55 @@
|
|||||||
using System;
|
using System;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Logging
|
namespace WireMock.Logging;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ILogEntry
|
||||||
|
/// </summary>
|
||||||
|
public interface ILogEntry
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ILogEntry
|
/// Gets the unique identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ILogEntry
|
Guid Guid { get; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the unique identifier.
|
|
||||||
/// </summary>
|
|
||||||
Guid Guid { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the mapping unique identifier.
|
/// Gets the mapping unique identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Guid? MappingGuid { get; }
|
Guid? MappingGuid { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the mapping unique title.
|
/// Gets the mapping unique title.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string MappingTitle { get; }
|
string? MappingTitle { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the partial mapping unique identifier.
|
/// Gets the partial mapping unique identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Guid? PartialMappingGuid { get; }
|
Guid? PartialMappingGuid { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the partial mapping unique title.
|
/// Gets the partial mapping unique title.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string PartialMappingTitle { get; }
|
string? PartialMappingTitle { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the partial match result.
|
/// Gets the partial match result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IRequestMatchResult PartialMatchResult { get; }
|
IRequestMatchResult PartialMatchResult { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request match result.
|
/// Gets the request match result.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IRequestMatchResult RequestMatchResult { get; }
|
IRequestMatchResult RequestMatchResult { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request message.
|
/// Gets the request message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IRequestMessage RequestMessage { get; }
|
IRequestMessage RequestMessage { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the response message.
|
/// Gets the response message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IResponseMessage ResponseMessage { get; }
|
IResponseMessage ResponseMessage { get; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
namespace WireMock.Models
|
namespace WireMock.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IWebhook
|
||||||
|
/// </summary>
|
||||||
|
public interface IWebhook
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IWebhook
|
/// Request
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IWebhook
|
IWebhookRequest Request { get; set; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Request
|
|
||||||
/// </summary>
|
|
||||||
IWebhookRequest Request { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
|
|||||||
MatchOperator = matchOperator;
|
MatchOperator = matchOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double IsMatch(string input)
|
public double IsMatch(string? input)
|
||||||
{
|
{
|
||||||
return IsMatchInternal(input);
|
return IsMatchInternal(input);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
@@ -6,67 +7,72 @@ using AnyOfTypes;
|
|||||||
using Microsoft.IdentityModel.Protocols;
|
using Microsoft.IdentityModel.Protocols;
|
||||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
using Stef.Validation;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Authentication
|
namespace WireMock.Authentication;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://www.c-sharpcorner.com/article/how-to-validate-azure-ad-token-using-console-application/
|
||||||
|
/// https://stackoverflow.com/questions/38684865/validation-of-an-azure-ad-bearer-token-in-a-console-application
|
||||||
|
/// </summary>
|
||||||
|
internal class AzureADAuthenticationMatcher : IStringMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
private const string BearerPrefix = "Bearer ";
|
||||||
/// https://www.c-sharpcorner.com/article/how-to-validate-azure-ad-token-using-console-application/
|
|
||||||
/// https://stackoverflow.com/questions/38684865/validation-of-an-azure-ad-bearer-token-in-a-console-application
|
private readonly string _audience;
|
||||||
/// </summary>
|
private readonly string _stsDiscoveryEndpoint;
|
||||||
internal class AzureADAuthenticationMatcher : IStringMatcher
|
|
||||||
|
public AzureADAuthenticationMatcher(string tenant, string audience)
|
||||||
{
|
{
|
||||||
private const string BearerPrefix = "Bearer ";
|
_audience = Guard.NotNullOrEmpty(audience);
|
||||||
|
_stsDiscoveryEndpoint = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}/.well-known/openid-configuration", Guard.NotNullOrEmpty(tenant));
|
||||||
|
}
|
||||||
|
|
||||||
private readonly string _audience;
|
public string Name => nameof(AzureADAuthenticationMatcher);
|
||||||
private readonly string _stsDiscoveryEndpoint;
|
|
||||||
|
|
||||||
public AzureADAuthenticationMatcher(string tenant, string audience)
|
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
|
||||||
|
|
||||||
|
public bool ThrowException => false;
|
||||||
|
|
||||||
|
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||||
|
{
|
||||||
|
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||||
|
|
||||||
|
public double IsMatch(string? input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
{
|
{
|
||||||
_audience = audience;
|
return MatchScores.Mismatch;
|
||||||
_stsDiscoveryEndpoint = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}/.well-known/openid-configuration", tenant);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name => nameof(AzureADAuthenticationMatcher);
|
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase);
|
||||||
|
|
||||||
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
|
try
|
||||||
|
|
||||||
public bool ThrowException => false;
|
|
||||||
|
|
||||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
|
||||||
{
|
{
|
||||||
return new AnyOf<string, StringPattern>[0];
|
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(_stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
|
||||||
|
var config = configManager.GetConfigurationAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
var validationParameters = new TokenValidationParameters
|
||||||
|
{
|
||||||
|
ValidAudience = _audience,
|
||||||
|
ValidIssuer = config.Issuer,
|
||||||
|
IssuerSigningKeys = config.SigningKeys,
|
||||||
|
ValidateLifetime = true
|
||||||
|
};
|
||||||
|
|
||||||
|
// Throws an Exception as the token is invalid (expired, invalid-formatted, etc.)
|
||||||
|
new JwtSecurityTokenHandler().ValidateToken(token, validationParameters, out var _);
|
||||||
|
|
||||||
|
return MatchScores.Perfect;
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
|
||||||
|
|
||||||
public double IsMatch(string input)
|
|
||||||
{
|
{
|
||||||
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase);
|
return MatchScores.Mismatch;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var configManager = new ConfigurationManager<OpenIdConnectConfiguration>(_stsDiscoveryEndpoint, new OpenIdConnectConfigurationRetriever());
|
|
||||||
var config = configManager.GetConfigurationAsync().ConfigureAwait(false).GetAwaiter().GetResult();
|
|
||||||
|
|
||||||
var validationParameters = new TokenValidationParameters
|
|
||||||
{
|
|
||||||
ValidAudience = _audience,
|
|
||||||
ValidIssuer = config.Issuer,
|
|
||||||
IssuerSigningKeys = config.SigningKeys,
|
|
||||||
ValidateLifetime = true
|
|
||||||
};
|
|
||||||
|
|
||||||
// Throws an Exception as the token is invalid (expired, invalid-formatted, etc.)
|
|
||||||
new JwtSecurityTokenHandler().ValidateToken(token, validationParameters, out var _);
|
|
||||||
|
|
||||||
return MatchScores.Perfect;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return MatchScores.Mismatch;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,19 +2,18 @@ using System;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
|
|
||||||
namespace WireMock.Authentication
|
namespace WireMock.Authentication;
|
||||||
|
|
||||||
|
internal class BasicAuthenticationMatcher : RegexMatcher
|
||||||
{
|
{
|
||||||
internal class BasicAuthenticationMatcher : RegexMatcher
|
public BasicAuthenticationMatcher(string username, string password) : base(BuildPattern(username, password))
|
||||||
{
|
{
|
||||||
public BasicAuthenticationMatcher(string username, string password) : base(BuildPattern(username, password))
|
}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string Name => nameof(BasicAuthenticationMatcher);
|
public override string Name => nameof(BasicAuthenticationMatcher);
|
||||||
|
|
||||||
private static string BuildPattern(string username, string password)
|
private static string BuildPattern(string username, string password)
|
||||||
{
|
{
|
||||||
return "^(?i)BASIC " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)) + "$";
|
return "^(?i)BASIC " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)) + "$";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace System;
|
||||||
|
|
||||||
|
internal static class EmptyArray<T>
|
||||||
|
{
|
||||||
|
#if NET451 || NET452
|
||||||
|
public static readonly T[] Value = new T[0];
|
||||||
|
#else
|
||||||
|
public static readonly T[] Value = Array.Empty<T>();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -1,28 +1,26 @@
|
|||||||
#if NETSTANDARD1_3
|
#if NETSTANDARD1_3
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
|
|
||||||
namespace System.Net
|
// ReSharper disable once CheckNamespace
|
||||||
|
namespace System.Net;
|
||||||
|
|
||||||
|
internal class WebProxy : IWebProxy
|
||||||
{
|
{
|
||||||
internal class WebProxy : IWebProxy
|
private readonly string _proxy;
|
||||||
|
public ICredentials? Credentials { get; set; }
|
||||||
|
|
||||||
|
public WebProxy(string proxy)
|
||||||
{
|
{
|
||||||
private readonly string _proxy;
|
_proxy = proxy;
|
||||||
public ICredentials Credentials { get; set; }
|
}
|
||||||
|
|
||||||
public WebProxy(string proxy)
|
public Uri GetProxy(Uri destination)
|
||||||
{
|
{
|
||||||
_proxy = proxy;
|
return new Uri(_proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Uri GetProxy(Uri destination)
|
public bool IsBypassed(Uri host)
|
||||||
{
|
{
|
||||||
return new Uri(_proxy);
|
return true;
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsBypassed(Uri host)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,26 +1,24 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Extensions
|
namespace WireMock.Extensions;
|
||||||
|
|
||||||
|
internal static class AnyOfExtensions
|
||||||
{
|
{
|
||||||
internal static class AnyOfExtensions
|
public static string GetPattern(this AnyOf<string, StringPattern> value)
|
||||||
{
|
{
|
||||||
public static string GetPattern([NotNull] this AnyOf<string, StringPattern> value)
|
return value.IsFirst ? value.First : value.Second.Pattern;
|
||||||
{
|
}
|
||||||
return value.IsFirst ? value.First : value.Second.Pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AnyOf<string, StringPattern>[] ToAnyOfPatterns([NotNull] this IEnumerable<string> patterns)
|
public static AnyOf<string, StringPattern>[] ToAnyOfPatterns(this IEnumerable<string> patterns)
|
||||||
{
|
{
|
||||||
return patterns.Select(p => p.ToAnyOfPattern()).ToArray();
|
return patterns.Select(p => p.ToAnyOfPattern()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AnyOf<string, StringPattern> ToAnyOfPattern([CanBeNull] this string pattern)
|
public static AnyOf<string, StringPattern> ToAnyOfPattern(this string pattern)
|
||||||
{
|
{
|
||||||
return new AnyOf<string, StringPattern>(pattern);
|
return new AnyOf<string, StringPattern>(pattern);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,30 +1,28 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Http
|
namespace WireMock.Http;
|
||||||
|
|
||||||
|
internal static class ByteArrayContentHelper
|
||||||
{
|
{
|
||||||
internal static class ByteArrayContentHelper
|
/// <summary>
|
||||||
|
/// Creates a ByteArrayContent object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">The byte[] content (cannot be null)</param>
|
||||||
|
/// <param name="contentType">The ContentType (can be null)</param>
|
||||||
|
/// <returns>ByteArrayContent</returns>
|
||||||
|
internal static ByteArrayContent Create(byte[] content, MediaTypeHeaderValue? contentType)
|
||||||
{
|
{
|
||||||
/// <summary>
|
Guard.NotNull(content);
|
||||||
/// Creates a ByteArrayContent object.
|
|
||||||
/// </summary>
|
var byteContent = new ByteArrayContent(content);
|
||||||
/// <param name="content">The byte[] content (cannot be null)</param>
|
if (contentType != null)
|
||||||
/// <param name="contentType">The ContentType (can be null)</param>
|
|
||||||
/// <returns>ByteArrayContent</returns>
|
|
||||||
internal static ByteArrayContent Create([NotNull] byte[] content, [CanBeNull] MediaTypeHeaderValue contentType)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(content, nameof(content));
|
byteContent.Headers.Remove(HttpKnownHeaderNames.ContentType);
|
||||||
|
byteContent.Headers.ContentType = contentType;
|
||||||
var byteContent = new ByteArrayContent(content);
|
|
||||||
if (contentType != null)
|
|
||||||
{
|
|
||||||
byteContent.Headers.Remove(HttpKnownHeaderNames.ContentType);
|
|
||||||
byteContent.Headers.ContentType = contentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return byteContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return byteContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,122 +1,121 @@
|
|||||||
// Licensed to the .NET Foundation under one or more agreements.
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||||
// The .NET Foundation licenses this file to you under the MIT license.
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||||
// See the LICENSE file in the project root for more information.
|
// See the LICENSE file in the project root for more information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace WireMock.Http
|
namespace WireMock.Http;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Copied from https://raw.githubusercontent.com/dotnet/corefx/master/src/Common/src/System/Net/HttpKnownHeaderNames.cs
|
||||||
|
/// </summary>
|
||||||
|
internal static class HttpKnownHeaderNames
|
||||||
{
|
{
|
||||||
/// <summary>
|
// https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted
|
||||||
/// Copied from https://raw.githubusercontent.com/dotnet/corefx/master/src/Common/src/System/Net/HttpKnownHeaderNames.cs
|
private static readonly string[] RestrictedResponseHeaders =
|
||||||
/// </summary>
|
|
||||||
internal static class HttpKnownHeaderNames
|
|
||||||
{
|
{
|
||||||
// https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted
|
Accept,
|
||||||
private static readonly string[] RestrictedResponseHeaders =
|
Connection,
|
||||||
{
|
ContentLength,
|
||||||
Accept,
|
ContentType,
|
||||||
Connection,
|
Date, // RFC1123Pattern
|
||||||
ContentLength,
|
Expect,
|
||||||
ContentType,
|
Host,
|
||||||
Date, // RFC1123Pattern
|
IfModifiedSince,
|
||||||
Expect,
|
Range,
|
||||||
Host,
|
Referer,
|
||||||
IfModifiedSince,
|
TransferEncoding,
|
||||||
Range,
|
UserAgent,
|
||||||
Referer,
|
ProxyConnection
|
||||||
TransferEncoding,
|
};
|
||||||
UserAgent,
|
|
||||||
ProxyConnection
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>Tests whether the specified HTTP header can be set for the response.</summary>
|
/// <summary>Tests whether the specified HTTP header can be set for the response.</summary>
|
||||||
/// <param name="headerName">The header to test.</param>
|
/// <param name="headerName">The header to test.</param>
|
||||||
/// <returns>true if the header is restricted; otherwise, false.</returns>
|
/// <returns>true if the header is restricted; otherwise, false.</returns>
|
||||||
public static bool IsRestrictedResponseHeader(string headerName) => RestrictedResponseHeaders.Contains(headerName, StringComparer.OrdinalIgnoreCase);
|
public static bool IsRestrictedResponseHeader(string headerName) => RestrictedResponseHeaders.Contains(headerName, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public const string Accept = "Accept";
|
public const string Accept = "Accept";
|
||||||
public const string AcceptCharset = "Accept-Charset";
|
public const string AcceptCharset = "Accept-Charset";
|
||||||
public const string AcceptEncoding = "Accept-Encoding";
|
public const string AcceptEncoding = "Accept-Encoding";
|
||||||
public const string AcceptLanguage = "Accept-Language";
|
public const string AcceptLanguage = "Accept-Language";
|
||||||
public const string AcceptPatch = "Accept-Patch";
|
public const string AcceptPatch = "Accept-Patch";
|
||||||
public const string AcceptRanges = "Accept-Ranges";
|
public const string AcceptRanges = "Accept-Ranges";
|
||||||
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
|
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
|
||||||
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
|
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
|
||||||
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
|
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
|
||||||
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
|
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
|
||||||
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
|
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
|
||||||
public const string AccessControlMaxAge = "Access-Control-Max-Age";
|
public const string AccessControlMaxAge = "Access-Control-Max-Age";
|
||||||
public const string Age = "Age";
|
public const string Age = "Age";
|
||||||
public const string Allow = "Allow";
|
public const string Allow = "Allow";
|
||||||
public const string AltSvc = "Alt-Svc";
|
public const string AltSvc = "Alt-Svc";
|
||||||
public const string Authorization = "Authorization";
|
public const string Authorization = "Authorization";
|
||||||
public const string CacheControl = "Cache-Control";
|
public const string CacheControl = "Cache-Control";
|
||||||
public const string Connection = "Connection";
|
public const string Connection = "Connection";
|
||||||
public const string ContentDisposition = "Content-Disposition";
|
public const string ContentDisposition = "Content-Disposition";
|
||||||
public const string ContentEncoding = "Content-Encoding";
|
public const string ContentEncoding = "Content-Encoding";
|
||||||
public const string ContentLanguage = "Content-Language";
|
public const string ContentLanguage = "Content-Language";
|
||||||
public const string ContentLength = "Content-Length";
|
public const string ContentLength = "Content-Length";
|
||||||
public const string ContentLocation = "Content-Location";
|
public const string ContentLocation = "Content-Location";
|
||||||
public const string ContentMD5 = "Content-MD5";
|
public const string ContentMD5 = "Content-MD5";
|
||||||
public const string ContentRange = "Content-Range";
|
public const string ContentRange = "Content-Range";
|
||||||
public const string ContentSecurityPolicy = "Content-Security-Policy";
|
public const string ContentSecurityPolicy = "Content-Security-Policy";
|
||||||
public const string ContentType = "Content-Type";
|
public const string ContentType = "Content-Type";
|
||||||
public const string Cookie = "Cookie";
|
public const string Cookie = "Cookie";
|
||||||
public const string Cookie2 = "Cookie2";
|
public const string Cookie2 = "Cookie2";
|
||||||
public const string Date = "Date";
|
public const string Date = "Date";
|
||||||
public const string ETag = "ETag";
|
public const string ETag = "ETag";
|
||||||
public const string Expect = "Expect";
|
public const string Expect = "Expect";
|
||||||
public const string Expires = "Expires";
|
public const string Expires = "Expires";
|
||||||
public const string From = "From";
|
public const string From = "From";
|
||||||
public const string Host = "Host";
|
public const string Host = "Host";
|
||||||
public const string IfMatch = "If-Match";
|
public const string IfMatch = "If-Match";
|
||||||
public const string IfModifiedSince = "If-Modified-Since";
|
public const string IfModifiedSince = "If-Modified-Since";
|
||||||
public const string IfNoneMatch = "If-None-Match";
|
public const string IfNoneMatch = "If-None-Match";
|
||||||
public const string IfRange = "If-Range";
|
public const string IfRange = "If-Range";
|
||||||
public const string IfUnmodifiedSince = "If-Unmodified-Since";
|
public const string IfUnmodifiedSince = "If-Unmodified-Since";
|
||||||
public const string KeepAlive = "Keep-Alive";
|
public const string KeepAlive = "Keep-Alive";
|
||||||
public const string LastModified = "Last-Modified";
|
public const string LastModified = "Last-Modified";
|
||||||
public const string Link = "Link";
|
public const string Link = "Link";
|
||||||
public const string Location = "Location";
|
public const string Location = "Location";
|
||||||
public const string MaxForwards = "Max-Forwards";
|
public const string MaxForwards = "Max-Forwards";
|
||||||
public const string Origin = "Origin";
|
public const string Origin = "Origin";
|
||||||
public const string P3P = "P3P";
|
public const string P3P = "P3P";
|
||||||
public const string Pragma = "Pragma";
|
public const string Pragma = "Pragma";
|
||||||
public const string ProxyAuthenticate = "Proxy-Authenticate";
|
public const string ProxyAuthenticate = "Proxy-Authenticate";
|
||||||
public const string ProxyAuthorization = "Proxy-Authorization";
|
public const string ProxyAuthorization = "Proxy-Authorization";
|
||||||
public const string ProxyConnection = "Proxy-Connection";
|
public const string ProxyConnection = "Proxy-Connection";
|
||||||
public const string PublicKeyPins = "Public-Key-Pins";
|
public const string PublicKeyPins = "Public-Key-Pins";
|
||||||
public const string Range = "Range";
|
public const string Range = "Range";
|
||||||
public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
|
public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
|
||||||
public const string RetryAfter = "Retry-After";
|
public const string RetryAfter = "Retry-After";
|
||||||
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
|
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
|
||||||
public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
|
public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
|
||||||
public const string SecWebSocketKey = "Sec-WebSocket-Key";
|
public const string SecWebSocketKey = "Sec-WebSocket-Key";
|
||||||
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
|
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
|
||||||
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
|
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
|
||||||
public const string Server = "Server";
|
public const string Server = "Server";
|
||||||
public const string SetCookie = "Set-Cookie";
|
public const string SetCookie = "Set-Cookie";
|
||||||
public const string SetCookie2 = "Set-Cookie2";
|
public const string SetCookie2 = "Set-Cookie2";
|
||||||
public const string StrictTransportSecurity = "Strict-Transport-Security";
|
public const string StrictTransportSecurity = "Strict-Transport-Security";
|
||||||
public const string TE = "TE";
|
public const string TE = "TE";
|
||||||
public const string TSV = "TSV";
|
public const string TSV = "TSV";
|
||||||
public const string Trailer = "Trailer";
|
public const string Trailer = "Trailer";
|
||||||
public const string TransferEncoding = "Transfer-Encoding";
|
public const string TransferEncoding = "Transfer-Encoding";
|
||||||
public const string Upgrade = "Upgrade";
|
public const string Upgrade = "Upgrade";
|
||||||
public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
|
public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
|
||||||
public const string UserAgent = "User-Agent";
|
public const string UserAgent = "User-Agent";
|
||||||
public const string Vary = "Vary";
|
public const string Vary = "Vary";
|
||||||
public const string Via = "Via";
|
public const string Via = "Via";
|
||||||
public const string WWWAuthenticate = "WWW-Authenticate";
|
public const string WWWAuthenticate = "WWW-Authenticate";
|
||||||
public const string Warning = "Warning";
|
public const string Warning = "Warning";
|
||||||
public const string XAspNetVersion = "X-AspNet-Version";
|
public const string XAspNetVersion = "X-AspNet-Version";
|
||||||
public const string XContentDuration = "X-Content-Duration";
|
public const string XContentDuration = "X-Content-Duration";
|
||||||
public const string XContentTypeOptions = "X-Content-Type-Options";
|
public const string XContentTypeOptions = "X-Content-Type-Options";
|
||||||
public const string XFrameOptions = "X-Frame-Options";
|
public const string XFrameOptions = "X-Frame-Options";
|
||||||
public const string XMSEdgeRef = "X-MSEdge-Ref";
|
public const string XMSEdgeRef = "X-MSEdge-Ref";
|
||||||
public const string XPoweredBy = "X-Powered-By";
|
public const string XPoweredBy = "X-Powered-By";
|
||||||
public const string XRequestID = "X-Request-ID";
|
public const string XRequestID = "X-Request-ID";
|
||||||
public const string XUACompatible = "X-UA-Compatible";
|
public const string XUACompatible = "X-UA-Compatible";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,89 +3,87 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using WireMock.Types;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Http
|
namespace WireMock.Http;
|
||||||
|
|
||||||
|
internal static class HttpRequestMessageHelper
|
||||||
{
|
{
|
||||||
internal static class HttpRequestMessageHelper
|
internal static HttpRequestMessage Create(IRequestMessage requestMessage, string url)
|
||||||
{
|
{
|
||||||
internal static HttpRequestMessage Create([NotNull] IRequestMessage requestMessage, [NotNull] string url)
|
Guard.NotNull(requestMessage);
|
||||||
|
Guard.NotNullOrEmpty(url);
|
||||||
|
|
||||||
|
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
|
||||||
|
|
||||||
|
MediaTypeHeaderValue? contentType = null;
|
||||||
|
if (requestMessage.Headers != null && requestMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
|
||||||
{
|
{
|
||||||
Guard.NotNull(requestMessage, nameof(requestMessage));
|
var value = requestMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
|
||||||
Guard.NotNullOrEmpty(url, nameof(url));
|
MediaTypeHeaderValue.TryParse(value, out contentType);
|
||||||
|
}
|
||||||
|
|
||||||
var httpRequestMessage = new HttpRequestMessage(new HttpMethod(requestMessage.Method), url);
|
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||||
|
{
|
||||||
|
case BodyType.Bytes:
|
||||||
|
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes, contentType);
|
||||||
|
break;
|
||||||
|
|
||||||
MediaTypeHeaderValue contentType = null;
|
case BodyType.Json:
|
||||||
if (requestMessage.Headers != null && requestMessage.Headers.ContainsKey(HttpKnownHeaderNames.ContentType))
|
httpRequestMessage.Content = StringContentHelper.Create(JsonConvert.SerializeObject(requestMessage.BodyData.BodyAsJson), contentType);
|
||||||
{
|
break;
|
||||||
var value = requestMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
|
|
||||||
MediaTypeHeaderValue.TryParse(value, out contentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
case BodyType.String:
|
||||||
{
|
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString, contentType);
|
||||||
case BodyType.Bytes:
|
break;
|
||||||
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes, contentType);
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Json:
|
// Overwrite the host header
|
||||||
httpRequestMessage.Content = StringContentHelper.Create(JsonConvert.SerializeObject(requestMessage.BodyData.BodyAsJson), contentType);
|
httpRequestMessage.Headers.Host = new Uri(url).Authority;
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.String:
|
|
||||||
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString, contentType);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite the host header
|
|
||||||
httpRequestMessage.Headers.Host = new Uri(url).Authority;
|
|
||||||
|
|
||||||
// Set other headers if present
|
|
||||||
if (requestMessage.Headers == null || requestMessage.Headers.Count == 0)
|
|
||||||
{
|
|
||||||
return httpRequestMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
var excludeHeaders = new List<string> { HttpKnownHeaderNames.Host, HttpKnownHeaderNames.ContentLength };
|
|
||||||
if (contentType != null)
|
|
||||||
{
|
|
||||||
// Content-Type should be set on the content
|
|
||||||
excludeHeaders.Add(HttpKnownHeaderNames.ContentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var header in requestMessage.Headers.Where(h => !excludeHeaders.Contains(h.Key, StringComparer.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
// Skip if already added. We need to ToList() else calling httpRequestMessage.Headers.Contains() with a header starting with a ":" throws an exception.
|
|
||||||
if (httpRequestMessage.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip if already added. We need to ToList() else calling httpRequestMessage.Content.Headers.Contains(...) with a header starting with a ":" throws an exception.
|
|
||||||
if (httpRequestMessage.Content != null && httpRequestMessage.Content.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to add to request headers. If failed - try to add to content headers. If still fails, just ignore this header.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
|
|
||||||
{
|
|
||||||
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Just continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Set other headers if present
|
||||||
|
if (requestMessage.Headers == null || requestMessage.Headers.Count == 0)
|
||||||
|
{
|
||||||
return httpRequestMessage;
|
return httpRequestMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var excludeHeaders = new List<string> { HttpKnownHeaderNames.Host, HttpKnownHeaderNames.ContentLength };
|
||||||
|
if (contentType != null)
|
||||||
|
{
|
||||||
|
// Content-Type should be set on the content
|
||||||
|
excludeHeaders.Add(HttpKnownHeaderNames.ContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var header in requestMessage.Headers.Where(h => !excludeHeaders.Contains(h.Key, StringComparer.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
// Skip if already added. We need to ToList() else calling httpRequestMessage.Headers.Contains() with a header starting with a ":" throws an exception.
|
||||||
|
if (httpRequestMessage.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip if already added. We need to ToList() else calling httpRequestMessage.Content.Headers.Contains(...) with a header starting with a ":" throws an exception.
|
||||||
|
if (httpRequestMessage.Content != null && httpRequestMessage.Content.Headers.ToList().Any(h => string.Equals(h.Key, header.Key, StringComparison.OrdinalIgnoreCase)))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to add to request headers. If failed - try to add to content headers. If still fails, just ignore this header.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!httpRequestMessage.Headers.TryAddWithoutValidation(header.Key, header.Value))
|
||||||
|
{
|
||||||
|
httpRequestMessage.Content?.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Just continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return httpRequestMessage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
namespace WireMock.Http
|
namespace WireMock.Http;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
||||||
|
/// </summary>
|
||||||
|
internal static class HttpRequestMethods
|
||||||
{
|
{
|
||||||
/// <summary>
|
public const string CONNECT = "CONNECT";
|
||||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
public const string DELETE = "DELETE";
|
||||||
/// </summary>
|
public const string GET = "GET";
|
||||||
internal static class HttpRequestMethods
|
public const string HEAD = "HEAD";
|
||||||
{
|
public const string OPTIONS = "OPTIONS";
|
||||||
public const string CONNECT = "CONNECT";
|
public const string PATCH = "PATCH";
|
||||||
public const string DELETE = "DELETE";
|
public const string POST = "POST";
|
||||||
public const string GET = "GET";
|
public const string PUT = "PUT";
|
||||||
public const string HEAD = "HEAD";
|
public const string TRACE = "TRACE";
|
||||||
public const string OPTIONS = "OPTIONS";
|
|
||||||
public const string PATCH = "PATCH";
|
|
||||||
public const string POST = "POST";
|
|
||||||
public const string PUT = "PUT";
|
|
||||||
public const string TRACE = "TRACE";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,23 @@
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Http
|
namespace WireMock.Http;
|
||||||
{
|
|
||||||
internal static class StringContentHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Creates a StringContent object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="content">The string content (cannot be null)</param>
|
|
||||||
/// <param name="contentType">The ContentType (can be null)</param>
|
|
||||||
/// <returns>StringContent</returns>
|
|
||||||
internal static StringContent Create([NotNull] string content, [CanBeNull] MediaTypeHeaderValue contentType)
|
|
||||||
{
|
|
||||||
Guard.NotNull(content, nameof(content));
|
|
||||||
|
|
||||||
var stringContent = new StringContent(content);
|
internal static class StringContentHelper
|
||||||
stringContent.Headers.ContentType = contentType;
|
{
|
||||||
return stringContent;
|
/// <summary>
|
||||||
}
|
/// Creates a StringContent object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="content">The string content (cannot be null)</param>
|
||||||
|
/// <param name="contentType">The ContentType (can be null)</param>
|
||||||
|
/// <returns>StringContent</returns>
|
||||||
|
internal static StringContent Create(string content, MediaTypeHeaderValue? contentType)
|
||||||
|
{
|
||||||
|
Guard.NotNull(content);
|
||||||
|
|
||||||
|
var stringContent = new StringContent(content);
|
||||||
|
stringContent.Headers.ContentType = contentType;
|
||||||
|
return stringContent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
using Stef.Validation;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Transformers;
|
using WireMock.Transformers;
|
||||||
@@ -11,75 +11,73 @@ using WireMock.Transformers.Handlebars;
|
|||||||
using WireMock.Transformers.Scriban;
|
using WireMock.Transformers.Scriban;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Stef.Validation;
|
|
||||||
|
|
||||||
namespace WireMock.Http
|
namespace WireMock.Http;
|
||||||
|
|
||||||
|
internal class WebhookSender
|
||||||
{
|
{
|
||||||
internal class WebhookSender
|
private const string ClientIp = "::1";
|
||||||
|
|
||||||
|
private readonly WireMockServerSettings _settings;
|
||||||
|
|
||||||
|
public WebhookSender(WireMockServerSettings settings)
|
||||||
{
|
{
|
||||||
private const string ClientIp = "::1";
|
_settings = Guard.NotNull(settings);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly WireMockServerSettings _settings;
|
public Task<HttpResponseMessage> SendAsync(HttpClient client, IWebhookRequest request, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage)
|
||||||
|
{
|
||||||
|
Guard.NotNull(client);
|
||||||
|
Guard.NotNull(request);
|
||||||
|
Guard.NotNull(originalRequestMessage);
|
||||||
|
Guard.NotNull(originalResponseMessage);
|
||||||
|
|
||||||
public WebhookSender(WireMockServerSettings settings)
|
IBodyData? bodyData;
|
||||||
|
IDictionary<string, WireMockList<string>>? headers;
|
||||||
|
if (request.UseTransformer == true)
|
||||||
{
|
{
|
||||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
ITransformer responseMessageTransformer;
|
||||||
}
|
switch (request.TransformerType)
|
||||||
|
|
||||||
public Task<HttpResponseMessage> SendAsync(HttpClient client, IWebhookRequest request, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage)
|
|
||||||
{
|
|
||||||
Guard.NotNull(client);
|
|
||||||
Guard.NotNull(request);
|
|
||||||
Guard.NotNull(originalRequestMessage);
|
|
||||||
Guard.NotNull(originalResponseMessage);
|
|
||||||
|
|
||||||
IBodyData? bodyData;
|
|
||||||
IDictionary<string, WireMockList<string>>? headers;
|
|
||||||
if (request.UseTransformer == true)
|
|
||||||
{
|
{
|
||||||
ITransformer responseMessageTransformer;
|
case TransformerType.Handlebars:
|
||||||
switch (request.TransformerType)
|
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
|
||||||
{
|
responseMessageTransformer = new Transformer(factoryHandlebars);
|
||||||
case TransformerType.Handlebars:
|
break;
|
||||||
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
|
|
||||||
responseMessageTransformer = new Transformer(factoryHandlebars);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TransformerType.Scriban:
|
case TransformerType.Scriban:
|
||||||
case TransformerType.ScribanDotLiquid:
|
case TransformerType.ScribanDotLiquid:
|
||||||
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, request.TransformerType);
|
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, request.TransformerType);
|
||||||
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
|
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
|
||||||
}
|
|
||||||
|
|
||||||
(bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers, request.TransformerReplaceNodeOptions);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bodyData = request.BodyData;
|
|
||||||
headers = request.Headers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create RequestMessage
|
(bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers, request.TransformerReplaceNodeOptions);
|
||||||
var requestMessage = new RequestMessage(
|
|
||||||
new UrlDetails(request.Url),
|
|
||||||
request.Method,
|
|
||||||
ClientIp,
|
|
||||||
bodyData,
|
|
||||||
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
|
|
||||||
)
|
|
||||||
{
|
|
||||||
DateTime = DateTime.UtcNow
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create HttpRequestMessage
|
|
||||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, request.Url);
|
|
||||||
|
|
||||||
// Call the URL
|
|
||||||
return client.SendAsync(httpRequestMessage);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bodyData = request.BodyData;
|
||||||
|
headers = request.Headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create RequestMessage
|
||||||
|
var requestMessage = new RequestMessage(
|
||||||
|
new UrlDetails(request.Url),
|
||||||
|
request.Method,
|
||||||
|
ClientIp,
|
||||||
|
bodyData,
|
||||||
|
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DateTime = DateTime.UtcNow
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create HttpRequestMessage
|
||||||
|
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, request.Url);
|
||||||
|
|
||||||
|
// Call the URL
|
||||||
|
return client.SendAsync(httpRequestMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,90 +2,40 @@ using System;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace WireMock.HttpsCertificate
|
namespace WireMock.HttpsCertificate;
|
||||||
|
|
||||||
|
internal static class CertificateLoader
|
||||||
{
|
{
|
||||||
internal static class CertificateLoader
|
/// <summary>
|
||||||
|
/// Used by the WireMock.Net server
|
||||||
|
/// </summary>
|
||||||
|
public static X509Certificate2 LoadCertificate(
|
||||||
|
string storeName,
|
||||||
|
string storeLocation,
|
||||||
|
string thumbprintOrSubjectName,
|
||||||
|
string filePath,
|
||||||
|
string password,
|
||||||
|
string host)
|
||||||
{
|
{
|
||||||
/// <summary>
|
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
|
||||||
/// Used by the WireMock.Net server
|
|
||||||
/// </summary>
|
|
||||||
public static X509Certificate2 LoadCertificate(
|
|
||||||
string storeName,
|
|
||||||
string storeLocation,
|
|
||||||
string thumbprintOrSubjectName,
|
|
||||||
string filePath,
|
|
||||||
string password,
|
|
||||||
string host)
|
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
|
var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
|
||||||
{
|
|
||||||
var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
|
|
||||||
|
|
||||||
var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
|
var certStore = new X509Store((StoreName)Enum.Parse(typeof(StoreName), storeName), (StoreLocation)Enum.Parse(typeof(StoreLocation), storeLocation));
|
||||||
try
|
|
||||||
{
|
|
||||||
certStore.Open(OpenFlags.ReadOnly);
|
|
||||||
|
|
||||||
// Attempt to find by Thumbprint first
|
|
||||||
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectNameOrHost, false);
|
|
||||||
if (matchingCertificates.Count == 0)
|
|
||||||
{
|
|
||||||
// Fallback to SubjectName
|
|
||||||
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectNameOrHost, false);
|
|
||||||
if (matchingCertificates.Count == 0)
|
|
||||||
{
|
|
||||||
// No certificates matched the search criteria.
|
|
||||||
throw new FileNotFoundException($"No Certificate found with in store '{storeName}', location '{storeLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the first matching certificate.
|
|
||||||
return matchingCertificates[0];
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
#if NETSTANDARD || NET46
|
|
||||||
certStore.Dispose();
|
|
||||||
#else
|
|
||||||
certStore.Close();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(password))
|
|
||||||
{
|
|
||||||
return new X509Certificate2(filePath, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(filePath))
|
|
||||||
{
|
|
||||||
return new X509Certificate2(filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath are mandatory. Note that X509CertificatePassword is optional.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used for Proxy
|
|
||||||
/// </summary>
|
|
||||||
public static X509Certificate2 LoadCertificate(string thumbprintOrSubjectName)
|
|
||||||
{
|
|
||||||
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Certificate must be in the local machine store
|
|
||||||
certStore.Open(OpenFlags.ReadOnly);
|
certStore.Open(OpenFlags.ReadOnly);
|
||||||
|
|
||||||
// Attempt to find by Thumbprint first
|
// Attempt to find by Thumbprint first
|
||||||
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
|
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectNameOrHost, false);
|
||||||
if (matchingCertificates.Count == 0)
|
if (matchingCertificates.Count == 0)
|
||||||
{
|
{
|
||||||
// Fallback to SubjectName
|
// Fallback to SubjectName
|
||||||
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
|
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectNameOrHost, false);
|
||||||
if (matchingCertificates.Count == 0)
|
if (matchingCertificates.Count == 0)
|
||||||
{
|
{
|
||||||
// No certificates matched the search criteria.
|
// No certificates matched the search criteria.
|
||||||
throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
|
throw new FileNotFoundException($"No Certificate found with in store '{storeName}', location '{storeLocation}' for Thumbprint or SubjectName '{thumbprintOrSubjectNameOrHost}'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,9 +47,58 @@ namespace WireMock.HttpsCertificate
|
|||||||
#if NETSTANDARD || NET46
|
#if NETSTANDARD || NET46
|
||||||
certStore.Dispose();
|
certStore.Dispose();
|
||||||
#else
|
#else
|
||||||
certStore.Close();
|
certStore.Close();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filePath) && !string.IsNullOrEmpty(password))
|
||||||
|
{
|
||||||
|
return new X509Certificate2(filePath, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(filePath))
|
||||||
|
{
|
||||||
|
return new X509Certificate2(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("X509StoreName and X509StoreLocation OR X509CertificateFilePath are mandatory. Note that X509CertificatePassword is optional.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for Proxy
|
||||||
|
/// </summary>
|
||||||
|
public static X509Certificate2 LoadCertificate(string thumbprintOrSubjectName)
|
||||||
|
{
|
||||||
|
var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Certificate must be in the local machine store
|
||||||
|
certStore.Open(OpenFlags.ReadOnly);
|
||||||
|
|
||||||
|
// Attempt to find by Thumbprint first
|
||||||
|
var matchingCertificates = certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprintOrSubjectName, false);
|
||||||
|
if (matchingCertificates.Count == 0)
|
||||||
|
{
|
||||||
|
// Fallback to SubjectName
|
||||||
|
matchingCertificates = certStore.Certificates.Find(X509FindType.FindBySubjectName, thumbprintOrSubjectName, false);
|
||||||
|
if (matchingCertificates.Count == 0)
|
||||||
|
{
|
||||||
|
// No certificates matched the search criteria.
|
||||||
|
throw new FileNotFoundException("No certificate found with specified Thumbprint or SubjectName.", thumbprintOrSubjectName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the first matching certificate.
|
||||||
|
return matchingCertificates[0];
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
#if NETSTANDARD || NET46
|
||||||
|
certStore.Dispose();
|
||||||
|
#else
|
||||||
|
certStore.Close();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,16 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace WireMock.HttpsCertificate
|
namespace WireMock.HttpsCertificate;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Only used for NetStandard 1.3
|
||||||
|
/// </summary>
|
||||||
|
internal static class PublicCertificateHelper
|
||||||
{
|
{
|
||||||
/// <summary>
|
// 1] Generate using https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net
|
||||||
/// Only used for NetStandard 1.3
|
// 2] Converted to Base64
|
||||||
/// </summary>
|
private const string Data = @"MIIQMgIBAzCCD+4GCSqGSIb3DQEHAaCCD98Egg/bMIIP1zCCCogGCSqGSIb3DQEHAaCCCnkEggp1
|
||||||
internal static class PublicCertificateHelper
|
|
||||||
{
|
|
||||||
// 1] Generate using https://www.pluralsight.com/blog/software-development/selfcert-create-a-self-signed-certificate-interactively-gui-or-programmatically-in-net
|
|
||||||
// 2] Converted to Base64
|
|
||||||
private const string Data = @"MIIQMgIBAzCCD+4GCSqGSIb3DQEHAaCCD98Egg/bMIIP1zCCCogGCSqGSIb3DQEHAaCCCnkEggp1
|
|
||||||
MIIKcTCCCm0GCyqGSIb3DQEMCgECoIIJfjCCCXowHAYKKoZIhvcNAQwBAzAOBAi1j9x1jTfUewIC
|
MIIKcTCCCm0GCyqGSIb3DQEMCgECoIIJfjCCCXowHAYKKoZIhvcNAQwBAzAOBAi1j9x1jTfUewIC
|
||||||
B9AEgglYa48lP16+isiGEVT7zwN3XwaPwPOHZcQ7tRA/DA8LZnZbwU7XhtPObF5bZcHn4engX2An
|
B9AEgglYa48lP16+isiGEVT7zwN3XwaPwPOHZcQ7tRA/DA8LZnZbwU7XhtPObF5bZcHn4engX2An
|
||||||
ISFpe2S5XJ7BfHmsGOO7Bxj6C2IcZIPTefvAd9vWE0WUAGN11SLhJ3fB/ZRt3Nys7JCJzywQCkYK
|
ISFpe2S5XJ7BfHmsGOO7Bxj6C2IcZIPTefvAd9vWE0WUAGN11SLhJ3fB/ZRt3Nys7JCJzywQCkYK
|
||||||
@@ -85,10 +85,9 @@ TLNGa+UmMnPsnBjlAJ6l9VPsa4uJM2DIQKtZXWq4DkhSAYKF6joIP7nKMDswHzAHBgUrDgMCGgQU
|
|||||||
wTM1Z+CJZG9xAcf1zAVGl4ggYyYEFGBFyJ8VBwijS2zy1qwN1WYGtcWoAgIH0A==
|
wTM1Z+CJZG9xAcf1zAVGl4ggYyYEFGBFyJ8VBwijS2zy1qwN1WYGtcWoAgIH0A==
|
||||||
";
|
";
|
||||||
|
|
||||||
public static X509Certificate2 GetX509Certificate2()
|
public static X509Certificate2 GetX509Certificate2()
|
||||||
{
|
{
|
||||||
byte[] data = Convert.FromBase64String(Data);
|
byte[] data = Convert.FromBase64String(Data);
|
||||||
return new X509Certificate2(data);
|
return new X509Certificate2(data);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,7 +117,7 @@ public interface IMapping
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestMessage">The request message.</param>
|
/// <param name="requestMessage">The request message.</param>
|
||||||
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
||||||
Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage);
|
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the RequestMatchResult based on the RequestMessage.
|
/// Gets the RequestMatchResult based on the RequestMessage.
|
||||||
|
|||||||
@@ -1,38 +1,37 @@
|
|||||||
using System;
|
using System;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.Logging
|
namespace WireMock.Logging;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// LogEntry
|
||||||
|
/// </summary>
|
||||||
|
public class LogEntry : ILogEntry
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc cref="ILogEntry.Guid" />
|
||||||
/// LogEntry
|
public Guid Guid { get; set; }
|
||||||
/// </summary>
|
|
||||||
public class LogEntry : ILogEntry
|
|
||||||
{
|
|
||||||
/// <inheritdoc cref="ILogEntry.Guid" />
|
|
||||||
public Guid Guid { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.RequestMessage" />
|
/// <inheritdoc cref="ILogEntry.RequestMessage" />
|
||||||
public IRequestMessage RequestMessage { get; set; }
|
public IRequestMessage RequestMessage { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.ResponseMessage" />
|
/// <inheritdoc cref="ILogEntry.ResponseMessage" />
|
||||||
public IResponseMessage ResponseMessage { get; set; }
|
public IResponseMessage ResponseMessage { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.RequestMatchResult" />
|
/// <inheritdoc cref="ILogEntry.RequestMatchResult" />
|
||||||
public IRequestMatchResult RequestMatchResult { get; set; }
|
public IRequestMatchResult RequestMatchResult { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.MappingGuid" />
|
/// <inheritdoc cref="ILogEntry.MappingGuid" />
|
||||||
public Guid? MappingGuid { get; set; }
|
public Guid? MappingGuid { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.MappingTitle" />
|
/// <inheritdoc cref="ILogEntry.MappingTitle" />
|
||||||
public string MappingTitle { get; set; }
|
public string? MappingTitle { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.PartialMappingGuid" />
|
/// <inheritdoc cref="ILogEntry.PartialMappingGuid" />
|
||||||
public Guid? PartialMappingGuid { get; set; }
|
public Guid? PartialMappingGuid { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.PartialMappingTitle" />
|
/// <inheritdoc cref="ILogEntry.PartialMappingTitle" />
|
||||||
public string PartialMappingTitle { get; set; }
|
public string? PartialMappingTitle { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="ILogEntry.PartialMatchResult" />
|
/// <inheritdoc cref="ILogEntry.PartialMatchResult" />
|
||||||
public IRequestMatchResult PartialMatchResult { get; set; }
|
public IRequestMatchResult PartialMatchResult { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+15
-15
@@ -15,14 +15,14 @@ public class Mapping : IMapping
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public Guid Guid { get; }
|
public Guid Guid { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string? Title { get; }
|
public string? Title { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string? Description { get; }
|
public string? Description { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string? Path { get; set; }
|
public string? Path { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int Priority { get; }
|
public int Priority { get; }
|
||||||
@@ -115,16 +115,16 @@ public class Mapping : IMapping
|
|||||||
TimeSettings = timeSettings;
|
TimeSettings = timeSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||||
public Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
|
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
return Provider.ProvideResponseAsync(requestMessage, Settings);
|
return Provider.ProvideResponseAsync(requestMessage, Settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
|
/// <inheritdoc cref="IMapping.GetRequestMatchResult" />
|
||||||
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
|
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
|
||||||
{
|
{
|
||||||
var result = new RequestMatchResult();
|
var result = new RequestMatchResult();
|
||||||
|
|
||||||
RequestMatcher.GetMatchingScore(requestMessage, result);
|
RequestMatcher.GetMatchingScore(requestMessage, result);
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class ContentTypeMatcher : WildcardMatcher
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pattern">The pattern.</param>
|
/// <param name="pattern">The pattern.</param>
|
||||||
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
||||||
public ContentTypeMatcher([NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
|
public ContentTypeMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ public class ContentTypeMatcher : WildcardMatcher
|
|||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
/// <param name="pattern">The pattern.</param>
|
/// <param name="pattern">The pattern.</param>
|
||||||
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
||||||
public ContentTypeMatcher(MatchBehaviour matchBehaviour, [NotNull] AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
|
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ public class ContentTypeMatcher : WildcardMatcher
|
|||||||
/// <inheritdoc cref="RegexMatcher.IsMatch"/>
|
/// <inheritdoc cref="RegexMatcher.IsMatch"/>
|
||||||
public override double IsMatch(string? input)
|
public override double IsMatch(string? input)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out MediaTypeHeaderValue contentType))
|
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
|
||||||
{
|
{
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CSharpCode / CS-Script Matcher
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="IObjectMatcher"/>
|
||||||
|
/// <inheritdoc cref="IStringMatcher"/>
|
||||||
|
public interface ICSharpCodeMatcher : IObjectMatcher, IStringMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// CSharpCode / CS-Script Matcher
|
|
||||||
/// </summary>
|
|
||||||
/// <inheritdoc cref="IObjectMatcher"/>
|
|
||||||
/// <inheritdoc cref="IStringMatcher"/>
|
|
||||||
public interface ICSharpCodeMatcher : IObjectMatcher, IStringMatcher
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IIgnoreCaseMatcher
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="IMatcher"/>
|
||||||
|
public interface IIgnoreCaseMatcher : IMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IIgnoreCaseMatcher
|
/// Ignore the case from the pattern.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc cref="IMatcher"/>
|
bool IgnoreCase { get; }
|
||||||
public interface IIgnoreCaseMatcher : IMatcher
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Ignore the case from the pattern.
|
|
||||||
/// </summary>
|
|
||||||
bool IgnoreCase { get; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,23 +1,22 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IMatcher
|
||||||
|
/// </summary>
|
||||||
|
public interface IMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IMatcher
|
/// Gets the name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IMatcher
|
string Name { get; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name.
|
|
||||||
/// </summary>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the match behaviour.
|
/// Gets the match behaviour.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MatchBehaviour MatchBehaviour { get; }
|
MatchBehaviour MatchBehaviour { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Should this matcher throw an exception?
|
/// Should this matcher throw an exception?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool ThrowException { get; }
|
bool ThrowException { get; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IObjectMatcher
|
||||||
|
/// </summary>
|
||||||
|
public interface IObjectMatcher : IMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IObjectMatcher
|
/// Determines whether the specified input is match.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IObjectMatcher : IMatcher
|
/// <param name="input">The input.</param>
|
||||||
{
|
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||||
/// <summary>
|
double IsMatch(object? input);
|
||||||
/// Determines whether the specified input is match.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="input">The input.</param>
|
|
||||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
|
||||||
double IsMatch(object? input);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,14 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IValueMatcher
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="IObjectMatcher" />
|
||||||
|
public interface IValueMatcher : IObjectMatcher
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IValueMatcher
|
/// Gets the value (can be a string or an object).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IObjectMatcher" />
|
/// <returns>Value</returns>
|
||||||
public interface IValueMatcher : IObjectMatcher
|
object Value { get; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value (can be a string or an object).
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Value</returns>
|
|
||||||
object Value { get; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -6,115 +6,114 @@ using Stef.Validation;
|
|||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// http://jmespath.org/
|
||||||
|
/// </summary>
|
||||||
|
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
||||||
{
|
{
|
||||||
|
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
|
||||||
|
public MatchBehaviour MatchBehaviour { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IMatcher.ThrowException"/>
|
||||||
|
public bool ThrowException { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// http://jmespath.org/
|
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
/// <param name="patterns">The patterns.</param>
|
||||||
|
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns.ToAnyOfPatterns())
|
||||||
{
|
{
|
||||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
|
|
||||||
public MatchBehaviour MatchBehaviour { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.ThrowException"/>
|
|
||||||
public bool ThrowException { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="patterns">The patterns.</param>
|
|
||||||
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns.ToAnyOfPatterns())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="patterns">The patterns.</param>
|
|
||||||
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
|
||||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
|
||||||
/// <param name="patterns">The patterns.</param>
|
|
||||||
public JmesPathMatcher(bool throwException = false, MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
|
|
||||||
this(MatchBehaviour.AcceptOnMatch, throwException, matchOperator, patterns)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
|
||||||
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
|
||||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
|
||||||
/// <param name="patterns">The patterns.</param>
|
|
||||||
public JmesPathMatcher(
|
|
||||||
MatchBehaviour matchBehaviour,
|
|
||||||
bool throwException = false,
|
|
||||||
MatchOperator matchOperator = MatchOperator.Or,
|
|
||||||
params AnyOf<string, StringPattern>[] patterns)
|
|
||||||
{
|
|
||||||
_patterns = Guard.NotNull(patterns);
|
|
||||||
MatchBehaviour = matchBehaviour;
|
|
||||||
ThrowException = throwException;
|
|
||||||
MatchOperator = matchOperator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
|
||||||
public double IsMatch(string? input)
|
|
||||||
{
|
|
||||||
double match = MatchScores.Mismatch;
|
|
||||||
if (input != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
|
|
||||||
match = MatchScores.ToScore(results, MatchOperator);
|
|
||||||
}
|
|
||||||
catch (JsonException)
|
|
||||||
{
|
|
||||||
if (ThrowException)
|
|
||||||
{
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
|
||||||
public double IsMatch(object? input)
|
|
||||||
{
|
|
||||||
double match = MatchScores.Mismatch;
|
|
||||||
|
|
||||||
// When input is null or byte[], return Mismatch.
|
|
||||||
if (input != null && !(input is byte[]))
|
|
||||||
{
|
|
||||||
string inputAsString = JsonConvert.SerializeObject(input);
|
|
||||||
return IsMatch(inputAsString);
|
|
||||||
}
|
|
||||||
|
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
|
|
||||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
|
||||||
{
|
|
||||||
return _patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public MatchOperator MatchOperator { get; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.Name"/>
|
|
||||||
public string Name => "JmesPathMatcher";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="patterns">The patterns.</param>
|
||||||
|
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, patterns)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
|
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
||||||
|
/// <param name="patterns">The patterns.</param>
|
||||||
|
public JmesPathMatcher(bool throwException = false, MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
|
||||||
|
this(MatchBehaviour.AcceptOnMatch, throwException, matchOperator, patterns)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
|
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
||||||
|
/// <param name="patterns">The patterns.</param>
|
||||||
|
public JmesPathMatcher(
|
||||||
|
MatchBehaviour matchBehaviour,
|
||||||
|
bool throwException = false,
|
||||||
|
MatchOperator matchOperator = MatchOperator.Or,
|
||||||
|
params AnyOf<string, StringPattern>[] patterns)
|
||||||
|
{
|
||||||
|
_patterns = Guard.NotNull(patterns);
|
||||||
|
MatchBehaviour = matchBehaviour;
|
||||||
|
ThrowException = throwException;
|
||||||
|
MatchOperator = matchOperator;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
||||||
|
public double IsMatch(string? input)
|
||||||
|
{
|
||||||
|
double match = MatchScores.Mismatch;
|
||||||
|
if (input != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
|
||||||
|
match = MatchScores.ToScore(results, MatchOperator);
|
||||||
|
}
|
||||||
|
catch (JsonException)
|
||||||
|
{
|
||||||
|
if (ThrowException)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
||||||
|
public double IsMatch(object? input)
|
||||||
|
{
|
||||||
|
double match = MatchScores.Mismatch;
|
||||||
|
|
||||||
|
// When input is null or byte[], return Mismatch.
|
||||||
|
if (input != null && !(input is byte[]))
|
||||||
|
{
|
||||||
|
string inputAsString = JsonConvert.SerializeObject(input);
|
||||||
|
return IsMatch(inputAsString);
|
||||||
|
}
|
||||||
|
|
||||||
|
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
|
||||||
|
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||||
|
{
|
||||||
|
return _patterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public MatchOperator MatchOperator { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IMatcher.Name"/>
|
||||||
|
public string Name => "JmesPathMatcher";
|
||||||
}
|
}
|
||||||
@@ -119,7 +119,7 @@ public class JsonMatcher : IValueMatcher, IIgnoreCaseMatcher
|
|||||||
return tokenValue;
|
return tokenValue;
|
||||||
|
|
||||||
case string stringValue:
|
case string stringValue:
|
||||||
return JsonUtils.Parse(stringValue)!;
|
return JsonUtils.Parse(stringValue);
|
||||||
|
|
||||||
case IEnumerable enumerableValue:
|
case IEnumerable enumerableValue:
|
||||||
return JArray.FromObject(enumerableValue);
|
return JArray.FromObject(enumerableValue);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Dynamic.Core;
|
using System.Linq.Dynamic.Core;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
@@ -68,7 +69,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
||||||
public double IsMatch(string input)
|
public double IsMatch(string? input)
|
||||||
{
|
{
|
||||||
double match = MatchScores.Mismatch;
|
double match = MatchScores.Mismatch;
|
||||||
|
|
||||||
@@ -94,7 +95,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
|
||||||
public double IsMatch(object input)
|
public double IsMatch(object? input)
|
||||||
{
|
{
|
||||||
double match = MatchScores.Mismatch;
|
double match = MatchScores.Mismatch;
|
||||||
|
|
||||||
@@ -105,9 +106,12 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
|
|||||||
value = valueAsJObject;
|
value = valueAsJObject;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case { } valueAsObject:
|
||||||
value = JObject.FromObject(input);
|
value = JObject.FromObject(valueAsObject);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return MatchScores.Mismatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert a single object to a Queryable JObject-list with 1 entry.
|
// Convert a single object to a Queryable JObject-list with 1 entry.
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// MatchBehaviour
|
||||||
|
/// </summary>
|
||||||
|
public enum MatchBehaviour
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// MatchBehaviour
|
/// Accept on match (default)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum MatchBehaviour
|
AcceptOnMatch,
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Accept on match (default)
|
|
||||||
/// </summary>
|
|
||||||
AcceptOnMatch,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reject on match
|
/// Reject on match
|
||||||
/// </summary>
|
/// </summary>
|
||||||
RejectOnMatch
|
RejectOnMatch
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,27 +1,26 @@
|
|||||||
namespace WireMock.Matchers
|
namespace WireMock.Matchers;
|
||||||
{
|
|
||||||
internal static class MatchBehaviourHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Converts the specified match behaviour and match value to a new match value.
|
|
||||||
///
|
|
||||||
/// if AcceptOnMatch --> return match (default)
|
|
||||||
/// if RejectOnMatch and match = 0.0 --> return 1.0
|
|
||||||
/// if RejectOnMatch and match = 0.? --> return 0.0
|
|
||||||
/// if RejectOnMatch and match = 1.0 --> return 0.0
|
|
||||||
/// </summary>
|
|
||||||
///
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
|
||||||
/// <param name="match">The match.</param>
|
|
||||||
/// <returns>match value</returns>
|
|
||||||
internal static double Convert(MatchBehaviour matchBehaviour, double match)
|
|
||||||
{
|
|
||||||
if (matchBehaviour == MatchBehaviour.AcceptOnMatch)
|
|
||||||
{
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
|
|
||||||
return match <= MatchScores.Tolerance ? MatchScores.Perfect : MatchScores.Mismatch;
|
internal static class MatchBehaviourHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Converts the specified match behaviour and match value to a new match value.
|
||||||
|
///
|
||||||
|
/// if AcceptOnMatch --> return match (default)
|
||||||
|
/// if RejectOnMatch and match = 0.0 --> return 1.0
|
||||||
|
/// if RejectOnMatch and match = 0.? --> return 0.0
|
||||||
|
/// if RejectOnMatch and match = 1.0 --> return 0.0
|
||||||
|
/// </summary>
|
||||||
|
///
|
||||||
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <param name="match">The match.</param>
|
||||||
|
/// <returns>match value</returns>
|
||||||
|
internal static double Convert(MatchBehaviour matchBehaviour, double match)
|
||||||
|
{
|
||||||
|
if (matchBehaviour == MatchBehaviour.AcceptOnMatch)
|
||||||
|
{
|
||||||
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return match <= MatchScores.Tolerance ? MatchScores.Perfect : MatchScores.Mismatch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
namespace WireMock.Matchers.Request
|
namespace WireMock.Matchers.Request;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// CompositeMatcherType
|
||||||
|
/// </summary>
|
||||||
|
public enum CompositeMatcherType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CompositeMatcherType
|
/// And
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum CompositeMatcherType
|
And = 0,
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// And
|
|
||||||
/// </summary>
|
|
||||||
And = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Or
|
/// Or
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Or = 1
|
Or = 1
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,56 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace WireMock.Matchers.Request
|
namespace WireMock.Matchers.Request;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// RequestMatchResult
|
||||||
|
/// </summary>
|
||||||
|
public class RequestMatchResult : IRequestMatchResult
|
||||||
{
|
{
|
||||||
|
/// <inheritdoc cref="IRequestMatchResult.TotalScore" />
|
||||||
|
public double TotalScore => MatchDetails.Sum(md => md.Score);
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IRequestMatchResult.TotalNumber" />
|
||||||
|
public int TotalNumber => MatchDetails.Count;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IRequestMatchResult.IsPerfectMatch" />
|
||||||
|
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IRequestMatchResult.AverageTotalScore" />
|
||||||
|
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IRequestMatchResult.MatchDetails" />
|
||||||
|
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// RequestMatchResult
|
/// Adds the score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class RequestMatchResult : IRequestMatchResult
|
/// <param name="matcherType">The matcher Type.</param>
|
||||||
|
/// <param name="score">The score.</param>
|
||||||
|
/// <returns>The score.</returns>
|
||||||
|
public double AddScore(Type matcherType, double score)
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="IRequestMatchResult.TotalScore" />
|
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score });
|
||||||
public double TotalScore => MatchDetails.Sum(md => md.Score);
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMatchResult.TotalNumber" />
|
return score;
|
||||||
public int TotalNumber => MatchDetails.Count;
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMatchResult.IsPerfectMatch" />
|
/// <summary>
|
||||||
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
|
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">An object to compare with this instance.</param>
|
||||||
|
/// <returns>
|
||||||
|
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
|
||||||
|
/// </returns>
|
||||||
|
public int CompareTo(object obj)
|
||||||
|
{
|
||||||
|
var compareObj = (RequestMatchResult)obj;
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMatchResult.AverageTotalScore" />
|
int averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
|
||||||
public double AverageTotalScore => TotalNumber == 0 ? 0.0 : TotalScore / TotalNumber;
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMatchResult.MatchDetails" />
|
// In case the score is equal, prefer the one with the most matchers.
|
||||||
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
|
return averageTotalScoreResult == 0 ? compareObj.TotalNumber.CompareTo(TotalNumber) : averageTotalScoreResult;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Adds the score.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matcherType">The matcher Type.</param>
|
|
||||||
/// <param name="score">The score.</param>
|
|
||||||
/// <returns>The score.</returns>
|
|
||||||
public double AddScore(Type matcherType, double score)
|
|
||||||
{
|
|
||||||
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score });
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj">An object to compare with this instance.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// A value that indicates the relative order of the objects being compared. The return value has these meanings: Value Meaning Less than zero This instance precedes <paramref name="obj" /> in the sort order. Zero This instance occurs in the same position in the sort order as <paramref name="obj" />. Greater than zero This instance follows <paramref name="obj" /> in the sort order.
|
|
||||||
/// </returns>
|
|
||||||
public int CompareTo(object obj)
|
|
||||||
{
|
|
||||||
var compareObj = (RequestMatchResult)obj;
|
|
||||||
|
|
||||||
int averageTotalScoreResult = compareObj.AverageTotalScore.CompareTo(AverageTotalScore);
|
|
||||||
|
|
||||||
// In case the score is equal, prefer the one with the most matchers.
|
|
||||||
return averageTotalScoreResult == 0 ? compareObj.TotalNumber.CompareTo(TotalNumber) : averageTotalScoreResult;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,50 +3,49 @@ using System.Linq;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Matchers.Request
|
namespace WireMock.Matchers.Request;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The composite request matcher.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private readonly CompositeMatcherType _type;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The composite request matcher.
|
/// Gets the request matchers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
/// <value>
|
||||||
|
/// The request matchers.
|
||||||
|
/// </value>
|
||||||
|
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="requestMatchers">The request matchers.</param>
|
||||||
|
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
|
||||||
|
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
||||||
{
|
{
|
||||||
private readonly CompositeMatcherType _type;
|
Guard.NotNull(requestMatchers, nameof(requestMatchers));
|
||||||
|
|
||||||
/// <summary>
|
_type = type;
|
||||||
/// Gets the request matchers.
|
RequestMatchers = requestMatchers;
|
||||||
/// </summary>
|
}
|
||||||
/// <value>
|
|
||||||
/// The request matchers.
|
|
||||||
/// </value>
|
|
||||||
private IEnumerable<IRequestMatcher> RequestMatchers { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Initializes a new instance of the <see cref="RequestMessageCompositeMatcher"/> class.
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
/// </summary>
|
{
|
||||||
/// <param name="requestMatchers">The request matchers.</param>
|
if (!RequestMatchers.Any())
|
||||||
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
|
|
||||||
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(requestMatchers, nameof(requestMatchers));
|
return MatchScores.Mismatch;
|
||||||
|
|
||||||
_type = type;
|
|
||||||
RequestMatchers = requestMatchers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
if (_type == CompositeMatcherType.And)
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
|
||||||
{
|
{
|
||||||
if (!RequestMatchers.Any())
|
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||||
{
|
|
||||||
return MatchScores.Mismatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_type == CompositeMatcherType.And)
|
|
||||||
{
|
|
||||||
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
|
||||||
}
|
|
||||||
|
|
||||||
return RequestMatchers.Max(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return RequestMatchers.Max(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,126 +4,125 @@ using System.Linq;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Matchers.Request
|
namespace WireMock.Matchers.Request;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The request cookie matcher.
|
||||||
|
/// </summary>
|
||||||
|
/// <inheritdoc cref="IRequestMatcher"/>
|
||||||
|
public class RequestMessageCookieMatcher : IRequestMatcher
|
||||||
{
|
{
|
||||||
|
private readonly MatchBehaviour _matchBehaviour;
|
||||||
|
private readonly bool _ignoreCase;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The request cookie matcher.
|
/// The functions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <inheritdoc cref="IRequestMatcher"/>
|
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
|
||||||
public class RequestMessageCookieMatcher : IRequestMatcher
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; }
|
||||||
|
|
||||||
|
/// <value>
|
||||||
|
/// The matchers.
|
||||||
|
/// </value>
|
||||||
|
public IStringMatcher[] Matchers { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name.</param>
|
||||||
|
/// <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)
|
||||||
{
|
{
|
||||||
private readonly MatchBehaviour _matchBehaviour;
|
Guard.NotNull(name, nameof(name));
|
||||||
private readonly bool _ignoreCase;
|
Guard.NotNull(pattern, nameof(pattern));
|
||||||
|
|
||||||
/// <summary>
|
_matchBehaviour = matchBehaviour;
|
||||||
/// The functions
|
_ignoreCase = ignoreCase;
|
||||||
/// </summary>
|
Name = name;
|
||||||
public Func<IDictionary<string, string>, bool>[] Funcs { get; }
|
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, pattern, ignoreCase) };
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The name
|
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; }
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <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) :
|
||||||
|
this(matchBehaviour, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast<IStringMatcher>().ToArray())
|
||||||
|
{
|
||||||
|
Guard.NotNull(patterns, nameof(patterns));
|
||||||
|
}
|
||||||
|
|
||||||
/// <value>
|
/// <summary>
|
||||||
/// The matchers.
|
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||||
/// </value>
|
/// </summary>
|
||||||
public IStringMatcher[] Matchers { get; }
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
Guard.NotNull(name, nameof(name));
|
||||||
|
Guard.NotNull(matchers, nameof(matchers));
|
||||||
|
|
||||||
/// <summary>
|
_matchBehaviour = matchBehaviour;
|
||||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
Name = name;
|
||||||
/// </summary>
|
Matchers = matchers;
|
||||||
/// <param name="name">The name.</param>
|
_ignoreCase = ignoreCase;
|
||||||
/// <param name="pattern">The pattern.</param>
|
}
|
||||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
/// <summary>
|
||||||
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, [NotNull] string name, [NotNull] string pattern, bool ignoreCase)
|
/// 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)
|
||||||
|
{
|
||||||
|
Guard.NotNull(funcs, nameof(funcs));
|
||||||
|
|
||||||
|
Funcs = funcs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||||
|
{
|
||||||
|
double score = IsMatch(requestMessage);
|
||||||
|
return requestMatchResult.AddScore(GetType(), score);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double IsMatch(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
if (requestMessage.Cookies == null)
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
|
||||||
Guard.NotNull(pattern, nameof(pattern));
|
|
||||||
|
|
||||||
_matchBehaviour = matchBehaviour;
|
|
||||||
_ignoreCase = ignoreCase;
|
|
||||||
Name = name;
|
|
||||||
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, pattern, ignoreCase) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
|
||||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
var cookies = !_ignoreCase ? requestMessage.Cookies : new Dictionary<string, string>(requestMessage.Cookies, StringComparer.OrdinalIgnoreCase);
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
if (Funcs != null)
|
||||||
/// <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) :
|
|
||||||
this(matchBehaviour, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast<IStringMatcher>().ToArray())
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(patterns, nameof(patterns));
|
return MatchScores.ToScore(Funcs.Any(f => f(cookies)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
if (Matchers == null)
|
||||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
|
||||||
/// <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)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(name, nameof(name));
|
return MatchScores.Mismatch;
|
||||||
Guard.NotNull(matchers, nameof(matchers));
|
|
||||||
|
|
||||||
_matchBehaviour = matchBehaviour;
|
|
||||||
Name = name;
|
|
||||||
Matchers = matchers;
|
|
||||||
_ignoreCase = ignoreCase;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
if (!cookies.ContainsKey(Name))
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(funcs, nameof(funcs));
|
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
|
||||||
|
|
||||||
Funcs = funcs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
string value = cookies[Name];
|
||||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
return Matchers.Max(m => m.IsMatch(value));
|
||||||
{
|
|
||||||
double score = IsMatch(requestMessage);
|
|
||||||
return requestMatchResult.AddScore(GetType(), score);
|
|
||||||
}
|
|
||||||
|
|
||||||
private double IsMatch(IRequestMessage requestMessage)
|
|
||||||
{
|
|
||||||
if (requestMessage.Cookies == null)
|
|
||||||
{
|
|
||||||
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we want to use IgnoreCase to compare the Cookie-Name and Cookie-Value
|
|
||||||
var cookies = !_ignoreCase ? requestMessage.Cookies : new Dictionary<string, string>(requestMessage.Cookies, StringComparer.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
if (Funcs != null)
|
|
||||||
{
|
|
||||||
return MatchScores.ToScore(Funcs.Any(f => f(cookies)));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Matchers == null)
|
|
||||||
{
|
|
||||||
return MatchScores.Mismatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cookies.ContainsKey(Name))
|
|
||||||
{
|
|
||||||
return MatchBehaviourHelper.Convert(_matchBehaviour, MatchScores.Mismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
string value = cookies[Name];
|
|
||||||
return Matchers.Max(m => m.IsMatch(value));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -30,9 +29,9 @@ namespace WireMock.Owin
|
|||||||
|
|
||||||
public bool IsStarted { get; private set; }
|
public bool IsStarted { get; private set; }
|
||||||
|
|
||||||
public List<string> Urls { get; } = new List<string>();
|
public List<string> Urls { get; } = new();
|
||||||
|
|
||||||
public List<int> Ports { get; } = new List<int>();
|
public List<int> Ports { get; } = new();
|
||||||
|
|
||||||
public Exception RunningException => _runningException;
|
public Exception RunningException => _runningException;
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ namespace WireMock.Owin.Mappers
|
|||||||
{
|
{
|
||||||
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -80,11 +79,10 @@ namespace WireMock.Owin.Mappers
|
|||||||
}
|
}
|
||||||
|
|
||||||
var statusCodeType = responseMessage.StatusCode?.GetType();
|
var statusCodeType = responseMessage.StatusCode?.GetType();
|
||||||
|
|
||||||
switch (statusCodeType)
|
switch (statusCodeType)
|
||||||
{
|
{
|
||||||
case Type typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
|
case Type typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
|
||||||
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode);
|
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type typeAsString when typeAsString == typeof(string):
|
case Type typeAsString when typeAsString == typeof(string):
|
||||||
@@ -138,7 +136,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
return responseMessage.BodyData.BodyAsBytes;
|
return responseMessage.BodyData.BodyAsBytes;
|
||||||
|
|
||||||
case BodyType.File:
|
case BodyType.File:
|
||||||
return _options.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
|
return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ namespace WireMock.Pact.Models.V2;
|
|||||||
|
|
||||||
public class Interaction
|
public class Interaction
|
||||||
{
|
{
|
||||||
public string Description { get; set; } = string.Empty;
|
public string? Description { get; set; }
|
||||||
|
|
||||||
public string ProviderState { get; set; }
|
public string? ProviderState { get; set; }
|
||||||
|
|
||||||
public PactRequest Request { get; set; } = new PactRequest();
|
public PactRequest Request { get; set; } = new();
|
||||||
|
|
||||||
public PactResponse Response { get; set; } = new PactResponse();
|
public PactResponse Response { get; set; } = new();
|
||||||
}
|
}
|
||||||
@@ -1,57 +1,56 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace WireMock.Plugin
|
namespace WireMock.Plugin;
|
||||||
|
|
||||||
|
internal static class PluginLoader
|
||||||
{
|
{
|
||||||
internal static class PluginLoader
|
private static readonly ConcurrentDictionary<Type, Type> Assemblies = new();
|
||||||
|
|
||||||
|
public static T Load<T>(params object[] args) where T : class
|
||||||
{
|
{
|
||||||
private static readonly ConcurrentDictionary<Type, Type> Assemblies = new ConcurrentDictionary<Type, Type>();
|
var foundType = Assemblies.GetOrAdd(typeof(T), (type) =>
|
||||||
|
|
||||||
public static T Load<T>(params object[] args) where T : class
|
|
||||||
{
|
{
|
||||||
var foundType = Assemblies.GetOrAdd(typeof(T), (type) =>
|
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
|
||||||
|
|
||||||
|
Type? pluginType = null;
|
||||||
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
var files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*.dll");
|
try
|
||||||
|
|
||||||
Type pluginType = null;
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
{
|
||||||
try
|
var assembly = Assembly.Load(new AssemblyName
|
||||||
{
|
{
|
||||||
var assembly = Assembly.Load(new AssemblyName
|
Name = Path.GetFileNameWithoutExtension(file)
|
||||||
{
|
});
|
||||||
Name = Path.GetFileNameWithoutExtension(file)
|
|
||||||
});
|
|
||||||
|
|
||||||
pluginType = GetImplementationTypeByInterface<T>(assembly);
|
pluginType = GetImplementationTypeByInterface<T>(assembly);
|
||||||
if (pluginType != null)
|
if (pluginType != null)
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
{
|
||||||
// no-op: just try next .dll
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch
|
||||||
if (pluginType != null)
|
|
||||||
{
|
{
|
||||||
return pluginType;
|
// no-op: just try next .dll
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
throw new DllNotFoundException($"No dll found which implements type '{type}'");
|
if (pluginType != null)
|
||||||
});
|
{
|
||||||
|
return pluginType;
|
||||||
|
}
|
||||||
|
|
||||||
return (T)Activator.CreateInstance(foundType, args);
|
throw new DllNotFoundException($"No dll found which implements type '{type}'");
|
||||||
}
|
});
|
||||||
|
|
||||||
private static Type GetImplementationTypeByInterface<T>(Assembly assembly)
|
return (T)Activator.CreateInstance(foundType, args);
|
||||||
{
|
}
|
||||||
return assembly.GetTypes().FirstOrDefault(t => typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
|
|
||||||
}
|
private static Type? GetImplementationTypeByInterface<T>(Assembly assembly)
|
||||||
|
{
|
||||||
|
return assembly.GetTypes().FirstOrDefault(t => typeof(T).IsAssignableFrom(t) && !t.GetTypeInfo().IsInterface);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,7 +77,7 @@ internal class RegexExtended : Regex
|
|||||||
/// <param name="pattern">Pattern to replace token for.</param>
|
/// <param name="pattern">Pattern to replace token for.</param>
|
||||||
private static string ReplaceGuidPattern(string pattern)
|
private static string ReplaceGuidPattern(string pattern)
|
||||||
{
|
{
|
||||||
Guard.NotNull(pattern, nameof(pattern));
|
Guard.NotNull(pattern);
|
||||||
|
|
||||||
foreach (var tokenPattern in GuidTokenPatterns)
|
foreach (var tokenPattern in GuidTokenPatterns)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -47,10 +47,10 @@ public class RequestMessage : IRequestMessage
|
|||||||
public string Method { get; }
|
public string Method { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.Headers" />
|
/// <inheritdoc cref="IRequestMessage.Headers" />
|
||||||
public IDictionary<string, WireMockList<string>> Headers { get; }
|
public IDictionary<string, WireMockList<string>>? Headers { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.Cookies" />
|
/// <inheritdoc cref="IRequestMessage.Cookies" />
|
||||||
public IDictionary<string, string> Cookies { get; }
|
public IDictionary<string, string>? Cookies { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.Query" />
|
/// <inheritdoc cref="IRequestMessage.Query" />
|
||||||
public IDictionary<string, WireMockList<string>>? Query { get; }
|
public IDictionary<string, WireMockList<string>>? Query { get; }
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the BodyDestinationFormat
|
||||||
|
/// </summary>
|
||||||
|
public static class BodyDestinationFormat
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the BodyDestinationFormat
|
/// Same as source (no conversion)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BodyDestinationFormat
|
public const string SameAsSource = "SameAsSource";
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Same as source (no conversion)
|
|
||||||
/// </summary>
|
|
||||||
public const string SameAsSource = "SameAsSource";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert to string
|
/// Convert to string
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string String = "String";
|
public const string String = "String";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert to bytes
|
/// Convert to bytes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Bytes = "Bytes";
|
public const string Bytes = "Bytes";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Convert to Json object
|
/// Convert to Json object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string Json = "Json";
|
public const string Json = "Json";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,72 +2,71 @@ using System;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The BodyResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The BodyResponseBuilder interface.
|
/// WithBody : Create a ... response based on a string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IBodyResponseBuilder : IFaultResponseBuilder
|
/// <param name="body">The body.</param>
|
||||||
{
|
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||||
/// <summary>
|
/// <param name="encoding">The body encoding.</param>
|
||||||
/// WithBody : Create a ... response based on a string.
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
/// </summary>
|
IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
||||||
/// <param name="body">The body.</param>
|
|
||||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
|
||||||
/// <param name="encoding">The body encoding.</param>
|
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBody : Create a ... response based on a callback function.
|
/// WithBody : Create a ... response based on a callback function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bodyFactory">The delegate to build the body.</param>
|
/// <param name="bodyFactory">The delegate to build the body.</param>
|
||||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||||
/// <param name="encoding">The body encoding.</param>
|
/// <param name="encoding">The body encoding.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBody : Create a ... response based on a callback function.
|
/// WithBody : Create a ... response based on a callback function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="bodyFactory">The async delegate to build the body.</param>
|
/// <param name="bodyFactory">The async delegate to build the body.</param>
|
||||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||||
/// <param name="encoding">The body encoding.</param>
|
/// <param name="encoding">The body encoding.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBody(Func<IRequestMessage, Task<string>> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
IResponseBuilder WithBody(Func<IRequestMessage, Task<string>> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBody : Create a ... response based on a bytearray.
|
/// WithBody : Create a ... response based on a bytearray.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="body">The body.</param>
|
/// <param name="body">The body.</param>
|
||||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||||
/// <param name="encoding">The body encoding.</param>
|
/// <param name="encoding">The body encoding.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBody(byte[] body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
IResponseBuilder WithBody(byte[] body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
|
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="body">The body.</param>
|
/// <param name="body">The body.</param>
|
||||||
/// <param name="encoding">The body encoding.</param>
|
/// <param name="encoding">The body encoding.</param>
|
||||||
/// <param name="indented">Use JSON indented.</param>
|
/// <param name="indented">Use JSON indented.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBodyAsJson(object body, Encoding? encoding = null, bool? indented = null);
|
IResponseBuilder WithBodyAsJson(object body, Encoding? encoding = null, bool? indented = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
|
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="body">The body.</param>
|
/// <param name="body">The body.</param>
|
||||||
/// <param name="indented">Define whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.</param>
|
/// <param name="indented">Define whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBodyAsJson(object body, bool indented);
|
IResponseBuilder WithBodyAsJson(object body, bool indented);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithBodyFromFile : Create a ... response based on a File.
|
/// WithBodyFromFile : Create a ... response based on a File.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="filename">The filename.</param>
|
/// <param name="filename">The filename.</param>
|
||||||
/// <param name="cache">Defines if this file is cached in memory or retrieved from disk every time the response is created.</param>
|
/// <param name="cache">Defines if this file is cached in memory or retrieved from disk every time the response is created.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBodyFromFile(string filename, bool cache = true);
|
IResponseBuilder WithBodyFromFile(string filename, bool cache = true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -3,25 +3,24 @@ using System.Threading.Tasks;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using WireMock.ResponseProviders;
|
using WireMock.ResponseProviders;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The CallbackResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface ICallbackResponseBuilder : IResponseProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The CallbackResponseBuilder interface.
|
/// The callback builder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ICallbackResponseBuilder : IResponseProvider
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
{
|
[PublicAPI]
|
||||||
/// <summary>
|
IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler);
|
||||||
/// The callback builder
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
[PublicAPI]
|
|
||||||
IResponseBuilder WithCallback([NotNull] Func<IRequestMessage, ResponseMessage> callbackHandler);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The async callback builder
|
/// The async callback builder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
IResponseBuilder WithCallback([NotNull] Func<IRequestMessage, Task<ResponseMessage>> callbackHandler);
|
IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,31 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The DelayResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDelayResponseBuilder : ICallbackResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The DelayResponseBuilder interface.
|
/// The delay defined as a <see cref="TimeSpan"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IDelayResponseBuilder : ICallbackResponseBuilder
|
/// <param name="delay">The TimeSpan to delay.</param>
|
||||||
{
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
/// <summary>
|
IResponseBuilder WithDelay(TimeSpan delay);
|
||||||
/// The delay defined as a <see cref="TimeSpan"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="delay">The TimeSpan to delay.</param>
|
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
IResponseBuilder WithDelay(TimeSpan delay);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The delay defined as milliseconds.
|
/// The delay defined as milliseconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="milliseconds">The milliseconds to delay.</param>
|
/// <param name="milliseconds">The milliseconds to delay.</param>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithDelay(int milliseconds);
|
IResponseBuilder WithDelay(int milliseconds);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Introduce random delay
|
/// Introduce random delay
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="minimumMilliseconds">Minimum milliseconds to delay</param>
|
/// <param name="minimumMilliseconds">Minimum milliseconds to delay</param>
|
||||||
/// <param name="maximumMilliseconds">Maximum milliseconds to delay</param>
|
/// <param name="maximumMilliseconds">Maximum milliseconds to delay</param>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000);
|
IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The FaultRequestBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IFaultResponseBuilder : ITransformResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The FaultRequestBuilder interface.
|
/// WithBody : Create a fault response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IFaultResponseBuilder : ITransformResponseBuilder
|
/// <param name="faultType">The FaultType.</param>
|
||||||
{
|
/// <param name="percentage">The percentage when this fault should occur. When null, it's always a fault.</param>
|
||||||
/// <summary>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
/// WithBody : Create a fault response.
|
IResponseBuilder WithFault(FaultType faultType, double? percentage = null);
|
||||||
/// </summary>
|
|
||||||
/// <param name="faultType">The FaultType.</param>
|
|
||||||
/// <param name="percentage">The percentage when this fault should occur. When null, it's always a fault.</param>
|
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
IResponseBuilder WithFault(FaultType faultType, [CanBeNull] double? percentage = null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
using JetBrains.Annotations;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,25 @@
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ProxyResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ProxyResponseBuilder interface.
|
/// WithProxy URL using Client X509Certificate2.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
/// <param name="proxyUrl">The proxy url.</param>
|
||||||
{
|
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
|
||||||
/// <summary>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
/// WithProxy URL using Client X509Certificate2.
|
IResponseBuilder WithProxy(string proxyUrl, string? clientX509Certificate2ThumbprintOrSubjectName = null);
|
||||||
/// </summary>
|
|
||||||
/// <param name="proxyUrl">The proxy url.</param>
|
|
||||||
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
|
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
IResponseBuilder WithProxy([NotNull] string proxyUrl, [CanBeNull] string clientX509Certificate2ThumbprintOrSubjectName = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WithProxy using IProxyAndRecordSettings.
|
/// WithProxy using IProxyAndRecordSettings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="settings">The IProxyAndRecordSettings.</param>
|
/// <param name="settings">The IProxyAndRecordSettings.</param>
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithProxy([NotNull] ProxyAndRecordSettings settings);
|
IResponseBuilder WithProxy([NotNull] ProxyAndRecordSettings settings);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IResponseBuilder : IProxyResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// The ResponseBuilder interface.
|
|
||||||
/// </summary>
|
|
||||||
public interface IResponseBuilder : IProxyResponseBuilder
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,47 +1,46 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The StatusCodeResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IStatusCodeResponseBuilder : IHeadersResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The StatusCodeResponseBuilder interface.
|
/// The with status code.
|
||||||
|
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IStatusCodeResponseBuilder : IHeadersResponseBuilder
|
/// <param name="code">The code.</param>
|
||||||
{
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
/// <summary>
|
IResponseBuilder WithStatusCode(int code);
|
||||||
/// The with status code.
|
|
||||||
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="code">The code.</param>
|
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
IResponseBuilder WithStatusCode(int code);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The with status code.
|
/// The with status code.
|
||||||
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
|
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="code">The code.</param>
|
/// <param name="code">The code.</param>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithStatusCode(string code);
|
IResponseBuilder WithStatusCode(string code);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The with status code.
|
/// The with status code.
|
||||||
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
|
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="code">The code.</param>
|
/// <param name="code">The code.</param>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithStatusCode(HttpStatusCode code);
|
IResponseBuilder WithStatusCode(HttpStatusCode code);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The with Success status code (200).
|
/// The with Success status code (200).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithSuccess();
|
IResponseBuilder WithSuccess();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The with NotFound status code (404).
|
/// The with NotFound status code (404).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithNotFound();
|
IResponseBuilder WithNotFound();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,34 +1,33 @@
|
|||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The TransformResponseBuilder interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface ITransformResponseBuilder : IDelayResponseBuilder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The TransformResponseBuilder interface.
|
/// Use the Handlebars.Net ResponseMessage transformer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITransformResponseBuilder : IDelayResponseBuilder
|
/// <returns>
|
||||||
{
|
/// The <see cref="IResponseBuilder"/>.
|
||||||
/// <summary>
|
/// </returns>
|
||||||
/// Use the Handlebars.Net ResponseMessage transformer.
|
IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile);
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// The <see cref="IResponseBuilder"/>.
|
|
||||||
/// </returns>
|
|
||||||
IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use the Handlebars.Net ResponseMessage transformer.
|
/// Use the Handlebars.Net ResponseMessage transformer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// The <see cref="IResponseBuilder"/>.
|
/// The <see cref="IResponseBuilder"/>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
IResponseBuilder WithTransformer(ReplaceNodeOptions options);
|
IResponseBuilder WithTransformer(ReplaceNodeOptions options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use a specific ResponseMessage transformer.
|
/// Use a specific ResponseMessage transformer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>
|
/// <returns>
|
||||||
/// The <see cref="IResponseBuilder"/>.
|
/// The <see cref="IResponseBuilder"/>.
|
||||||
/// </returns>
|
/// </returns>
|
||||||
IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None);
|
IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,60 +1,62 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
public partial class Response
|
||||||
{
|
{
|
||||||
public partial class Response
|
/// <summary>
|
||||||
|
/// A delegate to execute to generate the response.
|
||||||
|
/// </summary>
|
||||||
|
[MemberNotNullWhen(true, nameof(WithCallbackUsed))]
|
||||||
|
public Func<IRequestMessage, ResponseMessage>? Callback { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A delegate to execute to generate the response async.
|
||||||
|
/// </summary>
|
||||||
|
[MemberNotNullWhen(true, nameof(WithCallbackUsed))]
|
||||||
|
public Func<IRequestMessage, Task<ResponseMessage>>? CallbackAsync { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines if the method WithCallback(...) is used.
|
||||||
|
/// </summary>
|
||||||
|
public bool WithCallbackUsed { get; private set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler)
|
||||||
{
|
{
|
||||||
/// <summary>
|
Guard.NotNull(callbackHandler);
|
||||||
/// A delegate to execute to generate the response.
|
|
||||||
/// </summary>
|
|
||||||
public Func<IRequestMessage, ResponseMessage> Callback { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
return WithCallbackInternal(true, callbackHandler);
|
||||||
/// A delegate to execute to generate the response async.
|
}
|
||||||
/// </summary>
|
|
||||||
public Func<IRequestMessage, Task<ResponseMessage>> CallbackAsync { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Defines if the method WithCallback(...) is used.
|
public IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
|
||||||
/// </summary>
|
{
|
||||||
public bool WithCallbackUsed { get; private set; }
|
Guard.NotNull(callbackHandler);
|
||||||
|
|
||||||
/// <inheritdoc />
|
return WithCallbackInternal(true, callbackHandler);
|
||||||
public IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler)
|
}
|
||||||
{
|
|
||||||
Guard.NotNull(callbackHandler, nameof(callbackHandler));
|
|
||||||
|
|
||||||
return WithCallbackInternal(true, callbackHandler);
|
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, ResponseMessage> callbackHandler)
|
||||||
}
|
{
|
||||||
|
Guard.NotNull(callbackHandler);
|
||||||
|
|
||||||
/// <inheritdoc />
|
WithCallbackUsed = withCallbackUsed;
|
||||||
public IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
|
Callback = callbackHandler;
|
||||||
{
|
|
||||||
Guard.NotNull(callbackHandler, nameof(callbackHandler));
|
|
||||||
|
|
||||||
return WithCallbackInternal(true, callbackHandler);
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, ResponseMessage> callbackHandler)
|
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
|
||||||
{
|
{
|
||||||
Guard.NotNull(callbackHandler, nameof(callbackHandler));
|
Guard.NotNull(callbackHandler);
|
||||||
|
|
||||||
WithCallbackUsed = withCallbackUsed;
|
WithCallbackUsed = withCallbackUsed;
|
||||||
Callback = callbackHandler;
|
CallbackAsync = callbackHandler;
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
|
||||||
|
|
||||||
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
|
|
||||||
{
|
|
||||||
Guard.NotNull(callbackHandler, nameof(callbackHandler));
|
|
||||||
|
|
||||||
WithCallbackUsed = withCallbackUsed;
|
|
||||||
CallbackAsync = callbackHandler;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
{
|
|
||||||
public partial class Response
|
|
||||||
{
|
|
||||||
/// <inheritdoc cref="IFaultResponseBuilder.WithFault(FaultType, double?)"/>
|
|
||||||
public IResponseBuilder WithFault(FaultType faultType, double? percentage = null)
|
|
||||||
{
|
|
||||||
ResponseMessage.FaultType = faultType;
|
|
||||||
ResponseMessage.FaultPercentage = percentage;
|
|
||||||
|
|
||||||
return this;
|
public partial class Response
|
||||||
}
|
{
|
||||||
|
/// <inheritdoc cref="IFaultResponseBuilder.WithFault(FaultType, double?)"/>
|
||||||
|
public IResponseBuilder WithFault(FaultType faultType, double? percentage = null)
|
||||||
|
{
|
||||||
|
ResponseMessage.FaultType = faultType;
|
||||||
|
ResponseMessage.FaultPercentage = percentage;
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ namespace WireMock.ResponseBuilders;
|
|||||||
|
|
||||||
public partial class Response
|
public partial class Response
|
||||||
{
|
{
|
||||||
private HttpClient _httpClientForProxy;
|
private HttpClient? _httpClientForProxy;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The WebProxy settings.
|
/// The WebProxy settings.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ using System.Threading.Tasks;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Proxy;
|
using WireMock.Proxy;
|
||||||
using WireMock.ResponseProviders;
|
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Transformers;
|
using WireMock.Transformers;
|
||||||
using WireMock.Transformers.Handlebars;
|
using WireMock.Transformers.Handlebars;
|
||||||
@@ -18,464 +17,464 @@ using WireMock.Transformers.Scriban;
|
|||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.ResponseBuilders
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Response.
|
||||||
|
/// </summary>
|
||||||
|
public partial class Response : IResponseBuilder
|
||||||
{
|
{
|
||||||
|
private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond));
|
||||||
|
|
||||||
|
private TimeSpan? _delay;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Response.
|
/// The minimum random delay in milliseconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class Response : IResponseBuilder
|
public int? MinimumDelayMilliseconds { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum random delay in milliseconds.
|
||||||
|
/// </summary>
|
||||||
|
public int? MaximumDelayMilliseconds { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The delay
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan? Delay
|
||||||
{
|
{
|
||||||
private static readonly ThreadLocal<Random> Random = new ThreadLocal<Random>(() => new Random(DateTime.UtcNow.Millisecond));
|
get
|
||||||
|
|
||||||
private TimeSpan? _delay;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The minimum random delay in milliseconds.
|
|
||||||
/// </summary>
|
|
||||||
public int? MinimumDelayMilliseconds { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The maximum random delay in milliseconds.
|
|
||||||
/// </summary>
|
|
||||||
public int? MaximumDelayMilliseconds { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The delay
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan? Delay
|
|
||||||
{
|
{
|
||||||
get
|
if (MinimumDelayMilliseconds != null && MaximumDelayMilliseconds != null)
|
||||||
{
|
{
|
||||||
if (MinimumDelayMilliseconds != null && MaximumDelayMilliseconds != null)
|
return TimeSpan.FromMilliseconds(Random.Value!.Next(MinimumDelayMilliseconds.Value, MaximumDelayMilliseconds.Value));
|
||||||
{
|
|
||||||
return TimeSpan.FromMilliseconds(Random.Value.Next(MinimumDelayMilliseconds.Value, MaximumDelayMilliseconds.Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
return _delay;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private set => _delay = value;
|
return _delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private set => _delay = value;
|
||||||
/// Gets a value indicating whether [use transformer].
|
}
|
||||||
/// </summary>
|
|
||||||
public bool UseTransformer { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the transformer.
|
/// Gets a value indicating whether [use transformer].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TransformerType TransformerType { get; private set; }
|
public bool UseTransformer { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether to use the Handlebars transformer for the content from the referenced BodyAsFile.
|
/// Gets the type of the transformer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool UseTransformerForBodyAsFile { get; private set; }
|
public TransformerType TransformerType { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the ReplaceNodeOptions to use when transforming a JSON node.
|
/// Gets a value indicating whether to use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; private set; }
|
public bool UseTransformerForBodyAsFile { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the response message.
|
/// Gets the ReplaceNodeOptions to use when transforming a JSON node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ResponseMessage ResponseMessage { get; }
|
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates this instance.
|
/// Gets the response message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="responseMessage">ResponseMessage</param>
|
public ResponseMessage ResponseMessage { get; }
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
[PublicAPI]
|
/// <summary>
|
||||||
public static IResponseBuilder Create([CanBeNull] ResponseMessage responseMessage = null)
|
/// Creates this instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="responseMessage">ResponseMessage</param>
|
||||||
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public static IResponseBuilder Create(ResponseMessage? responseMessage = null)
|
||||||
|
{
|
||||||
|
var message = responseMessage ?? new ResponseMessage();
|
||||||
|
return new Response(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates this instance with the specified function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="func">The callback function.</param>
|
||||||
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public static IResponseBuilder Create(Func<ResponseMessage> func)
|
||||||
|
{
|
||||||
|
Guard.NotNull(func);
|
||||||
|
|
||||||
|
return new Response(func());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Response"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="responseMessage">
|
||||||
|
/// The response.
|
||||||
|
/// </param>
|
||||||
|
private Response(ResponseMessage responseMessage)
|
||||||
|
{
|
||||||
|
ResponseMessage = responseMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
|
||||||
|
[PublicAPI]
|
||||||
|
public IResponseBuilder WithStatusCode(int code)
|
||||||
|
{
|
||||||
|
ResponseMessage.StatusCode = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(string)"/>
|
||||||
|
[PublicAPI]
|
||||||
|
public IResponseBuilder WithStatusCode(string code)
|
||||||
|
{
|
||||||
|
ResponseMessage.StatusCode = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(HttpStatusCode)"/>
|
||||||
|
[PublicAPI]
|
||||||
|
public IResponseBuilder WithStatusCode(HttpStatusCode code)
|
||||||
|
{
|
||||||
|
return WithStatusCode((int)code);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The with Success status code (200).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public IResponseBuilder WithSuccess()
|
||||||
|
{
|
||||||
|
return WithStatusCode((int)HttpStatusCode.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The with NotFound status code (404).
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public IResponseBuilder WithNotFound()
|
||||||
|
{
|
||||||
|
return WithStatusCode((int)HttpStatusCode.NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeader(string, string[])"/>
|
||||||
|
public IResponseBuilder WithHeader(string name, params string[] values)
|
||||||
|
{
|
||||||
|
Guard.NotNull(name);
|
||||||
|
|
||||||
|
ResponseMessage.AddHeader(name, values);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string})"/>
|
||||||
|
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
|
||||||
|
{
|
||||||
|
Guard.NotNull(headers);
|
||||||
|
|
||||||
|
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string[]})"/>
|
||||||
|
public IResponseBuilder WithHeaders(IDictionary<string, string[]> headers)
|
||||||
|
{
|
||||||
|
Guard.NotNull(headers);
|
||||||
|
|
||||||
|
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, WireMockList{string}})"/>
|
||||||
|
public IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers)
|
||||||
|
{
|
||||||
|
Guard.NotNull(headers);
|
||||||
|
|
||||||
|
ResponseMessage.Headers = headers;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||||
|
{
|
||||||
|
Guard.NotNull(bodyFactory, nameof(bodyFactory));
|
||||||
|
|
||||||
|
return WithCallbackInternal(true, req => new ResponseMessage
|
||||||
{
|
{
|
||||||
var message = responseMessage ?? new ResponseMessage();
|
BodyData = new BodyData
|
||||||
return new Response(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates this instance with the specified function.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="func">The callback function.</param>
|
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
[PublicAPI]
|
|
||||||
public static IResponseBuilder Create([NotNull] Func<ResponseMessage> func)
|
|
||||||
{
|
|
||||||
Guard.NotNull(func, nameof(func));
|
|
||||||
|
|
||||||
return new Response(func());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Response"/> class.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="responseMessage">
|
|
||||||
/// The response.
|
|
||||||
/// </param>
|
|
||||||
private Response(ResponseMessage responseMessage)
|
|
||||||
{
|
|
||||||
ResponseMessage = responseMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
|
|
||||||
[PublicAPI]
|
|
||||||
public IResponseBuilder WithStatusCode(int code)
|
|
||||||
{
|
|
||||||
ResponseMessage.StatusCode = code;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(string)"/>
|
|
||||||
[PublicAPI]
|
|
||||||
public IResponseBuilder WithStatusCode(string code)
|
|
||||||
{
|
|
||||||
ResponseMessage.StatusCode = code;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(HttpStatusCode)"/>
|
|
||||||
[PublicAPI]
|
|
||||||
public IResponseBuilder WithStatusCode(HttpStatusCode code)
|
|
||||||
{
|
|
||||||
return WithStatusCode((int)code);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The with Success status code (200).
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
[PublicAPI]
|
|
||||||
public IResponseBuilder WithSuccess()
|
|
||||||
{
|
|
||||||
return WithStatusCode((int)HttpStatusCode.OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The with NotFound status code (404).
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
|
||||||
[PublicAPI]
|
|
||||||
public IResponseBuilder WithNotFound()
|
|
||||||
{
|
|
||||||
return WithStatusCode((int)HttpStatusCode.NotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeader(string, string[])"/>
|
|
||||||
public IResponseBuilder WithHeader(string name, params string[] values)
|
|
||||||
{
|
|
||||||
Guard.NotNull(name, nameof(name));
|
|
||||||
|
|
||||||
ResponseMessage.AddHeader(name, values);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string})"/>
|
|
||||||
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
|
|
||||||
{
|
|
||||||
Guard.NotNull(headers, nameof(headers));
|
|
||||||
|
|
||||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string[]})"/>
|
|
||||||
public IResponseBuilder WithHeaders(IDictionary<string, string[]> headers)
|
|
||||||
{
|
|
||||||
Guard.NotNull(headers, nameof(headers));
|
|
||||||
|
|
||||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, WireMockList{string}})"/>
|
|
||||||
public IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers)
|
|
||||||
{
|
|
||||||
ResponseMessage.Headers = headers;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null)
|
|
||||||
{
|
|
||||||
Guard.NotNull(bodyFactory, nameof(bodyFactory));
|
|
||||||
|
|
||||||
return WithCallbackInternal(true, req => new ResponseMessage
|
|
||||||
{
|
{
|
||||||
BodyData = new BodyData
|
DetectedBodyType = BodyType.String,
|
||||||
{
|
BodyAsString = bodyFactory(req),
|
||||||
DetectedBodyType = BodyType.String,
|
Encoding = encoding ?? Encoding.UTF8
|
||||||
BodyAsString = bodyFactory(req),
|
|
||||||
Encoding = encoding ?? Encoding.UTF8
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseBuilder WithBody(Func<IRequestMessage, Task<string>> bodyFactory, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null)
|
|
||||||
{
|
|
||||||
Guard.NotNull(bodyFactory, nameof(bodyFactory));
|
|
||||||
|
|
||||||
return WithCallbackInternal(true, async req => new ResponseMessage
|
|
||||||
{
|
|
||||||
BodyData = new BodyData
|
|
||||||
{
|
|
||||||
DetectedBodyType = BodyType.String,
|
|
||||||
BodyAsString = await bodyFactory(req).ConfigureAwait(false),
|
|
||||||
Encoding = encoding ?? Encoding.UTF8
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBody(byte[], string, Encoding)"/>
|
|
||||||
public IResponseBuilder WithBody(byte[] body, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null)
|
|
||||||
{
|
|
||||||
Guard.NotNull(body, nameof(body));
|
|
||||||
|
|
||||||
ResponseMessage.BodyDestination = destination;
|
|
||||||
ResponseMessage.BodyData = new BodyData();
|
|
||||||
|
|
||||||
switch (destination)
|
|
||||||
{
|
|
||||||
case BodyDestinationFormat.String:
|
|
||||||
var enc = encoding ?? Encoding.UTF8;
|
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.String;
|
|
||||||
ResponseMessage.BodyData.BodyAsString = enc.GetString(body);
|
|
||||||
ResponseMessage.BodyData.Encoding = enc;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
|
|
||||||
ResponseMessage.BodyData.BodyAsBytes = body;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return this;
|
/// <inheritdoc />
|
||||||
}
|
public IResponseBuilder WithBody(Func<IRequestMessage, Task<string>> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||||
|
{
|
||||||
|
Guard.NotNull(bodyFactory, nameof(bodyFactory));
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromFile"/>
|
return WithCallbackInternal(true, async req => new ResponseMessage
|
||||||
public IResponseBuilder WithBodyFromFile(string filename, bool cache = true)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(filename, nameof(filename));
|
BodyData = new BodyData
|
||||||
|
|
||||||
ResponseMessage.BodyData = new BodyData
|
|
||||||
{
|
{
|
||||||
BodyAsFileIsCached = cache,
|
DetectedBodyType = BodyType.String,
|
||||||
BodyAsFile = filename
|
BodyAsString = await bodyFactory(req).ConfigureAwait(false),
|
||||||
};
|
Encoding = encoding ?? Encoding.UTF8
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (cache && !UseTransformer)
|
/// <inheritdoc />
|
||||||
{
|
public IResponseBuilder WithBody(byte[] body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||||
|
{
|
||||||
|
Guard.NotNull(body);
|
||||||
|
|
||||||
|
ResponseMessage.BodyDestination = destination;
|
||||||
|
ResponseMessage.BodyData = new BodyData();
|
||||||
|
|
||||||
|
switch (destination)
|
||||||
|
{
|
||||||
|
case BodyDestinationFormat.String:
|
||||||
|
var enc = encoding ?? Encoding.UTF8;
|
||||||
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.String;
|
||||||
|
ResponseMessage.BodyData.BodyAsString = enc.GetString(body);
|
||||||
|
ResponseMessage.BodyData.Encoding = enc;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
|
||||||
|
ResponseMessage.BodyData.BodyAsBytes = body;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromFile"/>
|
||||||
|
public IResponseBuilder WithBodyFromFile(string filename, bool cache = true)
|
||||||
|
{
|
||||||
|
Guard.NotNull(filename);
|
||||||
|
|
||||||
|
ResponseMessage.BodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsFileIsCached = cache,
|
||||||
|
BodyAsFile = filename
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cache && !UseTransformer)
|
||||||
|
{
|
||||||
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.File;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||||
|
{
|
||||||
|
Guard.NotNull(body);
|
||||||
|
|
||||||
|
encoding ??= Encoding.UTF8;
|
||||||
|
|
||||||
|
ResponseMessage.BodyDestination = destination;
|
||||||
|
ResponseMessage.BodyData = new BodyData
|
||||||
|
{
|
||||||
|
Encoding = encoding
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (destination)
|
||||||
|
{
|
||||||
|
case BodyDestinationFormat.Bytes:
|
||||||
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
|
||||||
|
ResponseMessage.BodyData.BodyAsBytes = encoding.GetBytes(body);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyDestinationFormat.Json:
|
||||||
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.Json;
|
||||||
|
ResponseMessage.BodyData.BodyAsJson = JsonUtils.DeserializeObject(body);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ResponseMessage.BodyData.DetectedBodyType = BodyType.String;
|
||||||
|
ResponseMessage.BodyData.BodyAsString = body;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson(object, Encoding, bool?)"/>
|
||||||
|
public IResponseBuilder WithBodyAsJson(object body, Encoding? encoding = null, bool? indented = null)
|
||||||
|
{
|
||||||
|
Guard.NotNull(body);
|
||||||
|
|
||||||
|
ResponseMessage.BodyDestination = null;
|
||||||
|
ResponseMessage.BodyData = new BodyData
|
||||||
|
{
|
||||||
|
Encoding = encoding,
|
||||||
|
DetectedBodyType = BodyType.Json,
|
||||||
|
BodyAsJson = body,
|
||||||
|
BodyAsJsonIndented = indented
|
||||||
|
};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson(object, bool)"/>
|
||||||
|
public IResponseBuilder WithBodyAsJson(object body, bool indented)
|
||||||
|
{
|
||||||
|
return WithBodyAsJson(body, null, indented);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer(bool)"/>
|
||||||
|
public IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile)
|
||||||
|
{
|
||||||
|
return WithTransformer(TransformerType.Handlebars, transformContentFromBodyAsFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer(ReplaceNodeOptions)"/>
|
||||||
|
public IResponseBuilder WithTransformer(ReplaceNodeOptions options)
|
||||||
|
{
|
||||||
|
return WithTransformer(TransformerType.Handlebars, false, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithTransformer(TransformerType transformerType, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None)
|
||||||
|
{
|
||||||
|
UseTransformer = true;
|
||||||
|
TransformerType = transformerType;
|
||||||
|
UseTransformerForBodyAsFile = transformContentFromBodyAsFile;
|
||||||
|
TransformerReplaceNodeOptions = options;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithDelay(TimeSpan delay)
|
||||||
|
{
|
||||||
|
Guard.Condition(delay, d => d == Timeout.InfiniteTimeSpan || d > TimeSpan.Zero);
|
||||||
|
|
||||||
|
Delay = delay;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithDelay(int milliseconds)
|
||||||
|
{
|
||||||
|
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000)
|
||||||
|
{
|
||||||
|
Guard.Condition(minimumMilliseconds, min => min >= 0);
|
||||||
|
Guard.Condition(maximumMilliseconds, max => max > minimumMilliseconds);
|
||||||
|
|
||||||
|
MinimumDelayMilliseconds = minimumMilliseconds;
|
||||||
|
MaximumDelayMilliseconds = maximumMilliseconds;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
||||||
|
{
|
||||||
|
Guard.NotNull(requestMessage);
|
||||||
|
Guard.NotNull(settings);
|
||||||
|
|
||||||
|
if (Delay != null)
|
||||||
|
{
|
||||||
|
await Task.Delay(Delay.Value).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ProxyAndRecordSettings != null && _httpClientForProxy != null)
|
||||||
|
{
|
||||||
|
string RemoveFirstOccurrence(string source, string find)
|
||||||
|
{
|
||||||
|
int place = source.IndexOf(find, StringComparison.OrdinalIgnoreCase);
|
||||||
|
return place >= 0 ? source.Remove(place, find.Length) : source;
|
||||||
|
}
|
||||||
|
|
||||||
|
var requestUri = new Uri(requestMessage.Url);
|
||||||
|
|
||||||
|
// Build the proxy url and skip duplicates
|
||||||
|
string extra = RemoveFirstOccurrence(requestUri.LocalPath.TrimEnd('/'), new Uri(ProxyAndRecordSettings.Url).LocalPath.TrimEnd('/'));
|
||||||
|
requestMessage.ProxyUrl = ProxyAndRecordSettings.Url + extra + requestUri.Query;
|
||||||
|
|
||||||
|
var proxyHelper = new ProxyHelper(settings);
|
||||||
|
|
||||||
|
return await proxyHelper.SendAsync(
|
||||||
|
ProxyAndRecordSettings,
|
||||||
|
_httpClientForProxy,
|
||||||
|
requestMessage,
|
||||||
|
requestMessage.ProxyUrl
|
||||||
|
).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResponseMessage responseMessage;
|
||||||
|
if (!WithCallbackUsed)
|
||||||
|
{
|
||||||
|
responseMessage = ResponseMessage;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Callback != null)
|
||||||
|
{
|
||||||
|
responseMessage = Callback(requestMessage);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.File;
|
responseMessage = await CallbackAsync!(requestMessage).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
// Copy StatusCode from ResponseMessage (if defined)
|
||||||
|
if (ResponseMessage.StatusCode != null)
|
||||||
|
{
|
||||||
|
responseMessage.StatusCode = ResponseMessage.StatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy Headers from ResponseMessage (if defined)
|
||||||
|
if (ResponseMessage.Headers?.Count > 0)
|
||||||
|
{
|
||||||
|
responseMessage.Headers = ResponseMessage.Headers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBody(string, string, Encoding)"/>
|
if (UseTransformer)
|
||||||
public IResponseBuilder WithBody(string body, string destination = BodyDestinationFormat.SameAsSource, Encoding encoding = null)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(body, nameof(body));
|
ITransformer responseMessageTransformer;
|
||||||
|
switch (TransformerType)
|
||||||
encoding = encoding ?? Encoding.UTF8;
|
|
||||||
|
|
||||||
ResponseMessage.BodyDestination = destination;
|
|
||||||
|
|
||||||
ResponseMessage.BodyData = new BodyData
|
|
||||||
{
|
{
|
||||||
Encoding = encoding
|
case TransformerType.Handlebars:
|
||||||
};
|
var factoryHandlebars = new HandlebarsContextFactory(settings.FileSystemHandler, settings.HandlebarsRegistrationCallback);
|
||||||
|
responseMessageTransformer = new Transformer(factoryHandlebars);
|
||||||
switch (destination)
|
|
||||||
{
|
|
||||||
case BodyDestinationFormat.Bytes:
|
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
|
|
||||||
ResponseMessage.BodyData.BodyAsBytes = encoding.GetBytes(body);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BodyDestinationFormat.Json:
|
case TransformerType.Scriban:
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.Json;
|
case TransformerType.ScribanDotLiquid:
|
||||||
ResponseMessage.BodyData.BodyAsJson = JsonUtils.DeserializeObject(body);
|
var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, TransformerType);
|
||||||
|
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ResponseMessage.BodyData.DetectedBodyType = BodyType.String;
|
throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported.");
|
||||||
ResponseMessage.BodyData.BodyAsString = body;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return (responseMessageTransformer.Transform(requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson(object, Encoding, bool?)"/>
|
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)
|
||||||
public IResponseBuilder WithBodyAsJson(object body, Encoding encoding = null, bool? indented = null)
|
|
||||||
{
|
{
|
||||||
Guard.NotNull(body, nameof(body));
|
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData!.BodyAsFile);
|
||||||
|
|
||||||
ResponseMessage.BodyDestination = null;
|
|
||||||
ResponseMessage.BodyData = new BodyData
|
|
||||||
{
|
|
||||||
Encoding = encoding,
|
|
||||||
DetectedBodyType = BodyType.Json,
|
|
||||||
BodyAsJson = body,
|
|
||||||
BodyAsJsonIndented = indented
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson(object, bool)"/>
|
return (responseMessage, null);
|
||||||
public IResponseBuilder WithBodyAsJson(object body, bool indented)
|
|
||||||
{
|
|
||||||
return WithBodyAsJson(body, null, indented);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer(bool)"/>
|
|
||||||
public IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile)
|
|
||||||
{
|
|
||||||
return WithTransformer(TransformerType.Handlebars, transformContentFromBodyAsFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer(ReplaceNodeOptions)"/>
|
|
||||||
public IResponseBuilder WithTransformer(ReplaceNodeOptions options)
|
|
||||||
{
|
|
||||||
return WithTransformer(TransformerType.Handlebars, false, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseBuilder WithTransformer(TransformerType transformerType, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None)
|
|
||||||
{
|
|
||||||
UseTransformer = true;
|
|
||||||
TransformerType = transformerType;
|
|
||||||
UseTransformerForBodyAsFile = transformContentFromBodyAsFile;
|
|
||||||
TransformerReplaceNodeOptions = options;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseBuilder WithDelay(TimeSpan delay)
|
|
||||||
{
|
|
||||||
Guard.Condition(delay, d => d == Timeout.InfiniteTimeSpan || d > TimeSpan.Zero);
|
|
||||||
|
|
||||||
Delay = delay;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseBuilder WithDelay(int milliseconds)
|
|
||||||
{
|
|
||||||
return WithDelay(TimeSpan.FromMilliseconds(milliseconds));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000)
|
|
||||||
{
|
|
||||||
Guard.Condition(minimumMilliseconds, min => min >= 0);
|
|
||||||
Guard.Condition(maximumMilliseconds, max => max > minimumMilliseconds);
|
|
||||||
|
|
||||||
MinimumDelayMilliseconds = minimumMilliseconds;
|
|
||||||
MaximumDelayMilliseconds = maximumMilliseconds;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public async Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
|
||||||
{
|
|
||||||
Guard.NotNull(requestMessage, nameof(requestMessage));
|
|
||||||
Guard.NotNull(settings, nameof(settings));
|
|
||||||
|
|
||||||
if (Delay != null)
|
|
||||||
{
|
|
||||||
await Task.Delay(Delay.Value).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ProxyAndRecordSettings != null && _httpClientForProxy != null)
|
|
||||||
{
|
|
||||||
string RemoveFirstOccurrence(string source, string find)
|
|
||||||
{
|
|
||||||
int place = source.IndexOf(find, StringComparison.OrdinalIgnoreCase);
|
|
||||||
return place >= 0 ? source.Remove(place, find.Length) : source;
|
|
||||||
}
|
|
||||||
|
|
||||||
var requestUri = new Uri(requestMessage.Url);
|
|
||||||
|
|
||||||
// Build the proxy url and skip duplicates
|
|
||||||
string extra = RemoveFirstOccurrence(requestUri.LocalPath.TrimEnd('/'), new Uri(ProxyAndRecordSettings.Url).LocalPath.TrimEnd('/'));
|
|
||||||
requestMessage.ProxyUrl = ProxyAndRecordSettings.Url + extra + requestUri.Query;
|
|
||||||
|
|
||||||
var proxyHelper = new ProxyHelper(settings);
|
|
||||||
|
|
||||||
return await proxyHelper.SendAsync(
|
|
||||||
ProxyAndRecordSettings,
|
|
||||||
_httpClientForProxy,
|
|
||||||
requestMessage,
|
|
||||||
requestMessage.ProxyUrl
|
|
||||||
).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResponseMessage responseMessage;
|
|
||||||
if (!WithCallbackUsed)
|
|
||||||
{
|
|
||||||
responseMessage = ResponseMessage;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Callback != null)
|
|
||||||
{
|
|
||||||
responseMessage = Callback(requestMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
responseMessage = await CallbackAsync(requestMessage).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy StatusCode from ResponseMessage (if defined)
|
|
||||||
if (ResponseMessage.StatusCode != null)
|
|
||||||
{
|
|
||||||
responseMessage.StatusCode = ResponseMessage.StatusCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy Headers from ResponseMessage (if defined)
|
|
||||||
if (ResponseMessage.Headers?.Count > 0)
|
|
||||||
{
|
|
||||||
responseMessage.Headers = ResponseMessage.Headers;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UseTransformer)
|
|
||||||
{
|
|
||||||
ITransformer responseMessageTransformer;
|
|
||||||
switch (TransformerType)
|
|
||||||
{
|
|
||||||
case TransformerType.Handlebars:
|
|
||||||
var factoryHandlebars = new HandlebarsContextFactory(settings.FileSystemHandler, settings.HandlebarsRegistrationCallback);
|
|
||||||
responseMessageTransformer = new Transformer(factoryHandlebars);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TransformerType.Scriban:
|
|
||||||
case TransformerType.ScribanDotLiquid:
|
|
||||||
var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, TransformerType);
|
|
||||||
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (responseMessageTransformer.Transform(requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)
|
|
||||||
{
|
|
||||||
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (responseMessage, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,50 +7,51 @@ using WireMock.Types;
|
|||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock
|
namespace WireMock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ResponseMessage.
|
||||||
|
/// </summary>
|
||||||
|
public class ResponseMessage : IResponseMessage
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc cref="IResponseMessage.Headers" />
|
||||||
/// The ResponseMessage.
|
public IDictionary<string, WireMockList<string>>? Headers { get; set; } = new Dictionary<string, WireMockList<string>>();
|
||||||
/// </summary>
|
|
||||||
public class ResponseMessage : IResponseMessage
|
/// <inheritdoc cref="IResponseMessage.StatusCode" />
|
||||||
|
public object? StatusCode { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IResponseMessage.BodyOriginal" />
|
||||||
|
public string? BodyOriginal { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IResponseMessage.BodyDestination" />
|
||||||
|
public string? BodyDestination { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IResponseMessage.BodyData" />
|
||||||
|
public IBodyData? BodyData { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IResponseMessage.FaultType" />
|
||||||
|
public FaultType FaultType { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IResponseMessage.FaultPercentage" />
|
||||||
|
public double? FaultPercentage { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IResponseMessage.AddHeader(string, string)" />
|
||||||
|
public void AddHeader(string name, string value)
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="IResponseMessage.Headers" />
|
Headers ??= new Dictionary<string, WireMockList<string>>();
|
||||||
public IDictionary<string, WireMockList<string>>? Headers { get; set; } = new Dictionary<string, WireMockList<string>>();
|
Headers.Add(name, new WireMockList<string>(value));
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.StatusCode" />
|
/// <inheritdoc cref="IResponseMessage.AddHeader(string, string[])" />
|
||||||
public object StatusCode { get; set; }
|
public void AddHeader(string name, params string[] values)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(values);
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.BodyOriginal" />
|
Headers ??= new Dictionary<string, WireMockList<string>>();
|
||||||
public string BodyOriginal { get; set; }
|
var newHeaderValues = Headers.TryGetValue(name, out WireMockList<string>? existingValues)
|
||||||
|
? values.Union(existingValues).ToArray()
|
||||||
|
: values;
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.BodyDestination" />
|
Headers[name] = new WireMockList<string>(newHeaderValues);
|
||||||
public string BodyDestination { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.BodyData" />
|
|
||||||
public IBodyData? BodyData { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.FaultType" />
|
|
||||||
public FaultType FaultType { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.FaultPercentage" />
|
|
||||||
public double? FaultPercentage { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.AddHeader(string, string)" />
|
|
||||||
public void AddHeader(string name, string value)
|
|
||||||
{
|
|
||||||
Headers.Add(name, new WireMockList<string>(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IResponseMessage.AddHeader(string, string[])" />
|
|
||||||
public void AddHeader(string name, params string[] values)
|
|
||||||
{
|
|
||||||
Guard.NotNullOrEmpty(values, nameof(values));
|
|
||||||
|
|
||||||
var newHeaderValues = Headers.TryGetValue(name, out WireMockList<string> existingValues)
|
|
||||||
? values.Union(existingValues).ToArray()
|
|
||||||
: values;
|
|
||||||
|
|
||||||
Headers[name] = new WireMockList<string>(newHeaderValues);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,20 +2,19 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.ResponseProviders
|
namespace WireMock.ResponseProviders;
|
||||||
|
|
||||||
|
internal class DynamicAsyncResponseProvider : IResponseProvider
|
||||||
{
|
{
|
||||||
internal class DynamicAsyncResponseProvider : IResponseProvider
|
private readonly Func<IRequestMessage, Task<IResponseMessage>> _responseMessageFunc;
|
||||||
|
|
||||||
|
public DynamicAsyncResponseProvider(Func<IRequestMessage, Task<IResponseMessage>> responseMessageFunc)
|
||||||
{
|
{
|
||||||
private readonly Func<IRequestMessage, Task<IResponseMessage>> _responseMessageFunc;
|
_responseMessageFunc = responseMessageFunc;
|
||||||
|
}
|
||||||
|
|
||||||
public DynamicAsyncResponseProvider(Func<IRequestMessage, Task<IResponseMessage>> responseMessageFunc)
|
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
||||||
{
|
{
|
||||||
_responseMessageFunc = responseMessageFunc;
|
return (await _responseMessageFunc(requestMessage).ConfigureAwait(false), null);
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
|
||||||
{
|
|
||||||
return (await _responseMessageFunc(requestMessage).ConfigureAwait(false), null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,21 +2,20 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.ResponseProviders
|
namespace WireMock.ResponseProviders;
|
||||||
|
|
||||||
|
internal class DynamicResponseProvider : IResponseProvider
|
||||||
{
|
{
|
||||||
internal class DynamicResponseProvider : IResponseProvider
|
private readonly Func<IRequestMessage, IResponseMessage> _responseMessageFunc;
|
||||||
|
|
||||||
|
public DynamicResponseProvider(Func<IRequestMessage, IResponseMessage> responseMessageFunc)
|
||||||
{
|
{
|
||||||
private readonly Func<IRequestMessage, IResponseMessage> _responseMessageFunc;
|
_responseMessageFunc = responseMessageFunc;
|
||||||
|
}
|
||||||
|
|
||||||
public DynamicResponseProvider(Func<IRequestMessage, IResponseMessage> responseMessageFunc)
|
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
||||||
{
|
{
|
||||||
_responseMessageFunc = responseMessageFunc;
|
(IResponseMessage responseMessage, IMapping? mapping) result = (_responseMessageFunc(requestMessage), null);
|
||||||
}
|
return Task.FromResult(result);
|
||||||
|
|
||||||
public Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
|
||||||
{
|
|
||||||
(IResponseMessage responseMessage, IMapping mapping) result = (_responseMessageFunc(requestMessage), null);
|
|
||||||
return Task.FromResult(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
|
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
|
||||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.ResponseProviders
|
namespace WireMock.ResponseProviders;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The Response Provider interface.
|
||||||
|
/// </summary>
|
||||||
|
public interface IResponseProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Response Provider interface.
|
/// The provide response.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IResponseProvider
|
/// <param name="requestMessage">The request.</param>
|
||||||
{
|
/// <param name="settings">The WireMockServerSettings.</param>
|
||||||
/// <summary>
|
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
||||||
/// The provide response.
|
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings);
|
||||||
/// </summary>
|
|
||||||
/// <param name="requestMessage">The request.</param>
|
|
||||||
/// <param name="settings">The WireMockServerSettings.</param>
|
|
||||||
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
|
||||||
Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync([NotNull] IRequestMessage requestMessage, [NotNull] WireMockServerSettings settings);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -2,22 +2,21 @@ using System;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.ResponseProviders
|
namespace WireMock.ResponseProviders;
|
||||||
|
|
||||||
|
internal class ProxyAsyncResponseProvider : IResponseProvider
|
||||||
{
|
{
|
||||||
internal class ProxyAsyncResponseProvider : IResponseProvider
|
private readonly Func<IRequestMessage, WireMockServerSettings, Task<IResponseMessage>> _responseMessageFunc;
|
||||||
|
private readonly WireMockServerSettings _settings;
|
||||||
|
|
||||||
|
public ProxyAsyncResponseProvider(Func<IRequestMessage, WireMockServerSettings, Task<IResponseMessage>> responseMessageFunc, WireMockServerSettings settings)
|
||||||
{
|
{
|
||||||
private readonly Func<IRequestMessage, WireMockServerSettings, Task<IResponseMessage>> _responseMessageFunc;
|
_responseMessageFunc = responseMessageFunc;
|
||||||
private readonly WireMockServerSettings _settings;
|
_settings = settings;
|
||||||
|
}
|
||||||
|
|
||||||
public ProxyAsyncResponseProvider(Func<IRequestMessage, WireMockServerSettings, Task<IResponseMessage>> responseMessageFunc, WireMockServerSettings settings)
|
public async Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
||||||
{
|
{
|
||||||
_responseMessageFunc = responseMessageFunc;
|
return (await _responseMessageFunc(requestMessage, _settings).ConfigureAwait(false), null);
|
||||||
_settings = settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<(IResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
|
|
||||||
{
|
|
||||||
return (await _responseMessageFunc(requestMessage, _settings).ConfigureAwait(false), null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ internal static class LogEntryMapper
|
|||||||
MatchDetails = matchResult.MatchDetails.Select(md => new
|
MatchDetails = matchResult.MatchDetails.Select(md => new
|
||||||
{
|
{
|
||||||
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
Name = md.MatcherType.Name.Replace("RequestMessage", string.Empty),
|
||||||
Score = md.Score
|
md.Score
|
||||||
} as object).ToList()
|
} as object).ToList()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Stef.Validation;
|
|||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
using WireMock.Models;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
@@ -116,9 +117,10 @@ internal class MappingConverter
|
|||||||
mappingModel.Response.Delay = (int?)(response.Delay == Timeout.InfiniteTimeSpan ? TimeSpan.MaxValue.TotalMilliseconds : response.Delay?.TotalMilliseconds);
|
mappingModel.Response.Delay = (int?)(response.Delay == Timeout.InfiniteTimeSpan ? TimeSpan.MaxValue.TotalMilliseconds : response.Delay?.TotalMilliseconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mapping.Webhooks?.Length == 1)
|
var nonNullableWebHooks = mapping.Webhooks?.Where(wh => wh != null).ToArray() ?? new IWebhook[0];
|
||||||
|
if (nonNullableWebHooks.Length == 1)
|
||||||
{
|
{
|
||||||
mappingModel.Webhook = WebhookMapper.Map(mapping.Webhooks[0]);
|
mappingModel.Webhook = WebhookMapper.Map(nonNullableWebHooks[0]);
|
||||||
}
|
}
|
||||||
else if (mapping.Webhooks?.Length > 1)
|
else if (mapping.Webhooks?.Length > 1)
|
||||||
{
|
{
|
||||||
@@ -209,7 +211,7 @@ internal class MappingConverter
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.ResponseMessage.BodyData.Encoding != null && response.ResponseMessage.BodyData.Encoding.WebName != "utf-8")
|
if (response.ResponseMessage.BodyData?.Encoding != null && response.ResponseMessage.BodyData.Encoding.WebName != "utf-8")
|
||||||
{
|
{
|
||||||
mappingModel.Response.BodyEncoding = new EncodingModel
|
mappingModel.Response.BodyEncoding = new EncodingModel
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,10 +22,7 @@ internal class MappingToFileSaver
|
|||||||
|
|
||||||
public void SaveMappingToFile(IMapping mapping, string? folder = null)
|
public void SaveMappingToFile(IMapping mapping, string? folder = null)
|
||||||
{
|
{
|
||||||
if (folder == null)
|
folder ??= _settings.FileSystemHandler.GetMappingFolder();
|
||||||
{
|
|
||||||
folder = _settings.FileSystemHandler.GetMappingFolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_settings.FileSystemHandler.FolderExists(folder))
|
if (!_settings.FileSystemHandler.FolderExists(folder))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ internal class MatcherMapper
|
|||||||
return patternAsStringArray.ToAnyOfPatterns();
|
return patternAsStringArray.ToAnyOfPatterns();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matcher.Patterns?.OfType<string>() is IEnumerable<string> patternsAsStringArray)
|
if (matcher.Patterns?.OfType<string>() is { } patternsAsStringArray)
|
||||||
{
|
{
|
||||||
return patternsAsStringArray.ToAnyOfPatterns();
|
return patternsAsStringArray.ToAnyOfPatterns();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using DevLab.JmesPath.Interop;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Extensions;
|
using WireMock.Extensions;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
@@ -76,10 +77,25 @@ internal static class PactMapper
|
|||||||
{
|
{
|
||||||
Status = MapStatusCode(response.StatusCode),
|
Status = MapStatusCode(response.StatusCode),
|
||||||
Headers = MapResponseHeaders(response.Headers),
|
Headers = MapResponseHeaders(response.Headers),
|
||||||
Body = response.BodyAsJson
|
Body = MapBody(response)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static object? MapBody(ResponseModel? response)
|
||||||
|
{
|
||||||
|
if (response?.BodyAsJson != null)
|
||||||
|
{
|
||||||
|
return response.BodyAsJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response?.Body != null) // In case the body is a string, try to deserialize into object, else just return the string
|
||||||
|
{
|
||||||
|
return JsonUtils.TryDeserializeObject<object?>(response.Body) ?? response.Body;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static int MapStatusCode(object? statusCode)
|
private static int MapStatusCode(object? statusCode)
|
||||||
{
|
{
|
||||||
if (statusCode is string statusCodeAsString)
|
if (statusCode is string statusCodeAsString)
|
||||||
@@ -138,13 +154,13 @@ internal static class PactMapper
|
|||||||
return jsonMatcher?.Pattern;
|
return jsonMatcher?.Pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GetPatternAsStringFromMatchers(MatcherModel[]? matchers, string defaultValue)
|
//private static string GetPatternAsStringFromMatchers(MatcherModel[]? matchers, string defaultValue)
|
||||||
{
|
//{
|
||||||
if (matchers != null && matchers.Any() && matchers[0].Pattern is string patternAsString)
|
// if (matchers != null && matchers.Any() && matchers[0].Pattern is string patternAsString)
|
||||||
{
|
// {
|
||||||
return patternAsString;
|
// return patternAsString;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return defaultValue;
|
// return defaultValue;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
@@ -1,112 +1,109 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Http;
|
using WireMock.Http;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Serialization
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
|
internal static class WebhookMapper
|
||||||
{
|
{
|
||||||
internal static class WebhookMapper
|
public static IWebhook Map(WebhookModel model)
|
||||||
{
|
{
|
||||||
public static IWebhook Map(WebhookModel model)
|
var webhook = new Webhook
|
||||||
{
|
{
|
||||||
var webhook = new Webhook
|
Request = new WebhookRequest
|
||||||
{
|
{
|
||||||
Request = new WebhookRequest
|
Url = model.Request.Url,
|
||||||
{
|
Method = model.Request.Method,
|
||||||
Url = model.Request.Url,
|
Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList<string>(x.Value)) ?? new Dictionary<string, WireMockList<string>>()
|
||||||
Method = model.Request.Method,
|
}
|
||||||
Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList<string>(x.Value)) ?? new Dictionary<string, WireMockList<string>>()
|
};
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (model.Request.UseTransformer == true)
|
if (model.Request.UseTransformer == true)
|
||||||
|
{
|
||||||
|
webhook.Request.UseTransformer = true;
|
||||||
|
if (!Enum.TryParse<TransformerType>(model.Request.TransformerType, out var transformerType))
|
||||||
{
|
{
|
||||||
webhook.Request.UseTransformer = true;
|
transformerType = TransformerType.Handlebars;
|
||||||
if (!Enum.TryParse<TransformerType>(model.Request.TransformerType, out var transformerType))
|
}
|
||||||
{
|
webhook.Request.TransformerType = transformerType;
|
||||||
transformerType = TransformerType.Handlebars;
|
|
||||||
}
|
|
||||||
webhook.Request.TransformerType = transformerType;
|
|
||||||
|
|
||||||
if (!Enum.TryParse<ReplaceNodeOptions>(model.Request.TransformerReplaceNodeOptions, out var option))
|
if (!Enum.TryParse<ReplaceNodeOptions>(model.Request.TransformerReplaceNodeOptions, out var option))
|
||||||
{
|
{
|
||||||
option = ReplaceNodeOptions.None;
|
option = ReplaceNodeOptions.None;
|
||||||
}
|
|
||||||
|
|
||||||
webhook.Request.TransformerReplaceNodeOptions = option;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerable<string> contentTypeHeader = null;
|
webhook.Request.TransformerReplaceNodeOptions = option;
|
||||||
if (webhook.Request.Headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
|
||||||
contentTypeHeader = webhook.Request.Headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)).Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (model.Request.Body != null)
|
|
||||||
{
|
|
||||||
webhook.Request.BodyData = new BodyData
|
|
||||||
{
|
|
||||||
BodyAsString = model.Request.Body,
|
|
||||||
DetectedBodyType = BodyType.String,
|
|
||||||
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (model.Request.BodyAsJson != null)
|
|
||||||
{
|
|
||||||
webhook.Request.BodyData = new BodyData
|
|
||||||
{
|
|
||||||
BodyAsJson = model.Request.BodyAsJson,
|
|
||||||
DetectedBodyType = BodyType.Json,
|
|
||||||
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return webhook;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WebhookModel Map(IWebhook webhook)
|
IEnumerable<string>? contentTypeHeader = null;
|
||||||
|
if (webhook.Request.Headers != null && webhook.Request.Headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
if (webhook?.Request == null)
|
contentTypeHeader = webhook.Request.Headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)).Value;
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var model = new WebhookModel
|
|
||||||
{
|
|
||||||
Request = new WebhookRequestModel
|
|
||||||
{
|
|
||||||
Url = webhook.Request.Url,
|
|
||||||
Method = webhook.Request.Method,
|
|
||||||
Headers = webhook.Request.Headers?.ToDictionary(x => x.Key, x => x.Value.ToString()),
|
|
||||||
UseTransformer = webhook.Request.UseTransformer,
|
|
||||||
TransformerType = webhook.Request.UseTransformer == true ? webhook.Request.TransformerType.ToString() : null,
|
|
||||||
TransformerReplaceNodeOptions = webhook.Request.TransformerReplaceNodeOptions.ToString()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (webhook.Request.BodyData != null)
|
|
||||||
{
|
|
||||||
switch (webhook.Request.BodyData.DetectedBodyType)
|
|
||||||
{
|
|
||||||
case BodyType.String:
|
|
||||||
model.Request.Body = webhook.Request.BodyData.BodyAsString;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BodyType.Json:
|
|
||||||
model.Request.BodyAsJson = webhook.Request.BodyData.BodyAsJson;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Empty
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (model.Request.Body != null)
|
||||||
|
{
|
||||||
|
webhook.Request.BodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = model.Request.Body,
|
||||||
|
DetectedBodyType = BodyType.String,
|
||||||
|
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (model.Request.BodyAsJson != null)
|
||||||
|
{
|
||||||
|
webhook.Request.BodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsJson = model.Request.BodyAsJson,
|
||||||
|
DetectedBodyType = BodyType.Json,
|
||||||
|
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return webhook;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebhookModel Map(IWebhook webhook)
|
||||||
|
{
|
||||||
|
Guard.NotNull(webhook);
|
||||||
|
|
||||||
|
var model = new WebhookModel
|
||||||
|
{
|
||||||
|
Request = new WebhookRequestModel
|
||||||
|
{
|
||||||
|
Url = webhook.Request.Url,
|
||||||
|
Method = webhook.Request.Method,
|
||||||
|
Headers = webhook.Request.Headers?.ToDictionary(x => x.Key, x => x.Value.ToString()),
|
||||||
|
UseTransformer = webhook.Request.UseTransformer,
|
||||||
|
TransformerType = webhook.Request.UseTransformer == true ? webhook.Request.TransformerType.ToString() : null,
|
||||||
|
TransformerReplaceNodeOptions = webhook.Request.TransformerReplaceNodeOptions.ToString()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (webhook.Request.BodyData != null)
|
||||||
|
{
|
||||||
|
switch (webhook.Request.BodyData.DetectedBodyType)
|
||||||
|
{
|
||||||
|
case BodyType.String:
|
||||||
|
model.Request.Body = webhook.Request.BodyData.BodyAsString;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BodyType.Json:
|
||||||
|
model.Request.BodyAsJson = webhook.Request.BodyData.BodyAsJson;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Empty
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,7 +292,7 @@ public partial class WireMockServer
|
|||||||
|
|
||||||
private IResponseMessage SettingsUpdate(IRequestMessage requestMessage)
|
private IResponseMessage SettingsUpdate(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
var settings = DeserializeObject<SettingsModel>(requestMessage)!;
|
var settings = DeserializeObject<SettingsModel>(requestMessage);
|
||||||
|
|
||||||
// _settings
|
// _settings
|
||||||
_settings.AllowBodyForAllHttpMethods = settings.AllowBodyForAllHttpMethods;
|
_settings.AllowBodyForAllHttpMethods = settings.AllowBodyForAllHttpMethods;
|
||||||
@@ -571,7 +571,7 @@ public partial class WireMockServer
|
|||||||
{
|
{
|
||||||
var requestModel = DeserializeObject<RequestModel>(requestMessage);
|
var requestModel = DeserializeObject<RequestModel>(requestMessage);
|
||||||
|
|
||||||
var request = (Request)InitRequestBuilder(requestModel, false);
|
var request = (Request)InitRequestBuilder(requestModel, false)!;
|
||||||
|
|
||||||
var dict = new Dictionary<ILogEntry, RequestMatchResult>();
|
var dict = new Dictionary<ILogEntry, RequestMatchResult>();
|
||||||
foreach (var logEntry in LogEntries.Where(le => !le.RequestMessage.Path.StartsWith("/__admin/")))
|
foreach (var logEntry in LogEntries.Where(le => !le.RequestMessage.Path.StartsWith("/__admin/")))
|
||||||
@@ -625,6 +625,23 @@ public partial class WireMockServer
|
|||||||
_settings.FileSystemHandler.WriteFile(folder, filenameUpdated, bytes);
|
_settings.FileSystemHandler.WriteFile(folder, filenameUpdated, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the mappings as a Pact Json file V2.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stream">The (file) stream.</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public void SavePact(Stream stream)
|
||||||
|
{
|
||||||
|
var (_, bytes) = PactMapper.ToPact(this);
|
||||||
|
using var writer = new BinaryWriter(stream);
|
||||||
|
writer.Write(bytes);
|
||||||
|
|
||||||
|
if (stream.CanSeek)
|
||||||
|
{
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This stores details about the consumer of the interaction.
|
/// This stores details about the consumer of the interaction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -714,31 +731,28 @@ public partial class WireMockServer
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T? DeserializeObject<T>(IRequestMessage requestMessage)
|
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
|
||||||
{
|
{
|
||||||
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
|
return requestMessage.BodyData?.DetectedBodyType switch
|
||||||
{
|
{
|
||||||
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString);
|
BodyType.String => JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString),
|
||||||
}
|
|
||||||
|
|
||||||
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json)
|
BodyType.Json when requestMessage.BodyData?.BodyAsJson != null => ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!,
|
||||||
{
|
|
||||||
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return default(T);
|
_ => throw new NotSupportedException()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||||
{
|
{
|
||||||
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json)
|
if (requestMessage.BodyData?.DetectedBodyType == BodyType.Json && requestMessage.BodyData.BodyAsJson != null)
|
||||||
{
|
{
|
||||||
var bodyAsJson = requestMessage.BodyData.BodyAsJson;
|
var bodyAsJson = requestMessage.BodyData.BodyAsJson;
|
||||||
|
|
||||||
return DeserializeObjectToArray<T>(bodyAsJson);
|
return DeserializeObjectToArray<T>(bodyAsJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default(T[]);
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static T[] DeserializeJsonToArray<T>(string value)
|
private static T[] DeserializeJsonToArray<T>(string value)
|
||||||
@@ -754,6 +768,6 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
var singleResult = ((JObject)value).ToObject<T>();
|
var singleResult = ((JObject)value).ToObject<T>();
|
||||||
return new[] { singleResult };
|
return new[] { singleResult! };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -275,7 +275,7 @@ public partial class WireMockServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value) ?? new string[0];
|
var headers = JsonUtils.ParseJTokenToObject<string[]>(entry.Value);
|
||||||
responseBuilder.WithHeader(entry.Key, headers);
|
responseBuilder.WithHeader(entry.Key, headers);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,91 +2,105 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace WireMock.Settings
|
namespace WireMock.Settings;
|
||||||
|
|
||||||
|
// Based on http://blog.gauffin.org/2014/12/simple-command-line-parser/
|
||||||
|
internal class SimpleCommandLineParser
|
||||||
{
|
{
|
||||||
// Based on http://blog.gauffin.org/2014/12/simple-command-line-parser/
|
private const string Sigil = "--";
|
||||||
internal class SimpleCommandLineParser
|
|
||||||
|
private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
public void Parse(string[] arguments)
|
||||||
{
|
{
|
||||||
private const string Sigil = "--";
|
string currentName = string.Empty;
|
||||||
|
|
||||||
private IDictionary<string, string[]> Arguments { get; } = new Dictionary<string, string[]>(StringComparer.OrdinalIgnoreCase);
|
var values = new List<string>();
|
||||||
|
|
||||||
public void Parse(string[] arguments)
|
// Split a single argument on a space character to fix issue (e.g. Azure Service Fabric) when an argument is supplied like "--x abc" or '--x abc'
|
||||||
|
foreach (string arg in arguments.SelectMany(arg => arg.Split(' ')))
|
||||||
{
|
{
|
||||||
string currentName = null;
|
if (arg.StartsWith(Sigil))
|
||||||
|
|
||||||
var values = new List<string>();
|
|
||||||
|
|
||||||
// Split a single argument on a space character to fix issue (e.g. Azure Service Fabric) when an argument is supplied like "--x abc" or '--x abc'
|
|
||||||
foreach (string arg in arguments.SelectMany(arg => arg.Split(' ')))
|
|
||||||
{
|
{
|
||||||
if (arg.StartsWith(Sigil))
|
if (!string.IsNullOrEmpty(currentName))
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(currentName))
|
Arguments[currentName] = values.ToArray();
|
||||||
{
|
}
|
||||||
Arguments[currentName] = values.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
values.Clear();
|
values.Clear();
|
||||||
currentName = arg.Substring(Sigil.Length);
|
currentName = arg.Substring(Sigil.Length);
|
||||||
}
|
|
||||||
else if (string.IsNullOrEmpty(currentName))
|
|
||||||
{
|
|
||||||
Arguments[arg] = new string[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
values.Add(arg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else if (string.IsNullOrEmpty(currentName))
|
||||||
if (!string.IsNullOrEmpty(currentName))
|
|
||||||
{
|
{
|
||||||
Arguments[currentName] = values.ToArray();
|
Arguments[arg] = new string[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
values.Add(arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Contains(string name)
|
if (!string.IsNullOrEmpty(currentName))
|
||||||
{
|
{
|
||||||
return Arguments.ContainsKey(name);
|
Arguments[currentName] = values.ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
public string[] GetValues(string name, string[] defaultValue = null)
|
|
||||||
{
|
|
||||||
return Contains(name) ? Arguments[name] : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetValue<T>(string name, Func<string[], T> func, T defaultValue = default(T))
|
|
||||||
{
|
|
||||||
return Contains(name) ? func(Arguments[name]) : defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetBoolValue(string name, bool defaultValue = false)
|
|
||||||
{
|
|
||||||
return GetValue(name, values =>
|
|
||||||
{
|
|
||||||
string value = values.FirstOrDefault();
|
|
||||||
return !string.IsNullOrEmpty(value) ? bool.Parse(value) : defaultValue;
|
|
||||||
}, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool GetBoolSwitchValue(string name)
|
|
||||||
{
|
|
||||||
return Contains(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetIntValue(string name, int? defaultValue = null)
|
|
||||||
{
|
|
||||||
return GetValue(name, values =>
|
|
||||||
{
|
|
||||||
string value = values.FirstOrDefault();
|
|
||||||
return !string.IsNullOrEmpty(value) ? int.Parse(value) : defaultValue;
|
|
||||||
}, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetStringValue(string name, string defaultValue = null)
|
|
||||||
{
|
|
||||||
return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Contains(string name)
|
||||||
|
{
|
||||||
|
return Arguments.ContainsKey(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetValues(string name, string[] defaultValue)
|
||||||
|
{
|
||||||
|
return Contains(name) ? Arguments[name] : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[]? GetValues(string name)
|
||||||
|
{
|
||||||
|
return Contains(name) ? Arguments[name] : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T GetValue<T>(string name, Func<string[], T> func, T defaultValue)
|
||||||
|
{
|
||||||
|
return Contains(name) ? func(Arguments[name]) : defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T? GetValue<T>(string name, Func<string[], T> func)
|
||||||
|
{
|
||||||
|
return Contains(name) ? func(Arguments[name]) : default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetBoolValue(string name, bool defaultValue = false)
|
||||||
|
{
|
||||||
|
return GetValue(name, values =>
|
||||||
|
{
|
||||||
|
var value = values.FirstOrDefault();
|
||||||
|
return !string.IsNullOrEmpty(value) ? bool.Parse(value) : defaultValue;
|
||||||
|
}, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GetBoolSwitchValue(string name)
|
||||||
|
{
|
||||||
|
return Contains(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetIntValue(string name, int? defaultValue = null)
|
||||||
|
{
|
||||||
|
return GetValue(name, values =>
|
||||||
|
{
|
||||||
|
var value = values.FirstOrDefault();
|
||||||
|
return !string.IsNullOrEmpty(value) ? int.Parse(value) : defaultValue;
|
||||||
|
}, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetStringValue(string name, string defaultValue)
|
||||||
|
{
|
||||||
|
return GetValue(name, values => values.FirstOrDefault() ?? defaultValue, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? GetStringValue(string name)
|
||||||
|
{
|
||||||
|
return GetValue(name, values => values.FirstOrDefault());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,8 @@
|
|||||||
namespace WireMock.Settings
|
namespace WireMock.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WebhookSettings
|
||||||
|
/// </summary>
|
||||||
|
public class WebhookSettings : HttpClientSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// WebhookSettings
|
|
||||||
/// </summary>
|
|
||||||
public class WebhookSettings : HttpClientSettings
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -251,5 +251,11 @@ namespace WireMock.Settings
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI, JsonIgnore]
|
[PublicAPI, JsonIgnore]
|
||||||
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
|
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="JsonSerializerSettings"/> used when the a JSON response is generated.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI, JsonIgnore]
|
||||||
|
public JsonSerializerSettings? JsonSerializerSettings { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,129 +1,129 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Settings
|
namespace WireMock.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A static helper class to parse commandline arguments into WireMockServerSettings.
|
||||||
|
/// </summary>
|
||||||
|
public static class WireMockServerSettingsParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A static helper class to parse commandline arguments into WireMockServerSettings.
|
/// Parse commandline arguments into WireMockServerSettings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class WireMockServerSettingsParser
|
/// <param name="args">The commandline arguments</param>
|
||||||
|
/// <param name="logger">The logger (optional, can be null)</param>
|
||||||
|
/// <param name="settings">The parsed settings</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
|
||||||
{
|
{
|
||||||
/// <summary>
|
Guard.HasNoNulls(args, nameof(args));
|
||||||
/// Parse commandline arguments into WireMockServerSettings.
|
|
||||||
/// </summary>
|
var parser = new SimpleCommandLineParser();
|
||||||
/// <param name="args">The commandline arguments</param>
|
parser.Parse(args);
|
||||||
/// <param name="logger">The logger (optional, can be null)</param>
|
|
||||||
/// <param name="settings">The parsed settings</param>
|
if (parser.GetBoolSwitchValue("help"))
|
||||||
[PublicAPI]
|
|
||||||
public static bool TryParseArguments([NotNull] string[] args, out WireMockServerSettings settings, [CanBeNull] IWireMockLogger logger = null)
|
|
||||||
{
|
{
|
||||||
Guard.HasNoNulls(args, nameof(args));
|
(logger ?? new WireMockConsoleLogger()).Info("See https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-commandline-parameters for details on all commandline options.");
|
||||||
|
settings = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var parser = new SimpleCommandLineParser();
|
settings = new WireMockServerSettings
|
||||||
parser.Parse(args);
|
{
|
||||||
|
AdminAzureADAudience = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADAudience)),
|
||||||
if (parser.GetBoolSwitchValue("help"))
|
AdminAzureADTenant = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADTenant)),
|
||||||
{
|
AdminPassword = parser.GetStringValue("AdminPassword"),
|
||||||
(logger ?? new WireMockConsoleLogger()).Info("See https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-commandline-parameters for details on all commandline options.");
|
AdminUsername = parser.GetStringValue("AdminUsername"),
|
||||||
settings = null;
|
AllowBodyForAllHttpMethods = parser.GetBoolValue("AllowBodyForAllHttpMethods"),
|
||||||
return false;
|
AllowCSharpCodeMatcher = parser.GetBoolValue("AllowCSharpCodeMatcher"),
|
||||||
}
|
AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue("AllowOnlyDefinedHttpStatusCodeInResponse"),
|
||||||
|
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping"),
|
||||||
settings = new WireMockServerSettings
|
DisableJsonBodyParsing = parser.GetBoolValue("DisableJsonBodyParsing"),
|
||||||
{
|
HandleRequestsSynchronously = parser.GetBoolValue("HandleRequestsSynchronously"),
|
||||||
AdminAzureADAudience = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADAudience)),
|
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
|
||||||
AdminAzureADTenant = parser.GetStringValue(nameof(WireMockServerSettings.AdminAzureADTenant)),
|
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
|
||||||
AdminPassword = parser.GetStringValue("AdminPassword"),
|
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
|
||||||
AdminUsername = parser.GetStringValue("AdminUsername"),
|
SaveUnmatchedRequests = parser.GetBoolValue(nameof(WireMockServerSettings.SaveUnmatchedRequests)),
|
||||||
AllowBodyForAllHttpMethods = parser.GetBoolValue("AllowBodyForAllHttpMethods"),
|
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
|
||||||
AllowCSharpCodeMatcher = parser.GetBoolValue("AllowCSharpCodeMatcher"),
|
ThrowExceptionWhenMatcherFails = parser.GetBoolValue("ThrowExceptionWhenMatcherFails"),
|
||||||
AllowOnlyDefinedHttpStatusCodeInResponse = parser.GetBoolValue("AllowOnlyDefinedHttpStatusCodeInResponse"),
|
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
|
||||||
AllowPartialMapping = parser.GetBoolValue("AllowPartialMapping"),
|
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
|
||||||
DisableJsonBodyParsing = parser.GetBoolValue("DisableJsonBodyParsing"),
|
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
|
||||||
HandleRequestsSynchronously = parser.GetBoolValue("HandleRequestsSynchronously"),
|
};
|
||||||
MaxRequestLogCount = parser.GetIntValue("MaxRequestLogCount"),
|
|
||||||
ReadStaticMappings = parser.GetBoolValue("ReadStaticMappings"),
|
|
||||||
RequestLogExpirationDuration = parser.GetIntValue("RequestLogExpirationDuration"),
|
|
||||||
SaveUnmatchedRequests = parser.GetBoolValue(nameof(WireMockServerSettings.SaveUnmatchedRequests)),
|
|
||||||
StartAdminInterface = parser.GetBoolValue("StartAdminInterface", true),
|
|
||||||
ThrowExceptionWhenMatcherFails = parser.GetBoolValue("ThrowExceptionWhenMatcherFails"),
|
|
||||||
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
|
|
||||||
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
|
|
||||||
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
|
|
||||||
};
|
|
||||||
|
|
||||||
#if USE_ASPNETCORE
|
#if USE_ASPNETCORE
|
||||||
settings.CorsPolicyOptions = parser.GetValue(nameof(WireMockServerSettings.CorsPolicyOptions), values =>
|
settings.CorsPolicyOptions = parser.GetValue(nameof(WireMockServerSettings.CorsPolicyOptions), values =>
|
||||||
{
|
{
|
||||||
var value = string.Join(string.Empty, values);
|
var value = string.Join(string.Empty, values);
|
||||||
return Enum.TryParse<CorsPolicyOptions>(value, true, out var corsPolicyOptions) ? corsPolicyOptions : CorsPolicyOptions.None;
|
return Enum.TryParse<CorsPolicyOptions>(value, true, out var corsPolicyOptions) ? corsPolicyOptions : CorsPolicyOptions.None;
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (logger != null)
|
if (logger != null)
|
||||||
{
|
{
|
||||||
settings.Logger = logger;
|
settings.Logger = logger;
|
||||||
}
|
|
||||||
|
|
||||||
if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
|
|
||||||
{
|
|
||||||
settings.Logger = new WireMockConsoleLogger();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
|
||||||
{
|
|
||||||
settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
|
||||||
}
|
|
||||||
|
|
||||||
string proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
|
|
||||||
if (!string.IsNullOrEmpty(proxyUrl))
|
|
||||||
{
|
|
||||||
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
|
||||||
{
|
|
||||||
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
|
|
||||||
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
|
|
||||||
ExcludedCookies = parser.GetValues("ExcludedCookies"),
|
|
||||||
ExcludedHeaders = parser.GetValues("ExcludedHeaders"),
|
|
||||||
// PreferProxyMapping = parser.GetBoolValue(nameof(ProxyAndRecordSettings.PreferProxyMapping)),
|
|
||||||
SaveMapping = parser.GetBoolValue("SaveMapping"),
|
|
||||||
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern"),
|
|
||||||
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
|
||||||
Url = proxyUrl
|
|
||||||
};
|
|
||||||
|
|
||||||
string proxyAddress = parser.GetStringValue("WebProxyAddress");
|
|
||||||
if (!string.IsNullOrEmpty(proxyAddress))
|
|
||||||
{
|
|
||||||
settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
|
|
||||||
{
|
|
||||||
Address = proxyAddress,
|
|
||||||
UserName = parser.GetStringValue("WebProxyUserName"),
|
|
||||||
Password = parser.GetStringValue("WebProxyPassword")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var certificateSettings = new WireMockCertificateSettings
|
|
||||||
{
|
|
||||||
X509StoreName = parser.GetStringValue("X509StoreName"),
|
|
||||||
X509StoreLocation = parser.GetStringValue("X509StoreLocation"),
|
|
||||||
X509StoreThumbprintOrSubjectName = parser.GetStringValue("X509StoreThumbprintOrSubjectName"),
|
|
||||||
X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"),
|
|
||||||
X509CertificatePassword = parser.GetStringValue("X509CertificatePassword")
|
|
||||||
};
|
|
||||||
if (certificateSettings.IsDefined)
|
|
||||||
{
|
|
||||||
settings.CertificateSettings = certificateSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
|
||||||
|
{
|
||||||
|
settings.Logger = new WireMockConsoleLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
||||||
|
{
|
||||||
|
settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
|
||||||
|
if (!string.IsNullOrEmpty(proxyUrl))
|
||||||
|
{
|
||||||
|
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||||
|
{
|
||||||
|
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
|
||||||
|
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
|
||||||
|
ExcludedCookies = parser.GetValues("ExcludedCookies"),
|
||||||
|
ExcludedHeaders = parser.GetValues("ExcludedHeaders"),
|
||||||
|
// PreferProxyMapping = parser.GetBoolValue(nameof(ProxyAndRecordSettings.PreferProxyMapping)),
|
||||||
|
SaveMapping = parser.GetBoolValue("SaveMapping"),
|
||||||
|
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
|
||||||
|
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
||||||
|
Url = proxyUrl
|
||||||
|
};
|
||||||
|
|
||||||
|
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
||||||
|
if (!string.IsNullOrEmpty(proxyAddress))
|
||||||
|
{
|
||||||
|
settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
|
||||||
|
{
|
||||||
|
Address = proxyAddress,
|
||||||
|
UserName = parser.GetStringValue("WebProxyUserName"),
|
||||||
|
Password = parser.GetStringValue("WebProxyPassword")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var certificateSettings = new WireMockCertificateSettings
|
||||||
|
{
|
||||||
|
X509StoreName = parser.GetStringValue("X509StoreName"),
|
||||||
|
X509StoreLocation = parser.GetStringValue("X509StoreLocation"),
|
||||||
|
X509StoreThumbprintOrSubjectName = parser.GetStringValue("X509StoreThumbprintOrSubjectName"),
|
||||||
|
X509CertificateFilePath = parser.GetStringValue("X509CertificateFilePath"),
|
||||||
|
X509CertificatePassword = parser.GetStringValue("X509CertificatePassword")
|
||||||
|
};
|
||||||
|
if (certificateSettings.IsDefined)
|
||||||
|
{
|
||||||
|
settings.CertificateSettings = certificateSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace WireMock.Util
|
|||||||
private const int DefaultWatchInterval = 100;
|
private const int DefaultWatchInterval = 100;
|
||||||
|
|
||||||
// This Dictionary keeps the track of when an event occurred last for a particular file
|
// This Dictionary keeps the track of when an event occurred last for a particular file
|
||||||
private ConcurrentDictionary<string, DateTime> _lastFileEvent;
|
private ConcurrentDictionary<string, DateTime> _lastFileEvent = new();
|
||||||
|
|
||||||
// Watch Interval in Milliseconds
|
// Watch Interval in Milliseconds
|
||||||
private int _interval;
|
private int _interval;
|
||||||
@@ -58,7 +58,7 @@ namespace WireMock.Util
|
|||||||
/// <param name="interval">The interval.</param>
|
/// <param name="interval">The interval.</param>
|
||||||
public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval)
|
public EnhancedFileSystemWatcher(int interval = DefaultWatchInterval)
|
||||||
{
|
{
|
||||||
Guard.Condition(interval, i => i >= 0, nameof(interval));
|
Guard.Condition(interval, i => i >= 0);
|
||||||
|
|
||||||
InitializeMembers(interval);
|
InitializeMembers(interval);
|
||||||
}
|
}
|
||||||
@@ -68,10 +68,10 @@ namespace WireMock.Util
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
||||||
/// <param name="interval">The interval.</param>
|
/// <param name="interval">The interval.</param>
|
||||||
public EnhancedFileSystemWatcher([NotNull] string path, int interval = DefaultWatchInterval) : base(path)
|
public EnhancedFileSystemWatcher(string path, int interval = DefaultWatchInterval) : base(path)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
Guard.Condition(interval, i => i >= 0, nameof(interval));
|
Guard.Condition(interval, i => i >= 0);
|
||||||
|
|
||||||
InitializeMembers(interval);
|
InitializeMembers(interval);
|
||||||
}
|
}
|
||||||
@@ -82,11 +82,11 @@ namespace WireMock.Util
|
|||||||
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
/// <param name="path">The directory to monitor, in standard or Universal Naming Convention (UNC) notation.</param>
|
||||||
/// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
|
/// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
|
||||||
/// <param name="interval">The interval.</param>
|
/// <param name="interval">The interval.</param>
|
||||||
public EnhancedFileSystemWatcher([NotNull] string path, [NotNull] string filter, int interval = DefaultWatchInterval) : base(path, filter)
|
public EnhancedFileSystemWatcher(string path, string filter, int interval = DefaultWatchInterval) : base(path, filter)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
Guard.NotNullOrEmpty(filter, nameof(filter));
|
Guard.NotNullOrEmpty(filter);
|
||||||
Guard.Condition(interval, i => i >= 0, nameof(interval));
|
Guard.Condition(interval, i => i >= 0);
|
||||||
|
|
||||||
InitializeMembers(interval);
|
InitializeMembers(interval);
|
||||||
}
|
}
|
||||||
@@ -100,22 +100,22 @@ namespace WireMock.Util
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is changed.
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new event FileSystemEventHandler Changed;
|
public new event FileSystemEventHandler? Changed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is created.
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new event FileSystemEventHandler Created;
|
public new event FileSystemEventHandler? Created;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is deleted.
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is deleted.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new event FileSystemEventHandler Deleted;
|
public new event FileSystemEventHandler? Deleted;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is renamed.
|
/// Occurs when a file or directory in the specified <see cref="P:System.IO.FileSystemWatcher.Path" /> is renamed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new event RenamedEventHandler Renamed;
|
public new event RenamedEventHandler? Renamed;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Protected Methods to raise the Events for this class
|
#region Protected Methods to raise the Events for this class
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ internal static class HttpStatusRangeParser
|
|||||||
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
/// <param name="pattern">The pattern. (Can be null, in that case it's allowed.)</param>
|
||||||
/// <param name="httpStatusCode">The value.</param>
|
/// <param name="httpStatusCode">The value.</param>
|
||||||
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
/// <exception cref="ArgumentException"><paramref name="pattern"/> is invalid.</exception>
|
||||||
public static bool IsMatch(string pattern, object httpStatusCode)
|
public static bool IsMatch(string pattern, object? httpStatusCode)
|
||||||
{
|
{
|
||||||
switch (httpStatusCode)
|
switch (httpStatusCode)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -87,9 +87,9 @@ internal static class JsonUtils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="json">A System.String that contains JSON.</param>
|
/// <param name="json">A System.String that contains JSON.</param>
|
||||||
/// <returns>A Newtonsoft.Json.Linq.JToken populated from the string that contains JSON.</returns>
|
/// <returns>A Newtonsoft.Json.Linq.JToken populated from the string that contains JSON.</returns>
|
||||||
public static JToken? Parse(string json)
|
public static JToken Parse(string json)
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<JToken>(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone);
|
return JsonConvert.DeserializeObject<JToken>(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -98,9 +98,9 @@ internal static class JsonUtils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="json">A System.String that contains JSON.</param>
|
/// <param name="json">A System.String that contains JSON.</param>
|
||||||
/// <returns>The deserialized object from the JSON string.</returns>
|
/// <returns>The deserialized object from the JSON string.</returns>
|
||||||
public static object? DeserializeObject(string json)
|
public static object DeserializeObject(string json)
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone);
|
return JsonConvert.DeserializeObject(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -109,17 +109,35 @@ internal static class JsonUtils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="json">A System.String that contains JSON.</param>
|
/// <param name="json">A System.String that contains JSON.</param>
|
||||||
/// <returns>The deserialized object from the JSON string.</returns>
|
/// <returns>The deserialized object from the JSON string.</returns>
|
||||||
public static T? DeserializeObject<T>(string json)
|
public static T DeserializeObject<T>(string json)
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<T>(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone);
|
return JsonConvert.DeserializeObject<T>(json, JsonSerializationConstants.JsonDeserializerSettingsWithDateParsingNone)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T? ParseJTokenToObject<T>(object value)
|
public static T? TryDeserializeObject<T>(string json)
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(json);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static T ParseJTokenToObject<T>(object? value)
|
||||||
|
{
|
||||||
|
if (value != null && value.GetType() == typeof(T))
|
||||||
|
{
|
||||||
|
return (T)value;
|
||||||
|
}
|
||||||
|
|
||||||
return value switch
|
return value switch
|
||||||
{
|
{
|
||||||
JToken tokenValue => tokenValue.ToObject<T>(),
|
JToken tokenValue => tokenValue.ToObject<T>()!,
|
||||||
_ => default
|
|
||||||
|
_ => throw new NotSupportedException($"Unable to convert value to {typeof(T)}.")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,36 +1,39 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class PathUtils
|
||||||
{
|
{
|
||||||
internal static class PathUtils
|
/// <summary>
|
||||||
|
/// Robust handling of the user defined path.
|
||||||
|
/// Also supports Unix and Windows platforms
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to clean</param>
|
||||||
|
public static string? CleanPath(string? path)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||||
/// Robust handling of the user defined path.
|
}
|
||||||
/// Also supports Unix and Windows platforms
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="path">The path to clean</param>
|
|
||||||
public static string CleanPath(string path)
|
|
||||||
{
|
|
||||||
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
|
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
|
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
|
||||||
public static string RemoveLeadingDirectorySeparators(string path)
|
public static string? RemoveLeadingDirectorySeparators(string? path)
|
||||||
{
|
{
|
||||||
return path?.TrimStart(new[] { Path.DirectorySeparatorChar });
|
return path?.TrimStart(new[] { Path.DirectorySeparatorChar });
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Combine two paths
|
/// Combine two paths
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="root">The root path</param>
|
/// <param name="root">The root path</param>
|
||||||
/// <param name="path">The path</param>
|
/// <param name="path">The path</param>
|
||||||
public static string Combine(string root, string path)
|
public static string Combine(string root, string? path)
|
||||||
{
|
{
|
||||||
return Path.Combine(root, RemoveLeadingDirectorySeparators(path));
|
Guard.NotNull(root);
|
||||||
}
|
|
||||||
|
var result = RemoveLeadingDirectorySeparators(path);
|
||||||
|
return result == null ? root : Path.Combine(root, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,5 +4,5 @@ namespace WireMock.Util;
|
|||||||
|
|
||||||
internal static class SystemUtils
|
internal static class SystemUtils
|
||||||
{
|
{
|
||||||
public static readonly string Version = typeof(SystemUtils).GetTypeInfo().Assembly.GetName().Version.ToString();
|
public static readonly string Version = typeof(SystemUtils).GetTypeInfo().Assembly.GetName().Version!.ToString();
|
||||||
}
|
}
|
||||||
@@ -140,6 +140,11 @@
|
|||||||
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
|
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||||
|
<!--<PackageReference Include="System.Diagnostics.CodeAnalysis" Version="4.3.0" />-->
|
||||||
|
<PackageReference Include="Nullable" Version="1.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="Server\WireMockServer.*.cs">
|
<Compile Update="Server\WireMockServer.*.cs">
|
||||||
<DependentUpon>WireMockServer.cs</DependentUpon>
|
<DependentUpon>WireMockServer.cs</DependentUpon>
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
@@ -11,7 +15,7 @@ namespace WireMock.Net.Tests.Pact;
|
|||||||
public class PactTests
|
public class PactTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SavePact_Get_Request()
|
public void SavePact_Get_Request_And_Response_WithBodyAsJson()
|
||||||
{
|
{
|
||||||
var server = WireMockServer.Start();
|
var server = WireMockServer.Start();
|
||||||
server
|
server
|
||||||
@@ -41,6 +45,62 @@ public class PactTests
|
|||||||
server.SavePact(Path.Combine("../../../", "Pact", "files"), "pact-get.json");
|
server.SavePact(Path.Combine("../../../", "Pact", "files"), "pact-get.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SavePact_Get_Request_And_Response_WithBody_StringIsJson()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingGet()
|
||||||
|
.WithPath("/tester")
|
||||||
|
)
|
||||||
|
.RespondWith(
|
||||||
|
Response.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
|
.WithBody(@"{ foo: ""bar"" }")
|
||||||
|
);
|
||||||
|
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
server.SavePact(memoryStream);
|
||||||
|
|
||||||
|
var json = Encoding.UTF8.GetString(memoryStream.ToArray());
|
||||||
|
var pact = JsonConvert.DeserializeObject<WireMock.Pact.Models.V2.Pact>(json)!;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
pact.Interactions.Should().HaveCount(1);
|
||||||
|
|
||||||
|
var expectedBody = new JObject { { "foo", "bar" } };
|
||||||
|
pact.Interactions[0].Response.Body.Should().BeEquivalentTo(expectedBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SavePact_Get_Request_And_Response_WithBody_StringIsString()
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
server
|
||||||
|
.Given(Request.Create()
|
||||||
|
.UsingGet()
|
||||||
|
.WithPath("/tester")
|
||||||
|
)
|
||||||
|
.RespondWith(
|
||||||
|
Response.Create()
|
||||||
|
.WithStatusCode(HttpStatusCode.OK)
|
||||||
|
.WithBody("test")
|
||||||
|
);
|
||||||
|
|
||||||
|
var memoryStream = new MemoryStream();
|
||||||
|
server.SavePact(memoryStream);
|
||||||
|
|
||||||
|
var json = Encoding.UTF8.GetString(memoryStream.ToArray());
|
||||||
|
var pact = JsonConvert.DeserializeObject<WireMock.Pact.Models.V2.Pact>(json)!;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
pact.Interactions.Should().HaveCount(1);
|
||||||
|
pact.Interactions[0].Response.Body.Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void SavePact_Multiple_Requests()
|
public void SavePact_Multiple_Requests()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,102 +2,101 @@ using NFluent;
|
|||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Settings
|
namespace WireMock.Net.Tests.Settings;
|
||||||
|
|
||||||
|
public class SimpleCommandLineParserTests
|
||||||
{
|
{
|
||||||
public class SimpleCommandLineParserTests
|
private readonly SimpleCommandLineParser _parser;
|
||||||
|
|
||||||
|
public SimpleCommandLineParserTests()
|
||||||
{
|
{
|
||||||
private readonly SimpleCommandLineParser _parser;
|
_parser = new SimpleCommandLineParser();
|
||||||
|
}
|
||||||
|
|
||||||
public SimpleCommandLineParserTests()
|
[Fact]
|
||||||
{
|
public void SimpleCommandLineParser_Parse_Arguments()
|
||||||
_parser = new SimpleCommandLineParser();
|
{
|
||||||
}
|
// Assign
|
||||||
|
_parser.Parse(new[] { "--test1", "one", "--test2", "two", "--test3", "three" });
|
||||||
|
|
||||||
[Fact]
|
// Act
|
||||||
public void SimpleCommandLineParser_Parse_Arguments()
|
string? value1 = _parser.GetStringValue("test1");
|
||||||
{
|
string? value2 = _parser.GetStringValue("test2");
|
||||||
// Assign
|
string? value3 = _parser.GetStringValue("test3");
|
||||||
_parser.Parse(new[] { "--test1", "one", "--test2", "two", "--test3", "three" });
|
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
string value1 = _parser.GetStringValue("test1");
|
Check.That(value1).IsEqualTo("one");
|
||||||
string value2 = _parser.GetStringValue("test2");
|
Check.That(value2).IsEqualTo("two");
|
||||||
string value3 = _parser.GetStringValue("test3");
|
Check.That(value3).IsEqualTo("three");
|
||||||
|
}
|
||||||
|
|
||||||
// Assert
|
[Fact]
|
||||||
Check.That(value1).IsEqualTo("one");
|
public void SimpleCommandLineParser_Parse_ArgumentsAsCombinedKeyAndValue()
|
||||||
Check.That(value2).IsEqualTo("two");
|
{
|
||||||
Check.That(value3).IsEqualTo("three");
|
// Assign
|
||||||
}
|
_parser.Parse(new[] { "--test1 one", "--test2 two", "--test3 three" });
|
||||||
|
|
||||||
[Fact]
|
// Act
|
||||||
public void SimpleCommandLineParser_Parse_ArgumentsAsCombinedKeyAndValue()
|
string? value1 = _parser.GetStringValue("test1");
|
||||||
{
|
string? value2 = _parser.GetStringValue("test2");
|
||||||
// Assign
|
string? value3 = _parser.GetStringValue("test3");
|
||||||
_parser.Parse(new[] { "--test1 one", "--test2 two", "--test3 three" });
|
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
string value1 = _parser.GetStringValue("test1");
|
Check.That(value1).IsEqualTo("one");
|
||||||
string value2 = _parser.GetStringValue("test2");
|
Check.That(value2).IsEqualTo("two");
|
||||||
string value3 = _parser.GetStringValue("test3");
|
Check.That(value3).IsEqualTo("three");
|
||||||
|
}
|
||||||
|
|
||||||
// Assert
|
[Fact]
|
||||||
Check.That(value1).IsEqualTo("one");
|
public void SimpleCommandLineParser_Parse_ArgumentsMixed()
|
||||||
Check.That(value2).IsEqualTo("two");
|
{
|
||||||
Check.That(value3).IsEqualTo("three");
|
// Assign
|
||||||
}
|
_parser.Parse(new[] { "--test1 one", "--test2", "two", "--test3 three" });
|
||||||
|
|
||||||
[Fact]
|
// Act
|
||||||
public void SimpleCommandLineParser_Parse_ArgumentsMixed()
|
string? value1 = _parser.GetStringValue("test1");
|
||||||
{
|
string? value2 = _parser.GetStringValue("test2");
|
||||||
// Assign
|
string? value3 = _parser.GetStringValue("test3");
|
||||||
_parser.Parse(new[] { "--test1 one", "--test2", "two", "--test3 three" });
|
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
string value1 = _parser.GetStringValue("test1");
|
Check.That(value1).IsEqualTo("one");
|
||||||
string value2 = _parser.GetStringValue("test2");
|
Check.That(value2).IsEqualTo("two");
|
||||||
string value3 = _parser.GetStringValue("test3");
|
Check.That(value3).IsEqualTo("three");
|
||||||
|
}
|
||||||
|
|
||||||
// Assert
|
[Fact]
|
||||||
Check.That(value1).IsEqualTo("one");
|
public void SimpleCommandLineParser_Parse_GetBoolValue()
|
||||||
Check.That(value2).IsEqualTo("two");
|
{
|
||||||
Check.That(value3).IsEqualTo("three");
|
// Assign
|
||||||
}
|
_parser.Parse(new[] { "'--test1", "false'", "--test2 true" });
|
||||||
|
|
||||||
[Fact]
|
// Act
|
||||||
public void SimpleCommandLineParser_Parse_GetBoolValue()
|
bool value1 = _parser.GetBoolValue("test1");
|
||||||
{
|
bool value2 = _parser.GetBoolValue("test2");
|
||||||
// Assign
|
bool value3 = _parser.GetBoolValue("test3", true);
|
||||||
_parser.Parse(new[] { "'--test1", "false'", "--test2 true" });
|
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
bool value1 = _parser.GetBoolValue("test1");
|
Check.That(value1).IsEqualTo(false);
|
||||||
bool value2 = _parser.GetBoolValue("test2");
|
Check.That(value2).IsEqualTo(true);
|
||||||
bool value3 = _parser.GetBoolValue("test3", true);
|
Check.That(value3).IsEqualTo(true);
|
||||||
|
}
|
||||||
|
|
||||||
// Assert
|
[Fact]
|
||||||
Check.That(value1).IsEqualTo(false);
|
public void SimpleCommandLineParser_Parse_GetIntValue()
|
||||||
Check.That(value2).IsEqualTo(true);
|
{
|
||||||
Check.That(value3).IsEqualTo(true);
|
// Assign
|
||||||
}
|
_parser.Parse(new[] { "--test1", "42", "--test2 55" });
|
||||||
|
|
||||||
[Fact]
|
// Act
|
||||||
public void SimpleCommandLineParser_Parse_GetIntValue()
|
int? value1 = _parser.GetIntValue("test1");
|
||||||
{
|
int? value2 = _parser.GetIntValue("test2");
|
||||||
// Assign
|
int? value3 = _parser.GetIntValue("test3", 100);
|
||||||
_parser.Parse(new[] { "--test1", "42", "--test2 55" });
|
int? value4 = _parser.GetIntValue("test4");
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
int? value1 = _parser.GetIntValue("test1");
|
Check.That(value1).IsEqualTo(42);
|
||||||
int? value2 = _parser.GetIntValue("test2");
|
Check.That(value2).IsEqualTo(55);
|
||||||
int? value3 = _parser.GetIntValue("test3", 100);
|
Check.That(value3).IsEqualTo(100);
|
||||||
int? value4 = _parser.GetIntValue("test4");
|
Check.That(value4).IsNull();
|
||||||
|
|
||||||
// Assert
|
|
||||||
Check.That(value1).IsEqualTo(42);
|
|
||||||
Check.That(value2).IsEqualTo(55);
|
|
||||||
Check.That(value3).IsEqualTo(100);
|
|
||||||
Check.That(value4).IsNull();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace WireMock.Net.Tests.Util;
|
|||||||
public class JsonUtilsTests
|
public class JsonUtilsTests
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void JsonUtils_ParseJTokenToObject()
|
public void JsonUtils_ParseJTokenToObject_For_String()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
object value = "test";
|
object value = "test";
|
||||||
@@ -22,7 +22,33 @@ public class JsonUtilsTests
|
|||||||
string result = JsonUtils.ParseJTokenToObject<string>(value);
|
string result = JsonUtils.ParseJTokenToObject<string>(value);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(result).IsEqualTo(default(string));
|
result.Should().Be("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonUtils_ParseJTokenToObject_For_Int()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
object value = 123;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = JsonUtils.ParseJTokenToObject<int>(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Should().Be(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void JsonUtils_ParseJTokenToObject_For_Invalid_Throws()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
object value = "{ }";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
Action action = () => JsonUtils.ParseJTokenToObject<int>(value);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
action.Should().Throw<NotSupportedException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -1,44 +1,43 @@
|
|||||||
using NFluent;
|
using NFluent;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Util
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class PathUtilsTests
|
||||||
{
|
{
|
||||||
public class PathUtilsTests
|
[Theory]
|
||||||
|
[InlineData(@"subdirectory/MyXmlResponse.xml")]
|
||||||
|
[InlineData(@"subdirectory\MyXmlResponse.xml")]
|
||||||
|
public void PathUtils_CleanPath(string path)
|
||||||
{
|
{
|
||||||
[Theory]
|
// Act
|
||||||
[InlineData(@"subdirectory/MyXmlResponse.xml")]
|
var cleanPath = PathUtils.CleanPath(path);
|
||||||
[InlineData(@"subdirectory\MyXmlResponse.xml")]
|
|
||||||
public void PathUtils_CleanPath(string path)
|
|
||||||
{
|
|
||||||
// Act
|
|
||||||
var cleanPath = PathUtils.CleanPath(path);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml");
|
Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(null, null)]
|
[InlineData(null, null)]
|
||||||
[InlineData("", "")]
|
[InlineData("", "")]
|
||||||
[InlineData("a", "a")]
|
[InlineData("a", "a")]
|
||||||
[InlineData(@"/", "")]
|
[InlineData(@"/", "")]
|
||||||
[InlineData(@"//", "")]
|
[InlineData(@"//", "")]
|
||||||
[InlineData(@"//a", "a")]
|
[InlineData(@"//a", "a")]
|
||||||
[InlineData(@"\", "")]
|
[InlineData(@"\", "")]
|
||||||
[InlineData(@"\\", "")]
|
[InlineData(@"\\", "")]
|
||||||
[InlineData(@"\\a", "a")]
|
[InlineData(@"\\a", "a")]
|
||||||
public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected)
|
public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var cleanPath = PathUtils.CleanPath(path);
|
var cleanPath = PathUtils.CleanPath(path);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath);
|
var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(withoutDirectorySeparators).Equals(expected);
|
Check.That(withoutDirectorySeparators).Equals(expected);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,92 +3,92 @@ using NFluent;
|
|||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Util
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class PortUtilsTests
|
||||||
{
|
{
|
||||||
public class PortUtilsTests
|
[Fact]
|
||||||
|
public void PortUtils_TryExtract_InvalidUrl_Returns_False()
|
||||||
{
|
{
|
||||||
[Fact]
|
// Assign
|
||||||
public void PortUtils_TryExtract_InvalidUrl_Returns_False()
|
string url = "test";
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
string url = "test";
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeFalse();
|
result.Should().BeFalse();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
proto.Should().BeNull();
|
proto.Should().BeNull();
|
||||||
host.Should().BeNull();
|
host.Should().BeNull();
|
||||||
port.Should().Be(default(int));
|
port.Should().Be(default(int));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PortUtils_TryExtract_UrlIsMissingPort_Returns_False()
|
public void PortUtils_TryExtract_UrlIsMissingPort_Returns_False()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string url = "http://0.0.0.0";
|
string url = "http://0.0.0.0";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeFalse();
|
result.Should().BeFalse();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
proto.Should().BeNull();
|
proto.Should().BeNull();
|
||||||
host.Should().BeNull();
|
host.Should().BeNull();
|
||||||
port.Should().Be(default(int));
|
port.Should().Be(default(int));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PortUtils_TryExtract_Http_Returns_True()
|
public void PortUtils_TryExtract_Http_Returns_True()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string url = "http://wiremock.net:1234";
|
string url = "http://wiremock.net:1234";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
isHttps.Should().BeFalse();
|
isHttps.Should().BeFalse();
|
||||||
proto.Should().Be("http");
|
proto.Should().Be("http");
|
||||||
host.Should().Be("wiremock.net");
|
host.Should().Be("wiremock.net");
|
||||||
port.Should().Be(1234);
|
port.Should().Be(1234);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PortUtils_TryExtract_Https_Returns_True()
|
public void PortUtils_TryExtract_Https_Returns_True()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string url = "https://wiremock.net:5000";
|
string url = "https://wiremock.net:5000";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().BeTrue();
|
result.Should().BeTrue();
|
||||||
isHttps.Should().BeTrue();
|
isHttps.Should().BeTrue();
|
||||||
proto.Should().Be("https");
|
proto.Should().Be("https");
|
||||||
host.Should().Be("wiremock.net");
|
host.Should().Be("wiremock.net");
|
||||||
port.Should().Be(5000);
|
port.Should().Be(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void PortUtils_TryExtract_Https0_0_0_0_Returns_True()
|
public void PortUtils_TryExtract_Https0_0_0_0_Returns_True()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string url = "https://0.0.0.0:5000";
|
string url = "https://0.0.0.0:5000";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
bool result = PortUtils.TryExtract(url, out bool isHttps, out string proto, out string host, out int port);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Check.That(result).IsTrue();
|
result.Should().BeTrue();
|
||||||
Check.That(proto).IsEqualTo("https");
|
isHttps.Should().BeTrue();
|
||||||
Check.That(host).IsEqualTo("0.0.0.0");
|
proto.Should().Be("https");
|
||||||
Check.That(port).IsEqualTo(5000);
|
host.Should().Be("0.0.0.0");
|
||||||
}
|
port.Should().Be(5000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user