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

@@ -40,6 +40,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
| | Official | Preview [:information_source:](https://github.com/wiremock/WireMock.Net/wiki/MyGet-preview-versions) | | | Official | Preview [:information_source:](https://github.com/wiremock/WireMock.Net/wiki/MyGet-preview-versions) |
| - | - | - | | - | - | - |
| &nbsp;&nbsp;**WireMock.Net** | [![NuGet Badge WireMock.Net](https://img.shields.io/nuget/v/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | [![MyGet Badge WireMock.Net](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net) | &nbsp;&nbsp;**WireMock.Net** | [![NuGet Badge WireMock.Net](https://img.shields.io/nuget/v/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | [![MyGet Badge WireMock.Net](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net)
| &nbsp;&nbsp;**WireMock.Net.Minimal** 🔺| [![NuGet Badge WireMock.Net.Minimal](https://img.shields.io/nuget/v/WireMock.Net.Minimal)](https://www.nuget.org/packages/WireMock.Net.Minimal) | [![MyGet Badge WireMock.Net](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Minimal?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Minimal)
| &nbsp;&nbsp;**WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net](https://img.shields.io/nuget/v/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) | [![MyGet Badge WireMock.Net.StandAlone](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.StandAlone?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.StandAlone) | &nbsp;&nbsp;**WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net](https://img.shields.io/nuget/v/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) | [![MyGet Badge WireMock.Net.StandAlone](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.StandAlone?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.StandAlone)
| &nbsp;&nbsp;**WireMock.Net.Testcontainers** | [![NuGet Badge WireMock.Net.Testcontainers](https://img.shields.io/nuget/v/WireMock.Net.Testcontainers)](https://www.nuget.org/packages/WireMock.Net.Testcontainers) | [![MyGet Badge WireMock.Net.Testcontainers](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Testcontainers?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Testcontainers) | &nbsp;&nbsp;**WireMock.Net.Testcontainers** | [![NuGet Badge WireMock.Net.Testcontainers](https://img.shields.io/nuget/v/WireMock.Net.Testcontainers)](https://www.nuget.org/packages/WireMock.Net.Testcontainers) | [![MyGet Badge WireMock.Net.Testcontainers](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Testcontainers?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Testcontainers)
| &nbsp;&nbsp;**WireMock.Net.Aspire** | [![NuGet Badge WireMock.Net.Aspire](https://img.shields.io/nuget/v/WireMock.Net.Aspire)](https://www.nuget.org/packages/WireMock.Net.Aspire) | [![MyGet Badge WireMock.Net.Aspire](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Aspire?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Aspire) | &nbsp;&nbsp;**WireMock.Net.Aspire** | [![NuGet Badge WireMock.Net.Aspire](https://img.shields.io/nuget/v/WireMock.Net.Aspire)](https://www.nuget.org/packages/WireMock.Net.Aspire) | [![MyGet Badge WireMock.Net.Aspire](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Aspire?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Aspire)
@@ -52,10 +53,15 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
| | | | | | | |
| &nbsp;&nbsp;**WireMock.Net.Matchers.CSharpCode** | [![NuGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/nuget/v/WireMock.Net.Matchers.CSharpCode)](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [![MyGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Matchers.CSharpCode?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode) | &nbsp;&nbsp;**WireMock.Net.Matchers.CSharpCode** | [![NuGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/nuget/v/WireMock.Net.Matchers.CSharpCode)](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [![MyGet Badge WireMock.Net.Matchers.CSharpCode](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.Matchers.CSharpCode?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
| &nbsp;&nbsp;**WireMock.Net.OpenApiParser** | [![NuGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/nuget/v/WireMock.Net.OpenApiParser)](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [![MyGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.OpenApiParser?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser) | &nbsp;&nbsp;**WireMock.Net.OpenApiParser** | [![NuGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/nuget/v/WireMock.Net.OpenApiParser)](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [![MyGet Badge WireMock.Net.OpenApiParser](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.OpenApiParser?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
| &nbsp;&nbsp;**WireMock.Net.MimePart** | [![NuGet Badge WireMock.Net.MimePart](https://img.shields.io/nuget/v/WireMock.Net.MimePart)](https://www.nuget.org/packages/WireMock.Net.MimePart) | [![MyGet Badge WireMock.Net.MimePart](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.MimePart?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart)
| | | | | | | |
| &nbsp;&nbsp;**WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://img.shields.io/nuget/v/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient) | &nbsp;&nbsp;**WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://img.shields.io/nuget/v/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Net.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
| &nbsp;&nbsp;**WireMock.Org.RestClient** | [![NuGet Badge WireMock.Org.RestClient](https://img.shields.io/nuget/v/WireMock.Org.RestClient)](https://www.nuget.org/packages/WireMock.Org.RestClient) | [![MyGet Badge WireMock.Org.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Org.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient) | &nbsp;&nbsp;**WireMock.Org.RestClient** | [![NuGet Badge WireMock.Org.RestClient](https://img.shields.io/nuget/v/WireMock.Org.RestClient)](https://www.nuget.org/packages/WireMock.Org.RestClient) | [![MyGet Badge WireMock.Org.RestClient](https://img.shields.io/myget/wiremock-net/vpre/WireMock.Org.RestClient?includePreReleases=true&label=MyGet)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
<br />
🔺 **WireMock.Net.Minimal** does not include: **WireMock.Net.MimePart**
--- ---
## :exclamation: Breaking changes ## :exclamation: Breaking changes
@@ -65,6 +71,7 @@ A breaking change is introduced which is related to System.Linq.Dynamic.Core Dyn
- The `LinqMatcher` is not allowed. - The `LinqMatcher` is not allowed.
- The [Handlebars.Net.Helpers.DynamicLinq](https://www.nuget.org/packages/Handlebars.Net.Helpers.DynamicLinq) package is not included anymore. - The [Handlebars.Net.Helpers.DynamicLinq](https://www.nuget.org/packages/Handlebars.Net.Helpers.DynamicLinq) package is not included anymore.
### 1.8.0 ### 1.8.0
Some breaking changes are introduced in this version: Some breaking changes are introduced in this version:

View File

@@ -130,6 +130,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.AwesomeAsserti
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenApiParser", "src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj", "{E5B03EEF-822C-4295-952B-4479AD30082B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.OpenApiParser", "src\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj", "{E5B03EEF-822C-4295-952B-4479AD30082B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.MimePart", "src\WireMock.Net.MimePart\WireMock.Net.MimePart.csproj", "{F8B4A93E-46EF-4237-88FE-15FDAB7635D4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Shared", "src\WireMock.Net.Shared\WireMock.Net.Shared.csproj", "{D3804228-91F4-4502-9595-39584E5A0177}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Minimal", "src\WireMock.Net.Minimal\WireMock.Net.Minimal.csproj", "{BFEF8990-65B3-4274-310F-7355F0B84035}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -308,6 +314,18 @@ Global
{E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|Any CPU.Build.0 = Debug|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E5B03EEF-822C-4295-952B-4479AD30082B}.Release|Any CPU.ActiveCfg = Release|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E5B03EEF-822C-4295-952B-4479AD30082B}.Release|Any CPU.Build.0 = Release|Any CPU {E5B03EEF-822C-4295-952B-4479AD30082B}.Release|Any CPU.Build.0 = Release|Any CPU
{F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8B4A93E-46EF-4237-88FE-15FDAB7635D4}.Release|Any CPU.Build.0 = Release|Any CPU
{D3804228-91F4-4502-9595-39584E5A0177}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3804228-91F4-4502-9595-39584E5A0177}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3804228-91F4-4502-9595-39584E5A0177}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3804228-91F4-4502-9595-39584E5A0177}.Release|Any CPU.Build.0 = Release|Any CPU
{BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BFEF8990-65B3-4274-310F-7355F0B84035}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFEF8990-65B3-4274-310F-7355F0B84035}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFEF8990-65B3-4274-310F-7355F0B84035}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -358,6 +376,9 @@ Global
{A5FEF4F7-7DA2-4962-89A8-16BA942886E5} = {0BB8B634-407A-4610-A91F-11586990767A} {A5FEF4F7-7DA2-4962-89A8-16BA942886E5} = {0BB8B634-407A-4610-A91F-11586990767A}
{7753670F-7C7F-44BF-8BC7-08325588E60C} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {7753670F-7C7F-44BF-8BC7-08325588E60C} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{E5B03EEF-822C-4295-952B-4479AD30082B} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {E5B03EEF-822C-4295-952B-4479AD30082B} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{F8B4A93E-46EF-4237-88FE-15FDAB7635D4} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{D3804228-91F4-4502-9595-39584E5A0177} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{BFEF8990-65B3-4274-310F-7355F0B84035} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -339,8 +339,8 @@ namespace WireMock.Net.ConsoleApplication
}); });
System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); System.Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
//server.SetBasicAuthentication("a", "b"); server.SetBasicAuthentication("a", "b");
server.SetAzureADAuthentication(Environment.GetEnvironmentVariable("WIREMOCK_AAD_TENANT")!, "api://e083d51a-01a6-446c-8ad5-0c5c7f002208"); //server.SetAzureADAuthentication(Environment.GetEnvironmentVariable("WIREMOCK_AAD_TENANT")!, "api://e083d51a-01a6-446c-8ad5-0c5c7f002208");
//var http = new HttpClient(); //var http = new HttpClient();
//var response = await http.GetAsync($"{_wireMockServer.Url}/pricing"); //var response = await http.GetAsync($"{_wireMockServer.Url}/pricing");

View File

@@ -0,0 +1,8 @@
// Copyright © WireMock.Net
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
// Needed for Moq in the UnitTest project
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Commonly used models, enumerations and types.</Description> <Description>Commonly used interfaces, models, enumerations and types.</Description>
<AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle> <AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks> <TargetFrameworks>net45;net451;net461;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
@@ -17,7 +17,6 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>--> <!--<DelaySign>true</DelaySign>-->
@@ -25,6 +24,10 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>--> <!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
@@ -35,37 +38,46 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<!-- CVE-2018-8292 -->
<PackageReference Include="System.Net.Http " Version="4.3.4" />
<!-- See also https://mstack.nl/blog/20210801-source-generators --> <!-- See also https://mstack.nl/blog/20210801-source-generators -->
<PackageReference Include="FluentBuilder" Version="0.10.0"> <PackageReference Include="FluentBuilder" Version="0.10.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="PolySharp" Version="1.15.0">
<PackageReference Include="PolySharp" Version="1.14.1"> <PrivateAssets>all</PrivateAssets>
<PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<!--<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<!-- CVE-2018-8292 --> <PackageReference Include="Stef.Validation" Version="0.1.1" />
<PackageReference Include="System.Net.Http " Version="4.3.4" /> <PackageReference Include="AnyOf" Version="0.4.0" />-->
</ItemGroup> </ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetFramework)' != 'netstandard1.0'"> <ItemGroup Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetFramework)' != 'netstandard1.0'">
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.0" /> <PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net461'"> <!--<ItemGroup>
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net46" Version="1.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Nullable" Version="1.3.1"> <PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
</ItemGroup>-->
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net461'">
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
<!--<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>-->
<!--<PackageReference Include="PolySharp" Version="1.14.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>-->
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
@@ -16,7 +16,6 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
@@ -25,6 +24,10 @@
<ApplicationIcon>../../resources/WireMock.Net-LogoAspire.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-LogoAspire.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
@@ -16,7 +16,6 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
@@ -31,9 +30,13 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\WireMock.Net\Util\EnhancedFileSystemWatcher.cs" Link="Utils\EnhancedFileSystemWatcher.cs" /> <Compile Include="..\WireMock.Net.Minimal\Util\EnhancedFileSystemWatcher.cs" Link="Utils\EnhancedFileSystemWatcher.cs" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>

View File

@@ -16,7 +16,6 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>--> <!--<DelaySign>true</DelaySign>-->
@@ -24,6 +23,10 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>FluentAssertions extensions for WireMock.Net</Description> <Description>FluentAssertions extensions for WireMock.Net</Description>
@@ -16,7 +16,6 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly> <SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>--> <!--<DelaySign>true</DelaySign>-->
@@ -24,6 +23,10 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup> </PropertyGroup>

View File

@@ -25,13 +25,13 @@ internal class CSharpCodeMatcher : ICSharpCodeMatcher
private const string TemplateForIsMatchWithDynamic = "public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {0} }} }}"; private const string TemplateForIsMatchWithDynamic = "public class CodeHelper {{ public bool IsMatch(dynamic it) {{ {0} }} }}";
private readonly string[] _usings = private readonly string[] _usings =
{ [
"System", "System",
"System.Linq", "System.Linq",
"System.Collections.Generic", "System.Collections.Generic",
"Microsoft.CSharp", "Microsoft.CSharp",
"Newtonsoft.Json.Linq" "Newtonsoft.Json.Linq"
}; ];
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }

View File

@@ -1,49 +1,52 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>A CSharpCodeMatcher which can be used to match WireMock.Net Requests using C# code.</Description> <Description>A CSharpCodeMatcher which can be used to match WireMock.Net Requests using C# code.</Description>
<AssemblyTitle>WireMock.Net.Matchers.CSharpCode</AssemblyTitle> <AssemblyTitle>WireMock.Net.Matchers.CSharpCode</AssemblyTitle>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks> <TargetFrameworks>net451;net452;net46;net461;netstandard1.3;netstandard2.0;netstandard2.1;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>wiremock;matchers;matcher;csharp;csharpcode</PackageTags> <PackageTags>wiremock;matchers;matcher;csharp;csharpcode</PackageTags>
<RootNamespace>WireMock</RootNamespace> <RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{B6269AAC-170A-4346-8B9A-444DED3D9A44}</ProjectGuid> <ProjectGuid>{B6269AAC-170A-4346-8B9A-444DED3D9A44}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder> <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType> <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet> <SignAssembly>true</SignAssembly>
<SignAssembly>true</SignAssembly> <AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile> <!--<DelaySign>true</DelaySign>-->
<!--<DelaySign>true</DelaySign>--> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageLicenseExpression>MIT</PackageLicenseExpression> </PropertyGroup>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 --> <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<!-- This is needed else we cannot build net452 in Azure DevOps Pipeline --> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Target Name="CheckIfShouldKillVBCSCompiler" /> </PropertyGroup>
<ItemGroup> <!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 -->
<ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" /> <!-- This is needed else we cannot build net452 in Azure DevOps Pipeline -->
</ItemGroup> <Target Name="CheckIfShouldKillVBCSCompiler" />
<ItemGroup Condition=" '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net452' "> <ItemGroup>
<PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" /> <ProjectReference Include="..\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net46' or '$(TargetFramework)' == 'net461' "> <ItemGroup Condition=" '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net452' ">
<PackageReference Include="CS-Script" Version="3.30.3" /> <PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="3.6.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'"> <ItemGroup Condition=" '$(TargetFramework)' == 'net46' or '$(TargetFramework)' == 'net461' ">
<PackageReference Include="CS-Script" Version="4.8.17" /> <PackageReference Include="CS-Script" Version="3.30.3" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1' or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
<PackageReference Include="CS-Script" Version="4.8.17" />
</ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- See also https://github.com/ravibpatel/ILRepack.Lib.MSBuild.Task/issues/26 -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ILRepacker" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
<ItemGroup>
<InputAssemblies Include="$(OutputPath)WireMock.Net.MimePart.dll" />
<InputAssemblies Include="@(ReferencePathWithRefAssemblies)" Condition="'%(filename)' == 'MimeKitLite'" />
<LibraryPath Include="%(ReferencePathWithRefAssemblies.RelativeDir)" />
</ItemGroup>
<ItemGroup>
<DoNotInternalizeAssemblies Include="WireMock.Net.MimePart" />
</ItemGroup>
<ILRepack
Parallel="true"
Internalize="true"
RenameInternalized="true"
InternalizeExclude="@(DoNotInternalizeAssemblies)"
InputAssemblies="@(InputAssemblies)"
LibraryPath="@(LibraryPath)"
TargetKind="Dll"
KeyFile="../../src/WireMock.Net/WireMock.Net.snk"
OutputFile="$(OutputPath)$(AssemblyName).dll"
/>
</Target>
</Project>

View File

@@ -1,12 +1,8 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
#if MIMEKIT
using System; using System;
using MimeKit; using MimeKit;
using WireMock.Extensions;
using WireMock.Matchers;
using WireMock.Matchers.Helpers; using WireMock.Matchers.Helpers;
using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
@@ -14,31 +10,23 @@ namespace WireMock.Matchers;
/// <summary> /// <summary>
/// MimePartMatcher /// MimePartMatcher
/// </summary> /// </summary>
public class MimePartMatcher : IMatcher public class MimePartMatcher : IMimePartMatcher
{ {
private readonly Func<MimePart, MatchResult>[] _funcs; private readonly Func<MimePart, MatchResult>[] _funcs;
/// <inheritdoc /> /// <inheritdoc />
public string Name => nameof(MimePartMatcher); public string Name => nameof(MimePartMatcher);
/// <summary> /// <inheritdoc />
/// ContentType Matcher (image/png; name=image.png.)
/// </summary>
public IStringMatcher? ContentTypeMatcher { get; } public IStringMatcher? ContentTypeMatcher { get; }
/// <summary> /// <inheritdoc />
/// ContentDisposition Matcher (attachment; filename=image.png)
/// </summary>
public IStringMatcher? ContentDispositionMatcher { get; } public IStringMatcher? ContentDispositionMatcher { get; }
/// <summary> /// <inheritdoc />
/// ContentTransferEncoding Matcher (base64)
/// </summary>
public IStringMatcher? ContentTransferEncodingMatcher { get; } public IStringMatcher? ContentTransferEncodingMatcher { get; }
/// <summary> /// <inheritdoc />
/// Content Matcher
/// </summary>
public IMatcher? ContentMatcher { get; } public IMatcher? ContentMatcher { get; }
/// <inheritdoc /> /// <inheritdoc />
@@ -70,19 +58,15 @@ public class MimePartMatcher : IMatcher
]; ];
} }
/// <summary> /// <inheritdoc />
/// Determines whether the specified MimePart is match. public MatchResult IsMatch(object value)
/// </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; var score = MatchScores.Mismatch;
Exception? exception = null; Exception? exception = null;
try try
{ {
if (Array.TrueForAll(_funcs, func => func(mimePart).IsPerfect())) if (value is MimePart mimePart && Array.TrueForAll(_funcs, func => func(mimePart).IsPerfect()))
{ {
score = MatchScores.Perfect; score = MatchScores.Perfect;
} }
@@ -125,5 +109,4 @@ public class MimePartMatcher : IMatcher
{ {
return contentType?.ToString().Replace("Content-Type: ", string.Empty); return contentType?.ToString().Replace("Content-Type: ", string.Empty);
} }
} }
#endif

View File

@@ -0,0 +1,5 @@
// Copyright © WireMock.Net
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]

View File

@@ -1,7 +1,7 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
#if MIMEKIT
using System; using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -13,9 +13,16 @@ using WireMock.Types;
namespace WireMock.Util; namespace WireMock.Util;
internal static class MimeKitUtils internal class MimeKitUtils : IMimeKitUtils
{ {
public static bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out MimeMessage? mimeMessage) /// <inheritdoc />
public object LoadFromStream(Stream stream)
{
return MimeMessage.Load(Guard.NotNull(stream));
}
/// <inheritdoc />
public bool TryGetMimeMessage(IRequestMessage requestMessage, [NotNullWhen(true)] out object? mimeMessage)
{ {
Guard.NotNull(requestMessage); Guard.NotNull(requestMessage);
@@ -37,7 +44,7 @@ internal static class MimeKitUtils
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]); var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
mimeMessage = MimeMessage.Load(new MemoryStream(fixedBytes)); mimeMessage = LoadFromStream(new MemoryStream(fixedBytes));
return true; return true;
} }
@@ -45,6 +52,19 @@ internal static class MimeKitUtils
return false; return false;
} }
/// <inheritdoc />
public IReadOnlyList<object> GetBodyParts(object mimeMessage)
{
if (mimeMessage is not MimeMessage mm)
{
throw new ArgumentException($"The mimeMessage must be of type {nameof(MimeMessage)}", nameof(mimeMessage));
}
return mm.BodyParts
.OfType<MimePart>()
.ToArray();
}
private static bool StartsWithMultiPart(WireMockList<string> contentTypeHeader) private static bool StartsWithMultiPart(WireMockList<string> contentTypeHeader)
{ {
return contentTypeHeader.Any(ct => ct.TrimStart().StartsWith("multipart/", StringComparison.OrdinalIgnoreCase)); return contentTypeHeader.Any(ct => ct.TrimStart().StartsWith("multipart/", StringComparison.OrdinalIgnoreCase));
@@ -61,5 +81,4 @@ internal static class MimeKitUtils
return result; return result;
} }
} }
#endif

View File

@@ -0,0 +1,63 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>MultiPart Mime support for WireMock.Net using MimeKitLite</Description>
<AssemblyTitle>WireMock.Net.MimePart</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>netstandard2.0;netstandard2.1;net462;net47;net48;net6.0;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>wiremock;matchers;matcher;mime;multipart;mimekit</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{F8B4A93E-46EF-4237-88FE-15FDAB7635D4}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<SignAssembly>true</SignAssembly>
<!--<DelaySign>true</DelaySign>-->
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug - Sonar'">
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="PolySharp" Version="1.15.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Stef.Validation" Version="0.1.1" />
</ItemGroup>
<!--<ItemGroup>
<PackageReference Include="MimeKitLite" Version="4.12.0" />
</ItemGroup>-->
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<PackageReference Include="MimeKitLite" Version="4.12.0" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<PackageReference Include="ILRepack.Lib.MSBuild.Task" Version="2.0.40" PrivateAssets="All" />
<PackageReference Include="MimeKitLite" Version="4.12.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
</ItemGroup>
</Project>

View File

@@ -45,7 +45,7 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return EmptyArray<AnyOf<string, StringPattern>>.Value; return [];
} }
public MatchOperator MatchOperator => MatchOperator.Or; public MatchOperator MatchOperator => MatchOperator.Or;
@@ -57,7 +57,7 @@ internal class AzureADAuthenticationMatcher : IStringMatcher
return MatchScores.Mismatch; return MatchScores.Mismatch;
} }
var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, WireMockConstants.DefaultRegexTimeout); var token = Regex.Replace(input, BearerPrefix, string.Empty, RegexOptions.IgnoreCase, RegexConstants.DefaultTimeout);
try try
{ {

View File

@@ -12,7 +12,7 @@ internal static class StringExtensions
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison) public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
{ {
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None; var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
return Regex.Replace(text, oldValue, newValue, options, WireMockConstants.DefaultRegexTimeout); return Regex.Replace(text, oldValue, newValue, options, RegexConstants.DefaultTimeout);
} }
} }
#endif #endif

