Compare commits

..

40 Commits

Author SHA1 Message Date
Stef Heyenrath
ed74871a26 1.4.21 2021-09-16 13:06:56 +00:00
Stef Heyenrath
cb66c04199 Add WireMock.org RestClient (#631)
* wip...

* x

* .

* .

* .

* r

* 1.4.21-preview-02

* 1.4.21-preview-03

* .

* usings

* wip

* .

* ut

* .

* .

* .

* tests

* .

* comments

* readme
2021-09-16 14:35:08 +02:00
Stef Heyenrath
fd28ebdffa update 'Solution Items' folder 2021-08-14 13:47:18 +02:00
Stef Heyenrath
ce36daa326 Create CreateRelease.yml 2021-08-14 13:45:54 +02:00
Stef Heyenrath
b5a5f5e464 1.4.20 2021-08-06 10:20:28 +00:00
Stef Heyenrath
f5d624eeed Fix issue with FluentBuilder (#628) 2021-08-06 12:17:05 +02:00
Stef Heyenrath
799ea2d219 1.4.19 2021-08-04 14:38:41 +00:00
Stef Heyenrath
0f99e06acc Add NotNullOrEmptyMatcher (#625) 2021-08-04 16:22:22 +02:00
Stef Heyenrath
9d0682bff6 Add FluentBuilder for client models (#622) 2021-08-04 15:40:35 +02:00
Stef Heyenrath
cb1d2a5294 1.4.18 2021-07-10 11:48:54 +02:00
Stef Heyenrath
4b435faf0b Update Handlebars.Net.Helpers.XPath to fix issue with 'xml version' (#619)
Update Handlebars.Net.Helpers.XPath to fix issue with 'xml version'
2021-07-10 11:43:59 +02:00
Stef Heyenrath
5e7cb44525 Update FUNDING.yml 2021-07-07 20:07:40 +02:00
Stef Heyenrath
444298c28c 1.4.17 2021-07-07 18:31:07 +02:00
Stef Heyenrath
6f42aa99bc Handlebars.Net.Helpers.Humanizer (#617) 2021-07-07 17:02:15 +02:00
Stef Heyenrath
8a9ea1b843 Update FUNDING.yml 2021-07-06 12:13:13 +02:00
Stef Heyenrath
dade24de37 Update FUNDING.yml 2021-07-06 12:12:48 +02:00
Stef Heyenrath
6beaa631f4 Update FUNDING.yml 2021-07-03 09:44:33 +02:00
Stef Heyenrath
282281aa7f 1.4.16 2021-06-05 11:00:51 +02:00
Stef Heyenrath
4052a0ef3b Upgrade Handlebars.Net.Helpers to 2.19 (#616)
* Upgrade Handlebars.Net.Helpers to 2.19

* fix ut
2021-06-05 10:59:09 +02:00
Stef Heyenrath
2ca394b7f6 1.4.15 2021-05-19 12:01:19 +00:00
Stef Heyenrath
b04000bfdd Add support for multiple webhooks (#615) 2021-05-19 13:58:48 +02:00
Stef Heyenrath
93ab4e1853 1.4.14 2021-05-11 05:45:07 +00:00
starkpl
80b5eaac6e Add AdditionalServiceRegistration action for custom ASP.NET Core DI setup (#611) 2021-05-11 07:37:20 +02:00
Stef Heyenrath
a1dc2ba646 condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests (#612) 2021-05-10 22:06:42 +02:00
Stef Heyenrath
934c444902 Fix some SonarCloud issues in UnitTests (#610) 2021-05-07 18:23:23 +02:00
Stef Heyenrath
83d178bdb5 1.4.13 2021-04-26 14:53:15 +00:00
Stef Heyenrath
d91b5d5831 Add possibility to use settings to generate MappingModel models with wildcard path parameters. (#609)
* Add optional settings for WithMappingFromOpenApi

* .

* .

* cleanup
2021-04-26 14:44:45 +02:00
Stef Heyenrath
43b96ce340 Add OpenApiParser NuGet to readme 2021-04-23 17:51:45 +02:00
dependabot[bot]
4d8cf43357 Bump System.Text.Encodings.Web (#607)
Bumps [System.Text.Encodings.Web](https://github.com/dotnet/corefx) from 4.5.0 to 4.5.1.
- [Release notes](https://github.com/dotnet/corefx/releases)
- [Commits](https://github.com/dotnet/corefx/commits)

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 18:11:30 +02:00
Stef Heyenrath
328e9090b1 1.4.12 2021-04-22 06:46:24 +00:00
dependabot[bot]
a22b3bfbc5 Bump System.Net.Http in /examples/WireMock.Net.Service (#606)
Bumps System.Net.Http from 4.3.3 to 4.3.4.

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 07:49:32 +02:00
dependabot[bot]
29974c7ad4 Bump System.Net.Http from 4.3.3 to 4.3.4 in /src/WireMock.Net (#605)
Bumps System.Net.Http from 4.3.3 to 4.3.4.

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

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 07:48:39 +02:00
Stef Heyenrath
660a09e656 1.4.11 2021-04-18 21:32:42 +02:00
Stef Heyenrath
fbecd3b119 Fix match logic (#604) 2021-04-18 21:30:03 +02:00
Stef Heyenrath
b17840cea9 1.4.10 2021-04-15 19:46:41 +02:00
Stef Heyenrath
969b0da8e2 Fix callback with Headers (#603) 2021-04-15 19:41:45 +02:00
Stef Heyenrath
dc078b57ea 1.4.9 2021-03-31 18:23:49 +02:00
Stef Heyenrath
8140b37095 WithProxy() should save the new mapping (#600)
* WithProxy should save the new mapping

* fix ut

* .
2021-03-31 18:17:27 +02:00
Stef Heyenrath
2ad060bbd4 1.4.8 2021-03-24 17:20:31 +00:00
Stef Heyenrath
d758301e4f Webhook (#591)
Webhook
2021-03-24 18:15:31 +01:00
166 changed files with 13148 additions and 4061 deletions

6
.editorconfig Normal file
View File

@@ -0,0 +1,6 @@
[*]
indent_style = space
indent_size = 4
end_of_line = crlf
charset = utf-8
trim_trailing_whitespace = true

4
.github/FUNDING.yml vendored
View File

@@ -1,8 +1,8 @@
# These are supported funding model platforms # These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] github: [StefH]
patreon: # Replace with a single Patreon username patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username open_collective: # wiremocknet
ko_fi: # Replace with a single Ko-fi username ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry

15
.github/workflows/CreateRelease.yml vendored Normal file
View File

@@ -0,0 +1,15 @@
name: CreateRelease
on:
push:
tags:
- "*.*.*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Release
uses: softprops/action-gh-release@v1

File diff suppressed because it is too large Load Diff

View File

@@ -1,30 +1,36 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<MsBuildAllProjects>$(MsBuildAllProjects);$(MsBuildThisFileFullPath)</MsBuildAllProjects> <MsBuildAllProjects>$(MsBuildAllProjects);$(MsBuildThisFileFullPath)</MsBuildAllProjects>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<VersionPrefix>1.4.7</VersionPrefix> <VersionPrefix>1.4.21</VersionPrefix>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes> <PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl> <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>
<RepositoryType>git</RepositoryType> <PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/../../PackageReleaseNotes.txt"))</PackageReleaseNotes>
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl> <RepositoryType>git</RepositoryType>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
</PropertyGroup> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(TF_BUILD)' == 'true'"> <PropertyGroup Condition="'$(TF_BUILD)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild> <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup> </PropertyGroup>
<Choose> <ItemGroup>
<!-- The environment variable `Prerelease` is set in the azure-pipelines.yml file. --> <None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
<When Condition=" '$(Prerelease)' != '' "> <!--<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>-->
<PropertyGroup> </ItemGroup>
<!-- Set the version to x.x.x.x-{Prerelease}-1{Build_BuildId} (this is same buildId as defined in the azure-pipelines.yml file). -->
<VersionSuffix>$(Prerelease)-1$(BUILD_BUILDID)</VersionSuffix> <Choose>
</PropertyGroup> <!-- The environment variable `Prerelease` is set in the azure-pipelines.yml file. -->
</When> <When Condition=" '$(Prerelease)' != '' ">
</Choose> <PropertyGroup>
<!-- Set the version to x.x.x.x-{Prerelease}-1{Build_BuildId} (this is same buildId as defined in the azure-pipelines.yml file). -->
<VersionSuffix>$(Prerelease)-1$(BUILD_BUILDID)</VersionSuffix>
</PropertyGroup>
</When>
</Choose>
</Project> </Project>

View File

@@ -0,0 +1,7 @@
rem https://github.com/StefH/GitHubReleaseNotes
SET version=1.4.21
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version%
GitHubReleaseNotes --output PackageReleaseNotes.txt --skip-empty-releases --exclude-labels question invalid doc duplicate --template PackageReleaseNotes.template --version %version%

View File

@@ -1,3 +0,0 @@
https://github.com/StefH/GitHubReleaseNotes
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc --version 1.4.7

View File

@@ -0,0 +1,6 @@
# {{releaseInfos.0.FriendlyName}} ({{formatDate releaseInfos.0.When "dd MMMM yyyy"}})
{{#each releaseInfos.0.issueInfos}}
- #{{Number}} {{Title}}{{#if Labels}} [{{join Labels ", "}}]{{/if}}
{{/each}}
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

4
PackageReleaseNotes.txt Normal file
View File

@@ -0,0 +1,4 @@
# 1.4.21 (16 September 2021)
- #631 Add WireMock.org RestClient [feature]
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

View File

@@ -34,8 +34,11 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
| &nbsp;&nbsp;**WireMock.Net** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | [![MyGet Badge WireMock.Net](https://buildstats.info/myget/wiremock-net/WireMock.Net?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net) | &nbsp;&nbsp;**WireMock.Net** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net)](https://www.nuget.org/packages/WireMock.Net) | [![MyGet Badge WireMock.Net](https://buildstats.info/myget/wiremock-net/WireMock.Net?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net)
| &nbsp;&nbsp;**WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) | [![MyGet Badge WireMock.Net.StandAlone](https://buildstats.info/myget/wiremock-net/WireMock.Net.StandAlone?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.StandAlone) | &nbsp;&nbsp;**WireMock.Net.StandAlone** | [![NuGet Badge WireMock.Net](https://buildstats.info/nuget/WireMock.Net.StandAlone)](https://www.nuget.org/packages/WireMock.Net.StandAlone) | [![MyGet Badge WireMock.Net.StandAlone](https://buildstats.info/myget/wiremock-net/WireMock.Net.StandAlone?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.StandAlone)
| &nbsp;&nbsp;**WireMock.Net.FluentAssertions** | [![NuGet Badge WireMock.Net.FluentAssertions](https://buildstats.info/nuget/WireMock.Net.FluentAssertions)](https://www.nuget.org/packages/WireMock.Net.FluentAssertions) | [![MyGet Badge WireMock.Net.FluentAssertions](https://buildstats.info/myget/wiremock-net/WireMock.Net.FluentAssertions?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.FluentAssertions) | &nbsp;&nbsp;**WireMock.Net.FluentAssertions** | [![NuGet Badge WireMock.Net.FluentAssertions](https://buildstats.info/nuget/WireMock.Net.FluentAssertions)](https://www.nuget.org/packages/WireMock.Net.FluentAssertions) | [![MyGet Badge WireMock.Net.FluentAssertions](https://buildstats.info/myget/wiremock-net/WireMock.Net.FluentAssertions?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.FluentAssertions)
| &nbsp;&nbsp;**WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://buildstats.info/nuget/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://buildstats.info/myget/wiremock-net/WireMock.Net.RestClient?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
| &nbsp;&nbsp;**WireMock.Net.Matchers.CSharpCode** | [![NuGet Badge WireMock.Net.Matchers.CSharpCode](https://buildstats.info/nuget/WireMock.Net.Matchers.CSharpCode)](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [![MyGet Badge WireMock.Net.Matchers.CSharpCode](https://buildstats.info/myget/wiremock-net/WireMock.Net.Matchers.CSharpCode?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode) | &nbsp;&nbsp;**WireMock.Net.Matchers.CSharpCode** | [![NuGet Badge WireMock.Net.Matchers.CSharpCode](https://buildstats.info/nuget/WireMock.Net.Matchers.CSharpCode)](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [![MyGet Badge WireMock.Net.Matchers.CSharpCode](https://buildstats.info/myget/wiremock-net/WireMock.Net.Matchers.CSharpCode?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
| &nbsp;&nbsp;**WireMock.Net.OpenApiParser** | [![NuGet Badge WireMock.Net.OpenApiParser](https://buildstats.info/nuget/WireMock.Net.OpenApiParser)](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [![MyGet Badge WireMock.Net.OpenApiParser](https://buildstats.info/myget/wiremock-net/WireMock.Net.OpenApiParser?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
| &nbsp;&nbsp;**WireMock.Net.RestClient** | [![NuGet Badge WireMock.Net.RestClient](https://buildstats.info/nuget/WireMock.Net.RestClient)](https://www.nuget.org/packages/WireMock.Net.RestClient) | [![MyGet Badge WireMock.Net.RestClient](https://buildstats.info/myget/wiremock-net/WireMock.Net.RestClient?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
| &nbsp;&nbsp;**WireMock.Org.RestClient** | [![NuGet Badge WireMock.Org.RestClient](https://buildstats.info/nuget/WireMock.Org.RestClient)](https://www.nuget.org/packages/WireMock.Org.RestClient) | [![MyGet Badge WireMock.Org.RestClient](https://buildstats.info/myget/wiremock-net/WireMock.Org.RestClient?includePreReleases=true)](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
## Development ## Development
For the supported frameworks and build information, see [this](https://github.com/WireMock-Net/WireMock.Net/wiki/Development-Information) page. For the supported frameworks and build information, see [this](https://github.com/WireMock-Net/WireMock.Net/wiki/Development-Information) page.

View File

@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16 # Visual Studio Version 17
VisualStudioVersion = 16.0.30114.105 VisualStudioVersion = 17.0.31521.260
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}"
EndProject EndProject
@@ -21,16 +21,15 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{98
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
.gitignore = .gitignore .gitignore = .gitignore
azure-pipelines-ci-linux.yml = azure-pipelines-ci-linux.yml
azure-pipelines-ci.yml = azure-pipelines-ci.yml
azure-pipelines-linux.yml = azure-pipelines-linux.yml
azure-pipelines-nuget.yml = azure-pipelines-nuget.yml
build-info.md = build-info.md build-info.md = build-info.md
CHANGELOG.md = CHANGELOG.md CHANGELOG.md = CHANGELOG.md
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
GitHubReleaseNotes.txt = GitHubReleaseNotes.txt Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
nuget.config = nuget.config nuget.config = nuget.config
PackageReleaseNotes.template = PackageReleaseNotes.template
PackageReleaseNotes.txt = PackageReleaseNotes.txt
README.md = README.md README.md = README.md
EndProjectSection EndProjectSection
EndProject EndProject
@@ -82,7 +81,22 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Matchers.CShar
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net472.Classic", "examples\WireMock.Net.Console.Net472.Classic\WireMock.Net.Console.Net472.Classic.csproj", "{6580580B-1EFD-4922-B0EC-FF290DB279EE}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Console.Net472.Classic", "examples\WireMock.Net.Console.Net472.Classic\WireMock.Net.Console.Net472.Classic.csproj", "{6580580B-1EFD-4922-B0EC-FF290DB279EE}"
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.XamarinUI.Tests", "test\WireMock.Net.XamarinUI.Tests\WireMock.Net.XamarinUI.Tests.csproj", "{0DE14F1B-A51E-4B59-A87C-C6012DCD2844}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Actions", "Actions", "{1DAEFF47-D117-4E95-8B3E-4F7C8B92011A}"
ProjectSection(SolutionItems) = preProject
.github\workflows\CreateRelease.yml = .github\workflows\CreateRelease.yml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure Pipelines", "Azure Pipelines", "{5B64F6CA-BF6B-4F67-BB2A-9C47E441703E}"
ProjectSection(SolutionItems) = preProject
azure-pipelines-ci-linux.yml = azure-pipelines-ci-linux.yml
azure-pipelines-ci.yml = azure-pipelines-ci.yml
azure-pipelines-linux.yml = azure-pipelines-linux.yml
azure-pipelines-nuget.yml = azure-pipelines-nuget.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Org.RestClient", "src\WireMock.Org.RestClient\WireMock.Org.RestClient.csproj", "{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Org.Abstractions", "src\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj", "{3BA5109E-5F30-4CC2-B699-02EC82560AA6}"
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -206,14 +220,14 @@ Global
{6580580B-1EFD-4922-B0EC-FF290DB279EE}.Debug|Any CPU.Build.0 = Debug|Any CPU {6580580B-1EFD-4922-B0EC-FF290DB279EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6580580B-1EFD-4922-B0EC-FF290DB279EE}.Release|Any CPU.ActiveCfg = Release|Any CPU {6580580B-1EFD-4922-B0EC-FF290DB279EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6580580B-1EFD-4922-B0EC-FF290DB279EE}.Release|Any CPU.Build.0 = Release|Any CPU {6580580B-1EFD-4922-B0EC-FF290DB279EE}.Release|Any CPU.Build.0 = Release|Any CPU
{0DE14F1B-A51E-4B59-A87C-C6012DCD2844}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0DE14F1B-A51E-4B59-A87C-C6012DCD2844}.Debug|Any CPU.Build.0 = Debug|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0DE14F1B-A51E-4B59-A87C-C6012DCD2844}.Release|Any CPU.ActiveCfg = Release|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0DE14F1B-A51E-4B59-A87C-C6012DCD2844}.Release|Any CPU.Build.0 = Release|Any CPU {08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|Any CPU.Build.0 = Release|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A44}.Debug|Any CPU.Build.0 = Debug|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|Any CPU.ActiveCfg = Release|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B6269AAC-170A-4346-8B9A-444DED3D9A44}.Release|Any CPU.Build.0 = Release|Any CPU {3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -247,9 +261,11 @@ Global
{40BF24B5-12E6-4610-9489-138798632E28} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {40BF24B5-12E6-4610-9489-138798632E28} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{3F8CF0AE-5F24-4A54-89E7-A3EE829DB5F8} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {3F8CF0AE-5F24-4A54-89E7-A3EE829DB5F8} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{B6269AAC-170A-4346-8B9A-444DED3D9A44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2} {B6269AAC-170A-4346-8B9A-444DED3D9A44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{B6269AAC-170A-4346-8B9A-444DED3D9A44} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{6580580B-1EFD-4922-B0EC-FF290DB279EE} = {985E0ADB-D4B4-473A-AA40-567E279B7946} {6580580B-1EFD-4922-B0EC-FF290DB279EE} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{0DE14F1B-A51E-4B59-A87C-C6012DCD2844} = {0BB8B634-407A-4610-A91F-11586990767A} {1DAEFF47-D117-4E95-8B3E-4F7C8B92011A} = {7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}
{5B64F6CA-BF6B-4F67-BB2A-9C47E441703E} = {7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{3BA5109E-5F30-4CC2-B699-02EC82560AA6} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458} SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -13,6 +13,9 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Raml/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary> </wpf:ResourceDictionary>

View File

@@ -28,6 +28,7 @@ jobs:
- task: SonarCloudPrepare@1 - task: SonarCloudPrepare@1
displayName: 'Prepare analysis on SonarCloud' displayName: 'Prepare analysis on SonarCloud'
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
inputs: inputs:
SonarCloud: SonarCloud SonarCloud: SonarCloud
organization: wiremock-net organization: wiremock-net
@@ -52,9 +53,11 @@ jobs:
- task: SonarCloudAnalyze@1 - task: SonarCloudAnalyze@1
displayName: 'SonarCloud: Run Code Analysis' displayName: 'SonarCloud: Run Code Analysis'
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
- task: SonarCloudPublish@1 - task: SonarCloudPublish@1
displayName: 'SonarCloud: Publish Quality Gate Result' displayName: 'SonarCloud: Publish Quality Gate Result'
condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
- task: whitesource.ws-bolt.bolt.wss.WhiteSource Bolt@19 - task: whitesource.ws-bolt.bolt.wss.WhiteSource Bolt@19
displayName: 'WhiteSource Bolt' displayName: 'WhiteSource Bolt'

View File

@@ -22,6 +22,11 @@ namespace WireMock.Net.Client
var settings1 = await api.GetSettingsAsync(); var settings1 = await api.GetSettingsAsync();
Console.WriteLine($"settings1 = {JsonConvert.SerializeObject(settings1)}"); Console.WriteLine($"settings1 = {JsonConvert.SerializeObject(settings1)}");
var settingsViaBuilder = new FluentBuilder.SettingsModelBuilder()
.WithGlobalProcessingDelay(1077)
.WithoutGlobalProcessingDelay()
.Build();
settings1.GlobalProcessingDelay = 1077; settings1.GlobalProcessingDelay = 1077;
api.PostSettingsAsync(settings1).Wait(); api.PostSettingsAsync(settings1).Wait();

View File

@@ -3,12 +3,12 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="RestEase" Version="1.4.10" /> <PackageReference Include="RestEase" Version="1.5.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp1.1</TargetFramework> <TargetFramework>netcoreapp1.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework> <TargetFramework>netcoreapp2.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -59,6 +59,7 @@ namespace WireMock.Net.ConsoleApplication
//}, //},
PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); }, PreWireMockMiddlewareInit = app => { System.Console.WriteLine($"PreWireMockMiddlewareInit : {app.GetType()}"); },
PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); }, PostWireMockMiddlewareInit = app => { System.Console.WriteLine($"PostWireMockMiddlewareInit : {app.GetType()}"); },
AdditionalServiceRegistration = services => { System.Console.WriteLine($"AdditionalServiceRegistration : {services.GetType()}"); },
Logger = new WireMockConsoleLogger(), Logger = new WireMockConsoleLogger(),
HandlebarsRegistrationCallback = (handlebarsContext, fileSystemHandler) => HandlebarsRegistrationCallback = (handlebarsContext, fileSystemHandler) =>

View File

@@ -33,7 +33,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>..\..\resources\WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Handlebars, Version=2.0.4.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL"> <Reference Include="Handlebars, Version=2.0.4.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks> <TargetFrameworks>netcoreapp2.1;netcoreapp3.1;net5.0</TargetFrameworks>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,9 +3,11 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Logging; using WireMock.Logging;
using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
using WireMock.Server; using WireMock.Server;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Net.OpenApiParser.Extensions;
namespace WireMock.Net.OpenApiParser.ConsoleApp namespace WireMock.Net.OpenApiParser.ConsoleApp
{ {
@@ -23,13 +25,18 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
ReadStaticMappings = false, ReadStaticMappings = false,
WatchStaticMappings = false, WatchStaticMappings = false,
WatchStaticMappingsInSubdirectories = false, WatchStaticMappingsInSubdirectories = false,
Logger = new WireMockConsoleLogger(), Logger = new WireMockConsoleLogger()
}); });
Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls)); Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
server.SetBasicAuthentication("a", "b"); server.SetBasicAuthentication("a", "b");
server.WithMappingFromOpenApiFile(path, out var diag); var settings = new WireMockOpenApiParserSettings
{
PathPatternToUse = ExampleValueType.Wildcard
};
server.WithMappingFromOpenApiFile(path, settings, out var diag);
Console.WriteLine("Press any key to stop the server"); Console.WriteLine("Press any key to stop the server");
System.Console.ReadKey(); System.Console.ReadKey();

View File

@@ -32,11 +32,8 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Handlebars, Version=1.9.5.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL"> <Reference Include="log4net, Version=2.0.12.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\Handlebars.Net.1.9.5\lib\net452\Handlebars.dll</HintPath> <HintPath>..\..\packages\log4net.2.0.12\lib\net45\log4net.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.Owin, Version=2.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll</HintPath> <HintPath>..\..\packages\Microsoft.Owin.2.0.2\lib\net45\Microsoft.Owin.dll</HintPath>
@@ -57,6 +54,7 @@
<HintPath>..\..\packages\SimMetrics.Net.1.0.5\lib\net45\SimMetrics.Net.dll</HintPath> <HintPath>..\..\packages\SimMetrics.Net.1.0.5\lib\net45\SimMetrics.Net.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Configuration.Install" /> <Reference Include="System.Configuration.Install" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Management" /> <Reference Include="System.Management" />

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Handlebars.Net" version="1.9.5" targetFramework="net452" /> <package id="log4net" version="2.0.12" targetFramework="net452" />
<package id="log4net" version="2.0.10" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" /> <package id="Microsoft.AspNet.WebApi.Client" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" /> <package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" /> <package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
@@ -12,6 +11,6 @@
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" /> <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />
<package id="Owin" version="1.0" targetFramework="net452" /> <package id="Owin" version="1.0" targetFramework="net452" />
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net452" /> <package id="SimMetrics.Net" version="1.0.5" targetFramework="net452" />
<package id="System.Net.Http" version="4.3.3" targetFramework="net452" /> <package id="System.Net.Http" version="4.3.4" targetFramework="net452" />
<package id="XPath2" version="1.1.0" targetFramework="net452" /> <package id="XPath2" version="1.1.0" targetFramework="net452" />
</packages> </packages>

View File

@@ -1,40 +1,40 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using log4net; using log4net;
using log4net.Config; using log4net.Config;
using log4net.Repository; using log4net.Repository;
using WireMock.RequestBuilders; using WireMock.RequestBuilders;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Server; using WireMock.Server;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Util; using WireMock.Util;
namespace WireMock.Net.StandAlone.NETCoreApp namespace WireMock.Net.StandAlone.NETCoreApp
{ {
static class Program static class Program
{ {
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
// private static readonly ILog Log = LogManager.GetLogger(typeof(Program)); // private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
private static int sleepTime = 30000; private static int sleepTime = 30000;
private static WireMockServer _server; private static WireMockServer _server;
static void Main(string[] args) static void Main(string[] args)
{ {
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config")); XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger())) if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger()))
{ {
return; return;
} }
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'"))); settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
_server = WireMockServer.Start(settings); _server = WireMockServer.Start(settings);
_server.Given(Request.Create().WithPath("/api/sap") _server.Given(Request.Create().WithPath("/api/sap")
.UsingPost() .UsingPost()
.WithBody((IBodyData xmlData) => { .WithBody((IBodyData xmlData) => {
@@ -43,37 +43,37 @@ namespace WireMock.Net.StandAlone.NETCoreApp
})) }))
.RespondWith(Response.Create().WithStatusCode(System.Net.HttpStatusCode.OK)); .RespondWith(Response.Create().WithStatusCode(System.Net.HttpStatusCode.OK));
_server _server
.Given(Request.Create() .Given(Request.Create()
.UsingAnyMethod()) .UsingAnyMethod())
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithTransformer() .WithTransformer()
.WithBody("{{Random Type=\"Integer\" Min=100 Max=999999}} {{DateTime.Now}} {{DateTime.Now \"yyyy-MMM\"}} {{String.Format (DateTime.Now) \"MMM-dd\"}}")); .WithBody("{{Random Type=\"Integer\" Min=100 Max=999999}} {{DateTime.Now}} {{DateTime.Now \"yyyy-MMM\"}} {{String.Format (DateTime.Now) \"MMM-dd\"}}"));
Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down"); Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
Console.CancelKeyPress += (s, e) => Console.CancelKeyPress += (s, e) =>
{ {
Stop("CancelKeyPress"); Stop("CancelKeyPress");
}; };
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx => System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
{ {
Stop("AssemblyLoadContext.Default.Unloading"); Stop("AssemblyLoadContext.Default.Unloading");
}; };
while (true) while (true)
{ {
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running : {_server.IsStarted}"); Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running : {_server.IsStarted}");
Thread.Sleep(sleepTime); Thread.Sleep(sleepTime);
} }
} }
private static void Stop(string why) private static void Stop(string why)
{ {
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'"); Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
_server.Stop(); _server.Stop();
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped"); Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
} }
} }
} }

View File

@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"WireMock.Net.StandAlone.NETCoreApp": { "WireMock.Net.StandAlone.NETCoreApp": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "--Urls https://localhost:10080 --WireMockLogger WireMockConsoleLogger" "commandLineArgs": "--Urls http://localhost:9091 --WireMockLogger WireMockConsoleLogger"
} }
} }
} }

View File

@@ -1,9 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp3.1</TargetFrameworks> <TargetFrameworks>netcoreapp3.1</TargetFrameworks>
<ApplicationIcon>../../WireMock.Net-Logo.ico</ApplicationIcon>
<StartupObject>WireMock.Net.StandAlone.NETCoreApp.Program</StartupObject> <StartupObject>WireMock.Net.StandAlone.NETCoreApp.Program</StartupObject>
</PropertyGroup> </PropertyGroup>

View File

@@ -33,14 +33,14 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>..\..\WireMock.Net-Logo.ico</ApplicationIcon> <ApplicationIcon>..\..\resources\WireMock.Net-Logo.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>WireMock.Net.StandAlone.Net452.Program</StartupObject> <StartupObject>WireMock.Net.StandAlone.Net452.Program</StartupObject>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL"> <Reference Include="log4net, Version=2.0.12.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath> <HintPath>..\..\packages\log4net.2.0.12\lib\net45\log4net.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.Owin.Host.HttpListener, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="Microsoft.Owin.Host.HttpListener, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.4.0.0\lib\net451\Microsoft.Owin.Host.HttpListener.dll</HintPath> <HintPath>..\..\packages\Microsoft.Owin.Host.HttpListener.4.0.0\lib\net451\Microsoft.Owin.Host.HttpListener.dll</HintPath>
@@ -49,12 +49,14 @@
<HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath> <HintPath>..\..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.Formatting, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="System.Net.Http.Formatting, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.6\lib\net45\System.Net.Http.Formatting.dll</HintPath> <HintPath>..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.6\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Numerics" /> <Reference Include="System.Numerics" />
<Reference Include="System.Web" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="log4net" version="2.0.10" targetFramework="net452" /> <package id="log4net" version="2.0.12" targetFramework="net452" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.6" targetFramework="net452" /> <package id="Microsoft.AspNet.WebApi.Client" version="5.2.6" targetFramework="net452" />
<package id="Microsoft.Owin.Host.HttpListener" version="4.0.0" targetFramework="net452" /> <package id="Microsoft.Owin.Host.HttpListener" version="4.0.0" targetFramework="net452" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" /> <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net452" />

View File

@@ -204,8 +204,8 @@
<Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL"> <Reference Include="Owin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=f0ebd12fd5e55cc5, processorArchitecture=MSIL">
<HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath> <HintPath>..\..\packages\Owin.1.0\lib\net40\Owin.dll</HintPath>
</Reference> </Reference>
<Reference Include="RestEase, Version=1.4.10.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="RestEase, Version=1.5.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\packages\RestEase.1.4.10\lib\net45\RestEase.dll</HintPath> <HintPath>..\..\packages\RestEase.1.5.5\lib\net45\RestEase.dll</HintPath>
</Reference> </Reference>
<Reference Include="SimMetrics.Net, Version=1.0.5.0, Culture=neutral, PublicKeyToken=c58dc06d59f3391b, processorArchitecture=MSIL"> <Reference Include="SimMetrics.Net, Version=1.0.5.0, Culture=neutral, PublicKeyToken=c58dc06d59f3391b, processorArchitecture=MSIL">
<HintPath>..\..\packages\SimMetrics.Net.1.0.5\lib\net45\SimMetrics.Net.dll</HintPath> <HintPath>..\..\packages\SimMetrics.Net.1.0.5\lib\net45\SimMetrics.Net.dll</HintPath>

View File

@@ -59,7 +59,7 @@
<package id="MimeKitLite" version="2.0.7" targetFramework="net461" /> <package id="MimeKitLite" version="2.0.7" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" /> <package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
<package id="Owin" version="1.0" targetFramework="net461" /> <package id="Owin" version="1.0" targetFramework="net461" />
<package id="RestEase" version="1.4.10" targetFramework="net461" /> <package id="RestEase" version="1.5.5" targetFramework="net461" />
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net461" /> <package id="SimMetrics.Net" version="1.0.5" targetFramework="net461" />
<package id="System.Buffers" version="4.5.0" targetFramework="net461" /> <package id="System.Buffers" version="4.5.0" targetFramework="net461" />
<package id="System.Collections.Immutable" version="1.5.0" targetFramework="net461" /> <package id="System.Collections.Immutable" version="1.5.0" targetFramework="net461" />
@@ -76,7 +76,7 @@
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" /> <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" /> <package id="System.Security.Cryptography.X509Certificates" version="4.3.0" targetFramework="net461" />
<package id="System.Security.Principal.Windows" version="4.5.1" targetFramework="net461" /> <package id="System.Security.Principal.Windows" version="4.5.1" targetFramework="net461" />
<package id="System.Text.Encodings.Web" version="4.5.0" targetFramework="net461" /> <package id="System.Text.Encodings.Web" version="4.5.1" targetFramework="net461" />
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net461" /> <package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net461" />
<package id="XPath2" version="1.1.0" targetFramework="net461" /> <package id="XPath2" version="1.1.0" targetFramework="net461" />
</packages> </packages>

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// Body Model /// Body Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class BodyModel public class BodyModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// ClientIPModel /// ClientIPModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ClientIPModel public class ClientIPModel
{ {
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// Cookie Model /// Cookie Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class CookieModel public class CookieModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// EncodingModel /// EncodingModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class EncodingModel public class EncodingModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// Fault Model /// Fault Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class FaultModel public class FaultModel
{ {
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// Header Model /// Header Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class HeaderModel public class HeaderModel
{ {
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// MappingModel /// MappingModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class MappingModel public class MappingModel
{ {
/// <summary> /// <summary>
@@ -52,5 +53,15 @@ namespace WireMock.Admin.Mappings
/// Saves this mapping as a static mapping file. /// Saves this mapping as a static mapping file.
/// </summary> /// </summary>
public bool? SaveToFile { get; set; } public bool? SaveToFile { get; set; }
/// <summary>
/// The Webhook.
/// </summary>
public WebhookModel Webhook { get; set; }
/// <summary>
/// The Webhooks.
/// </summary>
public WebhookModel[] Webhooks { get; set; }
} }
} }

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// MatcherModel /// MatcherModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class MatcherModel public class MatcherModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// Param Model /// Param Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ParamModel public class ParamModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// PathModel /// PathModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class PathModel public class PathModel
{ {
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// RequestModel /// RequestModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class RequestModel public class RequestModel
{ {
/// <summary> /// <summary>

View File

@@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using WireMock.Types;
namespace WireMock.Admin.Mappings namespace WireMock.Admin.Mappings
{ {
/// <summary> /// <summary>
/// ResponseModel /// ResponseModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ResponseModel public class ResponseModel
{ {
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Mappings
/// <summary> /// <summary>
/// Status /// Status
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class StatusModel public class StatusModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// UrlModel /// UrlModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class UrlModel public class UrlModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// WebProxy settings /// WebProxy settings
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebProxyModel public class WebProxyModel
{ {
/// <summary> /// <summary>

View File

@@ -0,0 +1,14 @@
namespace WireMock.Admin.Mappings
{
/// <summary>
/// The Webhook
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookModel
{
/// <summary>
/// The Webhook Request.
/// </summary>
public WebhookRequestModel Request { get; set; }
}
}

View File

@@ -0,0 +1,46 @@
using System.Collections.Generic;
namespace WireMock.Admin.Mappings
{
/// <summary>
/// RequestModel
/// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class WebhookRequestModel
{
/// <summary>
/// Gets or sets the Url.
/// </summary>
public string Url { get; set; }
/// <summary>
/// The methods
/// </summary>
public string Method { get; set; }
/// <summary>
/// Gets or sets the headers.
/// </summary>
public IDictionary<string, string> Headers { get; set; }
/// <summary>
/// Gets or sets the body.
/// </summary>
public string Body { get; set; }
/// <summary>
/// Gets or sets the body (as JSON object).
/// </summary>
public object BodyAsJson { get; set; }
/// <summary>
/// Use ResponseMessage Transformer.
/// </summary>
public bool? UseTransformer { get; set; }
/// <summary>
/// Gets the type of the transformer.
/// </summary>
public string TransformerType { get; set; }
}
}

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Requests
/// <summary> /// <summary>
/// Request Log Model /// Request Log Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class LogEntryModel public class LogEntryModel
{ {
/// <summary> /// <summary>

View File

@@ -5,6 +5,7 @@ namespace WireMock.Admin.Requests
/// <summary> /// <summary>
/// LogRequestMatchModel /// LogRequestMatchModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class LogRequestMatchModel public class LogRequestMatchModel
{ {
/// <summary> /// <summary>

View File

@@ -8,6 +8,7 @@ namespace WireMock.Admin.Requests
/// <summary> /// <summary>
/// RequestMessage Model /// RequestMessage Model
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class LogRequestModel public class LogRequestModel
{ {
/// <summary> /// <summary>

View File

@@ -7,6 +7,7 @@ namespace WireMock.Admin.Requests
/// <summary> /// <summary>
/// Response MessageModel /// Response MessageModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class LogResponseModel public class LogResponseModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// ScenarioStateModel /// ScenarioStateModel
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class ScenarioStateModel public class ScenarioStateModel
{ {
/// <summary> /// <summary>

View File

@@ -3,6 +3,7 @@
/// <summary> /// <summary>
/// Settings /// Settings
/// </summary> /// </summary>
[FluentBuilder.AutoGenerateBuilder]
public class SettingsModel public class SettingsModel
{ {
/// <summary> /// <summary>

View File

@@ -0,0 +1,13 @@
namespace WireMock.Models
{
/// <summary>
/// IWebhook
/// </summary>
public interface IWebhook
{
/// <summary>
/// Request
/// </summary>
IWebhookRequest Request { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Models
{
/// <summary>
/// IWebhookRequest
/// </summary>
public interface IWebhookRequest
{
/// <summary>
/// The Webhook Url.
/// </summary>
string Url { get; set; }
/// <summary>
/// The method to use.
/// </summary>
string Method { get; set; }
/// <summary>
/// The Headers to send.
/// </summary>
IDictionary<string, WireMockList<string>> Headers { get; }
/// <summary>
/// The body to send.
/// </summary>
IBodyData BodyData { get; set; }
/// <summary>
/// Use Transformer.
/// </summary>
bool? UseTransformer { get; set; }
/// <summary>
/// The transformer type.
/// </summary>
TransformerType TransformerType { get; set; }
}
}

View File

@@ -1,37 +1,46 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>Commonly used interfaces, models, enumerations and types.</Description> <Description>Commonly used models, enumerations and types.</Description>
<AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle> <AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle>
<Authors>Stef Heyenrath</Authors> <Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks> <TargetFrameworks>net45;netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Net.Abstractions</AssemblyName>
<PackageId>WireMock.Net.Abstractions</PackageId>
<PackageTags>wiremock;interfaces;models;classes;enumerations;types</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A94}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' "> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>--> <NoWarn>$(NoWarn);1591;8603</NoWarn>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup> <AssemblyName>WireMock.Net.Abstractions</AssemblyName>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" /> <PackageId>WireMock.Net.Abstractions</PackageId>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <PackageTags>wiremock;wiremock.org;interfaces;models;classes;enumerations;types</PackageTags>
</ItemGroup> <RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A94}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
<PackageReference Include="FluentBuilder" Version="0.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project> </Project>

View File

@@ -1,12 +1,19 @@
using System; using System.IO;
using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices.ComTypes;
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 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 public static class WireMockServerExtensions
{ {
/// <summary> /// <summary>
@@ -15,18 +22,26 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// <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>
[PublicAPI]
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
{ {
if (server == null) return WithMappingFromOpenApiFile(server, path, null, out diagnostic);
{ }
throw new ArgumentNullException(nameof(server));
}
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
var mappings = new WireMockOpenApiParser().FromFile(path, out diagnostic); /// <summary>
/// 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>
/// <param name="settings">Additional settings</param>
[PublicAPI]
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{
Guard.NotNull(server, nameof(server));
Guard.NotNullOrEmpty(path, nameof(path));
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
return server.WithMapping(mappings.ToArray()); return server.WithMapping(mappings.ToArray());
} }
@@ -37,9 +52,27 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// <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]
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
{ {
var mappings = new WireMockOpenApiParser().FromStream(stream, out diagnostic); return WithMappingFromOpenApiStream(server, stream, null, out diagnostic);
}
/// <summary>
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
/// </summary>
/// <param name="server">The WireMockServer instance</param>
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
/// <param name="settings">Additional settings</param>
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
[PublicAPI]
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{
Guard.NotNull(server, nameof(server));
Guard.NotNull(stream, nameof(stream));
Guard.NotNull(settings, nameof(settings));
var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
return server.WithMapping(mappings.ToArray()); return server.WithMapping(mappings.ToArray());
} }
@@ -49,9 +82,14 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// </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>
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document) /// <param name="settings">Additional settings [optional]</param>
[PublicAPI]
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings = null)
{ {
var mappings = new WireMockOpenApiParser().FromDocument(document); Guard.NotNull(server, nameof(server));
Guard.NotNull(document, nameof(document));
var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
return server.WithMapping(mappings.ToArray()); return server.WithMapping(mappings.ToArray());
} }

View File

@@ -1,37 +1,57 @@
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;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Settings;
namespace WireMock.Net.OpenApiParser
{ namespace WireMock.Net.OpenApiParser
/// <summary> {
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels. /// <summary>
/// </summary> /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
public interface IWireMockOpenApiParser /// </summary>
{ public interface IWireMockOpenApiParser
/// <summary> {
/// Generate <see cref="IEnumerable{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
/// </summary>
/// <param name="document">The source OpenApiDocument</param>
/// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromDocument(OpenApiDocument document);
/// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
/// </summary>
/// <param name="stream">The source stream</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
/// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
/// <summary> /// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path. /// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
/// </summary> /// </summary>
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param> /// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param> /// <param name="diagnostic">OpenApiDiagnostic output</param>
/// <returns>MappingModel</returns> /// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic); IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic);
}
/// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
/// </summary>
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
/// <param name="settings">Additional settings</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
/// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
/// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
/// </summary>
/// <param name="document">The source OpenApiDocument</param>
/// <param name="settings">Additional settings [optional]</param>
/// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings settings = null);
/// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
/// </summary>
/// <param name="stream">The source stream</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
/// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
/// <summary>
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
/// </summary>
/// <param name="stream">The source stream</param>
/// <param name="settings">Additional settings</param>
/// <param name="diagnostic">OpenApiDiagnostic output</param>
/// <returns>MappingModel</returns>
IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
}
} }

View File

@@ -0,0 +1,276 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Writers;
using Newtonsoft.Json.Linq;
using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Extensions;
using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Types;
using WireMock.Net.OpenApiParser.Utils;
namespace WireMock.Net.OpenApiParser.Mappers
{
internal class OpenApiPathsMapper
{
private readonly WireMockOpenApiParserSettings _settings;
private readonly ExampleValueGenerator _exampleValueGenerator;
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_exampleValueGenerator = new ExampleValueGenerator(settings);
}
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths)
{
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
}
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths)
{
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x);
}
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem)
{
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value));
}
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation)
{
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
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 body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) : MapSchemaToObject(responseSchema);
if (int.TryParse(response.Key, out var httpStatusCode))
{
httpStatusCode = 200;
}
return new MappingModel
{
Guid = Guid.NewGuid(),
Request = new RequestModel
{
Methods = new[] { httpMethod },
Path = MapPathWithParameters(path, pathParameters),
Params = MapQueryParameters(queryParameters)
},
Response = new ResponseModel
{
StatusCode = httpStatusCode,
Headers = MapHeaders(responseContentType, response.Value?.Headers),
BodyAsJson = body
}
};
}
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 objectValue = MapSchemaToObject(property.Value, property.Key);
if (objectValue is JProperty jp)
{
arrayItem.Add(jp);
}
else
{
arrayItem.Add(new JProperty(property.Key, objectValue));
}
}
jArray.Add(arrayItem);
}
else
{
jArray.Add(MapSchemaToObject(schema.Items, name));
}
}
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)
{
string propertyName = schemaProperty.Key;
var openApiSchema = schemaProperty.Value;
if (openApiSchema.GetSchemaType() == SchemaType.Object)
{
var mapped = MapSchemaToObject(schemaProperty.Value, schemaProperty.Key);
if (mapped is JProperty jp)
{
propertyAsJObject.Add(jp);
}
}
else
{
bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
propertyAsJObject.Add(new JProperty(propertyName, _exampleValueGenerator.GetExampleValue(openApiSchema)));
}
}
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
default:
return null;
}
}
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters)
{
if (parameters == null)
{
return path;
}
string newPath = path;
foreach (var parameter in parameters)
{
newPath = newPath.Replace($"{{{parameter.Name}}}", GetExampleValue(parameter.Schema, _settings.PathPatternToUse));
}
return newPath;
}
private JToken MapOpenApiAnyToJToken(IOpenApiAny any)
{
if (any == null)
{
return null;
}
using var outputString = new StringWriter();
var writer = new OpenApiJsonWriter(outputString);
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
return JObject.Parse(outputString.ToString());
}
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
{
var mappedHeaders = headers.ToDictionary(
item => item.Key,
item => GetExampleValue(null, _settings.HeaderPatternToUse) as object
);
if (!string.IsNullOrEmpty(responseContentType))
{
if (!mappedHeaders.ContainsKey("Content-Type"))
{
mappedHeaders.Add("Content-Type", responseContentType);
}
else
{
mappedHeaders["Content-Type"] = responseContentType;
}
}
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
}
private IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
{
var list = queryParameters
.Select(qp => new ParamModel
{
Name = qp.Name,
Matchers = new[]
{
new MatcherModel
{
Name = "ExactMatcher",
Pattern = GetDefaultValueAsStringForSchemaType(qp.Schema)
}
}
})
.ToList();
return list.Any() ? list : null;
}
private string GetDefaultValueAsStringForSchemaType(OpenApiSchema schema)
{
var value = _exampleValueGenerator.GetExampleValue(schema);
switch (value)
{
case string valueAsString:
return valueAsString;
default:
return value.ToString();
}
}
private string GetExampleValue(OpenApiSchema schema, ExampleValueType type)
{
switch (type)
{
case ExampleValueType.Value:
return GetDefaultValueAsStringForSchemaType(schema);
default:
return "*";
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
namespace WireMock.Net.OpenApiParser.Settings
{
/// <summary>
/// A class defining the example values to use for the different types.
/// </summary>
public class WireMockOpenApiParserExampleValues
{
#pragma warning disable 1591
public bool Boolean { get; set; } = true;
public int Integer { get; set; } = 42;
public float Float { get; set; } = 4.2f;
public double Double { get; set; } = 4.2d;
public Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
public Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow;
public byte[] Bytes { get; set; } = { 48, 49, 50 };
public object Object { get; set; } = "example-object";
public string String { get; set; } = "example-string";
#pragma warning restore 1591
}
}

View File

@@ -0,0 +1,30 @@
using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Settings
{
/// <summary>
/// The WireMockOpenApiParser Settings
/// </summary>
public class WireMockOpenApiParserSettings
{
/// <summary>
/// The number of array items to generate (default is 3).
/// </summary>
public int NumberOfArrayItems { get; set; } = 3;
/// <summary>
/// The example value type to use when generating a Path
/// </summary>
public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value;
/// <summary>
/// The example value type to use when generating a Header
/// </summary>
public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value;
/// <summary>
/// The example values to use
/// </summary>
public WireMockOpenApiParserExampleValues ExampleValues { get; } = new WireMockOpenApiParserExampleValues();
}
}

View File

@@ -0,0 +1,18 @@
namespace WireMock.Net.OpenApiParser.Types
{
/// <summary>
/// The example value to use
/// </summary>
public enum ExampleValueType
{
/// <summary>
/// Use a generated example value based on the SchemaType (default).
/// </summary>
Value,
/// <summary>
/// Just use a Wildcard (*) character.
/// </summary>
Wildcard
}
}

View File

@@ -1,49 +1,57 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using WireMock.Net.OpenApiParser.Extensions; using WireMock.Net.OpenApiParser.Extensions;
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 static class ExampleValueGenerator internal class ExampleValueGenerator
{ {
public static object GetExampleValue(OpenApiSchema schema) private readonly WireMockOpenApiParserSettings _settings;
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public object GetExampleValue(OpenApiSchema schema)
{ {
switch (schema?.GetSchemaType()) switch (schema?.GetSchemaType())
{ {
case SchemaType.Boolean: case SchemaType.Boolean:
return true; return _settings.ExampleValues.Boolean;
case SchemaType.Integer: case SchemaType.Integer:
return 42; return _settings.ExampleValues.Integer;
case SchemaType.Number: case SchemaType.Number:
switch (schema?.GetSchemaFormat()) switch (schema?.GetSchemaFormat())
{ {
case SchemaFormat.Float: case SchemaFormat.Float:
return 4.2f; return _settings.ExampleValues.Float;
default: default:
return 4.2d; return _settings.ExampleValues.Double;
} }
default: default:
switch (schema?.GetSchemaFormat()) switch (schema?.GetSchemaFormat())
{ {
case SchemaFormat.Date: case SchemaFormat.Date:
return DateTimeUtils.ToRfc3339Date(DateTime.UtcNow); return DateTimeUtils.ToRfc3339Date(_settings.ExampleValues.Date());
case SchemaFormat.DateTime: case SchemaFormat.DateTime:
return DateTimeUtils.ToRfc3339DateTime(DateTime.UtcNow); return DateTimeUtils.ToRfc3339DateTime(_settings.ExampleValues.DateTime());
case SchemaFormat.Byte: case SchemaFormat.Byte:
return new byte[] { 48, 49, 50 }; return _settings.ExampleValues.Bytes;
case SchemaFormat.Binary: case SchemaFormat.Binary:
return "example-object"; return _settings.ExampleValues.Object;
default: default:
return "example-string"; return _settings.ExampleValues.String;
} }
} }
} }

View File

@@ -12,6 +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>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'"> <PropertyGroup Condition="'$(Configuration)' == 'Release'">
@@ -24,14 +25,11 @@
<PackageReference Include="RamlToOpenApiConverter" Version="0.1.1" /> <PackageReference Include="RamlToOpenApiConverter" Version="0.1.1" />
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" /> <PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Stef.Validation" Version="0.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" /> <ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Options\" />
</ItemGroup>
</Project> </Project>

View File

@@ -1,34 +1,33 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using Microsoft.OpenApi;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Microsoft.OpenApi.Readers; using Microsoft.OpenApi.Readers;
using Microsoft.OpenApi.Writers;
using Newtonsoft.Json.Linq;
using RamlToOpenApiConverter; using RamlToOpenApiConverter;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Net.OpenApiParser.Extensions; using WireMock.Net.OpenApiParser.Mappers;
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Settings;
using WireMock.Net.OpenApiParser.Utils;
namespace WireMock.Net.OpenApiParser namespace WireMock.Net.OpenApiParser
{ {
/// <summary> /// <summary>
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels. /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
/// </summary> /// </summary>
public class WireMockOpenApiParser : IWireMockOpenApiParser public class WireMockOpenApiParser : IWireMockOpenApiParser
{ {
private const int ArrayItems = 3;
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader(); private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
[PublicAPI] [PublicAPI]
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic) public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
{
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
}
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
[PublicAPI]
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{ {
OpenApiDocument document; OpenApiDocument document;
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase)) if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
@@ -42,248 +41,28 @@ namespace WireMock.Net.OpenApiParser
document = reader.Read(File.OpenRead(path), out diagnostic); document = reader.Read(File.OpenRead(path), out diagnostic);
} }
return FromDocument(document); return FromDocument(document, settings);
} }
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
[PublicAPI] [PublicAPI]
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic) public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
{ {
return FromDocument(_reader.Read(stream, out diagnostic)); return FromDocument(_reader.Read(stream, out diagnostic));
} }
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
[PublicAPI] [PublicAPI]
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument) public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
{ {
return MapPaths(openApiDocument.Paths); return FromDocument(_reader.Read(stream, out diagnostic), settings);
} }
private static IEnumerable<MappingModel> MapPaths(OpenApiPaths paths) /// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
[PublicAPI]
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null)
{ {
return paths.Select(p => MapPath(p.Key, p.Value)).SelectMany(x => x); return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths);
}
private static IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem)
{
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value));
}
private static MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation)
{
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
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 body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) : MapSchemaToObject(responseSchema);
if (int.TryParse(response.Key, out var httpStatusCode))
{
httpStatusCode = 200;
}
return new MappingModel
{
Guid = Guid.NewGuid(),
Request = new RequestModel
{
Methods = new[] { httpMethod },
Path = MapPathWithParameters(path, pathParameters),
Params = MapQueryParameters(queryParameters)
},
Response = new ResponseModel
{
StatusCode = httpStatusCode,
Headers = MapHeaders(responseContentType, response.Value?.Headers),
BodyAsJson = body
}
};
}
private static 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 static 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 < ArrayItems; i++)
{
if (schema.Items.Properties.Count > 0)
{
var arrayItem = new JObject();
foreach (var property in schema.Items.Properties)
{
var objectValue = MapSchemaToObject(property.Value, property.Key);
if (objectValue is JProperty jp)
{
arrayItem.Add(jp);
}
else
{
arrayItem.Add(new JProperty(property.Key, objectValue));
}
}
jArray.Add(arrayItem);
}
else
{
jArray.Add(MapSchemaToObject(schema.Items, name));
}
}
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)
{
string propertyName = schemaProperty.Key;
var openApiSchema = schemaProperty.Value;
if (openApiSchema.GetSchemaType() == SchemaType.Object)
{
var mapped = MapSchemaToObject(schemaProperty.Value, schemaProperty.Key);
if (mapped is JProperty jp)
{
propertyAsJObject.Add(jp);
}
}
else
{
bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
propertyAsJObject.Add(new JProperty(propertyName, ExampleValueGenerator.GetExampleValue(openApiSchema)));
}
}
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
default:
return null;
}
}
private static string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters)
{
if (parameters == null)
{
return path;
}
string newPath = path;
foreach (var parameter in parameters)
{
newPath = newPath.Replace($"{{{parameter.Name}}}", ExampleValueGenerator.GetExampleValue(parameter.Schema).ToString());
}
return newPath;
}
private static JToken MapOpenApiAnyToJToken(IOpenApiAny any)
{
if (any == null)
{
return null;
}
using (var outputString = new StringWriter())
{
var writer = new OpenApiJsonWriter(outputString);
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
return JObject.Parse(outputString.ToString());
}
}
private static IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
{
var mappedHeaders = headers.ToDictionary(item => item.Key, item => ExampleValueGenerator.GetExampleValue(null));
if (!string.IsNullOrEmpty(responseContentType))
{
if (!mappedHeaders.ContainsKey("Content-Type"))
{
mappedHeaders.Add("Content-Type", responseContentType);
}
else
{
mappedHeaders["Content-Type"] = responseContentType;
}
}
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
}
private static IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
{
var list = queryParameters
.Select(qp => new ParamModel
{
Name = qp.Name,
Matchers = new[]
{
new MatcherModel
{
Name = "ExactMatcher",
Pattern = GetDefaultValueAsStringForSchemaType(qp.Schema)
}
}
})
.ToList();
return list.Any() ? list : null;
}
private static string GetDefaultValueAsStringForSchemaType(OpenApiSchema schema)
{
var value = ExampleValueGenerator.GetExampleValue(schema);
switch (value)
{
case string valueAsString:
return valueAsString;
default:
return value.ToString();
}
} }
} }
} }

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<Description>A RestClient using RestEase to access the admin interface.</Description> <Description>A RestClient using RestEase to access the admin interface.</Description>
@@ -31,7 +31,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" /> <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="RestEase" Version="1.4.10" /> <PackageReference Include="RestEase" Version="1.5.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -7,7 +7,7 @@ namespace WireMock.Http
{ {
internal static class HttpClientBuilder internal static class HttpClientBuilder
{ {
public static HttpClient Build(IProxyAndRecordSettings settings) public static HttpClient Build(IHttpClientSettings settings)
{ {
#if NETSTANDARD || NETCOREAPP3_1 || NET5_0 #if NETSTANDARD || NETCOREAPP3_1 || NET5_0
var handler = new HttpClientHandler var handler = new HttpClientHandler

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.Models;
using WireMock.Settings;
using WireMock.Transformers;
using WireMock.Transformers.Handlebars;
using WireMock.Transformers.Scriban;
using WireMock.Types;
using WireMock.Util;
using WireMock.Validation;
namespace WireMock.Http
{
internal class WebhookSender
{
private const string ClientIp = "::1";
private readonly IWireMockServerSettings _settings;
public WebhookSender(IWireMockServerSettings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
}
public Task<HttpResponseMessage> SendAsync([NotNull] HttpClient client, [NotNull] IWebhookRequest request, [NotNull] RequestMessage originalRequestMessage, [NotNull] ResponseMessage originalResponseMessage)
{
Check.NotNull(client, nameof(client));
Check.NotNull(request, nameof(request));
Check.NotNull(originalRequestMessage, nameof(originalRequestMessage));
Check.NotNull(originalResponseMessage, nameof(originalResponseMessage));
IBodyData bodyData;
IDictionary<string, WireMockList<string>> headers;
if (request.UseTransformer == true)
{
ITransformer responseMessageTransformer;
switch (request.TransformerType)
{
case TransformerType.Handlebars:
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
responseMessageTransformer = new Transformer(factoryHandlebars);
break;
case TransformerType.Scriban:
case TransformerType.ScribanDotLiquid:
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, request.TransformerType);
responseMessageTransformer = new Transformer(factoryDotLiquid);
break;
default:
throw new NotImplementedException($"TransformerType '{request.TransformerType}' is not supported.");
}
(bodyData, headers) = responseMessageTransformer.Transform(originalRequestMessage, originalResponseMessage, request.BodyData, request.Headers);
}
else
{
bodyData = request.BodyData;
headers = request.Headers;
}
// Create RequestMessage
var requestMessage = new RequestMessage(
new UrlDetails(request.Url),
request.Method,
ClientIp,
bodyData,
headers?.ToDictionary(x => x.Key, x => x.Value.ToArray())
)
{
DateTime = DateTime.UtcNow
};
// Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, request.Url);
// Call the URL
return client.SendAsync(httpRequestMessage);
}
}
}

View File

@@ -2,6 +2,7 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Settings; using WireMock.Settings;
@@ -93,12 +94,17 @@ namespace WireMock
/// </value> /// </value>
bool LogMapping { get; } bool LogMapping { get; }
/// <summary>
/// The Webhooks.
/// </summary>
IWebhook[] Webhooks { get; }
/// <summary> /// <summary>
/// ProvideResponseAsync /// ProvideResponseAsync
/// </summary> /// </summary>
/// <param name="requestMessage">The request message.</param> /// <param name="requestMessage">The request message.</param>
/// <returns>The <see cref="ResponseMessage"/>.</returns> /// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage); Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage);
/// <summary> /// <summary>
/// Gets the RequestMatchResult based on the RequestMessage. /// Gets the RequestMatchResult based on the RequestMessage.

View File

@@ -2,6 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Matchers.Request; using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Settings; using WireMock.Settings;
@@ -54,6 +55,9 @@ namespace WireMock
/// <inheritdoc cref="IMapping.LogMapping" /> /// <inheritdoc cref="IMapping.LogMapping" />
public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider); public bool LogMapping => !(Provider is DynamicResponseProvider || Provider is DynamicAsyncResponseProvider);
/// <inheritdoc cref="IMapping.Webhooks" />
public IWebhook[] Webhooks { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Mapping"/> class. /// Initializes a new instance of the <see cref="Mapping"/> class.
/// </summary> /// </summary>
@@ -68,6 +72,7 @@ namespace WireMock
/// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param> /// <param name="executionConditionState">State in which the current mapping can occur. [Optional]</param>
/// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param> /// <param name="nextState">The next state which will occur after the current mapping execution. [Optional]</param>
/// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param> /// <param name="stateTimes">Only when the current state is executed this number, the next state which will occur. [Optional]</param>
/// <param name="webhooks">The Webhooks. [Optional]</param>
public Mapping( public Mapping(
Guid guid, Guid guid,
[CanBeNull] string title, [CanBeNull] string title,
@@ -79,7 +84,8 @@ namespace WireMock
[CanBeNull] string scenario, [CanBeNull] string scenario,
[CanBeNull] string executionConditionState, [CanBeNull] string executionConditionState,
[CanBeNull] string nextState, [CanBeNull] string nextState,
[CanBeNull] int? stateTimes) [CanBeNull] int? stateTimes,
[CanBeNull] IWebhook[] webhooks)
{ {
Guid = guid; Guid = guid;
Title = title; Title = title;
@@ -92,10 +98,11 @@ namespace WireMock
ExecutionConditionState = executionConditionState; ExecutionConditionState = executionConditionState;
NextState = nextState; NextState = nextState;
StateTimes = stateTimes; StateTimes = stateTimes;
Webhooks = webhooks;
} }
/// <inheritdoc cref="IMapping.ProvideResponseAsync" /> /// <inheritdoc cref="IMapping.ProvideResponseAsync" />
public async Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage) public async Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage)
{ {
return await Provider.ProvideResponseAsync(requestMessage, Settings); return await Provider.ProvideResponseAsync(requestMessage, Settings);
} }

View File

@@ -1,15 +1,15 @@
namespace WireMock.Matchers namespace WireMock.Matchers
{ {
/// <summary> /// <summary>
/// IValueMatcher /// IValueMatcher
/// </summary> /// </summary>
/// <seealso cref="IObjectMatcher" /> /// <seealso cref="IObjectMatcher" />
public interface IValueMatcher: IObjectMatcher public interface IValueMatcher : IObjectMatcher
{ {
/// <summary> /// <summary>
/// Gets the value (can be a string or an object). /// Gets the value (can be a string or an object).
/// </summary> /// </summary>
/// <returns>Value</returns> /// <returns>Value</returns>
object Value { get; } object Value { get; }
} }
} }

View File

@@ -0,0 +1,52 @@
using System.Linq;
namespace WireMock.Matchers
{
/// <summary>
/// NotNullOrEmptyMatcher
/// </summary>
/// <seealso cref="IObjectMatcher" />
public class NotNullOrEmptyMatcher : IObjectMatcher
{
/// <inheritdoc cref="IMatcher.Name"/>
public string Name => "NotNullOrEmptyMatcher";
/// <inheritdoc cref="IMatcher.MatchBehaviour"/>
public MatchBehaviour MatchBehaviour { get; }
/// <inheritdoc cref="IMatcher.ThrowException"/>
public bool ThrowException { get; }
/// <summary>
/// Initializes a new instance of the <see cref="NotNullOrEmptyMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour.</param>
public NotNullOrEmptyMatcher(MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
MatchBehaviour = matchBehaviour;
}
/// <inheritdoc cref="IObjectMatcher.IsMatch"/>
public double IsMatch(object input)
{
bool match;
switch (input)
{
case string @string:
match = !string.IsNullOrEmpty(@string);
break;
case byte[] bytes:
match = bytes != null && bytes.Any();
break;
default:
match = input != null;
break;
}
return MatchBehaviourHelper.Convert(MatchBehaviour, MatchScores.ToScore(match));
}
}
}

View File

@@ -1,113 +1,113 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using JetBrains.Annotations; using JetBrains.Annotations;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Matchers namespace WireMock.Matchers
{ {
/// <summary> /// <summary>
/// Regular Expression Matcher /// Regular Expression Matcher
/// </summary> /// </summary>
/// <inheritdoc cref="IStringMatcher"/> /// <inheritdoc cref="IStringMatcher"/>
/// <inheritdoc cref="IIgnoreCaseMatcher"/> /// <inheritdoc cref="IIgnoreCaseMatcher"/>
public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher public class RegexMatcher : IStringMatcher, IIgnoreCaseMatcher
{ {
private readonly string[] _patterns; private readonly string[] _patterns;
private readonly Regex[] _expressions; private readonly Regex[] _expressions;
/// <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; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class. /// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary> /// </summary>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher([NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase) public RegexMatcher([NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(new[] { pattern }, ignoreCase)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class. /// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="pattern">The pattern.</param> /// <param name="pattern">The pattern.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase) public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] string pattern, bool ignoreCase = false) : this(matchBehaviour, new[] { pattern }, ignoreCase)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class. /// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary> /// </summary>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
public RegexMatcher([NotNull, RegexPattern] string[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase) public RegexMatcher([NotNull, RegexPattern] string[] patterns, bool ignoreCase = false) : this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase)
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RegexMatcher"/> class. /// Initializes a new instance of the <see cref="RegexMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="patterns">The patterns.</param> /// <param name="patterns">The patterns.</param>
/// <param name="ignoreCase">Ignore the case from the pattern.</param> /// <param name="ignoreCase">Ignore the case from the pattern.</param>
/// <param name="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>
public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] string[] patterns, bool ignoreCase = false, bool throwException = false) public RegexMatcher(MatchBehaviour matchBehaviour, [NotNull, RegexPattern] string[] patterns, bool ignoreCase = false, bool throwException = false)
{ {
Check.NotNull(patterns, nameof(patterns)); Check.NotNull(patterns, nameof(patterns));
_patterns = patterns; _patterns = patterns;
IgnoreCase = ignoreCase; IgnoreCase = ignoreCase;
MatchBehaviour = matchBehaviour; MatchBehaviour = matchBehaviour;
ThrowException = throwException; ThrowException = throwException;
RegexOptions options = RegexOptions.Compiled | RegexOptions.Multiline; RegexOptions options = RegexOptions.Compiled | RegexOptions.Multiline;
if (ignoreCase) if (ignoreCase)
{ {
options |= RegexOptions.IgnoreCase; options |= RegexOptions.IgnoreCase;
}
_expressions = patterns.Select(p => new Regex(p, options)).ToArray();
}
/// <inheritdoc cref="IStringMatcher.IsMatch"/>
public virtual double IsMatch(string input)
{
double match = MatchScores.Mismatch;
if (input != null)
{
try
{
match = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)));
}
catch (Exception)
{
if (ThrowException)
{
throw;
}
}
} }
return MatchBehaviourHelper.Convert(MatchBehaviour, match); _expressions = patterns.Select(p => new Regex(p, options)).ToArray();
} }
/// <inheritdoc cref="IStringMatcher.GetPatterns"/> /// <inheritdoc cref="IStringMatcher.IsMatch"/>
public virtual string[] GetPatterns() public virtual double IsMatch(string input)
{ {
return _patterns; double match = MatchScores.Mismatch;
} if (input != null)
{
/// <inheritdoc cref="IMatcher.Name"/> try
public virtual string Name => "RegexMatcher"; {
match = MatchScores.ToScore(_expressions.Select(e => e.IsMatch(input)));
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/> }
public bool IgnoreCase { get; } catch (Exception)
} {
if (ThrowException)
{
throw;
}
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
}
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
public virtual string[] GetPatterns()
{
return _patterns;
}
/// <inheritdoc cref="IMatcher.Name"/>
public virtual string Name => "RegexMatcher";
/// <inheritdoc cref="IIgnoreCaseMatcher.IgnoreCase"/>
public bool IgnoreCase { get; }
}
} }

View File

@@ -1,185 +1,211 @@
using JetBrains.Annotations; using JetBrains.Annotations;
using System; using System;
using System.Linq; using System.Linq;
using WireMock.Types; using WireMock.Types;
using WireMock.Util; using WireMock.Util;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Matchers.Request namespace WireMock.Matchers.Request
{ {
/// <summary> /// <summary>
/// The request body matcher. /// The request body matcher.
/// </summary> /// </summary>
public class RequestMessageBodyMatcher : IRequestMatcher public class RequestMessageBodyMatcher : IRequestMatcher
{ {
/// <summary> /// <summary>
/// The body function /// The body function
/// </summary> /// </summary>
public Func<string, bool> Func { get; } public Func<string, bool> Func { get; }
/// <summary> /// <summary>
/// The body data function for byte[] /// The body data function for byte[]
/// </summary> /// </summary>
public Func<byte[], bool> DataFunc { get; } public Func<byte[], bool> DataFunc { get; }
/// <summary> /// <summary>
/// The body data function for json /// The body data function for json
/// </summary> /// </summary>
public Func<object, bool> JsonFunc { get; } public Func<object, bool> JsonFunc { get; }
/// <summary> /// <summary>
/// The body data function for BodyData /// The body data function for BodyData
/// </summary> /// </summary>
public Func<IBodyData, bool> BodyDataFunc { get; } public Func<IBodyData, bool> BodyDataFunc { get; }
/// <summary> /// <summary>
/// The matchers. /// The matchers.
/// </summary> /// </summary>
public IMatcher[] Matchers { get; } public IMatcher[] Matchers { get; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] string body) : this(new[] { new WildcardMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray()) public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] string body) : this(new[] { new WildcardMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] byte[] body) : this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray()) public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] byte[] body) : this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchBehaviour">The match behaviour.</param> /// <param name="matchBehaviour">The match behaviour.</param>
/// <param name="body">The body.</param> /// <param name="body">The body.</param>
public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] object body) : this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray()) public RequestMessageBodyMatcher(MatchBehaviour matchBehaviour, [NotNull] object body) : this(new[] { new ExactObjectMatcher(matchBehaviour, body) }.Cast<IMatcher>().ToArray())
{ {
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher([NotNull] Func<string, bool> func) public RequestMessageBodyMatcher([NotNull] Func<string, bool> func)
{ {
Check.NotNull(func, nameof(func)); Check.NotNull(func, nameof(func));
Func = func; Func = func;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher([NotNull] Func<byte[], bool> func) public RequestMessageBodyMatcher([NotNull] Func<byte[], bool> func)
{ {
Check.NotNull(func, nameof(func)); Check.NotNull(func, nameof(func));
DataFunc = func; DataFunc = func;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher([NotNull] Func<object, bool> func) public RequestMessageBodyMatcher([NotNull] Func<object, bool> func)
{ {
Check.NotNull(func, nameof(func)); Check.NotNull(func, nameof(func));
JsonFunc = func; JsonFunc = func;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="func">The function.</param> /// <param name="func">The function.</param>
public RequestMessageBodyMatcher([NotNull] Func<IBodyData, bool> func) public RequestMessageBodyMatcher([NotNull] Func<IBodyData, bool> func)
{ {
Check.NotNull(func, nameof(func)); Check.NotNull(func, nameof(func));
BodyDataFunc = func; BodyDataFunc = func;
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class. /// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
/// </summary> /// </summary>
/// <param name="matchers">The matchers.</param> /// <param name="matchers">The matchers.</param>
public RequestMessageBodyMatcher([NotNull] params IMatcher[] matchers) public RequestMessageBodyMatcher([NotNull] params IMatcher[] matchers)
{ {
Check.NotNull(matchers, nameof(matchers)); Check.NotNull(matchers, nameof(matchers));
Matchers = matchers; Matchers = matchers;
} }
/// <see cref="IRequestMatcher.GetMatchingScore"/> /// <see cref="IRequestMatcher.GetMatchingScore"/>
public double GetMatchingScore(IRequestMessage requestMessage, RequestMatchResult requestMatchResult) public double GetMatchingScore(IRequestMessage requestMessage, RequestMatchResult requestMatchResult)
{ {
double score = CalculateMatchScore(requestMessage); double score = CalculateMatchScore(requestMessage);
return requestMatchResult.AddScore(GetType(), score); return requestMatchResult.AddScore(GetType(), score);
} }
private double CalculateMatchScore(IRequestMessage requestMessage, IMatcher matcher) private double CalculateMatchScore(IRequestMessage requestMessage, IMatcher matcher)
{ {
// Check if the matcher is a IObjectMatcher if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
if (matcher is IObjectMatcher objectMatcher) {
{ switch (requestMessage?.BodyData?.DetectedBodyType)
// If the body is a JSON object, try to match. {
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json) case BodyType.Json:
{ case BodyType.String:
return objectMatcher.IsMatch(requestMessage.BodyData.BodyAsJson); return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
}
case BodyType.Bytes:
// If the body is a byte array, try to match. return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyData.BodyAsBytes);
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Bytes)
{ default:
return objectMatcher.IsMatch(requestMessage.BodyData.BodyAsBytes); return MatchScores.Mismatch;
} }
} }
// Check if the matcher is a IStringMatcher if (matcher is ExactObjectMatcher exactObjectMatcher)
if (matcher is IStringMatcher stringMatcher) {
{ // If the body is a byte array, try to match.
// If the body is a Json or a String, use the BodyAsString to match on. var detectedBodyType = requestMessage?.BodyData?.DetectedBodyType;
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json || requestMessage?.BodyData?.DetectedBodyType == BodyType.String) if (detectedBodyType == BodyType.Bytes || detectedBodyType == BodyType.String)
{ {
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString); return exactObjectMatcher.IsMatch(requestMessage.BodyData.BodyAsBytes);
} }
} }
return MatchScores.Mismatch; // Check if the matcher is a IObjectMatcher
} if (matcher is IObjectMatcher objectMatcher)
{
private double CalculateMatchScore(IRequestMessage requestMessage) // If the body is a JSON object, try to match.
{ if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json)
if (Matchers != null && Matchers.Any()) {
{ return objectMatcher.IsMatch(requestMessage.BodyData.BodyAsJson);
return Matchers.Max(matcher => CalculateMatchScore(requestMessage, matcher)); }
}
// If the body is a byte array, try to match.
if (Func != null) if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Bytes)
{ {
return MatchScores.ToScore(Func(requestMessage?.BodyData?.BodyAsString)); return objectMatcher.IsMatch(requestMessage.BodyData.BodyAsBytes);
} }
}
if (JsonFunc != null)
{ // Check if the matcher is a IStringMatcher
return MatchScores.ToScore(JsonFunc(requestMessage?.BodyData?.BodyAsJson)); if (matcher is IStringMatcher stringMatcher)
} {
// If the body is a Json or a String, use the BodyAsString to match on.
if (DataFunc != null) if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json || requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
{ {
return MatchScores.ToScore(DataFunc(requestMessage?.BodyData?.BodyAsBytes)); return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
} }
}
if (BodyDataFunc != null)
{ return MatchScores.Mismatch;
return MatchScores.ToScore(BodyDataFunc(requestMessage?.BodyData)); }
}
private double CalculateMatchScore(IRequestMessage requestMessage)
return MatchScores.Mismatch; {
} if (Matchers != null && Matchers.Any())
} {
return Matchers.Max(matcher => CalculateMatchScore(requestMessage, matcher));
}
if (Func != null)
{
return MatchScores.ToScore(Func(requestMessage?.BodyData?.BodyAsString));
}
if (JsonFunc != null)
{
return MatchScores.ToScore(JsonFunc(requestMessage?.BodyData?.BodyAsJson));
}
if (DataFunc != null)
{
return MatchScores.ToScore(DataFunc(requestMessage?.BodyData?.BodyAsBytes));
}
if (BodyDataFunc != null)
{
return MatchScores.ToScore(BodyDataFunc(requestMessage?.BodyData));
}
return MatchScores.Mismatch;
}
}
} }

View File

@@ -0,0 +1,11 @@
namespace WireMock.Models
{
/// <summary>
/// Webhook
/// </summary>
public class Webhook : IWebhook
{
/// <inheritdoc cref="IWebhook.Request"/>
public IWebhookRequest Request { get; set; }
}
}

View File

@@ -0,0 +1,30 @@
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Models
{
/// <summary>
/// WebhookRequest
/// </summary>
public class WebhookRequest : IWebhookRequest
{
/// <inheritdoc cref="IWebhookRequest.Url"/>
public string Url { get; set; }
/// <inheritdoc cref="IWebhookRequest.Method"/>
public string Method { get; set; }
/// <inheritdoc cref="IWebhookRequest.Headers"/>
public IDictionary<string, WireMockList<string>> Headers { get; set; }
/// <inheritdoc cref="IWebhookRequest.BodyData"/>
public IBodyData BodyData { get; set; }
/// <inheritdoc cref="IWebhookRequest.UseTransformer"/>
public bool? UseTransformer { get; set; }
/// <inheritdoc cref="IWebhookRequest.TransformerType"/>
public TransformerType TransformerType { get; set; }
}
}

View File

@@ -66,6 +66,8 @@ namespace WireMock.Owin
services.AddSingleton<IMappingMatcher, MappingMatcher>(); services.AddSingleton<IMappingMatcher, MappingMatcher>();
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>(); services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>(); services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
_wireMockMiddlewareOptions.AdditionalServiceRegistration?.Invoke(services);
}) })
.Configure(appBuilder => .Configure(appBuilder =>
{ {

View File

@@ -8,6 +8,7 @@ using WireMock.Util;
using Owin; using Owin;
#else #else
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder; using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
#endif #endif
namespace WireMock.Owin namespace WireMock.Owin
@@ -36,6 +37,10 @@ namespace WireMock.Owin
Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; } Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
Action<IServiceCollection> AdditionalServiceRegistration { get; set; }
#endif
IFileSystemHandler FileSystemHandler { get; set; } IFileSystemHandler FileSystemHandler { get; set; }
bool? AllowBodyForAllHttpMethods { get; set; } bool? AllowBodyForAllHttpMethods { get; set; }

View File

@@ -53,7 +53,7 @@ namespace WireMock.Owin.Mappers
} }
} }
BodyData body = null; IBodyData body = null;
if (request.Body != null && BodyParser.ShouldParseBody(method, options.AllowBodyForAllHttpMethods == true)) if (request.Body != null && BodyParser.ShouldParseBody(method, options.AllowBodyForAllHttpMethods == true))
{ {
var bodyParserSettings = new BodyParserSettings var bodyParserSettings = new BodyParserSettings

View File

@@ -9,6 +9,7 @@ using WireMock.Serialization;
using WireMock.Types; using WireMock.Types;
using WireMock.Validation; using WireMock.Validation;
using WireMock.ResponseBuilders; using WireMock.ResponseBuilders;
using WireMock.Settings;
#if !USE_ASPNETCORE #if !USE_ASPNETCORE
using Microsoft.Owin; using Microsoft.Owin;
using IContext = Microsoft.Owin.IOwinContext; using IContext = Microsoft.Owin.IOwinContext;
@@ -128,15 +129,16 @@ namespace WireMock.Owin
await Task.Delay(_options.RequestProcessingDelay.Value); await Task.Delay(_options.RequestProcessingDelay.Value);
} }
response = await targetMapping.ProvideResponseAsync(request); var (theResponse, theOptionalNewMapping) = await targetMapping.ProvideResponseAsync(request);
response = theResponse;
var responseBuilder = targetMapping.Provider as Response; var responseBuilder = targetMapping.Provider as Response;
if (!targetMapping.IsAdminInterface) if (!targetMapping.IsAdminInterface && theOptionalNewMapping != null)
{ {
if (responseBuilder?.ProxyAndRecordSettings?.SaveMapping == true || targetMapping?.Settings?.ProxyAndRecordSettings?.SaveMapping == true) if (responseBuilder?.ProxyAndRecordSettings?.SaveMapping == true || targetMapping?.Settings?.ProxyAndRecordSettings?.SaveMapping == true)
{ {
_options.Mappings.TryAdd(targetMapping.Guid, targetMapping); _options.Mappings.TryAdd(theOptionalNewMapping.Guid, theOptionalNewMapping);
} }
if (responseBuilder?.ProxyAndRecordSettings?.SaveMappingToFile == true || targetMapping?.Settings?.ProxyAndRecordSettings?.SaveMappingToFile == true) if (responseBuilder?.ProxyAndRecordSettings?.SaveMappingToFile == true || targetMapping?.Settings?.ProxyAndRecordSettings?.SaveMappingToFile == true)
@@ -145,7 +147,7 @@ namespace WireMock.Owin
var mappingConverter = new MappingConverter(matcherMapper); var mappingConverter = new MappingConverter(matcherMapper);
var mappingToFileSaver = new MappingToFileSaver(targetMapping.Settings, mappingConverter); var mappingToFileSaver = new MappingToFileSaver(targetMapping.Settings, mappingConverter);
mappingToFileSaver.SaveMappingToFile(targetMapping); mappingToFileSaver.SaveMappingToFile(theOptionalNewMapping);
} }
} }
@@ -153,6 +155,11 @@ namespace WireMock.Owin
{ {
UpdateScenarioState(targetMapping); UpdateScenarioState(targetMapping);
} }
if (!targetMapping.IsAdminInterface && targetMapping.Webhooks?.Length > 0)
{
await SendToWebhooksAsync(targetMapping, request, response).ConfigureAwait(false);
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -184,6 +191,24 @@ namespace WireMock.Owin
await CompletedTask; await CompletedTask;
} }
private async Task SendToWebhooksAsync(IMapping mapping, RequestMessage request, ResponseMessage response)
{
for (int index = 0; index < mapping.Webhooks.Length; index++)
{
var httpClientForWebhook = HttpClientBuilder.Build(mapping.Settings.WebhookSettings ?? new WebhookSettings());
var webhookSender = new WebhookSender(mapping.Settings);
try
{
await webhookSender.SendAsync(httpClientForWebhook, mapping.Webhooks[index].Request, request, response).ConfigureAwait(false);
}
catch (Exception ex)
{
_options.Logger.Error($"Sending message to Webhook [{index}] from Mapping '{mapping.Guid}' failed. Exception: {ex}");
}
}
}
private void UpdateScenarioState(IMapping mapping) private void UpdateScenarioState(IMapping mapping)
{ {
var scenario = _options.Scenarios[mapping.Scenario]; var scenario = _options.Scenarios[mapping.Scenario];

View File

@@ -8,6 +8,7 @@ using WireMock.Util;
using Owin; using Owin;
#else #else
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder; using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
using Microsoft.Extensions.DependencyInjection;
#endif #endif
namespace WireMock.Owin namespace WireMock.Owin
@@ -36,6 +37,10 @@ namespace WireMock.Owin
public Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; } public Action<IAppBuilder> PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
public Action<IServiceCollection> AdditionalServiceRegistration { get; set; }
#endif
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/> /// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
public IFileSystemHandler FileSystemHandler { get; set; } public IFileSystemHandler FileSystemHandler { get; set; }

View File

@@ -1,112 +1,111 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using HandlebarsDotNet.Helpers.Validation; using JetBrains.Annotations;
using JetBrains.Annotations; using WireMock.Http;
using WireMock.Http; using WireMock.Matchers;
using WireMock.Matchers; using WireMock.RequestBuilders;
using WireMock.RequestBuilders; using WireMock.ResponseBuilders;
using WireMock.ResponseBuilders; using WireMock.Settings;
using WireMock.Settings; using WireMock.Types;
using WireMock.Types; using WireMock.Util;
using WireMock.Util;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Proxy namespace WireMock.Proxy
{ {
internal class ProxyHelper internal class ProxyHelper
{ {
private readonly IWireMockServerSettings _settings; private readonly IWireMockServerSettings _settings;
public ProxyHelper([NotNull] IWireMockServerSettings settings) public ProxyHelper([NotNull] IWireMockServerSettings settings)
{ {
Check.NotNull(settings, nameof(settings)); Check.NotNull(settings, nameof(settings));
_settings = settings; _settings = settings;
} }
public async Task<(ResponseMessage ResponseMessage, IMapping Mapping)> SendAsync( public async Task<(ResponseMessage Message, IMapping Mapping)> SendAsync(
[NotNull] IProxyAndRecordSettings proxyAndRecordSettings, [NotNull] IProxyAndRecordSettings proxyAndRecordSettings,
[NotNull] HttpClient client, [NotNull] HttpClient client,
[NotNull] RequestMessage requestMessage, [NotNull] RequestMessage requestMessage,
[NotNull] string url) [NotNull] string url)
{ {
Check.NotNull(client, nameof(client)); Check.NotNull(client, nameof(client));
Check.NotNull(requestMessage, nameof(requestMessage)); Check.NotNull(requestMessage, nameof(requestMessage));
Check.NotNull(url, nameof(url)); Check.NotNull(url, nameof(url));
var originalUri = new Uri(requestMessage.Url); var originalUri = new Uri(requestMessage.Url);
var requiredUri = new Uri(url); var requiredUri = new Uri(url);
// Create HttpRequestMessage // Create HttpRequestMessage
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, url); var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, url);
// Call the URL // Call the URL
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead); var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead);
// Create ResponseMessage // Create ResponseMessage
bool deserializeJson = !_settings.DisableJsonBodyParsing.GetValueOrDefault(false); bool deserializeJson = !_settings.DisableJsonBodyParsing.GetValueOrDefault(false);
bool decompressGzipAndDeflate = !_settings.DisableRequestBodyDecompressing.GetValueOrDefault(false); bool decompressGzipAndDeflate = !_settings.DisableRequestBodyDecompressing.GetValueOrDefault(false);
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate); var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate);
IMapping mapping = null; IMapping mapping = null;
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) && if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile)) (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
{ {
mapping = ToMapping(proxyAndRecordSettings, requestMessage, responseMessage); mapping = ToMapping(proxyAndRecordSettings, requestMessage, responseMessage);
} }
return (responseMessage, mapping); return (responseMessage, mapping);
} }
private IMapping ToMapping(IProxyAndRecordSettings proxyAndRecordSettings, RequestMessage requestMessage, ResponseMessage responseMessage) private IMapping ToMapping(IProxyAndRecordSettings proxyAndRecordSettings, RequestMessage requestMessage, ResponseMessage responseMessage)
{ {
string[] excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }; string[] excludedHeaders = proxyAndRecordSettings.ExcludedHeaders ?? new string[] { };
string[] excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { }; string[] excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
var request = Request.Create(); var request = Request.Create();
request.WithPath(requestMessage.Path); request.WithPath(requestMessage.Path);
request.UsingMethod(requestMessage.Method); request.UsingMethod(requestMessage.Method);
requestMessage.Query.Loop((key, value) => request.WithParam(key, false, value.ToArray())); requestMessage.Query.Loop((key, value) => request.WithParam(key, false, value.ToArray()));
requestMessage.Cookies.Loop((key, value) => requestMessage.Cookies.Loop((key, value) =>
{ {
if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase)) if (!excludedCookies.Contains(key, StringComparer.OrdinalIgnoreCase))
{ {
request.WithCookie(key, value); request.WithCookie(key, value);
} }
}); });
var allExcludedHeaders = new List<string>(excludedHeaders) { "Cookie" }; var allExcludedHeaders = new List<string>(excludedHeaders) { "Cookie" };
requestMessage.Headers.Loop((key, value) => requestMessage.Headers.Loop((key, value) =>
{ {
if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase)) if (!allExcludedHeaders.Contains(key, StringComparer.OrdinalIgnoreCase))
{ {
request.WithHeader(key, value.ToArray()); request.WithHeader(key, value.ToArray());
} }
}); });
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
switch (requestMessage.BodyData?.DetectedBodyType) switch (requestMessage.BodyData?.DetectedBodyType)
{ {
case BodyType.Json: case BodyType.Json:
request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson, true, throwExceptionWhenMatcherFails)); request.WithBody(new JsonMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsJson, true, throwExceptionWhenMatcherFails));
break; break;
case BodyType.String: case BodyType.String:
request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, throwExceptionWhenMatcherFails, requestMessage.BodyData.BodyAsString)); request.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, throwExceptionWhenMatcherFails, requestMessage.BodyData.BodyAsString));
break; break;
case BodyType.Bytes: case BodyType.Bytes:
request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails)); request.WithBody(new ExactObjectMatcher(MatchBehaviour.AcceptOnMatch, requestMessage.BodyData.BodyAsBytes, throwExceptionWhenMatcherFails));
break; break;
} }
var response = Response.Create(responseMessage); var response = Response.Create(responseMessage);
return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null, null); return new Mapping(Guid.NewGuid(), string.Empty, null, _settings, request, response, 0, null, null, null, null, null);
} }
} }
} }

