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:
Stef Heyenrath
2025-05-24 12:17:42 +02:00
committed by GitHub
parent c15206ecd8
commit 96eca4262a
306 changed files with 9746 additions and 9285 deletions

View File

@@ -1,17 +0,0 @@
{
"help": "https://go.microsoft.com/fwlink/?linkid=866610",
"root": true,
"dependentFileProviders": {
"add": {
"addedExtension": {},
"pathSegment": {
"add": {
".*": [
".cs"
]
}
}
}
}
}

View File

@@ -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

View File

@@ -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)) + "$";
}
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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";
}

View File

@@ -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)
{
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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}";
}
}

View File

@@ -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
};
}
}

View File

@@ -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('-', '_');
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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";
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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!;
}

View File

@@ -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}";
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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)}" +
$")";
}
}

View File

@@ -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)}" +
$")";
}
}

View File

@@ -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";
}
}

View File

@@ -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()}" +
$")";
}
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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
{
}

View File

@@ -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);
}

View File

@@ -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; }
}

View File

@@ -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
{
}

View File

@@ -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();
}

View File

@@ -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);
}

View File

@@ -1,10 +0,0 @@
// Copyright © WireMock.Net
namespace WireMock.Matchers;
/// <summary>
/// IProtoBufMatcher
/// </summary>
public interface IProtoBufMatcher : IDecodeBytesMatcher, IBytesMatcher
{
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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)}" +
$")";
}
}

View File

@@ -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();
}
}

View File

@@ -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)}" +
$")";
}
}

View File

@@ -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)}" +
$")";
}
}

View File

@@ -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)}" +
$")";
}
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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()
};
}
}

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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()}" +
$")";
}
}

View File

@@ -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

View File

@@ -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()}" +
$")";
}
}

View File

@@ -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
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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]));
}
}

View File

@@ -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
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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.

View File

@@ -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}";
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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;
}
}

View File

@@ -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; }
}

View File

@@ -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; }
}

View File

@@ -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);
}
}

View File

@@ -1,12 +0,0 @@
// Copyright © WireMock.Net
namespace WireMock.Models;
/// <summary>
/// Webhook
/// </summary>
public class Webhook : IWebhook
{
/// <inheritdoc />
public IWebhookRequest Request { get; set; } = null!;
}

View File

@@ -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; }
}

View File

@@ -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

View File

@@ -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