View File

@@ -1,13 +1,9 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System;
namespace WireMock.Constants; namespace WireMock.Constants;
internal static class WireMockConstants internal static class WireMockConstants
{ {
internal static readonly TimeSpan DefaultRegexTimeout = TimeSpan.FromSeconds(10);
internal const int AdminPriority = int.MinValue; internal const int AdminPriority = int.MinValue;
internal const int MinPriority = -1_000_000; internal const int MinPriority = -1_000_000;
internal const int ProxyPriority = -2_000_000; internal const int ProxyPriority = -2_000_000;

View File

@@ -61,7 +61,7 @@ internal static class HttpRequestMessageHelper
if (contentLengthHeaderAllowed) if (contentLengthHeaderAllowed)
{ {
// Set Content to empty ByteArray to be able to set the Content-Length on the content in case of a HEAD method. // 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); httpRequestMessage.Content ??= new ByteArrayContent([]);
} }
else else
{ {

View File

@@ -1,122 +1,122 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Stef.Validation; using Stef.Validation;
using WireMock.Models; using WireMock.Models;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Transformers; using WireMock.Transformers;
using WireMock.Transformers.Handlebars; using WireMock.Transformers.Handlebars;
using WireMock.Transformers.Scriban; using WireMock.Transformers.Scriban;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Http; namespace WireMock.Http;
internal class WebhookSender internal class WebhookSender
{ {
private const string ClientIp = "::1"; private const string ClientIp = "::1";
private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond)); private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond));
private readonly WireMockServerSettings _settings; private readonly WireMockServerSettings _settings;
public WebhookSender(WireMockServerSettings settings) public WebhookSender(WireMockServerSettings settings)
{ {
_settings = Guard.NotNull(settings); _settings = Guard.NotNull(settings);
} }
public async Task<HttpResponseMessage> SendAsync( public async Task<HttpResponseMessage> SendAsync(
HttpClient client, HttpClient client,
IMapping mapping, IMapping mapping,
IWebhookRequest webhookRequest, IWebhookRequest webhookRequest,
IRequestMessage originalRequestMessage, IRequestMessage originalRequestMessage,
IResponseMessage originalResponseMessage IResponseMessage originalResponseMessage
) )
{ {
Guard.NotNull(client); Guard.NotNull(client);
Guard.NotNull(mapping); Guard.NotNull(mapping);
Guard.NotNull(webhookRequest); Guard.NotNull(webhookRequest);
Guard.NotNull(originalRequestMessage); Guard.NotNull(originalRequestMessage);
Guard.NotNull(originalResponseMessage); Guard.NotNull(originalResponseMessage);
IBodyData? bodyData; IBodyData? bodyData;
IDictionary<string, WireMockList<string>>? headers; IDictionary<string, WireMockList<string>>? headers;
string requestUrl; string requestUrl;
if (webhookRequest.UseTransformer == true) if (webhookRequest.UseTransformer == true)
{ {
ITransformer transformer; ITransformer transformer;
switch (webhookRequest.TransformerType) switch (webhookRequest.TransformerType)
{ {
case TransformerType.Handlebars: case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(_settings); var factoryHandlebars = new HandlebarsContextFactory(_settings);
transformer = new Transformer(_settings, factoryHandlebars); transformer = new Transformer(_settings, factoryHandlebars);
break; break;
case TransformerType.Scriban: case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid: case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType); var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
transformer = new Transformer(_settings, factoryDotLiquid); transformer = new Transformer(_settings, factoryDotLiquid);
break; break;
default: default:
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported."); throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
} }
bodyData = transformer.TransformBody(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.TransformerReplaceNodeOptions); bodyData = transformer.TransformBody(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.TransformerReplaceNodeOptions);
headers = transformer.TransformHeaders(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Headers); headers = transformer.TransformHeaders(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Headers);
requestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url); requestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url);
mapping.Settings.WebhookSettings?.PostTransform(mapping, requestUrl, bodyData, headers); mapping.Settings.WebhookSettings?.PostTransform(mapping, requestUrl, bodyData, headers);
} }
else else
{ {
bodyData = webhookRequest.BodyData; bodyData = webhookRequest.BodyData;
headers = webhookRequest.Headers; headers = webhookRequest.Headers;
requestUrl = webhookRequest.Url; requestUrl = webhookRequest.Url;
} }
// Create RequestMessage // Create RequestMessage
var requestMessage = new RequestMessage( var requestMessage = new RequestMessage(
new UrlDetails(requestUrl), new UrlDetails(requestUrl),
webhookRequest.Method, webhookRequest.Method,
ClientIp, ClientIp,
bodyData, bodyData,
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray()) headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
) )
{ {
DateTime = DateTime.UtcNow DateTime = DateTime.UtcNow
}; };
// Create HttpRequestMessage // Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, requestUrl); var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, requestUrl);
// Delay (if required) // Delay (if required)
if (TryGetDelay(webhookRequest, out var delay)) if (TryGetDelay(webhookRequest, out var delay))
{ {
await Task.Delay(delay.Value).ConfigureAwait(false); await Task.Delay(delay.Value).ConfigureAwait(false);
} }
// Call the URL // Call the URL
return await client.SendAsync(httpRequestMessage).ConfigureAwait(false); return await client.SendAsync(httpRequestMessage).ConfigureAwait(false);
} }
private static bool TryGetDelay(IWebhookRequest webhookRequest, [NotNullWhen(true)] out int? delay) private static bool TryGetDelay(IWebhookRequest webhookRequest, [NotNullWhen(true)] out int? delay)
{ {
delay = webhookRequest.Delay; delay = webhookRequest.Delay;
var minimumDelay = webhookRequest.MinimumRandomDelay; var minimumDelay = webhookRequest.MinimumRandomDelay;
var maximumDelay = webhookRequest.MaximumRandomDelay; var maximumDelay = webhookRequest.MaximumRandomDelay;
if (minimumDelay is not null && maximumDelay is not null && maximumDelay >= minimumDelay) if (minimumDelay is not null && maximumDelay is not null && maximumDelay >= minimumDelay)
{ {
delay = Random.Value!.Next(minimumDelay.Value, maximumDelay.Value); delay = Random.Value!.Next(minimumDelay.Value, maximumDelay.Value);
return true; return true;
} }
return delay is not null; return delay is not null;
} }
} }