View File

@@ -101,7 +101,7 @@ namespace WireMock
/// <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([NotNull] UrlDetails urlDetails, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] BodyData bodyData = null, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null) public RequestMessage([NotNull] UrlDetails urlDetails, [NotNull] string method, [NotNull] string clientIP, [CanBeNull] IBodyData bodyData = null, [CanBeNull] IDictionary<string, string[]> headers = null, [CanBeNull] IDictionary<string, string> cookies = null)
{ {
Check.NotNull(urlDetails, nameof(urlDetails)); Check.NotNull(urlDetails, nameof(urlDetails));
Check.NotNull(method, nameof(method)); Check.NotNull(method, nameof(method));

View File

@@ -335,7 +335,7 @@ namespace WireMock.ResponseBuilders
} }
/// <inheritdoc cref="IResponseProvider.ProvideResponseAsync(RequestMessage, IWireMockServerSettings)"/> /// <inheritdoc cref="IResponseProvider.ProvideResponseAsync(RequestMessage, IWireMockServerSettings)"/>
public async Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings) public async Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings)
{ {
Check.NotNull(requestMessage, nameof(requestMessage)); Check.NotNull(requestMessage, nameof(requestMessage));
Check.NotNull(settings, nameof(settings)); Check.NotNull(settings, nameof(settings));
@@ -361,14 +361,12 @@ namespace WireMock.ResponseBuilders
var proxyHelper = new ProxyHelper(settings); var proxyHelper = new ProxyHelper(settings);
var (proxyResponseMessage, _) = await proxyHelper.SendAsync( return await proxyHelper.SendAsync(
ProxyAndRecordSettings, ProxyAndRecordSettings,
_httpClientForProxy, _httpClientForProxy,
requestMessage, requestMessage,
requestMessage.ProxyUrl requestMessage.ProxyUrl
); );
return proxyResponseMessage;
} }
ResponseMessage responseMessage; ResponseMessage responseMessage;
@@ -394,7 +392,7 @@ namespace WireMock.ResponseBuilders
} }
// Copy Headers from ResponseMessage (if defined) // Copy Headers from ResponseMessage (if defined)
if (ResponseMessage.Headers != null) if (ResponseMessage.Headers?.Count > 0)
{ {
responseMessage.Headers = ResponseMessage.Headers; responseMessage.Headers = ResponseMessage.Headers;
} }
@@ -420,7 +418,7 @@ namespace WireMock.ResponseBuilders
throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported."); throw new NotImplementedException($"TransformerType '{TransformerType}' is not supported.");
} }
return responseMessageTransformer.Transform(requestMessage, responseMessage, UseTransformerForBodyAsFile); return (responseMessageTransformer.Transform(requestMessage, responseMessage, UseTransformerForBodyAsFile), null);
} }
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true) if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true)
@@ -428,7 +426,7 @@ namespace WireMock.ResponseBuilders
ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile); ResponseMessage.BodyData.BodyAsBytes = settings.FileSystemHandler.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
} }
return responseMessage; return (responseMessage, null);
} }
} }
} }

