Compare commits

..

14 Commits

Author SHA1 Message Date
Stef Heyenrath 04cba25bdf Add extra unit tests 2022-11-17 17:14:33 +01:00
Stef Heyenrath ef5f988786 Add Settings.QueryParameterMultipleValueSupport (#836)
* QueryParameterMultipleValueSupport

* .

* ,

* ,
2022-11-08 19:27:44 +01:00
Stef Heyenrath 1e44f52ad6 1.5.10 2022-11-06 13:29:12 +01:00
Stef Heyenrath 7fd1d30d0e Add WireMockNullLogger as valid commandline logger option (#845)
* Add WireMockNullLogger as valid commandline logger option

* .
2022-11-06 13:25:26 +01:00
Gerhard Gradnig 49b29d74dc Webhook: Use the transformed URL to create the HttpRequestMessage (#843)
Co-authored-by: Gerhard Gradnig <gerhard.gradnig@admiral.at>
2022-11-05 10:54:39 +01:00
Stef Heyenrath 80931e9fb5 Update Azure.Storage NuGets 2022-10-29 14:06:43 +02:00
Stef Heyenrath cce344ff83 1.5.9 2022-10-29 14:02:30 +02:00
Stef Heyenrath 0972d2cb8f Add option to ProxySettings to append guid to mapping file (#838)
* Add option to ProxySettings to append guid to mapping file

* .

* .

* .
2022-10-29 13:58:29 +02:00
dependabot[bot] a39b7fc633 Bump Microsoft.AspNetCore.Server.Kestrel.Core (#837)
Bumps [Microsoft.AspNetCore.Server.Kestrel.Core](https://github.com/aspnet/AspNetCore) from 2.1.7 to 2.1.25.
- [Release notes](https://github.com/aspnet/AspNetCore/releases)
- [Changelog](https://github.com/dotnet/aspnetcore/blob/main/docs/ReleasePlanning.md)
- [Commits](https://github.com/aspnet/AspNetCore/compare/v2.1.7...v2.1.25)

---
updated-dependencies:
- dependency-name: Microsoft.AspNetCore.Server.Kestrel.Core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-27 07:46:01 +02:00
Stef Heyenrath 31298d281d Support deleting / resetting a single scenario (#834)
* Support deleting / resetting a single scenario

* move files
2022-10-26 08:43:51 +02:00
Stef Heyenrath b4c32dd66b WireMock.Net.WebApplication.NET6 2022-10-25 17:19:13 +02:00
Stef Heyenrath 57115f1a3d Add setting to skip saving the string-response in the logging when using WithBody(Func...) (#828)
* Add extra unit-test for Response WithBody (dynamic code)

* DoNotSaveDynamicResponseInLogEntry

* update SettingsModel

* fix

* .
2022-10-21 14:47:26 +02:00
Stef Heyenrath 306c69f478 Fixes for WireMock.Net.FluentAssertions (callcount behaviour) (#832)
* UsingAnyMethod

* fix

* .
2022-10-17 21:50:24 +02:00
Stef Heyenrath fb8fec0376 Update funding.yml 2022-10-16 09:17:41 +02:00
108 changed files with 3301 additions and 2049 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
# These are supported funding model platforms # These are supported funding model platforms
github: # [StefH] github: [StefH]
patreon: # Replace with a single Patreon username patreon: # Replace with a single Patreon username
open_collective: # wiremocknet open_collective: # wiremocknet
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username
+12
View File
@@ -1,3 +1,15 @@
# 1.5.10 (06 November 2022)
- [#843](https://github.com/WireMock-Net/WireMock.Net/pull/843) - Webhook Templating: Use the transformed URL to create the HttpRequestMessage contributed by [ggradnig](https://github.com/ggradnig)
- [#845](https://github.com/WireMock-Net/WireMock.Net/pull/845) - Add WireMockNullLogger as valid commandline logger option [feature] contributed by [StefH](https://github.com/StefH)
# 1.5.9 (29 October 2022)
- [#828](https://github.com/WireMock-Net/WireMock.Net/pull/828) - Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature] contributed by [StefH](https://github.com/StefH)
- [#832](https://github.com/WireMock-Net/WireMock.Net/pull/832) - Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature] contributed by [StefH](https://github.com/StefH)
- [#834](https://github.com/WireMock-Net/WireMock.Net/pull/834) - Support deleting / resetting a single scenario [feature] contributed by [StefH](https://github.com/StefH)
- [#837](https://github.com/WireMock-Net/WireMock.Net/pull/837) - Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.7 to 2.1.25 in /examples/WireMock.Net.StandAlone.Net461 [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
- [#838](https://github.com/WireMock-Net/WireMock.Net/pull/838) - Add option to ProxySettings to append guid to mapping file contributed by [StefH](https://github.com/StefH)
- [#826](https://github.com/WireMock-Net/WireMock.Net/issues/826) - Dynamic Body not to be cached when a Func is used to created the body [feature]
# 1.5.8 (16 October 2022) # 1.5.8 (16 October 2022)
- [#816](https://github.com/WireMock-Net/WireMock.Net/pull/816) - Some fixes to WireMock.Net.Assertions [feature] contributed by [StefH](https://github.com/StefH) - [#816](https://github.com/WireMock-Net/WireMock.Net/pull/816) - Some fixes to WireMock.Net.Assertions [feature] contributed by [StefH](https://github.com/StefH)
- [#817](https://github.com/WireMock-Net/WireMock.Net/pull/817) - ExactMatcher : IgnoreCase [feature] contributed by [StefH](https://github.com/StefH) - [#817](https://github.com/WireMock-Net/WireMock.Net/pull/817) - ExactMatcher : IgnoreCase [feature] contributed by [StefH](https://github.com/StefH)
+1 -1
View File
@@ -4,7 +4,7 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<VersionPrefix>1.5.8</VersionPrefix> <VersionPrefix>1.5.10</VersionPrefix>
<PackageIcon>WireMock.Net-Logo.png</PackageIcon> <PackageIcon>WireMock.Net-Logo.png</PackageIcon>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl> <PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression> <PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
+1 -1
View File
@@ -1,6 +1,6 @@
rem https://github.com/StefH/GitHubReleaseNotes rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.5.8 SET version=1.5.10
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN% GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
+3 -7
View File
@@ -1,9 +1,5 @@
# 1.5.8 (16 October 2022) # 1.5.10 (06 November 2022)
- #816 Some fixes to WireMock.Net.Assertions [feature] - #843 Webhook Templating: Use the transformed URL to create the HttpRequestMessage
- #817 ExactMatcher : IgnoreCase [feature] - #845 Add WireMockNullLogger as valid commandline logger option [feature]
- #824 WebHook - Transform Url [feature]
- #814 WithHeader cannot handle multiple requests with the same header key values [bug]
- #815 Why does UsingMethod check _callscount? [bug]
- #822 Webhook with generic url, body and custom header values [feature]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
+15
View File
@@ -25,6 +25,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.gitignore = .gitignore .gitignore = .gitignore
CHANGELOG.md = CHANGELOG.md CHANGELOG.md = CHANGELOG.md
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
.github\FUNDING.yml = .github\FUNDING.yml
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
nuget.config = nuget.config nuget.config = nuget.config
PackageReleaseNotes.template = PackageReleaseNotes.template PackageReleaseNotes.template = PackageReleaseNotes.template
@@ -105,6 +106,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.xUnit", "src\W
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET6.WithCertificate", "examples\WireMock.Net.Console.NET6.WithCertificate\WireMock.Net.Console.NET6.WithCertificate.csproj", "{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET6.WithCertificate", "examples\WireMock.Net.Console.NET6.WithCertificate\WireMock.Net.Console.NET6.WithCertificate.csproj", "{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample", "examples\WireMockAzureQueueExample\WireMockAzureQueueExample.csproj", "{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -251,6 +256,14 @@ Global
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34}.Release|Any CPU.Build.0 = Release|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D}.Release|Any CPU.Build.0 = Release|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -293,6 +306,8 @@ Global
{670C7562-C154-442E-A249-7D26849BCD13} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {670C7562-C154-442E-A249-7D26849BCD13} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{0DE0954F-8C00-4E8D-B94A-4361FC1CBE44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {0DE0954F-8C00-4E8D-B94A-4361FC1CBE44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
+8
View File
@@ -1,10 +1,18 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=AD/@EntryIndexedValue">AD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CONNECT/@EntryIndexedValue">CONNECT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=CS/@EntryIndexedValue">CS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=DELETE/@EntryIndexedValue">DELETE</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EC/@EntryIndexedValue">EC</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=EC/@EntryIndexedValue">EC</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=GET/@EntryIndexedValue">GET</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=HEAD/@EntryIndexedValue">HEAD</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=ID/@EntryIndexedValue">ID</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OPTIONS/@EntryIndexedValue">OPTIONS</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PATCH/@EntryIndexedValue">PATCH</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=POST/@EntryIndexedValue">POST</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PUT/@EntryIndexedValue">PUT</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RSA/@EntryIndexedValue">RSA</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=RSA/@EntryIndexedValue">RSA</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=SSL/@EntryIndexedValue">SSL</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String> <s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=TE/@EntryIndexedValue">TE</s:String>
@@ -24,7 +24,7 @@
<package id="Microsoft.AspNetCore.Routing.Abstractions" version="2.1.1" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Routing.Abstractions" version="2.1.1" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.IISIntegration" version="2.1.2" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Server.IISIntegration" version="2.1.2" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel" version="2.1.3" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Server.Kestrel" version="2.1.3" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Core" version="2.1.7" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Server.Kestrel.Core" version="2.1.25" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Https" version="2.1.3" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Server.Kestrel.Https" version="2.1.3" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions" version="2.1.3" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions" version="2.1.3" targetFramework="net461" />
<package id="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" version="2.1.3" targetFramework="net461" /> <package id="Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets" version="2.1.3" targetFramework="net461" />
@@ -0,0 +1,173 @@
{
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"metadata": {
"_dependencyType": "compute.function.linux.appService"
},
"parameters": {
"resourceGroupName": {
"type": "string",
"defaultValue": "linux-app-service",
"metadata": {
"description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking."
}
},
"resourceGroupLocation": {
"type": "string",
"defaultValue": "westeurope",
"metadata": {
"description": "Location of the resource group. Resource groups could have different location than resources, however by default we use API versions from latest hybrid profile which support all locations for resource types we support."
}
},
"resourceName": {
"type": "string",
"defaultValue": "WireMockNetWebApplicationNET6Linux",
"metadata": {
"description": "Name of the main resource to be created by this template."
}
},
"resourceLocation": {
"type": "string",
"defaultValue": "[parameters('resourceGroupLocation')]",
"metadata": {
"description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there."
}
}
},
"resources": [
{
"type": "Microsoft.Resources/resourceGroups",
"name": "[parameters('resourceGroupName')]",
"location": "[parameters('resourceGroupLocation')]",
"apiVersion": "2019-10-01"
},
{
"type": "Microsoft.Resources/deployments",
"name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
"resourceGroup": "[parameters('resourceGroupName')]",
"apiVersion": "2019-10-01",
"dependsOn": [
"[parameters('resourceGroupName')]"
],
"properties": {
"mode": "Incremental",
"expressionEvaluationOptions": {
"scope": "inner"
},
"parameters": {
"resourceGroupName": {
"value": "[parameters('resourceGroupName')]"
},
"resourceGroupLocation": {
"value": "[parameters('resourceGroupLocation')]"
},
"resourceName": {
"value": "[parameters('resourceName')]"
},
"resourceLocation": {
"value": "[parameters('resourceLocation')]"
}
},
"template": {
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"resourceGroupName": {
"type": "string"
},
"resourceGroupLocation": {
"type": "string"
},
"resourceName": {
"type": "string"
},
"resourceLocation": {
"type": "string"
}
},
"variables": {
"storage_name": "[toLower(concat('storage', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId))))]",
"appServicePlan_name": "[concat('Plan', uniqueString(concat(parameters('resourceName'), subscription().subscriptionId)))]",
"storage_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Storage/storageAccounts/', variables('storage_name'))]",
"appServicePlan_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/serverFarms/', variables('appServicePlan_name'))]",
"function_ResourceId": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', parameters('resourceGroupName'), '/providers/Microsoft.Web/sites/', parameters('resourceName'))]"
},
"resources": [
{
"location": "[parameters('resourceLocation')]",
"name": "[parameters('resourceName')]",
"type": "Microsoft.Web/sites",
"apiVersion": "2015-08-01",
"tags": {
"[concat('hidden-related:', variables('appServicePlan_ResourceId'))]": "empty"
},
"dependsOn": [
"[variables('appServicePlan_ResourceId')]",
"[variables('storage_ResourceId')]"
],
"kind": "functionapp",
"properties": {
"name": "[parameters('resourceName')]",
"kind": "functionapp",
"httpsOnly": true,
"reserved": false,
"serverFarmId": "[variables('appServicePlan_ResourceId')]",
"siteConfig": {
"alwaysOn": true,
"linuxFxVersion": "dotnet|3.1"
}
},
"identity": {
"type": "SystemAssigned"
},
"resources": [
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[variables('function_ResourceId')]"
],
"properties": {
"AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', variables('storage_name'), ';AccountKey=', listKeys(variables('storage_ResourceId'), '2017-10-01').keys[0].value, ';EndpointSuffix=', 'core.windows.net')]",
"FUNCTIONS_EXTENSION_VERSION": "~3",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
]
},
{
"location": "[parameters('resourceGroupLocation')]",
"name": "[variables('storage_name')]",
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2017-10-01",
"tags": {
"[concat('hidden-related:', concat('/providers/Microsoft.Web/sites/', parameters('resourceName')))]": "empty"
},
"properties": {
"supportsHttpsTrafficOnly": true
},
"sku": {
"name": "Standard_LRS"
},
"kind": "Storage"
},
{
"location": "[parameters('resourceGroupLocation')]",
"name": "[variables('appServicePlan_name')]",
"type": "Microsoft.Web/serverFarms",
"apiVersion": "2015-02-01",
"kind": "linux",
"properties": {
"name": "[variables('appServicePlan_name')]",
"sku": "Standard",
"workerSizeId": "0",
"reserved": true
}
}
]
}
}
}
]
}
@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6</TargetFramework> <TargetFramework>net6</TargetFramework>
<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers> <!--<RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>-->
<StartupObject>WireMock.Net.WebApplication.Program</StartupObject> <StartupObject>WireMock.Net.WebApplication.Program</StartupObject>
<AssemblyName>WireMock.Net.WebApplication</AssemblyName> <AssemblyName>WireMock.Net.WebApplication</AssemblyName>
<RootNamespace>WireMock.Net.WebApplication</RootNamespace> <RootNamespace>WireMock.Net.WebApplication</RootNamespace>
@@ -10,10 +10,6 @@
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel> <AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Content Remove="Properties\1.launchSettings.json" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
@@ -15,15 +15,7 @@
"WireMockServerSettings": { "WireMockServerSettings": {
"StartAdminInterface": true, "StartAdminInterface": true,
"Urls": [ "Urls": [
"https://localhost:8081/" "http://localhost"
], ]
"AllowPartialMapping": false,
"HandleRequestsSynchronously": true,
"ThrowExceptionWhenMatcherFails": true,
"ProxyAndRecordSettings": {
"Url": "http://postman-echo.com/post",
"SaveMapping": true,
"SaveMappingToFile": true
}
} }
} }
@@ -0,0 +1,264 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# Azure Functions localsettings file
local.settings.json
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
@@ -0,0 +1,13 @@
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
namespace WireMockAzureQueueExample;
public class Function1
{
[FunctionName("Function1")]
public void Run([QueueTrigger("myqueue-items", Connection = "ConnectionStringToWireMock")]string myQueueItem, ILogger log)
{
log.LogWarning($"C# Queue trigger function processed: {myQueueItem}");
}
}
@@ -0,0 +1,9 @@
{
"profiles": {
"WireMockAzureQueueExample": {
"commandName": "Project",
"commandLineArgs": "--port 7290",
"launchBrowser": false
}
}
}
@@ -0,0 +1,14 @@
{
"dependencies": {
"appInsights1": {
"type": "appInsights"
},
"secrets1": {
"type": "secrets"
},
"storage1": {
"type": "storage",
"connectionId": "AzureWebJobsStorage"
}
}
}
@@ -0,0 +1,16 @@
{
"dependencies": {
"appInsights1": {
"type": "appInsights.sdk"
},
"secrets1": {
"type": "secrets.user"
},
"storage1": {
"secretStore": null,
"resourceId": null,
"type": "storage.emulator",
"connectionId": "AzureWebJobsStorage"
}
}
}
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
<UserSecretsId>bb7d8355-68c4-4f81-8c2d-6cdd80cd7602</UserSecretsId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.Storage.Blobs" Version="12.14.1" />
<PackageReference Include="Azure.Storage.Files.Shares" Version="12.12.1" />
<PackageReference Include="Azure.Storage.Queues" Version="12.12.0" />
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.Storage" Version="4.0.5" />
<PackageReference Include="Microsoft.Extensions.Azure" Version="1.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="6.0.1" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="4.1.3" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="local.settings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,11 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}
@@ -0,0 +1,7 @@
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
@@ -0,0 +1,40 @@
using Newtonsoft.Json;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
namespace WireMockAzureQueueProxy;
static class Program
{
static void Main(params string[] args)
{
var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new WireMockConsoleLogger(),
Urls = new[] { "http://localhost:20001/" },
StartAdminInterface = false,
ReadStaticMappings = true,
WatchStaticMappings = true,
WatchStaticMappingsInSubdirectories = true,
//ProxyAndRecordSettings = new ProxyAndRecordSettings
//{
// Url = "http://127.0.0.1:10001",
// SaveMapping = true,
// SaveMappingToFile = true,
// AppendGuidToSavedMappingFile = true
//}
});
System.Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey();
server.Stop();
System.Console.WriteLine("Displaying all requests");
var allRequests = server.LogEntries;
System.Console.WriteLine(JsonConvert.SerializeObject(allRequests, Formatting.Indented));
System.Console.WriteLine("Press any key to quit");
System.Console.ReadKey();
}
}
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="__admin\mappings\*.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
@@ -0,0 +1,44 @@
{
"Guid": "b4a2ff02-fb7f-4496-8c04-9aafc4f5f8f7",
"Title": "Proxy Mapping for DELETE /devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
"Description": "Proxy Mapping for DELETE /devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items/messages/41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88",
"IgnoreCase": false
}
]
},
"Methods": [
"DELETE"
],
"Params": [
{
"Name": "popreceipt",
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "*",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 204,
"Headers": {
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5"
},
"UseTransformer": true
}
}
@@ -0,0 +1,60 @@
{
"Scenario": "AzureQueue Get Messages",
"WhenStateIs": "No more messages",
"SetStateTo": "No more messages",
"Guid": "4c871968-29ee-472b-a548-170444d4cc3e",
"Title": "Proxy Mapping for GET NO MSG /devstoreaccount1/myqueue-items/messages",
"Description": "Proxy Mapping for GET NO MSG /devstoreaccount1/myqueue-items/messages",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items/messages",
"IgnoreCase": false
}
]
},
"Methods": [
"GET"
],
"Params": [
{
"Name": "numofmessages",
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "16",
"IgnoreCase": false
}
]
},
{
"Name": "visibilitytimeout",
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "600",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 200,
"Body": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><QueueMessagesList/>",
"Headers": {
"Content-Type": "application/xml",
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5",
"Transfer-Encoding": "chunked"
},
"UseTransformer": true
}
}
@@ -0,0 +1,59 @@
{
"Scenario": "AzureQueue Get Messages",
"SetStateTo": "No more messages",
"Guid": "da9c6799-fbf8-41b6-8933-0df50f821ebb",
"Title": "Proxy Mapping for GET /devstoreaccount1/myqueue-items/messages",
"Description": "Proxy Mapping for GET /devstoreaccount1/myqueue-items/messages",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items/messages",
"IgnoreCase": false
}
]
},
"Methods": [
"GET"
],
"Params": [
{
"Name": "numofmessages",
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "*",
"IgnoreCase": true
}
]
},
{
"Name": "visibilitytimeout",
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "*",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 200,
"Body": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><QueueMessagesList><QueueMessage><MessageId>41b2aadc-d6ea-4c3c-ae20-2ae72eb08d88</MessageId><InsertionTime>Sat, 29 Oct 2022 07:11:40 GMT</InsertionTime><ExpirationTime>Sat, 31 Dec 2022 07:11:40 GMT</ExpirationTime><PopReceipt>MjlPY3QyMDIyMDc6MTE6NDAyMjU2</PopReceipt><TimeNextVisible>Sat, 29 Oct 2022 07:21:40 GMT</TimeNextVisible><DequeueCount>1</DequeueCount><MessageText>c3RlZg==</MessageText></QueueMessage></QueueMessagesList>",
"Headers": {
"Content-Type": "application/xml",
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5",
"Transfer-Encoding": "chunked"
},
"UseTransformer": true
}
}
@@ -0,0 +1,46 @@
{
"Guid": "17c7a389-98e1-4383-975d-54c82d1e3860",
"Title": "Proxy Mapping for HEAD /devstoreaccount1/myqueue-items",
"Description": "Proxy Mapping for HEAD /devstoreaccount1/myqueue-items",
"Request": {
"Path": {
"Matchers": [
{
"Name": "WildcardMatcher",
"Pattern": "/devstoreaccount1/myqueue-items",
"IgnoreCase": false
}
]
},
"Methods": [
"HEAD"
],
"Params": [
{
"Name": "comp",
"Matchers": [
{
"Name": "ExactMatcher",
"Pattern": "metadata",
"IgnoreCase": false
}
]
}
]
},
"Response": {
"StatusCode": 200,
"Body": "",
"Headers": {
"Server": "Azurite-Queue/3.19.0",
"x-ms-client-request-id": "{{request.headers.x-ms-client-request-id}}",
"x-ms-approximate-messages-count": "0",
"x-ms-request-id": "{{Random Type=\"Guid\"}}",
"x-ms-version": "2021-10-04",
"Date": "{{DateTime.Now \"ddd, dd MMM yyy HH:mm:ss GMT\"}}",
"Connection": "keep-alive",
"Keep-Alive": "timeout=5"
},
"UseTransformer": true
}
}
@@ -1,8 +1,8 @@
namespace WireMock.Admin.Settings namespace WireMock.Admin.Settings;
[FluentBuilder.AutoGenerateBuilder]
public class ProxyAndRecordSettingsModel
{ {
[FluentBuilder.AutoGenerateBuilder]
public class ProxyAndRecordSettingsModel
{
/// <summary> /// <summary>
/// The clientCertificate thumbprint or subject name fragment to use. /// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com"" /// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
@@ -63,5 +63,9 @@ namespace WireMock.Admin.Settings
/// Default value is false. /// Default value is false.
/// </summary> /// </summary>
public bool UseDefinedRequestMatchers { get; set; } public bool UseDefinedRequestMatchers { get; set; }
}
/// <summary>
/// Append an unique GUID to the filename from the saved mapping file.
/// </summary>
public bool AppendGuidToSavedMappingFile { get; set; }
} }
@@ -1,14 +1,15 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Types;
namespace WireMock.Admin.Settings namespace WireMock.Admin.Settings;
/// <summary>
/// Settings
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class SettingsModel
{ {
/// <summary>
/// Settings
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class SettingsModel
{
/// <summary> /// <summary>
/// Gets or sets the global delay in milliseconds. /// Gets or sets the global delay in milliseconds.
/// </summary> /// </summary>
@@ -78,5 +79,21 @@ namespace WireMock.Admin.Settings
/// The proxy and record settings. /// The proxy and record settings.
/// </summary> /// </summary>
public ProxyAndRecordSettingsModel? ProxyAndRecordSettings { get; set; } public ProxyAndRecordSettingsModel? ProxyAndRecordSettings { get; set; }
}
/// <summary>
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
/// </summary>
public HostingScheme? HostingScheme { get; set; }
/// <summary>
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
/// </summary>
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
///
/// Default value = "All".
/// </summary>
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
} }
@@ -1,9 +1,9 @@
namespace WireMock.Http; namespace WireMock.Constants;
/// <summary> /// <summary>
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods /// https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
/// </summary> /// </summary>
internal static class HttpRequestMethods public static class HttpRequestMethod
{ {
public const string CONNECT = "CONNECT"; public const string CONNECT = "CONNECT";
public const string DELETE = "DELETE"; public const string DELETE = "DELETE";
@@ -28,7 +28,7 @@ public interface IRequestMessage
/// <summary> /// <summary>
/// The ProxyUrl (if a proxy is used). /// The ProxyUrl (if a proxy is used).
/// </summary> /// </summary>
string ProxyUrl { get; set; } string? ProxyUrl { get; set; }
/// <summary> /// <summary>
/// Gets the DateTime. /// Gets the DateTime.
@@ -1,6 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Text; using System.Text;
using JetBrains.Annotations;
using WireMock.Types; using WireMock.Types;
namespace WireMock.Util; namespace WireMock.Util;
@@ -59,4 +57,9 @@ public interface IBodyData
/// The body encoding. /// The body encoding.
/// </summary> /// </summary>
Encoding? Encoding { get; set; } Encoding? Encoding { get; set; }
/// <summary>
/// Defines if this BodyData is the result of a dynamically created response-string. (
/// </summary>
public string? IsFuncUsed { get; set; }
} }
@@ -1,10 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using JetBrains.Annotations;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers.Request;
namespace WireMock.Server namespace WireMock.Server
{ {
@@ -144,6 +142,11 @@ namespace WireMock.Server
/// </summary> /// </summary>
void ResetScenarios(); void ResetScenarios();
/// <summary>
/// Resets a specific Scenario by the name.
/// </summary>
bool ResetScenario(string name);
/// <summary> /// <summary>
/// Resets the LogEntries. /// Resets the LogEntries.
/// </summary> /// </summary>
@@ -0,0 +1,28 @@
using System;
namespace WireMock.Types;
[Flags]
public enum QueryParameterMultipleValueSupport
{
// Support none
None = 0x0,
// Support "&" as multi-value-separator --> "key=value&key=anotherValue"
Ampersand = 0x1,
// Support ";" as multi-value-separator --> "key=value;key=anotherValue"
SemiColon = 0x2,
// Support "," as multi-value-separator --> "key=1,2"
Comma = 0x4,
// Support "&" and ";" as multi-value-separator --> "key=value&key=anotherValue" and also "key=value;key=anotherValue"
AmpersandAndSemiColon = Ampersand | SemiColon,
// Support "&" and ";" as multi-value-separator
NoComma = AmpersandAndSemiColon,
// Support all multi-value-separators ("&" and ";" and ",")
All = Ampersand | SemiColon | Comma
}
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using FluentAssertions.Execution; using FluentAssertions.Execution;
using WireMock.Constants;
using WireMock.Server; using WireMock.Server;
using WireMock.Types; using WireMock.Types;
@@ -12,9 +13,10 @@ namespace WireMock.FluentAssertions;
public class WireMockAssertions public class WireMockAssertions
{ {
private const string Any = "*";
private readonly int? _callsCount; private readonly int? _callsCount;
private IReadOnlyList<IRequestMessage> _requestMessages; private IReadOnlyList<IRequestMessage> _requestMessages;
private IReadOnlyList<KeyValuePair<string, WireMockList<string>>> _headers; private readonly IReadOnlyList<KeyValuePair<string, WireMockList<string>>> _headers;
public WireMockAssertions(IWireMockServer subject, int? callsCount) public WireMockAssertions(IWireMockServer subject, int? callsCount)
{ {
@@ -27,12 +29,13 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs) public AndWhichConstraint<WireMockAssertions, string> AtAbsoluteUrl(string absoluteUrl, string because = "", params object[] becauseArgs)
{ {
Func<IRequestMessage, bool> predicate = request => string.Equals(request.AbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase); Func<IRequestMessage, bool> predicate = request => string.Equals(request.AbsoluteUrl, absoluteUrl, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate); var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion Execute.Assertion
.BecauseOf(because, becauseArgs) .BecauseOf(because, becauseArgs)
.Given(() => _requestMessages) .Given(() => _requestMessages)
.ForCondition(requests => requests.Any()) .ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.", "Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but no calls were made.",
absoluteUrl absoluteUrl
@@ -53,15 +56,17 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> AtUrl(string url, string because = "", params object[] becauseArgs) public AndWhichConstraint<WireMockAssertions, string> AtUrl(string url, string because = "", params object[] becauseArgs)
{ {
Func<IRequestMessage, bool> predicate = request => string.Equals(request.Url, url, StringComparison.OrdinalIgnoreCase); Func<IRequestMessage, bool> predicate = request => string.Equals(request.Url, url, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate); var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion Execute.Assertion
.BecauseOf(because, becauseArgs) .BecauseOf(because, becauseArgs)
.Given(() => _requestMessages) .Given(() => _requestMessages)
.ForCondition(requests => requests.Any()) .ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but no calls were made.", "Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but no calls were made.",
url) url
)
.Then .Then
.ForCondition(condition) .ForCondition(condition)
.FailWith( .FailWith(
@@ -79,12 +84,13 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> WithProxyUrl(string proxyUrl, string because = "", params object[] becauseArgs) public AndWhichConstraint<WireMockAssertions, string> WithProxyUrl(string proxyUrl, string because = "", params object[] becauseArgs)
{ {
Func<IRequestMessage, bool> predicate = request => string.Equals(request.ProxyUrl, proxyUrl, StringComparison.OrdinalIgnoreCase); Func<IRequestMessage, bool> predicate = request => string.Equals(request.ProxyUrl, proxyUrl, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate); var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion Execute.Assertion
.BecauseOf(because, becauseArgs) .BecauseOf(because, becauseArgs)
.Given(() => _requestMessages) .Given(() => _requestMessages)
.ForCondition(requests => requests.Any()) .ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but no calls were made.", "Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but no calls were made.",
proxyUrl proxyUrl
@@ -94,8 +100,7 @@ public class WireMockAssertions
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but didn't find it among the calls with {1}.", "Expected {context:wiremockserver} to have been called with proxy url {0}{reason}, but didn't find it among the calls with {1}.",
_ => proxyUrl, _ => proxyUrl,
requests => requests requests => requests.Select(request => request.ProxyUrl)
.Select(request => request.ProxyUrl)
); );
_requestMessages = filter(_requestMessages).ToList(); _requestMessages = filter(_requestMessages).ToList();
@@ -107,23 +112,23 @@ public class WireMockAssertions
public AndWhichConstraint<WireMockAssertions, string> FromClientIP(string clientIP, string because = "", params object[] becauseArgs) public AndWhichConstraint<WireMockAssertions, string> FromClientIP(string clientIP, string because = "", params object[] becauseArgs)
{ {
Func<IRequestMessage, bool> predicate = request => string.Equals(request.ClientIP, clientIP, StringComparison.OrdinalIgnoreCase); Func<IRequestMessage, bool> predicate = request => string.Equals(request.ClientIP, clientIP, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate); var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion Execute.Assertion
.BecauseOf(because, becauseArgs) .BecauseOf(because, becauseArgs)
.Given(() => _requestMessages) .Given(() => _requestMessages)
.ForCondition(requests => requests.Any()) .ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but no calls were made.", "Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but no calls were made.",
clientIP) clientIP
.Then
.ForCondition(requests =>
(_callsCount == null && requests.Any(req => req.ClientIP == clientIP)) ||
(_callsCount == requests.Count(req => req.ClientIP == clientIP))
) )
.Then
.ForCondition(condition)
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but didn't find it among the calls from IP(s) {1}.", "Expected {context:wiremockserver} to have been called from client IP {0}{reason}, but didn't find it among the calls from IP(s) {1}.",
_ => clientIP, requests => requests.Select(request => request.ClientIP)); _ => clientIP, requests => requests.Select(request => request.ClientIP)
);
_requestMessages = filter(_requestMessages).ToList(); _requestMessages = filter(_requestMessages).ToList();
@@ -165,50 +170,57 @@ public class WireMockAssertions
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingConnect(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingConnect(string because = "", params object[] becauseArgs)
=> UsingMethod("CONNECT", because, becauseArgs); => UsingMethod(HttpRequestMethod.CONNECT, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingDelete(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingDelete(string because = "", params object[] becauseArgs)
=> UsingMethod("DELETE", because, becauseArgs); => UsingMethod(HttpRequestMethod.DELETE, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingGet(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingGet(string because = "", params object[] becauseArgs)
=> UsingMethod("GET", because, becauseArgs); => UsingMethod(HttpRequestMethod.GET, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingHead(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingHead(string because = "", params object[] becauseArgs)
=> UsingMethod("HEAD", because, becauseArgs); => UsingMethod(HttpRequestMethod.HEAD, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingOptions(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingOptions(string because = "", params object[] becauseArgs)
=> UsingMethod("OPTIONS", because, becauseArgs); => UsingMethod(HttpRequestMethod.OPTIONS, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPost(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingPost(string because = "", params object[] becauseArgs)
=> UsingMethod("POST", because, becauseArgs); => UsingMethod(HttpRequestMethod.POST, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPatch(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingPatch(string because = "", params object[] becauseArgs)
=> UsingMethod("PATCH", because, becauseArgs); => UsingMethod(HttpRequestMethod.PATCH, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingPut(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingPut(string because = "", params object[] becauseArgs)
=> UsingMethod("PUT", because, becauseArgs); => UsingMethod(HttpRequestMethod.PUT, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingTrace(string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingTrace(string because = "", params object[] becauseArgs)
=> UsingMethod("TRACE", because, becauseArgs); => UsingMethod(HttpRequestMethod.TRACE, because, becauseArgs);
[CustomAssertion]
public AndConstraint<WireMockAssertions> UsingAnyMethod(string because = "", params object[] becauseArgs)
=> UsingMethod(Any, because, becauseArgs);
[CustomAssertion] [CustomAssertion]
public AndConstraint<WireMockAssertions> UsingMethod(string method, string because = "", params object[] becauseArgs) public AndConstraint<WireMockAssertions> UsingMethod(string method, string because = "", params object[] becauseArgs)
{ {
Func<IRequestMessage, bool> predicate = request => string.Equals(request.Method, method, StringComparison.OrdinalIgnoreCase); var any = method == Any;
Func<IRequestMessage, bool> predicate = request => (any && !string.IsNullOrEmpty(request.Method)) ||
string.Equals(request.Method, method, StringComparison.OrdinalIgnoreCase);
var (filter, condition) = BuildFilterAndCondition(predicate); var (filter, condition) = BuildFilterAndCondition(predicate);
Execute.Assertion Execute.Assertion
.BecauseOf(because, becauseArgs) .BecauseOf(because, becauseArgs)
.Given(() => _requestMessages) .Given(() => _requestMessages)
.ForCondition(requests => requests.Any()) .ForCondition(requests => _callsCount == 0 || requests.Any())
.FailWith( .FailWith(
"Expected {context:wiremockserver} to have been called using method {0}{reason}, but no calls were made.", "Expected {context:wiremockserver} to have been called using method {0}{reason}, but no calls were made.",
method method
@@ -230,12 +242,6 @@ public class WireMockAssertions
{ {
Func<IReadOnlyList<IRequestMessage>, IReadOnlyList<IRequestMessage>> filter = requests => requests.Where(predicate).ToList(); Func<IReadOnlyList<IRequestMessage>, IReadOnlyList<IRequestMessage>> filter = requests => requests.Where(predicate).ToList();
return return (filter, requests => (_callsCount is null && filter(requests).Any()) || _callsCount == filter(requests).Count);
(
filter,
requests =>
(_callsCount == null && filter(_requestMessages).Any()) ||
(_callsCount == filter(_requestMessages).Count())
);
} }
} }
@@ -1,11 +1,11 @@
#if NET46 || NETSTANDARD2_0 #if NET46 || NETSTANDARD2_0
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Net.OpenApiParser.Extensions namespace WireMock.Net.OpenApiParser.Extensions;
internal static class DictionaryExtensions
{ {
internal static class DictionaryExtensions public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue>? dictionary, TKey key, TValue value)
{
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
{ {
if (dictionary is null || dictionary.ContainsKey(key)) if (dictionary is null || dictionary.ContainsKey(key))
{ {
@@ -16,6 +16,5 @@ namespace WireMock.Net.OpenApiParser.Extensions
return true; return true;
} }
}
} }
#endif #endif
@@ -1,12 +1,12 @@
using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Interfaces;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Extensions namespace WireMock.Net.OpenApiParser.Extensions;
internal static class OpenApiSchemaExtensions
{ {
internal static class OpenApiSchemaExtensions
{
/// <summary> /// <summary>
/// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger /// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
/// </summary> /// </summary>
@@ -23,7 +23,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
return false; return false;
} }
public static SchemaType GetSchemaType(this OpenApiSchema schema) public static SchemaType GetSchemaType(this OpenApiSchema? schema)
{ {
switch (schema?.Type) switch (schema?.Type)
{ {
@@ -53,7 +53,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
} }
} }
public static SchemaFormat GetSchemaFormat(this OpenApiSchema schema) public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema)
{ {
switch (schema?.Format) switch (schema?.Format)
{ {
@@ -88,5 +88,4 @@ namespace WireMock.Net.OpenApiParser.Extensions
return SchemaFormat.Undefined; return SchemaFormat.Undefined;
} }
} }
}
} }
@@ -1,21 +1,19 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices.ComTypes;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Readers;
using SharpYaml.Model;
using Stef.Validation; using Stef.Validation;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
using WireMock.Server; using WireMock.Server;
namespace WireMock.Net.OpenApiParser.Extensions namespace WireMock.Net.OpenApiParser.Extensions;
/// <summary>
/// Some extension methods for <see cref="IWireMockServer"/>.
/// </summary>
public static class WireMockServerExtensions
{ {
/// <summary>
/// Some extension methods for <see cref="IWireMockServer"/>.
/// </summary>
public static class WireMockServerExtensions
{
/// <summary> /// <summary>
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file. /// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
/// </summary> /// </summary>
@@ -25,7 +23,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
{ {
return WithMappingFromOpenApiFile(server, path, null, out diagnostic); return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
} }
/// <summary> /// <summary>
@@ -55,7 +53,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
{ {
return WithMappingFromOpenApiStream(server, stream, null, out diagnostic); return WithMappingFromOpenApiStream(server, stream, new WireMockOpenApiParserSettings(), out diagnostic);
} }
/// <summary> /// <summary>
@@ -68,9 +66,9 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{ {
Guard.NotNull(server, nameof(server)); Guard.NotNull(server);
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream);
Guard.NotNull(settings, nameof(settings)); Guard.NotNull(settings);
var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic); var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
@@ -84,14 +82,13 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// <param name="document">The OpenAPI document to use as mappings.</param> /// <param name="document">The OpenAPI document to use as mappings.</param>
/// <param name="settings">Additional settings [optional]</param> /// <param name="settings">Additional settings [optional]</param>
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings = null) public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings)
{ {
Guard.NotNull(server, nameof(server)); Guard.NotNull(server);
Guard.NotNull(document, nameof(document)); Guard.NotNull(document);
var mappings = new WireMockOpenApiParser().FromDocument(document, settings); var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
return server.WithMapping(mappings.ToArray()); return server.WithMapping(mappings.ToArray());
} }
}
} }
@@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Readers;
@@ -35,7 +35,7 @@ namespace WireMock.Net.OpenApiParser
/// <param name="document">The source OpenApiDocument</param> /// <param name="document">The source OpenApiDocument</param>
/// <param name="settings">Additional settings [optional]</param> /// <param name="settings">Additional settings [optional]</param>
/// <returns>MappingModel</returns> /// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings settings = null); IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
/// <summary> /// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>. /// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
@@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using Microsoft.OpenApi; using Microsoft.OpenApi;
@@ -15,10 +16,10 @@ using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
using WireMock.Net.OpenApiParser.Utils; using WireMock.Net.OpenApiParser.Utils;
namespace WireMock.Net.OpenApiParser.Mappers namespace WireMock.Net.OpenApiParser.Mappers;
internal class OpenApiPathsMapper
{ {
internal class OpenApiPathsMapper
{
private const string HeaderContentType = "Content-Type"; private const string HeaderContentType = "Content-Type";
private readonly WireMockOpenApiParserSettings _settings; private readonly WireMockOpenApiParserSettings _settings;
@@ -26,7 +27,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings, nameof(settings)); _settings = Guard.NotNull(settings);
_exampleValueGenerator = new ExampleValueGenerator(settings); _exampleValueGenerator = new ExampleValueGenerator(settings);
} }
@@ -53,7 +54,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
var response = operation.Responses.FirstOrDefault(); var response = operation.Responses.FirstOrDefault();
TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType); TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType);
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema; var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
var responseExample = responseContent?.Example; var responseExample = responseContent?.Example;
var responseSchemaExample = responseContent?.Schema?.Example; var responseSchemaExample = responseContent?.Schema?.Example;
@@ -66,10 +67,10 @@ namespace WireMock.Net.OpenApiParser.Mappers
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required) if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
{ {
var request = operation.RequestBody.Content; var request = operation.RequestBody.Content;
TryGetContent(request, out OpenApiMediaType requestContent, out string requestContentType); TryGetContent(request, out OpenApiMediaType? requestContent, out _);
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema; var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
var requestBodyExample = requestContent.Example; var requestBodyExample = requestContent!.Example;
var requestBodySchemaExample = requestContent.Schema?.Example; var requestBodySchemaExample = requestContent.Schema?.Example;
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) : var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
@@ -104,7 +105,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private BodyModel MapRequestBody(object requestBody) private BodyModel? MapRequestBody(object? requestBody)
{ {
if (requestBody == null) if (requestBody == null)
{ {
@@ -122,7 +123,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private bool TryGetContent(IDictionary<string, OpenApiMediaType> contents, out OpenApiMediaType openApiMediaType, out string contentType) private bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
{ {
openApiMediaType = null; openApiMediaType = null;
contentType = null; contentType = null;
@@ -147,7 +148,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return true; return true;
} }
private object MapSchemaToObject(OpenApiSchema schema, string name = null) private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null)
{ {
if (schema == null) if (schema == null)
{ {
@@ -203,6 +204,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key)); propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
} }
if (schema.AllOf.Count > 0) if (schema.AllOf.Count > 0)
{ {
foreach (var property in schema.AllOf) foreach (var property in schema.AllOf)
@@ -214,7 +216,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
} }
} }
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject; return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject;
default: default:
return null; return null;
@@ -243,19 +245,15 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
return jp; return jp;
} }
else
{
return new JProperty(key, mapped); return new JProperty(key, mapped);
} }
}
else
{
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x); // bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema)); return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
} }
}
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters) private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter>? parameters)
{ {
if (parameters == null) if (parameters == null)
{ {
@@ -272,7 +270,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return newPath; return newPath;
} }
private string MapBasePath(IList<OpenApiServer> servers) private string MapBasePath(IList<OpenApiServer>? servers)
{ {
if (servers == null || servers.Count == 0) if (servers == null || servers.Count == 0)
{ {
@@ -288,7 +286,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return string.Empty; return string.Empty;
} }
private JToken MapOpenApiAnyToJToken(IOpenApiAny any) private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
{ {
if (any == null) if (any == null)
{ {
@@ -303,17 +301,15 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
return JArray.Parse(outputString.ToString()); return JArray.Parse(outputString.ToString());
} }
else
{
return JObject.Parse(outputString.ToString()); return JObject.Parse(outputString.ToString());
} }
}
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers) private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
{ {
var mappedHeaders = headers.ToDictionary( var mappedHeaders = headers.ToDictionary(
item => item.Key, item => item.Key,
item => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
); );
if (!string.IsNullOrEmpty(responseContentType)) if (!string.IsNullOrEmpty(responseContentType))
@@ -324,7 +320,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return mappedHeaders.Keys.Any() ? mappedHeaders : null; return mappedHeaders.Keys.Any() ? mappedHeaders : null;
} }
private IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters) private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
{ {
var list = queryParameters var list = queryParameters
.Where(req => req.Required) .Where(req => req.Required)
@@ -342,7 +338,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return list.Any() ? list : null; return list.Any() ? list : null;
} }
private IList<HeaderModel> MapRequestHeaders(IEnumerable<OpenApiParameter> headers) private IList<HeaderModel>? MapRequestHeaders(IEnumerable<OpenApiParameter> headers)
{ {
var list = headers var list = headers
.Where(req => req.Required) .Where(req => req.Required)
@@ -360,7 +356,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return list.Any() ? list : null; return list.Any() ? list : null;
} }
private MatcherModel GetExampleMatcherModel(OpenApiSchema schema, ExampleValueType type) private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type)
{ {
return type switch return type switch
{ {
@@ -370,7 +366,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private string GetExampleValueAsStringForSchemaType(OpenApiSchema schema) private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
{ {
var value = _exampleValueGenerator.GetExampleValue(schema); var value = _exampleValueGenerator.GetExampleValue(schema);
@@ -381,5 +377,4 @@ namespace WireMock.Net.OpenApiParser.Mappers
_ => value.ToString(), _ => value.ToString(),
}; };
} }
}
} }
@@ -1,25 +1,28 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// A interface defining the example values to use for the different types.
/// </summary>
public interface IWireMockOpenApiParserExampleValues
{ {
/// <summary>
/// A interface defining the example values to use for the different types.
/// </summary>
public interface IWireMockOpenApiParserExampleValues
{
/// <summary> /// <summary>
/// An example value for a Boolean. /// An example value for a Boolean.
/// </summary> /// </summary>
bool Boolean { get; set; } bool Boolean { get; set; }
/// <summary> /// <summary>
/// An example value for an Integer. /// An example value for an Integer.
/// </summary> /// </summary>
int Integer { get; set; } int Integer { get; set; }
/// <summary> /// <summary>
/// An example value for a Float. /// An example value for a Float.
/// </summary> /// </summary>
float Float { get; set; } float Float { get; set; }
/// <summary> /// <summary>
/// An example value for a Double. /// An example value for a Double.
/// </summary> /// </summary>
@@ -53,6 +56,5 @@ namespace WireMock.Net.OpenApiParser.Settings
/// <summary> /// <summary>
/// OpenApi Schema to generate dynamic examples more accurate /// OpenApi Schema to generate dynamic examples more accurate
/// </summary> /// </summary>
OpenApiSchema Schema { get; set; } OpenApiSchema? Schema { get; set; }
}
} }
@@ -1,34 +1,42 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using RandomDataGenerator.FieldOptions; using RandomDataGenerator.FieldOptions;
using RandomDataGenerator.Randomizers; using RandomDataGenerator.Randomizers;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// A class defining the random example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
{ {
/// <summary>
/// A class defining the random example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
{
/// <inheritdoc /> /// <inheritdoc />
public virtual bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } } public virtual bool Boolean { get => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual int Integer { get { return RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; } set { } } public virtual int Integer { get => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual float Float { get { return RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; } set { } } public virtual float Float { get => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual double Double { get { return RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; } set { } } public virtual double Double { get => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual Func<DateTime> Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } } public virtual Func<DateTime> Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual Func<DateTime> DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } } public virtual Func<DateTime> DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } } public virtual byte[] Bytes { get => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual object Object { get; set; } = "example-object"; public virtual object Object { get; set; } = "example-object";
/// <inheritdoc /> /// <inheritdoc />
public virtual string String { get { return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; } set { } } public virtual string String { get => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual OpenApiSchema Schema { get; set; } public virtual OpenApiSchema? Schema { get; set; }
}
} }
@@ -1,32 +1,40 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// A class defining the example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
{ {
/// <summary>
/// A class defining the example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
{
/// <inheritdoc /> /// <inheritdoc />
public virtual bool Boolean { get; set; } = true; public virtual bool Boolean { get; set; } = true;
/// <inheritdoc /> /// <inheritdoc />
public virtual int Integer { get; set; } = 42; public virtual int Integer { get; set; } = 42;
/// <inheritdoc /> /// <inheritdoc />
public virtual float Float { get; set; } = 4.2f; public virtual float Float { get; set; } = 4.2f;
/// <inheritdoc /> /// <inheritdoc />
public virtual double Double { get; set; } = 4.2d; public virtual double Double { get; set; } = 4.2d;
/// <inheritdoc /> /// <inheritdoc />
public virtual Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date; public virtual Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
/// <inheritdoc /> /// <inheritdoc />
public virtual Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow; public virtual Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow;
/// <inheritdoc /> /// <inheritdoc />
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 }; public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
/// <inheritdoc /> /// <inheritdoc />
public virtual object Object { get; set; } = "example-object"; public virtual object Object { get; set; } = "example-object";
/// <inheritdoc /> /// <inheritdoc />
public virtual string String { get; set; } = "example-string"; public virtual string String { get; set; } = "example-string";
/// <inheritdoc /> /// <inheritdoc />
public virtual OpenApiSchema Schema { get; set; } = new OpenApiSchema(); public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
}
} }
@@ -1,12 +1,12 @@
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
/// <summary>
/// The WireMockOpenApiParser Settings
/// </summary>
public class WireMockOpenApiParserSettings
{ {
/// <summary>
/// The WireMockOpenApiParser Settings
/// </summary>
public class WireMockOpenApiParserSettings
{
/// <summary> /// <summary>
/// The number of array items to generate (default is 3). /// The number of array items to generate (default is 3).
/// </summary> /// </summary>
@@ -34,7 +34,7 @@ namespace WireMock.Net.OpenApiParser.Settings
/// - <see cref="WireMockOpenApiParserExampleValues"/> /// - <see cref="WireMockOpenApiParserExampleValues"/>
/// - <see cref="WireMockOpenApiParserDynamicExampleValues"/> /// - <see cref="WireMockOpenApiParserDynamicExampleValues"/>
/// </summary> /// </summary>
public IWireMockOpenApiParserExampleValues ExampleValues { get; set; } public IWireMockOpenApiParserExampleValues? ExampleValues { get; set; }
/// <summary> /// <summary>
/// Is a Header match case insensitive? (default is true). /// Is a Header match case insensitive? (default is true).
@@ -60,5 +60,4 @@ namespace WireMock.Net.OpenApiParser.Settings
/// Are examples generated dynamically? (default is false). /// Are examples generated dynamically? (default is false).
/// </summary> /// </summary>
public bool DynamicExamples { get; set; } = false; public bool DynamicExamples { get; set; } = false;
}
} }
@@ -1,10 +1,10 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
/// <summary>
/// The example value to use
/// </summary>
public enum ExampleValueType
{ {
/// <summary>
/// The example value to use
/// </summary>
public enum ExampleValueType
{
/// <summary> /// <summary>
/// 1. Use a generated example value based on the SchemaType (default). /// 1. Use a generated example value based on the SchemaType (default).
/// 2. If there is no example value defined in the schema, /// 2. If there is no example value defined in the schema,
@@ -16,5 +16,4 @@ namespace WireMock.Net.OpenApiParser.Types
/// Just use a Wildcard (*) character. /// Just use a Wildcard (*) character.
/// </summary> /// </summary>
Wildcard Wildcard
}
} }
@@ -1,7 +1,7 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
internal enum SchemaFormat
{ {
internal enum SchemaFormat
{
Float, Float,
Double, Double,
@@ -21,5 +21,4 @@
Binary, Binary,
Undefined Undefined
}
} }
@@ -1,7 +1,7 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
internal enum SchemaType
{ {
internal enum SchemaType
{
Object, Object,
Array, Array,
@@ -17,5 +17,4 @@
File, File,
Unknown Unknown
}
} }
@@ -1,10 +1,10 @@
using System; using System;
using System.Globalization; using System.Globalization;
namespace WireMock.Net.OpenApiParser.Utils namespace WireMock.Net.OpenApiParser.Utils;
internal static class DateTimeUtils
{ {
internal static class DateTimeUtils
{
public static string ToRfc3339DateTime(DateTime dateTime) public static string ToRfc3339DateTime(DateTime dateTime)
{ {
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo); return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
@@ -14,5 +14,4 @@ namespace WireMock.Net.OpenApiParser.Utils
{ {
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
} }
}
} }
@@ -7,15 +7,15 @@ using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Utils namespace WireMock.Net.OpenApiParser.Utils;
internal class ExampleValueGenerator
{ {
internal class ExampleValueGenerator
{
private readonly WireMockOpenApiParserSettings _settings; private readonly WireMockOpenApiParserSettings _settings;
public ExampleValueGenerator(WireMockOpenApiParserSettings settings) public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings, nameof(settings)); _settings = Guard.NotNull(settings);
// Check if user provided an own implementation // Check if user provided an own implementation
if (settings.ExampleValues is null) if (settings.ExampleValues is null)
@@ -31,7 +31,7 @@ namespace WireMock.Net.OpenApiParser.Utils
} }
} }
public object GetExampleValue(OpenApiSchema schema) public object GetExampleValue(OpenApiSchema? schema)
{ {
var schemaExample = schema?.Example; var schemaExample = schema?.Example;
var schemaEnum = GetRandomEnumValue(schema?.Enum); var schemaEnum = GetRandomEnumValue(schema?.Enum);
@@ -41,7 +41,7 @@ namespace WireMock.Net.OpenApiParser.Utils
switch (schema?.GetSchemaType()) switch (schema?.GetSchemaType())
{ {
case SchemaType.Boolean: case SchemaType.Boolean:
var exampleBoolean = (OpenApiBoolean)schemaExample; var exampleBoolean = schemaExample as OpenApiBoolean;
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value; return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
case SchemaType.Integer: case SchemaType.Integer:
@@ -112,7 +112,7 @@ namespace WireMock.Net.OpenApiParser.Utils
} }
} }
private static IOpenApiAny GetRandomEnumValue(IList<IOpenApiAny> schemaEnum) private static IOpenApiAny? GetRandomEnumValue(IList<IOpenApiAny>? schemaEnum)
{ {
if (schemaEnum?.Count > 0) if (schemaEnum?.Count > 0)
{ {
@@ -123,5 +123,4 @@ namespace WireMock.Net.OpenApiParser.Utils
return null; return null;
} }
}
} }
@@ -12,7 +12,7 @@
<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>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<LangVersion>8.0</LangVersion> <LangVersion>10</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -22,6 +22,10 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" /> <PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" />
<PackageReference Include="Nullable" Version="1.3.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" /> <PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" />
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" /> <PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using JetBrains.Annotations; using JetBrains.Annotations;
@@ -9,13 +9,13 @@ using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Mappers; using WireMock.Net.OpenApiParser.Mappers;
using WireMock.Net.OpenApiParser.Settings; using WireMock.Net.OpenApiParser.Settings;
namespace WireMock.Net.OpenApiParser namespace WireMock.Net.OpenApiParser;
/// <summary>
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
/// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser
{ {
/// <summary>
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
/// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser
{
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader(); private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
@@ -60,9 +60,8 @@ namespace WireMock.Net.OpenApiParser
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
[PublicAPI] [PublicAPI]
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null) public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
{ {
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
} }
}
} }
@@ -1,4 +1,4 @@
using RestEase; using RestEase;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Http.Headers; using System.Net.Http.Headers;
@@ -179,6 +179,20 @@ namespace WireMock.Client
[Post("scenarios")] [Post("scenarios")]
Task<StatusModel> ResetScenariosAsync(); Task<StatusModel> ResetScenariosAsync();
/// <summary>
/// Delete (reset) a specific scenario
/// </summary>
[Delete("scenarios/{name}")]
[AllowAnyStatusCode]
Task<StatusModel> DeleteScenarioAsync([Path] string name);
/// <summary>
/// Delete (reset) all scenarios
/// </summary>
[Post("scenarios/{name}/reset")]
[AllowAnyStatusCode]
Task<StatusModel> ResetScenarioAsync([Path] string name);
/// <summary> /// <summary>
/// Create a new File /// Create a new File
/// </summary> /// </summary>
+1 -1
View File
@@ -15,7 +15,7 @@ namespace WireMock.Net.StandAlone;
/// </summary> /// </summary>
public static class StandAloneApp public static class StandAloneApp
{ {
private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version.ToString(); private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version!.ToString();
/// <summary> /// <summary>
/// Start WireMock.Net standalone Server based on the WireMockServerSettings. /// Start WireMock.Net standalone Server based on the WireMockServerSettings.
@@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Util; using WireMock.Util;
@@ -35,6 +36,8 @@ internal static class HttpResponseMessageHelper
contentEncodingHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)).Value; contentEncodingHeader = headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentEncoding, StringComparison.OrdinalIgnoreCase)).Value;
} }
if (httpResponseMessage.StatusCode != HttpStatusCode.NoContent) // A body is not allowed for 204.
{
var bodyParserSettings = new BodyParserSettings var bodyParserSettings = new BodyParserSettings
{ {
Stream = stream, Stream = stream,
@@ -45,6 +48,7 @@ internal static class HttpResponseMessageHelper
}; };
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false); responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
} }
}
foreach (var header in headers) foreach (var header in headers)
{ {
+1 -1
View File
@@ -89,7 +89,7 @@ internal class WebhookSender
}; };
// Create HttpRequestMessage // Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequest.Url); var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequestUrl);
// Delay (if required) // Delay (if required)
if (TryGetDelay(webhookRequest, out var delay)) if (TryGetDelay(webhookRequest, out var delay))
+4 -4
View File
@@ -12,13 +12,13 @@ public class LogEntry : ILogEntry
public Guid Guid { get; set; } public Guid Guid { get; set; }
/// <inheritdoc cref="ILogEntry.RequestMessage" /> /// <inheritdoc cref="ILogEntry.RequestMessage" />
public IRequestMessage RequestMessage { get; set; } public IRequestMessage RequestMessage { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.ResponseMessage" /> /// <inheritdoc cref="ILogEntry.ResponseMessage" />
public IResponseMessage ResponseMessage { get; set; } public IResponseMessage ResponseMessage { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.RequestMatchResult" /> /// <inheritdoc cref="ILogEntry.RequestMatchResult" />
public IRequestMatchResult RequestMatchResult { get; set; } public IRequestMatchResult RequestMatchResult { get; set; } = null!;
/// <inheritdoc cref="ILogEntry.MappingGuid" /> /// <inheritdoc cref="ILogEntry.MappingGuid" />
public Guid? MappingGuid { get; set; } public Guid? MappingGuid { get; set; }
@@ -33,5 +33,5 @@ public class LogEntry : ILogEntry
public string? PartialMappingTitle { get; set; } public string? PartialMappingTitle { get; set; }
/// <inheritdoc cref="ILogEntry.PartialMatchResult" /> /// <inheritdoc cref="ILogEntry.PartialMatchResult" />
public IRequestMatchResult PartialMatchResult { get; set; } public IRequestMatchResult PartialMatchResult { get; set; } = null!;
} }
@@ -2,14 +2,14 @@ using Newtonsoft.Json;
using System; using System;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
namespace WireMock.Logging namespace WireMock.Logging;
/// <summary>
/// WireMockConsoleLogger which logs to Console
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockConsoleLogger : IWireMockLogger
{ {
/// <summary>
/// WireMockConsoleLogger which logs to Console
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockConsoleLogger : IWireMockLogger
{
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class. /// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
/// </summary> /// </summary>
@@ -70,5 +70,4 @@ namespace WireMock.Logging
return $"{DateTime.UtcNow} [{level}] : {message}"; return $"{DateTime.UtcNow} [{level}] : {message}";
} }
}
} }
@@ -1,14 +1,14 @@
using System; using System;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
namespace WireMock.Logging namespace WireMock.Logging;
/// <summary>
/// WireMockNullLogger which does not log.
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockNullLogger : IWireMockLogger
{ {
/// <summary>
/// WireMockNullLogger which does not log.
/// </summary>
/// <seealso cref="IWireMockLogger" />
public class WireMockNullLogger : IWireMockLogger
{
/// <see cref="IWireMockLogger.Debug"/> /// <see cref="IWireMockLogger.Debug"/>
public void Debug(string formatString, params object[] args) public void Debug(string formatString, params object[] args)
{ {
@@ -44,5 +44,4 @@ namespace WireMock.Logging
{ {
// Log nothing // Log nothing
} }
}
} }
+1
View File
@@ -21,6 +21,7 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
/// <inheritdoc cref="IMatcher.MatchBehaviour"/> /// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; } public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/> /// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; } public bool ThrowException { get; }
+9 -7
View File
@@ -1,13 +1,13 @@
using System.Text; using System.Text;
using WireMock.Types; using WireMock.Types;
namespace WireMock.Util namespace WireMock.Util;
/// <summary>
/// BodyData
/// </summary>
public class BodyData : IBodyData
{ {
/// <summary>
/// BodyData
/// </summary>
public class BodyData : IBodyData
{
/// <inheritdoc cref="IBodyData.Encoding" /> /// <inheritdoc cref="IBodyData.Encoding" />
public Encoding? Encoding { get; set; } public Encoding? Encoding { get; set; }
@@ -37,5 +37,7 @@ namespace WireMock.Util
/// <inheritdoc cref="IRequestMessage.DetectedCompression" /> /// <inheritdoc cref="IRequestMessage.DetectedCompression" />
public string? DetectedCompression { get; set; } public string? DetectedCompression { get; set; }
}
/// <inheritdoc />
public string? IsFuncUsed { get; set; }
} }
@@ -3,13 +3,13 @@ using System.Collections.Concurrent;
using WireMock.Handlers; using WireMock.Handlers;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Types;
using WireMock.Util; using WireMock.Util;
#if !USE_ASPNETCORE #if !USE_ASPNETCORE
using Owin; using Owin;
#else #else
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder; using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using WireMock.Types;
#endif #endif
namespace WireMock.Owin; namespace WireMock.Owin;
@@ -69,4 +69,8 @@ internal interface IWireMockMiddlewareOptions
bool CustomCertificateDefined { get; } bool CustomCertificateDefined { get; }
bool? SaveUnmatchedRequests { get; set; } bool? SaveUnmatchedRequests { get; set; }
bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
} }
@@ -81,11 +81,11 @@ namespace WireMock.Owin.Mappers
var statusCodeType = responseMessage.StatusCode?.GetType(); var statusCodeType = responseMessage.StatusCode?.GetType();
switch (statusCodeType) switch (statusCodeType)
{ {
case Type typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum: case { } typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!); response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
break; break;
case Type typeAsString when typeAsString == typeof(string): case { } typeAsString when typeAsString == typeof(string):
// Note: this case will also match on null // Note: this case will also match on null
int.TryParse(responseMessage.StatusCode as string, out int result); int.TryParse(responseMessage.StatusCode as string, out int result);
response.StatusCode = MapStatusCode(result); response.StatusCode = MapStatusCode(result);
@@ -98,9 +98,16 @@ namespace WireMock.Owin.Mappers
SetResponseHeaders(responseMessage, response); SetResponseHeaders(responseMessage, response);
if (bytes != null) if (bytes != null)
{
try
{ {
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
} }
catch (Exception ex)
{
_options.Logger.Warn("Error writing response body. Exception : {0}", ex);
}
}
} }
private int MapStatusCode(int code) private int MapStatusCode(int code)
@@ -123,7 +130,7 @@ namespace WireMock.Owin.Mappers
switch (responseMessage.BodyData?.DetectedBodyType) switch (responseMessage.BodyData?.DetectedBodyType)
{ {
case BodyType.String: case BodyType.String:
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString); return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString!);
case BodyType.Json: case BodyType.Json:
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true var formatting = responseMessage.BodyData.BodyAsJsonIndented == true
@@ -136,7 +143,7 @@ namespace WireMock.Owin.Mappers
return responseMessage.BodyData.BodyAsBytes; return responseMessage.BodyData.BodyAsBytes;
case BodyType.File: case BodyType.File:
return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile); return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile!);
} }
return null; return null;
@@ -154,7 +161,7 @@ namespace WireMock.Owin.Mappers
}); });
// Set other headers // Set other headers
foreach (var item in responseMessage.Headers) foreach (var item in responseMessage.Headers!)
{ {
var headerName = item.Key; var headerName = item.Key;
var value = item.Value; var value = item.Value;
+12 -9
View File
@@ -32,22 +32,25 @@ namespace WireMock.Owin
private readonly IOwinRequestMapper _requestMapper; private readonly IOwinRequestMapper _requestMapper;
private readonly IOwinResponseMapper _responseMapper; private readonly IOwinResponseMapper _responseMapper;
private readonly IMappingMatcher _mappingMatcher; private readonly IMappingMatcher _mappingMatcher;
private readonly LogEntryMapper _logEntryMapper;
#if !USE_ASPNETCORE #if !USE_ASPNETCORE
public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next) public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) : base(next)
{ {
_options = Guard.NotNull(options, nameof(options)); _options = Guard.NotNull(options);
_requestMapper = Guard.NotNull(requestMapper, nameof(requestMapper)); _requestMapper = Guard.NotNull(requestMapper);
_responseMapper = Guard.NotNull(responseMapper, nameof(responseMapper)); _responseMapper = Guard.NotNull(responseMapper);
_mappingMatcher = Guard.NotNull(mappingMatcher, nameof(mappingMatcher)); _mappingMatcher = Guard.NotNull(mappingMatcher);
_logEntryMapper = new LogEntryMapper(options);
} }
#else #else
public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher) public WireMockMiddleware(Next next, IWireMockMiddlewareOptions options, IOwinRequestMapper requestMapper, IOwinResponseMapper responseMapper, IMappingMatcher mappingMatcher)
{ {
_options = Guard.NotNull(options, nameof(options)); _options = Guard.NotNull(options);
_requestMapper = Guard.NotNull(requestMapper, nameof(requestMapper)); _requestMapper = Guard.NotNull(requestMapper);
_responseMapper = Guard.NotNull(responseMapper, nameof(responseMapper)); _responseMapper = Guard.NotNull(responseMapper);
_mappingMatcher = Guard.NotNull(mappingMatcher, nameof(mappingMatcher)); _mappingMatcher = Guard.NotNull(mappingMatcher);
_logEntryMapper = new LogEntryMapper(options);
} }
#endif #endif
@@ -264,7 +267,7 @@ namespace WireMock.Owin
private void LogRequest(LogEntry entry, bool addRequest) private void LogRequest(LogEntry entry, bool addRequest)
{ {
_options.Logger.DebugRequestResponse(LogEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/")); _options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/"));
if (addRequest) if (addRequest)
{ {
@@ -26,7 +26,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>(); public ConcurrentDictionary<Guid, IMapping> Mappings { get; } = new ConcurrentDictionary<Guid, IMapping>();
public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new(); public ConcurrentDictionary<string, ScenarioState> Scenarios { get; } = new(StringComparer.OrdinalIgnoreCase);
public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new(); public ConcurrentObservableCollection<LogEntry> LogEntries { get; } = new();
@@ -84,4 +84,10 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
/// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/> /// <inheritdoc cref="IWireMockMiddlewareOptions.SaveUnmatchedRequests"/>
public bool? SaveUnmatchedRequests { get; set; } public bool? SaveUnmatchedRequests { get; set; }
/// <inheritdoc />
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <inheritdoc />
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
} }
@@ -1,3 +1,4 @@
#pragma warning disable CS1591
namespace WireMock.Pact.Models.V2; namespace WireMock.Pact.Models.V2;
public class Interaction public class Interaction
@@ -1,7 +1,8 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class MatchingRule
{ {
public class MatchingRule
{
/// <summary> /// <summary>
/// type or regex /// type or regex
/// </summary> /// </summary>
@@ -21,5 +22,4 @@ namespace WireMock.Pact.Models.V2
/// Used for Match = "regex" /// Used for Match = "regex"
/// </summary> /// </summary>
public string Regex { get; set; } public string Regex { get; set; }
}
} }
+4 -4
View File
@@ -1,9 +1,9 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class Metadata
{ {
public class Metadata
{
public string PactSpecificationVersion { get; set; } public string PactSpecificationVersion { get; set; }
public PactSpecification PactSpecification { get; set; } = new PactSpecification(); public PactSpecification PactSpecification { get; set; } = new PactSpecification();
}
} }
+4 -4
View File
@@ -1,9 +1,10 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Pact.Models.V2 namespace WireMock.Pact.Models.V2;
public class Pact
{ {
public class Pact
{
public Pacticipant Consumer { get; set; } public Pacticipant Consumer { get; set; }
public List<Interaction> Interactions { get; set; } = new List<Interaction>(); public List<Interaction> Interactions { get; set; } = new List<Interaction>();
@@ -11,5 +12,4 @@ namespace WireMock.Pact.Models.V2
public Metadata Metadata { get; set; } public Metadata Metadata { get; set; }
public Pacticipant Provider { get; set; } public Pacticipant Provider { get; set; }
}
} }
@@ -1,4 +1,6 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Constants;
namespace WireMock.Pact.Models.V2; namespace WireMock.Pact.Models.V2;
@@ -6,7 +8,7 @@ public class PactRequest
{ {
public IDictionary<string, string>? Headers { get; set; } public IDictionary<string, string>? Headers { get; set; }
public string Method { get; set; } = "GET"; public string Method { get; set; } = HttpRequestMethod.GET;
public string? Path { get; set; } = "/"; public string? Path { get; set; } = "/";
@@ -1,3 +1,4 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Pact.Models.V2; namespace WireMock.Pact.Models.V2;
+4 -4
View File
@@ -1,11 +1,11 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class PactRust
{ {
public class PactRust
{
public string Ffi { get; set; } public string Ffi { get; set; }
public string Mockserver { get; set; } public string Mockserver { get; set; }
public string Models { get; set; } public string Models { get; set; }
}
} }
@@ -1,7 +1,7 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class PactSpecification
{ {
public class PactSpecification
{
public string Version { get; set; } = "2.0"; public string Version { get; set; } = "2.0";
}
} }
@@ -1,7 +1,7 @@
namespace WireMock.Pact.Models.V2 #pragma warning disable CS1591
namespace WireMock.Pact.Models.V2;
public class Pacticipant
{ {
public class Pacticipant
{
public string Name { get; set; } public string Name { get; set; }
}
} }
@@ -1,11 +1,11 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
namespace WireMock.Pact.Models.V2 namespace WireMock.Pact.Models.V2;
public class ProviderState
{ {
public class ProviderState
{
public string Name { get; set; } public string Name { get; set; }
public IDictionary<string, string> Params { get; set; } public IDictionary<string, string> Params { get; set; }
}
} }
+1 -73
View File
@@ -1,17 +1,10 @@
using Stef.Validation;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Constants; using Stef.Validation;
using WireMock.Http; using WireMock.Http;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization; using WireMock.Serialization;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Types;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Proxy; namespace WireMock.Proxy;
@@ -62,69 +55,4 @@ internal class ProxyHelper
return (responseMessage, newMapping); return (responseMessage, newMapping);
} }
private IMapping ToMapping(ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
{
var excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { };
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
var request = Request.Create();
request.WithPath(requestMessage.Path);
request.UsingMethod(requestMessage.Method);
requestMessage.Query?.Loop((key, value) => request.WithParam(key, false, value.ToArray()));
requestMessage.Cookies?.Loop((key, value) =>
{
if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase))
{
request.WithCookie(key, value);
}
});
var allExcludedHeaders = new List<string>(excludedHeaders) { "Cookie" };
requestMessage.Headers?.Loop((key, value) =>
{
if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase))
{
request.WithHeader(key, value.ToArray());
}
});
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
switch (requestMessage.BodyData?.DetectedBodyType)
{
case BodyType.Json:
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson!, true, throwExceptionWhenMatcherFails));
break;
case BodyType.String:
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, false, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString));
break;
case BodyType.Bytes:
request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
break;
}
var response = Response.Create(responseMessage);
return new Mapping
(
guid: Guid.NewGuid(),
title: $"Proxy Mapping for {requestMessage.Method} {requestMessage.Path}",
description: string.Empty,
path: null,
settings: _settings,
request,
response,
priority: WireMockConstants.ProxyPriority, // This was 0
scenario: null,
executionConditionState: null,
nextState: null,
stateTimes: null,
webhooks: null,
useWebhooksFireAndForget: null,
timeSettings: null
);
}
} }
@@ -1,10 +1,10 @@
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License. // This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root. // For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System.Linq; using System.Linq;
using WireMock.Http; using Stef.Validation;
using WireMock.Constants;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using Stef.Validation;
namespace WireMock.RequestBuilders; namespace WireMock.RequestBuilders;
@@ -13,63 +13,63 @@ public partial class Request
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingConnect(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingConnect(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.CONNECT)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.CONNECT));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingDelete(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingDelete(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.DELETE)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.DELETE));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingGet(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingGet(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.GET)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.GET));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingHead(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingHead(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.HEAD)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.HEAD));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingOptions(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingOptions(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.OPTIONS)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.OPTIONS));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingPost(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingPost(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.POST)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.POST));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingPatch(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingPatch(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.PATCH)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.PATCH));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingPut(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingPut(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.PUT)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.PUT));
return this; return this;
} }
/// <inheritdoc /> /// <inheritdoc />
public IRequestBuilder UsingTrace(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch) public IRequestBuilder UsingTrace(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{ {
_requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethods.TRACE)); _requestMatchers.Add(new RequestMessageMethodMatcher(matchBehaviour, MatchOperator.Or, HttpRequestMethod.TRACE));
return this; return this;
} }
+24 -3
View File
@@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using Stef.Validation; using Stef.Validation;
using WireMock.Models; using WireMock.Models;
using WireMock.Owin;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
@@ -26,7 +27,7 @@ public class RequestMessage : IRequestMessage
public string AbsoluteUrl { get; } public string AbsoluteUrl { get; }
/// <inheritdoc cref="IRequestMessage.ProxyUrl" /> /// <inheritdoc cref="IRequestMessage.ProxyUrl" />
public string ProxyUrl { get; set; } public string? ProxyUrl { get; set; }
/// <inheritdoc cref="IRequestMessage.DateTime" /> /// <inheritdoc cref="IRequestMessage.DateTime" />
public DateTime DateTime { get; set; } public DateTime DateTime { get; set; }
@@ -91,16 +92,36 @@ public class RequestMessage : IRequestMessage
/// <inheritdoc cref="IRequestMessage.Origin" /> /// <inheritdoc cref="IRequestMessage.Origin" />
public string Origin { get; } public string Origin { get; }
/// <summary>
/// Used for Unit Testing
/// </summary>
public RequestMessage(
UrlDetails urlDetails,
string method,
string clientIP,
IBodyData? bodyData = null,
IDictionary<string, string[]>? headers = null,
IDictionary<string, string>? cookies = null) : this(null, urlDetails, method, clientIP, bodyData, headers, cookies)
{
}
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessage"/> class. /// Initializes a new instance of the <see cref="RequestMessage"/> class.
/// </summary> /// </summary>
/// <param name="options">The<seealso cref="IWireMockMiddlewareOptions"/>.</param>
/// <param name="urlDetails">The original url details.</param> /// <param name="urlDetails">The original url details.</param>
/// <param name="method">The HTTP method.</param> /// <param name="method">The HTTP method.</param>
/// <param name="clientIP">The client IP Address.</param> /// <param name="clientIP">The client IP Address.</param>
/// <param name="bodyData">The BodyData.</param> /// <param name="bodyData">The BodyData.</param>
/// <param name="headers">The headers.</param> /// <param name="headers">The headers.</param>
/// <param name="cookies">The cookies.</param> /// <param name="cookies">The cookies.</param>
public RequestMessage(UrlDetails urlDetails, string method, string clientIP, IBodyData? bodyData = null, IDictionary<string, string[]>? headers = null, IDictionary<string, string>? cookies = null) internal RequestMessage(
IWireMockMiddlewareOptions? options,
UrlDetails urlDetails, string method,
string clientIP,
IBodyData? bodyData = null,
IDictionary<string, string[]>? headers = null,
IDictionary<string, string>? cookies = null)
{ {
Guard.NotNull(urlDetails, nameof(urlDetails)); Guard.NotNull(urlDetails, nameof(urlDetails));
Guard.NotNull(method, nameof(method)); Guard.NotNull(method, nameof(method));
@@ -134,7 +155,7 @@ public class RequestMessage : IRequestMessage
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value)); Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
Cookies = cookies; Cookies = cookies;
RawQuery = urlDetails.Url.Query; RawQuery = urlDetails.Url.Query;
Query = QueryStringParser.Parse(RawQuery); Query = QueryStringParser.Parse(RawQuery, options?.QueryParameterMultipleValueSupport);
} }
/// <summary> /// <summary>
@@ -21,7 +21,8 @@ public partial class Response
{ {
DetectedBodyType = BodyType.String, DetectedBodyType = BodyType.String,
BodyAsString = bodyFactory(req), BodyAsString = bodyFactory(req),
Encoding = encoding ?? Encoding.UTF8 Encoding = encoding ?? Encoding.UTF8,
IsFuncUsed = "Func<IRequestMessage, string>"
} }
}); });
} }
@@ -37,7 +38,8 @@ public partial class Response
{ {
DetectedBodyType = BodyType.String, DetectedBodyType = BodyType.String,
BodyAsString = await bodyFactory(req).ConfigureAwait(false), BodyAsString = await bodyFactory(req).ConfigureAwait(false),
Encoding = encoding ?? Encoding.UTF8 Encoding = encoding ?? Encoding.UTF8,
IsFuncUsed = "Func<IRequestMessage, Task<string>>"
} }
}); });
} }
@@ -325,15 +325,15 @@ public partial class Response : IResponseBuilder
break; break;
default: default:
throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported."); throw new NotSupportedException($"TransformerType '{TransformerType}' is not supported.");
} }
return (responseMessageTransformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null); return (responseMessageTransformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
} }
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true) if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true && responseMessage.BodyData?.BodyAsFile is not null)
{ {
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData!.BodyAsFile); ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
} }
return (responseMessage, null); return (responseMessage, null);
+1 -1
View File
@@ -8,7 +8,7 @@ public class ScenarioState
/// <summary> /// <summary>
/// Gets or sets the Name (from the Scenario). /// Gets or sets the Name (from the Scenario).
/// </summary> /// </summary>
public string? Name { get; set; } public string Name { get; set; } = null!;
/// <summary> /// <summary>
/// Gets or sets the NextState. /// Gets or sets the NextState.
@@ -1,16 +1,25 @@
using System.Linq; using System.Linq;
using Stef.Validation;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Owin;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Types; using WireMock.Types;
namespace WireMock.Serialization; namespace WireMock.Serialization;
internal static class LogEntryMapper internal class LogEntryMapper
{ {
public static LogEntryModel Map(ILogEntry logEntry) private readonly IWireMockMiddlewareOptions _options;
public LogEntryMapper(IWireMockMiddlewareOptions options)
{
_options = Guard.NotNull(options);
}
public LogEntryModel Map(ILogEntry logEntry)
{ {
var logRequestModel = new LogRequestModel var logRequestModel = new LogRequestModel
{ {
@@ -78,25 +87,7 @@ internal static class LogEntryMapper
logResponseModel.DetectedBodyType = logEntry.ResponseMessage.BodyData.DetectedBodyType; logResponseModel.DetectedBodyType = logEntry.ResponseMessage.BodyData.DetectedBodyType;
logResponseModel.DetectedBodyTypeFromContentType = logEntry.ResponseMessage.BodyData.DetectedBodyTypeFromContentType; logResponseModel.DetectedBodyTypeFromContentType = logEntry.ResponseMessage.BodyData.DetectedBodyTypeFromContentType;
switch (logEntry.ResponseMessage.BodyData.DetectedBodyType) MapBody(logEntry, logResponseModel);
{
case BodyType.String:
logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString;
break;
case BodyType.Json:
logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson;
break;
case BodyType.Bytes:
logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes;
break;
case BodyType.File:
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
break;
}
logResponseModel.BodyEncoding = logEntry.ResponseMessage.BodyData.Encoding != null logResponseModel.BodyEncoding = logEntry.ResponseMessage.BodyData.Encoding != null
? new EncodingModel ? new EncodingModel
@@ -124,6 +115,36 @@ internal static class LogEntryMapper
}; };
} }
private void MapBody(ILogEntry logEntry, LogResponseModel logResponseModel)
{
switch (logEntry.ResponseMessage.BodyData!.DetectedBodyType)
{
case BodyType.String:
if (!string.IsNullOrEmpty(logEntry.ResponseMessage.BodyData.IsFuncUsed) && _options.DoNotSaveDynamicResponseInLogEntry == true)
{
logResponseModel.Body = logEntry.ResponseMessage.BodyData.IsFuncUsed;
}
else
{
logResponseModel.Body = logEntry.ResponseMessage.BodyData.BodyAsString;
}
break;
case BodyType.Json:
logResponseModel.BodyAsJson = logEntry.ResponseMessage.BodyData.BodyAsJson;
break;
case BodyType.Bytes:
logResponseModel.BodyAsBytes = logEntry.ResponseMessage.BodyData.BodyAsBytes;
break;
case BodyType.File:
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
break;
}
}
private static LogRequestMatchModel? Map(IRequestMatchResult? matchResult) private static LogRequestMatchModel? Map(IRequestMatchResult? matchResult)
{ {
if (matchResult == null) if (matchResult == null)
@@ -13,11 +13,8 @@ internal class MappingToFileSaver
public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter) public MappingToFileSaver(WireMockServerSettings settings, MappingConverter mappingConverter)
{ {
Guard.NotNull(settings); _settings = Guard.NotNull(settings);
Guard.NotNull(mappingConverter); _mappingConverter = Guard.NotNull(mappingConverter);
_settings = settings;
_mappingConverter = mappingConverter;
} }
public void SaveMappingToFile(IMapping mapping, string? folder = null) public void SaveMappingToFile(IMapping mapping, string? folder = null)
@@ -30,17 +27,31 @@ internal class MappingToFileSaver
} }
var model = _mappingConverter.ToMappingModel(mapping); var model = _mappingConverter.ToMappingModel(mapping);
string filename = (!string.IsNullOrEmpty(mapping.Title) ? SanitizeFileName(mapping.Title) : mapping.Guid.ToString()) + ".json";
string path = Path.Combine(folder, filename); var filename = BuildSanitizedFileName(mapping);
var path = Path.Combine(folder, filename);
_settings.Logger.Info("Saving Mapping file {0}", filename); _settings.Logger.Info("Saving Mapping file {0}", path);
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault)); _settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault));
} }
private static string SanitizeFileName(string name, char replaceChar = '_') private string BuildSanitizedFileName(IMapping mapping, char replaceChar = '_')
{ {
return Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar)); string name;
if (!string.IsNullOrEmpty(mapping.Title))
{
name = mapping.Title!;
if (_settings.ProxyAndRecordSettings?.AppendGuidToSavedMappingFile == true)
{
name += $"{replaceChar}{mapping.Guid}";
}
}
else
{
name = mapping.Guid.ToString();
}
return $"{Path.GetInvalidFileNameChars().Aggregate(name, (current, c) => current.Replace(c, replaceChar))}.json";
} }
} }
@@ -125,9 +125,9 @@ public interface IRespondWithAProvider
/// <summary> /// <summary>
/// Support FireAndForget for any configured Webhooks /// Support FireAndForget for any configured Webhooks
/// </summary> /// </summary>
/// <param name="UseWebhooksFireAndForget"></param> /// <param name="useWebhooksFireAndForget"></param>
/// <returns></returns> /// <returns></returns>
IRespondWithAProvider WithWebhookFireAndForget(bool UseWebhooksFireAndForget); IRespondWithAProvider WithWebhookFireAndForget(bool useWebhooksFireAndForget);
/// <summary> /// <summary>
/// Add a Webhook to call after the response has been generated. /// Add a Webhook to call after the response has been generated.
@@ -141,7 +141,7 @@ public interface IRespondWithAProvider
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns> /// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook( IRespondWithAProvider WithWebhook(
string url, string url,
string? method = "post", string method = "post",
IDictionary<string, WireMockList<string>>? headers = null, IDictionary<string, WireMockList<string>>? headers = null,
string? body = null, string? body = null,
bool useTransformer = true, bool useTransformer = true,
@@ -160,7 +160,7 @@ public interface IRespondWithAProvider
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns> /// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook( IRespondWithAProvider WithWebhook(
string url, string url,
string? method = "post", string method = "post",
IDictionary<string, WireMockList<string>>? headers = null, IDictionary<string, WireMockList<string>>? headers = null,
object? body = null, object? body = null,
bool useTransformer = true, bool useTransformer = true,
@@ -30,7 +30,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
private readonly WireMockServerSettings _settings; private readonly WireMockServerSettings _settings;
private readonly bool _saveToFile; private readonly bool _saveToFile;
private bool _useWebhookFireAndForget = false; private bool _useWebhookFireAndForget;
public Guid Guid { get; private set; } = Guid.NewGuid(); public Guid Guid { get; private set; } = Guid.NewGuid();
+25 -63
View File
@@ -3,9 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http;
using System.Text; using System.Text;
using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@@ -18,11 +16,9 @@ using WireMock.Http;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Proxy;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Serialization; using WireMock.Serialization;
using WireMock.Settings;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
@@ -46,6 +42,8 @@ public partial class WireMockServer
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true); private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$"); private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private static readonly RegexMatcher AdminScenariosNameMatcher = new(@"^\/__admin\/scenarios\/.+$");
private static readonly RegexMatcher AdminScenariosNameWithResetMatcher = new(@"^\/__admin\/scenarios\/.+\/reset$");
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher; private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
@@ -93,9 +91,11 @@ public partial class WireMockServer
// __admin/scenarios // __admin/scenarios
Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet)); Given(Request.Create().WithPath(AdminScenarios).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosGet));
Given(Request.Create().WithPath(AdminScenarios).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); Given(Request.Create().WithPath(AdminScenarios).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset));
Given(Request.Create().WithPath(AdminScenariosNameMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset));
// __admin/scenarios/reset // __admin/scenarios/reset
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset)); Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset));
Given(Request.Create().WithPath(AdminScenariosNameWithResetMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(ScenarioReset));
// __admin/files/{filename} // __admin/files/{filename}
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost)); Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FilePost));
@@ -204,62 +204,6 @@ public partial class WireMockServer
} }
#endregion #endregion
#region Proxy and Record
private HttpClient? _httpClientForProxy;
private void InitProxyAndRecord(WireMockServerSettings settings)
{
if (settings.ProxyAndRecordSettings == null)
{
_httpClientForProxy = null;
DeleteMapping(ProxyMappingGuid);
return;
}
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
if (settings.StartAdminInterface == true)
{
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
}
proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
}
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var proxyHelper = new ProxyHelper(settings);
var (responseMessage, mapping) = await proxyHelper.SendAsync(
null,
_settings.ProxyAndRecordSettings!,
_httpClientForProxy!,
requestMessage,
proxyUriWithRequestPathAndQuery.AbsoluteUri
).ConfigureAwait(false);
if (mapping != null)
{
if (settings.ProxyAndRecordSettings.SaveMapping)
{
_options.Mappings.TryAdd(mapping.Guid, mapping);
}
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
{
_mappingToFileSaver.SaveMappingToFile(mapping);
}
}
return responseMessage;
}
#endregion
#region Settings #region Settings
private IResponseMessage SettingsGet(IRequestMessage requestMessage) private IResponseMessage SettingsGet(IRequestMessage requestMessage)
{ {
@@ -277,6 +221,9 @@ public partial class WireMockServer
UseRegexExtended = _settings.UseRegexExtended, UseRegexExtended = _settings.UseRegexExtended,
WatchStaticMappings = _settings.WatchStaticMappings, WatchStaticMappings = _settings.WatchStaticMappings,
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories, WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
HostingScheme = _settings.HostingScheme,
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
#if USE_ASPNETCORE #if USE_ASPNETCORE
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString() CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
@@ -305,6 +252,8 @@ public partial class WireMockServer
_settings.UseRegexExtended = settings.UseRegexExtended; _settings.UseRegexExtended = settings.UseRegexExtended;
_settings.WatchStaticMappings = settings.WatchStaticMappings; _settings.WatchStaticMappings = settings.WatchStaticMappings;
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories; _settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
InitSettings(_settings); InitSettings(_settings);
@@ -525,7 +474,7 @@ public partial class WireMockServer
return ResponseMessageBuilder.Create("Request not found", 404); return ResponseMessageBuilder.Create("Request not found", 404);
} }
var model = LogEntryMapper.Map(entry); var model = new LogEntryMapper(_options).Map(entry);
return ToJson(model); return ToJson(model);
} }
@@ -546,9 +495,10 @@ public partial class WireMockServer
#region Requests #region Requests
private IResponseMessage RequestsGet(IRequestMessage requestMessage) private IResponseMessage RequestsGet(IRequestMessage requestMessage)
{ {
var logEntryMapper = new LogEntryMapper(_options);
var result = LogEntries var result = LogEntries
.Where(r => !r.RequestMessage.Path.StartsWith("/__admin/")) .Where(r => !r.RequestMessage.Path.StartsWith("/__admin/"))
.Select(LogEntryMapper.Map); .Select(logEntryMapper.Map);
return ToJson(result); return ToJson(result);
} }
@@ -578,7 +528,8 @@ public partial class WireMockServer
} }
} }
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(LogEntryMapper.Map); var logEntryMapper = new LogEntryMapper(_options);
var result = dict.OrderBy(x => x.Value.AverageTotalScore).Select(x => x.Key).Select(logEntryMapper.Map);
return ToJson(result); return ToJson(result);
} }
@@ -605,6 +556,17 @@ public partial class WireMockServer
return ResponseMessageBuilder.Create("Scenarios reset"); return ResponseMessageBuilder.Create("Scenarios reset");
} }
private IResponseMessage ScenarioReset(IRequestMessage requestMessage)
{
var name = string.Equals(HttpRequestMethod.DELETE, requestMessage.Method, StringComparison.OrdinalIgnoreCase) ?
requestMessage.Path.Substring(AdminScenarios.Length + 1) :
requestMessage.Path.Split('/').Reverse().Skip(1).First();
return ResetScenario(name) ?
ResponseMessageBuilder.Create("Scenario reset") :
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", 404);
}
#endregion #endregion
#region Pact #region Pact
@@ -0,0 +1,68 @@
using System;
using System.Net.Http;
using System.Threading.Tasks;
using WireMock.Constants;
using WireMock.Http;
using WireMock.Proxy;
using WireMock.RequestBuilders;
using WireMock.ResponseProviders;
using WireMock.Settings;
namespace WireMock.Server;
public partial class WireMockServer
{
private HttpClient? _httpClientForProxy;
private void InitProxyAndRecord(WireMockServerSettings settings)
{
if (settings.ProxyAndRecordSettings == null)
{
_httpClientForProxy = null;
DeleteMapping(ProxyMappingGuid);
return;
}
_httpClientForProxy = HttpClientBuilder.Build(settings.ProxyAndRecordSettings);
var proxyRespondProvider = Given(Request.Create().WithPath("/*").UsingAnyMethod()).WithGuid(ProxyMappingGuid).WithTitle("Default Proxy Mapping on /*");
if (settings.StartAdminInterface == true)
{
proxyRespondProvider.AtPriority(WireMockConstants.ProxyPriority);
}
proxyRespondProvider.RespondWith(new ProxyAsyncResponseProvider(ProxyAndRecordAsync, settings));
}
private async Task<IResponseMessage> ProxyAndRecordAsync(IRequestMessage requestMessage, WireMockServerSettings settings)
{
var requestUri = new Uri(requestMessage.Url);
var proxyUri = new Uri(settings.ProxyAndRecordSettings!.Url);
var proxyUriWithRequestPathAndQuery = new Uri(proxyUri, requestUri.PathAndQuery);
var proxyHelper = new ProxyHelper(settings);
var (responseMessage, mapping) = await proxyHelper.SendAsync(
null,
_settings.ProxyAndRecordSettings!,
_httpClientForProxy!,
requestMessage,
proxyUriWithRequestPathAndQuery.AbsoluteUri
).ConfigureAwait(false);
if (mapping != null)
{
if (settings.ProxyAndRecordSettings.SaveMapping)
{
_options.Mappings.TryAdd(mapping.Guid, mapping);
}
if (settings.ProxyAndRecordSettings.SaveMappingToFile)
{
_mappingToFileSaver.SaveMappingToFile(mapping);
}
}
return responseMessage;
}
}
@@ -295,6 +295,8 @@ public partial class WireMockServer : IWireMockServer
_options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing; _options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing;
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously; _options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
_options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests; _options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
_options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
_options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
if (settings.CustomCertificateDefined) if (settings.CustomCertificateDefined)
{ {
@@ -476,6 +478,13 @@ public partial class WireMockServer : IWireMockServer
_options.Scenarios.Clear(); _options.Scenarios.Clear();
} }
/// <inheritdoc />
[PublicAPI]
public bool ResetScenario(string name)
{
return _options.Scenarios.ContainsKey(name) && _options.Scenarios.TryRemove(name, out _);
}
/// <inheritdoc cref="IWireMockServer.WithMapping(MappingModel[])" /> /// <inheritdoc cref="IWireMockServer.WithMapping(MappingModel[])" />
[PublicAPI] [PublicAPI]
public IWireMockServer WithMapping(params MappingModel[] mappings) public IWireMockServer WithMapping(params MappingModel[] mappings)
@@ -58,4 +58,9 @@ public class ProxyAndRecordSettings : HttpClientSettings
/// Default value is false. /// Default value is false.
/// </summary> /// </summary>
public bool UseDefinedRequestMatchers { get; set; } public bool UseDefinedRequestMatchers { get; set; }
/// <summary>
/// Append an unique GUID to the filename from the saved mapping file.
/// </summary>
public bool AppendGuidToSavedMappingFile { get; set; }
} }
@@ -12,7 +12,6 @@ using WireMock.RegularExpressions;
using WireMock.Types; using WireMock.Types;
#if USE_ASPNETCORE #if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using WireMock.Types;
#endif #endif
namespace WireMock.Settings namespace WireMock.Settings
@@ -253,6 +252,20 @@ namespace WireMock.Settings
[PublicAPI] [PublicAPI]
public bool? SaveUnmatchedRequests { get; set; } public bool? SaveUnmatchedRequests { get; set; }
/// <summary>
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
/// </summary>
[PublicAPI]
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
///
/// Default value = "All".
/// </summary>
[PublicAPI]
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
/// <summary> /// <summary>
/// Custom matcher mappings for static mappings /// Custom matcher mappings for static mappings
/// </summary> /// </summary>
@@ -1,4 +1,3 @@
using System;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using JetBrains.Annotations; using JetBrains.Annotations;
using Stef.Validation; using Stef.Validation;
@@ -54,20 +53,32 @@ public static class WireMockServerSettingsParser
UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true), UseRegexExtended = parser.GetBoolValue(nameof(WireMockServerSettings.UseRegexExtended), true),
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"), WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"), WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)) HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport))
}; };
#if USE_ASPNETCORE #if USE_ASPNETCORE
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None); settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
#endif #endif
var loggerType = parser.GetStringValue("WireMockLogger");
switch (loggerType)
{
case nameof(WireMockConsoleLogger):
settings.Logger = new WireMockConsoleLogger();
break;
case nameof(WireMockNullLogger):
settings.Logger = new WireMockNullLogger();
break;
default:
if (logger != null) if (logger != null)
{ {
settings.Logger = logger; settings.Logger = logger;
} }
else if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger") break;
{
settings.Logger = new WireMockConsoleLogger();
} }
if (parser.Contains(nameof(WireMockServerSettings.Port))) if (parser.Contains(nameof(WireMockServerSettings.Port)))
@@ -93,6 +104,7 @@ public static class WireMockServerSettingsParser
SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"), SaveMappingForStatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"), SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)), UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
Url = proxyUrl! Url = proxyUrl!
}; };
@@ -10,7 +10,7 @@ interface ITransformer
IBodyData? TransformBody(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, ReplaceNodeOptions options); IBodyData? TransformBody(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, ReplaceNodeOptions options);
IDictionary<string, WireMockList<string>>? TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers); IDictionary<string, WireMockList<string>> TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers);
string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value); string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value);
} }
+4 -3
View File
@@ -36,7 +36,7 @@ internal class Transformer : ITransformer
return newBodyData; return newBodyData;
} }
public IDictionary<string, WireMockList<string>>? TransformHeaders( public IDictionary<string, WireMockList<string>> TransformHeaders(
IMapping mapping, IMapping mapping,
IRequestMessage originalRequestMessage, IRequestMessage originalRequestMessage,
IResponseMessage originalResponseMessage, IResponseMessage originalResponseMessage,
@@ -239,6 +239,7 @@ internal class Transformer : ITransformer
} }
} }
// ReSharper disable once UnusedParameter.Local
private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString) private static void ReplaceNodeValue(ReplaceNodeOptions options, JToken node, string transformedString)
{ {
StringUtils.TryParseQuotedString(transformedString, out var result, out _); StringUtils.TryParseQuotedString(transformedString, out var result, out _);
@@ -270,13 +271,13 @@ internal class Transformer : ITransformer
Encoding = original.Encoding, Encoding = original.Encoding,
DetectedBodyType = original.DetectedBodyType, DetectedBodyType = original.DetectedBodyType,
DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType, DetectedBodyTypeFromContentType = original.DetectedBodyTypeFromContentType,
BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString, model) BodyAsString = handlebarsContext.ParseAndRender(original.BodyAsString!, model)
}; };
} }
private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile) private static IBodyData TransformBodyAsFile(ITransformerContext handlebarsContext, object model, IBodyData original, bool useTransformerForBodyAsFile)
{ {
string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile, model); string transformedBodyAsFilename = handlebarsContext.ParseAndRender(original.BodyAsFile!, model);
if (!useTransformerForBodyAsFile) if (!useTransformerForBodyAsFile)
{ {
+11 -11
View File
@@ -5,7 +5,7 @@ using System.Net.Http.Headers;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Stef.Validation; using Stef.Validation;
using WireMock.Http; using WireMock.Constants;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Types; using WireMock.Types;
@@ -29,15 +29,15 @@ internal static class BodyParser
*/ */
private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool> private static readonly IDictionary<string, bool> BodyAllowedForMethods = new Dictionary<string, bool>
{ {
{ HttpRequestMethods.HEAD, false }, { HttpRequestMethod.HEAD, false },
{ HttpRequestMethods.GET, false }, { HttpRequestMethod.GET, false },
{ HttpRequestMethods.PUT, true }, { HttpRequestMethod.PUT, true },
{ HttpRequestMethods.POST, true }, { HttpRequestMethod.POST, true },
{ HttpRequestMethods.DELETE, true }, { HttpRequestMethod.DELETE, true },
{ HttpRequestMethods.TRACE, false }, { HttpRequestMethod.TRACE, false },
{ HttpRequestMethods.OPTIONS, true }, { HttpRequestMethod.OPTIONS, true },
{ HttpRequestMethods.CONNECT, false }, { HttpRequestMethod.CONNECT, false },
{ HttpRequestMethods.PATCH, true } { HttpRequestMethod.PATCH, true }
}; };
private static readonly IStringMatcher[] MultipartContentTypesMatchers = { private static readonly IStringMatcher[] MultipartContentTypesMatchers = {
@@ -122,7 +122,7 @@ internal static class BodyParser
// In case of MultiPart: check if the BodyAsBytes is a valid UTF8 or ASCII string, in that case read as String else keep as-is // In case of MultiPart: check if the BodyAsBytes is a valid UTF8 or ASCII string, in that case read as String else keep as-is
if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart) if (data.DetectedBodyTypeFromContentType == BodyType.MultiPart)
{ {
if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out Encoding encoding) && if (BytesEncodingUtils.TryGetEncoding(data.BodyAsBytes, out var encoding) &&
SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any()) SupportedBodyAsStringEncodingForMultipart.Select(x => x.Equals(encoding)).Any())
{ {
data.BodyAsString = encoding.GetString(data.BodyAsBytes); data.BodyAsString = encoding.GetString(data.BodyAsBytes);
+21 -5
View File
@@ -11,25 +11,41 @@ namespace WireMock.Util;
/// </summary> /// </summary>
internal static class QueryStringParser internal static class QueryStringParser
{ {
public static IDictionary<string, WireMockList<string>> Parse(string queryString) private static readonly Dictionary<string, WireMockList<string>> Empty = new();
public static IDictionary<string, WireMockList<string>> Parse(string? queryString, QueryParameterMultipleValueSupport? support = null)
{ {
if (string.IsNullOrEmpty(queryString)) if (string.IsNullOrEmpty(queryString))
{ {
return new Dictionary<string, WireMockList<string>>(); return Empty;
} }
var queryParameterMultipleValueSupport = support ?? QueryParameterMultipleValueSupport.All;
string[] JoinParts(string[] parts) string[] JoinParts(string[] parts)
{ {
if (parts.Length > 1) if (parts.Length > 1)
{ {
return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2" return queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.Comma) ?
parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) : // Support "?key=1,2"
new[] { parts[1] };
} }
return new string[0]; return new string[0];
} }
return queryString.TrimStart('?') var splitOn = new List<string>();
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue" if (queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.Ampersand))
{
splitOn.Add("&"); // Support "?key=value&key=anotherValue"
}
if (queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.SemiColon))
{
splitOn.Add(";"); // Support "?key=value;key=anotherValue"
}
return queryString!.TrimStart('?')
.Split(splitOn.ToArray(), StringSplitOptions.RemoveEmptyEntries)
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries)) .Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
.GroupBy(parts => parts[0], JoinParts) .GroupBy(parts => parts[0], JoinParts)
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode))); .ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));
-5
View File
@@ -21,10 +21,6 @@
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign> <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup> </PropertyGroup>
<!--<ItemGroup>
<None Include="WireMock.Net-Logo.png" Pack="true" PackagePath="../../"/>
</ItemGroup>-->
<!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 --> <!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 -->
<!-- This is needed else we cannot build net452 in Azure DevOps Pipeline --> <!-- This is needed else we cannot build net452 in Azure DevOps Pipeline -->
<!--<Target Name="CheckIfShouldKillVBCSCompiler" />--> <!--<Target Name="CheckIfShouldKillVBCSCompiler" />-->
@@ -145,7 +141,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
<!--<PackageReference Include="System.Diagnostics.CodeAnalysis" Version="4.3.0" />-->
<PackageReference Include="Nullable" Version="1.3.0" /> <PackageReference Include="Nullable" Version="1.3.0" />
</ItemGroup> </ItemGroup>
+10 -11
View File
@@ -5,12 +5,12 @@ using WireMock.Logging;
using WireMock.Net.StandAlone; using WireMock.Net.StandAlone;
using WireMock.Server; using WireMock.Server;
namespace WireMock.Net namespace WireMock.Net;
public class Program
{ {
public class Program
{
private static readonly int SleepTime = 30000; private static readonly int SleepTime = 30000;
private static readonly ILogger xLogger = LoggerFactory.Create(o => private static readonly ILogger XLogger = LoggerFactory.Create(o =>
{ {
o.SetMinimumLevel(LogLevel.Debug); o.SetMinimumLevel(LogLevel.Debug);
o.AddSimpleConsole(options => o.AddSimpleConsole(options =>
@@ -20,13 +20,13 @@ namespace WireMock.Net
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss "; options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
}); });
}).CreateLogger("WireMock.Net"); }).CreateLogger("WireMock.Net");
private static readonly IWireMockLogger Logger = new WireMockLogger(xLogger); private static readonly IWireMockLogger Logger = new WireMockLogger(XLogger);
private static WireMockServer Server; private static WireMockServer _server = null!;
static async Task Main(string[] args) static async Task Main(string[] args)
{ {
if (!StandAloneApp.TryStart(args, out Server, Logger)) if (!StandAloneApp.TryStart(args, out _server!, Logger))
{ {
return; return;
} }
@@ -45,16 +45,15 @@ namespace WireMock.Net
while (true) while (true)
{ {
Logger.Info("Server running : {IsStarted}", Server.IsStarted); Logger.Info("Server running : {0}", _server.IsStarted);
await Task.Delay(SleepTime).ConfigureAwait(false); await Task.Delay(SleepTime).ConfigureAwait(false);
} }
} }
private static void Stop(string why) private static void Stop(string why)
{ {
Logger.Info("Server stopping because '{why}'", why); Logger.Info("Server stopping because '{0}'", why);
Server.Stop(); _server.Stop();
Logger.Info("Server stopped"); Logger.Info("Server stopped");
} }
}
} }
@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"dotnet-WireMock.Net": { "dotnet-WireMock.Net": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "" "commandLineArgs": "--WireMockLogger s"
} }
} }
} }
+5 -6
View File
@@ -4,11 +4,11 @@ using Microsoft.Extensions.Logging;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
using WireMock.Logging; using WireMock.Logging;
namespace WireMock.Net namespace WireMock.Net;
public class WireMockLogger : IWireMockLogger
{ {
public class WireMockLogger : IWireMockLogger private readonly JsonSerializerOptions _options = new()
{
private readonly JsonSerializerOptions options = new JsonSerializerOptions
{ {
WriteIndented = true WriteIndented = true
}; };
@@ -53,9 +53,8 @@ namespace WireMock.Net
/// <see cref="IWireMockLogger.DebugRequestResponse"/> /// <see cref="IWireMockLogger.DebugRequestResponse"/>
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest) public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
{ {
string message = JsonSerializer.Serialize(logEntryModel, options); string message = JsonSerializer.Serialize(logEntryModel, _options);
_logger.LogDebug("Admin[{IsAdmin}] {Message}", isAdminRequest, message); _logger.LogDebug("Admin[{IsAdmin}] {Message}", isAdminRequest, message);
} }
}
} }
@@ -562,6 +562,44 @@ public class WireMockAssertionsTests : IDisposable
.UsingTrace(); .UsingTrace();
} }
[Fact]
public async Task HaveReceivedACall_UsingAnyMethod_WhenACallWasMadeUsingGet_Should_BeOK()
{
await _httpClient.SendAsync(new HttpRequestMessage(new HttpMethod("GET"), "anyurl")).ConfigureAwait(false);
_server.Should()
.HaveReceivedACall()
.UsingAnyMethod();
}
[Fact]
public void HaveReceivedNoCalls_UsingAnyMethod_WhenNoCallsWereMade_Should_BeOK()
{
_server
.Should()
.HaveReceived(0)
.Calls()
.UsingAnyMethod();
_server
.Should()
.HaveReceivedNoCalls()
.UsingAnyMethod();
}
[Fact]
public void HaveReceivedNoCalls_AtUrl_WhenNoCallsWereMade_Should_BeOK()
{
_server.Should()
.HaveReceived(0)
.Calls()
.AtUrl(_server.Url ?? string.Empty);
_server.Should()
.HaveReceivedNoCalls()
.AtUrl(_server.Url ?? string.Empty);
}
public void Dispose() public void Dispose()
{ {
_server?.Stop(); _server?.Stop();
+23 -5
View File
@@ -1,16 +1,17 @@
using NFluent; using NFluent;
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
using WireMock.Owin;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using Xunit; using Xunit;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests;
public class RequestTests
{ {
public class RequestTests
{
private const string ClientIp = "::1"; private const string ClientIp = "::1";
[Fact] [Fact]
@@ -212,6 +213,24 @@ namespace WireMock.Net.Tests
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
[Fact]
public void Should_specify_requests_matching_given_param_WithComma()
{
// given
var options = new WireMockMiddlewareOptions
{
QueryParameterMultipleValueSupport = QueryParameterMultipleValueSupport.NoComma
};
var spec = Request.Create().WithParam("$filter", "startswith(name,'testName')");
// when
var request = new RequestMessage(options, new UrlDetails("http://localhost/?$filter=startswith(name,'testName')"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact] [Fact]
public void Should_specify_requests_matching_given_param_func() public void Should_specify_requests_matching_given_param_func()
{ {
@@ -239,5 +258,4 @@ namespace WireMock.Net.Tests
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }
}
} }
+42 -42
View File
@@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using NFluent; using NFluent;
using WireMock.Matchers; using WireMock.Matchers;
using Xunit;
using WireMock.RequestBuilders;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models; using WireMock.Models;
using WireMock.RequestBuilders;
using WireMock.Util; using WireMock.Util;
using Xunit;
namespace WireMock.Net.Tests namespace WireMock.Net.Tests
{ {
@@ -19,11 +19,11 @@ namespace WireMock.Net.Tests
// Assign // Assign
var spec = Request.Create().WithPath("/path/a b").UsingAnyMethod(); var spec = Request.Create().WithPath("/path/a b").UsingAnyMethod();
// when // Act
var body = new BodyData(); var body = new BodyData();
var request = new RequestMessage(new UrlDetails("http://localhost/path/a b"), "GET", ClientIp, body); var request = new RequestMessage(new UrlDetails("http://localhost/path/a b"), "GET", ClientIp, body);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -31,17 +31,17 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPath_WithHeader_Match() public void Request_WithPath_WithHeader_Match()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithHeader("X-toto", "tata"); var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithHeader("X-toto", "tata");
// when // Act
var body = new BodyData var body = new BodyData
{ {
BodyAsString = "abc" BodyAsString = "abc"
}; };
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } }); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -49,13 +49,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPath() public void Request_WithPath()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo"); var spec = Request.Create().WithPath("/foo");
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -76,13 +76,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPathFunc() public void Request_WithPathFunc()
{ {
// given // Arrange
var spec = Request.Create().WithPath(url => url.EndsWith("/foo")); var spec = Request.Create().WithPath(url => url.EndsWith("/foo"));
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -90,13 +90,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPathRegexMatcher_HasMatch() public void Request_WithPathRegexMatcher_HasMatch()
{ {
// given // Arrange
var spec = Request.Create().WithPath(new RegexMatcher("^/foo")); var spec = Request.Create().WithPath(new RegexMatcher("^/foo"));
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -104,13 +104,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Request_WithPathRegexMatcher_HasNoMatch() public void Request_WithPathRegexMatcher_HasNoMatch()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo"); var spec = Request.Create().WithPath("/foo");
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/bar"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/bar"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }
@@ -126,10 +126,10 @@ namespace WireMock.Net.Tests
}; };
var spec = Request.Create().WithPath(new RegexMatcher(pattern)); var spec = Request.Create().WithPath(new RegexMatcher(pattern));
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo/bar"), "blabla", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -137,17 +137,17 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_delete() public void Should_specify_requests_matching_given_path_and_method_delete()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingDelete(); var spec = Request.Create().WithPath("/foo").UsingDelete();
// when // Act
var body = new BodyData var body = new BodyData
{ {
BodyAsString = "whatever" BodyAsString = "whatever"
}; };
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "Delete", ClientIp, body); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "Delete", ClientIp, body);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -155,13 +155,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_get() public void Should_specify_requests_matching_given_path_and_method_get()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingGet(); var spec = Request.Create().WithPath("/foo").UsingGet();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -169,13 +169,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_head() public void Should_specify_requests_matching_given_path_and_method_head()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingHead(); var spec = Request.Create().WithPath("/foo").UsingHead();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -183,13 +183,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_post() public void Should_specify_requests_matching_given_path_and_method_post()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPost(); var spec = Request.Create().WithPath("/foo").UsingPost();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -197,13 +197,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_put() public void Should_specify_requests_matching_given_path_and_method_put()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPut(); var spec = Request.Create().WithPath("/foo").UsingPut();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -211,13 +211,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_specify_requests_matching_given_path_and_method_patch() public void Should_specify_requests_matching_given_path_and_method_patch()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPatch(); var spec = Request.Create().WithPath("/foo").UsingPatch();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PATCH", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PATCH", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
@@ -225,13 +225,13 @@ namespace WireMock.Net.Tests
[Fact] [Fact]
public void Should_exclude_requests_matching_given_path_but_not_http_method() public void Should_exclude_requests_matching_given_path_but_not_http_method()
{ {
// given // Arrange
var spec = Request.Create().WithPath("/foo").UsingPut(); var spec = Request.Create().WithPath("/foo").UsingPut();
// when // Act
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp); var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "HEAD", ClientIp);
// then // Assert
var requestMatchResult = new RequestMatchResult(); var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }
@@ -0,0 +1,30 @@
using FluentAssertions;
using WireMock.Matchers;
using Xunit;
using WireMock.RequestBuilders;
using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.Util;
namespace WireMock.Net.Tests
{
public class RequestWithUrlTests
{
private const string ClientIp = "::1";
[Fact]
public void Request_WithUrl_Regex()
{
// Assign
var spec = Request.Create().WithUrl(new RegexMatcher("(some\\/service\\/v1\\/name)([?]{1})(param.source=SYSTEM){1}([&]{1})(param.id=123457890){1}$")).UsingAnyMethod();
// Act
var body = new BodyData();
var request = new RequestMessage(new UrlDetails("https://localhost/some/service/v1/name?param.source=SYSTEM&param.id=123457890"), "POST", ClientIp, body);
// Assert
var requestMatchResult = new RequestMatchResult();
spec.GetMatchingScore(request, requestMatchResult).Should().Be(1.0);
}
}
}

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