View File

@@ -1,39 +1,39 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
namespace WireMock.Logging; namespace WireMock.Logging;
/// <summary> /// <summary>
/// LogEntry /// LogEntry
/// </summary> /// </summary>
public class LogEntry : ILogEntry public class LogEntry : ILogEntry
{ {
/// <inheritdoc cref="ILogEntry.Guid" /> /// <inheritdoc cref="ILogEntry.Guid" />
public Guid Guid { get; set; } public Guid Guid { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMessage" /> /// <inheritdoc cref="ILogEntry.RequestMessage" />
public IRequestMessage RequestMessage { get; set; } = null!; public IRequestMessage RequestMessage { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.ResponseMessage" /> /// <inheritdoc cref="ILogEntry.ResponseMessage" />
public IResponseMessage ResponseMessage { get; set; } = null!; public IResponseMessage ResponseMessage { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.RequestMatchResult" /> /// <inheritdoc cref="ILogEntry.RequestMatchResult" />
public IRequestMatchResult RequestMatchResult { get; set; } = null!; public IRequestMatchResult RequestMatchResult { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.MappingGuid" /> /// <inheritdoc cref="ILogEntry.MappingGuid" />
public Guid? MappingGuid { get; set; } public Guid? MappingGuid { get; set; }
/// <inheritdoc cref="ILogEntry.MappingTitle" /> /// <inheritdoc cref="ILogEntry.MappingTitle" />
public string? MappingTitle { get; set; } public string? MappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMappingGuid" /> /// <inheritdoc cref="ILogEntry.PartialMappingGuid" />
public Guid? PartialMappingGuid { get; set; } public Guid? PartialMappingGuid { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMappingTitle" /> /// <inheritdoc cref="ILogEntry.PartialMappingTitle" />
public string? PartialMappingTitle { get; set; } public string? PartialMappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMatchResult" /> /// <inheritdoc cref="ILogEntry.PartialMatchResult" />
public IRequestMatchResult PartialMatchResult { get; set; } = null!; public IRequestMatchResult PartialMatchResult { get; set; } = null!;
} }

View File

@@ -1,197 +1,197 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Stef.Validation; using Stef.Validation;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Settings; using WireMock.Settings;
namespace WireMock; namespace WireMock;
/// <summary> /// <summary>
/// The Mapping. /// The Mapping.
/// </summary> /// </summary>
public class Mapping : IMapping public class Mapping : IMapping
{ {
/// <inheritdoc /> /// <inheritdoc />
public Guid Guid { get; } public Guid Guid { get; }
/// <inheritdoc /> /// <inheritdoc />
public DateTime? UpdatedAt { get; set; } public DateTime? UpdatedAt { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public string? Title { get; } public string? Title { get; }
/// <inheritdoc /> /// <inheritdoc />
public string? Description { get; } public string? Description { get; }
/// <inheritdoc /> /// <inheritdoc />
public string? Path { get; set; } public string? Path { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public int Priority { get; } public int Priority { get; }
/// <inheritdoc /> /// <inheritdoc />
public string? Scenario { get; private set; } public string? Scenario { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public string? ExecutionConditionState { get; } public string? ExecutionConditionState { get; }
/// <inheritdoc /> /// <inheritdoc />
public string? NextState { get; } public string? NextState { get; }
/// <inheritdoc /> /// <inheritdoc />
public int? StateTimes { get; } public int? StateTimes { get; }
/// <inheritdoc /> /// <inheritdoc />
public IRequestMatcher RequestMatcher { get; } public IRequestMatcher RequestMatcher { get; }
/// <inheritdoc /> /// <inheritdoc />
public IResponseProvider Provider { get; } public IResponseProvider Provider { get; }
/// <inheritdoc /> /// <inheritdoc />
public WireMockServerSettings Settings { get; } public WireMockServerSettings Settings { get; }
/// <inheritdoc /> /// <inheritdoc />
public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null; public bool IsStartState => Scenario == null || Scenario != null && NextState != null && ExecutionConditionState == null;
/// <inheritdoc /> /// <inheritdoc />
public bool IsAdminInterface => Provider is DynamicResponseProvider or DynamicAsyncResponseProvider or ProxyAsyncResponseProvider; public bool IsAdminInterface => Provider is DynamicResponseProvider or DynamicAsyncResponseProvider or ProxyAsyncResponseProvider;
/// <inheritdoc /> /// <inheritdoc />
public bool IsProxy => Provider is ProxyAsyncResponseProvider; public bool IsProxy => Provider is ProxyAsyncResponseProvider;
/// <inheritdoc /> /// <inheritdoc />
public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider); public bool LogMapping => Provider is not (DynamicResponseProvider or DynamicAsyncResponseProvider);
/// <inheritdoc /> /// <inheritdoc />
public IWebhook[]? Webhooks { get; } public IWebhook[]? Webhooks { get; }
/// <inheritdoc /> /// <inheritdoc />
public bool? UseWebhooksFireAndForget { get; } public bool? UseWebhooksFireAndForget { get; }
/// <inheritdoc /> /// <inheritdoc />
public ITimeSettings? TimeSettings { get; } public ITimeSettings? TimeSettings { get; }
/// <inheritdoc /> /// <inheritdoc />
public object? Data { get; } public object? Data { get; }
/// <inheritdoc /> /// <inheritdoc />
public double? Probability { get; private set; } public double? Probability { get; private set; }
/// <inheritdoc /> /// <inheritdoc />
public IdOrTexts? ProtoDefinition { get; private set; } public IdOrTexts? ProtoDefinition { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class. /// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary> /// </summary>
/// <param name="guid">The unique identifier.</param> /// <param name="guid">The unique identifier.</param>
/// <param name="updatedAt">The datetime when this mapping was created.</param> /// <param name="updatedAt">The datetime when this mapping was created.</param>
/// <param name="title">The unique title (can be null).</param> /// <param name="title">The unique title (can be null).</param>
/// <param name="description">The description (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="path">The full file path from this mapping title (can be null).</param>
/// <param name="settings">The WireMockServerSettings.</param> /// <param name="settings">The WireMockServerSettings.</param>
/// <param name="requestMatcher">The request matcher.</param> /// <param name="requestMatcher">The request matcher.</param>
/// <param name="provider">The provider.</param> /// <param name="provider">The provider.</param>
/// <param name="priority">The priority for this mapping.</param> /// <param name="priority">The priority for this mapping.</param>
/// <param name="scenario">The scenario. [Optional]</param> /// <param name="scenario">The scenario. [Optional]</param>
/// <param name="executionConditionState">State in which the current mapping can occur. [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="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="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="webhooks">The Webhooks. [Optional]</param>
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [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="timeSettings">The TimeSettings. [Optional]</param>
/// <param name="data">The data object. [Optional]</param> /// <param name="data">The data object. [Optional]</param>
public Mapping public Mapping
( (
Guid guid, Guid guid,
DateTime updatedAt, DateTime updatedAt,
string? title, string? title,
string? description, string? description,
string? path, string? path,
WireMockServerSettings settings, WireMockServerSettings settings,
IRequestMatcher requestMatcher, IRequestMatcher requestMatcher,
IResponseProvider provider, IResponseProvider provider,
int priority, int priority,
string? scenario, string? scenario,
string? executionConditionState, string? executionConditionState,
string? nextState, string? nextState,
int? stateTimes, int? stateTimes,
IWebhook[]? webhooks, IWebhook[]? webhooks,
bool? useWebhooksFireAndForget, bool? useWebhooksFireAndForget,
ITimeSettings? timeSettings, ITimeSettings? timeSettings,
object? data object? data
) )
{ {
Guid = guid; Guid = guid;
UpdatedAt = updatedAt; UpdatedAt = updatedAt;
Title = title; Title = title;
Description = description; Description = description;
Path = path; Path = path;
Settings = settings; Settings = settings;
RequestMatcher = requestMatcher; RequestMatcher = requestMatcher;
Provider = provider; Provider = provider;
Priority = priority; Priority = priority;
Scenario = scenario; Scenario = scenario;
ExecutionConditionState = executionConditionState; ExecutionConditionState = executionConditionState;
NextState = nextState; NextState = nextState;
StateTimes = stateTimes; StateTimes = stateTimes;
Webhooks = webhooks; Webhooks = webhooks;
UseWebhooksFireAndForget = useWebhooksFireAndForget; UseWebhooksFireAndForget = useWebhooksFireAndForget;
TimeSettings = timeSettings; TimeSettings = timeSettings;
Data = data; Data = data;
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage) public Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage)
{ {
return Provider.ProvideResponseAsync(this, requestMessage, Settings); return Provider.ProvideResponseAsync(this, requestMessage, Settings);
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState) public IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState)
{ {
var result = new RequestMatchResult(); var result = new RequestMatchResult();
RequestMatcher.GetMatchingScore(requestMessage, result); RequestMatcher.GetMatchingScore(requestMessage, result);
// Only check state if Scenario is defined // Only check state if Scenario is defined
if (Scenario != null) if (Scenario != null)
{ {
var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState); var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
matcher.GetMatchingScore(requestMessage, result); matcher.GetMatchingScore(requestMessage, result);
//// If ExecutionConditionState is null, this means that request is the start from a scenario. So just return. //// If ExecutionConditionState is null, this means that request is the start from a scenario. So just return.
//if (ExecutionConditionState != null) //if (ExecutionConditionState != null)
//{ //{
// // ExecutionConditionState is not null, so get score for matching with the nextState. // // ExecutionConditionState is not null, so get score for matching with the nextState.
// var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState); // var matcher = new RequestMessageScenarioAndStateMatcher(nextState, ExecutionConditionState);
// matcher.GetMatchingScore(requestMessage, result); // matcher.GetMatchingScore(requestMessage, result);
//} //}
} }
return result; return result;
} }
/// <inheritdoc /> /// <inheritdoc />
public IMapping WithProbability(double probability) public IMapping WithProbability(double probability)
{ {
Probability = Guard.NotNull(probability); Probability = Guard.NotNull(probability);
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IMapping WithScenario(string scenario) public IMapping WithScenario(string scenario)
{ {
Scenario = Guard.NotNullOrWhiteSpace(scenario); Scenario = Guard.NotNullOrWhiteSpace(scenario);
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IMapping WithProtoDefinition(IdOrTexts protoDefinition) public IMapping WithProtoDefinition(IdOrTexts protoDefinition)
{ {
ProtoDefinition = protoDefinition; ProtoDefinition = protoDefinition;
return this; return this;
} }
} }

View File

@@ -1,90 +1,90 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Net.Http.Headers; using System.Net.Http.Headers;
using AnyOfTypes; using AnyOfTypes;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// ContentTypeMatcher which accepts also all charsets /// ContentTypeMatcher which accepts also all charsets
/// </summary> /// </summary>
/// <seealso cref="RegexMatcher" /> /// <seealso cref="RegexMatcher" />
public class ContentTypeMatcher : WildcardMatcher public class ContentTypeMatcher : WildcardMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class. /// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary> /// </summary>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param> /// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this([pattern], ignoreCase) public ContentTypeMatcher(AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this([pattern], ignoreCase)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class. /// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param> /// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour, public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, bool ignoreCase = false) : this(matchBehaviour,
[pattern], ignoreCase) [pattern], ignoreCase)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class. /// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param> /// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase) public ContentTypeMatcher(AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class. /// Initializes a new instance of the <see cref="ContentTypeMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">IgnoreCase (default false)</param> /// <param name="ignoreCase">IgnoreCase (default false)</param>
public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : base(matchBehaviour, patterns, ignoreCase) public ContentTypeMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern>[] patterns, bool ignoreCase = false) : base(matchBehaviour, patterns, ignoreCase)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
} }
/// <inheritdoc /> /// <inheritdoc />
public override MatchResult IsMatch(string? input) public override MatchResult IsMatch(string? input)
{ {
if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType)) if (string.IsNullOrEmpty(input) || !MediaTypeHeaderValue.TryParse(input, out var contentType))
{ {
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch); return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.Mismatch);
} }
return base.IsMatch(contentType.MediaType); return base.IsMatch(contentType.MediaType);
} }
/// <inheritdoc /> /// <inheritdoc />
public override AnyOf<string, StringPattern>[] GetPatterns() public override AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public override string Name => nameof(ContentTypeMatcher); public override string Name => nameof(ContentTypeMatcher);
/// <inheritdoc /> /// <inheritdoc />
public override string GetCSharpCodeArguments() public override string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}" + $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}" +
$")"; $")";
} }
} }

View File