View File

@@ -13,9 +13,9 @@ namespace WireMock.ResponseProviders
_responseMessageFunc = responseMessageFunc; _responseMessageFunc = responseMessageFunc;
} }
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings) public async Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings)
{ {
return _responseMessageFunc(requestMessage); return (await _responseMessageFunc(requestMessage), null);
} }
} }
} }

View File

@@ -13,9 +13,10 @@ namespace WireMock.ResponseProviders
_responseMessageFunc = responseMessageFunc; _responseMessageFunc = responseMessageFunc;
} }
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings) public Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings)
{ {
return Task.FromResult(_responseMessageFunc(requestMessage)); (ResponseMessage responseMessage, IMapping mapping) result = (_responseMessageFunc(requestMessage), null);
return Task.FromResult(result);
} }
} }
} }

View File

@@ -16,7 +16,7 @@ namespace WireMock.ResponseProviders
/// </summary> /// </summary>
/// <param name="requestMessage">The request.</param> /// <param name="requestMessage">The request.</param>
/// <param name="settings">The WireMockServerSettings.</param> /// <param name="settings">The WireMockServerSettings.</param>
/// <returns>The <see cref="ResponseMessage"/>.</returns> /// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
Task<ResponseMessage> ProvideResponseAsync([NotNull] RequestMessage requestMessage, [NotNull] IWireMockServerSettings settings); Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync([NotNull] RequestMessage requestMessage, [NotNull] IWireMockServerSettings settings);
} }
} }

