mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-05-05 22:53:22 +02:00
Compare commits
18 Commits
1.5.7
...
stef-847-r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04cba25bdf | ||
|
|
ef5f988786 | ||
|
|
1e44f52ad6 | ||
|
|
7fd1d30d0e | ||
|
|
49b29d74dc | ||
|
|
80931e9fb5 | ||
|
|
cce344ff83 | ||
|
|
0972d2cb8f | ||
|
|
a39b7fc633 | ||
|
|
31298d281d | ||
|
|
b4c32dd66b | ||
|
|
57115f1a3d | ||
|
|
306c69f478 | ||
|
|
fb8fec0376 | ||
|
|
dd1a6fa508 | ||
|
|
36037627bc | ||
|
|
55afc8041f | ||
|
|
b523ab9125 |
2
.github/FUNDING.yml
vendored
2
.github/FUNDING.yml
vendored
@@ -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
|
||||||
|
|||||||
20
CHANGELOG.md
20
CHANGELOG.md
@@ -1,3 +1,23 @@
|
|||||||
|
# 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)
|
||||||
|
- [#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)
|
||||||
|
- [#824](https://github.com/WireMock-Net/WireMock.Net/pull/824) - WebHook - Transform Url [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#814](https://github.com/WireMock-Net/WireMock.Net/issues/814) - WithHeader cannot handle multiple requests with the same header key values [bug]
|
||||||
|
- [#815](https://github.com/WireMock-Net/WireMock.Net/issues/815) - Why does UsingMethod check _callscount? [bug]
|
||||||
|
- [#822](https://github.com/WireMock-Net/WireMock.Net/issues/822) - Webhook with generic url, body and custom header values [feature]
|
||||||
|
|
||||||
# 1.5.7 (11 October 2022)
|
# 1.5.7 (11 October 2022)
|
||||||
- [#818](https://github.com/WireMock-Net/WireMock.Net/pull/818) - Add option to run the server on http & https [feature] contributed by [StefH](https://github.com/StefH)
|
- [#818](https://github.com/WireMock-Net/WireMock.Net/pull/818) - Add option to run the server on http & https [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
- [#821](https://github.com/WireMock-Net/WireMock.Net/pull/821) - Add UseDefinedRequestMatchers to ProxyAndRecordSettings [feature] contributed by [StefH](https://github.com/StefH)
|
- [#821](https://github.com/WireMock-Net/WireMock.Net/pull/821) - Add UseDefinedRequestMatchers to ProxyAndRecordSettings [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.5.7</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,6 +1,6 @@
|
|||||||
rem https://github.com/StefH/GitHubReleaseNotes
|
rem https://github.com/StefH/GitHubReleaseNotes
|
||||||
|
|
||||||
SET version=1.5.7
|
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%
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
# 1.5.7 (11 October 2022)
|
# 1.5.10 (06 November 2022)
|
||||||
- #818 Add option to run the server on http & https [feature]
|
- #843 Webhook Templating: Use the transformed URL to create the HttpRequestMessage
|
||||||
- #821 Add UseDefinedRequestMatchers to ProxyAndRecordSettings [feature]
|
- #845 Add WireMockNullLogger as valid commandline logger option [feature]
|
||||||
- #823 Add implicit operators to WireMockList
|
|
||||||
- #819 Can I preserve Mapping title and matchers for proxy response? [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
|
||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"WSL": {
|
||||||
|
"commandName": "WSL2",
|
||||||
|
"distributionName": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,33 +1,29 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<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>
|
||||||
<UserSecretsId>efcf4a18-fd7c-4622-1111-336d65290599</UserSecretsId>
|
<UserSecretsId>efcf4a18-fd7c-4622-1111-336d65290599</UserSecretsId>
|
||||||
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
|
<AspNetCoreHostingModel>OutOfProcess</AspNetCoreHostingModel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Remove="Properties\1.launchSettings.json" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
||||||
</ItemGroup>
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
<Content Update="appsettings.json">
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
</ItemGroup>
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<Content Update="appsettings.json">
|
|
||||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
264
examples/WireMockAzureQueueExample/.gitignore
vendored
Normal file
264
examples/WireMockAzureQueueExample/.gitignore
vendored
Normal file
@@ -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
|
||||||
13
examples/WireMockAzureQueueExample/Function1.cs
Normal file
13
examples/WireMockAzureQueueExample/Function1.cs
Normal file
@@ -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>
|
||||||
11
examples/WireMockAzureQueueExample/host.json
Normal file
11
examples/WireMockAzureQueueExample/host.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0",
|
||||||
|
"logging": {
|
||||||
|
"applicationInsights": {
|
||||||
|
"samplingSettings": {
|
||||||
|
"isEnabled": true,
|
||||||
|
"excludedTypes": "Request"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
examples/WireMockAzureQueueExample/local.settings.json
Normal file
7
examples/WireMockAzureQueueExample/local.settings.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"IsEncrypted": false,
|
||||||
|
"Values": {
|
||||||
|
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||||
|
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
|
||||||
|
}
|
||||||
|
}
|
||||||
40
examples/WireMockAzureQueueProxy/Program.cs
Normal file
40
examples/WireMockAzureQueueProxy/Program.cs
Normal file
@@ -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,67 +1,71 @@
|
|||||||
namespace WireMock.Admin.Settings
|
namespace WireMock.Admin.Settings;
|
||||||
|
|
||||||
|
[FluentBuilder.AutoGenerateBuilder]
|
||||||
|
public class ProxyAndRecordSettingsModel
|
||||||
{
|
{
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
/// <summary>
|
||||||
public class ProxyAndRecordSettingsModel
|
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||||
{
|
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||||
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
|
||||||
/// </summary>
|
|
||||||
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the WebProxySettings.
|
/// Defines the WebProxySettings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WebProxySettingsModel WebProxySettings { get; set; }
|
public WebProxySettingsModel WebProxySettings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Proxy requests should follow redirection (30x).
|
/// Proxy requests should follow redirection (30x).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? AllowAutoRedirect { get; set; }
|
public bool? AllowAutoRedirect { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The URL to proxy.
|
/// The URL to proxy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the mapping for each request/response to the internal Mappings.
|
/// Save the mapping for each request/response to the internal Mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SaveMapping { get; set; }
|
public bool SaveMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SaveMappingToFile { get; set; }
|
public bool SaveMappingToFile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] ExcludedHeaders { get; set; }
|
public string[] ExcludedHeaders { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] ExcludedCookies { get; set; }
|
public string[] ExcludedCookies { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// public bool PreferProxyMapping { get; set; }
|
// public bool PreferProxyMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||||
/// - <c>false</c>, the default matchers will be used.
|
/// - <c>false</c>, the default matchers will be used.
|
||||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||||
///
|
///
|
||||||
/// 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,82 +1,99 @@
|
|||||||
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>
|
/// <summary>
|
||||||
/// Settings
|
/// Gets or sets the global delay in milliseconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[FluentBuilder.AutoGenerateBuilder]
|
public int? GlobalProcessingDelay { get; set; }
|
||||||
public class SettingsModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the global delay in milliseconds.
|
|
||||||
/// </summary>
|
|
||||||
public int? GlobalProcessingDelay { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets if partial mapping is allowed.
|
/// Gets or sets if partial mapping is allowed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? AllowPartialMapping { get; set; }
|
public bool? AllowPartialMapping { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the RequestLog expiration in hours
|
/// Gets or sets the RequestLog expiration in hours
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? RequestLogExpirationDuration { get; set; }
|
public int? RequestLogExpirationDuration { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the MaxRequestLog count.
|
/// Gets or sets the MaxRequestLog count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? MaxRequestLogCount { get; set; }
|
public int? MaxRequestLogCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allow a Body for all HTTP Methods. (default set to false).
|
/// Allow a Body for all HTTP Methods. (default set to false).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? AllowBodyForAllHttpMethods { get; set; }
|
public bool? AllowBodyForAllHttpMethods { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle all requests synchronously. (default set to false).
|
/// Handle all requests synchronously. (default set to false).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? HandleRequestsSynchronously { get; set; }
|
public bool? HandleRequestsSynchronously { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Throw an exception when the Matcher fails because of invalid input. (default set to false).
|
/// Throw an exception when the Matcher fails because of invalid input. (default set to false).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to true).
|
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? UseRegexExtended { get; set; }
|
public bool? UseRegexExtended { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to false).
|
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to false).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? SaveUnmatchedRequests { get; set; }
|
public bool? SaveUnmatchedRequests { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets if the static mappings should be read at startup.
|
/// Gets or sets if the static mappings should be read at startup.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? ReadStaticMappings { get; set; }
|
public bool? ReadStaticMappings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Watch the static mapping files + folder for changes when running.
|
/// Watch the static mapping files + folder for changes when running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? WatchStaticMappings { get; set; }
|
public bool? WatchStaticMappings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A value indicating whether subdirectories within the static mappings path should be monitored.
|
/// A value indicating whether subdirectories within the static mappings path should be monitored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? WatchStaticMappingsInSubdirectories { get; set; }
|
public bool? WatchStaticMappingsInSubdirectories { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
|
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string? CorsPolicyOptions { get; set; }
|
public string? CorsPolicyOptions { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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;
|
||||||
@@ -13,12 +11,12 @@ public interface IBodyData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body (as bytearray).
|
/// The body (as bytearray).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte[] BodyAsBytes { get; set; }
|
byte[]? BodyAsBytes { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the body as a file.
|
/// Gets or sets the body as a file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string BodyAsFile { get; set; }
|
string? BodyAsFile { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is the body as file cached?
|
/// Is the body as file cached?
|
||||||
@@ -38,7 +36,7 @@ public interface IBodyData
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body as string, this is defined when BodyAsString or BodyAsJson are not null.
|
/// The body as string, this is defined when BodyAsString or BodyAsJson are not null.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string BodyAsString { get; set; }
|
string? BodyAsString { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The detected body type (detection based on body content).
|
/// The detected body type (detection based on body content).
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -1,97 +1,138 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
using System;
|
||||||
|
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;
|
||||||
|
|
||||||
// ReSharper disable once CheckNamespace
|
// ReSharper disable once CheckNamespace
|
||||||
namespace WireMock.FluentAssertions;
|
namespace WireMock.FluentAssertions;
|
||||||
|
|
||||||
public class WireMockAssertions
|
public class WireMockAssertions
|
||||||
{
|
{
|
||||||
private readonly IWireMockServer _subject;
|
private const string Any = "*";
|
||||||
private readonly int? _callsCount;
|
private readonly int? _callsCount;
|
||||||
|
private IReadOnlyList<IRequestMessage> _requestMessages;
|
||||||
|
private readonly IReadOnlyList<KeyValuePair<string, WireMockList<string>>> _headers;
|
||||||
|
|
||||||
public WireMockAssertions(IWireMockServer subject, int? callsCount)
|
public WireMockAssertions(IWireMockServer subject, int? callsCount)
|
||||||
{
|
{
|
||||||
_subject = subject;
|
|
||||||
_callsCount = callsCount;
|
_callsCount = callsCount;
|
||||||
|
_requestMessages = subject.LogEntries.Select(logEntry => logEntry.RequestMessage).ToList();
|
||||||
|
_headers = _requestMessages.SelectMany(req => req.Headers).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
[CustomAssertion]
|
||||||
public AndConstraint<WireMockAssertions> 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);
|
||||||
|
|
||||||
|
var (filter, condition) = BuildFilterAndCondition(predicate);
|
||||||
|
|
||||||
Execute.Assertion
|
Execute.Assertion
|
||||||
.BecauseOf(because, becauseArgs)
|
.BecauseOf(because, becauseArgs)
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
.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
|
||||||
|
)
|
||||||
.Then
|
.Then
|
||||||
.ForCondition(x => (_callsCount == null && x.Any(y => y.AbsoluteUrl == absoluteUrl)) || (_callsCount == x.Count(y => y.AbsoluteUrl == absoluteUrl)))
|
.ForCondition(condition)
|
||||||
.FailWith(
|
.FailWith(
|
||||||
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but didn't find it among the calls to {1}.",
|
"Expected {context:wiremockserver} to have been called at address matching the absolute url {0}{reason}, but didn't find it among the calls to {1}.",
|
||||||
_ => absoluteUrl, requests => requests.Select(request => request.AbsoluteUrl));
|
_ => absoluteUrl, requests => requests.Select(request => request.AbsoluteUrl)
|
||||||
|
);
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
_requestMessages = filter(_requestMessages).ToList();
|
||||||
|
|
||||||
|
return new AndWhichConstraint<WireMockAssertions, string>(this, absoluteUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
[CustomAssertion]
|
||||||
public AndConstraint<WireMockAssertions> 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);
|
||||||
|
|
||||||
|
var (filter, condition) = BuildFilterAndCondition(predicate);
|
||||||
|
|
||||||
Execute.Assertion
|
Execute.Assertion
|
||||||
.BecauseOf(because, becauseArgs)
|
.BecauseOf(because, becauseArgs)
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
.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(x => (_callsCount == null && x.Any(y => y.Url == url)) || (_callsCount == x.Count(y => y.Url == url)))
|
.ForCondition(condition)
|
||||||
.FailWith(
|
.FailWith(
|
||||||
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but didn't find it among the calls to {1}.",
|
"Expected {context:wiremockserver} to have been called at address matching the url {0}{reason}, but didn't find it among the calls to {1}.",
|
||||||
_ => url, requests => requests.Select(request => request.Url));
|
_ => url,
|
||||||
|
requests => requests.Select(request => request.Url)
|
||||||
|
);
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
_requestMessages = filter(_requestMessages).ToList();
|
||||||
|
|
||||||
|
return new AndWhichConstraint<WireMockAssertions, string>(this, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
[CustomAssertion]
|
||||||
public AndConstraint<WireMockAssertions> 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);
|
||||||
|
|
||||||
|
var (filter, condition) = BuildFilterAndCondition(predicate);
|
||||||
|
|
||||||
Execute.Assertion
|
Execute.Assertion
|
||||||
.BecauseOf(because, becauseArgs)
|
.BecauseOf(because, becauseArgs)
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
.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
|
||||||
|
)
|
||||||
.Then
|
.Then
|
||||||
.ForCondition(x => (_callsCount == null && x.Any(y => y.ProxyUrl == proxyUrl)) || (_callsCount == x.Count(y => y.ProxyUrl == proxyUrl)))
|
.ForCondition(condition)
|
||||||
.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, requests => requests.Select(request => request.ProxyUrl));
|
_ => proxyUrl,
|
||||||
|
requests => requests.Select(request => request.ProxyUrl)
|
||||||
|
);
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
_requestMessages = filter(_requestMessages).ToList();
|
||||||
|
|
||||||
|
return new AndWhichConstraint<WireMockAssertions, string>(this, proxyUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
[CustomAssertion]
|
||||||
public AndConstraint<WireMockAssertions> 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);
|
||||||
|
|
||||||
|
var (filter, condition) = BuildFilterAndCondition(predicate);
|
||||||
|
|
||||||
Execute.Assertion
|
Execute.Assertion
|
||||||
.BecauseOf(because, becauseArgs)
|
.BecauseOf(because, becauseArgs)
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
.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
|
.Then
|
||||||
.ForCondition(x => (_callsCount == null && x.Any(y => y.ClientIP == clientIP)) || (_callsCount == x.Count(y => y.ClientIP == clientIP)))
|
.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)
|
||||||
|
);
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
_requestMessages = filter(_requestMessages).ToList();
|
||||||
|
|
||||||
|
return new AndWhichConstraint<WireMockAssertions, string>(this, clientIP);
|
||||||
}
|
}
|
||||||
|
|
||||||
[CustomAssertion]
|
[CustomAssertion]
|
||||||
@@ -101,28 +142,25 @@ public class WireMockAssertions
|
|||||||
[CustomAssertion]
|
[CustomAssertion]
|
||||||
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string[] expectedValues, string because = "", params object[] becauseArgs)
|
public AndConstraint<WireMockAssertions> WithHeader(string expectedKey, string[] expectedValues, string because = "", params object[] becauseArgs)
|
||||||
{
|
{
|
||||||
var headersDictionary = _subject.LogEntries.SelectMany(x => x.RequestMessage.Headers)
|
|
||||||
.ToDictionary(x => x.Key, x => x.Value);
|
|
||||||
|
|
||||||
using (new AssertionScope("headers from requests sent"))
|
using (new AssertionScope("headers from requests sent"))
|
||||||
{
|
{
|
||||||
headersDictionary.Should().ContainKey(expectedKey, because, becauseArgs);
|
_headers.Select(h => h.Key).Should().Contain(expectedKey, because, becauseArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (new AssertionScope($"header \"{expectedKey}\" from requests sent with value(s)"))
|
using (new AssertionScope($"header \"{expectedKey}\" from requests sent with value(s)"))
|
||||||
{
|
{
|
||||||
|
var headerValues = _headers.First(h => h.Key == expectedKey).Value;
|
||||||
|
|
||||||
if (expectedValues.Length == 1)
|
if (expectedValues.Length == 1)
|
||||||
{
|
{
|
||||||
headersDictionary[expectedKey].Should().Contain(expectedValues.First());
|
headerValues.Should().Contain(expectedValues.First(), because, becauseArgs);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var trimmedHeaderValues = string.Join(",", headersDictionary[expectedKey].Select(x => x)).Split(',')
|
var trimmedHeaderValues = string.Join(",", headerValues.Select(x => x)).Split(',').Select(x => x.Trim()).ToList();
|
||||||
.Select(x => x.Trim())
|
|
||||||
.ToList();
|
|
||||||
foreach (var expectedValue in expectedValues)
|
foreach (var expectedValue in expectedValues)
|
||||||
{
|
{
|
||||||
trimmedHeaderValues.Should().Contain(expectedValue);
|
trimmedHeaderValues.Should().Contain(expectedValue, because, becauseArgs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,56 +170,78 @@ 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)
|
||||||
{
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
Execute.Assertion
|
Execute.Assertion
|
||||||
.BecauseOf(because, becauseArgs)
|
.BecauseOf(because, becauseArgs)
|
||||||
.Given(() => _subject.LogEntries.Select(x => x.RequestMessage).ToList())
|
.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
|
||||||
|
)
|
||||||
.Then
|
.Then
|
||||||
.ForCondition(x => (_callsCount == null && x.Any(y => y.Method == method)) || (_callsCount == x.Count(y => y.Method == method)))
|
.ForCondition(condition)
|
||||||
.FailWith(
|
.FailWith(
|
||||||
"Expected {context:wiremockserver} to have been called using method {0}{reason}, but didn't find it among the methods {1}.",
|
"Expected {context:wiremockserver} to have been called using method {0}{reason}, but didn't find it among the methods {1}.",
|
||||||
_ => method, requests => requests.Select(request => request.Method));
|
_ => method,
|
||||||
|
requests => requests.Select(request => request.Method)
|
||||||
|
);
|
||||||
|
|
||||||
|
_requestMessages = filter(_requestMessages).ToList();
|
||||||
|
|
||||||
return new AndConstraint<WireMockAssertions>(this);
|
return new AndConstraint<WireMockAssertions>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (Func<IReadOnlyList<IRequestMessage>, IReadOnlyList<IRequestMessage>> Filter, Func<IReadOnlyList<IRequestMessage>, bool> Condition) BuildFilterAndCondition(Func<IRequestMessage, bool> predicate)
|
||||||
|
{
|
||||||
|
Func<IReadOnlyList<IRequestMessage>, IReadOnlyList<IRequestMessage>> filter = requests => requests.Where(predicate).ToList();
|
||||||
|
|
||||||
|
return (filter, requests => (_callsCount is null && filter(requests).Any()) || _callsCount == filter(requests).Count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
<!--<DelaySign>true</DelaySign>-->
|
<!--<DelaySign>true</DelaySign>-->
|
||||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
#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))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dictionary[key] = value;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dictionary[key] = value;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,92 +1,91 @@
|
|||||||
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>
|
||||||
|
/// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryGetXNullable(this OpenApiSchema schema, out bool value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
value = false;
|
||||||
/// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
|
|
||||||
/// </summary>
|
if (schema.Extensions.TryGetValue("x-nullable", out IOpenApiExtension e) && e is OpenApiBoolean openApiBoolean)
|
||||||
public static bool TryGetXNullable(this OpenApiSchema schema, out bool value)
|
|
||||||
{
|
{
|
||||||
value = false;
|
value = openApiBoolean.Value;
|
||||||
|
return true;
|
||||||
if (schema.Extensions.TryGetValue("x-nullable", out IOpenApiExtension e) && e is OpenApiBoolean openApiBoolean)
|
|
||||||
{
|
|
||||||
value = openApiBoolean.Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SchemaType GetSchemaType(this OpenApiSchema schema)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchemaType GetSchemaType(this OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
switch (schema?.Type)
|
||||||
{
|
{
|
||||||
switch (schema?.Type)
|
case "object":
|
||||||
{
|
return SchemaType.Object;
|
||||||
case "object":
|
|
||||||
return SchemaType.Object;
|
|
||||||
|
|
||||||
case "array":
|
case "array":
|
||||||
return SchemaType.Array;
|
return SchemaType.Array;
|
||||||
|
|
||||||
case "integer":
|
case "integer":
|
||||||
return SchemaType.Integer;
|
return SchemaType.Integer;
|
||||||
|
|
||||||
case "number":
|
case "number":
|
||||||
return SchemaType.Number;
|
return SchemaType.Number;
|
||||||
|
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return SchemaType.Boolean;
|
return SchemaType.Boolean;
|
||||||
|
|
||||||
case "string":
|
case "string":
|
||||||
return SchemaType.String;
|
return SchemaType.String;
|
||||||
|
|
||||||
case "file":
|
case "file":
|
||||||
return SchemaType.File;
|
return SchemaType.File;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return SchemaType.Unknown;
|
return SchemaType.Unknown;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static SchemaFormat GetSchemaFormat(this OpenApiSchema schema)
|
public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
switch (schema?.Format)
|
||||||
{
|
{
|
||||||
switch (schema?.Format)
|
case "float":
|
||||||
{
|
return SchemaFormat.Float;
|
||||||
case "float":
|
|
||||||
return SchemaFormat.Float;
|
|
||||||
|
|
||||||
case "double":
|
case "double":
|
||||||
return SchemaFormat.Double;
|
return SchemaFormat.Double;
|
||||||
|
|
||||||
case "int32":
|
case "int32":
|
||||||
return SchemaFormat.Int32;
|
return SchemaFormat.Int32;
|
||||||
|
|
||||||
case "int64":
|
case "int64":
|
||||||
return SchemaFormat.Int64;
|
return SchemaFormat.Int64;
|
||||||
|
|
||||||
case "date":
|
case "date":
|
||||||
return SchemaFormat.Date;
|
return SchemaFormat.Date;
|
||||||
|
|
||||||
case "date-time":
|
case "date-time":
|
||||||
return SchemaFormat.DateTime;
|
return SchemaFormat.DateTime;
|
||||||
|
|
||||||
case "password":
|
case "password":
|
||||||
return SchemaFormat.Password;
|
return SchemaFormat.Password;
|
||||||
|
|
||||||
case "byte":
|
case "byte":
|
||||||
return SchemaFormat.Byte;
|
return SchemaFormat.Byte;
|
||||||
|
|
||||||
case "binary":
|
case "binary":
|
||||||
return SchemaFormat.Binary;
|
return SchemaFormat.Binary;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return SchemaFormat.Undefined;
|
return SchemaFormat.Undefined;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,97 +1,94 @@
|
|||||||
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>
|
/// <summary>
|
||||||
/// Some extension methods for <see cref="IWireMockServer"/>.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class WireMockServerExtensions
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
|
||||||
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
|
||||||
[PublicAPI]
|
|
||||||
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
|
|
||||||
{
|
|
||||||
return WithMappingFromOpenApiFile(server, path, null, out diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
Guard.NotNull(server, nameof(server));
|
Guard.NotNull(server, nameof(server));
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path, nameof(path));
|
||||||
|
|
||||||
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
||||||
|
|
||||||
return server.WithMapping(mappings.ToArray());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
[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>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
[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);
|
||||||
|
|
||||||
return server.WithMapping(mappings.ToArray());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 document.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 document.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <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,371 +16,365 @@ 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 readonly WireMockOpenApiParserSettings _settings;
|
||||||
|
private readonly ExampleValueGenerator _exampleValueGenerator;
|
||||||
|
|
||||||
|
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
private const string HeaderContentType = "Content-Type";
|
_settings = Guard.NotNull(settings);
|
||||||
|
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths, IList<OpenApiServer> servers)
|
||||||
private readonly ExampleValueGenerator _exampleValueGenerator;
|
{
|
||||||
|
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths, IList<OpenApiServer> servers)
|
||||||
|
{
|
||||||
|
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||||
|
{
|
||||||
|
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
||||||
|
{
|
||||||
|
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
|
||||||
|
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
|
||||||
|
var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header);
|
||||||
|
|
||||||
|
var response = operation.Responses.FirstOrDefault();
|
||||||
|
|
||||||
|
TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType);
|
||||||
|
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
|
||||||
|
var responseExample = responseContent?.Example;
|
||||||
|
var responseSchemaExample = responseContent?.Schema?.Example;
|
||||||
|
|
||||||
|
var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) :
|
||||||
|
responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) :
|
||||||
|
MapSchemaToObject(responseSchema);
|
||||||
|
|
||||||
|
var requestBodyModel = new BodyModel();
|
||||||
|
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings, nameof(settings));
|
var request = operation.RequestBody.Content;
|
||||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
TryGetContent(request, out OpenApiMediaType? requestContent, out _);
|
||||||
|
|
||||||
|
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
|
||||||
|
var requestBodyExample = requestContent!.Example;
|
||||||
|
var requestBodySchemaExample = requestContent.Schema?.Example;
|
||||||
|
|
||||||
|
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
|
||||||
|
requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) :
|
||||||
|
MapSchemaToObject(requestBodySchema);
|
||||||
|
|
||||||
|
requestBodyModel = MapRequestBody(requestBodyMapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths, IList<OpenApiServer> servers)
|
if (!int.TryParse(response.Key, out var httpStatusCode))
|
||||||
{
|
{
|
||||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
httpStatusCode = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths, IList<OpenApiServer> servers)
|
return new MappingModel
|
||||||
{
|
{
|
||||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
Guid = Guid.NewGuid(),
|
||||||
}
|
Request = new RequestModel
|
||||||
|
|
||||||
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
|
||||||
{
|
|
||||||
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
|
|
||||||
}
|
|
||||||
|
|
||||||
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
|
||||||
{
|
|
||||||
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
|
|
||||||
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
|
|
||||||
var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header);
|
|
||||||
|
|
||||||
var response = operation.Responses.FirstOrDefault();
|
|
||||||
|
|
||||||
TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType);
|
|
||||||
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
|
|
||||||
var responseExample = responseContent?.Example;
|
|
||||||
var responseSchemaExample = responseContent?.Schema?.Example;
|
|
||||||
|
|
||||||
var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) :
|
|
||||||
responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) :
|
|
||||||
MapSchemaToObject(responseSchema);
|
|
||||||
|
|
||||||
var requestBodyModel = new BodyModel();
|
|
||||||
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
|
|
||||||
{
|
{
|
||||||
var request = operation.RequestBody.Content;
|
Methods = new[] { httpMethod },
|
||||||
TryGetContent(request, out OpenApiMediaType requestContent, out string requestContentType);
|
Path = MapBasePath(servers) + MapPathWithParameters(path, pathParameters),
|
||||||
|
Params = MapQueryParameters(queryParameters),
|
||||||
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
|
Headers = MapRequestHeaders(headers),
|
||||||
var requestBodyExample = requestContent.Example;
|
Body = requestBodyModel
|
||||||
var requestBodySchemaExample = requestContent.Schema?.Example;
|
},
|
||||||
|
Response = new ResponseModel
|
||||||
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
|
{
|
||||||
requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) :
|
StatusCode = httpStatusCode,
|
||||||
MapSchemaToObject(requestBodySchema);
|
Headers = MapHeaders(responseContentType, response.Value?.Headers),
|
||||||
|
BodyAsJson = body
|
||||||
requestBodyModel = MapRequestBody(requestBodyMapped);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!int.TryParse(response.Key, out var httpStatusCode))
|
private BodyModel? MapRequestBody(object? requestBody)
|
||||||
|
{
|
||||||
|
if (requestBody == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BodyModel
|
||||||
|
{
|
||||||
|
Matcher = new MatcherModel
|
||||||
{
|
{
|
||||||
httpStatusCode = 200;
|
Name = "JsonMatcher",
|
||||||
|
Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented),
|
||||||
|
IgnoreCase = _settings.RequestBodyIgnoreCase
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new MappingModel
|
private bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
|
||||||
{
|
{
|
||||||
Guid = Guid.NewGuid(),
|
openApiMediaType = null;
|
||||||
Request = new RequestModel
|
contentType = null;
|
||||||
|
|
||||||
|
if (contents == null || contents.Values.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.TryGetValue("application/json", out var content))
|
||||||
|
{
|
||||||
|
openApiMediaType = content;
|
||||||
|
contentType = "application/json";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var first = contents.FirstOrDefault();
|
||||||
|
openApiMediaType = first.Value;
|
||||||
|
contentType = first.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null)
|
||||||
|
{
|
||||||
|
if (schema == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (schema.GetSchemaType())
|
||||||
|
{
|
||||||
|
case SchemaType.Array:
|
||||||
|
var jArray = new JArray();
|
||||||
|
for (int i = 0; i < _settings.NumberOfArrayItems; i++)
|
||||||
{
|
{
|
||||||
Methods = new[] { httpMethod },
|
if (schema.Items.Properties.Count > 0)
|
||||||
Path = MapBasePath(servers) + MapPathWithParameters(path, pathParameters),
|
|
||||||
Params = MapQueryParameters(queryParameters),
|
|
||||||
Headers = MapRequestHeaders(headers),
|
|
||||||
Body = requestBodyModel
|
|
||||||
},
|
|
||||||
Response = new ResponseModel
|
|
||||||
{
|
|
||||||
StatusCode = httpStatusCode,
|
|
||||||
Headers = MapHeaders(responseContentType, response.Value?.Headers),
|
|
||||||
BodyAsJson = body
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private BodyModel MapRequestBody(object requestBody)
|
|
||||||
{
|
|
||||||
if (requestBody == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BodyModel
|
|
||||||
{
|
|
||||||
Matcher = new MatcherModel
|
|
||||||
{
|
|
||||||
Name = "JsonMatcher",
|
|
||||||
Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented),
|
|
||||||
IgnoreCase = _settings.RequestBodyIgnoreCase
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetContent(IDictionary<string, OpenApiMediaType> contents, out OpenApiMediaType openApiMediaType, out string contentType)
|
|
||||||
{
|
|
||||||
openApiMediaType = null;
|
|
||||||
contentType = null;
|
|
||||||
|
|
||||||
if (contents == null || contents.Values.Count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contents.TryGetValue("application/json", out var content))
|
|
||||||
{
|
|
||||||
openApiMediaType = content;
|
|
||||||
contentType = "application/json";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var first = contents.FirstOrDefault();
|
|
||||||
openApiMediaType = first.Value;
|
|
||||||
contentType = first.Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object MapSchemaToObject(OpenApiSchema schema, string name = null)
|
|
||||||
{
|
|
||||||
if (schema == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (schema.GetSchemaType())
|
|
||||||
{
|
|
||||||
case SchemaType.Array:
|
|
||||||
var jArray = new JArray();
|
|
||||||
for (int i = 0; i < _settings.NumberOfArrayItems; i++)
|
|
||||||
{
|
{
|
||||||
if (schema.Items.Properties.Count > 0)
|
var arrayItem = new JObject();
|
||||||
|
foreach (var property in schema.Items.Properties)
|
||||||
{
|
{
|
||||||
var arrayItem = new JObject();
|
var objectValue = MapSchemaToObject(property.Value, property.Key);
|
||||||
foreach (var property in schema.Items.Properties)
|
if (objectValue is JProperty jp)
|
||||||
{
|
{
|
||||||
var objectValue = MapSchemaToObject(property.Value, property.Key);
|
arrayItem.Add(jp);
|
||||||
if (objectValue is JProperty jp)
|
|
||||||
{
|
|
||||||
arrayItem.Add(jp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
arrayItem.Add(new JProperty(property.Key, objectValue));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
jArray.Add(arrayItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jArray.Add(MapSchemaToObject(schema.Items, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schema.AllOf.Count > 0)
|
|
||||||
{
|
|
||||||
jArray.Add(MapSchemaAllOfToObject(schema));
|
|
||||||
}
|
|
||||||
|
|
||||||
return jArray;
|
|
||||||
|
|
||||||
case SchemaType.Boolean:
|
|
||||||
case SchemaType.Integer:
|
|
||||||
case SchemaType.Number:
|
|
||||||
case SchemaType.String:
|
|
||||||
return _exampleValueGenerator.GetExampleValue(schema);
|
|
||||||
|
|
||||||
case SchemaType.Object:
|
|
||||||
var propertyAsJObject = new JObject();
|
|
||||||
foreach (var schemaProperty in schema.Properties)
|
|
||||||
{
|
|
||||||
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
|
|
||||||
}
|
|
||||||
if (schema.AllOf.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var property in schema.AllOf)
|
|
||||||
{
|
|
||||||
foreach (var item in property.Properties)
|
|
||||||
{
|
{
|
||||||
propertyAsJObject.Add(MapPropertyAsJObject(item.Value, item.Key));
|
arrayItem.Add(new JProperty(property.Key, objectValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jArray.Add(arrayItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jArray.Add(MapSchemaToObject(schema.Items, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JObject MapSchemaAllOfToObject(OpenApiSchema schema)
|
|
||||||
{
|
|
||||||
var arrayItem = new JObject();
|
|
||||||
foreach (var property in schema.AllOf)
|
|
||||||
{
|
|
||||||
foreach (var item in property.Properties)
|
|
||||||
{
|
|
||||||
arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return arrayItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key)
|
if (schema.AllOf.Count > 0)
|
||||||
{
|
|
||||||
if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array)
|
|
||||||
{
|
|
||||||
var mapped = MapSchemaToObject(openApiSchema, key);
|
|
||||||
if (mapped is JProperty jp)
|
|
||||||
{
|
{
|
||||||
return jp;
|
jArray.Add(MapSchemaAllOfToObject(schema));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
return jArray;
|
||||||
|
|
||||||
|
case SchemaType.Boolean:
|
||||||
|
case SchemaType.Integer:
|
||||||
|
case SchemaType.Number:
|
||||||
|
case SchemaType.String:
|
||||||
|
return _exampleValueGenerator.GetExampleValue(schema);
|
||||||
|
|
||||||
|
case SchemaType.Object:
|
||||||
|
var propertyAsJObject = new JObject();
|
||||||
|
foreach (var schemaProperty in schema.Properties)
|
||||||
{
|
{
|
||||||
return new JProperty(key, mapped);
|
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (schema.AllOf.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var property in schema.AllOf)
|
||||||
|
{
|
||||||
|
foreach (var item in property.Properties)
|
||||||
|
{
|
||||||
|
propertyAsJObject.Add(MapPropertyAsJObject(item.Value, item.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JObject MapSchemaAllOfToObject(OpenApiSchema schema)
|
||||||
|
{
|
||||||
|
var arrayItem = new JObject();
|
||||||
|
foreach (var property in schema.AllOf)
|
||||||
|
{
|
||||||
|
foreach (var item in property.Properties)
|
||||||
{
|
{
|
||||||
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
|
arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key));
|
||||||
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return arrayItem;
|
||||||
|
}
|
||||||
|
|
||||||
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters)
|
private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key)
|
||||||
|
{
|
||||||
|
if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array)
|
||||||
{
|
{
|
||||||
if (parameters == null)
|
var mapped = MapSchemaToObject(openApiSchema, key);
|
||||||
|
if (mapped is JProperty jp)
|
||||||
{
|
{
|
||||||
return path;
|
return jp;
|
||||||
}
|
}
|
||||||
|
|
||||||
string newPath = path;
|
return new JProperty(key, mapped);
|
||||||
foreach (var parameter in parameters)
|
|
||||||
{
|
|
||||||
var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse);
|
|
||||||
newPath = newPath.Replace($"{{{parameter.Name}}}", exampleMatcherModel.Pattern as string);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MapBasePath(IList<OpenApiServer> servers)
|
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
|
||||||
|
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter>? parameters)
|
||||||
|
{
|
||||||
|
if (parameters == null)
|
||||||
{
|
{
|
||||||
if (servers == null || servers.Count == 0)
|
return path;
|
||||||
{
|
}
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenApiServer server = servers.First();
|
string newPath = path;
|
||||||
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
foreach (var parameter in parameters)
|
||||||
{
|
{
|
||||||
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse);
|
||||||
}
|
newPath = newPath.Replace($"{{{parameter.Name}}}", exampleMatcherModel.Pattern as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapBasePath(IList<OpenApiServer>? servers)
|
||||||
|
{
|
||||||
|
if (servers == null || servers.Count == 0)
|
||||||
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JToken MapOpenApiAnyToJToken(IOpenApiAny any)
|
OpenApiServer server = servers.First();
|
||||||
|
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
||||||
{
|
{
|
||||||
if (any == null)
|
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var outputString = new StringWriter();
|
|
||||||
var writer = new OpenApiJsonWriter(outputString);
|
|
||||||
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
|
||||||
|
|
||||||
if (any.AnyType == AnyType.Array)
|
|
||||||
{
|
|
||||||
return JArray.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return JObject.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
|
||||||
|
{
|
||||||
|
if (any == null)
|
||||||
{
|
{
|
||||||
var mappedHeaders = headers.ToDictionary(
|
return null;
|
||||||
item => item.Key,
|
|
||||||
item => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(responseContentType))
|
|
||||||
{
|
|
||||||
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
|
using var outputString = new StringWriter();
|
||||||
|
var writer = new OpenApiJsonWriter(outputString);
|
||||||
|
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
||||||
|
|
||||||
|
if (any.AnyType == AnyType.Array)
|
||||||
{
|
{
|
||||||
var list = queryParameters
|
return JArray.Parse(outputString.ToString());
|
||||||
.Where(req => req.Required)
|
}
|
||||||
.Select(qp => new ParamModel
|
|
||||||
|
return JObject.Parse(outputString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
|
||||||
|
{
|
||||||
|
var mappedHeaders = headers.ToDictionary(
|
||||||
|
item => item.Key,
|
||||||
|
_ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(responseContentType))
|
||||||
|
{
|
||||||
|
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
|
||||||
|
{
|
||||||
|
var list = queryParameters
|
||||||
|
.Where(req => req.Required)
|
||||||
|
.Select(qp => new ParamModel
|
||||||
|
{
|
||||||
|
Name = qp.Name,
|
||||||
|
IgnoreCase = _settings.QueryParameterPatternIgnoreCase,
|
||||||
|
Matchers = new[]
|
||||||
{
|
{
|
||||||
Name = qp.Name,
|
GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse)
|
||||||
IgnoreCase = _settings.QueryParameterPatternIgnoreCase,
|
}
|
||||||
Matchers = new[]
|
})
|
||||||
{
|
.ToList();
|
||||||
GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
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)
|
||||||
.Select(qp => new HeaderModel
|
.Select(qp => new HeaderModel
|
||||||
|
{
|
||||||
|
Name = qp.Name,
|
||||||
|
IgnoreCase = _settings.HeaderPatternIgnoreCase,
|
||||||
|
Matchers = new[]
|
||||||
{
|
{
|
||||||
Name = qp.Name,
|
GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse)
|
||||||
IgnoreCase = _settings.HeaderPatternIgnoreCase,
|
}
|
||||||
Matchers = new[]
|
})
|
||||||
{
|
.ToList();
|
||||||
GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
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
|
ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues },
|
||||||
{
|
|
||||||
ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues },
|
|
||||||
|
|
||||||
_ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" }
|
_ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetExampleValueAsStringForSchemaType(OpenApiSchema schema)
|
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
var value = _exampleValueGenerator.GetExampleValue(schema);
|
||||||
|
|
||||||
|
return value switch
|
||||||
{
|
{
|
||||||
var value = _exampleValueGenerator.GetExampleValue(schema);
|
string valueAsString => valueAsString,
|
||||||
|
|
||||||
return value switch
|
_ => value.ToString(),
|
||||||
{
|
};
|
||||||
string valueAsString => valueAsString,
|
|
||||||
|
|
||||||
_ => value.ToString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,58 +1,60 @@
|
|||||||
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>
|
/// <summary>
|
||||||
/// A interface defining the example values to use for the different types.
|
/// An example value for a Boolean.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IWireMockOpenApiParserExampleValues
|
bool Boolean { get; set; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for a Boolean.
|
|
||||||
/// </summary>
|
|
||||||
bool Boolean { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for an Integer.
|
|
||||||
/// </summary>
|
|
||||||
int Integer { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for a Float.
|
|
||||||
/// </summary>
|
|
||||||
float Float { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for a Double.
|
|
||||||
/// </summary>
|
|
||||||
double Double { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a Date.
|
/// An example value for an Integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Func<DateTime> Date { get; set; }
|
int Integer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a DateTime.
|
/// An example value for a Float.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Func<DateTime> DateTime { get; set; }
|
float Float { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for Bytes.
|
/// An example value for a Double.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte[] Bytes { get; set; }
|
double Double { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a Object.
|
/// An example value for a Date.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object Object { get; set; }
|
Func<DateTime> Date { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a String.
|
/// An example value for a DateTime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string String { get; set; }
|
Func<DateTime> DateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OpenApi Schema to generate dynamic examples more accurate
|
/// An example value for Bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
OpenApiSchema Schema { get; set; }
|
byte[] Bytes { get; set; }
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// An example value for a Object.
|
||||||
|
/// </summary>
|
||||||
|
object Object { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An example value for a String.
|
||||||
|
/// </summary>
|
||||||
|
string String { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OpenApi Schema to generate dynamic examples more accurate
|
||||||
|
/// </summary>
|
||||||
|
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>
|
/// <inheritdoc />
|
||||||
/// A class defining the random example values to use for the different types.
|
public virtual bool Boolean { get => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; set { } }
|
||||||
/// </summary>
|
|
||||||
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
|
/// <inheritdoc />
|
||||||
{
|
public virtual int Integer { get => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; set { } }
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } }
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual float Float { get => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; set { } }
|
||||||
public virtual int Integer { get { return 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 double Double { get => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; set { } }
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual double Double { get { return 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 />
|
|
||||||
public virtual byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } }
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual byte[] Bytes { get => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); set { } }
|
||||||
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 object Object { get; set; } = "example-object";
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual OpenApiSchema Schema { get; set; }
|
/// <inheritdoc />
|
||||||
}
|
public virtual string String { get => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; set { } }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
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>
|
/// <inheritdoc />
|
||||||
/// A class defining the example values to use for the different types.
|
public virtual bool Boolean { get; set; } = true;
|
||||||
/// </summary>
|
|
||||||
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
|
/// <inheritdoc />
|
||||||
{
|
public virtual int Integer { get; set; } = 42;
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual bool Boolean { get; set; } = true;
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual float Float { get; set; } = 4.2f;
|
||||||
public virtual int Integer { get; set; } = 42;
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual float Float { get; set; } = 4.2f;
|
public virtual double Double { get; set; } = 4.2d;
|
||||||
/// <inheritdoc />
|
|
||||||
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 />
|
|
||||||
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
|
||||||
public virtual object Object { get; set; } = "example-object";
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual string String { get; set; } = "example-string";
|
public virtual object Object { get; set; } = "example-object";
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual OpenApiSchema Schema { get; set; } = new OpenApiSchema();
|
/// <inheritdoc />
|
||||||
}
|
public virtual string String { get; set; } = "example-string";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
|
||||||
}
|
}
|
||||||
@@ -1,64 +1,63 @@
|
|||||||
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>
|
/// <summary>
|
||||||
/// The WireMockOpenApiParser Settings
|
/// The number of array items to generate (default is 3).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WireMockOpenApiParserSettings
|
public int NumberOfArrayItems { get; set; } = 3;
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The number of array items to generate (default is 3).
|
|
||||||
/// </summary>
|
|
||||||
public int NumberOfArrayItems { get; set; } = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value type to use when generating a Path
|
/// The example value type to use when generating a Path
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value;
|
public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value type to use when generating a Header
|
/// The example value type to use when generating a Header
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value;
|
public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value type to use when generating a Query Parameter
|
/// The example value type to use when generating a Query Parameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExampleValueType QueryParameterPatternToUse { get; set; } = ExampleValueType.Value;
|
public ExampleValueType QueryParameterPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example values to use.
|
/// The example values to use.
|
||||||
///
|
///
|
||||||
/// Default implementations are:
|
/// Default implementations are:
|
||||||
/// - <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).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HeaderPatternIgnoreCase { get; set; } = true;
|
public bool HeaderPatternIgnoreCase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a Query Parameter match case insensitive? (default is true).
|
/// Is a Query Parameter match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool QueryParameterPatternIgnoreCase { get; set; } = true;
|
public bool QueryParameterPatternIgnoreCase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a Request Body match case insensitive? (default is true).
|
/// Is a Request Body match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequestBodyIgnoreCase { get; set; } = true;
|
public bool RequestBodyIgnoreCase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a ExampleValue match case insensitive? (default is true).
|
/// Is a ExampleValue match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IgnoreCaseExampleValues { get; set; } = true;
|
public bool IgnoreCaseExampleValues { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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,20 +1,19 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The example value to use
|
||||||
|
/// </summary>
|
||||||
|
public enum ExampleValueType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value to use
|
/// 1. Use a generated example value based on the SchemaType (default).
|
||||||
|
/// 2. If there is no example value defined in the schema,
|
||||||
|
/// then the <see cref="Settings.IWireMockOpenApiParserExampleValues"/> will be used (custom, fixed or dynamic).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ExampleValueType
|
Value,
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 1. Use a generated example value based on the SchemaType (default).
|
|
||||||
/// 2. If there is no example value defined in the schema,
|
|
||||||
/// then the <see cref="Settings.IWireMockOpenApiParserExampleValues"/> will be used (custom, fixed or dynamic).
|
|
||||||
/// </summary>
|
|
||||||
Value,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Just use a Wildcard (*) character.
|
/// Just use a Wildcard (*) character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Wildcard
|
Wildcard
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,24 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
internal enum SchemaFormat
|
||||||
{
|
{
|
||||||
internal enum SchemaFormat
|
Float,
|
||||||
{
|
|
||||||
Float,
|
|
||||||
|
|
||||||
Double,
|
Double,
|
||||||
|
|
||||||
Int32,
|
Int32,
|
||||||
|
|
||||||
Int64,
|
Int64,
|
||||||
|
|
||||||
Date,
|
Date,
|
||||||
|
|
||||||
DateTime,
|
DateTime,
|
||||||
|
|
||||||
Password,
|
Password,
|
||||||
|
|
||||||
Byte,
|
Byte,
|
||||||
|
|
||||||
Binary,
|
Binary,
|
||||||
|
|
||||||
Undefined
|
Undefined
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
internal enum SchemaType
|
||||||
{
|
{
|
||||||
internal enum SchemaType
|
Object,
|
||||||
{
|
|
||||||
Object,
|
|
||||||
|
|
||||||
Array,
|
Array,
|
||||||
|
|
||||||
String,
|
String,
|
||||||
|
|
||||||
Integer,
|
Integer,
|
||||||
|
|
||||||
Number,
|
Number,
|
||||||
|
|
||||||
Boolean,
|
Boolean,
|
||||||
|
|
||||||
File,
|
File,
|
||||||
|
|
||||||
Unknown
|
Unknown
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Utils
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
{
|
|
||||||
internal static class DateTimeUtils
|
|
||||||
{
|
|
||||||
public static string ToRfc3339DateTime(DateTime dateTime)
|
|
||||||
{
|
|
||||||
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToRfc3339Date(DateTime dateTime)
|
internal static class DateTimeUtils
|
||||||
{
|
{
|
||||||
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
|
public static string ToRfc3339DateTime(DateTime dateTime)
|
||||||
}
|
{
|
||||||
|
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToRfc3339Date(DateTime dateTime)
|
||||||
|
{
|
||||||
|
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,121 +7,120 @@ 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;
|
||||||
|
|
||||||
|
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
_settings = Guard.NotNull(settings);
|
||||||
|
|
||||||
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
// Check if user provided an own implementation
|
||||||
|
if (settings.ExampleValues is null)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings, nameof(settings));
|
if (_settings.DynamicExamples)
|
||||||
|
|
||||||
// Check if user provided an own implementation
|
|
||||||
if (settings.ExampleValues is null)
|
|
||||||
{
|
{
|
||||||
if (_settings.DynamicExamples)
|
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
||||||
{
|
|
||||||
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
public object GetExampleValue(OpenApiSchema schema)
|
|
||||||
{
|
|
||||||
var schemaExample = schema?.Example;
|
|
||||||
var schemaEnum = GetRandomEnumValue(schema?.Enum);
|
|
||||||
|
|
||||||
_settings.ExampleValues.Schema = schema;
|
|
||||||
|
|
||||||
switch (schema?.GetSchemaType())
|
|
||||||
{
|
{
|
||||||
case SchemaType.Boolean:
|
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
|
||||||
var exampleBoolean = (OpenApiBoolean)schemaExample;
|
|
||||||
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
|
|
||||||
|
|
||||||
case SchemaType.Integer:
|
|
||||||
switch (schema?.GetSchemaFormat())
|
|
||||||
{
|
|
||||||
case SchemaFormat.Int64:
|
|
||||||
var exampleLong = (OpenApiLong)schemaExample;
|
|
||||||
var enumLong = (OpenApiLong)schemaEnum;
|
|
||||||
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
|
|
||||||
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var exampleInteger = (OpenApiInteger)schemaExample;
|
|
||||||
var enumInteger = (OpenApiInteger)schemaEnum;
|
|
||||||
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
|
|
||||||
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SchemaType.Number:
|
|
||||||
switch (schema?.GetSchemaFormat())
|
|
||||||
{
|
|
||||||
case SchemaFormat.Float:
|
|
||||||
var exampleFloat = (OpenApiFloat)schemaExample;
|
|
||||||
var enumFloat = (OpenApiFloat)schemaEnum;
|
|
||||||
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
|
|
||||||
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var exampleDouble = (OpenApiDouble)schemaExample;
|
|
||||||
var enumDouble = (OpenApiDouble)schemaEnum;
|
|
||||||
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
|
|
||||||
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
switch (schema?.GetSchemaFormat())
|
|
||||||
{
|
|
||||||
case SchemaFormat.Date:
|
|
||||||
var exampleDate = (OpenApiDate)schemaExample;
|
|
||||||
var enumDate = (OpenApiDate)schemaEnum;
|
|
||||||
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
|
|
||||||
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
|
|
||||||
|
|
||||||
case SchemaFormat.DateTime:
|
|
||||||
var exampleDateTime = (OpenApiDateTime)schemaExample;
|
|
||||||
var enumDateTime = (OpenApiDateTime)schemaEnum;
|
|
||||||
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
|
|
||||||
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
|
|
||||||
|
|
||||||
case SchemaFormat.Byte:
|
|
||||||
var exampleByte = (OpenApiByte)schemaExample;
|
|
||||||
var enumByte = (OpenApiByte)schemaEnum;
|
|
||||||
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
|
|
||||||
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
|
|
||||||
|
|
||||||
case SchemaFormat.Binary:
|
|
||||||
var exampleBinary = (OpenApiBinary)schemaExample;
|
|
||||||
var enumBinary = (OpenApiBinary)schemaEnum;
|
|
||||||
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
|
|
||||||
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var exampleString = (OpenApiString)schemaExample;
|
|
||||||
var enumString = (OpenApiString)schemaEnum;
|
|
||||||
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
|
|
||||||
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static IOpenApiAny GetRandomEnumValue(IList<IOpenApiAny> schemaEnum)
|
|
||||||
{
|
public object GetExampleValue(OpenApiSchema? schema)
|
||||||
if (schemaEnum?.Count > 0)
|
{
|
||||||
{
|
var schemaExample = schema?.Example;
|
||||||
int maxValue = schemaEnum.Count - 1;
|
var schemaEnum = GetRandomEnumValue(schema?.Enum);
|
||||||
int randomEnum = new Random().Next(0, maxValue);
|
|
||||||
return schemaEnum[randomEnum];
|
_settings.ExampleValues.Schema = schema;
|
||||||
}
|
|
||||||
|
switch (schema?.GetSchemaType())
|
||||||
return null;
|
{
|
||||||
}
|
case SchemaType.Boolean:
|
||||||
|
var exampleBoolean = schemaExample as OpenApiBoolean;
|
||||||
|
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
|
||||||
|
|
||||||
|
case SchemaType.Integer:
|
||||||
|
switch (schema?.GetSchemaFormat())
|
||||||
|
{
|
||||||
|
case SchemaFormat.Int64:
|
||||||
|
var exampleLong = (OpenApiLong)schemaExample;
|
||||||
|
var enumLong = (OpenApiLong)schemaEnum;
|
||||||
|
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
|
||||||
|
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var exampleInteger = (OpenApiInteger)schemaExample;
|
||||||
|
var enumInteger = (OpenApiInteger)schemaEnum;
|
||||||
|
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
|
||||||
|
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SchemaType.Number:
|
||||||
|
switch (schema?.GetSchemaFormat())
|
||||||
|
{
|
||||||
|
case SchemaFormat.Float:
|
||||||
|
var exampleFloat = (OpenApiFloat)schemaExample;
|
||||||
|
var enumFloat = (OpenApiFloat)schemaEnum;
|
||||||
|
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
|
||||||
|
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var exampleDouble = (OpenApiDouble)schemaExample;
|
||||||
|
var enumDouble = (OpenApiDouble)schemaEnum;
|
||||||
|
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
|
||||||
|
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch (schema?.GetSchemaFormat())
|
||||||
|
{
|
||||||
|
case SchemaFormat.Date:
|
||||||
|
var exampleDate = (OpenApiDate)schemaExample;
|
||||||
|
var enumDate = (OpenApiDate)schemaEnum;
|
||||||
|
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
|
||||||
|
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
|
||||||
|
|
||||||
|
case SchemaFormat.DateTime:
|
||||||
|
var exampleDateTime = (OpenApiDateTime)schemaExample;
|
||||||
|
var enumDateTime = (OpenApiDateTime)schemaEnum;
|
||||||
|
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
|
||||||
|
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
|
||||||
|
|
||||||
|
case SchemaFormat.Byte:
|
||||||
|
var exampleByte = (OpenApiByte)schemaExample;
|
||||||
|
var enumByte = (OpenApiByte)schemaEnum;
|
||||||
|
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
|
||||||
|
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
|
||||||
|
|
||||||
|
case SchemaFormat.Binary:
|
||||||
|
var exampleBinary = (OpenApiBinary)schemaExample;
|
||||||
|
var enumBinary = (OpenApiBinary)schemaEnum;
|
||||||
|
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
|
||||||
|
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var exampleString = (OpenApiString)schemaExample;
|
||||||
|
var enumString = (OpenApiString)schemaEnum;
|
||||||
|
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
|
||||||
|
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IOpenApiAny? GetRandomEnumValue(IList<IOpenApiAny>? schemaEnum)
|
||||||
|
{
|
||||||
|
if (schemaEnum?.Count > 0)
|
||||||
|
{
|
||||||
|
int maxValue = schemaEnum.Count - 1;
|
||||||
|
int randomEnum = new Random().Next(0, maxValue);
|
||||||
|
return schemaEnum[randomEnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
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,60 +9,59 @@ 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>
|
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
||||||
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
|
|
||||||
/// </summary>
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
||||||
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
[PublicAPI]
|
||||||
|
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
OpenApiDocument document;
|
||||||
|
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
diagnostic = new OpenApiDiagnostic();
|
||||||
|
document = new RamlConverter().ConvertToOpenApiDocument(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var reader = new OpenApiStreamReader();
|
||||||
|
document = reader.Read(File.OpenRead(path), out diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
return FromDocument(document, settings);
|
||||||
[PublicAPI]
|
}
|
||||||
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
|
||||||
{
|
|
||||||
OpenApiDocument document;
|
|
||||||
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
diagnostic = new OpenApiDiagnostic();
|
|
||||||
document = new RamlConverter().ConvertToOpenApiDocument(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var reader = new OpenApiStreamReader();
|
|
||||||
document = reader.Read(File.OpenRead(path), out diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FromDocument(document, settings);
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
||||||
}
|
[PublicAPI]
|
||||||
|
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
return FromDocument(_reader.Read(stream, out diagnostic));
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return FromDocument(_reader.Read(stream, out diagnostic));
|
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
|
||||||
{
|
{
|
||||||
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
|
||||||
[PublicAPI]
|
|
||||||
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null)
|
|
||||||
{
|
|
||||||
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>
|
||||||
|
|||||||
@@ -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,15 +36,18 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
var bodyParserSettings = new BodyParserSettings
|
if (httpResponseMessage.StatusCode != HttpStatusCode.NoContent) // A body is not allowed for 204.
|
||||||
{
|
{
|
||||||
Stream = stream,
|
var bodyParserSettings = new BodyParserSettings
|
||||||
ContentType = contentTypeHeader?.FirstOrDefault(),
|
{
|
||||||
DeserializeJson = deserializeJson,
|
Stream = stream,
|
||||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
ContentType = contentTypeHeader?.FirstOrDefault(),
|
||||||
DecompressGZipAndDeflate = decompressGzipAndDeflate
|
DeserializeJson = deserializeJson,
|
||||||
};
|
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
DecompressGZipAndDeflate = decompressGzipAndDeflate
|
||||||
|
};
|
||||||
|
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var header in headers)
|
foreach (var header in headers)
|
||||||
|
|||||||
@@ -44,37 +44,41 @@ internal class WebhookSender
|
|||||||
|
|
||||||
IBodyData? bodyData;
|
IBodyData? bodyData;
|
||||||
IDictionary<string, WireMockList<string>>? headers;
|
IDictionary<string, WireMockList<string>>? headers;
|
||||||
|
string webhookRequestUrl;
|
||||||
if (webhookRequest.UseTransformer == true)
|
if (webhookRequest.UseTransformer == true)
|
||||||
{
|
{
|
||||||
ITransformer responseMessageTransformer;
|
ITransformer transformer;
|
||||||
switch (webhookRequest.TransformerType)
|
switch (webhookRequest.TransformerType)
|
||||||
{
|
{
|
||||||
case TransformerType.Handlebars:
|
case TransformerType.Handlebars:
|
||||||
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
|
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
|
||||||
responseMessageTransformer = new Transformer(factoryHandlebars);
|
transformer = new Transformer(factoryHandlebars);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TransformerType.Scriban:
|
case TransformerType.Scriban:
|
||||||
case TransformerType.ScribanDotLiquid:
|
case TransformerType.ScribanDotLiquid:
|
||||||
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
|
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
|
||||||
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
transformer = new Transformer(factoryDotLiquid);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
|
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
(bodyData, headers) = responseMessageTransformer.Transform(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.Headers, webhookRequest.TransformerReplaceNodeOptions);
|
bodyData = transformer.TransformBody(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.TransformerReplaceNodeOptions);
|
||||||
|
headers = transformer.TransformHeaders(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Headers);
|
||||||
|
webhookRequestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bodyData = webhookRequest.BodyData;
|
bodyData = webhookRequest.BodyData;
|
||||||
headers = webhookRequest.Headers;
|
headers = webhookRequest.Headers;
|
||||||
|
webhookRequestUrl = webhookRequest.Url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create RequestMessage
|
// Create RequestMessage
|
||||||
var requestMessage = new RequestMessage(
|
var requestMessage = new RequestMessage(
|
||||||
new UrlDetails(webhookRequest.Url),
|
new UrlDetails(webhookRequestUrl),
|
||||||
webhookRequest.Method,
|
webhookRequest.Method,
|
||||||
ClientIp,
|
ClientIp,
|
||||||
bodyData,
|
bodyData,
|
||||||
@@ -85,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))
|
||||||
|
|||||||
@@ -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,73 +2,72 @@ 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>
|
/// <summary>
|
||||||
/// WireMockConsoleLogger which logs to Console
|
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IWireMockLogger" />
|
public WireMockConsoleLogger()
|
||||||
public class WireMockConsoleLogger : IWireMockLogger
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||||
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
|
}
|
||||||
/// </summary>
|
|
||||||
public WireMockConsoleLogger()
|
|
||||||
{
|
|
||||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Debug"/>
|
/// <see cref="IWireMockLogger.Debug"/>
|
||||||
public void Debug(string formatString, params object[] args)
|
public void Debug(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Debug", formatString, args));
|
Console.WriteLine(Format("Debug", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Info"/>
|
/// <see cref="IWireMockLogger.Info"/>
|
||||||
public void Info(string formatString, params object[] args)
|
public void Info(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Info", formatString, args));
|
Console.WriteLine(Format("Info", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Warn"/>
|
/// <see cref="IWireMockLogger.Warn"/>
|
||||||
public void Warn(string formatString, params object[] args)
|
public void Warn(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Warn", formatString, args));
|
Console.WriteLine(Format("Warn", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||||
public void Error(string formatString, params object[] args)
|
public void Error(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Error", formatString, args));
|
Console.WriteLine(Format("Error", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||||
public void Error(string formatString, Exception exception)
|
public void Error(string formatString, Exception exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Error", formatString, exception.Message));
|
Console.WriteLine(Format("Error", formatString, exception.Message));
|
||||||
|
|
||||||
if (exception is AggregateException ae)
|
if (exception is AggregateException ae)
|
||||||
|
{
|
||||||
|
ae.Handle(ex =>
|
||||||
{
|
{
|
||||||
ae.Handle(ex =>
|
Console.WriteLine(Format("Error", "Exception {0}", ex.Message));
|
||||||
{
|
return true;
|
||||||
Console.WriteLine(Format("Error", "Exception {0}", ex.Message));
|
});
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
|
||||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
|
||||||
{
|
|
||||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
|
||||||
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Format(string level, string formatString, params object[] args)
|
|
||||||
{
|
|
||||||
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
|
||||||
|
|
||||||
return $"{DateTime.UtcNow} [{level}] : {message}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||||
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
|
{
|
||||||
|
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||||
|
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(string level, string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
||||||
|
|
||||||
|
return $"{DateTime.UtcNow} [{level}] : {message}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,48 +1,47 @@
|
|||||||
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>
|
/// <see cref="IWireMockLogger.Debug"/>
|
||||||
/// WireMockNullLogger which does not log.
|
public void Debug(string formatString, params object[] args)
|
||||||
/// </summary>
|
|
||||||
/// <seealso cref="IWireMockLogger" />
|
|
||||||
public class WireMockNullLogger : IWireMockLogger
|
|
||||||
{
|
{
|
||||||
/// <see cref="IWireMockLogger.Debug"/>
|
// Log nothing
|
||||||
public void Debug(string formatString, params object[] args)
|
}
|
||||||
{
|
|
||||||
// Log nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Info"/>
|
/// <see cref="IWireMockLogger.Info"/>
|
||||||
public void Info(string formatString, params object[] args)
|
public void Info(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Warn"/>
|
/// <see cref="IWireMockLogger.Warn"/>
|
||||||
public void Warn(string formatString, params object[] args)
|
public void Warn(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||||
public void Error(string formatString, params object[] args)
|
public void Error(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||||
public void Error(string formatString, Exception exception)
|
public void Error(string formatString, Exception exception)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Extensions;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Matchers;
|
namespace WireMock.Matchers;
|
||||||
@@ -9,8 +9,8 @@ namespace WireMock.Matchers;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// ExactMatcher
|
/// ExactMatcher
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IStringMatcher" />
|
/// <seealso cref="IStringMatcher" /> and <seealso cref="IIgnoreCaseMatcher" />
|
||||||
public class ExactMatcher : IStringMatcher
|
public class ExactMatcher : IStringMatcher, IIgnoreCaseMatcher
|
||||||
{
|
{
|
||||||
private readonly AnyOf<string, StringPattern>[] _values;
|
private readonly AnyOf<string, StringPattern>[] _values;
|
||||||
|
|
||||||
@@ -24,7 +24,16 @@ public class ExactMatcher : IStringMatcher
|
|||||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="values">The values.</param>
|
/// <param name="values">The values.</param>
|
||||||
public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, MatchOperator.Or, values)
|
public ExactMatcher(params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, false, false, MatchOperator.Or, values)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
|
||||||
|
/// <param name="values">The values.</param>
|
||||||
|
public ExactMatcher(bool ignoreCase, params AnyOf<string, StringPattern>[] values) : this(MatchBehaviour.AcceptOnMatch, ignoreCase, false, MatchOperator.Or, values)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,11 +41,13 @@ public class ExactMatcher : IStringMatcher
|
|||||||
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
/// Initializes a new instance of the <see cref="ExactMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||||
|
/// <param name="ignoreCase">Ignore the case from the pattern(s).</param>
|
||||||
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
/// <param name="throwException">Throw an exception when the internal matching fails because of invalid input.</param>
|
||||||
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
|
||||||
/// <param name="values">The values.</param>
|
/// <param name="values">The values.</param>
|
||||||
public ExactMatcher(
|
public ExactMatcher(
|
||||||
MatchBehaviour matchBehaviour,
|
MatchBehaviour matchBehaviour,
|
||||||
|
bool ignoreCase = false,
|
||||||
bool throwException = false,
|
bool throwException = false,
|
||||||
MatchOperator matchOperator = MatchOperator.Or,
|
MatchOperator matchOperator = MatchOperator.Or,
|
||||||
params AnyOf<string, StringPattern>[] values)
|
params AnyOf<string, StringPattern>[] values)
|
||||||
@@ -45,13 +56,18 @@ public class ExactMatcher : IStringMatcher
|
|||||||
|
|
||||||
MatchBehaviour = matchBehaviour;
|
MatchBehaviour = matchBehaviour;
|
||||||
ThrowException = throwException;
|
ThrowException = throwException;
|
||||||
|
IgnoreCase = ignoreCase;
|
||||||
MatchOperator = matchOperator;
|
MatchOperator = matchOperator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
|
||||||
public double IsMatch(string? input)
|
public double IsMatch(string? input)
|
||||||
{
|
{
|
||||||
double score = MatchScores.ToScore(_values.Select(v => v.GetPattern() == input).ToArray(), MatchOperator);
|
Func<string?, bool> equals = IgnoreCase
|
||||||
|
? pattern => string.Equals(pattern, input, StringComparison.OrdinalIgnoreCase)
|
||||||
|
: pattern => pattern == input;
|
||||||
|
|
||||||
|
double score = MatchScores.ToScore(_values.Select(v => equals(v)).ToArray(), MatchOperator);
|
||||||
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
return MatchBehaviourHelper.Convert(MatchBehaviour, score);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,4 +82,7 @@ public class ExactMatcher : IStringMatcher
|
|||||||
|
|
||||||
/// <inheritdoc cref="IMatcher.Name"/>
|
/// <inheritdoc cref="IMatcher.Name"/>
|
||||||
public string Name => "ExactMatcher";
|
public string Name => "ExactMatcher";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public bool IgnoreCase { get; }
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ public class JsonPartialMatcher : AbstractJsonPartialMatcher
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override bool IsMatch(string value, string input)
|
protected override bool IsMatch(string value, string input)
|
||||||
{
|
{
|
||||||
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, ThrowException, MatchOperator.Or, value);
|
var exactStringMatcher = new ExactMatcher(MatchBehaviour.AcceptOnMatch, IgnoreCase, ThrowException, MatchOperator.Or, value);
|
||||||
return MatchScores.IsPerfect(exactStringMatcher.IsMatch(input));
|
return MatchScores.IsPerfect(exactStringMatcher.IsMatch(input));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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; }
|
||||||
|
|
||||||
|
|||||||
@@ -18,5 +18,5 @@ public enum MatchOperator
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The average value from all patterns.
|
/// The average value from all patterns.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Average,
|
Average
|
||||||
}
|
}
|
||||||
@@ -16,22 +16,22 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body function
|
/// The body function
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<string, bool>? Func { get; }
|
public Func<string?, bool>? Func { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for byte[]
|
/// The body data function for byte[]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<byte[], bool>? DataFunc { get; }
|
public Func<byte[]?, bool>? DataFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for json
|
/// The body data function for json
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<object, bool>? JsonFunc { get; }
|
public Func<object?, bool>? JsonFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body data function for BodyData
|
/// The body data function for BodyData
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Func<IBodyData, bool>? BodyDataFunc { get; }
|
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The matchers.
|
/// The matchers.
|
||||||
@@ -77,7 +77,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<string, bool> func)
|
public RequestMessageBodyMatcher(Func<string?, bool> func)
|
||||||
{
|
{
|
||||||
Func = Guard.NotNull(func);
|
Func = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<byte[], bool> func)
|
public RequestMessageBodyMatcher(Func<byte[]?, bool> func)
|
||||||
{
|
{
|
||||||
DataFunc = Guard.NotNull(func);
|
DataFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
@@ -95,7 +95,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<object, bool> func)
|
public RequestMessageBodyMatcher(Func<object?, bool> func)
|
||||||
{
|
{
|
||||||
JsonFunc = Guard.NotNull(func);
|
JsonFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
@@ -158,9 +158,9 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
{
|
{
|
||||||
// If the body is a byte array, try to match.
|
// If the body is a byte array, try to match.
|
||||||
var detectedBodyType = requestMessage.BodyData?.DetectedBodyType;
|
var detectedBodyType = requestMessage.BodyData?.DetectedBodyType;
|
||||||
if (detectedBodyType == BodyType.Bytes || detectedBodyType == BodyType.String)
|
if (detectedBodyType is BodyType.Bytes or BodyType.String)
|
||||||
{
|
{
|
||||||
return exactObjectMatcher.IsMatch(requestMessage.BodyData.BodyAsBytes);
|
return exactObjectMatcher.IsMatch(requestMessage.BodyData?.BodyAsBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
|||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||||
/// <param name="values">The values.</param>
|
/// <param name="values">The values.</param>
|
||||||
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, string[]? values) : this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, false, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
|
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, string[]? values) : this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, false, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,43 @@
|
|||||||
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>
|
/// <inheritdoc cref="IBodyData.Encoding" />
|
||||||
/// BodyData
|
public Encoding? Encoding { get; set; }
|
||||||
/// </summary>
|
|
||||||
public class BodyData : IBodyData
|
|
||||||
{
|
|
||||||
/// <inheritdoc cref="IBodyData.Encoding" />
|
|
||||||
public Encoding? Encoding { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.BodyAsBytes" />
|
/// <inheritdoc />
|
||||||
public string? BodyAsString { get; set; }
|
public string? BodyAsString { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.BodyAsJson" />
|
/// <inheritdoc cref="IBodyData.BodyAsJson" />
|
||||||
public object? BodyAsJson { get; set; }
|
public object? BodyAsJson { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.BodyAsBytes" />
|
/// <inheritdoc cref="IBodyData.BodyAsBytes" />
|
||||||
public byte[]? BodyAsBytes { get; set; }
|
public byte[]? BodyAsBytes { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.BodyAsJsonIndented" />
|
/// <inheritdoc cref="IBodyData.BodyAsJsonIndented" />
|
||||||
public bool? BodyAsJsonIndented { get; set; }
|
public bool? BodyAsJsonIndented { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.BodyAsFile" />
|
/// <inheritdoc cref="IBodyData.BodyAsFile" />
|
||||||
public string? BodyAsFile { get; set; }
|
public string? BodyAsFile { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.BodyAsFileIsCached" />
|
/// <inheritdoc cref="IBodyData.BodyAsFileIsCached" />
|
||||||
public bool? BodyAsFileIsCached { get; set; }
|
public bool? BodyAsFileIsCached { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.DetectedBodyType" />
|
/// <inheritdoc cref="IBodyData.DetectedBodyType" />
|
||||||
public BodyType? DetectedBodyType { get; set; }
|
public BodyType? DetectedBodyType { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IBodyData.DetectedBodyTypeFromContentType" />
|
/// <inheritdoc cref="IBodyData.DetectedBodyTypeFromContentType" />
|
||||||
public BodyType? DetectedBodyTypeFromContentType { get; set; }
|
public BodyType? DetectedBodyTypeFromContentType { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.DetectedCompression" />
|
/// <inheritdoc cref="IRequestMessage.DetectedCompression" />
|
||||||
public string? DetectedCompression { get; set; }
|
public string? DetectedCompression { get; set; }
|
||||||
}
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string? IsFuncUsed { get; set; }
|
||||||
}
|
}
|
||||||
@@ -107,7 +107,7 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var appLifetime = (IApplicationLifetime)_host.Services.GetService(typeof(IApplicationLifetime));
|
var appLifetime = _host.Services.GetRequiredService<IApplicationLifetime>();
|
||||||
appLifetime.ApplicationStarted.Register(() =>
|
appLifetime.ApplicationStarted.Register(() =>
|
||||||
{
|
{
|
||||||
var addresses = _host.ServerFeatures
|
var addresses = _host.ServerFeatures
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -99,7 +99,14 @@ namespace WireMock.Owin.Mappers
|
|||||||
|
|
||||||
if (bytes != null)
|
if (bytes != null)
|
||||||
{
|
{
|
||||||
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
try
|
||||||
|
{
|
||||||
|
await response.Body.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_options.Logger.Warn("Error writing response body. Exception : {0}", ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,25 +1,25 @@
|
|||||||
namespace WireMock.Pact.Models.V2
|
#pragma warning disable CS1591
|
||||||
|
namespace WireMock.Pact.Models.V2;
|
||||||
|
|
||||||
|
public class MatchingRule
|
||||||
{
|
{
|
||||||
public class MatchingRule
|
/// <summary>
|
||||||
{
|
/// type or regex
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// type or regex
|
public string Match { get; set; } = "type";
|
||||||
/// </summary>
|
|
||||||
public string Match { get; set; } = "type";
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for Match = "type"
|
/// Used for Match = "type"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Min { get; set; }
|
public string Min { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for Match = "type"
|
/// Used for Match = "type"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Max { get; set; }
|
public string Max { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used for Match = "regex"
|
/// Used for Match = "regex"
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Regex { get; set; }
|
public string Regex { get; set; }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
namespace WireMock.Pact.Models.V2
|
#pragma warning disable CS1591
|
||||||
{
|
namespace WireMock.Pact.Models.V2;
|
||||||
public class Metadata
|
|
||||||
{
|
|
||||||
public string PactSpecificationVersion { get; set; }
|
|
||||||
|
|
||||||
public PactSpecification PactSpecification { get; set; } = new PactSpecification();
|
public class Metadata
|
||||||
}
|
{
|
||||||
|
public string PactSpecificationVersion { get; set; }
|
||||||
|
|
||||||
|
public PactSpecification PactSpecification { get; set; } = new PactSpecification();
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
|
#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>();
|
||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -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 string Name { get; set; }
|
|
||||||
|
|
||||||
public IDictionary<string, string> Params { get; set; }
|
public class ProviderState
|
||||||
}
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public IDictionary<string, string> Params { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using Stef.Validation;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Stef.Validation;
|
||||||
using WireMock.Http;
|
using WireMock.Http;
|
||||||
using WireMock.Serialization;
|
using WireMock.Serialization;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +66,7 @@ internal class MatcherMapper
|
|||||||
return new LinqMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
|
return new LinqMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
|
||||||
|
|
||||||
case nameof(ExactMatcher):
|
case nameof(ExactMatcher):
|
||||||
return new ExactMatcher(matchBehaviour, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
|
return new ExactMatcher(matchBehaviour, ignoreCase, throwExceptionWhenMatcherFails, matchOperator, stringPatterns);
|
||||||
|
|
||||||
case nameof(ExactObjectMatcher):
|
case nameof(ExactObjectMatcher):
|
||||||
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails);
|
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails);
|
||||||
|
|||||||
@@ -140,11 +140,11 @@ internal class ProxyMappingConverter
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString));
|
newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, true, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString!));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BodyType.Bytes:
|
case BodyType.Bytes:
|
||||||
newRequest.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
|
newRequest.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes!, throwExceptionWhenMatcherFails));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,171 +1,169 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.ResponseProviders;
|
using WireMock.ResponseProviders;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
|
||||||
namespace WireMock.Server
|
namespace WireMock.Server;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IRespondWithAProvider
|
||||||
|
/// </summary>
|
||||||
|
public interface IRespondWithAProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IRespondWithAProvider
|
/// Gets the unique identifier for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRespondWithAProvider
|
Guid Guid { get; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the unique identifier for this mapping.
|
|
||||||
/// </summary>
|
|
||||||
Guid Guid { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define a unique identifier for this mapping.
|
/// Define a unique identifier for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="guid">The unique identifier.</param>
|
/// <param name="guid">The unique identifier.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithGuid(Guid guid);
|
IRespondWithAProvider WithGuid(Guid guid);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define the TimeSettings for this mapping.
|
/// Define the TimeSettings for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="timeSettings">The TimeSettings.</param>
|
/// <param name="timeSettings">The TimeSettings.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithTimeSettings(ITimeSettings timeSettings);
|
IRespondWithAProvider WithTimeSettings(ITimeSettings timeSettings);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define a unique title for this mapping.
|
/// Define a unique title for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="title">The unique title.</param>
|
/// <param name="title">The unique title.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithTitle(string title);
|
IRespondWithAProvider WithTitle(string title);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define a description for this mapping.
|
/// Define a description for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="description">The description.</param>
|
/// <param name="description">The description.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithDescription(string description);
|
IRespondWithAProvider WithDescription(string description);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define the full filepath for this mapping.
|
/// Define the full filepath for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The full filepath.</param>
|
/// <param name="path">The full filepath.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithPath(string path);
|
IRespondWithAProvider WithPath(string path);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define a unique identifier for this mapping.
|
/// Define a unique identifier for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="guid">The unique identifier.</param>
|
/// <param name="guid">The unique identifier.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithGuid(string guid);
|
IRespondWithAProvider WithGuid(string guid);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Define the priority for this mapping.
|
/// Define the priority for this mapping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="priority">The priority.</param>
|
/// <param name="priority">The priority.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider AtPriority(int priority);
|
IRespondWithAProvider AtPriority(int priority);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The respond with.
|
/// The respond with.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="provider">The provider.</param>
|
/// <param name="provider">The provider.</param>
|
||||||
void RespondWith(IResponseProvider provider);
|
void RespondWith(IResponseProvider provider);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the the scenario.
|
/// Sets the the scenario.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scenario">The scenario.</param>
|
/// <param name="scenario">The scenario.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider InScenario(string scenario);
|
IRespondWithAProvider InScenario(string scenario);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the the scenario with an integer value.
|
/// Sets the the scenario with an integer value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="scenario">The scenario.</param>
|
/// <param name="scenario">The scenario.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider InScenario(int scenario);
|
IRespondWithAProvider InScenario(int scenario);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execute this respond only in case the current state is equal to specified one.
|
/// Execute this respond only in case the current state is equal to specified one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Any object which identifies the current state</param>
|
/// <param name="state">Any object which identifies the current state</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WhenStateIs(string state);
|
IRespondWithAProvider WhenStateIs(string state);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Execute this respond only in case the current state is equal to specified one.
|
/// Execute this respond only in case the current state is equal to specified one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Any object which identifies the current state</param>
|
/// <param name="state">Any object which identifies the current state</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WhenStateIs(int state);
|
IRespondWithAProvider WhenStateIs(int state);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Once this mapping is executed the state will be changed to specified one.
|
/// Once this mapping is executed the state will be changed to specified one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Any object which identifies the new state</param>
|
/// <param name="state">Any object which identifies the new state</param>
|
||||||
/// <param name="times">The number of times this match should be matched before the state will be changed to the specified one. Default value is 1.</param>
|
/// <param name="times">The number of times this match should be matched before the state will be changed to the specified one. Default value is 1.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WillSetStateTo(string state, int? times = 1);
|
IRespondWithAProvider WillSetStateTo(string state, int? times = 1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Once this mapping is executed the state will be changed to specified one.
|
/// Once this mapping is executed the state will be changed to specified one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">Any object which identifies the new state</param>
|
/// <param name="state">Any object which identifies the new state</param>
|
||||||
/// <param name="times">The number of times this match should be matched before the state will be changed to the specified one. Default value is 1.</param>
|
/// <param name="times">The number of times this match should be matched before the state will be changed to the specified one. Default value is 1.</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WillSetStateTo(int state, int? times = 1);
|
IRespondWithAProvider WillSetStateTo(int state, int? times = 1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add (multiple) Webhook(s) to call after the response has been generated.
|
/// Add (multiple) Webhook(s) to call after the response has been generated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="webhooks">The Webhooks</param>
|
/// <param name="webhooks">The Webhooks</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithWebhook(params IWebhook[] webhooks);
|
IRespondWithAProvider WithWebhook(params IWebhook[] webhooks);
|
||||||
|
|
||||||
/// <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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="url">The Webhook Url</param>
|
/// <param name="url">The Webhook Url</param>
|
||||||
/// <param name="method">The method to use. [optional]</param>
|
/// <param name="method">The method to use. [optional]</param>
|
||||||
/// <param name="headers">The Headers to send. [optional]</param>
|
/// <param name="headers">The Headers to send. [optional]</param>
|
||||||
/// <param name="body">The body (as string) to send. [optional]</param>
|
/// <param name="body">The body (as string) to send. [optional]</param>
|
||||||
/// <param name="useTransformer">Use Transformer. [optional]</param>
|
/// <param name="useTransformer">Use Transformer. [optional]</param>
|
||||||
/// <param name="transformerType">The transformer type. [optional]</param>
|
/// <param name="transformerType">The transformer type. [optional]</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithWebhook(
|
IRespondWithAProvider WithWebhook(
|
||||||
[NotNull] string url,
|
string url,
|
||||||
[CanBeNull] string method = "post",
|
string method = "post",
|
||||||
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
|
IDictionary<string, WireMockList<string>>? headers = null,
|
||||||
[CanBeNull] string body = null,
|
string? body = null,
|
||||||
bool useTransformer = true,
|
bool useTransformer = true,
|
||||||
TransformerType transformerType = TransformerType.Handlebars
|
TransformerType transformerType = TransformerType.Handlebars
|
||||||
);
|
);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a Webhook to call after the response has been generated.
|
/// Add a Webhook to call after the response has been generated.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="url">The Webhook Url</param>
|
/// <param name="url">The Webhook Url</param>
|
||||||
/// <param name="method">The method to use. [optional]</param>
|
/// <param name="method">The method to use. [optional]</param>
|
||||||
/// <param name="headers">The Headers to send. [optional]</param>
|
/// <param name="headers">The Headers to send. [optional]</param>
|
||||||
/// <param name="body">The body (as json) to send. [optional]</param>
|
/// <param name="body">The body (as json) to send. [optional]</param>
|
||||||
/// <param name="useTransformer">Use Transformer. [optional]</param>
|
/// <param name="useTransformer">Use Transformer. [optional]</param>
|
||||||
/// <param name="transformerType">The transformer type. [optional]</param>
|
/// <param name="transformerType">The transformer type. [optional]</param>
|
||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithWebhook(
|
IRespondWithAProvider WithWebhook(
|
||||||
[NotNull] string url,
|
string url,
|
||||||
[CanBeNull] string method = "post",
|
string method = "post",
|
||||||
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
|
IDictionary<string, WireMockList<string>>? headers = null,
|
||||||
[CanBeNull] object body = null,
|
object? body = null,
|
||||||
bool useTransformer = true,
|
bool useTransformer = true,
|
||||||
TransformerType transformerType = TransformerType.Handlebars
|
TransformerType transformerType = TransformerType.Handlebars
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
68
src/WireMock.Net/Server/WireMockServer.Proxy.cs
Normal file
68
src/WireMock.Net/Server/WireMockServer.Proxy.cs
Normal file
@@ -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
|
||||||
|
|
||||||
if (logger != null)
|
var loggerType = parser.GetStringValue("WireMockLogger");
|
||||||
|
switch (loggerType)
|
||||||
{
|
{
|
||||||
settings.Logger = logger;
|
case nameof(WireMockConsoleLogger):
|
||||||
}
|
settings.Logger = new WireMockConsoleLogger();
|
||||||
else if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
|
break;
|
||||||
{
|
|
||||||
settings.Logger = new WireMockConsoleLogger();
|
case nameof(WireMockNullLogger):
|
||||||
|
settings.Logger = new WireMockNullLogger();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (logger != null)
|
||||||
|
{
|
||||||
|
settings.Logger = logger;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
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!
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,5 +8,9 @@ interface ITransformer
|
|||||||
{
|
{
|
||||||
ResponseMessage Transform(IMapping mapping, IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options);
|
ResponseMessage Transform(IMapping mapping, IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options);
|
||||||
|
|
||||||
(IBodyData? BodyData, IDictionary<string, WireMockList<string>>? Headers) Transform(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IBodyData? bodyData, IDictionary<string, WireMockList<string>>? headers, 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);
|
||||||
|
|
||||||
|
string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value);
|
||||||
}
|
}
|
||||||
13
src/WireMock.Net/Transformers/TransformModel.cs
Normal file
13
src/WireMock.Net/Transformers/TransformModel.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace WireMock.Transformers;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
|
internal struct TransformModel
|
||||||
|
{
|
||||||
|
public IMapping mapping { get; set; }
|
||||||
|
|
||||||
|
public IRequestMessage request { get; set; }
|
||||||
|
|
||||||
|
public IResponseMessage? response { get; set; }
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user