@@ -1,107 +1,107 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Linq; using System.Linq;
using AnyOfTypes; using AnyOfTypes;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// ExactMatcher /// ExactMatcher
/// </summary> /// </summary>
/// <seealso cref="IStringMatcher" /> and <seealso cref="IIgnoreCaseMatcher" /> /// <seealso cref="IStringMatcher" /> and <seealso cref="IIgnoreCaseMatcher" />
public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _values; private readonly AnyOf<string, StringPattern>[] _values;
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class. /// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The string value.</param> /// <param name="value">The string value.</param>
public ExactMatcher(MatchBehaviour matchBehaviour, string value) : this(matchBehaviour, true, MatchOperator.Or, new AnyOf<string, StringPattern>(value)) public ExactMatcher(MatchBehaviour matchBehaviour, string value) : this(matchBehaviour, true, MatchOperator.Or, new AnyOf<string, StringPattern>(value))
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class. /// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary> /// </summary>
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, values) public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, values)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class. /// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary> /// </summary>
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param> /// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
public ExactMatcher(bool ignoreCase, params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, ignoreCase, MatchOperator.Or, values) public ExactMatcher(bool ignoreCase, params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, ignoreCase, MatchOperator.Or, values)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ExactMatcher"/> class. /// Initializes a new instance of the <see cref="ExactMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="ignoreCase">Ignore the case from the pattern(s).</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="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="values">The values.</param> /// <param name="values">The values.</param>
public ExactMatcher( public ExactMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
bool ignoreCase = false, bool ignoreCase = false,
MatchOperator matchOperator = MatchOperator.Or, MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] values) params AnyOf<string, StringPattern>[] values)
{ {
_values = Guard.NotNull(values); _values = Guard.NotNull(values);
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase; IgnoreCase = ignoreCase;
MatchOperator = matchOperator; MatchOperator = matchOperator;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
Func<string?, bool> equals = IgnoreCase Func<string?, bool> equals = IgnoreCase
? pattern => string.Equals(pattern, input, StringComparison.OrdinalIgnoreCase) ? pattern => string.Equals(pattern, input, StringComparison.OrdinalIgnoreCase)
: pattern => pattern == input; : pattern => pattern == input;
var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator); var score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score)); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
} }
/// <inheritdoc /> /// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _values; return _values;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } public MatchOperator MatchOperator { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Name => nameof(ExactMatcher); public string Name => nameof(ExactMatcher);
/// <inheritdoc /> /// <inheritdoc />
public bool IgnoreCase { get; } public bool IgnoreCase { get; }
/// <inheritdoc /> /// <inheritdoc />
public string GetCSharpCodeArguments() public string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " + $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_values)}" + $"{MappingConverterUtils.ToCSharpCodeArguments(_values)}" +
$")"; $")";
} }
} }

View File

@@ -1,179 +1,179 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using AnyOfTypes; using AnyOfTypes;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// FormUrl Encoded fields Matcher /// FormUrl Encoded fields Matcher
/// </summary> /// </summary>
/// <inheritdoc cref="IStringMatcher"/> /// <inheritdoc cref="IStringMatcher"/>
/// <inheritdoc cref="IIgnoreCaseMatcher"/> /// <inheritdoc cref="IIgnoreCaseMatcher"/>
public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = []; private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class. /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
/// </summary> /// </summary>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from 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> /// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher( public FormUrlEncodedMatcher(
AnyOf<string, StringPattern> pattern, AnyOf<string, StringPattern> pattern,
bool ignoreCase = false, bool ignoreCase = false,
MatchOperator matchOperator = MatchOperator.Or) : MatchOperator matchOperator = MatchOperator.Or) :
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator) this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class. /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from 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> /// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher( public FormUrlEncodedMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern> pattern, AnyOf<string, StringPattern> pattern,
bool ignoreCase = false, bool ignoreCase = false,
MatchOperator matchOperator = MatchOperator.Or) : MatchOperator matchOperator = MatchOperator.Or) :
this(matchBehaviour, [pattern], ignoreCase, matchOperator) this(matchBehaviour, [pattern], ignoreCase, matchOperator)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class. /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from 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> /// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher( public FormUrlEncodedMatcher(
AnyOf<string, StringPattern>[] patterns, AnyOf<string, StringPattern>[] patterns,
bool ignoreCase = false, bool ignoreCase = false,
MatchOperator matchOperator = MatchOperator.Or) : MatchOperator matchOperator = MatchOperator.Or) :
this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator) this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class. /// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from 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> /// <param name="matchOperator">The <see cref="MatchOperator"/> to use. (default = "Or")</param>
public FormUrlEncodedMatcher( public FormUrlEncodedMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern>[] patterns, AnyOf<string, StringPattern>[] patterns,
bool ignoreCase = false, bool ignoreCase = false,
MatchOperator matchOperator = MatchOperator.Or) MatchOperator matchOperator = MatchOperator.Or)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
IgnoreCase = ignoreCase; IgnoreCase = ignoreCase;
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator; MatchOperator = matchOperator;
foreach (var pattern in _patterns) foreach (var pattern in _patterns)
{ {
if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection)) if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection))
{ {
foreach (var nameValue in nameValueCollection) foreach (var nameValue in nameValueCollection)
{ {
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator); var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator); var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
_pairs.Add((keyMatcher, valueMatcher)); _pairs.Add((keyMatcher, valueMatcher));
} }
} }
} }
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
// Input is null or empty and if no patterns defined, return Perfect match. // Input is null or empty and if no patterns defined, return Perfect match.
if (string.IsNullOrEmpty(input) && _patterns.Length == 0) if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
{ {
return new MatchResult(MatchScores.Perfect); return new MatchResult(MatchScores.Perfect);
} }
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection)) if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
{ {
return new MatchResult(MatchScores.Mismatch); return new MatchResult(MatchScores.Mismatch);
} }
var matches = GetMatches(inputNameValueCollection); var matches = GetMatches(inputNameValueCollection);
var score = MatchScores.ToScore(matches, MatchOperator); var score = MatchScores.ToScore(matches, MatchOperator);
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score)); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
} }
private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection) private bool[] GetMatches(IDictionary<string, string> inputNameValueCollection)
{ {
var matches = new List<bool>(); var matches = new List<bool>();
if (_pairs.Count > inputNameValueCollection.Count) if (_pairs.Count > inputNameValueCollection.Count)
{ {
matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count)); matches.AddRange(Enumerable.Repeat(false, _pairs.Count - inputNameValueCollection.Count));
} }
foreach (var inputKeyValuePair in inputNameValueCollection) foreach (var inputKeyValuePair in inputNameValueCollection)
{ {
var match = false; var match = false;
foreach (var pair in _pairs) foreach (var pair in _pairs)
{ {
var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect(); var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
if (keyMatchResult) if (keyMatchResult)
{ {
match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false; match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
if (match) if (match)
{ {
break; break;
} }
} }
} }
matches.Add(match); matches.Add(match);
} }
return matches.ToArray(); return matches.ToArray();
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual AnyOf<string, StringPattern>[] GetPatterns() public virtual AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual string Name => nameof(FormUrlEncodedMatcher); public virtual string Name => nameof(FormUrlEncodedMatcher);
/// <inheritdoc /> /// <inheritdoc />
public bool IgnoreCase { get; } public bool IgnoreCase { get; }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } public MatchOperator MatchOperator { get; }
/// <inheritdoc /> /// <inheritdoc />
public string GetCSharpCodeArguments() public string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " + $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" + $"{MatchOperator.GetFullyQualifiedEnumValue()}" +
$")"; $")";
} }
} }

View File

@@ -1,167 +1,167 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Linq; using System.Linq;
using AnyOfTypes; using AnyOfTypes;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// JsonPathMatcher /// JsonPathMatcher
/// </summary> /// </summary>
/// <seealso cref="IStringMatcher" /> /// <seealso cref="IStringMatcher" />
/// <seealso cref="IObjectMatcher" /> /// <seealso cref="IObjectMatcher" />
public class JsonPathMatcher : IStringMatcher, IObjectMatcher public class JsonPathMatcher : IStringMatcher, IObjectMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc /> /// <inheritdoc />
public object Value { get; } public object Value { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class. /// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JsonPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, public JsonPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or,
patterns.ToAnyOfPatterns()) patterns.ToAnyOfPatterns())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class. /// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JsonPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, public JsonPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch,
MatchOperator.Or, patterns) MatchOperator.Or, patterns)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonPathMatcher"/> class. /// Initializes a new instance of the <see cref="JsonPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param> /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JsonPathMatcher( public JsonPathMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
MatchOperator matchOperator = MatchOperator.Or, MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns) params AnyOf<string, StringPattern>[] patterns)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator; MatchOperator = matchOperator;
Value = patterns; Value = patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
Exception? exception = null; Exception? exception = null;
if (!string.IsNullOrWhiteSpace(input)) if (!string.IsNullOrWhiteSpace(input))
{ {
try try
{ {
var jToken = JToken.Parse(input); var jToken = JToken.Parse(input);
score = IsMatch(jToken); score = IsMatch(jToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
exception = ex; exception = ex;
} }
} }
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(object? input) public MatchResult IsMatch(object? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
Exception? exception = null; Exception? exception = null;
// When input is null or byte[], return Mismatch. // When input is null or byte[], return Mismatch.
if (input != null && input is not byte[]) if (input != null && input is not byte[])
{ {
try try
{ {
// Check if JToken or object // Check if JToken or object
JToken jToken = input as JToken ?? JObject.FromObject(input); JToken jToken = input as JToken ?? JObject.FromObject(input);
score = IsMatch(jToken); score = IsMatch(jToken);
} }
catch (Exception ex) catch (Exception ex)
{ {
exception = ex; exception = ex;
} }
} }
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
} }
/// <inheritdoc /> /// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } public MatchOperator MatchOperator { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Name => nameof(JsonPathMatcher); public string Name => nameof(JsonPathMatcher);
/// <inheritdoc /> /// <inheritdoc />
public string GetCSharpCodeArguments() public string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
$")"; $")";
} }
private double IsMatch(JToken jToken) private double IsMatch(JToken jToken)
{ {
var array = ConvertJTokenToJArrayIfNeeded(jToken); var array = ConvertJTokenToJArrayIfNeeded(jToken);
// The SelectToken method can accept a string path to a child token ( i.e. "Manufacturers[0].Products[0].Price"). // 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. // 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(); var values = _patterns.Select(pattern => array.SelectToken(pattern.GetPattern()) != null).ToArray();
return MatchScores.ToScore(values, MatchOperator); return MatchScores.ToScore(values, MatchOperator);
} }
// https://github.com/wiremock/WireMock.Net/issues/965 // https://github.com/wiremock/WireMock.Net/issues/965
// https://stackoverflow.com/questions/66922188/newtonsoft-jsonpath-with-c-sharp-syntax // 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. // 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. // 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) private static JToken ConvertJTokenToJArrayIfNeeded(JToken jToken)
{ {
if (jToken.Count() == 1) if (jToken.Count() == 1)
{ {
var property = jToken.First(); var property = jToken.First();
var item = property.First(); var item = property.First();
if (item is JArray) if (item is JArray)
{ {
return jToken; return jToken;
} }
return new JObject return new JObject
{ {
[property.Path] = new JArray(item) [property.Path] = new JArray(item)
}; };
} }
return jToken; return jToken;
} }
} }

View File

@@ -1,130 +1,130 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Linq; using System.Linq;
using AnyOfTypes; using AnyOfTypes;
using DevLab.JmesPath; using DevLab.JmesPath;
using Newtonsoft.Json; using Newtonsoft.Json;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// http://jmespath.org/ /// http://jmespath.org/
/// </summary> /// </summary>
public class JmesPathMatcher : IStringMatcher, IObjectMatcher public class JmesPathMatcher : IStringMatcher, IObjectMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc /> /// <inheritdoc />
public object Value { get; } public object Value { get; }
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class. /// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns.ToAnyOfPatterns()) public JmesPathMatcher(params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns.ToAnyOfPatterns())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class. /// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns) public JmesPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class. /// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param> /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JmesPathMatcher(MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) : public JmesPathMatcher(MatchOperator matchOperator = MatchOperator.Or, params AnyOf<string, StringPattern>[] patterns) :
this(MatchBehaviour.AcceptOnMatch, matchOperator, patterns) this(MatchBehaviour.AcceptOnMatch, matchOperator, patterns)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JmesPathMatcher"/> class. /// Initializes a new instance of the <see cref="JmesPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param> /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public JmesPathMatcher( public JmesPathMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
MatchOperator matchOperator = MatchOperator.Or, MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns) params AnyOf<string, StringPattern>[] patterns)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator; MatchOperator = matchOperator;
Value = patterns; Value = patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
Exception? exception = null; Exception? exception = null;
if (!string.IsNullOrWhiteSpace(input)) if (!string.IsNullOrWhiteSpace(input))
{ {
try try
{ {
var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray(); var results = _patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern.GetPattern()))).ToArray();
score = MatchScores.ToScore(results, MatchOperator); score = MatchScores.ToScore(results, MatchOperator);
} }
catch (Exception ex) catch (Exception ex)
{ {
exception = ex; exception = ex;
} }
} }
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(object? input) public MatchResult IsMatch(object? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
// When input is null or byte[], return Mismatch. // When input is null or byte[], return Mismatch.
if (input != null && !(input is byte[])) if (input != null && !(input is byte[]))
{ {
var inputAsString = JsonConvert.SerializeObject(input); var inputAsString = JsonConvert.SerializeObject(input);
return IsMatch(inputAsString); return IsMatch(inputAsString);
} }
return MatchBehaviourHelper.Convert(MatchBehaviour, score); return MatchBehaviourHelper.Convert(MatchBehaviour, score);
} }
/// <inheritdoc /> /// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } public MatchOperator MatchOperator { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Name => nameof(JmesPathMatcher); public string Name => nameof(JmesPathMatcher);
/// <inheritdoc /> /// <inheritdoc />
public string GetCSharpCodeArguments() public string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
$")"; $")";
} }
} }

View File