View File

@@ -15,9 +15,9 @@ namespace WireMock.ResponseProviders
_settings = settings; _settings = settings;
} }
public Task<ResponseMessage> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings) public async Task<(ResponseMessage Message, IMapping Mapping)> ProvideResponseAsync(RequestMessage requestMessage, IWireMockServerSettings settings)
{ {
return _responseMessageFunc(requestMessage, _settings); return (await _responseMessageFunc(requestMessage, _settings), null);
} }
} }
} }

View File

@@ -87,6 +87,15 @@ namespace WireMock.Serialization
} }
}; };
if (mapping.Webhooks?.Length == 1)
{
mappingModel.Webhook = WebhookMapper.Map(mapping.Webhooks[0]);
}
else if (mapping.Webhooks?.Length > 1)
{
mappingModel.Webhooks = mapping.Webhooks.Select(WebhookMapper.Map).ToArray();
}
if (bodyMatcher?.Matchers != null) if (bodyMatcher?.Matchers != null)
{ {
mappingModel.Request.Body = new BodyModel(); mappingModel.Request.Body = new BodyModel();

View File

@@ -1,164 +1,167 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using SimMetrics.Net; using SimMetrics.Net;
using WireMock.Admin.Mappings; using WireMock.Admin.Mappings;
using WireMock.Matchers; using WireMock.Matchers;
using WireMock.Plugin; using WireMock.Plugin;
using WireMock.Settings; using WireMock.Settings;
using WireMock.Validation; using WireMock.Validation;
namespace WireMock.Serialization namespace WireMock.Serialization
{ {
internal class MatcherMapper internal class MatcherMapper
{ {
private readonly IWireMockServerSettings _settings; private readonly IWireMockServerSettings _settings;
public MatcherMapper(IWireMockServerSettings settings) public MatcherMapper(IWireMockServerSettings settings)
{ {
Check.NotNull(settings, nameof(settings)); Check.NotNull(settings, nameof(settings));
_settings = settings; _settings = settings;
} }
public IMatcher[] Map([CanBeNull] IEnumerable<MatcherModel> matchers) public IMatcher[] Map([CanBeNull] IEnumerable<MatcherModel> matchers)
{ {
return matchers?.Select(Map).Where(m => m != null).ToArray(); return matchers?.Select(Map).Where(m => m != null).ToArray();
} }
public IMatcher Map([CanBeNull] MatcherModel matcher) public IMatcher Map([CanBeNull] MatcherModel matcher)
{ {
if (matcher == null) if (matcher == null)
{ {
return null; return null;
} }
string[] parts = matcher.Name.Split('.'); string[] parts = matcher.Name.Split('.');
string matcherName = parts[0]; string matcherName = parts[0];
string matcherType = parts.Length > 1 ? parts[1] : null; string matcherType = parts.Length > 1 ? parts[1] : null;
string[] stringPatterns = (matcher.Patterns != null ? matcher.Patterns : new[] { matcher.Pattern }).OfType<string>().ToArray(); string[] stringPatterns = (matcher.Patterns != null ? matcher.Patterns : new[] { matcher.Pattern }).OfType<string>().ToArray();
MatchBehaviour matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch; MatchBehaviour matchBehaviour = matcher.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch;
bool ignoreCase = matcher.IgnoreCase == true; bool ignoreCase = matcher.IgnoreCase == true;
bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true; bool throwExceptionWhenMatcherFails = _settings.ThrowExceptionWhenMatcherFails == true;
switch (matcherName) switch (matcherName)
{ {
case "CSharpCodeMatcher": case "NotNullOrEmptyMatcher":
if (_settings.AllowCSharpCodeMatcher == true) return new NotNullOrEmptyMatcher(matchBehaviour);
{
return PluginLoader.Load<ICSharpCodeMatcher>(matchBehaviour, stringPatterns); case "CSharpCodeMatcher":
} if (_settings.AllowCSharpCodeMatcher == true)
{
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because IWireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'."); return PluginLoader.Load<ICSharpCodeMatcher>(matchBehaviour, stringPatterns);
}
case "LinqMatcher":
return new LinqMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns); throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because IWireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
case "ExactMatcher": case "LinqMatcher":
return new ExactMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns); return new LinqMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "ExactObjectMatcher": case "ExactMatcher":
return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails); return new ExactMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "RegexMatcher": case "ExactObjectMatcher":
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails); return CreateExactObjectMatcher(matchBehaviour, stringPatterns[0], throwExceptionWhenMatcherFails);
case "JsonMatcher": case "RegexMatcher":
object value = matcher.Pattern ?? matcher.Patterns; return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
return new JsonMatcher(matchBehaviour, value, ignoreCase, throwExceptionWhenMatcherFails);
case "JsonMatcher":
case "JsonPartialMatcher": object value = matcher.Pattern ?? matcher.Patterns;
object matcherValue = matcher.Pattern ?? matcher.Patterns; return new JsonMatcher(matchBehaviour, value, ignoreCase, throwExceptionWhenMatcherFails);
return new JsonPartialMatcher(matchBehaviour, matcherValue, ignoreCase, throwExceptionWhenMatcherFails);
case "JsonPartialMatcher":
case "JsonPathMatcher": object matcherValue = matcher.Pattern ?? matcher.Patterns;
return new JsonPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns); return new JsonPartialMatcher(matchBehaviour, matcherValue, ignoreCase, throwExceptionWhenMatcherFails);
case "JmesPathMatcher": case "JsonPathMatcher":
return new JmesPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns); return new JsonPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "XPathMatcher": case "JmesPathMatcher":
return new XPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns); return new JmesPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "WildcardMatcher": case "XPathMatcher":
return new WildcardMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails); return new XPathMatcher(matchBehaviour, throwExceptionWhenMatcherFails, stringPatterns);
case "ContentTypeMatcher": case "WildcardMatcher":
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails); return new WildcardMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
case "SimMetricsMatcher": case "ContentTypeMatcher":
SimMetricType type = SimMetricType.Levenstein; return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase, throwExceptionWhenMatcherFails);
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
{ case "SimMetricsMatcher":
throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported."); SimMetricType type = SimMetricType.Levenstein;
} if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
{
return new SimMetricsMatcher(matchBehaviour, stringPatterns, type, throwExceptionWhenMatcherFails); throw new NotSupportedException($"Matcher '{matcherName}' with Type '{matcherType}' is not supported.");
}
default:
throw new NotSupportedException($"Matcher '{matcherName}' is not supported."); return new SimMetricsMatcher(matchBehaviour, stringPatterns, type, throwExceptionWhenMatcherFails);
}
} default:
throw new NotSupportedException($"Matcher '{matcherName}' is not supported.");
public MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers) }
{ }
return matchers?.Select(Map).Where(m => m != null).ToArray();
} public MatcherModel[] Map([CanBeNull] IEnumerable<IMatcher> matchers)
{
public MatcherModel Map([CanBeNull] IMatcher matcher) return matchers?.Select(Map).Where(m => m != null).ToArray();
{ }
if (matcher == null)
{ public MatcherModel Map([CanBeNull] IMatcher matcher)
return null; {
} if (matcher == null)
{
object[] patterns = new object[0]; // Default empty array return null;
switch (matcher) }
{
// If the matcher is a IStringMatcher, get the patterns. object[] patterns = new object[0]; // Default empty array
case IStringMatcher stringMatcher: switch (matcher)
patterns = stringMatcher.GetPatterns().Cast<object>().ToArray(); {
break; // If the matcher is a IStringMatcher, get the patterns.
case IStringMatcher stringMatcher:
// If the matcher is a IValueMatcher, get the value (can be string or object). patterns = stringMatcher.GetPatterns().Cast<object>().ToArray();
case IValueMatcher valueMatcher: break;
patterns = new[] { valueMatcher.Value };
break; // If the matcher is a IValueMatcher, get the value (can be string or object).
case IValueMatcher valueMatcher:
// If the matcher is a ExactObjectMatcher, get the ValueAsObject or ValueAsBytes. patterns = new[] { valueMatcher.Value };
case ExactObjectMatcher exactObjectMatcher: break;
patterns = new[] { exactObjectMatcher.ValueAsObject ?? exactObjectMatcher.ValueAsBytes };
break; // If the matcher is a ExactObjectMatcher, get the ValueAsObject or ValueAsBytes.
} case ExactObjectMatcher exactObjectMatcher:
patterns = new[] { exactObjectMatcher.ValueAsObject ?? exactObjectMatcher.ValueAsBytes };
bool? ignoreCase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null; break;
}
bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
bool? ignoreCase = matcher is IIgnoreCaseMatcher ignoreCaseMatcher ? ignoreCaseMatcher.IgnoreCase : (bool?)null;
return new MatcherModel
{ bool? rejectOnMatch = matcher.MatchBehaviour == MatchBehaviour.RejectOnMatch ? true : (bool?)null;
RejectOnMatch = rejectOnMatch,
IgnoreCase = ignoreCase, return new MatcherModel
Name = matcher.Name, {
Pattern = patterns.Length == 1 ? patterns.First() : null, RejectOnMatch = rejectOnMatch,
Patterns = patterns.Length > 1 ? patterns : null IgnoreCase = ignoreCase,
}; Name = matcher.Name,
} Pattern = patterns.Length == 1 ? patterns.First() : null,
Patterns = patterns.Length > 1 ? patterns : null
private ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, string stringPattern, bool throwException) };
{ }
byte[] bytePattern;
try private ExactObjectMatcher CreateExactObjectMatcher(MatchBehaviour matchBehaviour, string stringPattern, bool throwException)
{ {
bytePattern = Convert.FromBase64String(stringPattern); byte[] bytePattern;
} try
catch {
{ bytePattern = Convert.FromBase64String(stringPattern);
throw new ArgumentException($"Matcher 'ExactObjectMatcher' has invalid pattern. The pattern value '{stringPattern}' is not a Base64String.", nameof(stringPattern)); }
} catch
{
return new ExactObjectMatcher(matchBehaviour, bytePattern, throwException); throw new ArgumentException($"Matcher 'ExactObjectMatcher' has invalid pattern. The pattern value '{stringPattern}' is not a Base64String.", nameof(stringPattern));
} }
}
return new ExactObjectMatcher(matchBehaviour, bytePattern, throwException);
}
}
} }

