mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-27 03:21:04 +01:00
Create WireMock.Net.MimePart project (#1300)
* Create WireMock.Net.MimePart project * . * REFACTOR * ILRepack * -- * ... * x * x * . * fix * public class MimePartMatcher * shared * min * . * <!--<DelaySign>true</DelaySign>--> * Update README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"help": "https://go.microsoft.com/fwlink/?linkid=866610",
|
||||
"root": true,
|
||||
|
||||
"dependentFileProviders": {
|
||||
"add": {
|
||||
"addedExtension": {},
|
||||
"pathSegment": {
|
||||
"add": {
|
||||
".*": [
|
||||
".cs"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Text.RegularExpressions;
|
||||
using AnyOfTypes;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Models;
|
||||
|
||||
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
|
||||
/// https://github.com/AzureAD/microsoft-identity-web/blob/36fb5f555638787823a89e89c67f17d6a10006ed/tools/CrossPlatformValidator/CrossPlatformValidation/CrossPlatformValidation/RequestValidator.cs#L42
|
||||
/// </summary>
|
||||
internal class AzureADAuthenticationMatcher : IStringMatcher
|
||||
{
|
||||
private const string BearerPrefix = "Bearer ";
|
||||
private static readonly Regex ExtractTenantIdRegex = new(@"https:\/\/(?:sts\.windows\.net|login\.microsoftonline\.com)\/([a-z0-9-]{36}|[a-zA-Z0-9\.]+)/", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||
|
||||
private readonly JwtSecurityTokenHandler _jwtSecurityTokenHandler;
|
||||
private readonly IConfigurationManager<OpenIdConnectConfiguration> _configurationManager;
|
||||
private readonly string _tenant;
|
||||
private readonly string _audience;
|
||||
|
||||
public AzureADAuthenticationMatcher(JwtSecurityTokenHandler jwtSecurityTokenHandler, IConfigurationManager<OpenIdConnectConfiguration> configurationManager, string tenant, string audience)
|
||||
{
|
||||
_jwtSecurityTokenHandler = Guard.NotNull(jwtSecurityTokenHandler);
|
||||
_configurationManager = Guard.NotNull(configurationManager);
|
||||
_audience = Guard.NotNullOrEmpty(audience);
|
||||
_tenant = Guard.NotNullOrEmpty(tenant);
|
||||
}
|
||||
|
||||
public string Name => nameof(AzureADAuthenticationMatcher);
|
||||
|
||||
public MatchBehaviour MatchBehaviour => MatchBehaviour.AcceptOnMatch;
|
||||
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||
}
|
||||
|
||||
public MatchOperator MatchOperator => MatchOperator.Or;
|
||||
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input))
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, WireMockConstants.DefaultRegexTimeout);
|
||||
|
||||
try
|
||||
{
|
||||
var config = _configurationManager.GetConfigurationAsync(default).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
|
||||
var validationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidAudience = _audience,
|
||||
ValidIssuer = config.Issuer,
|
||||
IssuerValidator = (issuer, _, _) =>
|
||||
{
|
||||
if (TryExtractTenantId(issuer, out var extractedTenant) && string.Equals(extractedTenant, _tenant, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return issuer;
|
||||
}
|
||||
|
||||
throw new SecurityTokenInvalidIssuerException($"tenant {extractedTenant} does not match {_tenant}.");
|
||||
},
|
||||
IssuerSigningKeys = config.SigningKeys,
|
||||
ValidateLifetime = true
|
||||
};
|
||||
|
||||
// Throws an Exception as the token is invalid (expired, invalid-formatted, tenant mismatch, etc.)
|
||||
_jwtSecurityTokenHandler.ValidateToken(token, validationParameters, out _);
|
||||
|
||||
return MatchScores.Perfect;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new MatchResult(MatchScores.Mismatch, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetCSharpCodeArguments()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// Handles: https://sts.windows.net/{tenant}/, https://login.microsoftonline.com/{tenant}/ or /v2.0
|
||||
private static bool TryExtractTenantId(string issuer, [NotNullWhen(true)] out string? tenant)
|
||||
{
|
||||
var match = ExtractTenantIdRegex.Match(issuer);
|
||||
|
||||
if (match is { Success: true, Groups.Count: > 1 })
|
||||
{
|
||||
tenant = match.Groups[1].Value;
|
||||
return !string.IsNullOrEmpty(tenant);
|
||||
}
|
||||
|
||||
tenant = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Text;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Authentication;
|
||||
|
||||
internal class BasicAuthenticationMatcher(string username, string password)
|
||||
: RegexMatcher(BuildPattern(username, password))
|
||||
{
|
||||
public override string Name => nameof(BasicAuthenticationMatcher);
|
||||
|
||||
private static string BuildPattern(string username, string password)
|
||||
{
|
||||
return "^(?i)BASIC " + Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + password)) + "$";
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
// 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,18 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if NET451 || NET452 || NET46 || NET451 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0
|
||||
using System.Text.RegularExpressions;
|
||||
using WireMock.Constants;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
|
||||
{
|
||||
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||
return Regex.Replace(text, oldValue, newValue, options, WireMockConstants.DefaultRegexTimeout);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System.Net;
|
||||
|
||||
internal class WebProxy : IWebProxy
|
||||
{
|
||||
private readonly string _proxy;
|
||||
public ICredentials? Credentials { get; set; }
|
||||
|
||||
public WebProxy(string proxy)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
public Uri GetProxy(Uri destination)
|
||||
{
|
||||
return new Uri(_proxy);
|
||||
}
|
||||
|
||||
public bool IsBypassed(Uri host)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
|
||||
namespace WireMock.Constants;
|
||||
|
||||
internal static class WireMockConstants
|
||||
{
|
||||
internal static readonly TimeSpan DefaultRegexTimeout = TimeSpan.FromSeconds(10);
|
||||
|
||||
internal const int AdminPriority = int.MinValue;
|
||||
internal const int MinPriority = -1_000_000;
|
||||
internal const int ProxyPriority = -2_000_000;
|
||||
|
||||
internal const string ContentTypeJson = "application/json";
|
||||
internal const string ContentTypeTextPlain = "text/plain";
|
||||
|
||||
internal const string NoMatchingFound = "No matching mapping found";
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
|
||||
namespace WireMock.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// WireMockException
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
public class WireMockException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
public WireMockException()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public WireMockException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="inner">The inner.</param>
|
||||
public WireMockException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Some extensions for AnyOf.
|
||||
/// </summary>
|
||||
public static class AnyOfExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the pattern.
|
||||
/// </summary>
|
||||
/// <param name="value">AnyOf type</param>
|
||||
/// <returns>string value</returns>
|
||||
public static string GetPattern(this AnyOf<string, StringPattern> value)
|
||||
{
|
||||
return value.IsFirst ? value.First : value.Second.Pattern;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <param name="values">AnyOf types</param>
|
||||
/// <returns>string values</returns>
|
||||
public static string[] GetPatterns(this AnyOf<string, StringPattern>[] values)
|
||||
{
|
||||
return values.Select(GetPattern).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string-patterns to AnyOf patterns.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The string patterns</param>
|
||||
/// <returns>The AnyOf patterns</returns>
|
||||
public static AnyOf<string, StringPattern>[] ToAnyOfPatterns(this IEnumerable<string> patterns)
|
||||
{
|
||||
return patterns.Select(p => p.ToAnyOfPattern()).ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string-pattern to AnyOf pattern.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The string pattern</param>
|
||||
/// <returns>The AnyOf pattern</returns>
|
||||
public static AnyOf<string, StringPattern> ToAnyOfPattern(this string pattern)
|
||||
{
|
||||
return new AnyOf<string, StringPattern>(pattern);
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class DictionaryExtensions
|
||||
{
|
||||
public static bool TryGetStringValue(this IDictionary dictionary, string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
Guard.NotNull(dictionary);
|
||||
|
||||
if (dictionary[key] is string valueIsString)
|
||||
{
|
||||
value = valueIsString;
|
||||
return true;
|
||||
}
|
||||
|
||||
var valueToString = dictionary[key]?.ToString();
|
||||
if (valueToString != null)
|
||||
{
|
||||
value = valueToString;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
public static string GetFullyQualifiedEnumValue<T>(this T enumValue)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (!type.GetTypeInfo().IsEnum)
|
||||
{
|
||||
throw new ArgumentException("T must be an enum");
|
||||
}
|
||||
|
||||
return $"{type.Namespace}.{type.Name}.{enumValue}";
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class ExceptionExtensions
|
||||
{
|
||||
public static Exception? ToException(this Exception[] exceptions)
|
||||
{
|
||||
return exceptions.Length switch
|
||||
{
|
||||
1 => exceptions[0],
|
||||
> 1 => new AggregateException(exceptions),
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
// See https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/
|
||||
public static string GetDeterministicHashCodeAsString(this string str)
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
int hash1 = (5381 << 16) + 5381;
|
||||
int hash2 = hash1;
|
||||
|
||||
for (int i = 0; i < str.Length; i += 2)
|
||||
{
|
||||
hash1 = ((hash1 << 5) + hash1) ^ str[i];
|
||||
if (i == str.Length - 1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
|
||||
}
|
||||
|
||||
int result = hash1 + hash2 * 1566083941;
|
||||
|
||||
return result.ToString(CultureInfo.InvariantCulture).Replace('-', '_');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class TimeSettingsExtensions
|
||||
{
|
||||
public static bool IsValid(this ITimeSettings? settings)
|
||||
{
|
||||
if (settings == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var now = DateTime.Now;
|
||||
var start = settings.Start ?? now;
|
||||
DateTime end;
|
||||
|
||||
if (settings.End != null)
|
||||
{
|
||||
end = settings.End.Value;
|
||||
}
|
||||
else if (settings.TTL != null)
|
||||
{
|
||||
end = start.AddSeconds(settings.TTL.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
end = DateTime.MaxValue;
|
||||
}
|
||||
|
||||
return now >= start && now <= end;
|
||||
}
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using WireMock.Util;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
||||
/// </summary>
|
||||
public class LocalFileSystemHandler : IFileSystemHandler
|
||||
{
|
||||
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
|
||||
private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched");
|
||||
|
||||
private readonly string _rootFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||
/// </summary>
|
||||
public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rootFolder">The root folder.</param>
|
||||
public LocalFileSystemHandler(string rootFolder)
|
||||
{
|
||||
_rootFolder = rootFolder;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
||||
public virtual bool FolderExists(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
||||
public virtual void CreateFolder(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
||||
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
|
||||
public virtual string GetMappingFolder()
|
||||
{
|
||||
return Path.Combine(_rootFolder, AdminMappingsFolder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
||||
public virtual string ReadMappingFile(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile(string, string)"/>
|
||||
public virtual void WriteMappingFile(string path, string text)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
Guard.NotNull(text);
|
||||
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
||||
public virtual byte[] ReadResponseBodyAsFile(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
path = FilePathUtils.CleanPath(path)!;
|
||||
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllBytes(File.Exists(FilePathUtils.Combine(GetMappingFolder(), path)) ? FilePathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||
public virtual string ReadResponseBodyAsString(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
path = FilePathUtils.CleanPath(path)!;
|
||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllText(File.Exists(FilePathUtils.Combine(GetMappingFolder(), path)) ? FilePathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||
public virtual bool FileExists(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
|
||||
return File.Exists(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void WriteFile(string filename, byte[] bytes)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(bytes);
|
||||
|
||||
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void WriteFile(string folder, string filename, byte[] bytes)
|
||||
{
|
||||
Guard.NotNullOrEmpty(folder);
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(bytes);
|
||||
|
||||
File.WriteAllBytes(FilePathUtils.Combine(folder, filename), bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
||||
public virtual void DeleteFile(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
|
||||
File.Delete(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
||||
public virtual byte[] ReadFile(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
|
||||
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
|
||||
public virtual string ReadFileAsString(string filename)
|
||||
{
|
||||
return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename)));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.GetUnmatchedRequestsFolder"/>
|
||||
public virtual string GetUnmatchedRequestsFolder()
|
||||
{
|
||||
return Path.Combine(_rootFolder, UnmatchedRequestsFolder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
||||
public virtual void WriteUnmatchedRequest(string filename, string text)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(text);
|
||||
|
||||
var folder = GetUnmatchedRequestsFolder();
|
||||
if (!FolderExists(folder))
|
||||
{
|
||||
CreateFolder(folder);
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.Combine(folder, filename), text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the path to the MappingFolder.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path.</param>
|
||||
/// <returns>Adjusted path</returns>
|
||||
private string AdjustPathForMappingFolder(string filename)
|
||||
{
|
||||
return Path.Combine(GetMappingFolder(), filename);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
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)
|
||||
{
|
||||
var byteContent = new ByteArrayContent(content);
|
||||
if (contentType != null)
|
||||
{
|
||||
byteContent.Headers.Remove(HttpKnownHeaderNames.ContentType);
|
||||
byteContent.Headers.ContentType = contentType;
|
||||
}
|
||||
|
||||
return byteContent;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using WireMock.HttpsCertificate;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal static class HttpClientBuilder
|
||||
{
|
||||
public static HttpClient Build(HttpClientSettings settings)
|
||||
{
|
||||
#if NETSTANDARD || NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
CheckCertificateRevocationList = false,
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true,
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
};
|
||||
#elif NET46
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true,
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
};
|
||||
#else
|
||||
var handler = new WebRequestHandler
|
||||
{
|
||||
ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true,
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
};
|
||||
#endif
|
||||
|
||||
if (!string.IsNullOrEmpty(settings.ClientX509Certificate2ThumbprintOrSubjectName))
|
||||
{
|
||||
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
|
||||
|
||||
var x509Certificate2 = CertificateLoader.LoadCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName!);
|
||||
handler.ClientCertificates.Add(x509Certificate2);
|
||||
}
|
||||
else if (settings.Certificate != null)
|
||||
{
|
||||
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
|
||||
handler.ClientCertificates.Add(settings.Certificate);
|
||||
}
|
||||
|
||||
handler.AllowAutoRedirect = settings.AllowAutoRedirect == true;
|
||||
|
||||
// If UseCookies enabled, httpClient ignores Cookie header
|
||||
handler.UseCookies = false;
|
||||
|
||||
if (settings.WebProxySettings != null)
|
||||
{
|
||||
handler.UseProxy = true;
|
||||
|
||||
handler.Proxy = new WebProxy(settings.WebProxySettings.Address);
|
||||
if (settings.WebProxySettings.UserName != null && settings.WebProxySettings.Password != null)
|
||||
{
|
||||
handler.Proxy.Credentials = new NetworkCredential(settings.WebProxySettings.UserName, settings.WebProxySettings.Password);
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;
|
||||
#endif
|
||||
|
||||
return HttpClientFactory2.Create(handler);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal static class HttpClientFactory2
|
||||
{
|
||||
public static HttpClient Create(params DelegatingHandler[] handlers)
|
||||
{
|
||||
var handler = CreateHandlerPipeline(new HttpClientHandler(), handlers);
|
||||
return new HttpClient(handler);
|
||||
}
|
||||
|
||||
public static HttpClient Create(HttpMessageHandler innerHandler, params DelegatingHandler[] handlers)
|
||||
{
|
||||
var handler = CreateHandlerPipeline(innerHandler, handlers);
|
||||
return new HttpClient(handler);
|
||||
}
|
||||
|
||||
private static HttpMessageHandler CreateHandlerPipeline(HttpMessageHandler handler, params DelegatingHandler[] delegatingHandlers)
|
||||
{
|
||||
if (delegatingHandlers.Length == 0)
|
||||
{
|
||||
return handler;
|
||||
}
|
||||
|
||||
var next = handler;
|
||||
|
||||
foreach (var delegatingHandler in delegatingHandlers.Reverse())
|
||||
{
|
||||
delegatingHandler.InnerHandler = next;
|
||||
next = delegatingHandler;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
@@ -1,123 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
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
|
||||
{
|
||||
// - https://docs.microsoft.com/en-us/dotnet/api/system.net.webheadercollection.isrestricted
|
||||
// - ContentLength is allowed per #720
|
||||
private static readonly string[] RestrictedResponseHeaders =
|
||||
{
|
||||
Accept,
|
||||
Connection,
|
||||
ContentType,
|
||||
Date, // RFC1123Pattern
|
||||
Expect,
|
||||
Host,
|
||||
IfModifiedSince,
|
||||
Range,
|
||||
Referer,
|
||||
TransferEncoding,
|
||||
UserAgent,
|
||||
ProxyConnection
|
||||
};
|
||||
|
||||
/// <summary>Tests whether the specified HTTP header can be set for the response.</summary>
|
||||
/// <param name="headerName">The header to test.</param>
|
||||
/// <returns>true if the header is restricted; otherwise, false.</returns>
|
||||
public static bool IsRestrictedResponseHeader(string headerName) => RestrictedResponseHeaders.Contains(headerName, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public const string Accept = "Accept";
|
||||
public const string AcceptCharset = "Accept-Charset";
|
||||
public const string AcceptEncoding = "Accept-Encoding";
|
||||
public const string AcceptLanguage = "Accept-Language";
|
||||
public const string AcceptPatch = "Accept-Patch";
|
||||
public const string AcceptRanges = "Accept-Ranges";
|
||||
public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
|
||||
public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
|
||||
public const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
|
||||
public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin";
|
||||
public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers";
|
||||
public const string AccessControlMaxAge = "Access-Control-Max-Age";
|
||||
public const string Age = "Age";
|
||||
public const string Allow = "Allow";
|
||||
public const string AltSvc = "Alt-Svc";
|
||||
public const string Authorization = "Authorization";
|
||||
public const string CacheControl = "Cache-Control";
|
||||
public const string Connection = "Connection";
|
||||
public const string ContentDisposition = "Content-Disposition";
|
||||
public const string ContentEncoding = "Content-Encoding";
|
||||
public const string ContentLanguage = "Content-Language";
|
||||
public const string ContentLength = "Content-Length";
|
||||
public const string ContentLocation = "Content-Location";
|
||||
public const string ContentMD5 = "Content-MD5";
|
||||
public const string ContentRange = "Content-Range";
|
||||
public const string ContentSecurityPolicy = "Content-Security-Policy";
|
||||
public const string ContentType = "Content-Type";
|
||||
public const string Cookie = "Cookie";
|
||||
public const string Cookie2 = "Cookie2";
|
||||
public const string Date = "Date";
|
||||
public const string ETag = "ETag";
|
||||
public const string Expect = "Expect";
|
||||
public const string Expires = "Expires";
|
||||
public const string From = "From";
|
||||
public const string Host = "Host";
|
||||
public const string IfMatch = "If-Match";
|
||||
public const string IfModifiedSince = "If-Modified-Since";
|
||||
public const string IfNoneMatch = "If-None-Match";
|
||||
public const string IfRange = "If-Range";
|
||||
public const string IfUnmodifiedSince = "If-Unmodified-Since";
|
||||
public const string KeepAlive = "Keep-Alive";
|
||||
public const string LastModified = "Last-Modified";
|
||||
public const string Link = "Link";
|
||||
public const string Location = "Location";
|
||||
public const string MaxForwards = "Max-Forwards";
|
||||
public const string Origin = "Origin";
|
||||
public const string P3P = "P3P";
|
||||
public const string Pragma = "Pragma";
|
||||
public const string ProxyAuthenticate = "Proxy-Authenticate";
|
||||
public const string ProxyAuthorization = "Proxy-Authorization";
|
||||
public const string ProxyConnection = "Proxy-Connection";
|
||||
public const string PublicKeyPins = "Public-Key-Pins";
|
||||
public const string Range = "Range";
|
||||
public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched.
|
||||
public const string RetryAfter = "Retry-After";
|
||||
public const string SecWebSocketAccept = "Sec-WebSocket-Accept";
|
||||
public const string SecWebSocketExtensions = "Sec-WebSocket-Extensions";
|
||||
public const string SecWebSocketKey = "Sec-WebSocket-Key";
|
||||
public const string SecWebSocketProtocol = "Sec-WebSocket-Protocol";
|
||||
public const string SecWebSocketVersion = "Sec-WebSocket-Version";
|
||||
public const string Server = "Server";
|
||||
public const string SetCookie = "Set-Cookie";
|
||||
public const string SetCookie2 = "Set-Cookie2";
|
||||
public const string StrictTransportSecurity = "Strict-Transport-Security";
|
||||
public const string TE = "TE";
|
||||
public const string TSV = "TSV";
|
||||
public const string Trailer = "Trailer";
|
||||
public const string TransferEncoding = "Transfer-Encoding";
|
||||
public const string Upgrade = "Upgrade";
|
||||
public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests";
|
||||
public const string UserAgent = "User-Agent";
|
||||
public const string Vary = "Vary";
|
||||
public const string Via = "Via";
|
||||
public const string WWWAuthenticate = "WWW-Authenticate";
|
||||
public const string Warning = "Warning";
|
||||
public const string XAspNetVersion = "X-AspNet-Version";
|
||||
public const string XContentDuration = "X-Content-Duration";
|
||||
public const string XContentTypeOptions = "X-Content-Type-Options";
|
||||
public const string XFrameOptions = "X-Frame-Options";
|
||||
public const string XMSEdgeRef = "X-MSEdge-Ref";
|
||||
public const string XPoweredBy = "X-Powered-By";
|
||||
public const string XRequestID = "X-Request-ID";
|
||||
public const string XUACompatible = "X-UA-Compatible";
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal static class HttpRequestMessageHelper
|
||||
{
|
||||
private static readonly IDictionary<string, bool> ContentLengthHeaderAllowed = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ HttpRequestMethod.HEAD, true }
|
||||
};
|
||||
|
||||
internal static HttpRequestMessage Create(IRequestMessage requestMessage, 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))
|
||||
{
|
||||
var value = requestMessage.Headers[HttpKnownHeaderNames.ContentType].FirstOrDefault();
|
||||
MediaTypeHeaderValue.TryParse(value, out contentType);
|
||||
}
|
||||
|
||||
var bodyData = requestMessage.BodyData;
|
||||
httpRequestMessage.Content = bodyData?.DetectedBodyType switch
|
||||
{
|
||||
BodyType.Bytes => ByteArrayContentHelper.Create(bodyData.BodyAsBytes!, contentType),
|
||||
BodyType.Json => StringContentHelper.Create(JsonConvert.SerializeObject(bodyData.BodyAsJson), contentType),
|
||||
BodyType.String => StringContentHelper.Create(bodyData.BodyAsString!, contentType),
|
||||
BodyType.FormUrlEncoded => StringContentHelper.Create(bodyData.BodyAsString!, contentType),
|
||||
BodyType.MultiPart => StringContentHelper.Create(bodyData.BodyAsString!, contentType),
|
||||
|
||||
_ => httpRequestMessage.Content
|
||||
};
|
||||
|
||||
// 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 };
|
||||
|
||||
var contentLengthHeaderAllowed = ContentLengthHeaderAllowed.TryGetValue(requestMessage.Method, out var allowed) && allowed;
|
||||
if (contentLengthHeaderAllowed)
|
||||
{
|
||||
// Set Content to empty ByteArray to be able to set the Content-Length on the content in case of a HEAD method.
|
||||
httpRequestMessage.Content ??= new ByteArrayContent(EmptyArray<byte>.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
excludeHeaders.Add(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,76 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal static class HttpResponseMessageHelper
|
||||
{
|
||||
public static async Task<ResponseMessage> CreateAsync(
|
||||
HttpResponseMessage httpResponseMessage,
|
||||
Uri requiredUri,
|
||||
Uri originalUri,
|
||||
bool deserializeJson,
|
||||
bool decompressGzipAndDeflate,
|
||||
bool deserializeFormUrlEncoded)
|
||||
{
|
||||
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
|
||||
|
||||
// Set both content and response headers, replacing URLs in values
|
||||
var headers = (httpResponseMessage.Content?.Headers.Union(httpResponseMessage.Headers) ?? Enumerable.Empty<KeyValuePair<string, IEnumerable<string>>>()).ToArray();
|
||||
if (httpResponseMessage.Content != null)
|
||||
{
|
||||
var stream = await httpResponseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
|
||||
IEnumerable<string>? contentTypeHeader = null;
|
||||
if (headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
contentTypeHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
}
|
||||
|
||||
IEnumerable<string>? contentEncodingHeader = null;
|
||||
if (headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
contentEncodingHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)).Value;
|
||||
}
|
||||
|
||||
if (httpResponseMessage.StatusCode != HttpStatusCode.NoContent) // A body is not allowed for 204.
|
||||
{
|
||||
var bodyParserSettings = new BodyParserSettings
|
||||
{
|
||||
Stream = stream,
|
||||
ContentType = contentTypeHeader?.FirstOrDefault(),
|
||||
DeserializeJson = deserializeJson,
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate,
|
||||
DeserializeFormUrlEncoded = deserializeFormUrlEncoded
|
||||
};
|
||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var header in headers)
|
||||
{
|
||||
// If Location header contains absolute redirect URL, and base URL is one that we proxy to,
|
||||
// we need to replace it to original one.
|
||||
if (string.Equals(header.Key, HttpKnownHeaderNames.Location, StringComparison.OrdinalIgnoreCase)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out var absoluteLocationUri)
|
||||
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
|
||||
responseMessage.AddHeader(header.Key, replacedLocationUri.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
responseMessage.AddHeader(header.Key, header.Value.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
return responseMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
|
||||
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(string content, MediaTypeHeaderValue? contentType)
|
||||
{
|
||||
var stringContent = new StringContent(content);
|
||||
stringContent.Headers.ContentType = contentType;
|
||||
return stringContent;
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Transformers.Handlebars;
|
||||
using WireMock.Transformers.Scriban;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal class WebhookSender
|
||||
{
|
||||
private const string ClientIp = "::1";
|
||||
private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond));
|
||||
|
||||
private readonly WireMockServerSettings _settings;
|
||||
|
||||
public WebhookSender(WireMockServerSettings settings)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> SendAsync(
|
||||
HttpClient client,
|
||||
IMapping mapping,
|
||||
IWebhookRequest webhookRequest,
|
||||
IRequestMessage originalRequestMessage,
|
||||
IResponseMessage originalResponseMessage
|
||||
)
|
||||
{
|
||||
Guard.NotNull(client);
|
||||
Guard.NotNull(mapping);
|
||||
Guard.NotNull(webhookRequest);
|
||||
Guard.NotNull(originalRequestMessage);
|
||||
Guard.NotNull(originalResponseMessage);
|
||||
|
||||
IBodyData? bodyData;
|
||||
IDictionary<string, WireMockList<string>>? headers;
|
||||
string requestUrl;
|
||||
if (webhookRequest.UseTransformer == true)
|
||||
{
|
||||
ITransformer transformer;
|
||||
switch (webhookRequest.TransformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
var factoryHandlebars = new HandlebarsContextFactory(_settings);
|
||||
transformer = new Transformer(_settings, factoryHandlebars);
|
||||
break;
|
||||
|
||||
case TransformerType.Scriban:
|
||||
case TransformerType.ScribanDotLiquid:
|
||||
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
|
||||
transformer = new Transformer(_settings, factoryDotLiquid);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
|
||||
}
|
||||
|
||||
bodyData = transformer.TransformBody(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.TransformerReplaceNodeOptions);
|
||||
headers = transformer.TransformHeaders(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Headers);
|
||||
requestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url);
|
||||
|
||||
mapping.Settings.WebhookSettings?.PostTransform(mapping, requestUrl, bodyData, headers);
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyData = webhookRequest.BodyData;
|
||||
headers = webhookRequest.Headers;
|
||||
requestUrl = webhookRequest.Url;
|
||||
}
|
||||
|
||||
// Create RequestMessage
|
||||
var requestMessage = new RequestMessage(
|
||||
new UrlDetails(requestUrl),
|
||||
webhookRequest.Method,
|
||||
ClientIp,
|
||||
bodyData,
|
||||
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
|
||||
)
|
||||
{
|
||||
DateTime = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// Create HttpRequestMessage
|
||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, requestUrl);
|
||||
|
||||
// Delay (if required)
|
||||
if (TryGetDelay(webhookRequest, out var delay))
|
||||
{
|
||||
await Task.Delay(delay.Value).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Call the URL
|
||||
return await client.SendAsync(httpRequestMessage).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static bool TryGetDelay(IWebhookRequest webhookRequest, [NotNullWhen(true)] out int? delay)
|
||||
{
|
||||
delay = webhookRequest.Delay;
|
||||
var minimumDelay = webhookRequest.MinimumRandomDelay;
|
||||
var maximumDelay = webhookRequest.MaximumRandomDelay;
|
||||
|
||||
if (minimumDelay is not null && maximumDelay is not null && maximumDelay >= minimumDelay)
|
||||
{
|
||||
delay = Random.Value!.Next(minimumDelay.Value, maximumDelay.Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return delay is not null;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WireMock.HttpsCertificate;
|
||||
|
||||
internal static class CertificateLoader
|
||||
{
|
||||
private const string ExtensionPem = ".PEM";
|
||||
|
||||
/// <summary>
|
||||
/// Used by the WireMock.Net server
|
||||
/// </summary>
|
||||
public static X509Certificate2 LoadCertificate(
|
||||
string? storeName,
|
||||
string? storeLocation,
|
||||
string? thumbprintOrSubjectName,
|
||||
string? filePath,
|
||||
string? passwordOrKey,
|
||||
string host)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(storeName) && !string.IsNullOrEmpty(storeLocation))
|
||||
{
|
||||
var thumbprintOrSubjectNameOrHost = thumbprintOrSubjectName ?? host;
|
||||
|
||||
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))
|
||||
{
|
||||
if (filePath!.EndsWith(ExtensionPem, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// PEM logic based on: https://www.scottbrady91.com/c-sharp/pem-loading-in-dotnet-core-and-dotnet
|
||||
#if NET5_0_OR_GREATER
|
||||
if (!string.IsNullOrEmpty(passwordOrKey))
|
||||
{
|
||||
var certPem = File.ReadAllText(filePath);
|
||||
var cert = X509Certificate2.CreateFromPem(certPem, passwordOrKey);
|
||||
const string defaultPasswordPem = "WireMock.Net";
|
||||
return new X509Certificate2(cert.Export(X509ContentType.Pfx, defaultPasswordPem), defaultPasswordPem);
|
||||
}
|
||||
return X509Certificate2.CreateFromPemFile(filePath);
|
||||
|
||||
#elif NETCOREAPP3_1
|
||||
var cert = new X509Certificate2(filePath);
|
||||
if (!string.IsNullOrEmpty(passwordOrKey))
|
||||
{
|
||||
var key = System.Security.Cryptography.ECDsa.Create()!;
|
||||
key.ImportECPrivateKey(System.Text.Encoding.UTF8.GetBytes(passwordOrKey), out _);
|
||||
return cert.CopyWithPrivateKey(key);
|
||||
}
|
||||
return cert;
|
||||
#else
|
||||
throw new InvalidOperationException("Loading a PEM Certificate is only supported for .NET Core App 3.1, .NET 5.0 and higher.");
|
||||
#endif
|
||||
}
|
||||
|
||||
return !string.IsNullOrEmpty(passwordOrKey) ? new X509Certificate2(filePath, passwordOrKey) : 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,95 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WireMock.HttpsCertificate;
|
||||
|
||||
/// <summary>
|
||||
/// Only used for NetStandard 1.3
|
||||
/// </summary>
|
||||
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
|
||||
B9AEgglYa48lP16+isiGEVT7zwN3XwaPwPOHZcQ7tRA/DA8LZnZbwU7XhtPObF5bZcHn4engX2An
|
||||
ISFpe2S5XJ7BfHmsGOO7Bxj6C2IcZIPTefvAd9vWE0WUAGN11SLhJ3fB/ZRt3Nys7JCJzywQCkYK
|
||||
dCA35V7WfETCLT6+ArtRU4qsjop2YXyUzcLw3OuumBAoRsazgUKz8rkZJbifkSikbdxs+Hupcf2I
|
||||
NOOuKStKoqouqCO/vmRi8u8g0KQhf2LcQBSqLk6OZ8TQuv07W5tVO2Ky5qCYu6aXBBlHhGSY9fGL
|
||||
vqaYcxMcVJQpXUUL6nSWCoaLdaAAB+Anw1Tpbd47W7ieTK5Yq2IROPQIr1mk8nvFbxoTcBuIQ6oU
|
||||
RiiLX+mb3hYgbTL3LqDmmm9FFI8enJu6pUxP8iKKROtCqhYXhF1i3EwReBzJzpDGZ+y4rJxb0Es4
|
||||
sPVc/TaVPSJCTmgcKzwps7M12uxm8G9Dv3lKgZVmgDRivovCJFxHdCdgCYB08FvNWuFtXO+schsE
|
||||
N0nY2i07A2joaJC18yvoNGZ+ySBTBPOBN+5XbiQs0vsQ4MfLETb2O2dFjwE/tErgo6RWYg2qQNAe
|
||||
DSh2wHzI0YJM3PqaUR0Q9KnjEWc92hsLI34KnuNkNkVk4NEjPOetxeIBcYN7CDD6tTxp42sU++bT
|
||||
o9zyjy1BPS+LuEblxDTSlVPb0dxvkwNBXBi0RXIOWfD15BWcV1Uv972jB6To1XPDIOc+eq7fa5yn
|
||||
HRW/GYdGPmOYKietdw6V3t2Et9cPlw4v08IelucF06Ju2a73QtidtkA89vxjn7qOEVACAXpsiMsM
|
||||
4JCcAzF7jh0U2mskSB14I9HcZh1Sei0J2ZULcXNyuIw9nsWp8vrH04OOoUDe7/UpX7c8+A+tqDUy
|
||||
1W2V1dJDhlwu2SXL5jJFBK4P9p2e+XyHJ+AcYXrHQIKxqoCgvnywT4HnI2bvZ1+lmIR99mp1Cvzj
|
||||
RUgJaqhI7u6WH3i6fmkA92hF8MP5WDYTGwjsHPrCg6Xkqykuvub4osu+gLq5t+JC9rOczgrRNYXg
|
||||
54FMzlCyQxTfgY6IRwPH/xYHKGbViFF+jA4ksLMRjI2XOV4swbI232SxMoQQDNsjx1la2nZM3P5z
|
||||
g7zpmaiyzY44q3kU1viMMR7qM/w1S6nTW0ZkzTXk8Gttor/0JWT1K8KgwK02B6Q9zNwfK60a0cQm
|
||||
SbA/dXkWapuIzQLz+ZUPyG/1EP5KuKNnsp0hfVi8TTgOFceoV9kyIhrTQNI0o5O91dqkyWd/bMl2
|
||||
OnrnRnhka6f839zJKUpWPTfRX9RMJTk/5HVcL/qsHYcJedZyPawJjMU+cxW1ZZjr1Lo6M0fjKuTI
|
||||
Askg0ZKS0FJ60jTKnc1DmldQLtieHh1UTdty+yn1A0rGnrFEyN7if2/d1EduWJaf6bvWzfH+d/36
|
||||
1gDwp4OXi0qWu7c5zByGZi2j2sFo4v0cGgjPZUJvF5z0V8OkE15DiA4xtTSkIjCnEmOhODViwrfc
|
||||
ONvHB+inBW9wLp26qFgNcYrYxP2FC54pPCxO+KJaAJrtfE3A4lkboB7V0xFu23ecOy4n2gho39tg
|
||||
Bxkt1Xj3GCLgeKvcYLzOPytZldXDTtoCcsm5uhCmBHEmPUsnLc7Tt1cFflFtWTOjtyv0Lk8Qu2BU
|
||||
B/hSSgjZRpqE+hzjtghjXdFOT5wKULvtUz5eu+lH7kjGghQbF962vLcRCsr+tMPba6MFqhy5Q+dM
|
||||
NcHc+HIx2WiuRZ6jdCZSUpH3f1+kH6XYj2P3/F0kLBRAMPGKbXpIi9Px4AVDEDClL77mDpVgeEoh
|
||||
kkVh5zvk2PsEPonmTFK0kQE8Q4cYFWTKa9lAE4Wc35EzvpKFdTwQKhr5kN7tEq17n5wJt/499164
|
||||
ho0+LjzYy62JI/fv3RPISL0gXr2INLW7fgZ5KNjcnu/AITJu3ycw/XH8BKsx4dcbBgBdrKY8afEn
|
||||
IZ1EIv/TuNvmifmAEGX/DWuVmZIOU6pTystzTQwfz0wko3lUKjkPM40RLN9o6lddV6fM3QNtK0ac
|
||||
hqOOmG68LzI1U33nvUBol/FeEV0DLvGjvsIRC/TCtDu5Vk2tKS13p9kNj4owJK8d343PZ/eyi/Oe
|
||||
sNZCCJJuj4iIodekm1hzj7zc6ZLNudgab/WkF7TbWDOhDPwG1gE/McffGNWPFlwsaopoZaH8E8tl
|
||||
QSOnHqAiNa7B3ifxMrGDWHDlxkWddClbKd9ujL4mgB88Wo8JceLawDOcSVGImvWGxsrK2RX7FK57
|
||||
GQYuc0zcq3NH4jpoOyS+vpMeigTPDOvQdqjGgEUW3aXdA8Ma0KYVEyAhK/rFXw8FelGREM2ku1kN
|
||||
kziOTAe52SWqcrv6NUPfo1/Uc6u7KpwQrcDlFZWxPCKomeRQTm6AmP2QHMgl2gaUbmuBF/C9Ccl2
|
||||
1Iqh8vUdDT3lHdf5I5SjRPJJXX2/0/oUFmwIC5AFp8/XXnIG05BER1X2Y+SL15QHW7lYsvd4ZKTv
|
||||
tvYaZNWajgAVZl8gJYFUQG+U+cFYcpHhf6SGzcuwcmQJGxptZAVnRtDzNjJ04vJB616/uoI2Qkp2
|
||||
ysGAjDNlwgeckmU7TSYoYaML29pRupTTQqKItyFDuebUpSKBTxsEFIJBCTErA8TD8I9T5nzT+rTy
|
||||
+Lpp9mqcxQR1RhxgTx5bE7D4igdblobX0IONARg4EIAk8xj1Ba3k4skdjAQcJOHKd+xVo+vsrIqg
|
||||
a+ycemROE5F3D3s21ozMOn1Dy8iIeQusJQTSkP82+wHYWXRg59N0cq/40CaJskK+yp7afOWFoCqY
|
||||
T/D9OGlfeIIiLivwMh8naPZyM4+pd/CFZwcWoGtIno04nWWR/xQVe17jqPMov1xonC2E3AcKxUXl
|
||||
PMdZoOARkR+KHZnoBZ/vjqxeDPARKupijkw5QQI3jNmd5IELjcL8OraHlo+e884mRsa/66J7p94D
|
||||
bidzjiVLUhCsZnVks9eZF6PIEg49eq/+w8ph3X7XBNnZYAbgola/0yy/PlPzgJ3p/AgXMoGr+HXO
|
||||
p2WAs1WRo9mAdeOBMrAMvXKFD04bZPNHke0Ri03O5U7NRRs1T0LnqdZHyF39As8FiktfJl1bn/U0
|
||||
sUjhc4fDNnDaBN8VF8VsEa7UolRC6NqQ1oHaeEZRoq0li6NbXIXdxIIT4bbqwajqFkvvO6qc6SEG
|
||||
iAlUzTGB2zATBgkqhkiG9w0BCRUxBgQEAQAAADBXBgkqhkiG9w0BCRQxSh5IADcAOAA1ADAAZABj
|
||||
ADAAYwAtAGIAZQBmAGIALQA0AGEANQBiAC0AOAA5ADcAMAAtAGIAZAA5ADkAOQAxADEAOQA2AGIA
|
||||
YgA1MGsGCSsGAQQBgjcRATFeHlwATQBpAGMAcgBvAHMAbwBmAHQAIABFAG4AaABhAG4AYwBlAGQA
|
||||
IABDAHIAeQBwAHQAbwBnAHIAYQBwAGgAaQBjACAAUAByAG8AdgBpAGQAZQByACAAdgAxAC4AMDCC
|
||||
BUcGCSqGSIb3DQEHBqCCBTgwggU0AgEAMIIFLQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQMwDgQI
|
||||
Amt1zAZpKWkCAgfQgIIFAJoBLiXPEZGuUfsyM2ed66sBIirBctHbhyydZsQDT8V2crq6AI3P0dBT
|
||||
Agf/MouK/JcAfdGEEpU6SKxqZBDZoTRbyK7VdW5YiKGUurFGf66L9K3c4MHhVLMWnSkwK+0gwiIB
|
||||
RB82Or4ru0cSQbF32vsuJgJY9Ax4YODKokPFUzZjPrmch4AgZWKxslDFDq4xs3tpLIeZbALWYdrR
|
||||
PUZaReO+NaLfNKwTZsinzjPkCst7R1Jfjf1abikPrOMPgYFUiQL7GNQFbefeJu9SCXlj09u/qw+l
|
||||
uLrwEyMamG0cgjrWbMOohGtvAuHqeZIIWuLnGE2quq7Ah/U55lliZn3IFgs1n6FalW/XGH0T0aRA
|
||||
im6f1EgxtJShBNdG6qNrkzYMyY2Xvpk82qtmvMbhJMDstpovsdT1rZ6OGfsyJAlG94BcCWC2wuvW
|
||||
MYs6H7AirBG/iocNm7JwSBQ7wjl0keH9vuHDQsI+uu9yWGuyZK6MmxDEOEMZRndU0GrlLIp7zQx8
|
||||
gPeagKqSHhPK0ghHhDilMyToE3Euvi5jneh90eofWK6E4E8KtQTWvFeCe9fYRhMaflH9lwfHfXPo
|
||||
9J+7GZLRF/MDTlE8jzWoP5csUxV3jnXQkTUOfHvO7QK6POKwLGkCyZ5wKFydyYdTekG+KU2Vml2y
|
||||
s6pZ+kME1VMYiHRF3CaXX1ZYKikEdnuB1Qp8BE2uKQTaYO4Wns0vQIVRWTrk/Gp5RXu1ihzoTiFb
|
||||
YqLFROQTM+dVTUk3C6W82OroNwofW5ErZwpbdgJJ83gbPLr9W4KZ6YegvEWFT1MawnIYDC6RSKtg
|
||||
fI1blSZOo1SIF6+nR0gFPivSEGBJHclIg7TtvGSB1q7TQFS4W5l+AHdgGPqqURnjuJ7/DVFZHqEV
|
||||
jJM1QEvH/RY1rDXP8FZC6CZR/Le7tVm5o8bHnlU4gyStvNyLXHCP6o3gHmaBcfYmqJr/0ZEKnwnd
|
||||
MBmdIgwQ3+JnKvjAeDe8Z/l0U03CbzeyN3TgUolDlUGVDF2FdPZqrVsLKw6VJv3hoFZmgEbb1fDq
|
||||
Jblc82lrYIEW8E7ltRLDyNDmRdyaqkqw0e7SygXXxv6SKerxoJ5E1NyJdHeQXSahRrsj5UZpxlTE
|
||||
ylb3upIF0F006w2D5J7xUDktPetVgM97dN3whbOrzWPPkn5CTZMaUdYGTYQK3/K/jQSKuXcaM04A
|
||||
EWmcQOQ/5Quco/aMYWTTwFI9+bKZayZKHqQjNKuwss+iWTW2b26cA2XAr2XWCL9PEybVRK/5n4km
|
||||
5qkGIMjjczZxNy1+H1QoEOwkGVzQd390ktQajJBFcf9wBO+Ar14EgPKvdL4DRvCvXOK3CtrgbAq6
|
||||
GK7uULRrz9t/5lu27ba5wcwxg6bFhgCsJsoSCJnLCR7H+QMB4LhnWA10U4hFWCamCKFoGkiXW6yV
|
||||
OF4x+0D0MmrjrrAGi05KzfrsXNtRG0xbkmvrsjqzmsOKyjvtiBCrR0S6NUtKhyxoiz5bCCm+d8rm
|
||||
jaPk9q01k52pjJAKYW0f+5+r15LamBecnjXtJ07LCl6cMA1Cj4L0mQUSefyFi666GC3TmhzHwhnj
|
||||
SV64nTApS0gBsc6c18fUBsMcUj5nCNclIzfxwnARd/30yg22r09nUY2gtQTwk/W6VCpAH+7yZkH1
|
||||
TLNGa+UmMnPsnBjlAJ6l9VPsa4uJM2DIQKtZXWq4DkhSAYKF6joIP7nKMDswHzAHBgUrDgMCGgQU
|
||||
wTM1Z+CJZG9xAcf1zAVGl4ggYyYEFGBFyJ8VBwijS2zy1qwN1WYGtcWoAgIH0A==
|
||||
";
|
||||
|
||||
public static X509Certificate2 GetX509Certificate2()
|
||||
{
|
||||
byte[] data = Convert.FromBase64String(Data);
|
||||
return new X509Certificate2(data);
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
/// <summary>
|
||||
/// The IMapping interface.
|
||||
/// </summary>
|
||||
public interface IMapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique identifier.
|
||||
/// </summary>
|
||||
Guid Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The datetime when this mapping was created or updated.
|
||||
/// </summary>
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TimeSettings (Start, End and TTL).
|
||||
/// </summary>
|
||||
ITimeSettings? TimeSettings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique title.
|
||||
/// </summary>
|
||||
string? Title { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description.
|
||||
/// </summary>
|
||||
string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The full filename path for this mapping (only defined for static mappings).
|
||||
/// </summary>
|
||||
string? Path { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority. (A low value means higher priority.)
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Scenario.
|
||||
/// </summary>
|
||||
string? Scenario { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Execution state condition for the current mapping.
|
||||
/// </summary>
|
||||
string? ExecutionConditionState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The next state which will be signaled after the current mapping execution.
|
||||
/// In case the value is null, state will not be changed.
|
||||
/// </summary>
|
||||
string? NextState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of times this match should be matched before the state will be changed to the next state.
|
||||
/// </summary>
|
||||
int? StateTimes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The RequestMatcher.
|
||||
/// </summary>
|
||||
IRequestMatcher RequestMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The ResponseProvider.
|
||||
/// </summary>
|
||||
IResponseProvider Provider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The WireMockServerSettings.
|
||||
/// </summary>
|
||||
WireMockServerSettings Settings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Is State started ?
|
||||
/// </summary>
|
||||
bool IsStartState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is an Admin Interface.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is an Admin Interface; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsAdminInterface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping is a Proxy Mapping.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping is a Proxy Mapping; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool IsProxy { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this mapping to be logged.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// <c>true</c> if this mapping to be logged; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
bool LogMapping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Webhooks.
|
||||
/// </summary>
|
||||
IWebhook[]? Webhooks { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
||||
/// </summary>
|
||||
bool? UseWebhooksFireAndForget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
object? Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
double? Probability { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Grpc ProtoDefinition which is used for this mapping (request and response). [Optional]
|
||||
/// </summary>
|
||||
IdOrTexts? ProtoDefinition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ProvideResponseAsync
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
||||
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the RequestMatchResult based on the RequestMessage.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The request message.</param>
|
||||
/// <param name="nextState">The Next State.</param>
|
||||
/// <returns>The <see cref="IRequestMatchResult"/>.</returns>
|
||||
IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState);
|
||||
|
||||
/// <summary>
|
||||
/// Define the scenario.
|
||||
/// </summary>
|
||||
/// <param name="scenario">The scenario.</param>
|
||||
/// <returns>The <see cref="IMapping"/>.</returns>
|
||||
IMapping WithScenario(string scenario);
|
||||
|
||||
/// <summary>
|
||||
/// Define the probability when this request should be matched. [Optional]
|
||||
/// </summary>
|
||||
/// <param name="probability">The probability.</param>
|
||||
/// <returns>The <see cref="IMapping"/>.</returns>
|
||||
IMapping WithProbability(double probability);
|
||||
|
||||
/// <summary>
|
||||
/// Define a Grpc ProtoDefinition which is used for this mapping (request and response).
|
||||
/// </summary>
|
||||
/// <param name="protoDefinition">The proto definitions as id or text.</param>
|
||||
/// <returns>The <see cref="IMapping"/>.</returns>
|
||||
IMapping WithProtoDefinition(IdOrTexts protoDefinition);
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Server;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
/// <summary>
|
||||
/// IMappingBuilder
|
||||
/// </summary>
|
||||
public interface IMappingBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The given.
|
||||
/// </summary>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the mappings as a list.
|
||||
/// </summary>
|
||||
/// <returns>A list from <see cref="MappingModel"/>s.</returns>
|
||||
MappingModel[] GetMappings();
|
||||
|
||||
/// <summary>
|
||||
/// Convert all mappings to JSON.
|
||||
/// </summary>
|
||||
/// <returns>JSON</returns>
|
||||
string ToJson();
|
||||
|
||||
/// <summary>
|
||||
/// Save all mappings as a single JSON to a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to write to.</param>
|
||||
void SaveMappingsToFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Save all mappings as multiple JSON files (each file is 1 mapping).
|
||||
/// </summary>
|
||||
/// <param name="folder">The folder to write the files to.</param>
|
||||
void SaveMappingsToFolder(string folder);
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code for a mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Mapping Guid.</param>
|
||||
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
|
||||
/// <returns>C# code (null in case the mapping is not found)</returns>
|
||||
string? ToCSharpCode(Guid guid, MappingConverterType converterType);
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code for all mappings.
|
||||
/// </summary>
|
||||
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
|
||||
/// <returns>C# code</returns>
|
||||
public string ToCSharpCode(MappingConverterType converterType);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal class DynamicJsonClassOptions
|
||||
{
|
||||
public IntegerBehavior IntegerConvertBehavior { get; set; } = IntegerBehavior.UseLong;
|
||||
|
||||
public FloatBehavior FloatConvertBehavior { get; set; } = FloatBehavior.UseDouble;
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert a Float in the Json Object.
|
||||
/// </summary>
|
||||
internal enum FloatBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a double. (default)
|
||||
/// </summary>
|
||||
UseDouble = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a float (unless overflow).
|
||||
/// </summary>
|
||||
UseFloat = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a decimal (unless overflow).
|
||||
/// </summary>
|
||||
UseDecimal = 2
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Integer in the Json Object.
|
||||
/// </summary>
|
||||
internal enum IntegerBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a int (unless overflow).
|
||||
/// (default)
|
||||
/// </summary>
|
||||
UseInt = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a long.
|
||||
/// </summary>
|
||||
UseLong = 1
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// LogEntry
|
||||
/// </summary>
|
||||
public class LogEntry : ILogEntry
|
||||
{
|
||||
/// <inheritdoc cref="ILogEntry.Guid" />
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.RequestMessage" />
|
||||
public IRequestMessage RequestMessage { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.ResponseMessage" />
|
||||
public IResponseMessage ResponseMessage { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.RequestMatchResult" />
|
||||
public IRequestMatchResult RequestMatchResult { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.MappingGuid" />
|
||||
public Guid? MappingGuid { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.MappingTitle" />
|
||||
public string? MappingTitle { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.PartialMappingGuid" />
|
||||
public Guid? PartialMappingGuid { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.PartialMappingTitle" />
|
||||
public string? PartialMappingTitle { get; set; }
|
||||
|
||||
/// <inheritdoc cref="ILogEntry.PartialMatchResult" />
|
||||
public IRequestMatchResult PartialMatchResult { get; set; } = null!;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using WireMock.Admin.Requests;
|
||||
|
||||
namespace WireMock.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// WireMockConsoleLogger which logs to Console
|
||||
/// </summary>
|
||||
/// <seealso cref="IWireMockLogger" />
|
||||
public class WireMockConsoleLogger : IWireMockLogger
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
|
||||
/// </summary>
|
||||
public WireMockConsoleLogger()
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignored
|
||||
}
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Debug"/>
|
||||
public void Debug(string formatString, params object[] args)
|
||||
{
|
||||
Console.WriteLine(Format("Debug", formatString, args));
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Info"/>
|
||||
public void Info(string formatString, params object[] args)
|
||||
{
|
||||
Console.WriteLine(Format("Info", formatString, args));
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Warn"/>
|
||||
public void Warn(string formatString, params object[] args)
|
||||
{
|
||||
Console.WriteLine(Format("Warn", formatString, args));
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||
public void Error(string formatString, params object[] args)
|
||||
{
|
||||
Console.WriteLine(Format("Error", formatString, args));
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
Console.WriteLine(Format("Error", $"{message} {{0}}", exception));
|
||||
|
||||
if (exception is AggregateException ae)
|
||||
{
|
||||
ae.Handle(ex =>
|
||||
{
|
||||
Console.WriteLine(Format("Error", "Exception {0}", ex));
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||
{
|
||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||
}
|
||||
|
||||
private static string Format(string level, string formatString, params object[] args)
|
||||
{
|
||||
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
||||
|
||||
return $"{DateTime.UtcNow} [{level}] : {message}";
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Admin.Requests;
|
||||
|
||||
namespace WireMock.Logging;
|
||||
|
||||
/// <summary>
|
||||
/// WireMockNullLogger which does not log.
|
||||
/// </summary>
|
||||
/// <seealso cref="IWireMockLogger" />
|
||||
public class WireMockNullLogger : IWireMockLogger
|
||||
{
|
||||
/// <see cref="IWireMockLogger.Debug"/>
|
||||
public void Debug(string formatString, params object[] args)
|
||||
{
|
||||
// Log nothing
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Info"/>
|
||||
public void Info(string formatString, params object[] args)
|
||||
{
|
||||
// Log nothing
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Warn"/>
|
||||
public void Warn(string formatString, params object[] args)
|
||||
{
|
||||
// Log nothing
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||
public void Error(string formatString, params object[] args)
|
||||
{
|
||||
// Log nothing
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
// Log nothing
|
||||
}
|
||||
|
||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||
{
|
||||
// Log nothing
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
using WireMock.ResponseProviders;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
/// <summary>
|
||||
/// The Mapping.
|
||||
/// </summary>
|
||||
public class Mapping : IMapping
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Guid Guid { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Title { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Description { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Path { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int Priority { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Scenario { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? ExecutionConditionState { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? NextState { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? StateTimes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestMatcher RequestMatcher { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseProvider Provider { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public WireMockServerSettings Settings { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsAdminInterface => Provider is DynamicResponseProvider or DynamicAsyncResponseProvider or ProxyAsyncResponseProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsProxy => Provider is ProxyAsyncResponseProvider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
|
||||
|
||||
/// <inheritdoc />
|
||||
public IWebhook[]? Webhooks { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? UseWebhooksFireAndForget { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITimeSettings? TimeSettings { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? Data { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public double? Probability { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IdOrTexts? ProtoDefinition { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <param name="updatedAt">The datetime when this mapping was created.</param>
|
||||
/// <param name="title">The unique title (can be null).</param>
|
||||
/// <param name="description">The description (can be null).</param>
|
||||
/// <param name="path">The full file path from this mapping title (can be null).</param>
|
||||
/// <param name="settings">The WireMockServerSettings.</param>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <param name="priority">The priority for this mapping.</param>
|
||||
/// <param name="scenario">The scenario. [Optional]</param>
|
||||
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
|
||||
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
|
||||
/// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
|
||||
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||
/// <param name="data">The data object. [Optional]</param>
|
||||
public Mapping
|
||||
(
|
||||
Guid guid,
|
||||
DateTime updatedAt,
|
||||
string? title,
|
||||
string? description,
|
||||
string? path,
|
||||
WireMockServerSettings settings,
|
||||
IRequestMatcher requestMatcher,
|
||||
IResponseProvider provider,
|
||||
int priority,
|
||||
string? scenario,
|
||||
string? executionConditionState,
|
||||
string? nextState,
|
||||
int? stateTimes,
|
||||
IWebhook[]? webhooks,
|
||||
bool? useWebhooksFireAndForget,
|
||||
ITimeSettings? timeSettings,
|
||||
object? data
|
||||
)
|
||||
{
|
||||
Guid = guid;
|
||||
UpdatedAt = updatedAt;
|
||||
Title = title;
|
||||
Description = description;
|
||||
Path = path;
|
||||
Settings = settings;
|
||||
RequestMatcher = requestMatcher;
|
||||
Provider = provider;
|
||||
Priority = priority;
|
||||
Scenario = scenario;
|
||||
ExecutionConditionState = executionConditionState;
|
||||
NextState = nextState;
|
||||
StateTimes = stateTimes;
|
||||
Webhooks = webhooks;
|
||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||
TimeSettings = timeSettings;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
|
||||
{
|
||||
return Provider.ProvideResponseAsync(this, requestMessage, Settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
|
||||
{
|
||||
var result = new RequestMatchResult();
|
||||
|
||||
RequestMatcher.GetMatchingScore(requestMessage, result);
|
||||
|
||||
// Only check state if Scenario is defined
|
||||
if (Scenario != null)
|
||||
{
|
||||
var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
|
||||
matcher.GetMatchingScore(requestMessage, result);
|
||||
//// If ExecutionConditionState is null, this means that request is the start from a scenario. So just return.
|
||||
//if (ExecutionConditionState != null)
|
||||
//{
|
||||
// // ExecutionConditionState is not null, so get score for matching with the nextState.
|
||||
// var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
|
||||
// matcher.GetMatchingScore(requestMessage, result);
|
||||
//}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMapping WithProbability(double probability)
|
||||
{
|
||||
Probability = Guard.NotNull(probability);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMapping WithScenario(string scenario)
|
||||
{
|
||||
Scenario = Guard.NotNullOrWhiteSpace(scenario);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IMapping WithProtoDefinition(IdOrTexts protoDefinition)
|
||||
{
|
||||
ProtoDefinition = protoDefinition;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Owin;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
/// <summary>
|
||||
/// MappingBuilder
|
||||
/// </summary>
|
||||
public class MappingBuilder : IMappingBuilder
|
||||
{
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly MappingConverter _mappingConverter;
|
||||
private readonly MappingToFileSaver _mappingToFileSaver;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
|
||||
/// <summary>
|
||||
/// Create a MappingBuilder
|
||||
/// </summary>
|
||||
/// <param name="settings">The optional <see cref="WireMockServerSettings"/>.</param>
|
||||
public MappingBuilder(WireMockServerSettings? settings = null)
|
||||
{
|
||||
_settings = settings ?? new WireMockServerSettings();
|
||||
_options = WireMockMiddlewareOptionsHelper.InitFromSettings(_settings);
|
||||
|
||||
var matcherMapper = new MatcherMapper(_settings);
|
||||
_mappingConverter = new MappingConverter(matcherMapper);
|
||||
_mappingToFileSaver = new MappingToFileSaver(_settings, _mappingConverter);
|
||||
|
||||
_guidUtils = new GuidUtils();
|
||||
_dateTimeUtils = new DateTimeUtils();
|
||||
}
|
||||
|
||||
internal MappingBuilder(
|
||||
WireMockServerSettings settings,
|
||||
IWireMockMiddlewareOptions options,
|
||||
MappingConverter mappingConverter,
|
||||
MappingToFileSaver mappingToFileSaver,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
_options = Guard.NotNull(options);
|
||||
_mappingConverter = Guard.NotNull(mappingConverter);
|
||||
_mappingToFileSaver = Guard.NotNull(mappingToFileSaver);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MappingModel[] GetMappings()
|
||||
{
|
||||
return GetNonAdminMappings().Select(_mappingConverter.ToMappingModel).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ToJson()
|
||||
{
|
||||
return ToJson(GetMappings());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
|
||||
{
|
||||
var mapping = GetNonAdminMappings().FirstOrDefault(m => m.Guid == guid);
|
||||
if (mapping is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var settings = new MappingConverterSettings { AddStart = true, ConverterType = converterType };
|
||||
return _mappingConverter.ToCSharpCode(mapping, settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ToCSharpCode(MappingConverterType converterType)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
bool addStart = true;
|
||||
foreach (var mapping in GetNonAdminMappings())
|
||||
{
|
||||
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
||||
|
||||
if (addStart)
|
||||
{
|
||||
addStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveMappingsToFile(string path)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingsToFile(GetNonAdminMappings(), path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveMappingsToFolder(string? folder)
|
||||
{
|
||||
foreach (var mapping in GetNonAdminMappings())
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
||||
}
|
||||
}
|
||||
|
||||
private IMapping[] GetNonAdminMappings()
|
||||
{
|
||||
return _options.Mappings.Values
|
||||
.Where(m => !m.IsAdminInterface)
|
||||
.OrderBy(m => m.Guid)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
||||
{
|
||||
// Check a mapping exists with the same Guid. If so, update the datetime and replace it.
|
||||
if (_options.Mappings.ContainsKey(mapping.Guid))
|
||||
{
|
||||
mapping.UpdatedAt = _dateTimeUtils.UtcNow;
|
||||
_options.Mappings[mapping.Guid] = mapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
_options.Mappings.TryAdd(mapping.Guid, mapping);
|
||||
}
|
||||
|
||||
if (saveToFile)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping);
|
||||
}
|
||||
|
||||
// Link this mapping to the Request
|
||||
((Request)mapping.RequestMatcher).Mapping = mapping;
|
||||
|
||||
// Link this mapping to the Response
|
||||
if (mapping.Provider is Response response)
|
||||
{
|
||||
response.Mapping = mapping;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToJson(object value)
|
||||
{
|
||||
return JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock
|
||||
{
|
||||
/// <summary>
|
||||
/// The registration callback.
|
||||
/// </summary>
|
||||
/// <param name="mapping">The mapping.</param>
|
||||
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
|
||||
public delegate void RegistrationCallback(IMapping mapping, bool saveToFile = false);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Generic AbstractJsonPartialMatcher
|
||||
/// </summary>
|
||||
public abstract class AbstractJsonPartialMatcher : JsonMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractJsonPartialMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
protected AbstractJsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false) :
|
||||
base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractJsonPartialMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The object value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
protected AbstractJsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false) :
|
||||
base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AbstractJsonPartialMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
protected AbstractJsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false) :
|
||||
base(matchBehaviour, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsMatch(JToken value, JToken? input)
|
||||
{
|
||||
if (value == input)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Regex && value.Type == JTokenType.String && input != null)
|
||||
{
|
||||
var valueAsString = value.ToString();
|
||||
|
||||
var (valid, result) = RegexUtils.MatchRegex(valueAsString, input.ToString());
|
||||
if (valid)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (input != null &&
|
||||
((value.Type == JTokenType.Guid && input.Type == JTokenType.String) ||
|
||||
(value.Type == JTokenType.String && input.Type == JTokenType.Guid)))
|
||||
{
|
||||
return IsMatch(value.ToString().ToUpperInvariant(), input.ToString().ToUpperInvariant());
|
||||
}
|
||||
|
||||
if (input == null || value.Type != input.Type)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (value.Type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
var nestedValues = value.ToObject<Dictionary<string, JToken>>();
|
||||
return nestedValues?.Any() != true ||
|
||||
nestedValues.All(pair => IsMatch(pair.Value, input.SelectToken(pair.Key) ?? input[pair.Key])); // First try to select based on JPath expression, else just get the value.
|
||||
|
||||
case JTokenType.Array:
|
||||
var valuesArray = value.ToObject<JToken[]>();
|
||||
var tokenArray = input.ToObject<JToken[]>();
|
||||
|
||||
if (valuesArray?.Any() != true)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return tokenArray?.Any() == true &&
|
||||
valuesArray.All(subFilter => tokenArray.Any(subToken => IsMatch(subFilter, subToken)));
|
||||
|
||||
default:
|
||||
return IsMatch(value.ToString(), input.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if two strings are a match (matching can be done exact or wildcard)
|
||||
/// </summary>
|
||||
protected abstract bool IsMatch(string value, string input);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Net.Http.Headers;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// ContentTypeMatcher which accepts also all charsets
|
||||
/// </summary>
|
||||
/// <seealso cref="RegexMatcher" />
|
||||
public class ContentTypeMatcher : WildcardMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
||||
public ContentTypeMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this([pattern], ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
||||
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour,
|
||||
[pattern], ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
||||
public ContentTypeMatcher(AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase (default false)</param>
|
||||
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : base(matchBehaviour, patterns, ignoreCase)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override MatchResult IsMatch(string? input)
|
||||
{
|
||||
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
return base.IsMatch(contentType.MediaType);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(ContentTypeMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// ExactMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IStringMatcher" /> and <seealso cref="IIgnoreCaseMatcher" />
|
||||
public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _values;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The string value.</param>
|
||||
public ExactMatcher(MatchBehaviour matchBehaviour, string value) : this(matchBehaviour, true, MatchOperator.Or, new AnyOf<string, StringPattern>(value))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
|
||||
/// <param name="values">The values.</param>
|
||||
public ExactMatcher(bool ignoreCase, params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, ignoreCase, MatchOperator.Or, values)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
/// <param name="values">The values.</param>
|
||||
public ExactMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or,
|
||||
params AnyOf<string, StringPattern>[] values)
|
||||
{
|
||||
_values = Guard.NotNull(values);
|
||||
|
||||
MatchBehaviour = matchBehaviour;
|
||||
IgnoreCase = ignoreCase;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
Func<string?, bool> equals = IgnoreCase
|
||||
? pattern => string.Equals(pattern, input, StringComparison.OrdinalIgnoreCase)
|
||||
: pattern => pattern == input;
|
||||
|
||||
var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _values;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(ExactMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_values)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// ExactObjectMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IObjectMatcher" />
|
||||
public class ExactObjectMatcher : IObjectMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(object value) : this(MatchBehaviour.AcceptOnMatch, value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(MatchBehaviour matchBehaviour, object value)
|
||||
{
|
||||
Value = Guard.NotNull(value);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(byte[] value) : this(MatchBehaviour.AcceptOnMatch, value)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ExactObjectMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
public ExactObjectMatcher(MatchBehaviour matchBehaviour, byte[] value)
|
||||
{
|
||||
Value = Guard.NotNull(value);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
bool equals;
|
||||
if (Value is byte[] valueAsBytes && input is byte[] inputAsBytes)
|
||||
{
|
||||
equals = valueAsBytes.SequenceEqual(inputAsBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
equals = Equals(Value, input);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(equals));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(ExactObjectMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return "NotImplemented";
|
||||
}
|
||||
}
|
||||
@@ -1,179 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// FormUrl Encoded fields Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
||||
public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(matchBehaviour, [pattern], ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
public FormUrlEncodedMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
IgnoreCase = ignoreCase;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
|
||||
foreach (var pattern in _patterns)
|
||||
{
|
||||
if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection))
|
||||
{
|
||||
foreach (var nameValue in nameValueCollection)
|
||||
{
|
||||
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
|
||||
var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
|
||||
_pairs.Add((keyMatcher, valueMatcher));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
// Input is null or empty and if no patterns defined, return Perfect match.
|
||||
if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
|
||||
{
|
||||
return new MatchResult(MatchScores.Perfect);
|
||||
}
|
||||
|
||||
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
|
||||
{
|
||||
return new MatchResult(MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
var matches = GetMatches(inputNameValueCollection);
|
||||
|
||||
var score = MatchScores.ToScore(matches, MatchOperator);
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
|
||||
}
|
||||
|
||||
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
|
||||
{
|
||||
var matches = new List<bool>();
|
||||
if (_pairs.Count > inputNameValueCollection.Count)
|
||||
{
|
||||
matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count));
|
||||
}
|
||||
|
||||
foreach (var inputKeyValuePair in inputNameValueCollection)
|
||||
{
|
||||
var match = false;
|
||||
foreach (var pair in _pairs)
|
||||
{
|
||||
var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
|
||||
if (keyMatchResult)
|
||||
{
|
||||
match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
|
||||
if (match)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
matches.Add(match);
|
||||
}
|
||||
|
||||
return matches.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(FormUrlEncodedMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,213 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if GRAPHQL
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using GraphQL;
|
||||
using GraphQL.Types;
|
||||
using GraphQLParser;
|
||||
using GraphQLParser.AST;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Exceptions;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers.Models;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// GrapQLMatcher Schema Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
public class GraphQLMatcher : IStringMatcher
|
||||
{
|
||||
private sealed class GraphQLRequest
|
||||
{
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
public string? Query { get; set; }
|
||||
|
||||
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||
public Dictionary<string, object?>? Variables { get; set; }
|
||||
}
|
||||
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
private readonly ISchema _schema;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// An optional dictionary defining the custom Scalar and the type.
|
||||
/// </summary>
|
||||
public IDictionary<string, Type>? CustomScalars { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GraphQLMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="schema">The schema.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public GraphQLMatcher(
|
||||
AnyOf<string, StringPattern, ISchema> schema,
|
||||
MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch,
|
||||
MatchOperator matchOperator = MatchOperator.Or
|
||||
) : this(schema, null, matchBehaviour, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GraphQLMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="schema">The schema.</param>
|
||||
/// <param name="customScalars">A dictionary defining the custom scalars used in this schema. (optional)</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public GraphQLMatcher(
|
||||
AnyOf<string, StringPattern, ISchema> schema,
|
||||
IDictionary<string, Type>? customScalars,
|
||||
MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch,
|
||||
MatchOperator matchOperator = MatchOperator.Or
|
||||
)
|
||||
{
|
||||
Guard.NotNull(schema);
|
||||
CustomScalars = customScalars;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
|
||||
var patterns = new List<AnyOf<string, StringPattern>>();
|
||||
switch (schema.CurrentType)
|
||||
{
|
||||
case AnyOfType.First:
|
||||
patterns.Add(schema.First);
|
||||
_schema = BuildSchema(schema);
|
||||
break;
|
||||
|
||||
case AnyOfType.Second:
|
||||
patterns.Add(schema.Second);
|
||||
_schema = BuildSchema(schema.Second.Pattern);
|
||||
break;
|
||||
|
||||
case AnyOfType.Third:
|
||||
_schema = schema.Third;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
_patterns = patterns.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (input != null && TryGetGraphQLRequest(input, out var graphQLRequest))
|
||||
{
|
||||
try
|
||||
{
|
||||
var executionResult = new DocumentExecuter().ExecuteAsync(eo =>
|
||||
{
|
||||
eo.ThrowOnUnhandledException = true;
|
||||
|
||||
eo.Schema = _schema;
|
||||
eo.Query = graphQLRequest.Query;
|
||||
|
||||
if (graphQLRequest.Variables != null)
|
||||
{
|
||||
eo.Variables = new Inputs(graphQLRequest.Variables);
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
|
||||
if (executionResult.Errors == null || executionResult.Errors.Count == 0)
|
||||
{
|
||||
score = MatchScores.Perfect;
|
||||
}
|
||||
else
|
||||
{
|
||||
exception = executionResult.Errors.OfType<Exception>().ToArray().ToException();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(GraphQLMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return "NotImplemented";
|
||||
}
|
||||
|
||||
private static bool TryGetGraphQLRequest(string input, [NotNullWhen(true)] out GraphQLRequest? graphQLRequest)
|
||||
{
|
||||
try
|
||||
{
|
||||
graphQLRequest = JsonConvert.DeserializeObject<GraphQLRequest>(input);
|
||||
return graphQLRequest != null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
graphQLRequest = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <param name="typeDefinitions">A textual description of the schema in SDL (Schema Definition Language) format.</param>
|
||||
private ISchema BuildSchema(string typeDefinitions)
|
||||
{
|
||||
var schema = Schema.For(typeDefinitions);
|
||||
|
||||
// #984
|
||||
var graphTypes = schema.BuiltInTypeMappings.Select(tm => tm.graphType).ToArray();
|
||||
schema.RegisterTypes(graphTypes);
|
||||
|
||||
var doc = Parser.Parse(typeDefinitions);
|
||||
var scalarTypeDefinitions = doc.Definitions
|
||||
.Where(d => d.Kind == ASTNodeKind.ScalarTypeDefinition)
|
||||
.OfType<GraphQLTypeDefinition>();
|
||||
|
||||
foreach (var scalarTypeDefinitionName in scalarTypeDefinitions.Select(s => s.Name.StringValue))
|
||||
{
|
||||
var customScalarGraphTypeName = $"{scalarTypeDefinitionName}GraphType";
|
||||
if (graphTypes.All(t => t.Name != customScalarGraphTypeName)) // Only process when not built-in.
|
||||
{
|
||||
// Check if this custom Scalar is defined in the dictionary
|
||||
if (CustomScalars == null || !CustomScalars.TryGetValue(scalarTypeDefinitionName, out var clrType))
|
||||
{
|
||||
throw new WireMockException($"The GraphQL Scalar type '{scalarTypeDefinitionName}' is not defined in the CustomScalars dictionary.");
|
||||
}
|
||||
|
||||
// Create a this custom Scalar GraphType (extending the WireMockCustomScalarGraphType<{clrType}> class)
|
||||
var customScalarGraphType = ReflectionUtils.CreateGenericType(customScalarGraphTypeName, typeof(WireMockCustomScalarGraphType<>), clrType);
|
||||
schema.RegisterType(customScalarGraphType);
|
||||
}
|
||||
}
|
||||
|
||||
return schema;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers.Helpers;
|
||||
|
||||
internal static class BodyDataMatchScoreCalculator
|
||||
{
|
||||
public static MatchResult CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
|
||||
{
|
||||
Guard.NotNull(matcher);
|
||||
|
||||
if (requestMessage == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
|
||||
{
|
||||
switch (requestMessage.DetectedBodyType)
|
||||
{
|
||||
case BodyType.Json:
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsString);
|
||||
|
||||
case BodyType.Bytes:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
|
||||
default:
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
if (matcher is ExactObjectMatcher exactObjectMatcher)
|
||||
{
|
||||
// If the body is a byte array, try to match.
|
||||
var detectedBodyType = requestMessage.DetectedBodyType;
|
||||
if (detectedBodyType is BodyType.Bytes or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return exactObjectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the matcher is a IObjectMatcher
|
||||
if (matcher is IObjectMatcher objectMatcher)
|
||||
{
|
||||
// If the body is a JSON object, try to match.
|
||||
if (requestMessage.DetectedBodyType == BodyType.Json)
|
||||
{
|
||||
return objectMatcher.IsMatch(requestMessage.BodyAsJson);
|
||||
}
|
||||
|
||||
// If the body is a byte array, try to match.
|
||||
if (requestMessage.DetectedBodyType == BodyType.Bytes)
|
||||
{
|
||||
return objectMatcher.IsMatch(requestMessage.BodyAsBytes);
|
||||
}
|
||||
}
|
||||
|
||||
// In case the matcher is a IStringMatcher and If body is a Json or a String, use the BodyAsString to match on.
|
||||
if (matcher is IStringMatcher stringMatcher && requestMessage.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyAsString);
|
||||
}
|
||||
|
||||
// In case the matcher is a IProtoBufMatcher, use the BodyAsBytes to match on.
|
||||
if (matcher is IProtoBufMatcher protoBufMatcher)
|
||||
{
|
||||
return protoBufMatcher.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IBytesMatcher
|
||||
/// </summary>
|
||||
public interface IBytesMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input byte array.</param>
|
||||
/// <param name="cancellationToken">The CancellationToken [optional].</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// CSharpCode / CS-Script Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IObjectMatcher"/>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
public interface ICSharpCodeMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IDecodeBytesMatcher
|
||||
/// </summary>
|
||||
public interface IDecodeBytesMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Decode byte array to an object.
|
||||
/// </summary>
|
||||
/// <param name="input">The byte array</param>
|
||||
/// <param name="cancellationToken">The CancellationToken [optional].</param>
|
||||
/// <returns>object</returns>
|
||||
Task<object?> DecodeAsync(byte[]? input, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IIgnoreCaseMatcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IMatcher"/>
|
||||
public interface IIgnoreCaseMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Ignore the case from the pattern.
|
||||
/// </summary>
|
||||
bool IgnoreCase { get; }
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IJsonMatcher
|
||||
/// <seealso cref="IObjectMatcher"/> and <seealso cref="IIgnoreCaseMatcher"/>.
|
||||
/// </summary>
|
||||
public interface IJsonMatcher : IObjectMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IMatcher
|
||||
/// </summary>
|
||||
public interface IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the name.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the match behaviour.
|
||||
/// </summary>
|
||||
MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code arguments.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string GetCSharpCodeArguments();
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IObjectMatcher
|
||||
/// </summary>
|
||||
public interface IObjectMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the value (can be a string or an object).
|
||||
/// </summary>
|
||||
/// <returns>Value</returns>
|
||||
object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
MatchResult IsMatch(object? input);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IProtoBufMatcher
|
||||
/// </summary>
|
||||
public interface IProtoBufMatcher : IDecodeBytesMatcher, IBytesMatcher
|
||||
{
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using AnyOfTypes;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// IStringMatcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IMatcher"/>
|
||||
public interface IStringMatcher : IMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether the specified input is match.
|
||||
/// </summary>
|
||||
/// <param name="input">The input.</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
MatchResult IsMatch(string? input);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patterns.
|
||||
/// </summary>
|
||||
/// <returns>Patterns</returns>
|
||||
AnyOf<string, StringPattern>[] GetPatterns();
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Matchers.MatchOperator"/>.
|
||||
/// </summary>
|
||||
MatchOperator MatchOperator { get; }
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// JsonPathMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IStringMatcher" />
|
||||
/// <seealso cref="IObjectMatcher" />
|
||||
public class JsonPathMatcher : IStringMatcher, IObjectMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public JsonPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or,
|
||||
patterns.ToAnyOfPatterns())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public JsonPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch,
|
||||
MatchOperator.Or, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public JsonPathMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator = MatchOperator.Or,
|
||||
params AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
Value = patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
try
|
||||
{
|
||||
var jToken = JToken.Parse(input);
|
||||
score = IsMatch(jToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
// When input is null or byte[], return Mismatch.
|
||||
if (input != null && input is not byte[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Check if JToken or object
|
||||
JToken jToken = input as JToken ?? JObject.FromObject(input);
|
||||
score = IsMatch(jToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(JsonPathMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
private double IsMatch(JToken jToken)
|
||||
{
|
||||
var array = ConvertJTokenToJArrayIfNeeded(jToken);
|
||||
|
||||
// The SelectToken method can accept a string path to a child token ( i.e. "Manufacturers[0].Products[0].Price").
|
||||
// In that case it will return a JValue (some type) which does not implement the IEnumerable interface.
|
||||
var values = _patterns.Select(pattern => array.SelectToken(pattern.GetPattern()) != null).ToArray();
|
||||
|
||||
return MatchScores.ToScore(values, MatchOperator);
|
||||
}
|
||||
|
||||
// https://github.com/wiremock/WireMock.Net/issues/965
|
||||
// https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax
|
||||
// Filtering using SelectToken() isn't guaranteed to work for objects inside objects -- only objects inside arrays.
|
||||
// So this code checks if it's an JArray, if it's not an array, construct a new JArray.
|
||||
private static JToken ConvertJTokenToJArrayIfNeeded(JToken jToken)
|
||||
{
|
||||
if (jToken.Count() == 1)
|
||||
{
|
||||
var property = jToken.First();
|
||||
var item = property.First();
|
||||
if (item is JArray)
|
||||
{
|
||||
return jToken;
|
||||
}
|
||||
|
||||
return new JObject
|
||||
{
|
||||
[property.Path] = new JArray(item)
|
||||
};
|
||||
}
|
||||
|
||||
return jToken;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using DevLab.JmesPath;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// http://jmespath.org/
|
||||
/// </summary>
|
||||
public class JmesPathMatcher : IStringMatcher, IObjectMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { 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, 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, MatchOperator.Or, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public JmesPathMatcher(MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
|
||||
this(MatchBehaviour.AcceptOnMatch, matchOperator, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public JmesPathMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator = MatchOperator.Or,
|
||||
params AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
Value = patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(input))
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
|
||||
score = MatchScores.ToScore(results, MatchOperator);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
|
||||
// When input is null or byte[], return Mismatch.
|
||||
if (input != null && !(input is byte[]))
|
||||
{
|
||||
var inputAsString = JsonConvert.SerializeObject(input);
|
||||
return IsMatch(inputAsString);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(JmesPathMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,250 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
using JsonUtils = WireMock.Util.JsonUtils;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// JsonMatcher
|
||||
/// </summary>
|
||||
public class JsonMatcher : IJsonMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(JsonMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Support Regex
|
||||
/// </summary>
|
||||
public bool Regex { get; }
|
||||
|
||||
private readonly JToken _valueAsJToken;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The string value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="value">The object value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="value">The value to check for equality.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
|
||||
/// <param name="regex">Support Regex.</param>
|
||||
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
{
|
||||
Guard.NotNull(value);
|
||||
|
||||
MatchBehaviour = matchBehaviour;
|
||||
IgnoreCase = ignoreCase;
|
||||
Regex = regex;
|
||||
|
||||
Value = value;
|
||||
_valueAsJToken = JsonUtils.ConvertValueToJToken(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? error = null;
|
||||
|
||||
// When input is null or byte[], return Mismatch.
|
||||
if (input != null && input is not byte[])
|
||||
{
|
||||
try
|
||||
{
|
||||
var inputAsJToken = JsonUtils.ConvertValueToJToken(input);
|
||||
|
||||
var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken));
|
||||
score = MatchScores.ToScore(match);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the input against the matcher value
|
||||
/// </summary>
|
||||
/// <param name="value">Matcher value</param>
|
||||
/// <param name="input">Input value</param>
|
||||
/// <returns></returns>
|
||||
protected virtual bool IsMatch(JToken value, JToken? input)
|
||||
{
|
||||
// If equal, return true.
|
||||
if (input == value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If input is null, return false.
|
||||
if (input == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// If using Regex and the value is a string, use the MatchRegex method.
|
||||
if (Regex && value.Type == JTokenType.String)
|
||||
{
|
||||
var valueAsString = value.ToString();
|
||||
|
||||
var (valid, result) = RegexUtils.MatchRegex(valueAsString, input.ToString());
|
||||
if (valid)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// If the value is a Guid and the input is a string, or vice versa, convert them to strings and compare the string values.
|
||||
if ((value.Type == JTokenType.Guid && input.Type == JTokenType.String) || (value.Type == JTokenType.String && input.Type == JTokenType.Guid))
|
||||
{
|
||||
return JToken.DeepEquals(value.ToString().ToUpperInvariant(), input.ToString().ToUpperInvariant());
|
||||
}
|
||||
|
||||
switch (value.Type)
|
||||
{
|
||||
// If the value is an object, compare all properties.
|
||||
case JTokenType.Object:
|
||||
var valueProperties = value.ToObject<Dictionary<string, JToken>>() ?? new Dictionary<string, JToken>();
|
||||
var inputProperties = input.ToObject<Dictionary<string, JToken>>() ?? new Dictionary<string, JToken>();
|
||||
|
||||
// If the number of properties is different, return false.
|
||||
if (valueProperties.Count != inputProperties.Count)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare all properties. The input must match all properties of the value.
|
||||
foreach (var pair in valueProperties)
|
||||
{
|
||||
if (!IsMatch(pair.Value, inputProperties[pair.Key]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
// If the value is an array, compare all elements.
|
||||
case JTokenType.Array:
|
||||
var valueArray = value.ToObject<JToken[]>() ?? EmptyArray<JToken>.Value;
|
||||
var inputArray = input.ToObject<JToken[]>() ?? EmptyArray<JToken>.Value;
|
||||
|
||||
// If the number of elements is different, return false.
|
||||
if (valueArray.Length != inputArray.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
|
||||
|
||||
default:
|
||||
// Use JToken.DeepEquals() for all other types.
|
||||
return JToken.DeepEquals(value, input);
|
||||
}
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/11679804/json-net-rename-properties
|
||||
private JToken RenameJToken(JToken input)
|
||||
{
|
||||
if (!IgnoreCase)
|
||||
{
|
||||
return input;
|
||||
}
|
||||
|
||||
return input switch
|
||||
{
|
||||
JProperty property => RenameJProperty(property),
|
||||
JArray array => RenameJArray(array),
|
||||
JObject obj => RenameJObject(obj),
|
||||
_ => input
|
||||
};
|
||||
}
|
||||
|
||||
private JProperty RenameJProperty(JProperty property)
|
||||
{
|
||||
if (!IgnoreCase)
|
||||
{
|
||||
return property;
|
||||
}
|
||||
|
||||
var propertyValue = property.Value;
|
||||
if (propertyValue.Type == JTokenType.String && !Regex)
|
||||
{
|
||||
var stringValue = propertyValue.Value<string>()!;
|
||||
propertyValue = ToUpper(stringValue);
|
||||
}
|
||||
|
||||
return new JProperty(ToUpper(property.Name)!, RenameJToken(propertyValue));
|
||||
}
|
||||
|
||||
private JArray RenameJArray(JArray array)
|
||||
{
|
||||
if (Regex)
|
||||
{
|
||||
return array;
|
||||
}
|
||||
|
||||
var renamedValues = array.Select(RenameJToken);
|
||||
return new JArray(renamedValues);
|
||||
}
|
||||
|
||||
private JObject RenameJObject(JObject obj)
|
||||
{
|
||||
var renamedProperties = obj.Properties().Select(RenameJProperty);
|
||||
return new JObject(renamedProperties);
|
||||
}
|
||||
|
||||
private static string? ToUpper(string? input)
|
||||
{
|
||||
return input?.ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// JsonPartialMatcher
|
||||
/// </summary>
|
||||
public class JsonPartialMatcher : AbstractJsonPartialMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(JsonPartialMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public JsonPartialMatcher(string value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public JsonPartialMatcher(object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public JsonPartialMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(matchBehaviour, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsMatch(string value, string input)
|
||||
{
|
||||
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, IgnoreCase, MatchOperator.Or, value);
|
||||
return exactStringMatcher.IsMatch(input).IsPerfect();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// JsonPartialWildCardMatcher
|
||||
/// </summary>
|
||||
public class JsonPartialWildcardMatcher : AbstractJsonPartialMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(JsonPartialWildcardMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public JsonPartialWildcardMatcher(string value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public JsonPartialWildcardMatcher(object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public JsonPartialWildcardMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
|
||||
: base(matchBehaviour, value, ignoreCase, regex)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsMatch(string value, string input)
|
||||
{
|
||||
var wildcardStringMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, value, IgnoreCase);
|
||||
return wildcardStringMatcher.IsMatch(input).IsPerfect();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using AnyOfTypes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Json;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// System.Linq.Dynamic.Core Expression Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IObjectMatcher"/>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
public class LinqMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
public LinqMatcher(AnyOf<string, StringPattern> pattern) : this(new[] { pattern })
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public LinqMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
public LinqMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern) : this(matchBehaviour, MatchOperator.Or, pattern)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LinqMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public LinqMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator = MatchOperator.Or,
|
||||
params AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
Value = patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? error = null;
|
||||
|
||||
// Convert a single input string to a Queryable string-list with 1 entry.
|
||||
IQueryable queryable = new[] { input }.AsQueryable();
|
||||
|
||||
try
|
||||
{
|
||||
// Use the Any(...) method to check if the result matches
|
||||
score = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = e;
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? error = null;
|
||||
|
||||
JArray jArray;
|
||||
try
|
||||
{
|
||||
jArray = new JArray { input };
|
||||
}
|
||||
catch
|
||||
{
|
||||
jArray = input == null ? new JArray() : new JArray { JToken.FromObject(input) };
|
||||
}
|
||||
|
||||
// Convert a single object to a Queryable JObject-list with 1 entry.
|
||||
var queryable = jArray.ToDynamicClassArray().AsQueryable();
|
||||
|
||||
try
|
||||
{
|
||||
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
|
||||
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
|
||||
|
||||
score = MatchScores.ToScore(scores, MatchOperator);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error = e;
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(LinqMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MatchBehaviour (Accept or Reject)
|
||||
/// </summary>
|
||||
public enum MatchBehaviour
|
||||
{
|
||||
/// <summary>
|
||||
/// Accept on match (default)
|
||||
/// </summary>
|
||||
AcceptOnMatch,
|
||||
|
||||
/// <summary>
|
||||
/// Reject on match
|
||||
/// </summary>
|
||||
RejectOnMatch
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
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 MatchResult Convert(MatchBehaviour matchBehaviour, MatchResult result)
|
||||
{
|
||||
return matchBehaviour == MatchBehaviour.AcceptOnMatch ?
|
||||
result :
|
||||
new MatchResult(Convert(matchBehaviour, result.Score), result.Exception);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// The Operator to use when multiple patterns are defined.
|
||||
/// </summary>
|
||||
public enum MatchOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// Only one pattern needs to match. [Default]
|
||||
/// </summary>
|
||||
Or,
|
||||
|
||||
/// <summary>
|
||||
/// All patterns should match.
|
||||
/// </summary>
|
||||
And,
|
||||
|
||||
/// <summary>
|
||||
/// The average value from all patterns.
|
||||
/// </summary>
|
||||
Average
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// The MatchResult which contains the score (value between 0.0 - 1.0 of the similarity) and an optional error message.
|
||||
/// </summary>
|
||||
public struct MatchResult
|
||||
{
|
||||
/// <summary>
|
||||
/// A value between 0.0 - 1.0 of the similarity.
|
||||
/// </summary>
|
||||
public double Score { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The exception message) in case the matching fails.
|
||||
/// [Optional]
|
||||
/// </summary>
|
||||
public Exception? Exception { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult
|
||||
/// </summary>
|
||||
/// <param name="score">A value between 0.0 - 1.0 of the similarity.</param>
|
||||
/// <param name="exception">The exception in case the matching fails. [Optional]</param>
|
||||
public MatchResult(double score, Exception? exception = null)
|
||||
{
|
||||
Score = score;
|
||||
Exception = exception;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult
|
||||
/// </summary>
|
||||
/// <param name="exception">The exception in case the matching fails.</param>
|
||||
public MatchResult(Exception exception)
|
||||
{
|
||||
Exception = Guard.NotNull(exception);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implicitly converts a double to a MatchResult.
|
||||
/// </summary>
|
||||
/// <param name="score">The score</param>
|
||||
public static implicit operator MatchResult(double score)
|
||||
{
|
||||
return new MatchResult(score);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Is the value a perfect match?
|
||||
/// </summary>
|
||||
public bool IsPerfect() => MatchScores.IsPerfect(Score);
|
||||
|
||||
/// <summary>
|
||||
/// Create a MatchResult from multiple MatchResults.
|
||||
/// </summary>
|
||||
/// <param name="matchResults">A list of MatchResults.</param>
|
||||
/// <param name="matchOperator">The MatchOperator</param>
|
||||
/// <returns>MatchResult</returns>
|
||||
public static MatchResult From(IReadOnlyList<MatchResult> matchResults, MatchOperator matchOperator)
|
||||
{
|
||||
Guard.NotNullOrEmpty(matchResults);
|
||||
|
||||
if (matchResults.Count == 1)
|
||||
{
|
||||
return matchResults[0];
|
||||
}
|
||||
|
||||
return new MatchResult
|
||||
{
|
||||
Score = MatchScores.ToScore(matchResults.Select(r => r.Score).ToArray(), matchOperator),
|
||||
Exception = matchResults.Select(m => m.Exception).OfType<Exception>().ToArray().ToException()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Expand to Tuple
|
||||
/// </summary>
|
||||
/// <returns>Tuple : Score and Exception</returns>
|
||||
public (double Score, Exception? Exception) Expand()
|
||||
{
|
||||
return (Score, Exception);
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MatchScores
|
||||
/// </summary>
|
||||
public static class MatchScores
|
||||
{
|
||||
/// <summary>
|
||||
/// The tolerance
|
||||
/// </summary>
|
||||
public const double Tolerance = 0.000001;
|
||||
|
||||
/// <summary>
|
||||
/// The default mismatch score
|
||||
/// </summary>
|
||||
public const double Mismatch = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// The default perfect match score
|
||||
/// </summary>
|
||||
public const double Perfect = 1.0;
|
||||
|
||||
/// <summary>
|
||||
/// The almost perfect match score
|
||||
/// </summary>
|
||||
public const double AlmostPerfect = 0.99;
|
||||
|
||||
/// <summary>
|
||||
/// Is the value a perfect match?
|
||||
/// </summary>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <returns>true/false</returns>
|
||||
public static bool IsPerfect(double value)
|
||||
{
|
||||
return Math.Abs(value - Perfect) < Tolerance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert a bool to the score.
|
||||
/// </summary>
|
||||
/// <param name="value">if set to <c>true</c> [value].</param>
|
||||
/// <returns>score</returns>
|
||||
public static double ToScore(bool value)
|
||||
{
|
||||
return value ? Perfect : Mismatch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple values.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/>.</param>
|
||||
/// <returns>average score</returns>
|
||||
public static double ToScore(IReadOnlyCollection<bool> values, MatchOperator matchOperator)
|
||||
{
|
||||
return ToScore(values.Select(ToScore).ToArray(), matchOperator);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the score from multiple values.
|
||||
/// </summary>
|
||||
/// <param name="values">The values.</param>
|
||||
/// <param name="matchOperator"></param>
|
||||
/// <returns>average score</returns>
|
||||
public static double ToScore(IReadOnlyCollection<double> values, MatchOperator matchOperator)
|
||||
{
|
||||
if (!values.Any())
|
||||
{
|
||||
return Mismatch;
|
||||
}
|
||||
|
||||
return matchOperator switch
|
||||
{
|
||||
MatchOperator.Or => ToScore(values.Any(IsPerfect)),
|
||||
MatchOperator.And => ToScore(values.All(IsPerfect)),
|
||||
_ => values.Average()
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Provides some extension methods for matchers.
|
||||
/// </summary>
|
||||
public static class MatcherExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if the match result is a perfect match.
|
||||
/// </summary>
|
||||
/// <param name="matcher">The string matcher.</param>
|
||||
/// <param name="input">The input string to match.</param>
|
||||
/// <returns><c>true</c>> if the match is perfect; otherwise, <c>false</c>>.</returns>
|
||||
public static bool IsPerfectMatch(this IStringMatcher matcher, string? input)
|
||||
{
|
||||
return matcher.IsMatch(input).IsPerfect();
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if MIMEKIT
|
||||
using System;
|
||||
using MimeKit;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Helpers;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// MimePartMatcher
|
||||
/// </summary>
|
||||
public class MimePartMatcher : IMatcher
|
||||
{
|
||||
private readonly Func<MimePart, MatchResult>[] _funcs;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(MimePartMatcher);
|
||||
|
||||
/// <summary>
|
||||
/// ContentType Matcher (image/png; name=image.png.)
|
||||
/// </summary>
|
||||
public IStringMatcher? ContentTypeMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ContentDisposition Matcher (attachment; filename=image.png)
|
||||
/// </summary>
|
||||
public IStringMatcher? ContentDispositionMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ContentTransferEncoding Matcher (base64)
|
||||
/// </summary>
|
||||
public IStringMatcher? ContentTransferEncodingMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Content Matcher
|
||||
/// </summary>
|
||||
public IMatcher? ContentMatcher { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="MimePartMatcher"/> class.
|
||||
/// </summary>
|
||||
public MimePartMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
IStringMatcher? contentTypeMatcher,
|
||||
IStringMatcher? contentDispositionMatcher,
|
||||
IStringMatcher? contentTransferEncodingMatcher,
|
||||
IMatcher? contentMatcher
|
||||
)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
ContentTypeMatcher = contentTypeMatcher;
|
||||
ContentDispositionMatcher = contentDispositionMatcher;
|
||||
ContentTransferEncodingMatcher = contentTransferEncodingMatcher;
|
||||
ContentMatcher = contentMatcher;
|
||||
|
||||
_funcs =
|
||||
[
|
||||
mp => ContentTypeMatcher?.IsMatch(GetContentTypeAsString(mp.ContentType)) ?? MatchScores.Perfect,
|
||||
mp => ContentDispositionMatcher?.IsMatch(mp.ContentDisposition.ToString().Replace("Content-Disposition: ", string.Empty)) ?? MatchScores.Perfect,
|
||||
mp => ContentTransferEncodingMatcher?.IsMatch(mp.ContentTransferEncoding.ToString().ToLowerInvariant()) ?? MatchScores.Perfect,
|
||||
MatchOnContent
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified MimePart is match.
|
||||
/// </summary>
|
||||
/// <param name="mimePart">The MimePart.</param>
|
||||
/// <returns>A value between 0.0 - 1.0 of the similarity.</returns>
|
||||
public MatchResult IsMatch(MimePart mimePart)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
try
|
||||
{
|
||||
if (Array.TrueForAll(_funcs, func => func(mimePart).IsPerfect()))
|
||||
{
|
||||
score = MatchScores.Perfect;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return "NotImplemented";
|
||||
}
|
||||
|
||||
private MatchResult MatchOnContent(MimePart mimePart)
|
||||
{
|
||||
if (ContentMatcher == null)
|
||||
{
|
||||
return MatchScores.Perfect;
|
||||
}
|
||||
|
||||
var bodyParserSettings = new BodyParserSettings
|
||||
{
|
||||
Stream = mimePart.Content.Open(),
|
||||
ContentType = GetContentTypeAsString(mimePart.ContentType),
|
||||
DeserializeJson = true,
|
||||
ContentEncoding = null, // mimePart.ContentType.CharsetEncoding.ToString(),
|
||||
DecompressGZipAndDeflate = true
|
||||
};
|
||||
|
||||
var bodyData = BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false).GetAwaiter().GetResult();
|
||||
return BodyDataMatchScoreCalculator.CalculateMatchScore(bodyData, ContentMatcher);
|
||||
}
|
||||
|
||||
private static string? GetContentTypeAsString(ContentType? contentType)
|
||||
{
|
||||
return contentType?.ToString().Replace("Content-Type: ", string.Empty);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if GRAPHQL
|
||||
using System;
|
||||
using GraphQL.Types;
|
||||
|
||||
namespace WireMock.Matchers.Models;
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract class WireMockCustomScalarGraphType<T> : ScalarGraphType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override object? ParseValue(object? value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case null:
|
||||
return null;
|
||||
|
||||
case T:
|
||||
return value;
|
||||
}
|
||||
|
||||
if (value is string && typeof(T) != typeof(string))
|
||||
{
|
||||
throw new InvalidCastException($"Unable to convert value '{value}' of type '{typeof(string)}' to type '{typeof(T)}'.");
|
||||
}
|
||||
|
||||
return (T)Convert.ChangeType(value, typeof(T));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// NotNullOrEmptyMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IObjectMatcher" />
|
||||
public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(NotNullOrEmptyMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object Value { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotNullOrEmptyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
public NotNullOrEmptyMatcher(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
Value = string.Empty;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(object? input)
|
||||
{
|
||||
bool match;
|
||||
|
||||
switch (input)
|
||||
{
|
||||
case string @string:
|
||||
match = !string.IsNullOrEmpty(@string);
|
||||
break;
|
||||
|
||||
case byte[] bytes:
|
||||
match = bytes.Any();
|
||||
break;
|
||||
|
||||
default:
|
||||
match = input != null;
|
||||
break;
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var match = !string.IsNullOrEmpty(input);
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator => MatchOperator.Or;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ProtoBufJsonConverter;
|
||||
using ProtoBufJsonConverter.Models;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Grpc ProtoBuf Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IProtoBufMatcher"/>
|
||||
public class ProtoBufMatcher : IProtoBufMatcher
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(ProtoBufMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Func to define the proto definition as id or texts.
|
||||
/// </summary>
|
||||
public Func<IdOrTexts> ProtoDefinition { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".
|
||||
/// </summary>
|
||||
public string MessageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Matcher to use (optional).
|
||||
/// </summary>
|
||||
public IObjectMatcher? Matcher { get; }
|
||||
|
||||
private static readonly Converter ProtoBufToJsonConverter = SingletonFactory<Converter>.GetInstance();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ProtoBufMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="protoDefinition">The proto definition as id or text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <param name="matcher">The optional jsonMatcher to use to match the ProtoBuf as (json) object.</param>
|
||||
public ProtoBufMatcher(
|
||||
Func<IdOrTexts> protoDefinition,
|
||||
string messageType,
|
||||
MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch,
|
||||
IObjectMatcher? matcher = null
|
||||
)
|
||||
{
|
||||
ProtoDefinition = Guard.NotNull(protoDefinition);
|
||||
MessageType = Guard.NotNullOrWhiteSpace(messageType);
|
||||
Matcher = matcher;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var result = new MatchResult();
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var instance = await DecodeAsync(input, true, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
result = Matcher?.IsMatch(instance) ?? new MatchResult(MatchScores.Perfect);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result = new MatchResult(MatchScores.Mismatch, e);
|
||||
}
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, result);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<object?> DecodeAsync(byte[]? input, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return DecodeAsync(input, false, cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return "NotImplemented";
|
||||
}
|
||||
|
||||
private async Task<object?> DecodeAsync(byte[]? input, bool throwException, CancellationToken cancellationToken)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var protoDefinitions = ProtoDefinition().Texts;
|
||||
|
||||
var resolver = new WireMockProtoFileResolver(protoDefinitions);
|
||||
var request = new ConvertToObjectRequest(protoDefinitions[0], MessageType, input)
|
||||
.WithProtoFileResolver(resolver);
|
||||
|
||||
try
|
||||
{
|
||||
return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (throwException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,144 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AnyOfTypes;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.RegularExpressions;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// Regular Expression Matcher
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IStringMatcher"/>
|
||||
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
|
||||
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
private readonly Regex[] _expressions;
|
||||
private readonly bool _useRegexExtended;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public RegexMatcher(
|
||||
[RegexPattern] AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
bool useRegexExtended = true,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, useRegexExtended, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public RegexMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
[RegexPattern] AnyOf<string, StringPattern> pattern,
|
||||
bool ignoreCase = false,
|
||||
bool useRegexExtended = true,
|
||||
MatchOperator matchOperator = MatchOperator.Or) :
|
||||
this(matchBehaviour, [pattern], ignoreCase, useRegexExtended, matchOperator)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegexMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="useRegexExtended">Use RegexExtended (default = true).</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public RegexMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
[RegexPattern] AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
bool useRegexExtended = true,
|
||||
MatchOperator matchOperator = MatchOperator.Or)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
IgnoreCase = ignoreCase;
|
||||
_useRegexExtended = useRegexExtended;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
|
||||
var options = RegexOptions.Compiled | RegexOptions.Multiline;
|
||||
|
||||
if (ignoreCase)
|
||||
{
|
||||
options |= RegexOptions.IgnoreCase;
|
||||
}
|
||||
|
||||
_expressions = patterns.Select(p => useRegexExtended ? new RegexExtended(p.GetPattern(), options) : new Regex(p.GetPattern(), options, WireMockConstants.DefaultRegexTimeout)).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (input != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
score = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)).ToArray(), MatchOperator);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string Name => nameof(RegexMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(_useRegexExtended)}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// CompositeMatcherType
|
||||
/// </summary>
|
||||
public enum CompositeMatcherType
|
||||
{
|
||||
/// <summary>
|
||||
/// And
|
||||
/// </summary>
|
||||
And = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Or
|
||||
/// </summary>
|
||||
Or = 1
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// RequestMatchResult
|
||||
/// </summary>
|
||||
public class RequestMatchResult : IRequestMatchResult
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public double TotalScore => MatchDetails.Sum(md => md.Score);
|
||||
|
||||
/// <inheritdoc />
|
||||
public int TotalNumber => MatchDetails.Count;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPerfectMatch => Math.Abs(TotalScore - TotalNumber) < MatchScores.Tolerance;
|
||||
|
||||
/// <inheritdoc />
|
||||
public double AverageTotalScore => TotalNumber == 0 ? MatchScores.Mismatch : TotalScore / TotalNumber;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IList<MatchDetail> MatchDetails { get; } = new List<MatchDetail>();
|
||||
|
||||
/// <inheritdoc />
|
||||
public double AddScore(Type matcherType, double score, Exception? exception)
|
||||
{
|
||||
MatchDetails.Add(new MatchDetail { MatcherType = matcherType, Score = score, Exception = exception });
|
||||
|
||||
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)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var compareObj = (RequestMatchResult)obj;
|
||||
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Helpers;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request body matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The body function
|
||||
/// </summary>
|
||||
public Func<string?, bool>? Func { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for byte[]
|
||||
/// </summary>
|
||||
public Func<byte[]?, bool>? DataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for json
|
||||
/// </summary>
|
||||
public Func<object?, bool>? JsonFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for BodyData
|
||||
/// </summary>
|
||||
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for FormUrlEncoded
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
public IMatcher[]? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, string body) :
|
||||
this(new[] { new WildcardMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, byte[] body) :
|
||||
this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, object body) :
|
||||
this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<string?, bool> func)
|
||||
{
|
||||
Func = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<byte[]?, bool> func)
|
||||
{
|
||||
DataFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<object?, bool> func)
|
||||
{
|
||||
JsonFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<IBodyData?, bool> func)
|
||||
{
|
||||
BodyDataFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
FormUrlEncodedFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageBodyMatcher(params IMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
public RequestMessageBodyMatcher(MatchOperator matchOperator, params IMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = CalculateMatchScore(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null && Matchers.Any())
|
||||
{
|
||||
var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Func != null)
|
||||
{
|
||||
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
|
||||
}
|
||||
|
||||
if (FormUrlEncodedFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
|
||||
}
|
||||
|
||||
if (JsonFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
|
||||
}
|
||||
|
||||
if (DataFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(DataFunc(requestMessage.BodyData?.BodyAsBytes));
|
||||
}
|
||||
|
||||
if (BodyDataFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(BodyDataFunc(requestMessage.BodyData));
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request clientIP matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageClientIPMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matchers
|
||||
/// </summary>
|
||||
public IReadOnlyList<IStringMatcher>? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The clientIP functions
|
||||
/// </summary>
|
||||
public Func<string, bool>[]? Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
/// </summary>
|
||||
public MatchBehaviour Behaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="clientIPs">The clientIPs.</param>
|
||||
public RequestMessageClientIPMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator,
|
||||
params string[] clientIPs) :
|
||||
this(matchBehaviour, matchOperator, clientIPs
|
||||
.Select(clientIP => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { clientIP }, false, matchOperator))
|
||||
.Cast<IStringMatcher>().ToArray())
|
||||
{
|
||||
Behaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageClientIPMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, params IStringMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
Behaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The clientIP functions.</param>
|
||||
public RequestMessageClientIPMatcher(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null)
|
||||
{
|
||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.ClientIP)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var results = Funcs.Select(func => func(requestMessage.ClientIP)).ToArray();
|
||||
return MatchScores.ToScore(results, MatchOperator);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The composite request matcher.
|
||||
/// </summary>
|
||||
public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||
{
|
||||
private readonly CompositeMatcherType _type;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request matchers.
|
||||
/// </summary>
|
||||
/// <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(IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
||||
{
|
||||
RequestMatchers = Guard.NotNull(requestMatchers);
|
||||
_type = type;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
if (!RequestMatchers.Any())
|
||||
{
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
if (_type == CompositeMatcherType.And)
|
||||
{
|
||||
return RequestMatchers.Average(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||
}
|
||||
|
||||
return RequestMatchers.Max(requestMatcher => requestMatcher.GetMatchingScore(requestMessage, requestMatchResult));
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Stef.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request cookie matcher.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IRequestMatcher"/>
|
||||
public class RequestMessageCookieMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
/// </summary>
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// IgnoreCase
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The functions
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>, bool>[]? Funcs { get; }
|
||||
|
||||
/// <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, string name, string pattern, bool ignoreCase)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
IgnoreCase = ignoreCase;
|
||||
Name = Guard.NotNull(name);
|
||||
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, Guard.NotNull(pattern), ignoreCase) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
public RequestMessageCookieMatcher(MatchBehaviour matchBehaviour, string name, bool ignoreCase, params string[] patterns) :
|
||||
this(matchBehaviour, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast<IStringMatcher>().ToArray())
|
||||
{
|
||||
Guard.NotNull(patterns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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, string name, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
Name = Guard.NotNull(name);
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
IgnoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageCookieMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageCookieMatcher(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNull(funcs);
|
||||
|
||||
Funcs = funcs;
|
||||
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(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 default;
|
||||
}
|
||||
|
||||
if (!cookies.ContainsKey(Name))
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
return Matchers.Max(m => m.IsMatch(cookies[Name]));
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request body GraphQL matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageGraphQLMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
public IMatcher[]? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageGraphQLMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="schema">The schema.</param>
|
||||
/// <param name="customScalars">A dictionary defining the custom scalars used in this schema. [optional]</param>
|
||||
public RequestMessageGraphQLMatcher(MatchBehaviour matchBehaviour, string schema, IDictionary<string, Type>? customScalars = null) :
|
||||
this(CreateMatcherArray(matchBehaviour, schema, customScalars))
|
||||
{
|
||||
}
|
||||
|
||||
#if GRAPHQL
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageGraphQLMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="schema">The schema.</param>
|
||||
/// <param name="customScalars">A dictionary defining the custom scalars used in this schema. [optional]</param>
|
||||
public RequestMessageGraphQLMatcher(MatchBehaviour matchBehaviour, GraphQL.Types.ISchema schema, IDictionary<string, Type>? customScalars = null) :
|
||||
this(CreateMatcherArray(matchBehaviour, new AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern, GraphQL.Types.ISchema>(schema), customScalars))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageGraphQLMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageGraphQLMatcher(params IMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageGraphQLMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
public RequestMessageGraphQLMatcher(MatchOperator matchOperator, params IMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var results = CalculateMatchResults(requestMessage);
|
||||
var (score, exception) = MatchResult.From(results, MatchOperator).Expand();
|
||||
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private static MatchResult CalculateMatchResult(IRequestMessage requestMessage, IMatcher matcher)
|
||||
{
|
||||
// In case the matcher is a IStringMatcher and the body is a Json or a String, use the BodyAsString to match on.
|
||||
if (matcher is IStringMatcher stringMatcher && requestMessage.BodyData?.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private IReadOnlyList<MatchResult> CalculateMatchResults(IRequestMessage requestMessage)
|
||||
{
|
||||
return Matchers == null ? new[] { new MatchResult() } : Matchers.Select(matcher => CalculateMatchResult(requestMessage, matcher)).ToArray();
|
||||
}
|
||||
|
||||
#if GRAPHQL
|
||||
private static IMatcher[] CreateMatcherArray(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOfTypes.AnyOf<string, WireMock.Models.StringPattern, GraphQL.Types.ISchema> schema,
|
||||
IDictionary<string, Type>? customScalars
|
||||
)
|
||||
{
|
||||
return new[] { new GraphQLMatcher(schema, customScalars, matchBehaviour) }.Cast<IMatcher>().ToArray();
|
||||
}
|
||||
#else
|
||||
private static IMatcher[] CreateMatcherArray(MatchBehaviour matchBehaviour, object schema, IDictionary<string, Type>? customScalars)
|
||||
{
|
||||
throw new System.NotSupportedException("The GrapQLMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request header matcher.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="IRequestMatcher"/>
|
||||
public class RequestMessageHeaderMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
/// </summary>
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// IgnoreCase
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The functions
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string[]>, bool>[]? Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The name
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <value>
|
||||
/// The matchers.
|
||||
/// </value>
|
||||
public IStringMatcher[]? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> 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 RequestMessageHeaderMatcher(MatchBehaviour matchBehaviour, string name, string pattern, bool ignoreCase)
|
||||
{
|
||||
Guard.NotNull(name);
|
||||
Guard.NotNull(pattern);
|
||||
|
||||
MatchBehaviour = matchBehaviour;
|
||||
IgnoreCase = ignoreCase;
|
||||
Name = name;
|
||||
Matchers = new IStringMatcher[] { new WildcardMatcher(matchBehaviour, pattern, ignoreCase) };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
public RequestMessageHeaderMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, string name, bool ignoreCase, params string[] patterns) :
|
||||
this(matchBehaviour, matchOperator, name, ignoreCase, patterns.Select(pattern => new WildcardMatcher(matchBehaviour, pattern, ignoreCase)).Cast<IStringMatcher>().ToArray())
|
||||
{
|
||||
Guard.NotNull(patterns);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
public RequestMessageHeaderMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, string name, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name);
|
||||
Guard.NotNull(matchers);
|
||||
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
Name = name;
|
||||
Matchers = matchers;
|
||||
IgnoreCase = ignoreCase;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHeaderMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageHeaderMatcher(params Func<IDictionary<string, string[]>, bool>[] funcs)
|
||||
{
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
Name = string.Empty; // Not used when Func, but set to a non-null valid value.
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
if (requestMessage.Headers == null)
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
// Check if we want to use IgnoreCase to compare the Header-Name and Header-Value(s)
|
||||
var headers = !IgnoreCase ? requestMessage.Headers : new Dictionary<string, WireMockList<string>>(requestMessage.Headers, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var funcResults = Funcs.Select(f => f(headers.ToDictionary(entry => entry.Key, entry => entry.Value.ToArray()))).ToArray();
|
||||
return MatchScores.ToScore(funcResults, MatchOperator);
|
||||
}
|
||||
|
||||
if (Matchers != null)
|
||||
{
|
||||
if (!headers.ContainsKey(Name))
|
||||
{
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
}
|
||||
|
||||
var results = new List<MatchResult>();
|
||||
foreach (var matcher in Matchers)
|
||||
{
|
||||
var resultsPerMatcher = headers[Name].Select(matcher.IsMatch).ToArray();
|
||||
|
||||
results.Add(MatchResult.From(resultsPerMatcher, MatchOperator.And));
|
||||
}
|
||||
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
}
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request HTTP Version matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageHttpVersionMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matcher.
|
||||
/// </summary>
|
||||
public IStringMatcher? Matcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The func.
|
||||
/// </summary>
|
||||
public Func<string, bool>? Func { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
/// </summary>
|
||||
public MatchBehaviour Behaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP Version
|
||||
/// </summary>
|
||||
public string? HttpVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageHttpVersionMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="httpVersion">The HTTP Version.</param>
|
||||
public RequestMessageHttpVersionMatcher(MatchBehaviour matchBehaviour, string httpVersion) :
|
||||
this(matchBehaviour, new ExactMatcher(matchBehaviour, httpVersion))
|
||||
{
|
||||
HttpVersion = httpVersion;
|
||||
Behaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matcher">The matcher.</param>
|
||||
public RequestMessageHttpVersionMatcher(MatchBehaviour matchBehaviour, IStringMatcher matcher)
|
||||
{
|
||||
Matcher = Guard.NotNull(matcher);
|
||||
Behaviour = matchBehaviour;
|
||||
HttpVersion = matcher.GetPatterns().FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageClientIPMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageHttpVersionMatcher(Func<string, bool> func)
|
||||
{
|
||||
Func = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Matcher != null)
|
||||
{
|
||||
return Matcher.IsMatch(requestMessage.HttpVersion);
|
||||
}
|
||||
|
||||
if (Func != null)
|
||||
{
|
||||
return MatchScores.ToScore(Func(requestMessage.HttpVersion));
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request method matcher.
|
||||
/// </summary>
|
||||
internal class RequestMessageMethodMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Matchers.MatchBehaviour"/>
|
||||
/// </summary>
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Matchers.MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The methods
|
||||
/// </summary>
|
||||
public string[] Methods { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageMethodMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="methods">The methods.</param>
|
||||
public RequestMessageMethodMatcher(params string[] methods) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, methods)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageMethodMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
|
||||
/// <param name="methods">The methods.</param>
|
||||
public RequestMessageMethodMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, params string[] methods)
|
||||
{
|
||||
Methods = Guard.NotNull(methods);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var scores = Methods.Select(m => string.Equals(m, requestMessage.Method, StringComparison.OrdinalIgnoreCase)).ToArray();
|
||||
var score = MatchScores.ToScore(scores, MatchOperator);
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request body MultiPart matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
public IMatcher[]? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
/// </summary>
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageMultiPartMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageMultiPartMatcher(params IMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageMultiPartMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageMultiPartMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, params IMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
#if !MIMEKIT
|
||||
throw new System.NotSupportedException("The MultiPartMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
var score = MatchScores.Mismatch;
|
||||
Exception? exception = null;
|
||||
|
||||
if (Matchers?.Any() != true)
|
||||
{
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
}
|
||||
|
||||
if (!MimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
|
||||
{
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var mimePartMatchers = Matchers.OfType<MimePartMatcher>().ToArray();
|
||||
|
||||
foreach (var mimePartMatcher in Matchers.OfType<MimePartMatcher>().ToArray())
|
||||
{
|
||||
score = MatchScores.Mismatch;
|
||||
foreach (var mimeBodyPart in message.BodyParts.OfType<MimeKit.MimePart>())
|
||||
{
|
||||
var matchResult = mimePartMatcher.IsMatch(mimeBodyPart);
|
||||
if (matchResult.IsPerfect())
|
||||
{
|
||||
score = MatchScores.Perfect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((MatchOperator == MatchOperator.Or && MatchScores.IsPerfect(score))
|
||||
|| (MatchOperator == MatchOperator.And && !MatchScores.IsPerfect(score)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
exception = ex;
|
||||
}
|
||||
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request parameters matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageParamMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// MatchBehaviour
|
||||
/// </summary>
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The funcs
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, WireMockList<string>>, bool>[]? Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The key
|
||||
/// </summary>
|
||||
public string Key { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the key should be matched using case-ignore.
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IStringMatcher>? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase) : this(matchBehaviour, key, ignoreCase, (IStringMatcher[]?)null)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||
/// <param name="values">The values.</param>
|
||||
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params string[]? values) :
|
||||
this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params IStringMatcher[]? matchers)
|
||||
{
|
||||
MatchBehaviour = matchBehaviour;
|
||||
Key = Guard.NotNull(key);
|
||||
IgnoreCase = ignoreCase;
|
||||
Matchers = matchers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageParamMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The funcs.</param>
|
||||
public RequestMessageParamMatcher(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
||||
{
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var score = GetMatchScore(requestMessage);
|
||||
return requestMatchResult.AddScore(GetType(), MatchBehaviourHelper.Convert(MatchBehaviour, score), null);
|
||||
}
|
||||
|
||||
private double GetMatchScore(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Funcs != null)
|
||||
{
|
||||
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
|
||||
}
|
||||
|
||||
var valuesPresentInRequestMessage = ((RequestMessage)requestMessage).GetParameter(Key, IgnoreCase);
|
||||
if (valuesPresentInRequestMessage == null)
|
||||
{
|
||||
// Key is not present at all, just return Mismatch
|
||||
return MatchScores.Mismatch;
|
||||
}
|
||||
|
||||
if (Matchers == null || !Matchers.Any())
|
||||
{
|
||||
// Matchers are null or not defined, and Key is present, just return Perfect.
|
||||
return MatchScores.Perfect;
|
||||
}
|
||||
|
||||
// Return the score based on Matchers and valuesPresentInRequestMessage
|
||||
return CalculateScore(Matchers, valuesPresentInRequestMessage);
|
||||
}
|
||||
|
||||
private static double CalculateScore(IReadOnlyList<IStringMatcher> matchers, WireMockList<string> valuesPresentInRequestMessage)
|
||||
{
|
||||
var total = new List<double>();
|
||||
|
||||
// If the total patterns in all matchers > values in message, use the matcher as base
|
||||
if (matchers.Sum(m => m.GetPatterns().Length) > valuesPresentInRequestMessage.Count)
|
||||
{
|
||||
foreach (var matcher in matchers)
|
||||
{
|
||||
double score = 0d;
|
||||
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
|
||||
{
|
||||
score += matcher.IsMatch(valuePresentInRequestMessage).Score / matcher.GetPatterns().Length;
|
||||
}
|
||||
|
||||
total.Add(score);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (string valuePresentInRequestMessage in valuesPresentInRequestMessage)
|
||||
{
|
||||
var score = matchers.Max(m => m.IsMatch(valuePresentInRequestMessage).Score);
|
||||
total.Add(score);
|
||||
}
|
||||
}
|
||||
|
||||
return total.Any() ? MatchScores.ToScore(total, MatchOperator.Average) : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request path matcher.
|
||||
/// </summary>
|
||||
public class RequestMessagePathMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matchers
|
||||
/// </summary>
|
||||
public IReadOnlyList<IStringMatcher>? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path functions
|
||||
/// </summary>
|
||||
public Func<string, bool>[]? Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
/// </summary>
|
||||
public MatchBehaviour Behaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="paths">The paths.</param>
|
||||
public RequestMessagePathMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator,
|
||||
params string[] paths) :
|
||||
this(matchBehaviour, matchOperator, paths
|
||||
.Select(path => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { path }, false, matchOperator))
|
||||
.Cast<IStringMatcher>().ToArray())
|
||||
{
|
||||
Behaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessagePathMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, params IStringMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
Behaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessagePathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The path functions.</param>
|
||||
public RequestMessagePathMatcher(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null)
|
||||
{
|
||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.Path)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var results = Funcs.Select(func => func(requestMessage.Path)).ToArray();
|
||||
return MatchScores.ToScore(results, MatchOperator);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request body Grpc ProtoBuf matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageProtoBufMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The ProtoBufMatcher.
|
||||
/// </summary>
|
||||
public IProtoBufMatcher? Matcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageProtoBufMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <param name="protoDefinition">The Func to define the proto definitions as id or text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The optional matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
public RequestMessageProtoBufMatcher(MatchBehaviour matchBehaviour, Func<IdOrTexts> protoDefinition, string messageType, IObjectMatcher? matcher = null)
|
||||
{
|
||||
#if PROTOBUF
|
||||
Matcher = new ProtoBufMatcher(protoDefinition, messageType, matchBehaviour, matcher);
|
||||
#else
|
||||
throw new System.NotSupportedException("The ProtoBufMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
return Matcher?.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult() ?? default;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The scenario and state matcher.
|
||||
/// </summary>
|
||||
internal class RequestMessageScenarioAndStateMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Execution state condition for the current mapping.
|
||||
/// </summary>
|
||||
private readonly string? _executionConditionState;
|
||||
|
||||
/// <summary>
|
||||
/// The next state which will be signaled after the current mapping execution.
|
||||
/// In case the value is null state will not be changed.
|
||||
/// </summary>
|
||||
private readonly string? _nextState;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageScenarioAndStateMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="nextState">The next state.</param>
|
||||
/// <param name="executionConditionState">Execution state condition for the current mapping.</param>
|
||||
public RequestMessageScenarioAndStateMatcher(string? nextState, string? executionConditionState)
|
||||
{
|
||||
_nextState = nextState;
|
||||
_executionConditionState = executionConditionState;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
return requestMatchResult.AddScore(GetType(), GetScore(), null);
|
||||
}
|
||||
|
||||
private double GetScore()
|
||||
{
|
||||
return Equals(_executionConditionState, _nextState) ? MatchScores.Perfect : MatchScores.Mismatch;
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
/// <summary>
|
||||
/// The request url matcher.
|
||||
/// </summary>
|
||||
public class RequestMessageUrlMatcher : IRequestMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The matchers
|
||||
/// </summary>
|
||||
public IReadOnlyList<IStringMatcher>? Matchers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The url functions
|
||||
/// </summary>
|
||||
public Func<string, bool>[]? Funcs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchBehaviour"/>
|
||||
/// </summary>
|
||||
public MatchBehaviour Behaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MatchOperator"/>
|
||||
/// </summary>
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
|
||||
/// <param name="urls">The urls.</param>
|
||||
public RequestMessageUrlMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator,
|
||||
params string[] urls) :
|
||||
this(matchBehaviour, matchOperator, urls
|
||||
.Select(url => new WildcardMatcher(matchBehaviour, new AnyOf<string, StringPattern>[] { url }, false, matchOperator))
|
||||
.Cast<IStringMatcher>().ToArray())
|
||||
{
|
||||
Behaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
public RequestMessageUrlMatcher(MatchBehaviour matchBehaviour, MatchOperator matchOperator, params IStringMatcher[] matchers)
|
||||
{
|
||||
Matchers = Guard.NotNull(matchers);
|
||||
Behaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageUrlMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The url functions.</param>
|
||||
public RequestMessageUrlMatcher(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Funcs = Guard.NotNull(funcs);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
|
||||
{
|
||||
var (score, exception) = GetMatchResult(requestMessage).Expand();
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private MatchResult GetMatchResult(IRequestMessage requestMessage)
|
||||
{
|
||||
if (Matchers != null)
|
||||
{
|
||||
var results = Matchers.Select(m => m.IsMatch(requestMessage.Url)).ToArray();
|
||||
return MatchResult.From(results, MatchOperator);
|
||||
}
|
||||
|
||||
if (Funcs != null)
|
||||
{
|
||||
var results = Funcs.Select(func => func(requestMessage.Url)).ToArray();
|
||||
return MatchScores.ToScore(results, MatchOperator);
|
||||
}
|
||||
|
||||
return default;
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
Some source files in this folder are 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.
|
||||
@@ -1,140 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using SimMetrics.Net;
|
||||
using SimMetrics.Net.API;
|
||||
using SimMetrics.Net.Metric;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// SimMetricsMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IStringMatcher" />
|
||||
public class SimMetricsMatcher : IStringMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
private readonly SimMetricType _simMetricType;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
public SimMetricsMatcher(AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(new[] { pattern }, simMetricType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
public SimMetricsMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(matchBehaviour, new[] { pattern }, simMetricType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
public SimMetricsMatcher(string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns.ToAnyOfPatterns(), simMetricType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
public SimMetricsMatcher(AnyOf<string, StringPattern>[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns, simMetricType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="simMetricType">The SimMetric Type</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
public SimMetricsMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
SimMetricType simMetricType = SimMetricType.Levenstein,
|
||||
MatchOperator matchOperator = MatchOperator.Average)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
_simMetricType = simMetricType;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
IStringMetric stringMetricType = GetStringMetricType();
|
||||
|
||||
var score = MatchScores.ToScore(_patterns.Select(p => stringMetricType.GetSimilarity(p.GetPattern(), input)).ToArray(), MatchOperator);
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{_simMetricType.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
private IStringMetric GetStringMetricType()
|
||||
{
|
||||
return _simMetricType switch
|
||||
{
|
||||
SimMetricType.BlockDistance => new BlockDistance(),
|
||||
SimMetricType.ChapmanLengthDeviation => new ChapmanLengthDeviation(),
|
||||
SimMetricType.CosineSimilarity => new CosineSimilarity(),
|
||||
SimMetricType.DiceSimilarity => new DiceSimilarity(),
|
||||
SimMetricType.EuclideanDistance => new EuclideanDistance(),
|
||||
SimMetricType.JaccardSimilarity => new JaccardSimilarity(),
|
||||
SimMetricType.Jaro => new Jaro(),
|
||||
SimMetricType.JaroWinkler => new JaroWinkler(),
|
||||
SimMetricType.MatchingCoefficient => new MatchingCoefficient(),
|
||||
SimMetricType.MongeElkan => new MongeElkan(),
|
||||
SimMetricType.NeedlemanWunch => new NeedlemanWunch(),
|
||||
SimMetricType.OverlapCoefficient => new OverlapCoefficient(),
|
||||
SimMetricType.QGramsDistance => new QGramsDistance(),
|
||||
SimMetricType.SmithWaterman => new SmithWaterman(),
|
||||
SimMetricType.SmithWatermanGotoh => new SmithWatermanGotoh(),
|
||||
SimMetricType.SmithWatermanGotohWindowedAffine => new SmithWatermanGotohWindowedAffine(),
|
||||
SimMetricType.ChapmanMeanLength => new ChapmanMeanLength(),
|
||||
_ => new Levenstein()
|
||||
};
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; } = MatchOperator.Average;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => $"SimMetricsMatcher.{_simMetricType}";
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// WildcardMatcher
|
||||
/// </summary>
|
||||
/// <seealso cref="RegexMatcher" />
|
||||
public class WildcardMatcher : RegexMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
public WildcardMatcher(AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WildcardMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">IgnoreCase</param>
|
||||
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
|
||||
public WildcardMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
AnyOf<string, StringPattern>[] patterns,
|
||||
bool ignoreCase = false,
|
||||
MatchOperator matchOperator = MatchOperator.Or) : base(matchBehaviour, CreateArray(patterns), ignoreCase, true, matchOperator)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => nameof(WildcardMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
|
||||
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
private static AnyOf<string, StringPattern>[] CreateArray(AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
return patterns
|
||||
.Select(pattern => new AnyOf<string, StringPattern>(
|
||||
new StringPattern
|
||||
{
|
||||
Pattern = "^" + Regex.Escape(pattern.GetPattern()).Replace(@"\*", ".*").Replace(@"\?", ".") + "$",
|
||||
PatternAsFile = pattern.IsSecond ? pattern.Second.PatternAsFile : null
|
||||
}))
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.XPath;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Models;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Util;
|
||||
#if !NETSTANDARD1_3
|
||||
using Wmhelp.XPath2;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
/// XPath2Matcher
|
||||
/// </summary>
|
||||
/// <seealso cref="IStringMatcher" />
|
||||
public class XPathMatcher : IStringMatcher
|
||||
{
|
||||
private readonly AnyOf<string, StringPattern>[] _patterns;
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Array of namespace prefix and uri.
|
||||
/// </summary>
|
||||
public XmlNamespace[]? XmlNamespaceMap { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public XPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, null, patterns)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="XPathMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||
/// <param name="xmlNamespaceMap">The xml namespaces of the xml document.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
public XPathMatcher(
|
||||
MatchBehaviour matchBehaviour,
|
||||
MatchOperator matchOperator = MatchOperator.Or,
|
||||
XmlNamespace[]? xmlNamespaceMap = null,
|
||||
params AnyOf<string, StringPattern>[] patterns)
|
||||
{
|
||||
_patterns = Guard.NotNull(patterns);
|
||||
XmlNamespaceMap = xmlNamespaceMap;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
MatchOperator = matchOperator;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchResult IsMatch(string? input)
|
||||
{
|
||||
var score = MatchScores.Mismatch;
|
||||
|
||||
if (input == null)
|
||||
{
|
||||
return CreateMatchResult(score);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var xPathEvaluator = new XPathEvaluator();
|
||||
xPathEvaluator.Load(input);
|
||||
|
||||
if (!xPathEvaluator.IsXmlDocumentLoaded)
|
||||
{
|
||||
return CreateMatchResult(score);
|
||||
}
|
||||
|
||||
score = MatchScores.ToScore(xPathEvaluator.Evaluate(_patterns, XmlNamespaceMap), MatchOperator);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
return CreateMatchResult(score, exception);
|
||||
}
|
||||
|
||||
return CreateMatchResult(score);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return _patterns;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MatchOperator MatchOperator { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => nameof(XPathMatcher);
|
||||
|
||||
/// <inheritdoc />
|
||||
public string GetCSharpCodeArguments()
|
||||
{
|
||||
return $"new {Name}" +
|
||||
$"(" +
|
||||
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
|
||||
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
|
||||
$"null, " +
|
||||
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
|
||||
$")";
|
||||
}
|
||||
|
||||
private MatchResult CreateMatchResult(double score, Exception? exception = null)
|
||||
{
|
||||
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
|
||||
}
|
||||
|
||||
private sealed class XPathEvaluator
|
||||
{
|
||||
private XmlDocument? _xmlDocument;
|
||||
private XPathNavigator? _xpathNavigator;
|
||||
|
||||
public bool IsXmlDocumentLoaded => _xmlDocument != null;
|
||||
|
||||
public void Load(string input)
|
||||
{
|
||||
try
|
||||
{
|
||||
_xmlDocument = new XmlDocument { InnerXml = input };
|
||||
_xpathNavigator = _xmlDocument.CreateNavigator();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_xmlDocument = default;
|
||||
}
|
||||
}
|
||||
|
||||
public bool[] Evaluate(AnyOf<string, StringPattern>[] patterns, IEnumerable<XmlNamespace>? xmlNamespaceMap)
|
||||
{
|
||||
return _xpathNavigator == null ? [] : patterns.Select(pattern => true.Equals(Evaluate(_xpathNavigator, pattern, xmlNamespaceMap))).ToArray();
|
||||
}
|
||||
|
||||
private object Evaluate(XPathNavigator navigator, AnyOf<string, StringPattern> pattern, IEnumerable<XmlNamespace>? xmlNamespaceMap)
|
||||
{
|
||||
var xpath = $"boolean({pattern.GetPattern()})";
|
||||
|
||||
var xmlNamespaceManager = GetXmlNamespaceManager(xmlNamespaceMap);
|
||||
if (xmlNamespaceManager == null)
|
||||
{
|
||||
#if NETSTANDARD1_3
|
||||
return navigator.Evaluate(xpath);
|
||||
#else
|
||||
return navigator.XPath2Evaluate(xpath);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if NETSTANDARD1_3
|
||||
return navigator.Evaluate(xpath, xmlNamespaceManager);
|
||||
#else
|
||||
return navigator.XPath2Evaluate(xpath, xmlNamespaceManager);
|
||||
#endif
|
||||
}
|
||||
|
||||
private XmlNamespaceManager? GetXmlNamespaceManager(IEnumerable<XmlNamespace>? xmlNamespaceMap)
|
||||
{
|
||||
if (_xpathNavigator == null || xmlNamespaceMap == null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var nsManager = new XmlNamespaceManager(_xpathNavigator.NameTable);
|
||||
foreach (var xmlNamespace in xmlNamespaceMap)
|
||||
{
|
||||
nsManager.AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri);
|
||||
}
|
||||
|
||||
return nsManager;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Threading;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <inheritdoc />
|
||||
internal class BlockingQueue<T>(TimeSpan? readTimeout = null) : IBlockingQueue<T>
|
||||
{
|
||||
private readonly TimeSpan _readTimeout = readTimeout ?? TimeSpan.FromHours(1);
|
||||
private readonly Queue<T?> _queue = new();
|
||||
private readonly object _lockObject = new();
|
||||
|
||||
private bool _isClosed;
|
||||
|
||||
/// <summary>
|
||||
/// Writes an item to the queue and signals that an item is available.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to be added to the queue.</param>
|
||||
public void Write(T item)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
if (_isClosed)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot write to a closed queue.");
|
||||
}
|
||||
|
||||
_queue.Enqueue(item);
|
||||
|
||||
// Signal that an item is available
|
||||
Monitor.Pulse(_lockObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to read an item from the queue.
|
||||
/// - waits until an item is available
|
||||
/// - or the timeout occurs
|
||||
/// - or queue is closed
|
||||
/// </summary>
|
||||
/// <param name="item">The item read from the queue, or default if the timeout occurs.</param>
|
||||
/// <returns>True if an item was successfully read; otherwise, false.</returns>
|
||||
public bool TryRead([NotNullWhen(true)] out T? item)
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
// Wait until an item is available or timeout occurs
|
||||
while (_queue.Count == 0 && !_isClosed)
|
||||
{
|
||||
// Wait with timeout
|
||||
if (!Monitor.Wait(_lockObject, _readTimeout))
|
||||
{
|
||||
item = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// After waiting, check if we have items
|
||||
if (_queue.Count == 0)
|
||||
{
|
||||
item = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
item = _queue.Dequeue();
|
||||
return item != null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the queue and signals all waiting threads.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
lock (_lockObject)
|
||||
{
|
||||
_isClosed = true;
|
||||
Monitor.PulseAll(_lockObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.Util;
|
||||
|
||||
/// <summary>
|
||||
/// BodyData
|
||||
/// </summary>
|
||||
public class BodyData : IBodyData
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Encoding? Encoding { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? BodyAsString { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? BodyAsFile { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public BodyType? DetectedBodyType { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public BodyType? DetectedBodyTypeFromContentType { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? DetectedCompression { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? IsFuncUsed { get; set; }
|
||||
|
||||
#region ProtoBuf
|
||||
/// <inheritdoc />
|
||||
public Func<IdOrTexts>? ProtoDefinition { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? ProtoBufMessageType { get; set; }
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBlockingQueue<string?>? SseStringQueue { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task? BodyAsSseStringTask { get; set; }
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AnyOfTypes;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// GraphQLSchemaDetails
|
||||
/// </summary>
|
||||
public class GraphQLSchemaDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// The GraphQL schema as a string.
|
||||
/// </summary>
|
||||
public string? SchemaAsString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GraphQL schema as a StringPattern.
|
||||
/// </summary>
|
||||
public StringPattern? SchemaAsStringPattern { get; set; }
|
||||
|
||||
#if GRAPHQL
|
||||
/// <summary>
|
||||
/// The GraphQL schema as a <seealso cref="GraphQL.Types.ISchema"/>.
|
||||
/// </summary>
|
||||
public GraphQL.Types.ISchema? SchemaAsISchema { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The GraphQL Schema.
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public AnyOf<string, StringPattern, GraphQL.Types.ISchema>? Schema
|
||||
{
|
||||
get
|
||||
{
|
||||
if (SchemaAsString != null)
|
||||
{
|
||||
return SchemaAsString;
|
||||
}
|
||||
|
||||
if (SchemaAsStringPattern != null)
|
||||
{
|
||||
return SchemaAsStringPattern;
|
||||
}
|
||||
|
||||
if (SchemaAsISchema != null)
|
||||
{
|
||||
return new AnyOf<string, StringPattern, GraphQL.Types.ISchema>(SchemaAsISchema);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The custom Scalars to define for this schema.
|
||||
/// </summary>
|
||||
public IDictionary<string, Type>? CustomScalars { get; set; }
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// A placeholder class for Proto Definitions.
|
||||
/// </summary>
|
||||
public class ProtoDefinitionData
|
||||
{
|
||||
private readonly IDictionary<string, string> _filenameMappedToProtoDefinition;
|
||||
|
||||
internal ProtoDefinitionData(IDictionary<string, string> filenameMappedToProtoDefinition)
|
||||
{
|
||||
_filenameMappedToProtoDefinition = filenameMappedToProtoDefinition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get all the ProtoDefinitions.
|
||||
/// Note: the main ProtoDefinition will be the first one in the list.
|
||||
/// </summary>
|
||||
/// <param name="mainProtoFilename">The main ProtoDefinition filename.</param>
|
||||
public IReadOnlyList<string> ToList(string mainProtoFilename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(mainProtoFilename);
|
||||
|
||||
if (!_filenameMappedToProtoDefinition.TryGetValue(mainProtoFilename, out var mainProtoDefinition))
|
||||
{
|
||||
throw new KeyNotFoundException($"The ProtoDefinition with filename '{mainProtoFilename}' was not found.");
|
||||
}
|
||||
|
||||
var list = new List<string> { mainProtoDefinition };
|
||||
list.AddRange(_filenameMappedToProtoDefinition.Where(kvp => kvp.Key != mainProtoFilename).Select(kvp => kvp.Value));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// StringPattern which defines the Pattern as a string, and optionally the filepath pattern file.
|
||||
/// </summary>
|
||||
public struct StringPattern
|
||||
{
|
||||
/// <summary>
|
||||
/// The pattern as string.
|
||||
/// </summary>
|
||||
public string Pattern { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The filepath (optionally)
|
||||
/// </summary>
|
||||
public string? PatternAsFile { get; set; }
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// TimeSettingsModel: Start, End and TTL
|
||||
/// </summary>
|
||||
public class TimeSettings : ITimeSettings
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DateTime? Start { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? End { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? TTL { get; set; }
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// UrlDetails
|
||||
/// </summary>
|
||||
public class UrlDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the url (relative).
|
||||
/// </summary>
|
||||
public Uri Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AbsoluteUrl.
|
||||
/// </summary>
|
||||
public Uri AbsoluteUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public UrlDetails(string url) : this(new Uri(url))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public UrlDetails(Uri url) : this(url, url)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="absoluteUrl">The absolute URL.</param>
|
||||
/// <param name="url">The URL (relative).</param>
|
||||
public UrlDetails(Uri absoluteUrl, Uri url)
|
||||
{
|
||||
AbsoluteUrl = Guard.NotNull(absoluteUrl);
|
||||
Url = Guard.NotNull(url);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Webhook
|
||||
/// </summary>
|
||||
public class Webhook : IWebhook
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IWebhookRequest Request { get; set; } = null!;
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// WebhookRequest
|
||||
/// </summary>
|
||||
public class WebhookRequest : IWebhookRequest
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Url { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Method { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, WireMockList<string>>? Headers { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IBodyData? BodyData { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? UseTransformer { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TransformerType TransformerType { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if NETCOREAPP3_1 || NET5_0_OR_GREATER
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal partial class AspNetCoreSelfHost
|
||||
{
|
||||
public void AddCors(IServiceCollection services)
|
||||
{
|
||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions > CorsPolicyOptions.None)
|
||||
{
|
||||
/* https://stackoverflow.com/questions/31942037/how-to-enable-cors-in-asp-net-core */
|
||||
/* Enable Cors */
|
||||
services.AddCors(corsOptions => corsOptions
|
||||
.AddPolicy(CorsPolicyName,
|
||||
corsPolicyBuilder =>
|
||||
{
|
||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyHeader))
|
||||
{
|
||||
corsPolicyBuilder.AllowAnyHeader();
|
||||
}
|
||||
|
||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyMethod))
|
||||
{
|
||||
corsPolicyBuilder.AllowAnyMethod();
|
||||
}
|
||||
|
||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions.Value.HasFlag(CorsPolicyOptions.AllowAnyOrigin))
|
||||
{
|
||||
corsPolicyBuilder.AllowAnyOrigin();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
public void UseCors(IApplicationBuilder appBuilder)
|
||||
{
|
||||
if (_wireMockMiddlewareOptions.CorsPolicyOptions > CorsPolicyOptions.None)
|
||||
{
|
||||
/* Use Cors */
|
||||
appBuilder.UseCors(CorsPolicyName);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,123 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if USE_ASPNETCORE && !NETSTANDARD1_3
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using CertificateLoader = WireMock.HttpsCertificate.CertificateLoader;
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
internal partial class AspNetCoreSelfHost
|
||||
{
|
||||
private static void SetKestrelOptionsLimits(KestrelServerOptions options)
|
||||
{
|
||||
options.Limits.MaxRequestBodySize = null; // https://stackoverflow.com/questions/46738364/increase-upload-request-length-limit-in-kestrel
|
||||
options.Limits.MaxRequestBufferSize = null;
|
||||
options.Limits.MaxRequestHeaderCount = 100;
|
||||
options.Limits.MaxResponseBufferSize = null;
|
||||
}
|
||||
|
||||
private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
|
||||
{
|
||||
foreach (var urlDetail in urlDetails)
|
||||
{
|
||||
if (urlDetail.IsHttps)
|
||||
{
|
||||
Listen(kestrelOptions, urlDetail, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(options =>
|
||||
{
|
||||
if (wireMockMiddlewareOptions.CustomCertificateDefined)
|
||||
{
|
||||
options.ServerCertificate = CertificateLoader.LoadCertificate(
|
||||
wireMockMiddlewareOptions.X509StoreName,
|
||||
wireMockMiddlewareOptions.X509StoreLocation,
|
||||
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
|
||||
wireMockMiddlewareOptions.X509CertificateFilePath,
|
||||
wireMockMiddlewareOptions.X509CertificatePassword,
|
||||
urlDetail.Host
|
||||
);
|
||||
}
|
||||
|
||||
options.ClientCertificateMode = (ClientCertificateMode)wireMockMiddlewareOptions.ClientCertificateMode;
|
||||
if (wireMockMiddlewareOptions.AcceptAnyClientCertificate)
|
||||
{
|
||||
options.ClientCertificateValidation = (_, _, _) => true;
|
||||
}
|
||||
});
|
||||
|
||||
if (urlDetail.IsHttp2)
|
||||
{
|
||||
listenOptions.Protocols = HttpProtocols.Http2;
|
||||
}
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
if (urlDetail.IsHttp2)
|
||||
{
|
||||
Listen(kestrelOptions, urlDetail, listenOptions =>
|
||||
{
|
||||
listenOptions.Protocols = HttpProtocols.Http2;
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
Listen(kestrelOptions, urlDetail, _ => { });
|
||||
}
|
||||
}
|
||||
|
||||
private static void Listen(KestrelServerOptions kestrelOptions, HostUrlDetails urlDetail, Action<ListenOptions> configure)
|
||||
{
|
||||
// Listens on any IP with the given port.
|
||||
if (urlDetail is { Port: > 0, Host: "0.0.0.0" })
|
||||
{
|
||||
kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
|
||||
return;
|
||||
}
|
||||
|
||||
// Listens on ::1 and 127.0.0.1 with the given port.
|
||||
if (urlDetail is { Port: > 0, Host: "localhost" or "127.0.0.1" or "::1" })
|
||||
{
|
||||
kestrelOptions.ListenLocalhost(urlDetail.Port, configure);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to parse the host as a valid IP address and bind to the given IP address and port.
|
||||
if (IPAddress.TryParse(urlDetail.Host, out var ipAddress))
|
||||
{
|
||||
kestrelOptions.Listen(ipAddress, urlDetail.Port, configure);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise, listen on all IPs.
|
||||
kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
|
||||
}
|
||||
}
|
||||
|
||||
internal static class IWebHostBuilderExtensions
|
||||
{
|
||||
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder)
|
||||
{
|
||||
return builder.ConfigureAppConfiguration(config =>
|
||||
{
|
||||
config.AddEnvironmentVariables();
|
||||
});
|
||||
}
|
||||
|
||||
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
|
||||
{
|
||||
return builder.ConfigureServices((context, services) =>
|
||||
{
|
||||
services.Configure<KestrelServerOptions>(context.Configuration.GetSection("Kestrel"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user