@@ -1,250 +1,250 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Util; using WireMock.Util;
using JsonUtils = WireMock.Util.JsonUtils; using JsonUtils = WireMock.Util.JsonUtils;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// JsonMatcher /// JsonMatcher
/// </summary> /// </summary>
public class JsonMatcher : IJsonMatcher public class JsonMatcher : IJsonMatcher
{ {
/// <inheritdoc /> /// <inheritdoc />
public virtual string Name => nameof(JsonMatcher); public virtual string Name => nameof(JsonMatcher);
/// <inheritdoc /> /// <inheritdoc />
public object Value { get; } public object Value { get; }
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/> /// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
public bool IgnoreCase { get; } public bool IgnoreCase { get; }
/// <summary> /// <summary>
/// Support Regex /// Support Regex
/// </summary> /// </summary>
public bool Regex { get; } public bool Regex { get; }
private readonly JToken _valueAsJToken; private readonly JToken _valueAsJToken;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonMatcher"/> class. /// Initializes a new instance of the <see cref="JsonMatcher"/> class.
/// </summary> /// </summary>
/// <param name="value">The string value to check for equality.</param> /// <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="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param> /// <param name="regex">Support Regex.</param>
public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex) public JsonMatcher(string value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonMatcher"/> class. /// Initializes a new instance of the <see cref="JsonMatcher"/> class.
/// </summary> /// </summary>
/// <param name="value">The object value to check for equality.</param> /// <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="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param> /// <param name="regex">Support Regex.</param>
public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex) public JsonMatcher(object value, bool ignoreCase = false, bool regex = false) : this(MatchBehaviour.AcceptOnMatch, value, ignoreCase, regex)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="JsonMatcher"/> class. /// Initializes a new instance of the <see cref="JsonMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="value">The value to check for equality.</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="ignoreCase">Ignore the case from the PropertyName and PropertyValue (string only).</param>
/// <param name="regex">Support Regex.</param> /// <param name="regex">Support Regex.</param>
public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false) public JsonMatcher(MatchBehaviour matchBehaviour, object value, bool ignoreCase = false, bool regex = false)
{ {
Guard.NotNull(value); Guard.NotNull(value);
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
IgnoreCase = ignoreCase; IgnoreCase = ignoreCase;
Regex = regex; Regex = regex;
Value = value; Value = value;
_valueAsJToken = JsonUtils.ConvertValueToJToken(value); _valueAsJToken = JsonUtils.ConvertValueToJToken(value);
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(object? input) public MatchResult IsMatch(object? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
Exception? error = null; Exception? error = null;
// When input is null or byte[], return Mismatch. // When input is null or byte[], return Mismatch.
if (input != null && input is not byte[]) if (input != null && input is not byte[])
{ {
try try
{ {
var inputAsJToken = JsonUtils.ConvertValueToJToken(input); var inputAsJToken = JsonUtils.ConvertValueToJToken(input);
var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken)); var match = IsMatch(RenameJToken(_valueAsJToken), RenameJToken(inputAsJToken));
score = MatchScores.ToScore(match); score = MatchScores.ToScore(match);
} }
catch (Exception ex) catch (Exception ex)
{ {
error = ex; error = ex;
} }
} }
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual string GetCSharpCodeArguments() public virtual string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " + $"{CSharpFormatter.ConvertToAnonymousObjectDefinition(Value, 3)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " + $"{CSharpFormatter.ToCSharpBooleanLiteral(IgnoreCase)}, " +
$"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" + $"{CSharpFormatter.ToCSharpBooleanLiteral(Regex)}" +
$")"; $")";
} }
/// <summary> /// <summary>
/// Compares the input against the matcher value /// Compares the input against the matcher value
/// </summary> /// </summary>
/// <param name="value">Matcher value</param> /// <param name="value">Matcher value</param>
/// <param name="input">Input value</param> /// <param name="input">Input value</param>
/// <returns></returns> /// <returns></returns>
protected virtual bool IsMatch(JToken value, JToken? input) protected virtual bool IsMatch(JToken value, JToken? input)
{ {
// If equal, return true. // If equal, return true.
if (input == value) if (input == value)
{ {
return true; return true;
} }
// If input is null, return false. // If input is null, return false.
if (input == null) if (input == null)
{ {
return false; return false;
} }
// If using Regex and the value is a string, use the MatchRegex method. // If using Regex and the value is a string, use the MatchRegex method.
if (Regex && value.Type == JTokenType.String) if (Regex && value.Type == JTokenType.String)
{ {
var valueAsString = value.ToString(); var valueAsString = value.ToString();
var (valid, result) = RegexUtils.MatchRegex(valueAsString, input.ToString()); var (valid, result) = RegexUtils.MatchRegex(valueAsString, input.ToString());
if (valid) if (valid)
{ {
return result; 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 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)) 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()); return JToken.DeepEquals(value.ToString().ToUpperInvariant(), input.ToString().ToUpperInvariant());
} }
switch (value.Type) switch (value.Type)
{ {
// If the value is an object, compare all properties. // If the value is an object, compare all properties.
case JTokenType.Object: case JTokenType.Object:
var valueProperties = value.ToObject<Dictionary<string, JToken>>() ?? new Dictionary<string, JToken>(); var valueProperties = value.ToObject<Dictionary<string, JToken>>() ?? new Dictionary<string, JToken>();
var inputProperties = input.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 the number of properties is different, return false.
if (valueProperties.Count != inputProperties.Count) if (valueProperties.Count != inputProperties.Count)
{ {
return false; return false;
} }
// Compare all properties. The input must match all properties of the value. // Compare all properties. The input must match all properties of the value.
foreach (var pair in valueProperties) foreach (var pair in valueProperties)
{ {
if (!IsMatch(pair.Value, inputProperties[pair.Key])) if (!IsMatch(pair.Value, inputProperties[pair.Key]))
{ {
return false; return false;
} }
} }
return true; return true;
// If the value is an array, compare all elements. // If the value is an array, compare all elements.
case JTokenType.Array: case JTokenType.Array:
var valueArray = value.ToObject<JToken[]>() ?? EmptyArray<JToken>.Value; var valueArray = value.ToObject<JToken[]>() ?? [];
var inputArray = input.ToObject<JToken[]>() ?? EmptyArray<JToken>.Value; var inputArray = input.ToObject<JToken[]>() ?? [];
// If the number of elements is different, return false. // If the number of elements is different, return false.
if (valueArray.Length != inputArray.Length) if (valueArray.Length != inputArray.Length)
{ {
return false; return false;
} }
return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any(); return !valueArray.Where((valueToken, index) => !IsMatch(valueToken, inputArray[index])).Any();
default: default:
// Use JToken.DeepEquals() for all other types. // Use JToken.DeepEquals() for all other types.
return JToken.DeepEquals(value, input); return JToken.DeepEquals(value, input);
} }
} }
// https://stackoverflow.com/questions/11679804/json-net-rename-properties // https://stackoverflow.com/questions/11679804/json-net-rename-properties
private JToken RenameJToken(JToken input) private JToken RenameJToken(JToken input)
{ {
if (!IgnoreCase) if (!IgnoreCase)
{ {
return input; return input;
} }
return input switch return input switch
{ {
JProperty property => RenameJProperty(property), JProperty property => RenameJProperty(property),
JArray array => RenameJArray(array), JArray array => RenameJArray(array),
JObject obj => RenameJObject(obj), JObject obj => RenameJObject(obj),
_ => input _ => input
}; };
} }
private JProperty RenameJProperty(JProperty property) private JProperty RenameJProperty(JProperty property)
{ {
if (!IgnoreCase) if (!IgnoreCase)
{ {
return property; return property;
} }
var propertyValue = property.Value; var propertyValue = property.Value;
if (propertyValue.Type == JTokenType.String && !Regex) if (propertyValue.Type == JTokenType.String && !Regex)
{ {
var stringValue = propertyValue.Value<string>()!; var stringValue = propertyValue.Value<string>()!;
propertyValue = ToUpper(stringValue); propertyValue = ToUpper(stringValue);
} }
return new JProperty(ToUpper(property.Name)!, RenameJToken(propertyValue)); return new JProperty(ToUpper(property.Name)!, RenameJToken(propertyValue));
} }
private JArray RenameJArray(JArray array) private JArray RenameJArray(JArray array)
{ {
if (Regex) if (Regex)
{ {
return array; return array;
} }
var renamedValues = array.Select(RenameJToken); var renamedValues = array.Select(RenameJToken);
return new JArray(renamedValues); return new JArray(renamedValues);
} }
private JObject RenameJObject(JObject obj) private JObject RenameJObject(JObject obj)
{ {
var renamedProperties = obj.Properties().Select(RenameJProperty); var renamedProperties = obj.Properties().Select(RenameJProperty);
return new JObject(renamedProperties); return new JObject(renamedProperties);
} }
private static string? ToUpper(string? input) private static string? ToUpper(string? input)
{ {
return input?.ToUpperInvariant(); return input?.ToUpperInvariant();
} }
} }

View File

@@ -1,151 +1,151 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Linq; using System.Linq;
using System.Linq.Dynamic.Core; using System.Linq.Dynamic.Core;
using AnyOfTypes; using AnyOfTypes;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Json; using WireMock.Json;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// System.Linq.Dynamic.Core Expression Matcher /// System.Linq.Dynamic.Core Expression Matcher
/// </summary> /// </summary>
/// <inheritdoc cref="IObjectMatcher"/> /// <inheritdoc cref="IObjectMatcher"/>
/// <inheritdoc cref="IStringMatcher"/> /// <inheritdoc cref="IStringMatcher"/>
public class LinqMatcher : IObjectMatcher, IStringMatcher public class LinqMatcher : IObjectMatcher, IStringMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc /> /// <inheritdoc />
public object Value { get; } public object Value { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LinqMatcher"/> class. /// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary> /// </summary>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
public LinqMatcher(AnyOf<string, StringPattern> pattern) : this(new[] { pattern }) public LinqMatcher(AnyOf<string, StringPattern> pattern) : this(new[] { pattern })
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LinqMatcher"/> class. /// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public LinqMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns) public LinqMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, patterns)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LinqMatcher"/> class. /// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
public LinqMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern) : this(matchBehaviour, MatchOperator.Or, pattern) public LinqMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern) : this(matchBehaviour, MatchOperator.Or, pattern)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LinqMatcher"/> class. /// Initializes a new instance of the <see cref="LinqMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param> /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public LinqMatcher( public LinqMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
MatchOperator matchOperator = MatchOperator.Or, MatchOperator matchOperator = MatchOperator.Or,
params AnyOf<string, StringPattern>[] patterns) params AnyOf<string, StringPattern>[] patterns)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator; MatchOperator = matchOperator;
Value = patterns; Value = patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
Exception? error = null; Exception? error = null;
// Convert a single input string to a Queryable string-list with 1 entry. // Convert a single input string to a Queryable string-list with 1 entry.
IQueryable queryable = new[] { input }.AsQueryable(); IQueryable queryable = new[] { input }.AsQueryable();
try try
{ {
// Use the Any(...) method to check if the result matches // Use the Any(...) method to check if the result matches
score = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator); score = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
} }
catch (Exception e) catch (Exception e)
{ {
error = e; error = e;
} }
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(object? input) public MatchResult IsMatch(object? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
Exception? error = null; Exception? error = null;
JArray jArray; JArray jArray;
try try
{ {
jArray = new JArray { input }; jArray = new JArray { input };
} }
catch catch
{ {
jArray = input == null ? new JArray() : new JArray { JToken.FromObject(input) }; jArray = input == null ? new JArray() : new JArray { JToken.FromObject(input) };
} }
// Convert a single object to a Queryable JObject-list with 1 entry. // Convert a single object to a Queryable JObject-list with 1 entry.
var queryable = jArray.ToDynamicClassArray().AsQueryable(); var queryable = jArray.ToDynamicClassArray().AsQueryable();
try try
{ {
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray(); var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray(); var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
score = MatchScores.ToScore(scores, MatchOperator); score = MatchScores.ToScore(scores, MatchOperator);
} }
catch (Exception e) catch (Exception e)
{ {
error = e; error = e;
} }
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), error);
} }
/// <inheritdoc /> /// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } public MatchOperator MatchOperator { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Name => nameof(LinqMatcher); public string Name => nameof(LinqMatcher);
/// <inheritdoc /> /// <inheritdoc />
public string GetCSharpCodeArguments() public string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
$")"; $")";
} }
} }

View File

@@ -1,189 +1,189 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Stef.Validation; using Stef.Validation;
using WireMock.Matchers.Helpers; using WireMock.Matchers.Helpers;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers.Request; namespace WireMock.Matchers.Request;
/// <summary> /// <summary>
/// The request body matcher. /// The request body matcher.
/// </summary> /// </summary>
public class RequestMessageBodyMatcher : IRequestMatcher public class RequestMessageBodyMatcher : IRequestMatcher
{ {
/// <summary> /// <summary>
/// The body function /// The body function
/// </summary> /// </summary>
public Func<string?, bool>? Func { get; } public Func<string?, bool>? Func { get; }
/// <summary> /// <summary>
/// The body data function for byte[] /// The body data function for byte[]
/// </summary> /// </summary>
public Func<byte[]?, bool>? DataFunc { get; } public Func<byte[]?, bool>? DataFunc { get; }
/// <summary> /// <summary>
/// The body data function for json /// The body data function for json
/// </summary> /// </summary>
public Func<object?, bool>? JsonFunc { get; } public Func<object?, bool>? JsonFunc { get; }
/// <summary> /// <summary>
/// The body data function for BodyData /// The body data function for BodyData
/// </summary> /// </summary>
public Func<IBodyData?, bool>? BodyDataFunc { get; } public Func<IBodyData?, bool>? BodyDataFunc { get; }
/// <summary> /// <summary>
/// The body data function for FormUrlEncoded /// The body data function for FormUrlEncoded
/// </summary> /// </summary>
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; } public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
/// <summary> /// <summary>
/// The matchers. /// The matchers.
/// </summary> /// </summary>
public IMatcher[]? Matchers { get; } public IMatcher[]? Matchers { get; }
/// <summary> /// <summary>
/// The <see cref="MatchOperator"/> /// The <see cref="MatchOperator"/>
/// </summary> /// </summary>
public MatchOperator MatchOperator { get; } = MatchOperator.Or; public MatchOperator MatchOperator { get; } = MatchOperator.Or;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, string body) : public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, string body) :
this(new[] { new WildcardMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray()) this(new[] { new WildcardMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, byte[] body) : public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, byte[] body) :
this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray()) this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, object body) : public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, object body) :
this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray()) this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<string?, bool> func) public RequestMessageBodyMatcher(Func<string?, bool> func)
{ {
Func = Guard.NotNull(func); Func = Guard.NotNull(func);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<byte[]?, bool> func) public RequestMessageBodyMatcher(Func<byte[]?, bool> func)
{ {
DataFunc = Guard.NotNull(func); DataFunc = Guard.NotNull(func);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<object?, bool> func) public RequestMessageBodyMatcher(Func<object?, bool> func)
{ {
JsonFunc = Guard.NotNull(func); JsonFunc = Guard.NotNull(func);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<IBodyData?, bool> func) public RequestMessageBodyMatcher(Func<IBodyData?, bool> func)
{ {
BodyDataFunc = Guard.NotNull(func); BodyDataFunc = Guard.NotNull(func);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func) public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
{ {
FormUrlEncodedFunc = Guard.NotNull(func); FormUrlEncodedFunc = Guard.NotNull(func);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessageBodyMatcher(params IMatcher[] matchers) public RequestMessageBodyMatcher(params IMatcher[] matchers)
{ {
Matchers = Guard.NotNull(matchers); Matchers = Guard.NotNull(matchers);
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param> /// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
public RequestMessageBodyMatcher(MatchOperator matchOperator, params IMatcher[] matchers) public RequestMessageBodyMatcher(MatchOperator matchOperator, params IMatcher[] matchers)
{ {
Matchers = Guard.NotNull(matchers); Matchers = Guard.NotNull(matchers);
MatchOperator = matchOperator; MatchOperator = matchOperator;
} }
/// <inheritdoc /> /// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult) public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{ {
var (score, exception) = CalculateMatchScore(requestMessage).Expand(); var (score, exception) = CalculateMatchScore(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception); return requestMatchResult.AddScore(GetType(), score, exception);
} }
private MatchResult CalculateMatchScore(IRequestMessage requestMessage) private MatchResult CalculateMatchScore(IRequestMessage requestMessage)
{ {
if (Matchers != null && Matchers.Any()) if (Matchers != null && Matchers.Any())
{ {
var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray(); var results = Matchers.Select(matcher => BodyDataMatchScoreCalculator.CalculateMatchScore(requestMessage.BodyData, matcher)).ToArray();
return MatchResult.From(results, MatchOperator); return MatchResult.From(results, MatchOperator);
} }
if (Func != null) if (Func != null)
{ {
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString)); return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
} }
if (FormUrlEncodedFunc != null) if (FormUrlEncodedFunc != null)
{ {
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded)); return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
} }
if (JsonFunc != null) if (JsonFunc != null)
{ {
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson)); return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
} }
if (DataFunc != null) if (DataFunc != null)
{ {
return MatchScores.ToScore(DataFunc(requestMessage.BodyData?.BodyAsBytes)); return MatchScores.ToScore(DataFunc(requestMessage.BodyData?.BodyAsBytes));
} }
if (BodyDataFunc != null) if (BodyDataFunc != null)
{ {
return MatchScores.ToScore(BodyDataFunc(requestMessage.BodyData)); return MatchScores.ToScore(BodyDataFunc(requestMessage.BodyData));
} }
return default; return default;
} }
} }