View File

@@ -0,0 +1,103 @@
using System;
using System.Collections.Generic;
using System.Linq;
using WireMock.Admin.Mappings;
using WireMock.Http;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Serialization
{
internal static class WebhookMapper
{
public static IWebhook Map(WebhookModel model)
{
var webhook = new Webhook
{
Request = new WebhookRequest
{
Url = model.Request.Url,
Method = model.Request.Method,
Headers = model.Request.Headers?.ToDictionary(x => x.Key, x => new WireMockList<string>(x.Value)) ?? new Dictionary<string, WireMockList<string>>()
}
};
if (model.Request.UseTransformer == true)
{
webhook.Request.UseTransformer = true;
if (!Enum.TryParse<TransformerType>(model.Request.TransformerType, out var transformerType))
{
transformerType = TransformerType.Handlebars;
}
webhook.Request.TransformerType = transformerType;
}
IEnumerable<string> contentTypeHeader = null;
if (webhook.Request.Headers.Any(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)))
{
contentTypeHeader = webhook.Request.Headers.First(header => string.Equals(header.Key, HttpKnownHeaderNames.ContentType, StringComparison.OrdinalIgnoreCase)).Value;
}
if (model.Request.Body != null)
{
webhook.Request.BodyData = new BodyData
{
BodyAsString = model.Request.Body,
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
};
}
else if (model.Request.BodyAsJson != null)
{
webhook.Request.BodyData = new BodyData
{
BodyAsJson = model.Request.BodyAsJson,
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyParser.DetectBodyTypeFromContentType(contentTypeHeader?.FirstOrDefault())
};
}
return webhook;
}
public static WebhookModel Map(IWebhook webhook)
{
if (webhook?.Request == null)
{
return null;
}
var model = new WebhookModel
{
Request = new WebhookRequestModel
{
Url = webhook.Request.Url,
Method = webhook.Request.Method,
Headers = webhook.Request.Headers?.ToDictionary(x => x.Key, x => x.Value.ToString()),
UseTransformer = webhook.Request.UseTransformer,
TransformerType = webhook.Request.UseTransformer == true ? webhook.Request.TransformerType.ToString() : null
}
};
if (webhook.Request.BodyData != null)
{
switch (webhook.Request.BodyData.DetectedBodyType)
{
case BodyType.String:
model.Request.Body = webhook.Request.BodyData.BodyAsString;
break;
case BodyType.Json:
model.Request.BodyAsJson = webhook.Request.BodyData.BodyAsJson;
break;
default:
break;
}
}
return model;
}
}
}