View File

@@ -1,7 +1,6 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Stef.Validation; using Stef.Validation;
using WireMock.Util; using WireMock.Util;
@@ -13,6 +12,8 @@ namespace WireMock.Matchers.Request;
/// </summary> /// </summary>
public class RequestMessageMultiPartMatcher : IRequestMatcher public class RequestMessageMultiPartMatcher : IRequestMatcher
{ {
private static readonly IMimeKitUtils MimeKitUtils = TypeLoader.LoadStaticInstance<IMimeKitUtils>();
/// <summary> /// <summary>
/// The matchers. /// The matchers.
/// </summary> /// </summary>
@@ -53,9 +54,6 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
/// <inheritdoc /> /// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult) 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; var score = MatchScores.Mismatch;
Exception? exception = null; Exception? exception = null;
@@ -71,12 +69,10 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
try try
{ {
var mimePartMatchers = Matchers.OfType<MimePartMatcher>().ToArray(); foreach (var mimePartMatcher in Matchers.OfType<IMimePartMatcher>().ToArray())
foreach (var mimePartMatcher in Matchers.OfType<MimePartMatcher>().ToArray())
{ {
score = MatchScores.Mismatch; score = MatchScores.Mismatch;
foreach (var mimeBodyPart in message.BodyParts.OfType<MimeKit.MimePart>()) foreach (var mimeBodyPart in MimeKitUtils.GetBodyParts(message))
{ {
var matchResult = mimePartMatcher.IsMatch(mimeBodyPart); var matchResult = mimePartMatcher.IsMatch(mimeBodyPart);
if (matchResult.IsPerfect()) if (matchResult.IsPerfect())
@@ -85,8 +81,8 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
break; break;
} }
} }
if ((MatchOperator == MatchOperator.Or && MatchScores.IsPerfect(score))
|| (MatchOperator == MatchOperator.And && !MatchScores.IsPerfect(score))) if ((MatchOperator == MatchOperator.Or && MatchScores.IsPerfect(score)) || (MatchOperator == MatchOperator.And && !MatchScores.IsPerfect(score)))
{ {
break; break;
} }
@@ -98,6 +94,5 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
} }
return requestMatchResult.AddScore(GetType(), score, exception); return requestMatchResult.AddScore(GetType(), score, exception);
#endif
} }
} }

View File

@@ -1,140 +1,140 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Linq; using System.Linq;
using AnyOfTypes; using AnyOfTypes;
using SimMetrics.Net; using SimMetrics.Net;
using SimMetrics.Net.API; using SimMetrics.Net.API;
using SimMetrics.Net.Metric; using SimMetrics.Net.Metric;
using Stef.Validation; using Stef.Validation;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// SimMetricsMatcher /// SimMetricsMatcher
/// </summary> /// </summary>
/// <seealso cref="IStringMatcher" /> /// <seealso cref="IStringMatcher" />
public class SimMetricsMatcher : IStringMatcher public class SimMetricsMatcher : IStringMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
private readonly SimMetricType _simMetricType; private readonly SimMetricType _simMetricType;
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class. /// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary> /// </summary>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="simMetricType">The SimMetric Type</param> /// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher(AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(new[] { pattern }, simMetricType) public SimMetricsMatcher(AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(new[] { pattern }, simMetricType)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class. /// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="simMetricType">The SimMetric Type</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) public SimMetricsMatcher(MatchBehaviour matchBehaviour, AnyOf<string, StringPattern> pattern, SimMetricType simMetricType = SimMetricType.Levenstein) : this(matchBehaviour, new[] { pattern }, simMetricType)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class. /// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param> /// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher(string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns.ToAnyOfPatterns(), simMetricType) public SimMetricsMatcher(string[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns.ToAnyOfPatterns(), simMetricType)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class. /// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param> /// <param name="simMetricType">The SimMetric Type</param>
public SimMetricsMatcher(AnyOf<string, StringPattern>[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns, simMetricType) public SimMetricsMatcher(AnyOf<string, StringPattern>[] patterns, SimMetricType simMetricType = SimMetricType.Levenstein) : this(MatchBehaviour.AcceptOnMatch, patterns, simMetricType)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class. /// Initializes a new instance of the <see cref="SimMetricsMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="simMetricType">The SimMetric Type</param> /// <param name="simMetricType">The SimMetric Type</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param> /// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
public SimMetricsMatcher( public SimMetricsMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
AnyOf<string, StringPattern>[] patterns, AnyOf<string, StringPattern>[] patterns,
SimMetricType simMetricType = SimMetricType.Levenstein, SimMetricType simMetricType = SimMetricType.Levenstein,
MatchOperator matchOperator = MatchOperator.Average) MatchOperator matchOperator = MatchOperator.Average)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
_simMetricType = simMetricType; _simMetricType = simMetricType;
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator; MatchOperator = matchOperator;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
IStringMetric stringMetricType = GetStringMetricType(); IStringMetric stringMetricType = GetStringMetricType();
var score = MatchScores.ToScore(_patterns.Select(p => stringMetricType.GetSimilarity(p.GetPattern(), input)).ToArray(), MatchOperator); var score = MatchScores.ToScore(_patterns.Select(p => stringMetricType.GetSimilarity(p.GetPattern(), input)).ToArray(), MatchOperator);
return MatchBehaviourHelper.Convert(MatchBehaviour, score); return MatchBehaviourHelper.Convert(MatchBehaviour, score);
} }
/// <inheritdoc /> /// <inheritdoc />
public virtual string GetCSharpCodeArguments() public virtual string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}, " +
$"{_simMetricType.GetFullyQualifiedEnumValue()}, " + $"{_simMetricType.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}" + $"{MatchOperator.GetFullyQualifiedEnumValue()}" +
$")"; $")";
} }
private IStringMetric GetStringMetricType() private IStringMetric GetStringMetricType()
{ {
return _simMetricType switch return _simMetricType switch
{ {
SimMetricType.BlockDistance => new BlockDistance(), SimMetricType.BlockDistance => new BlockDistance(),
SimMetricType.ChapmanLengthDeviation => new ChapmanLengthDeviation(), SimMetricType.ChapmanLengthDeviation => new ChapmanLengthDeviation(),
SimMetricType.CosineSimilarity => new CosineSimilarity(), SimMetricType.CosineSimilarity => new CosineSimilarity(),
SimMetricType.DiceSimilarity => new DiceSimilarity(), SimMetricType.DiceSimilarity => new DiceSimilarity(),
SimMetricType.EuclideanDistance => new EuclideanDistance(), SimMetricType.EuclideanDistance => new EuclideanDistance(),
SimMetricType.JaccardSimilarity => new JaccardSimilarity(), SimMetricType.JaccardSimilarity => new JaccardSimilarity(),
SimMetricType.Jaro => new Jaro(), SimMetricType.Jaro => new Jaro(),
SimMetricType.JaroWinkler => new JaroWinkler(), SimMetricType.JaroWinkler => new JaroWinkler(),
SimMetricType.MatchingCoefficient => new MatchingCoefficient(), SimMetricType.MatchingCoefficient => new MatchingCoefficient(),
SimMetricType.MongeElkan => new MongeElkan(), SimMetricType.MongeElkan => new MongeElkan(),
SimMetricType.NeedlemanWunch => new NeedlemanWunch(), SimMetricType.NeedlemanWunch => new NeedlemanWunch(),
SimMetricType.OverlapCoefficient => new OverlapCoefficient(), SimMetricType.OverlapCoefficient => new OverlapCoefficient(),
SimMetricType.QGramsDistance => new QGramsDistance(), SimMetricType.QGramsDistance => new QGramsDistance(),
SimMetricType.SmithWaterman => new SmithWaterman(), SimMetricType.SmithWaterman => new SmithWaterman(),
SimMetricType.SmithWatermanGotoh => new SmithWatermanGotoh(), SimMetricType.SmithWatermanGotoh => new SmithWatermanGotoh(),
SimMetricType.SmithWatermanGotohWindowedAffine => new SmithWatermanGotohWindowedAffine(), SimMetricType.SmithWatermanGotohWindowedAffine => new SmithWatermanGotohWindowedAffine(),
SimMetricType.ChapmanMeanLength => new ChapmanMeanLength(), SimMetricType.ChapmanMeanLength => new ChapmanMeanLength(),
_ => new Levenstein() _ => new Levenstein()
}; };
} }
/// <inheritdoc /> /// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } = MatchOperator.Average; public MatchOperator MatchOperator { get; } = MatchOperator.Average;
/// <inheritdoc /> /// <inheritdoc />
public string Name => $"SimMetricsMatcher.{_simMetricType}"; public string Name => $"SimMetricsMatcher.{_simMetricType}";
} }

View File

@@ -1,184 +1,184 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
using System.Xml.XPath; using System.Xml.XPath;
using AnyOfTypes; using AnyOfTypes;
using WireMock.Extensions; using WireMock.Extensions;
using WireMock.Models; using WireMock.Models;
using Stef.Validation; using Stef.Validation;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Util; using WireMock.Util;
#if !NETSTANDARD1_3 #if !NETSTANDARD1_3
using Wmhelp.XPath2; using Wmhelp.XPath2;
#endif #endif
namespace WireMock.Matchers; namespace WireMock.Matchers;
/// <summary> /// <summary>
/// XPath2Matcher /// XPath2Matcher
/// </summary> /// </summary>
/// <seealso cref="IStringMatcher" /> /// <seealso cref="IStringMatcher" />
public class XPathMatcher : IStringMatcher public class XPathMatcher : IStringMatcher
{ {
private readonly AnyOf<string, StringPattern>[] _patterns; private readonly AnyOf<string, StringPattern>[] _patterns;
/// <inheritdoc /> /// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <summary> /// <summary>
/// Array of namespace prefix and uri. /// Array of namespace prefix and uri.
/// </summary> /// </summary>
public XmlNamespace[]? XmlNamespaceMap { get; private set; } public XmlNamespace[]? XmlNamespaceMap { get; private set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="XPathMatcher"/> class. /// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public XPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, null, patterns) public XPathMatcher(params AnyOf<string, StringPattern>[] patterns) : this(MatchBehaviour.AcceptOnMatch, MatchOperator.Or, null, patterns)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="XPathMatcher"/> class. /// Initializes a new instance of the <see cref="XPathMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</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="xmlNamespaceMap">The xml namespaces of the xml document.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
public XPathMatcher( public XPathMatcher(
MatchBehaviour matchBehaviour, MatchBehaviour matchBehaviour,
MatchOperator matchOperator = MatchOperator.Or, MatchOperator matchOperator = MatchOperator.Or,
XmlNamespace[]? xmlNamespaceMap = null, XmlNamespace[]? xmlNamespaceMap = null,
params AnyOf<string, StringPattern>[] patterns) params AnyOf<string, StringPattern>[] patterns)
{ {
_patterns = Guard.NotNull(patterns); _patterns = Guard.NotNull(patterns);
XmlNamespaceMap = xmlNamespaceMap; XmlNamespaceMap = xmlNamespaceMap;
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
MatchOperator = matchOperator; MatchOperator = matchOperator;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchResult IsMatch(string? input) public MatchResult IsMatch(string? input)
{ {
var score = MatchScores.Mismatch; var score = MatchScores.Mismatch;
if (input == null) if (input == null)
{ {
return CreateMatchResult(score); return CreateMatchResult(score);
} }
try try
{ {
var xPathEvaluator = new XPathEvaluator(); var xPathEvaluator = new XPathEvaluator();
xPathEvaluator.Load(input); xPathEvaluator.Load(input);
if (!xPathEvaluator.IsXmlDocumentLoaded) if (!xPathEvaluator.IsXmlDocumentLoaded)
{ {
return CreateMatchResult(score); return CreateMatchResult(score);
} }
score = MatchScores.ToScore(xPathEvaluator.Evaluate(_patterns, XmlNamespaceMap), MatchOperator); score = MatchScores.ToScore(xPathEvaluator.Evaluate(_patterns, XmlNamespaceMap), MatchOperator);
} }
catch (Exception exception) catch (Exception exception)
{ {
return CreateMatchResult(score, exception); return CreateMatchResult(score, exception);
} }
return CreateMatchResult(score); return CreateMatchResult(score);
} }
/// <inheritdoc /> /// <inheritdoc />
public AnyOf<string, StringPattern>[] GetPatterns() public AnyOf<string, StringPattern>[] GetPatterns()
{ {
return _patterns; return _patterns;
} }
/// <inheritdoc /> /// <inheritdoc />
public MatchOperator MatchOperator { get; } public MatchOperator MatchOperator { get; }
/// <inheritdoc /> /// <inheritdoc />
public string Name => nameof(XPathMatcher); public string Name => nameof(XPathMatcher);
/// <inheritdoc /> /// <inheritdoc />
public string GetCSharpCodeArguments() public string GetCSharpCodeArguments()
{ {
return $"new {Name}" + return $"new {Name}" +
$"(" + $"(" +
$"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " + $"{MatchBehaviour.GetFullyQualifiedEnumValue()}, " +
$"{MatchOperator.GetFullyQualifiedEnumValue()}, " + $"{MatchOperator.GetFullyQualifiedEnumValue()}, " +
$"null, " + $"null, " +
$"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" + $"{MappingConverterUtils.ToCSharpCodeArguments(_patterns)}" +
$")"; $")";
} }
private MatchResult CreateMatchResult(double score, Exception? exception = null) private MatchResult CreateMatchResult(double score, Exception? exception = null)
{ {
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception); return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score), exception);
} }
private sealed class XPathEvaluator private sealed class XPathEvaluator
{ {
private XmlDocument? _xmlDocument; private XmlDocument? _xmlDocument;
private XPathNavigator? _xpathNavigator; private XPathNavigator? _xpathNavigator;
public bool IsXmlDocumentLoaded => _xmlDocument != null; public bool IsXmlDocumentLoaded => _xmlDocument != null;
public void Load(string input) public void Load(string input)
{ {
try try
{ {
_xmlDocument = new XmlDocument { InnerXml = input }; _xmlDocument = new XmlDocument { InnerXml = input };
_xpathNavigator = _xmlDocument.CreateNavigator(); _xpathNavigator = _xmlDocument.CreateNavigator();
} }
catch catch
{ {
_xmlDocument = default; _xmlDocument = default;
} }
} }
public bool[] Evaluate(AnyOf<string, StringPattern>[] patterns, IEnumerable<XmlNamespace>? xmlNamespaceMap) public bool[] Evaluate(AnyOf<string, StringPattern>[] patterns, IEnumerable<XmlNamespace>? xmlNamespaceMap)
{ {
return _xpathNavigator == null ? [] : patterns.Select(pattern => true.Equals(Evaluate(_xpathNavigator, pattern, xmlNamespaceMap))).ToArray(); 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) private object Evaluate(XPathNavigator navigator, AnyOf<string, StringPattern> pattern, IEnumerable<XmlNamespace>? xmlNamespaceMap)
{ {
var xpath = $"boolean({pattern.GetPattern()})"; var xpath = $"boolean({pattern.GetPattern()})";
var xmlNamespaceManager = GetXmlNamespaceManager(xmlNamespaceMap); var xmlNamespaceManager = GetXmlNamespaceManager(xmlNamespaceMap);
if (xmlNamespaceManager == null) if (xmlNamespaceManager == null)
{ {
#if NETSTANDARD1_3 #if NETSTANDARD1_3
return navigator.Evaluate(xpath); return navigator.Evaluate(xpath);
#else #else
return navigator.XPath2Evaluate(xpath); return navigator.XPath2Evaluate(xpath);
#endif #endif
} }
#if NETSTANDARD1_3 #if NETSTANDARD1_3
return navigator.Evaluate(xpath, xmlNamespaceManager); return navigator.Evaluate(xpath, xmlNamespaceManager);
#else #else
return navigator.XPath2Evaluate(xpath, xmlNamespaceManager); return navigator.XPath2Evaluate(xpath, xmlNamespaceManager);
#endif #endif
} }
private XmlNamespaceManager? GetXmlNamespaceManager(IEnumerable<XmlNamespace>? xmlNamespaceMap) private XmlNamespaceManager? GetXmlNamespaceManager(IEnumerable<XmlNamespace>? xmlNamespaceMap)
{ {
if (_xpathNavigator == null || xmlNamespaceMap == null) if (_xpathNavigator == null || xmlNamespaceMap == null)
{ {
return default; return default;
} }
var nsManager = new XmlNamespaceManager(_xpathNavigator.NameTable); var nsManager = new XmlNamespaceManager(_xpathNavigator.NameTable);
foreach (var xmlNamespace in xmlNamespaceMap) foreach (var xmlNamespace in xmlNamespaceMap)
{ {
nsManager.AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri); nsManager.AddNamespace(xmlNamespace.Prefix, xmlNamespace.Uri);
} }
return nsManager; return nsManager;
} }
} }
} }

View File

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

View File

@@ -1,43 +1,43 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Models; namespace WireMock.Models;
/// <summary> /// <summary>
/// WebhookRequest /// WebhookRequest
/// </summary> /// </summary>
public class WebhookRequest : IWebhookRequest public class WebhookRequest : IWebhookRequest
{ {
/// <inheritdoc /> /// <inheritdoc />
public string Url { get; set; } = null!; public string Url { get; set; } = null!;
/// <inheritdoc /> /// <inheritdoc />
public string Method { get; set; } = null!; public string Method { get; set; } = null!;
/// <inheritdoc /> /// <inheritdoc />
public IDictionary<string, WireMockList<string>>? Headers { get; set; } public IDictionary<string, WireMockList<string>>? Headers { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public IBodyData? BodyData { get; set; } public IBodyData? BodyData { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public bool? UseTransformer { get; set; } public bool? UseTransformer { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public TransformerType TransformerType { get; set; } public TransformerType TransformerType { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; } public ReplaceNodeOptions TransformerReplaceNodeOptions { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public int? Delay { get; set; } public int? Delay { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public int? MinimumRandomDelay { get; set; } public int? MinimumRandomDelay { get; set; }
/// <inheritdoc /> /// <inheritdoc />
public int? MaximumRandomDelay { get; set; } public int? MaximumRandomDelay { get; set; }
} }

View File

@@ -1,123 +1,123 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
#if USE_ASPNETCORE && !NETSTANDARD1_3 #if USE_ASPNETCORE && !NETSTANDARD1_3
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using CertificateLoader = WireMock.HttpsCertificate.CertificateLoader; using CertificateLoader = WireMock.HttpsCertificate.CertificateLoader;
namespace WireMock.Owin namespace WireMock.Owin
{ {
internal partial class AspNetCoreSelfHost internal partial class AspNetCoreSelfHost
{ {
private static void SetKestrelOptionsLimits(KestrelServerOptions options) private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{ {
options.Limits.MaxRequestBodySize = null; // https://stackoverflow.com/questions/46738364/increase-upload-request-length-limit-in-kestrel options.Limits.MaxRequestBodySize = null; // https://stackoverflow.com/questions/46738364/increase-upload-request-length-limit-in-kestrel
options.Limits.MaxRequestBufferSize = null; options.Limits.MaxRequestBufferSize = null;
options.Limits.MaxRequestHeaderCount = 100; options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxResponseBufferSize = null; options.Limits.MaxResponseBufferSize = null;
} }
private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails) private static void SetHttpsAndUrls(KestrelServerOptions kestrelOptions, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
{ {
foreach (var urlDetail in urlDetails) foreach (var urlDetail in urlDetails)
{ {
if (urlDetail.IsHttps) if (urlDetail.IsHttps)
{ {
Listen(kestrelOptions, urlDetail, listenOptions => Listen(kestrelOptions, urlDetail, listenOptions =>
{ {
listenOptions.UseHttps(options => listenOptions.UseHttps(options =>
{ {
if (wireMockMiddlewareOptions.CustomCertificateDefined) if (wireMockMiddlewareOptions.CustomCertificateDefined)
{ {
options.ServerCertificate = CertificateLoader.LoadCertificate( options.ServerCertificate = CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName, wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation, wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName, wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath, wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword, wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host urlDetail.Host
); );
} }
options.ClientCertificateMode = (ClientCertificateMode)wireMockMiddlewareOptions.ClientCertificateMode; options.ClientCertificateMode = (ClientCertificateMode)wireMockMiddlewareOptions.ClientCertificateMode;
if (wireMockMiddlewareOptions.AcceptAnyClientCertificate) if (wireMockMiddlewareOptions.AcceptAnyClientCertificate)
{ {
options.ClientCertificateValidation = (_, _, _) => true; options.ClientCertificateValidation = (_, _, _) => true;
} }
}); });
if (urlDetail.IsHttp2) if (urlDetail.IsHttp2)
{ {
listenOptions.Protocols = HttpProtocols.Http2; listenOptions.Protocols = HttpProtocols.Http2;
} }
}); });
continue; continue;
} }
if (urlDetail.IsHttp2) if (urlDetail.IsHttp2)
{ {
Listen(kestrelOptions, urlDetail, listenOptions => Listen(kestrelOptions, urlDetail, listenOptions =>
{ {
listenOptions.Protocols = HttpProtocols.Http2; listenOptions.Protocols = HttpProtocols.Http2;
}); });
continue; continue;
} }
Listen(kestrelOptions, urlDetail, _ => { }); Listen(kestrelOptions, urlDetail, _ => { });
} }
} }
private static void Listen(KestrelServerOptions kestrelOptions, HostUrlDetails urlDetail, Action<ListenOptions> configure) private static void Listen(KestrelServerOptions kestrelOptions, HostUrlDetails urlDetail, Action<ListenOptions> configure)
{ {
// Listens on any IP with the given port. // Listens on any IP with the given port.
if (urlDetail is { Port: > 0, Host: "0.0.0.0" }) if (urlDetail is { Port: > 0, Host: "0.0.0.0" })
{ {
kestrelOptions.ListenAnyIP(urlDetail.Port, configure); kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
return; return;
} }
// Listens on ::1 and 127.0.0.1 with the given port. // 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" }) if (urlDetail is { Port: > 0, Host: "localhost" or "127.0.0.1" or "::1" })
{ {
kestrelOptions.ListenLocalhost(urlDetail.Port, configure); kestrelOptions.ListenLocalhost(urlDetail.Port, configure);
return; return;
} }
// Try to parse the host as a valid IP address and bind to the given IP address and port. // 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)) if (IPAddress.TryParse(urlDetail.Host, out var ipAddress))
{ {
kestrelOptions.Listen(ipAddress, urlDetail.Port, configure); kestrelOptions.Listen(ipAddress, urlDetail.Port, configure);
return; return;
} }
// Otherwise, listen on all IPs. // Otherwise, listen on all IPs.
kestrelOptions.ListenAnyIP(urlDetail.Port, configure); kestrelOptions.ListenAnyIP(urlDetail.Port, configure);
} }
} }
internal static class IWebHostBuilderExtensions internal static class IWebHostBuilderExtensions
{ {
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder)
{ {
return builder.ConfigureAppConfiguration(config => return builder.ConfigureAppConfiguration(config =>
{ {
config.AddEnvironmentVariables(); config.AddEnvironmentVariables();
}); });
} }
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder) internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
{ {
return builder.ConfigureServices((context, services) => return builder.ConfigureServices((context, services) =>
{ {
services.Configure<KestrelServerOptions>(context.Configuration.GetSection("Kestrel")); services.Configure<KestrelServerOptions>(context.Configuration.GetSection("Kestrel"));
}); });
} }
} }
} }
#endif #endif

View File

@@ -1,67 +1,67 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
#if USE_ASPNETCORE && NETSTANDARD1_3 #if USE_ASPNETCORE && NETSTANDARD1_3
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel; using Microsoft.AspNetCore.Server.Kestrel;
using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using WireMock.HttpsCertificate; using WireMock.HttpsCertificate;
namespace WireMock.Owin; namespace WireMock.Owin;
internal partial class AspNetCoreSelfHost internal partial class AspNetCoreSelfHost
{ {
private static void SetKestrelOptionsLimits(KestrelServerOptions options) private static void SetKestrelOptionsLimits(KestrelServerOptions options)
{ {
options.Limits.MaxRequestBufferSize = null; options.Limits.MaxRequestBufferSize = null;
options.Limits.MaxRequestHeaderCount = 100; options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxResponseBufferSize = null; options.Limits.MaxResponseBufferSize = null;
} }
private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails) private static void SetHttpsAndUrls(KestrelServerOptions options, IWireMockMiddlewareOptions wireMockMiddlewareOptions, IEnumerable<HostUrlDetails> urlDetails)
{ {
foreach (var urlDetail in urlDetails) foreach (var urlDetail in urlDetails)
{ {
if (urlDetail.IsHttps) if (urlDetail.IsHttps)
{ {
options.UseHttps(new HttpsConnectionFilterOptions options.UseHttps(new HttpsConnectionFilterOptions
{ {
ServerCertificate = wireMockMiddlewareOptions.CustomCertificateDefined ServerCertificate = wireMockMiddlewareOptions.CustomCertificateDefined
? CertificateLoader.LoadCertificate( ? CertificateLoader.LoadCertificate(
wireMockMiddlewareOptions.X509StoreName, wireMockMiddlewareOptions.X509StoreName,
wireMockMiddlewareOptions.X509StoreLocation, wireMockMiddlewareOptions.X509StoreLocation,
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName, wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
wireMockMiddlewareOptions.X509CertificateFilePath, wireMockMiddlewareOptions.X509CertificateFilePath,
wireMockMiddlewareOptions.X509CertificatePassword, wireMockMiddlewareOptions.X509CertificatePassword,
urlDetail.Host) urlDetail.Host)
: PublicCertificateHelper.GetX509Certificate2(), : PublicCertificateHelper.GetX509Certificate2(),
ClientCertificateMode = (ClientCertificateMode) wireMockMiddlewareOptions.ClientCertificateMode, ClientCertificateMode = (ClientCertificateMode) wireMockMiddlewareOptions.ClientCertificateMode,
ClientCertificateValidation = wireMockMiddlewareOptions.AcceptAnyClientCertificate ClientCertificateValidation = wireMockMiddlewareOptions.AcceptAnyClientCertificate
? (_, _, _) => true ? (_, _, _) => true
: null, : null,
}); });
} }
} }
} }
} }
internal static class IWebHostBuilderExtensions internal static class IWebHostBuilderExtensions
{ {
internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) => builder; internal static IWebHostBuilder ConfigureAppConfigurationUsingEnvironmentVariables(this IWebHostBuilder builder) => builder;
internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder) internal static IWebHostBuilder ConfigureKestrelServerOptions(this IWebHostBuilder builder)
{ {
var configuration = new ConfigurationBuilder() var configuration = new ConfigurationBuilder()
.AddEnvironmentVariables() .AddEnvironmentVariables()
.Build(); .Build();
return builder.ConfigureServices(services => return builder.ConfigureServices(services =>
{ {
services.Configure<KestrelServerOptions>(configuration.GetSection("Kestrel")); services.Configure<KestrelServerOptions>(configuration.GetSection("Kestrel"));
}); });
} }
} }
#endif #endif