View File

@@ -1,5 +1,9 @@
using System; using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using WireMock.Models;
using WireMock.ResponseProviders; using WireMock.ResponseProviders;
using WireMock.Types;
namespace WireMock.Server namespace WireMock.Server
{ {
@@ -97,5 +101,50 @@ namespace WireMock.Server
/// <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>
/// Add (multiple) Webhook(s) to call after the response has been generated.
/// </summary>
/// <param name="webhooks">The Webhooks</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook(params IWebhook[] webhooks);
/// <summary>
/// Add a Webhook to call after the response has been generated.
/// </summary>
/// <param name="url">The Webhook Url</param>
/// <param name="method">The method to use. [optional]</param>
/// <param name="headers">The Headers to send. [optional]</param>
/// <param name="body">The body (as string) to send. [optional]</param>
/// <param name="useTransformer">Use Transformer. [optional]</param>
/// <param name="transformerType">The transformer type. [optional]</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] string body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars
);
/// <summary>
/// Add a Webhook to call after the response has been generated.
/// </summary>
/// <param name="url">The Webhook Url</param>
/// <param name="method">The method to use. [optional]</param>
/// <param name="headers">The Headers to send. [optional]</param>
/// <param name="body">The body (as json) to send. [optional]</param>
/// <param name="useTransformer">Use Transformer. [optional]</param>
/// <param name="transformerType">The transformer type. [optional]</param>
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] object body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars
);
} }
} }

View File

@@ -1,144 +1,230 @@
// 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; using System;
using WireMock.Matchers.Request; using System.Collections.Generic;
using WireMock.ResponseProviders; using JetBrains.Annotations;
using WireMock.Settings; using WireMock.Matchers.Request;
using WireMock.Models;
namespace WireMock.Server using WireMock.ResponseProviders;
{ using WireMock.Settings;
/// <summary> using WireMock.Types;
/// The respond with a provider. using WireMock.Util;
/// </summary> using WireMock.Validation;
internal class RespondWithAProvider : IRespondWithAProvider
{ namespace WireMock.Server
private int _priority; {
private string _title; /// <summary>
private string _path; /// The respond with a provider.
private string _executionConditionState; /// </summary>
private string _nextState; internal class RespondWithAProvider : IRespondWithAProvider
private string _scenario; {
private int _timesInSameState = 1; private int _priority;
private readonly RegistrationCallback _registrationCallback; private string _title;
private readonly IRequestMatcher _requestMatcher; private string _path;
private readonly IWireMockServerSettings _settings; private string _executionConditionState;
private readonly bool _saveToFile; private string _nextState;
private string _scenario;
public Guid Guid { get; private set; } = Guid.NewGuid(); private int _timesInSameState = 1;
private readonly RegistrationCallback _registrationCallback;
/// <summary> private readonly IRequestMatcher _requestMatcher;
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class. private readonly IWireMockServerSettings _settings;
/// </summary> private readonly bool _saveToFile;
/// <param name="registrationCallback">The registration callback.</param>
/// <param name="requestMatcher">The request matcher.</param> public Guid Guid { get; private set; } = Guid.NewGuid();
/// <param name="settings">The WireMockServerSettings.</param>
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param> public IWebhook[] Webhooks { get; private set; }
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher, IWireMockServerSettings settings, bool saveToFile = false)
{ /// <summary>
_registrationCallback = registrationCallback; /// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
_requestMatcher = requestMatcher; /// </summary>
_settings = settings; /// <param name="registrationCallback">The registration callback.</param>
_saveToFile = saveToFile; /// <param name="requestMatcher">The request matcher.</param>
} /// <param name="settings">The WireMockServerSettings.</param>
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
/// <summary> public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher, IWireMockServerSettings settings, bool saveToFile = false)
/// The respond with. {
/// </summary> _registrationCallback = registrationCallback;
/// <param name="provider">The provider.</param> _requestMatcher = requestMatcher;
public void RespondWith(IResponseProvider provider) _settings = settings;
{ _saveToFile = saveToFile;
_registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState), _saveToFile); }
}
/// <summary>
/// <see cref="IRespondWithAProvider.WithGuid(string)"/> /// The respond with.
public IRespondWithAProvider WithGuid(string guid) /// </summary>
{ /// <param name="provider">The provider.</param>
return WithGuid(Guid.Parse(guid)); public void RespondWith(IResponseProvider provider)
} {
_registrationCallback(new Mapping(Guid, _title, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks), _saveToFile);
/// <see cref="IRespondWithAProvider.WithGuid(Guid)"/> }
public IRespondWithAProvider WithGuid(Guid guid)
{ /// <see cref="IRespondWithAProvider.WithGuid(string)"/>
Guid = guid; public IRespondWithAProvider WithGuid(string guid)
{
return this; return WithGuid(Guid.Parse(guid));
} }
/// <see cref="IRespondWithAProvider.WithTitle"/> /// <see cref="IRespondWithAProvider.WithGuid(Guid)"/>
public IRespondWithAProvider WithTitle(string title) public IRespondWithAProvider WithGuid(Guid guid)
{ {
_title = title; Guid = guid;
return this; return this;
} }
/// <see cref="IRespondWithAProvider.WithPath"/> /// <see cref="IRespondWithAProvider.WithTitle"/>
public IRespondWithAProvider WithPath(string path) public IRespondWithAProvider WithTitle(string title)
{ {
_path = path; _title = title;
return this; return this;
} }
/// <see cref="IRespondWithAProvider.AtPriority"/> /// <see cref="IRespondWithAProvider.WithPath"/>
public IRespondWithAProvider AtPriority(int priority) public IRespondWithAProvider WithPath(string path)
{ {
_priority = priority; _path = path;
return this; return this;
} }
/// <see cref="IRespondWithAProvider.InScenario(string)"/> /// <see cref="IRespondWithAProvider.AtPriority"/>
public IRespondWithAProvider InScenario(string scenario) public IRespondWithAProvider AtPriority(int priority)
{ {
_scenario = scenario; _priority = priority;
return this; return this;
} }
/// <see cref="IRespondWithAProvider.InScenario(int)"/> /// <see cref="IRespondWithAProvider.InScenario(string)"/>
public IRespondWithAProvider InScenario(int scenario) public IRespondWithAProvider InScenario(string scenario)
{ {
return InScenario(scenario.ToString()); _scenario = scenario;
}
return this;
/// <see cref="IRespondWithAProvider.WhenStateIs(string)"/> }
public IRespondWithAProvider WhenStateIs(string state)
{ /// <see cref="IRespondWithAProvider.InScenario(int)"/>
if (string.IsNullOrEmpty(_scenario)) public IRespondWithAProvider InScenario(int scenario)
{ {
throw new NotSupportedException("Unable to set state condition when no scenario is defined."); return InScenario(scenario.ToString());
} }
_executionConditionState = state; /// <see cref="IRespondWithAProvider.WhenStateIs(string)"/>
public IRespondWithAProvider WhenStateIs(string state)
return this; {
} if (string.IsNullOrEmpty(_scenario))
{
/// <see cref="IRespondWithAProvider.WhenStateIs(int)"/> throw new NotSupportedException("Unable to set state condition when no scenario is defined.");
public IRespondWithAProvider WhenStateIs(int state) }
{
return WhenStateIs(state.ToString()); _executionConditionState = state;
}
return this;
/// <see cref="IRespondWithAProvider.WillSetStateTo(string, int?)"/> }
public IRespondWithAProvider WillSetStateTo(string state, int? times = 1)
{ /// <see cref="IRespondWithAProvider.WhenStateIs(int)"/>
if (string.IsNullOrEmpty(_scenario)) public IRespondWithAProvider WhenStateIs(int state)
{ {
throw new NotSupportedException("Unable to set next state when no scenario is defined."); return WhenStateIs(state.ToString());
} }
_nextState = state; /// <see cref="IRespondWithAProvider.WillSetStateTo(string, int?)"/>
_timesInSameState = times ?? 1; public IRespondWithAProvider WillSetStateTo(string state, int? times = 1)
{
return this; if (string.IsNullOrEmpty(_scenario))
} {
throw new NotSupportedException("Unable to set next state when no scenario is defined.");
/// <see cref="IRespondWithAProvider.WillSetStateTo(int, int?)"/> }
public IRespondWithAProvider WillSetStateTo(int state, int? times = 1)
{ _nextState = state;
return WillSetStateTo(state.ToString(), times); _timesInSameState = times ?? 1;
}
} return this;
}
/// <see cref="IRespondWithAProvider.WillSetStateTo(int, int?)"/>
public IRespondWithAProvider WillSetStateTo(int state, int? times = 1)
{
return WillSetStateTo(state.ToString(), times);
}
/// <see cref="IRespondWithAProvider.WithWebhook(IWebhook[])"/>
public IRespondWithAProvider WithWebhook(params IWebhook[] webhooks)
{
Check.HasNoNulls(webhooks, nameof(webhooks));
Webhooks = webhooks;
return this;
}
/// <see cref="IRespondWithAProvider.WithWebhook(string, string, IDictionary{string, WireMockList{string}}, string, bool, TransformerType)"/>
public IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] string body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Webhooks = new[] { InitWebhook(url, method, headers, useTransformer, transformerType) };
if (body != null)
{
Webhooks[0].Request.BodyData = new BodyData
{
BodyAsString = body,
DetectedBodyType = BodyType.String,
DetectedBodyTypeFromContentType = BodyType.String
};
}
return this;
}
/// <see cref="IRespondWithAProvider.WithWebhook(string, string, IDictionary{string, WireMockList{string}}, object, bool, TransformerType)"/>
public IRespondWithAProvider WithWebhook(
[NotNull] string url,
[CanBeNull] string method = "post",
[CanBeNull] IDictionary<string, WireMockList<string>> headers = null,
[CanBeNull] object body = null,
bool useTransformer = true,
TransformerType transformerType = TransformerType.Handlebars)
{
Webhooks = new[] { InitWebhook(url, method, headers, useTransformer, transformerType) };
if (body != null)
{
Webhooks[0].Request.BodyData = new BodyData
{
BodyAsJson = body,
DetectedBodyType = BodyType.Json,
DetectedBodyTypeFromContentType = BodyType.Json
};
}
return this;
}
private IWebhook InitWebhook(
string url,
string method,
IDictionary<string, WireMockList<string>> headers,
bool useTransformer,
TransformerType transformerType)
{
return new Webhook
{
Request = new WebhookRequest
{
Url = url,
Method = method ?? "post",
Headers = headers,
UseTransformer = useTransformer,
TransformerType = transformerType
}
};
}
}
} }

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