View File

@@ -1,189 +1,189 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
#if USE_ASPNETCORE #if USE_ASPNETCORE
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Stef.Validation; using Stef.Validation;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Owin.Mappers; using WireMock.Owin.Mappers;
using WireMock.Services; using WireMock.Services;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Owin; namespace WireMock.Owin;
internal partial class AspNetCoreSelfHost : IOwinSelfHost internal partial class AspNetCoreSelfHost : IOwinSelfHost
{ {
private const string CorsPolicyName = "WireMock.Net - Policy"; private const string CorsPolicyName = "WireMock.Net - Policy";
private readonly CancellationTokenSource _cts = new(); private readonly CancellationTokenSource _cts = new();
private readonly IWireMockMiddlewareOptions _wireMockMiddlewareOptions; private readonly IWireMockMiddlewareOptions _wireMockMiddlewareOptions;
private readonly IWireMockLogger _logger; private readonly IWireMockLogger _logger;
private readonly HostUrlOptions _urlOptions; private readonly HostUrlOptions _urlOptions;
private Exception _runningException; private Exception _runningException;
private IWebHost _host; private IWebHost _host;
public bool IsStarted { get; private set; } public bool IsStarted { get; private set; }
public List<string> Urls { get; } = new(); public List<string> Urls { get; } = new();
public List<int> Ports { get; } = new(); public List<int> Ports { get; } = new();
public Exception RunningException => _runningException; public Exception RunningException => _runningException;
public AspNetCoreSelfHost(IWireMockMiddlewareOptions wireMockMiddlewareOptions, HostUrlOptions urlOptions) public AspNetCoreSelfHost(IWireMockMiddlewareOptions wireMockMiddlewareOptions, HostUrlOptions urlOptions)
{ {
Guard.NotNull(wireMockMiddlewareOptions); Guard.NotNull(wireMockMiddlewareOptions);
Guard.NotNull(urlOptions); Guard.NotNull(urlOptions);
_logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger(); _logger = wireMockMiddlewareOptions.Logger ?? new WireMockConsoleLogger();
_wireMockMiddlewareOptions = wireMockMiddlewareOptions; _wireMockMiddlewareOptions = wireMockMiddlewareOptions;
_urlOptions = urlOptions; _urlOptions = urlOptions;
} }
public Task StartAsync() public Task StartAsync()
{ {
var builder = new WebHostBuilder(); var builder = new WebHostBuilder();
// Workaround for https://github.com/wiremock/WireMock.Net/issues/292 // Workaround for https://github.com/wiremock/WireMock.Net/issues/292
// On some platforms, AppContext.BaseDirectory is null, which causes WebHostBuilder to fail if ContentRoot is not // On some platforms, AppContext.BaseDirectory is null, which causes WebHostBuilder to fail if ContentRoot is not
// specified (even though we don't actually use that base path mechanism, since we have our own way of configuring // specified (even though we don't actually use that base path mechanism, since we have our own way of configuring
// a filesystem handler). // a filesystem handler).
if (string.IsNullOrEmpty(AppContext.BaseDirectory)) if (string.IsNullOrEmpty(AppContext.BaseDirectory))
{ {
builder.UseContentRoot(Directory.GetCurrentDirectory()); builder.UseContentRoot(Directory.GetCurrentDirectory());
} }
_host = builder _host = builder
.UseSetting("suppressStatusMessages", "True") // https://andrewlock.net/suppressing-the-startup-and-shutdown-messages-in-asp-net-core/ .UseSetting("suppressStatusMessages", "True") // https://andrewlock.net/suppressing-the-startup-and-shutdown-messages-in-asp-net-core/
.ConfigureAppConfigurationUsingEnvironmentVariables() .ConfigureAppConfigurationUsingEnvironmentVariables()
.ConfigureServices(services => .ConfigureServices(services =>
{ {
services.AddSingleton(_wireMockMiddlewareOptions); services.AddSingleton(_wireMockMiddlewareOptions);
services.AddSingleton<IMappingMatcher, MappingMatcher>(); services.AddSingleton<IMappingMatcher, MappingMatcher>();
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>(); services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>(); services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>(); services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
services.AddSingleton<IGuidUtils, GuidUtils>(); services.AddSingleton<IGuidUtils, GuidUtils>();
#if NETCOREAPP3_1 || NET5_0_OR_GREATER #if NETCOREAPP3_1 || NET5_0_OR_GREATER
AddCors(services); AddCors(services);
#endif #endif
_wireMockMiddlewareOptions.AdditionalServiceRegistration?.Invoke(services); _wireMockMiddlewareOptions.AdditionalServiceRegistration?.Invoke(services);
}) })
.Configure(appBuilder => .Configure(appBuilder =>
{ {
appBuilder.UseMiddleware<GlobalExceptionMiddleware>(); appBuilder.UseMiddleware<GlobalExceptionMiddleware>();
#if NETCOREAPP3_1 || NET5_0_OR_GREATER #if NETCOREAPP3_1 || NET5_0_OR_GREATER
UseCors(appBuilder); UseCors(appBuilder);
#endif #endif
_wireMockMiddlewareOptions.PreWireMockMiddlewareInit?.Invoke(appBuilder); _wireMockMiddlewareOptions.PreWireMockMiddlewareInit?.Invoke(appBuilder);
appBuilder.UseMiddleware<WireMockMiddleware>(); appBuilder.UseMiddleware<WireMockMiddleware>();
_wireMockMiddlewareOptions.PostWireMockMiddlewareInit?.Invoke(appBuilder); _wireMockMiddlewareOptions.PostWireMockMiddlewareInit?.Invoke(appBuilder);
}) })
.UseKestrel(options => .UseKestrel(options =>
{ {
SetKestrelOptionsLimits(options); SetKestrelOptionsLimits(options);
SetHttpsAndUrls(options, _wireMockMiddlewareOptions, _urlOptions.GetDetails()); SetHttpsAndUrls(options, _wireMockMiddlewareOptions, _urlOptions.GetDetails());
}) })
.ConfigureKestrelServerOptions() .ConfigureKestrelServerOptions()
#if NETSTANDARD1_3 #if NETSTANDARD1_3
.UseUrls(_urlOptions.GetDetails().Select(u => u.Url).ToArray()) .UseUrls(_urlOptions.GetDetails().Select(u => u.Url).ToArray())
#endif #endif
.Build(); .Build();
return RunHost(_cts.Token); return RunHost(_cts.Token);
} }
private Task RunHost(CancellationToken token) private Task RunHost(CancellationToken token)
{ {
try try
{ {
#if NETCOREAPP3_1 || NET5_0_OR_GREATER #if NETCOREAPP3_1 || NET5_0_OR_GREATER
var appLifetime = _host.Services.GetRequiredService<Microsoft.Extensions.Hosting.IHostApplicationLifetime>(); var appLifetime = _host.Services.GetRequiredService<Microsoft.Extensions.Hosting.IHostApplicationLifetime>();
#else #else
var appLifetime = _host.Services.GetRequiredService<IApplicationLifetime>(); var appLifetime = _host.Services.GetRequiredService<IApplicationLifetime>();
#endif #endif
appLifetime.ApplicationStarted.Register(() => appLifetime.ApplicationStarted.Register(() =>
{ {
var addresses = _host.ServerFeatures var addresses = _host.ServerFeatures
.Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>()! .Get<Microsoft.AspNetCore.Hosting.Server.Features.IServerAddressesFeature>()!
.Addresses; .Addresses;
foreach (var address in addresses) foreach (var address in addresses)
{ {
Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost")); Urls.Add(address.Replace("0.0.0.0", "localhost").Replace("[::]", "localhost"));
PortUtils.TryExtract(address, out _, out _, out _, out _, out var port); PortUtils.TryExtract(address, out _, out _, out _, out _, out var port);
Ports.Add(port); Ports.Add(port);
} }
IsStarted = true; IsStarted = true;
}); });
#if NETSTANDARD1_3 #if NETSTANDARD1_3
_logger.Info("Server using netstandard1.3"); _logger.Info("Server using netstandard1.3");
#elif NETSTANDARD2_0 #elif NETSTANDARD2_0
_logger.Info("Server using netstandard2.0"); _logger.Info("Server using netstandard2.0");
#elif NETSTANDARD2_1 #elif NETSTANDARD2_1
_logger.Info("Server using netstandard2.1"); _logger.Info("Server using netstandard2.1");
#elif NETCOREAPP3_1 #elif NETCOREAPP3_1
_logger.Info("Server using .NET Core App 3.1"); _logger.Info("Server using .NET Core App 3.1");
#elif NET5_0 #elif NET5_0
_logger.Info("Server using .NET 5.0"); _logger.Info("Server using .NET 5.0");
#elif NET6_0 #elif NET6_0
_logger.Info("Server using .NET 6.0"); _logger.Info("Server using .NET 6.0");
#elif NET7_0 #elif NET7_0
_logger.Info("Server using .NET 7.0"); _logger.Info("Server using .NET 7.0");
#elif NET8_0 #elif NET8_0
_logger.Info("Server using .NET 8.0"); _logger.Info("Server using .NET 8.0");
#elif NET46 #elif NET46
_logger.Info("Server using .NET Framework 4.6.1 or higher"); _logger.Info("Server using .NET Framework 4.6.1 or higher");
#endif #endif
#if NETSTANDARD1_3 #if NETSTANDARD1_3
return Task.Run(() => return Task.Run(() =>
{ {
_host.Run(token); _host.Run(token);
}); });
#else #else
return _host.RunAsync(token); return _host.RunAsync(token);
#endif #endif
} }
catch (Exception e) catch (Exception e)
{ {
_runningException = e; _runningException = e;
_logger.Error(e.ToString()); _logger.Error(e.ToString());
IsStarted = false; IsStarted = false;
return Task.CompletedTask; return Task.CompletedTask;
} }
} }
public Task StopAsync() public Task StopAsync()
{ {
_cts.Cancel(); _cts.Cancel();
IsStarted = false; IsStarted = false;
#if NETSTANDARD1_3 #if NETSTANDARD1_3
return Task.CompletedTask; return Task.CompletedTask;
#else #else
return _host.StopAsync(); return _host.StopAsync();
#endif #endif
} }
} }
#endif #endif

View File

@@ -1,64 +1,64 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Owin; namespace WireMock.Owin;
internal class HostUrlOptions internal class HostUrlOptions
{ {
private const string Star = "*"; private const string Star = "*";
public ICollection<string>? Urls { get; set; } public ICollection<string>? Urls { get; set; }
public int? Port { get; set; } public int? Port { get; set; }
public HostingScheme HostingScheme { get; set; } public HostingScheme HostingScheme { get; set; }
public bool? UseHttp2 { get; set; } public bool? UseHttp2 { get; set; }
public IReadOnlyList<HostUrlDetails> GetDetails() public IReadOnlyList<HostUrlDetails> GetDetails()
{ {
var list = new List<HostUrlDetails>(); var list = new List<HostUrlDetails>();
if (Urls == null) if (Urls == null)
{ {
if (HostingScheme is HostingScheme.Http or HostingScheme.Https) if (HostingScheme is HostingScheme.Http or HostingScheme.Https)
{ {
var port = Port > 0 ? Port.Value : FindFreeTcpPort(); var port = Port > 0 ? Port.Value : FindFreeTcpPort();
var scheme = HostingScheme == HostingScheme.Https ? "https" : "http"; var scheme = HostingScheme == HostingScheme.Https ? "https" : "http";
list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port }); list.Add(new HostUrlDetails { IsHttps = HostingScheme == HostingScheme.Https, IsHttp2 = UseHttp2 == true, Url = $"{scheme}://{Star}:{port}", Scheme = scheme, Host = Star, Port = port });
} }
if (HostingScheme == HostingScheme.HttpAndHttps) if (HostingScheme == HostingScheme.HttpAndHttps)
{ {
var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort(); var httpPort = Port > 0 ? Port.Value : FindFreeTcpPort();
list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"http://{Star}:{httpPort}", Scheme = "http", Host = Star, Port = httpPort }); list.Add(new HostUrlDetails { IsHttps = false, IsHttp2 = UseHttp2 == true, Url = $"http://{Star}:{httpPort}", Scheme = "http", Host = Star, Port = httpPort });
var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https. var httpsPort = FindFreeTcpPort(); // In this scenario, always get a free port for https.
list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"https://{Star}:{httpsPort}", Scheme = "https", Host = Star, Port = httpsPort }); list.Add(new HostUrlDetails { IsHttps = true, IsHttp2 = UseHttp2 == true, Url = $"https://{Star}:{httpsPort}", Scheme = "https", Host = Star, Port = httpsPort });
} }
} }
else else
{ {
foreach (var url in Urls) foreach (var url in Urls)
{ {
if (PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var protocol, out var host, out var port)) if (PortUtils.TryExtract(url, out var isHttps, out var isGrpc, out var protocol, out var host, out var port))
{ {
list.Add(new HostUrlDetails { IsHttps = isHttps, IsHttp2 = isGrpc, Url = url, Scheme = protocol, Host = host, Port = port }); list.Add(new HostUrlDetails { IsHttps = isHttps, IsHttp2 = isGrpc, Url = url, Scheme = protocol, Host = host, Port = port });
} }
} }
} }
return list; return list;
} }
private static int FindFreeTcpPort() private static int FindFreeTcpPort()
{ {
#if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1 #if USE_ASPNETCORE || NETSTANDARD2_0 || NETSTANDARD2_1
return 0; return 0;
#else #else
return PortUtils.FindFreeTcpPort(); return PortUtils.FindFreeTcpPort();
#endif #endif
} }
} }

View File

@@ -1,8 +1,8 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
namespace WireMock.Owin; namespace WireMock.Owin;
internal interface IMappingMatcher internal interface IMappingMatcher
{ {
(MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request); (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request);
} }

Some files were not shown because too many files have changed in this diff Show More