mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-13 13:53:34 +01:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ce24249d0 | ||
|
|
7ca70309cb | ||
|
|
5d0bf6f4e1 | ||
|
|
f6e35cbe2d | ||
|
|
dc4c8d1dba | ||
|
|
adf1914877 | ||
|
|
1f1bc05f00 | ||
|
|
c107e38e3b | ||
|
|
a77c4fe1ac | ||
|
|
c1e71707c5 | ||
|
|
69499afe43 | ||
|
|
aadac78577 | ||
|
|
71393204cc | ||
|
|
e5cc6f570c | ||
|
|
7c3a0c815d | ||
|
|
e61f08fe48 | ||
|
|
11f4c47851 | ||
|
|
3956cd703b | ||
|
|
27682d0ce4 | ||
|
|
8444c8c506 | ||
|
|
6ef116a295 | ||
|
|
59195eaed8 | ||
|
|
7d9e450814 | ||
|
|
7019a5a78c | ||
|
|
d29f3e81f3 | ||
|
|
ccd8026884 | ||
|
|
1214ba5108 | ||
|
|
427715a38a | ||
|
|
d949dfb64c | ||
|
|
0a2763c06e | ||
|
|
9ef8bd0b7b | ||
|
|
090e0eb437 | ||
|
|
f3d52adbb2 | ||
|
|
a8775c3b77 | ||
|
|
3e24e3452b | ||
|
|
95bf8e31aa | ||
|
|
090989ea7f | ||
|
|
651486f718 | ||
|
|
9dea577da1 | ||
|
|
7ca4294de6 | ||
|
|
66245409f9 | ||
|
|
da6cb9fe0a | ||
|
|
b30e4faab6 | ||
|
|
1221d52c69 | ||
|
|
52d2109c7e | ||
|
|
30064b922b | ||
|
|
78b94d2ebc | ||
|
|
19701f5260 | ||
|
|
1269fb178f | ||
|
|
7426bf76ee | ||
|
|
36c9d95abb | ||
|
|
674fa89c3e | ||
|
|
61cdc13fae | ||
|
|
c344b73f45 |
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,3 +1,66 @@
|
||||
# 1.5.29 (22 June 2023)
|
||||
- [#954](https://github.com/WireMock-Net/WireMock.Net/pull/954) - Support setting WireMockServerSettings via Environment [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#955](https://github.com/WireMock-Net/WireMock.Net/pull/955) - Fix some SonarCloud issues [refactor] contributed by [StefH](https://github.com/StefH)
|
||||
- [#953](https://github.com/WireMock-Net/WireMock.Net/issues/953) - How to use environment variable [feature]
|
||||
|
||||
# 1.5.28 (11 June 2023)
|
||||
- [#948](https://github.com/WireMock-Net/WireMock.Net/pull/948) - WireMock.Net.Testcontainers [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#951](https://github.com/WireMock-Net/WireMock.Net/pull/951) - Allow setting the Content-Length header for a HTTP method HEAD [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.27 (03 June 2023)
|
||||
- [#946](https://github.com/WireMock-Net/WireMock.Net/pull/946) - Add warning logging when sending a request to a Webhook does not return status 200 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#949](https://github.com/WireMock-Net/WireMock.Net/pull/949) - Add ".NET Framework 4.7" to WireMock.Net.FluentAssertions [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#928](https://github.com/WireMock-Net/WireMock.Net/issues/928) - TypeLoadException when using WithHeader method. [bug]
|
||||
- [#945](https://github.com/WireMock-Net/WireMock.Net/issues/945) - Webhook logging [feature]
|
||||
|
||||
# 1.5.26 (25 May 2023)
|
||||
- [#938](https://github.com/WireMock-Net/WireMock.Net/pull/938) - Add more unitests for CSharpFormatter utils [test] contributed by [StefH](https://github.com/StefH)
|
||||
- [#939](https://github.com/WireMock-Net/WireMock.Net/pull/939) - WireMockMiddleware should use HandleRequestsSynchronously correctly [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#940](https://github.com/WireMock-Net/WireMock.Net/pull/940) - Code generator improvements contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||
- [#942](https://github.com/WireMock-Net/WireMock.Net/pull/942) - Add GetParameter method to IRequestMessage [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#941](https://github.com/WireMock-Net/WireMock.Net/issues/941) - RequestMessage.GetParameter method missing from IRequestMessage interface [feature]
|
||||
|
||||
# 1.5.25 (13 May 2023)
|
||||
- [#934](https://github.com/WireMock-Net/WireMock.Net/pull/934) - Code generator improvements [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||
|
||||
# 1.5.24 (07 May 2023)
|
||||
- [#926](https://github.com/WireMock-Net/WireMock.Net/pull/926) - Fix C# mapping code generator for header names [bug] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||
- [#927](https://github.com/WireMock-Net/WireMock.Net/pull/927) - Enrich generated code with status code [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||
- [#930](https://github.com/WireMock-Net/WireMock.Net/pull/930) - Update C# mapping code generator for WithStatusCode [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#931](https://github.com/WireMock-Net/WireMock.Net/pull/931) - Add property 'IsStartedWithAdminInterface' to 'IWireMockServer' [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#933](https://github.com/WireMock-Net/WireMock.Net/pull/933) - C# code generator improvements [feature] contributed by [cezarypiatek](https://github.com/cezarypiatek)
|
||||
|
||||
# 1.5.23 (23 April 2023)
|
||||
- [#922](https://github.com/WireMock-Net/WireMock.Net/pull/922) - Add WithProbability [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#924](https://github.com/WireMock-Net/WireMock.Net/pull/924) - Allow removal of prefix when proxying to another server (#630) [feature] contributed by [nudejustin](https://github.com/nudejustin)
|
||||
- [#925](https://github.com/WireMock-Net/WireMock.Net/pull/925) - Add IgnoreCase option to ProxyUrlReplaceSettings [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.22 (08 April 2023)
|
||||
- [#914](https://github.com/WireMock-Net/WireMock.Net/pull/914) - #912 add excluded params to proxy mapping [feature] contributed by [walidhaidarii](https://github.com/walidhaidarii)
|
||||
- [#916](https://github.com/WireMock-Net/WireMock.Net/pull/916) - Include WireMockOpenApiParser project [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#912](https://github.com/WireMock-Net/WireMock.Net/issues/912) - Feature: adding excluded params to proxy and records settings [feature]
|
||||
|
||||
# 1.5.21 (22 March 2023)
|
||||
- [#908](https://github.com/WireMock-Net/WireMock.Net/pull/908) - RequestBuilder : add WithBodyAsJson and WithBody (with IJsonConverter) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#911](https://github.com/WireMock-Net/WireMock.Net/pull/911) - Fixed QueryStringParser for UrlEncoded values [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#901](https://github.com/WireMock-Net/WireMock.Net/issues/901) - Matching one form-urlencoded value [feature]
|
||||
|
||||
# 1.5.20 (19 March 2023)
|
||||
- [#905](https://github.com/WireMock-Net/WireMock.Net/pull/905) - Add DeserializeFormUrl Encoded to the settings [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#907](https://github.com/WireMock-Net/WireMock.Net/pull/907) - Fix issue with application/x-www-form-urlencoded and ExactMatcher [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#906](https://github.com/WireMock-Net/WireMock.Net/issues/906) - Upgrade to 1.5.19 breaks a form data test [bug]
|
||||
|
||||
# 1.5.19 (17 March 2023)
|
||||
- [#903](https://github.com/WireMock-Net/WireMock.Net/pull/903) - Add WithBody with IDictionary (form-urlencoded values) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#904](https://github.com/WireMock-Net/WireMock.Net/pull/904) - Update Handlebars.Net.Helpers to 2.3.15 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.18 (09 March 2023)
|
||||
- [#893](https://github.com/WireMock-Net/WireMock.Net/pull/893) - Add 'Data' to response which can be used during transforming the response [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#896](https://github.com/WireMock-Net/WireMock.Net/pull/896) - Bump Microsoft.Owin from 2.0.2 to 4.2.2 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#900](https://github.com/WireMock-Net/WireMock.Net/pull/900) - ProxySettings : Add logic to not save some requests depending on HttpMethods [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#897](https://github.com/WireMock-Net/WireMock.Net/issues/897) - WebHostBuilder.ConfigureServices method not found when using nunit3testadapter 4.4.0 [bug]
|
||||
- [#899](https://github.com/WireMock-Net/WireMock.Net/issues/899) - Ignore OPTIONS request when using proxyandrecord [feature]
|
||||
|
||||
# 1.5.17 (25 February 2023)
|
||||
- [#881](https://github.com/WireMock-Net/WireMock.Net/pull/881) - Add WithBodyAsJson builder method with accepts a Func [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#882](https://github.com/WireMock-Net/WireMock.Net/pull/882) - Add example code to test HTTP Status 400 and 500 [test] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.5.17</VersionPrefix>
|
||||
<VersionPrefix>1.5.29</VersionPrefix>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
@@ -12,6 +12,7 @@
|
||||
<RepositoryType>git</RepositoryType>
|
||||
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
||||
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
|
||||
<LangVersion>Latest</LangVersion>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
@@ -29,7 +30,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
|
||||
<!--<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>-->
|
||||
<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>
|
||||
</ItemGroup>
|
||||
|
||||
<Choose>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=1.5.17
|
||||
SET version=1.5.29
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
55
PackageReadme.md
Normal file
55
PackageReadme.md
Normal file
@@ -0,0 +1,55 @@
|
||||
## WireMock.Net
|
||||
Lightweight Http Mocking Server for .NET, inspired by [WireMock(http://WireMock.org) from the Java landscape.
|
||||
|
||||
### :star: Key Features
|
||||
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||
* Library can be used in unit tests and integration tests
|
||||
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
|
||||
* Configurable via a fluent C# .NET API, JSON files and JSON over HTTP
|
||||
* Record/playback of stubs (proxying)
|
||||
* Per-request conditional proxying
|
||||
* Stateful behaviour simulation
|
||||
* Response templating / transformation using Handlebars and extensions
|
||||
* Can be used locally or in CI/CD scenarios
|
||||
|
||||
### :star: Stubbing
|
||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
||||
See [Wiki : Stubbing](https://github.com/WireMock-Net/WireMock.Net/wiki/Stubbing).
|
||||
|
||||
### :star: Request Matching
|
||||
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/Request-Matching).
|
||||
|
||||
### :star: Response Templating
|
||||
The response which is returned WireMock.Net can be changed using templating. This is described here [Wiki : Response Templating](https://github.com/WireMock-Net/WireMock.Net/wiki/Response-Templating).
|
||||
|
||||
### :star: Admin API Reference
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
|
||||
|
||||
### :star: Using
|
||||
WireMock.Net can be used in several ways:
|
||||
|
||||
#### UnitTesting
|
||||
You can use your favorite test framework and use WireMock within your tests, see
|
||||
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
||||
|
||||
#### As a dotnet tool
|
||||
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
|
||||
|
||||
#### As standalone process / console application
|
||||
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
||||
|
||||
#### As a Windows Service
|
||||
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
|
||||
|
||||
#### As a Web Job in Azure or application in IIS
|
||||
See this link [WireMock-as-a-(Azure)-Web-App](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-(Azure)-Web-App)
|
||||
|
||||
#### In a docker container
|
||||
There is also a Linux and Windows-Nano container available at [hub.docker.com](https://hub.docker.com/r/sheyenrath).
|
||||
For more details see also [Docker](https://github.com/WireMock-Net/WireMock.Net-docker).
|
||||
|
||||
#### HTTPS / SSL
|
||||
More details on using HTTPS (SSL) can be found here [Wiki : HTTPS](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-HTTPS-(SSL))
|
||||
|
||||
## :books: Documentation
|
||||
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||
@@ -1,6 +1,6 @@
|
||||
# 1.5.17 (25 February 2023)
|
||||
- #881 Add WithBodyAsJson builder method with accepts a Func [feature]
|
||||
- #882 Add example code to test HTTP Status 400 and 500 [test]
|
||||
- #890 AdminApiMappingBuilder [feature]
|
||||
# 1.5.29 (22 June 2023)
|
||||
- #954 Support setting WireMockServerSettings via Environment [feature]
|
||||
- #955 Fix some SonarCloud issues [refactor]
|
||||
- #953 How to use environment variable [feature]
|
||||
|
||||
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
||||
33
README.md
33
README.md
@@ -1,24 +1,28 @@
|
||||
# WireMock.Net
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock.org](http://WireMock.org).
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock](http://WireMock.org).
|
||||
|
||||
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/WireMock-Net/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||
|
||||
## Key Features
|
||||
## :star: Key Features
|
||||
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||
* Library can be used in unit tests and integration tests
|
||||
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
|
||||
* Configurable via a fluent DotNet API, JSON files and JSON over HTTP
|
||||
* Configurable via a fluent C# .NET API, JSON files and JSON over HTTP
|
||||
* Record/playback of stubs (proxying)
|
||||
* Per-request conditional proxying
|
||||
* Stateful behaviour simulation
|
||||
* Response templating / transformation using Handlebars and extensions
|
||||
* Can be used locally or in CI/CD scenarios
|
||||
|
||||
## Info
|
||||
## :memo: Blogs
|
||||
- [mStack.nl : Generate C# Code from Mapping(s)](https://mstack.nl/blog/20230201-wiremock.net-tocode/)
|
||||
|
||||
|
||||
## :computer: Project Info
|
||||
| | |
|
||||
| --- | --- |
|
||||
| ***Project*** | |
|
||||
| **Chat** | [](https://gitter.im/wiremock_dotnet/Lobby) |
|
||||
| **Chat** | [](https://slack.wiremock.org/) [](https://gitter.im/wiremock_dotnet/Lobby) |
|
||||
| **Issues** | [](https://github.com/WireMock-Net/WireMock.Net/issues) |
|
||||
| | |
|
||||
| ***Quality*** | |
|
||||
@@ -27,7 +31,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
||||
| **Sonar Bugs** | [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=BUG) [](https://sonarcloud.io/project/issues?id=WireMock-Net_WireMock.Net&resolved=false&types=CODE_SMELL) |
|
||||
| **Coverage** | [](https://sonarcloud.io/component_measures?id=WireMock-Net_WireMock.Net&metric=coverage) [](https://codecov.io/gh/WireMock-Net/WireMock.Net)|
|
||||
|
||||
### NuGet packages
|
||||
### :package: NuGet packages
|
||||
|
||||
| | Official | Preview [:information_source:](https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions) |
|
||||
| - | - | - |
|
||||
@@ -38,39 +42,42 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
||||
| **WireMock.Net.OpenApiParser** | [](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
|
||||
| **WireMock.Net.RestClient** | [](https://www.nuget.org/packages/WireMock.Net.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
|
||||
| **WireMock.Net.xUnit** | [](https://www.nuget.org/packages/WireMock.Net.xUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit)
|
||||
| **WireMock.Net.Testcontainers** | [](https://www.nuget.org/packages/WireMock.Net.Testcontainers) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Testcontainers)
|
||||
| **WireMock.Org.RestClient** | [](https://www.nuget.org/packages/WireMock.Org.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
|
||||
|
||||
|
||||
## Development
|
||||
## :memo: Development
|
||||
For the supported frameworks and build information, see [this](https://github.com/WireMock-Net/WireMock.Net/wiki/Development-Information) page.
|
||||
|
||||
## Stubbing
|
||||
## :star: Stubbing
|
||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
||||
See [Wiki : Stubbing](https://github.com/WireMock-Net/WireMock.Net/wiki/Stubbing).
|
||||
|
||||
## Request Matching
|
||||
## :star: Request Matching
|
||||
WireMock.Net support advanced request-matching logic, see [Wiki : Request Matching](https://github.com/WireMock-Net/WireMock.Net/wiki/Request-Matching).
|
||||
|
||||
## Response Templating
|
||||
## :star: Response Templating
|
||||
The response which is returned WireMock.Net can be changed using templating. This is described here [Wiki : Response Templating](https://github.com/WireMock-Net/WireMock.Net/wiki/Response-Templating).
|
||||
|
||||
## Admin API Reference
|
||||
## :star: Admin API Reference
|
||||
The WireMock admin API provides functionality to define the mappings via a http interface see [Wiki : Admin API Reference](https://github.com/StefH/WireMock.Net/wiki/Admin-API-Reference).
|
||||
|
||||
## Using
|
||||
## :star: Using
|
||||
WireMock.Net can be used in several ways:
|
||||
|
||||
### UnitTesting
|
||||
You can use your favorite test framework and use WireMock within your tests, see
|
||||
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
||||
|
||||
### Unit/Integration Testing using Testcontainers.DotNet
|
||||
You can use [Wiki : WireMock.Net.Testcontainers](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
|
||||
### As a dotnet tool
|
||||
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
|
||||
|
||||
### As standalone process / console application
|
||||
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
||||
|
||||
|
||||
### As a Windows Service
|
||||
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||
.github\FUNDING.yml = .github\FUNDING.yml
|
||||
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
|
||||
nuget.config = nuget.config
|
||||
PackageReadme.md = PackageReadme.md
|
||||
PackageReleaseNotes.template = PackageReleaseNotes.template
|
||||
PackageReleaseNotes.txt = PackageReleaseNotes.txt
|
||||
README.md = README.md
|
||||
@@ -110,6 +111,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample",
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Testcontainers", "src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj", "{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TestcontainersExample", "examples\WireMock.Net.TestcontainersExample\WireMock.Net.TestcontainersExample.csproj", "{56A38798-C48B-4A4A-B805-071E05C02CE1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -264,6 +269,14 @@ Global
|
||||
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -308,6 +321,8 @@ Global
|
||||
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{56A38798-C48B-4A4A-B805-071E05C02CE1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OPTIONS/@EntryIndexedValue">OPTIONS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue">OS</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PATCH/@EntryIndexedValue">PATCH</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=POST/@EntryIndexedValue">POST</s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PUT/@EntryIndexedValue">PUT</s:String>
|
||||
@@ -27,10 +28,12 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Guids/@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/=openapi/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pacticipant/@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/=Scriban/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=sheyenrath/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sigil/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=templated/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
|
||||
<!--<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />-->
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
||||
@@ -643,6 +643,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBodyAsJson(new { Id = "5bdf076c-5654-4b3e-842c-7caf1fabf8c9" }));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/random200or505").UsingGet())
|
||||
.RespondWith(Response.Create().WithCallback(request =>
|
||||
@@ -650,7 +651,11 @@ namespace WireMock.Net.ConsoleApplication
|
||||
int code = new Random().Next(1, 2) == 1 ? 505 : 200;
|
||||
return new ResponseMessage
|
||||
{
|
||||
BodyData = new BodyData { BodyAsString = "random200or505:" + code, DetectedBodyType = Types.BodyType.String },
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
|
||||
DetectedBodyType = Types.BodyType.String,
|
||||
},
|
||||
StatusCode = code
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -11,7 +11,7 @@ public class DynamicDataGeneration : WireMockOpenApiParserDynamicExampleValues
|
||||
get
|
||||
{
|
||||
// Since you have your Schema, you can get if max-length is set. You can generate accurate examples with this settings
|
||||
var maxLength = Schema.MaxLength ?? 9;
|
||||
var maxLength = Schema?.MaxLength ?? 9;
|
||||
|
||||
return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ class Program
|
||||
|
||||
private static void RunMockServerWithDynamicExampleGeneration()
|
||||
{
|
||||
//Run your mocking framework specifieing youur Example Values generator class.
|
||||
// Run your mocking framework specifying your Example Values generator class.
|
||||
var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), Types.ExampleValueType.Value, Types.ExampleValueType.Value);
|
||||
Console.WriteLine("Press any key to stop the servers");
|
||||
|
||||
@@ -27,15 +27,15 @@ class Program
|
||||
|
||||
private static void RunOthersOpenApiParserExample()
|
||||
{
|
||||
var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "https://localhost:9091/");
|
||||
var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "https://localhost:9092/");
|
||||
var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "https://localhost:9093/");
|
||||
var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "https://localhost:9094/");
|
||||
var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "https://localhost:9095/");
|
||||
var testopenapifile_json = Run.RunServer(Path.Combine(Folder, "testopenapifile.json"), "https://localhost:9096/");
|
||||
var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "https://localhost:9097/");
|
||||
var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "https://localhost:9098/");
|
||||
var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "https://localhost:9099/");
|
||||
var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "http://localhost:9091/");
|
||||
var serverPetstore_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.json"), "http://localhost:9092/");
|
||||
var serverPetstore_V2_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V2.0.yaml"), "http://localhost:9093/");
|
||||
var serverPetstore_V300_yaml = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.0.yaml"), "http://localhost:9094/");
|
||||
var serverPetstore_V302_json = Run.RunServer(Path.Combine(Folder, "Swagger_Petstore_V3.0.2.json"), "http://localhost:9095/");
|
||||
var testopenapifile_json = Run.RunServer(Path.Combine(Folder, "testopenapifile.json"), "http://localhost:9096/");
|
||||
var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "http://localhost:9097/");
|
||||
var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "http://localhost:9098/");
|
||||
var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "http://localhost:9099/");
|
||||
|
||||
testopenapifile_json
|
||||
.Given(Request.Create().WithPath("/x").UsingGet())
|
||||
|
||||
@@ -9,64 +9,70 @@ using WireMock.Net.OpenApiParser.Types;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Net.OpenApiParser.ConsoleApp
|
||||
namespace WireMock.Net.OpenApiParser.ConsoleApp;
|
||||
|
||||
public static class Run
|
||||
{
|
||||
public static class Run
|
||||
public static WireMockServer RunServer(
|
||||
string path,
|
||||
string url,
|
||||
bool dynamicExamples = true,
|
||||
IWireMockOpenApiParserExampleValues? examplesValuesGenerator = null,
|
||||
ExampleValueType pathPatternToUse = ExampleValueType.Wildcard,
|
||||
ExampleValueType headerPatternToUse = ExampleValueType.Wildcard
|
||||
)
|
||||
{
|
||||
public static WireMockServer RunServer(string path, string url, bool dynamicExamples = true, IWireMockOpenApiParserExampleValues examplesValuesGenerator = null, ExampleValueType pathPatternToUse = ExampleValueType.Wildcard, ExampleValueType headerPatternToUse = ExampleValueType.Wildcard)
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
AllowCSharpCodeMatcher = true,
|
||||
Urls = new[] { url },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true,
|
||||
WatchStaticMappings = false,
|
||||
WatchStaticMappingsInSubdirectories = false,
|
||||
Logger = new WireMockConsoleLogger(),
|
||||
SaveUnmatchedRequests = true
|
||||
});
|
||||
AllowCSharpCodeMatcher = true,
|
||||
Urls = new[] { url },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = true,
|
||||
WatchStaticMappings = false,
|
||||
WatchStaticMappingsInSubdirectories = false,
|
||||
Logger = new WireMockConsoleLogger(),
|
||||
SaveUnmatchedRequests = true
|
||||
});
|
||||
|
||||
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");
|
||||
|
||||
var settings = new WireMockOpenApiParserSettings
|
||||
{
|
||||
DynamicExamples = dynamicExamples,
|
||||
ExampleValues = examplesValuesGenerator,
|
||||
PathPatternToUse = pathPatternToUse,
|
||||
HeaderPatternToUse = headerPatternToUse,
|
||||
};
|
||||
|
||||
server.WithMappingFromOpenApiFile(path, settings, out var diag);
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
public static void RunServer(IEnumerable<MappingModel> mappings)
|
||||
var settings = new WireMockOpenApiParserSettings
|
||||
{
|
||||
string url1 = "http://localhost:9091/";
|
||||
DynamicExamples = dynamicExamples,
|
||||
ExampleValues = examplesValuesGenerator,
|
||||
PathPatternToUse = pathPatternToUse,
|
||||
HeaderPatternToUse = headerPatternToUse,
|
||||
};
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
AllowCSharpCodeMatcher = true,
|
||||
Urls = new[] { url1 },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = false,
|
||||
WatchStaticMappings = false,
|
||||
WatchStaticMappingsInSubdirectories = false,
|
||||
Logger = new WireMockConsoleLogger(),
|
||||
});
|
||||
Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
|
||||
server.WithMappingFromOpenApiFile(path, settings, out var diag);
|
||||
|
||||
server.SetBasicAuthentication("a", "b");
|
||||
return server;
|
||||
}
|
||||
|
||||
server.WithMapping(mappings.ToArray());
|
||||
public static void RunServer(IEnumerable<MappingModel> mappings)
|
||||
{
|
||||
string url1 = "http://localhost:9091/";
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
System.Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
AllowCSharpCodeMatcher = true,
|
||||
Urls = new[] { url1 },
|
||||
StartAdminInterface = true,
|
||||
ReadStaticMappings = false,
|
||||
WatchStaticMappings = false,
|
||||
WatchStaticMappingsInSubdirectories = false,
|
||||
Logger = new WireMockConsoleLogger(),
|
||||
});
|
||||
Console.WriteLine("WireMockServer listening at {0}", string.Join(",", server.Urls));
|
||||
|
||||
server.SetBasicAuthentication("a", "b");
|
||||
|
||||
server.WithMapping(mappings.ToArray());
|
||||
|
||||
Console.WriteLine("Press any key to stop the server");
|
||||
System.Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<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.OwinSelfHost" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
|
||||
|
||||
@@ -24,12 +24,12 @@ static class Program
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
await TestAsync().ConfigureAwait(false);
|
||||
return;
|
||||
//await TestAsync().ConfigureAwait(false);
|
||||
//return;
|
||||
|
||||
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||
|
||||
if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger()))
|
||||
if (!WireMockServerSettingsParser.TryParseArguments(args, Environment.GetEnvironmentVariables(), out var settings, new WireMockLog4NetLogger()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
using System;
|
||||
using System;
|
||||
using log4net;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Logging;
|
||||
|
||||
namespace WireMock.Net.StandAlone.NETCoreApp
|
||||
namespace WireMock.Net.StandAlone.NETCoreApp;
|
||||
|
||||
internal class WireMockLog4NetLogger : IWireMockLogger
|
||||
{
|
||||
internal class WireMockLog4NetLogger : IWireMockLogger
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
|
||||
public void Debug(string formatString, params object[] args)
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
Log.DebugFormat(formatString, args);
|
||||
}
|
||||
|
||||
public void Debug(string formatString, params object[] args)
|
||||
{
|
||||
Log.DebugFormat(formatString, args);
|
||||
}
|
||||
public void Info(string formatString, params object[] args)
|
||||
{
|
||||
Log.InfoFormat(formatString, args);
|
||||
}
|
||||
|
||||
public void Info(string formatString, params object[] args)
|
||||
{
|
||||
Log.InfoFormat(formatString, args);
|
||||
}
|
||||
public void Warn(string formatString, params object[] args)
|
||||
{
|
||||
Log.WarnFormat(formatString, args);
|
||||
}
|
||||
|
||||
public void Warn(string formatString, params object[] args)
|
||||
{
|
||||
Log.WarnFormat(formatString, args);
|
||||
}
|
||||
public void Error(string formatString, params object[] args)
|
||||
{
|
||||
Log.ErrorFormat(formatString, args);
|
||||
}
|
||||
|
||||
public void Error(string formatString, params object[] args)
|
||||
{
|
||||
Log.ErrorFormat(formatString, args);
|
||||
}
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
Log.Error(message, exception);
|
||||
}
|
||||
|
||||
public void Error(string message, Exception exception)
|
||||
{
|
||||
Log.Error(message, exception);
|
||||
}
|
||||
|
||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||
{
|
||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||
Log.DebugFormat("Admin[{0}] {1}", isAdminRequest, message);
|
||||
}
|
||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||
{
|
||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||
Log.DebugFormat("Admin[{0}] {1}", isAdminRequest, message);
|
||||
}
|
||||
}
|
||||
43
examples/WireMock.Net.TestcontainersExample/Program.cs
Normal file
43
examples/WireMock.Net.TestcontainersExample/Program.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
using Newtonsoft.Json;
|
||||
using Testcontainers.MsSql;
|
||||
using WireMock.Net.Testcontainers;
|
||||
|
||||
namespace WireMock.Net.TestcontainersExample;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
private static async Task Main(string[] args)
|
||||
{
|
||||
var container = new WireMockContainerBuilder()
|
||||
.WithAdminUserNameAndPassword("x", "y")
|
||||
.WithMappings(@"C:\Dev\GitHub\WireMock.Net\examples\WireMock.Net.Console.NET6\__admin\mappings")
|
||||
.WithWatchStaticMappings(true)
|
||||
.WithAutoRemove(true)
|
||||
.WithCleanUp(true)
|
||||
.Build();
|
||||
|
||||
await container.StartAsync().ConfigureAwait(false);
|
||||
|
||||
var logs = await container.GetLogsAsync(DateTime.Now.AddDays(-1)).ConfigureAwait(false);
|
||||
Console.WriteLine("logs = " + logs.Stdout);
|
||||
|
||||
var restEaseApiClient = container.CreateWireMockAdminClient();
|
||||
|
||||
var settings = await restEaseApiClient.GetSettingsAsync();
|
||||
Console.WriteLine("settings = " + JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||
|
||||
var mappings = await restEaseApiClient.GetMappingsAsync();
|
||||
Console.WriteLine("mappings = " + JsonConvert.SerializeObject(mappings, Formatting.Indented));
|
||||
|
||||
var client = container.CreateClient();
|
||||
var result = await client.GetStringAsync("/static/mapping");
|
||||
Console.WriteLine("result = " + result);
|
||||
|
||||
await container.StopAsync();
|
||||
|
||||
var sql = new MsSqlBuilder()
|
||||
.WithAutoRemove(true)
|
||||
.WithCleanUp(true)
|
||||
.Build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Testcontainers.MsSql" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="873d495f-940e-4b86-a1f4-4f0fc7be8b8b.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -19,12 +19,12 @@ public class CookieModel
|
||||
public IList<MatcherModel>? Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ignore case.
|
||||
/// Gets or sets the ignore case for the Cookie Name.
|
||||
/// </summary>
|
||||
public bool? IgnoreCase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reject on match.
|
||||
/// Gets or sets the Reject on match for the Cookie Name.
|
||||
/// </summary>
|
||||
public bool? RejectOnMatch { get; set; }
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
namespace WireMock.Admin.Mappings
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// Fault Model
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class FaultModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Fault Model
|
||||
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE or MALFORMED_RESPONSE_CHUNK.
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class FaultModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK or RANDOM_DATA_THEN_CLOSE.
|
||||
/// </summary>
|
||||
public string Type { get; set; }
|
||||
public string? Type { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fault percentage.
|
||||
/// </summary>
|
||||
public double? Percentage { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the fault percentage.
|
||||
/// </summary>
|
||||
public double? Percentage { get; set; }
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace WireMock.Admin.Mappings;
|
||||
public class HeaderModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the name.
|
||||
/// Gets or sets the name (key).
|
||||
/// </summary>
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
@@ -19,12 +19,12 @@ public class HeaderModel
|
||||
public IList<MatcherModel>? Matchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ignore case.
|
||||
/// Gets or sets the ignore case for the Header Key.
|
||||
/// </summary>
|
||||
public bool? IgnoreCase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Reject on match.
|
||||
/// Gets or sets the Reject on match for the Header Key.
|
||||
/// </summary>
|
||||
public bool? RejectOnMatch { get; set; }
|
||||
|
||||
|
||||
@@ -84,4 +84,18 @@ public class MappingModel
|
||||
/// Fire and forget for webhooks.
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
public double? Probability { get; set; }
|
||||
}
|
||||
@@ -1,121 +1,120 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
public object? StatusCode { get; set; }
|
||||
public object? StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string? BodyDestination { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string? BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string? Body { 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>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string? BodyAsFile { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string? BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public EncodingModel? BodyEncoding { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public EncodingModel? BodyEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use ResponseMessage Transformer.
|
||||
/// </summary>
|
||||
public bool? UseTransformer { 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; }
|
||||
/// <summary>
|
||||
/// Gets the type of the transformer.
|
||||
/// </summary>
|
||||
public string? TransformerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||
/// </summary>
|
||||
public bool? UseTransformerForBodyAsFile { get; set; }
|
||||
/// <summary>
|
||||
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||
/// </summary>
|
||||
public bool? UseTransformerForBodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
/// <summary>
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? Headers { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
public string? HeadersRaw { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
public string? HeadersRaw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
public string? ProxyUrl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
public string? ProxyUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fault.
|
||||
/// </summary>
|
||||
public FaultModel? Fault { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the fault.
|
||||
/// </summary>
|
||||
public FaultModel? Fault { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WebProxy settings.
|
||||
/// </summary>
|
||||
public WebProxyModel? WebProxy { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the WebProxy settings.
|
||||
/// </summary>
|
||||
public WebProxyModel? WebProxy { get; set; }
|
||||
}
|
||||
@@ -1,71 +1,76 @@
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ProxyAndRecordSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||
/// </summary>
|
||||
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the WebProxySettings.
|
||||
/// </summary>
|
||||
public WebProxySettingsModel WebProxySettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Proxy requests should follow redirection (30x).
|
||||
/// </summary>
|
||||
public bool? AllowAutoRedirect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to proxy.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response to the internal Mappings.
|
||||
/// </summary>
|
||||
public bool SaveMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
public bool SaveMappingToFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||
/// </summary>
|
||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedCookies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||
/// </summary>
|
||||
// public bool PreferProxyMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||
/// - <c>false</c>, the default matchers will be used.
|
||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||
///
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Append an unique GUID to the filename from the saved mapping file.
|
||||
/// </summary>
|
||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ProxyAndRecordSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
|
||||
/// </summary>
|
||||
public string ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the WebProxySettings.
|
||||
/// </summary>
|
||||
public WebProxySettingsModel WebProxySettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Proxy requests should follow redirection (30x).
|
||||
/// </summary>
|
||||
public bool? AllowAutoRedirect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL to proxy.
|
||||
/// </summary>
|
||||
public string Url { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response to the internal Mappings.
|
||||
/// </summary>
|
||||
public bool SaveMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
public bool SaveMappingToFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||
/// </summary>
|
||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||
/// </summary>
|
||||
public string[] ExcludedCookies { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||
/// </summary>
|
||||
// public bool PreferProxyMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
|
||||
/// - <c>false</c>, the default matchers will be used.
|
||||
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
|
||||
///
|
||||
/// Default value is false.
|
||||
/// </summary>
|
||||
public bool UseDefinedRequestMatchers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Append an unique GUID to the filename from the saved mapping file.
|
||||
/// </summary>
|
||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines the Replace Settings
|
||||
/// </summary>
|
||||
public ProxyUrlReplaceSettingsModel? ReplaceSettings { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// Defines an old path param and a new path param to be replaced when proxying.
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ProxyUrlReplaceSettingsModel
|
||||
{
|
||||
/// <summary>
|
||||
/// The old path value to be replaced by the new path value
|
||||
/// </summary>
|
||||
public string OldValue { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The new path value to replace the old value with
|
||||
/// </summary>
|
||||
public string NewValue { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the case should be ignore when replacing.
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; set; }
|
||||
}
|
||||
@@ -31,27 +31,49 @@ public class SettingsModel
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow a Body for all HTTP Methods. (default set to false).
|
||||
/// Allow a Body for all HTTP Methods. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? AllowBodyForAllHttpMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to false).
|
||||
/// Allow only a HttpStatus Code in the response which is defined. (default set to <c>false</c>).
|
||||
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
|
||||
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
|
||||
/// </summary>
|
||||
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable Json deserialization when processing requests. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DisableJsonBodyParsing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disable support for GZip and Deflate request body decompression. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DisableRequestBodyDecompressing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable FormUrlEncoded deserializing when processing requests. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DisableDeserializeFormUrlEncoded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? HandleRequestsSynchronously { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Throw an exception when the Matcher fails because of invalid input. (default set to false).
|
||||
/// Throw an exception when the Matcher fails because of invalid input. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to true).
|
||||
/// Use the RegexExtended instead of the default <see cref="Regex"/>. (default set to <c>true</c>).
|
||||
/// </summary>
|
||||
public bool? UseRegexExtended { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to false).
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/>. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? SaveUnmatchedRequests { get; set; }
|
||||
|
||||
@@ -86,7 +108,7 @@ public class SettingsModel
|
||||
public HostingScheme? HostingScheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to <c>false</c>).
|
||||
/// </summary>
|
||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||
|
||||
|
||||
@@ -143,6 +143,14 @@ public interface IRequestMessage
|
||||
/// </summary>
|
||||
string Origin { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Get a query parameter.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||
/// <returns>The query parameter value as WireMockList or null when not found.</returns>
|
||||
WireMockList<string>? GetParameter(string key, bool ignoreCase = false);
|
||||
|
||||
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||
/// <summary>
|
||||
/// Gets the connection's client certificate
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface IResponseMessage
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
object? StatusCode { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the header.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -38,6 +39,11 @@ public interface IBodyData
|
||||
/// </summary>
|
||||
string? BodyAsString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The body as Form UrlEncoded dictionary.
|
||||
/// </summary>
|
||||
IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The detected body type (detection based on body content).
|
||||
/// </summary>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
namespace WireMock.ResponseBuilders
|
||||
// ReSharper disable InconsistentNaming
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The FaultType enumeration
|
||||
/// </summary>
|
||||
public enum FaultType
|
||||
{
|
||||
/// <summary>
|
||||
/// The FaultType enumeration
|
||||
/// No Fault
|
||||
/// </summary>
|
||||
public enum FaultType
|
||||
{
|
||||
/// <summary>
|
||||
/// No Fault
|
||||
/// </summary>
|
||||
NONE,
|
||||
NONE,
|
||||
|
||||
/// <summary>
|
||||
/// Return a completely empty response.
|
||||
/// </summary>
|
||||
EMPTY_RESPONSE,
|
||||
/// <summary>
|
||||
/// Return a completely empty response.
|
||||
/// </summary>
|
||||
EMPTY_RESPONSE,
|
||||
|
||||
/// <summary>
|
||||
/// Send a defined status header, then garbage, then close the connection.
|
||||
/// </summary>
|
||||
MALFORMED_RESPONSE_CHUNK
|
||||
}
|
||||
/// <summary>
|
||||
/// Send a defined status header, then garbage, then close the connection.
|
||||
/// </summary>
|
||||
MALFORMED_RESPONSE_CHUNK
|
||||
}
|
||||
@@ -17,6 +17,11 @@ public interface IWireMockServer : IDisposable
|
||||
/// </summary>
|
||||
bool IsStarted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this server is started with the admin interface enabled.
|
||||
/// </summary>
|
||||
bool IsStartedWithAdminInterface { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request logs.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,38 +1,42 @@
|
||||
namespace WireMock.Types
|
||||
namespace WireMock.Types;
|
||||
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// No body present
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// No body present
|
||||
/// </summary>
|
||||
None,
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File,
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a MultiPart
|
||||
/// </summary>
|
||||
MultiPart
|
||||
}
|
||||
/// <summary>
|
||||
/// Body is a MultiPart
|
||||
/// </summary>
|
||||
MultiPart,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String which is x-www-form-urlencoded.
|
||||
/// </summary>
|
||||
FormUrlEncoded
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
|
||||
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
|
||||
<PackageReference Include="FluentBuilder" Version="0.7.0">
|
||||
<PackageReference Include="FluentBuilder" Version="0.9.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,49 +1,49 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
||||
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net451;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
||||
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
||||
<PackageTags>wiremock;FluentAssertions;UnitTest;Assert;Assertions</PackageTags>
|
||||
<RootNamespace>WireMock.FluentAssertions</RootNamespace>
|
||||
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A95}</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>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
||||
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net451;net47;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
||||
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
||||
<PackageTags>wiremock;FluentAssertions;UnitTest;Assert;Assertions</PackageTags>
|
||||
<RootNamespace>WireMock.FluentAssertions</RootNamespace>
|
||||
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A95}</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>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" PrivateAssets="All" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'netstandard1.3'">
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'netstandard1.3'">
|
||||
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'netstandard1.3'">
|
||||
<PackageReference Include="FluentAssertions" Version="6.5.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'netstandard1.3'">
|
||||
<PackageReference Include="FluentAssertions" Version="6.5.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -31,13 +31,13 @@ public static class WireMockServerExtensions
|
||||
/// </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>
|
||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</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));
|
||||
Guard.NotNull(server);
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
||||
|
||||
@@ -80,9 +80,9 @@ public static class WireMockServerExtensions
|
||||
/// </summary>
|
||||
/// <param name="server">The WireMockServer instance</param>
|
||||
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
||||
/// <param name="settings">Additional settings [optional]</param>
|
||||
/// <param name="settings">Additional settings [optional].</param>
|
||||
[PublicAPI]
|
||||
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings)
|
||||
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
|
||||
{
|
||||
Guard.NotNull(server);
|
||||
Guard.NotNull(document);
|
||||
|
||||
@@ -5,53 +5,69 @@ using Microsoft.OpenApi.Readers;
|
||||
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>
|
||||
public interface IWireMockOpenApiParser
|
||||
{
|
||||
/// <summary>
|
||||
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
|
||||
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a file-path.
|
||||
/// </summary>
|
||||
public interface IWireMockOpenApiParser
|
||||
{
|
||||
/// <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="diagnostic">OpenApiDiagnostic output</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic);
|
||||
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
IReadOnlyList<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="IReadOnlyList{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>
|
||||
IReadOnlyList<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="IReadOnlyList{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
|
||||
/// </summary>
|
||||
/// <param name="document">The source OpenApiDocument</param>
|
||||
/// <param name="settings">Additional settings [optional]</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
IReadOnlyList<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="IReadOnlyList{MappingModel}"/> from a <seealso cref="Stream"/>.
|
||||
/// </summary>
|
||||
/// <param name="stream">The source stream</param>
|
||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
IReadOnlyList<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);
|
||||
}
|
||||
/// <summary>
|
||||
/// Generate <see cref="IReadOnlyList{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>
|
||||
IReadOnlyList<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
||||
|
||||
/// <summary>
|
||||
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="string"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">The source text</param>
|
||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
IReadOnlyList<MappingModel> FromText(string text, out OpenApiDiagnostic diagnostic);
|
||||
|
||||
/// <summary>
|
||||
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="string"/>.
|
||||
/// </summary>
|
||||
/// <param name="text">The source text</param>
|
||||
/// <param name="settings">Additional settings</param>
|
||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
IReadOnlyList<MappingModel> FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
||||
}
|
||||
@@ -31,19 +31,29 @@ internal class OpenApiPathsMapper
|
||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
||||
}
|
||||
|
||||
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths, IList<OpenApiServer> servers)
|
||||
public IReadOnlyList<MappingModel> ToMappingModels(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
||||
{
|
||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
||||
return paths?
|
||||
.OrderBy(p => p.Key)
|
||||
.Select(p => MapPath(p.Key, p.Value, servers))
|
||||
.SelectMany(x => x)
|
||||
.ToArray() ??
|
||||
Array.Empty<MappingModel>();
|
||||
}
|
||||
|
||||
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths, IList<OpenApiServer> servers)
|
||||
private IReadOnlyList<MappingModel> MapPaths(OpenApiPaths? paths, IList<OpenApiServer> servers)
|
||||
{
|
||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
||||
return paths?
|
||||
.OrderBy(p => p.Key)
|
||||
.Select(p => MapPath(p.Key, p.Value, servers))
|
||||
.SelectMany(x => x)
|
||||
.ToArray() ??
|
||||
Array.Empty<MappingModel>();
|
||||
}
|
||||
|
||||
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||
private IReadOnlyList<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||
{
|
||||
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
|
||||
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray();
|
||||
}
|
||||
|
||||
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
||||
@@ -123,7 +133,7 @@ internal class OpenApiPathsMapper
|
||||
};
|
||||
}
|
||||
|
||||
private bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
|
||||
private static bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
|
||||
{
|
||||
openApiMediaType = null;
|
||||
contentType = null;
|
||||
@@ -305,16 +315,16 @@ internal class OpenApiPathsMapper
|
||||
return JObject.Parse(outputString.ToString());
|
||||
}
|
||||
|
||||
private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
|
||||
private IDictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, OpenApiHeader>? headers)
|
||||
{
|
||||
var mappedHeaders = headers.ToDictionary(
|
||||
var mappedHeaders = headers?.ToDictionary(
|
||||
item => item.Key,
|
||||
_ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
|
||||
);
|
||||
_ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!
|
||||
) ?? new Dictionary<string, object>();
|
||||
|
||||
if (!string.IsNullOrEmpty(responseContentType))
|
||||
{
|
||||
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
||||
mappedHeaders.TryAdd(HeaderContentType, responseContentType!);
|
||||
}
|
||||
|
||||
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
||||
@@ -360,9 +370,18 @@ internal class OpenApiPathsMapper
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues },
|
||||
ExampleValueType.Value => new MatcherModel
|
||||
{
|
||||
Name = "ExactMatcher",
|
||||
Pattern = GetExampleValueAsStringForSchemaType(schema),
|
||||
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||
},
|
||||
|
||||
_ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" }
|
||||
_ => new MatcherModel
|
||||
{
|
||||
Name = "WildcardMatcher",
|
||||
Pattern = "*"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -36,5 +36,5 @@ public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleV
|
||||
public virtual string String { get; set; } = "example-string";
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
|
||||
public virtual OpenApiSchema? Schema { get; set; } = new();
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.OpenApi.Any;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Stef.Validation;
|
||||
@@ -11,116 +10,108 @@ namespace WireMock.Net.OpenApiParser.Utils;
|
||||
|
||||
internal class ExampleValueGenerator
|
||||
{
|
||||
private readonly WireMockOpenApiParserSettings _settings;
|
||||
private readonly IWireMockOpenApiParserExampleValues _exampleValues;
|
||||
|
||||
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
Guard.NotNull(settings);
|
||||
|
||||
// Check if user provided an own implementation
|
||||
if (settings.ExampleValues is null)
|
||||
{
|
||||
if (_settings.DynamicExamples)
|
||||
if (settings.DynamicExamples)
|
||||
{
|
||||
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
||||
_exampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
||||
}
|
||||
else
|
||||
{
|
||||
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
|
||||
_exampleValues = new WireMockOpenApiParserExampleValues();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_exampleValues = settings.ExampleValues;
|
||||
}
|
||||
}
|
||||
|
||||
public object GetExampleValue(OpenApiSchema? schema)
|
||||
{
|
||||
var schemaExample = schema?.Example;
|
||||
var schemaEnum = GetRandomEnumValue(schema?.Enum);
|
||||
var schemaEnum = schema?.Enum?.FirstOrDefault();
|
||||
|
||||
_settings.ExampleValues.Schema = schema;
|
||||
_exampleValues.Schema = schema;
|
||||
|
||||
switch (schema?.GetSchemaType())
|
||||
{
|
||||
case SchemaType.Boolean:
|
||||
var exampleBoolean = schemaExample as OpenApiBoolean;
|
||||
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
|
||||
return exampleBoolean?.Value ?? _exampleValues.Boolean;
|
||||
|
||||
case SchemaType.Integer:
|
||||
switch (schema?.GetSchemaFormat())
|
||||
{
|
||||
case SchemaFormat.Int64:
|
||||
var exampleLong = (OpenApiLong)schemaExample;
|
||||
var enumLong = (OpenApiLong)schemaEnum;
|
||||
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
|
||||
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
|
||||
var exampleLong = schemaExample as OpenApiLong;
|
||||
var enumLong = schemaEnum as OpenApiLong;
|
||||
var valueLongEnumOrExample = enumLong?.Value ?? exampleLong?.Value;
|
||||
return valueLongEnumOrExample ?? _exampleValues.Integer;
|
||||
|
||||
default:
|
||||
var exampleInteger = (OpenApiInteger)schemaExample;
|
||||
var enumInteger = (OpenApiInteger)schemaEnum;
|
||||
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
|
||||
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
|
||||
var exampleInteger = schemaExample as OpenApiInteger;
|
||||
var enumInteger = schemaEnum as OpenApiInteger;
|
||||
var valueIntegerEnumOrExample = enumInteger?.Value ?? exampleInteger?.Value;
|
||||
return valueIntegerEnumOrExample ?? _exampleValues.Integer;
|
||||
}
|
||||
|
||||
case SchemaType.Number:
|
||||
switch (schema?.GetSchemaFormat())
|
||||
{
|
||||
case SchemaFormat.Float:
|
||||
var exampleFloat = (OpenApiFloat)schemaExample;
|
||||
var enumFloat = (OpenApiFloat)schemaEnum;
|
||||
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
|
||||
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
|
||||
var exampleFloat = schemaExample as OpenApiFloat;
|
||||
var enumFloat = schemaEnum as OpenApiFloat;
|
||||
var valueFloatEnumOrExample = enumFloat?.Value ?? exampleFloat?.Value;
|
||||
return valueFloatEnumOrExample ?? _exampleValues.Float;
|
||||
|
||||
default:
|
||||
var exampleDouble = (OpenApiDouble)schemaExample;
|
||||
var enumDouble = (OpenApiDouble)schemaEnum;
|
||||
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
|
||||
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
|
||||
var exampleDouble = schemaExample as OpenApiDouble;
|
||||
var enumDouble = schemaEnum as OpenApiDouble;
|
||||
var valueDoubleEnumOrExample = enumDouble?.Value ?? exampleDouble?.Value;
|
||||
return valueDoubleEnumOrExample ?? _exampleValues.Double;
|
||||
}
|
||||
|
||||
default:
|
||||
switch (schema?.GetSchemaFormat())
|
||||
{
|
||||
case SchemaFormat.Date:
|
||||
var exampleDate = (OpenApiDate)schemaExample;
|
||||
var enumDate = (OpenApiDate)schemaEnum;
|
||||
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
|
||||
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
|
||||
var exampleDate = schemaExample as OpenApiDate;
|
||||
var enumDate = schemaEnum as OpenApiDate;
|
||||
var valueDateEnumOrExample = enumDate?.Value ?? exampleDate?.Value;
|
||||
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _exampleValues.Date());
|
||||
|
||||
case SchemaFormat.DateTime:
|
||||
var exampleDateTime = (OpenApiDateTime)schemaExample;
|
||||
var enumDateTime = (OpenApiDateTime)schemaEnum;
|
||||
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
|
||||
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
|
||||
var exampleDateTime = schemaExample as OpenApiDateTime;
|
||||
var enumDateTime = schemaEnum as OpenApiDateTime;
|
||||
var valueDateTimeEnumOrExample = enumDateTime?.Value ?? exampleDateTime?.Value;
|
||||
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _exampleValues.DateTime());
|
||||
|
||||
case SchemaFormat.Byte:
|
||||
var exampleByte = (OpenApiByte)schemaExample;
|
||||
var enumByte = (OpenApiByte)schemaEnum;
|
||||
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
|
||||
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
|
||||
var exampleByte = schemaExample as OpenApiByte;
|
||||
var enumByte = schemaEnum as OpenApiByte;
|
||||
var valueByteEnumOrExample = enumByte?.Value ?? exampleByte?.Value;
|
||||
return valueByteEnumOrExample ?? _exampleValues.Bytes;
|
||||
|
||||
case SchemaFormat.Binary:
|
||||
var exampleBinary = (OpenApiBinary)schemaExample;
|
||||
var enumBinary = (OpenApiBinary)schemaEnum;
|
||||
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
|
||||
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
|
||||
var exampleBinary = schemaExample as OpenApiBinary;
|
||||
var enumBinary = schemaEnum as OpenApiBinary;
|
||||
var valueBinaryEnumOrExample = enumBinary?.Value ?? exampleBinary?.Value;
|
||||
return valueBinaryEnumOrExample ?? _exampleValues.Object;
|
||||
|
||||
default:
|
||||
var exampleString = (OpenApiString)schemaExample;
|
||||
var enumString = (OpenApiString)schemaEnum;
|
||||
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
|
||||
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
|
||||
var exampleString = schemaExample as OpenApiString;
|
||||
var enumString = schemaEnum as OpenApiString;
|
||||
var valueStringEnumOrExample = enumString?.Value ?? exampleString?.Value;
|
||||
return valueStringEnumOrExample ?? _exampleValues.String;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IOpenApiAny? GetRandomEnumValue(IList<IOpenApiAny>? schemaEnum)
|
||||
{
|
||||
if (schemaEnum?.Count > 0)
|
||||
{
|
||||
int maxValue = schemaEnum.Count - 1;
|
||||
int randomEnum = new Random().Next(0, maxValue);
|
||||
return schemaEnum[randomEnum];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<Description>An OpenApi (swagger) parser to generate MappingModel or mapping.json file.</Description>
|
||||
<TargetFrameworks>net46;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;openapi;OAS;converter;parser;openapiparser</PackageTags>
|
||||
<PackageTags>wiremock;openapi;OAS;raml;converter;parser;openapiparser</PackageTags>
|
||||
<ProjectGuid>{D3804228-91F4-4502-9595-39584E5AADAD}</ProjectGuid>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi.Readers;
|
||||
@@ -16,18 +17,18 @@ namespace WireMock.Net.OpenApiParser;
|
||||
/// </summary>
|
||||
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
||||
{
|
||||
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
||||
private readonly OpenApiStreamReader _reader = new();
|
||||
|
||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||
public IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||
{
|
||||
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||
public IReadOnlyList<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||
{
|
||||
OpenApiDocument document;
|
||||
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -44,24 +45,38 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
|
||||
return FromDocument(document, settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
||||
public IReadOnlyList<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
|
||||
{
|
||||
return new OpenApiPathsMapper(settings ?? new WireMockOpenApiParserSettings()).ToMappingModels(document.Paths, document.Servers);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IReadOnlyList<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
||||
{
|
||||
return FromDocument(_reader.Read(stream, out diagnostic));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||
public IReadOnlyList<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||
{
|
||||
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
|
||||
public IReadOnlyList<MappingModel> FromText(string text, out OpenApiDiagnostic diagnostic)
|
||||
{
|
||||
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
||||
return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), out diagnostic);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IReadOnlyList<MappingModel> FromText(string text, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||
{
|
||||
return FromStream(new MemoryStream(Encoding.UTF8.GetBytes(text)), settings, out diagnostic);
|
||||
}
|
||||
}
|
||||
@@ -280,4 +280,20 @@ public interface IWireMockAdminApi
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Head("files/{filename}")]
|
||||
Task FileExistsAsync([Path] string filename, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Convert an OpenApi / RAML document to mappings.
|
||||
/// </summary>
|
||||
/// <param name="text">The OpenApi or RAML document as text.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("openapi/convert")]
|
||||
Task<IReadOnlyList<MappingModel>> OpenApiConvertAsync([Body] string text, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Convert an OpenApi / RAML document to mappings and save these.
|
||||
/// </summary>
|
||||
/// <param name="text">The OpenApi or RAML document as text.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("openapi/save")]
|
||||
Task<StatusModel> OpenApiSaveAsync([Body] string text, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@@ -24,7 +25,7 @@ public static class StandAloneApp
|
||||
[PublicAPI]
|
||||
public static WireMockServer Start(WireMockServerSettings settings)
|
||||
{
|
||||
Guard.NotNull(settings, nameof(settings));
|
||||
Guard.NotNull(settings);
|
||||
|
||||
var server = WireMockServer.Start(settings);
|
||||
|
||||
@@ -42,7 +43,7 @@ public static class StandAloneApp
|
||||
[PublicAPI]
|
||||
public static WireMockServer Start(string[] args, IWireMockLogger? logger = null)
|
||||
{
|
||||
Guard.NotNull(args, nameof(args));
|
||||
Guard.NotNull(args);
|
||||
|
||||
if (TryStart(args, out var server, logger))
|
||||
{
|
||||
@@ -61,9 +62,9 @@ public static class StandAloneApp
|
||||
[PublicAPI]
|
||||
public static bool TryStart(string[] args, [NotNullWhen(true)] out WireMockServer? server, IWireMockLogger? logger = null)
|
||||
{
|
||||
Guard.NotNull(args, nameof(args));
|
||||
Guard.NotNull(args);
|
||||
|
||||
if (WireMockServerSettingsParser.TryParseArguments(args, out var settings, logger))
|
||||
if (WireMockServerSettingsParser.TryParseArguments(args, Environment.GetEnvironmentVariables(), out var settings, logger))
|
||||
{
|
||||
settings.Logger?.Info("Version [{0}]", Version);
|
||||
settings.Logger?.Debug("Server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
|
||||
|
||||
7
src/WireMock.Net.Testcontainers/Models/ContainerInfo.cs
Normal file
7
src/WireMock.Net.Testcontainers/Models/ContainerInfo.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
namespace WireMock.Net.Testcontainers.Models;
|
||||
|
||||
internal record ContainerInfo
|
||||
(
|
||||
string Image,
|
||||
string MappingsPath
|
||||
);
|
||||
@@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>A fluent testcontainer builder for the Docker version of WireMock.Net</Description>
|
||||
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;docker;testcontainer;testcontainers</PackageTags>
|
||||
<ProjectGuid>{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}</ProjectGuid>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||
<PackageReference Include="Testcontainers" Version="3.2.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" VersionOverride="2022.3.1" PrivateAssets="All" Version="2022.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
66
src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
Normal file
66
src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using Docker.DotNet.Models;
|
||||
using DotNet.Testcontainers.Builders;
|
||||
using DotNet.Testcontainers.Configurations;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Net.Testcontainers;
|
||||
|
||||
/// <inheritdoc cref="ContainerConfiguration" />
|
||||
[PublicAPI]
|
||||
public sealed class WireMockConfiguration : ContainerConfiguration
|
||||
{
|
||||
#pragma warning disable CS1591
|
||||
public string? Username { get; }
|
||||
|
||||
public string? Password { get; }
|
||||
|
||||
public bool HasBasicAuthentication => !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password);
|
||||
|
||||
public WireMockConfiguration(
|
||||
string? username = null,
|
||||
string? password = null
|
||||
)
|
||||
{
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
#pragma warning restore CS1591
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||
/// </summary>
|
||||
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
|
||||
public WireMockConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) : base(resourceConfiguration)
|
||||
{
|
||||
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||
/// </summary>
|
||||
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
|
||||
public WireMockConfiguration(IContainerConfiguration resourceConfiguration) : base(resourceConfiguration)
|
||||
{
|
||||
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||
/// </summary>
|
||||
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
|
||||
public WireMockConfiguration(WireMockConfiguration resourceConfiguration) : this(new WireMockConfiguration(), resourceConfiguration)
|
||||
{
|
||||
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||
/// </summary>
|
||||
/// <param name="oldValue">The old Docker resource configuration.</param>
|
||||
/// <param name="newValue">The new Docker resource configuration.</param>
|
||||
public WireMockConfiguration(WireMockConfiguration oldValue, WireMockConfiguration newValue) : base(oldValue, newValue)
|
||||
{
|
||||
Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username);
|
||||
Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
|
||||
}
|
||||
}
|
||||
110
src/WireMock.Net.Testcontainers/WireMockContainer.cs
Normal file
110
src/WireMock.Net.Testcontainers/WireMockContainer.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using DotNet.Testcontainers.Containers;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RestEase;
|
||||
using Stef.Validation;
|
||||
using WireMock.Client;
|
||||
|
||||
namespace WireMock.Net.Testcontainers;
|
||||
|
||||
/// <summary>
|
||||
/// A container for running WireMock in a docker environment.
|
||||
/// </summary>
|
||||
public sealed class WireMockContainer : DockerContainer
|
||||
{
|
||||
internal const int ContainerPort = 80;
|
||||
|
||||
private readonly WireMockConfiguration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockContainer" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The container configuration.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
public WireMockContainer(WireMockConfiguration configuration, ILogger logger) : base(configuration, logger)
|
||||
{
|
||||
_configuration = Guard.NotNull(configuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the public Url.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string GetPublicUrl() => GetPublicUri().ToString();
|
||||
|
||||
/// <summary>
|
||||
/// Create a RestEase Admin client which can be used to call the admin REST endpoint.
|
||||
/// </summary>
|
||||
/// <returns>A <see cref="IWireMockAdminApi"/></returns>
|
||||
[PublicAPI]
|
||||
public IWireMockAdminApi CreateWireMockAdminClient()
|
||||
{
|
||||
ValidateIfRunning();
|
||||
|
||||
var api = RestClient.For<IWireMockAdminApi>(GetPublicUri());
|
||||
|
||||
if (_configuration.HasBasicAuthentication)
|
||||
{
|
||||
api.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_configuration.Username}:{_configuration.Password}")));
|
||||
}
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="HttpClient"/> which can be used to call this instance.
|
||||
/// <param name="handlers">
|
||||
/// An ordered list of System.Net.Http.DelegatingHandler instances to be invoked
|
||||
/// as an System.Net.Http.HttpRequestMessage travels from the System.Net.Http.HttpClient
|
||||
/// to the network and an System.Net.Http.HttpResponseMessage travels from the network
|
||||
/// back to System.Net.Http.HttpClient. The handlers are invoked in a top-down fashion.
|
||||
/// That is, the first entry is invoked first for an outbound request message but
|
||||
/// last for an inbound response message.
|
||||
/// </param>
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public HttpClient CreateClient(params DelegatingHandler[] handlers)
|
||||
{
|
||||
ValidateIfRunning();
|
||||
|
||||
var client = HttpClientFactory.Create(handlers);
|
||||
client.BaseAddress = GetPublicUri();
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="HttpClient"/> (one for each URL) which can be used to call this instance.
|
||||
/// <param name="innerHandler">The inner handler represents the destination of the HTTP message channel.</param>
|
||||
/// <param name="handlers">
|
||||
/// An ordered list of System.Net.Http.DelegatingHandler instances to be invoked
|
||||
/// as an System.Net.Http.HttpRequestMessage travels from the System.Net.Http.HttpClient
|
||||
/// to the network and an System.Net.Http.HttpResponseMessage travels from the network
|
||||
/// back to System.Net.Http.HttpClient. The handlers are invoked in a top-down fashion.
|
||||
/// That is, the first entry is invoked first for an outbound request message but
|
||||
/// last for an inbound response message.
|
||||
/// </param>
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public HttpClient CreateClient(HttpMessageHandler innerHandler, params DelegatingHandler[] handlers)
|
||||
{
|
||||
ValidateIfRunning();
|
||||
|
||||
var client = HttpClientFactory.Create(innerHandler, handlers);
|
||||
client.BaseAddress = GetPublicUri();
|
||||
return client;
|
||||
}
|
||||
|
||||
private void ValidateIfRunning()
|
||||
{
|
||||
if (State != TestcontainersStates.Running)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to create HttpClient because the WireMock.Net is not yet running.");
|
||||
}
|
||||
}
|
||||
|
||||
private Uri GetPublicUri() => new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(ContainerPort)).Uri;
|
||||
}
|
||||
175
src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
Normal file
175
src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Docker.DotNet.Models;
|
||||
using DotNet.Testcontainers.Builders;
|
||||
using DotNet.Testcontainers.Configurations;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Net.Testcontainers.Models;
|
||||
|
||||
namespace WireMock.Net.Testcontainers;
|
||||
|
||||
/// <summary>
|
||||
/// An specific fluent Docker container builder for WireMock.Net
|
||||
/// </summary>
|
||||
public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContainerBuilder, WireMockContainer, WireMockConfiguration>
|
||||
{
|
||||
private readonly Dictionary<bool, ContainerInfo> _info = new()
|
||||
{
|
||||
{ false, new ContainerInfo("sheyenrath/wiremock.net:latest", "/app/__admin/mappings") },
|
||||
{ true, new ContainerInfo("sheyenrath/wiremock.net-windows:latest", @"c:\app\__admin\mappings") }
|
||||
};
|
||||
|
||||
private const string DefaultLogger = "WireMockConsoleLogger";
|
||||
|
||||
private readonly Lazy<Task<bool>> _isWindowsAsLazy = new(async () =>
|
||||
{
|
||||
using var dockerClientConfig = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
|
||||
using var dockerClient = dockerClientConfig.CreateClient();
|
||||
|
||||
var version = await dockerClient.System.GetVersionAsync();
|
||||
return version.Os.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) > -1;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ContainerBuilder" /> class.
|
||||
/// </summary>
|
||||
public WireMockContainerBuilder() : this(new WireMockConfiguration())
|
||||
{
|
||||
DockerResourceConfiguration = Init().DockerResourceConfiguration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Automatically set the correct image (Linux or Windows) for WireMock which to create the container.
|
||||
/// </summary>
|
||||
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||
[PublicAPI]
|
||||
public WireMockContainerBuilder WithImage()
|
||||
{
|
||||
var isWindows = _isWindowsAsLazy.Value.GetAwaiter().GetResult();
|
||||
return WithImage(_info[isWindows].Image);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the admin username and password for the container (basic authentication).
|
||||
/// </summary>
|
||||
/// <param name="username">The admin username.</param>
|
||||
/// <param name="password">The admin password.</param>
|
||||
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||
public WireMockContainerBuilder WithAdminUserNameAndPassword(string username, string password)
|
||||
{
|
||||
Guard.NotNull(username);
|
||||
Guard.NotNull(password);
|
||||
|
||||
if (string.IsNullOrEmpty(username) && string.IsNullOrEmpty(password))
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
return Merge(DockerResourceConfiguration, new WireMockConfiguration(username, password))
|
||||
.WithCommand($"--AdminUserName {username}", $"--AdminPassword {password}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use the WireMockNullLogger.
|
||||
/// </summary>
|
||||
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||
[PublicAPI]
|
||||
public WireMockContainerBuilder WithNullLogger()
|
||||
{
|
||||
return WithCommand("--WireMockLogger WireMockNullLogger");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the static mappings should be read at startup (default set to false).
|
||||
/// </summary>
|
||||
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||
[PublicAPI]
|
||||
public WireMockContainerBuilder WithReadStaticMappings()
|
||||
{
|
||||
return WithCommand("--ReadStaticMappings true");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Watch the static mapping files + folder for changes when running.
|
||||
/// </summary>
|
||||
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||
[PublicAPI]
|
||||
public WireMockContainerBuilder WithWatchStaticMappings(bool includeSubDirectories)
|
||||
{
|
||||
return WithCommand("--WatchStaticMappings true").WithCommand($"--WatchStaticMappingsInSubdirectories {includeSubDirectories}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the path for the (static) mapping json files.
|
||||
/// </summary>
|
||||
/// <param name="path">The path</param>
|
||||
/// <returns></returns>
|
||||
[PublicAPI]
|
||||
public WireMockContainerBuilder WithMappings(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
var isWindows = _isWindowsAsLazy.Value.GetAwaiter().GetResult();
|
||||
|
||||
return WithReadStaticMappings().WithBindMount(path, _info[isWindows].MappingsPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockContainerBuilder" /> class.
|
||||
/// </summary>
|
||||
/// <param name="dockerResourceConfiguration">The Docker resource configuration.</param>
|
||||
private WireMockContainerBuilder(WireMockConfiguration dockerResourceConfiguration) : base(dockerResourceConfiguration)
|
||||
{
|
||||
DockerResourceConfiguration = dockerResourceConfiguration;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override WireMockConfiguration DockerResourceConfiguration { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override WireMockContainer Build()
|
||||
{
|
||||
Validate();
|
||||
|
||||
return new WireMockContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override WireMockContainerBuilder Init()
|
||||
{
|
||||
var builder = base.Init();
|
||||
|
||||
// In case no image has been set, set the image using internal logic.
|
||||
if (builder.DockerResourceConfiguration.Image == null)
|
||||
{
|
||||
builder = builder.WithImage();
|
||||
}
|
||||
|
||||
var isWindows = _isWindowsAsLazy.Value.GetAwaiter().GetResult();
|
||||
var waitForContainerOS = isWindows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
||||
return builder
|
||||
.WithPortBinding(WireMockContainer.ContainerPort, true)
|
||||
.WithCommand($"--WireMockLogger {DefaultLogger}")
|
||||
.WithWaitStrategy(waitForContainerOS.UntilMessageIsLogged("By Stef Heyenrath"));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override WireMockContainerBuilder Clone(IContainerConfiguration resourceConfiguration)
|
||||
{
|
||||
return Merge(DockerResourceConfiguration, new WireMockConfiguration(resourceConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override WireMockContainerBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
|
||||
{
|
||||
return Merge(DockerResourceConfiguration, new WireMockConfiguration(resourceConfiguration));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override WireMockContainerBuilder Merge(WireMockConfiguration oldValue, WireMockConfiguration newValue)
|
||||
{
|
||||
return new WireMockContainerBuilder(new WireMockConfiguration(oldValue, newValue));
|
||||
}
|
||||
}
|
||||
17
src/WireMock.Net/.filenesting.json
Normal file
17
src/WireMock.Net/.filenesting.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"help": "https://go.microsoft.com/fwlink/?linkid=866610",
|
||||
"root": true,
|
||||
|
||||
"dependentFileProviders": {
|
||||
"add": {
|
||||
"addedExtension": {},
|
||||
"pathSegment": {
|
||||
"add": {
|
||||
".*": [
|
||||
".cs"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/WireMock.Net/Compatibility/StringExtensions.cs
Normal file
15
src/WireMock.Net/Compatibility/StringExtensions.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#if NET451 || NET452 || NET46 || NET451 || NET461 || NETSTANDARD1_3 || NETSTANDARD2_0
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace System;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string Replace(this string text, string oldValue, string newValue, StringComparison stringComparison)
|
||||
{
|
||||
var options = stringComparison == StringComparison.OrdinalIgnoreCase ? RegexOptions.IgnoreCase : RegexOptions.None;
|
||||
return Regex.Replace(text, oldValue, newValue, options);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,35 +1,34 @@
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace WireMock.Exceptions
|
||||
namespace WireMock.Exceptions;
|
||||
|
||||
/// <summary>
|
||||
/// WireMockException
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
public class WireMockException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// WireMockException
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <seealso cref="Exception" />
|
||||
public class WireMockException : Exception
|
||||
public WireMockException()
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
public WireMockException()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public WireMockException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public WireMockException(string message) : base(message)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="inner">The inner.</param>
|
||||
public WireMockException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="inner">The inner.</param>
|
||||
public WireMockException(string message, Exception inner) : base(message, inner)
|
||||
{
|
||||
}
|
||||
}
|
||||
29
src/WireMock.Net/Extensions/DictionaryExtensions.cs
Normal file
29
src/WireMock.Net/Extensions/DictionaryExtensions.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using System.Collections;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class DictionaryExtensions
|
||||
{
|
||||
public static bool TryGetStringValue(this IDictionary dictionary, string key, [NotNullWhen(true)] out string? value)
|
||||
{
|
||||
Guard.NotNull(dictionary);
|
||||
|
||||
if (dictionary[key] is string valueIsString)
|
||||
{
|
||||
value = valueIsString;
|
||||
return true;
|
||||
}
|
||||
|
||||
var valueToString = dictionary[key]?.ToString();
|
||||
if (valueToString != null)
|
||||
{
|
||||
value = valueToString;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -3,179 +3,178 @@ using System.IO;
|
||||
using WireMock.Util;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Handlers
|
||||
namespace WireMock.Handlers;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
||||
/// </summary>
|
||||
public class LocalFileSystemHandler : IFileSystemHandler
|
||||
{
|
||||
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
|
||||
private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched");
|
||||
|
||||
private readonly string _rootFolder;
|
||||
|
||||
/// <summary>
|
||||
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||
/// </summary>
|
||||
public class LocalFileSystemHandler : IFileSystemHandler
|
||||
public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory())
|
||||
{
|
||||
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
|
||||
private static readonly string UnmatchedRequestsFolder = Path.Combine("requests", "unmatched");
|
||||
}
|
||||
|
||||
private readonly string _rootFolder;
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rootFolder">The root folder.</param>
|
||||
public LocalFileSystemHandler(string rootFolder)
|
||||
{
|
||||
_rootFolder = rootFolder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||
/// </summary>
|
||||
public LocalFileSystemHandler() : this(Directory.GetCurrentDirectory())
|
||||
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
||||
public virtual bool FolderExists(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
||||
public virtual void CreateFolder(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
||||
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
|
||||
public virtual string GetMappingFolder()
|
||||
{
|
||||
return Path.Combine(_rootFolder, AdminMappingsFolder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
||||
public virtual string ReadMappingFile(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile(string, string)"/>
|
||||
public virtual void WriteMappingFile(string path, string text)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
Guard.NotNull(text, nameof(text));
|
||||
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
||||
public virtual byte[] ReadResponseBodyAsFile(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
path = PathUtils.CleanPath(path);
|
||||
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||
public virtual string ReadResponseBodyAsString(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path);
|
||||
path = PathUtils.CleanPath(path);
|
||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||
public virtual bool FileExists(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
|
||||
return File.Exists(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void WriteFile(string filename, byte[] bytes)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(bytes);
|
||||
|
||||
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void WriteFile(string folder, string filename, byte[] bytes)
|
||||
{
|
||||
Guard.NotNullOrEmpty(folder);
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(bytes);
|
||||
|
||||
File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
||||
public virtual void DeleteFile(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
|
||||
File.Delete(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
||||
public virtual byte[] ReadFile(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
|
||||
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
|
||||
public virtual string ReadFileAsString(string filename)
|
||||
{
|
||||
return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename, nameof(filename))));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.GetUnmatchedRequestsFolder"/>
|
||||
public virtual string GetUnmatchedRequestsFolder()
|
||||
{
|
||||
return Path.Combine(_rootFolder, UnmatchedRequestsFolder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
||||
public virtual void WriteUnmatchedRequest(string filename, string text)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(text);
|
||||
|
||||
var folder = GetUnmatchedRequestsFolder();
|
||||
if (!FolderExists(folder))
|
||||
{
|
||||
CreateFolder(folder);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LocalFileSystemHandler"/> class.
|
||||
/// </summary>
|
||||
/// <param name="rootFolder">The root folder.</param>
|
||||
public LocalFileSystemHandler(string rootFolder)
|
||||
{
|
||||
_rootFolder = rootFolder;
|
||||
}
|
||||
File.WriteAllText(Path.Combine(folder, filename), text);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
||||
public virtual bool FolderExists(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
return Directory.Exists(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
||||
public virtual void CreateFolder(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
||||
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.GetMappingFolder"/>
|
||||
public virtual string GetMappingFolder()
|
||||
{
|
||||
return Path.Combine(_rootFolder, AdminMappingsFolder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
||||
public virtual string ReadMappingFile(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.WriteMappingFile(string, string)"/>
|
||||
public virtual void WriteMappingFile(string path, string text)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
Guard.NotNull(text, nameof(text));
|
||||
|
||||
File.WriteAllText(path, text);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
||||
public virtual byte[] ReadResponseBodyAsFile(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
path = PathUtils.CleanPath(path);
|
||||
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||
public virtual string ReadResponseBodyAsString(string path)
|
||||
{
|
||||
Guard.NotNullOrEmpty(path, nameof(path));
|
||||
path = PathUtils.CleanPath(path);
|
||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||
public virtual bool FileExists(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
||||
|
||||
return File.Exists(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void WriteFile(string filename, byte[] bytes)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
||||
Guard.NotNull(bytes, nameof(bytes));
|
||||
|
||||
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual void WriteFile(string folder, string filename, byte[] bytes)
|
||||
{
|
||||
Guard.NotNullOrEmpty(folder);
|
||||
Guard.NotNullOrEmpty(filename);
|
||||
Guard.NotNull(bytes);
|
||||
|
||||
File.WriteAllBytes(PathUtils.Combine(folder, filename), bytes);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
||||
public virtual void DeleteFile(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
||||
|
||||
File.Delete(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
||||
public virtual byte[] ReadFile(string filename)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
||||
|
||||
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadFileAsString"/>
|
||||
public virtual string ReadFileAsString(string filename)
|
||||
{
|
||||
return File.ReadAllText(AdjustPathForMappingFolder(Guard.NotNullOrEmpty(filename, nameof(filename))));
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.GetUnmatchedRequestsFolder"/>
|
||||
public virtual string GetUnmatchedRequestsFolder()
|
||||
{
|
||||
return Path.Combine(_rootFolder, UnmatchedRequestsFolder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
||||
public virtual void WriteUnmatchedRequest(string filename, string text)
|
||||
{
|
||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
||||
Guard.NotNull(text, nameof(text));
|
||||
|
||||
var folder = GetUnmatchedRequestsFolder();
|
||||
if (!FolderExists(folder))
|
||||
{
|
||||
CreateFolder(folder);
|
||||
}
|
||||
|
||||
File.WriteAllText(Path.Combine(folder, filename), text);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the path to the MappingFolder.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path.</param>
|
||||
/// <returns>Adjusted path</returns>
|
||||
private string AdjustPathForMappingFolder(string filename)
|
||||
{
|
||||
return Path.Combine(GetMappingFolder(), filename);
|
||||
}
|
||||
/// <summary>
|
||||
/// Adjusts the path to the MappingFolder.
|
||||
/// </summary>
|
||||
/// <param name="filename">The path.</param>
|
||||
/// <returns>Adjusted path</returns>
|
||||
private string AdjustPathForMappingFolder(string filename)
|
||||
{
|
||||
return Path.Combine(GetMappingFolder(), filename);
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,18 @@ using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal static class HttpRequestMessageHelper
|
||||
{
|
||||
private static readonly IDictionary<string, bool> ContentLengthHeaderAllowed = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
{ HttpRequestMethod.HEAD, true }
|
||||
};
|
||||
|
||||
internal static HttpRequestMessage Create(IRequestMessage requestMessage, string url)
|
||||
{
|
||||
Guard.NotNull(requestMessage);
|
||||
@@ -28,7 +34,7 @@ internal static class HttpRequestMessageHelper
|
||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.Bytes:
|
||||
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes, contentType);
|
||||
httpRequestMessage.Content = ByteArrayContentHelper.Create(requestMessage.BodyData.BodyAsBytes!, contentType);
|
||||
break;
|
||||
|
||||
case BodyType.Json:
|
||||
@@ -36,7 +42,8 @@ internal static class HttpRequestMessageHelper
|
||||
break;
|
||||
|
||||
case BodyType.String:
|
||||
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString, contentType);
|
||||
case BodyType.FormUrlEncoded:
|
||||
httpRequestMessage.Content = StringContentHelper.Create(requestMessage.BodyData.BodyAsString!, contentType);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -49,7 +56,19 @@ internal static class HttpRequestMessageHelper
|
||||
return httpRequestMessage;
|
||||
}
|
||||
|
||||
var excludeHeaders = new List<string> { HttpKnownHeaderNames.Host, HttpKnownHeaderNames.ContentLength };
|
||||
var excludeHeaders = new List<string> { HttpKnownHeaderNames.Host };
|
||||
|
||||
var contentLengthHeaderAllowed = ContentLengthHeaderAllowed.TryGetValue(requestMessage.Method, out var allowed) && allowed;
|
||||
if (contentLengthHeaderAllowed)
|
||||
{
|
||||
// Set Content to empty ByteArray to be able to set the Content-Length on the content in case of a HEAD method.
|
||||
httpRequestMessage.Content ??= new ByteArrayContent(EmptyArray<byte>.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
excludeHeaders.Add(HttpKnownHeaderNames.ContentLength);
|
||||
}
|
||||
|
||||
if (contentType != null)
|
||||
{
|
||||
// Content-Type should be set on the content
|
||||
|
||||
@@ -15,7 +15,8 @@ internal static class HttpResponseMessageHelper
|
||||
Uri requiredUri,
|
||||
Uri originalUri,
|
||||
bool deserializeJson,
|
||||
bool decompressGzipAndDeflate)
|
||||
bool decompressGzipAndDeflate,
|
||||
bool deserializeFormUrlEncoded)
|
||||
{
|
||||
var responseMessage = new ResponseMessage { StatusCode = (int)httpResponseMessage.StatusCode };
|
||||
|
||||
@@ -44,7 +45,8 @@ internal static class HttpResponseMessageHelper
|
||||
ContentType = contentTypeHeader?.FirstOrDefault(),
|
||||
DeserializeJson = deserializeJson,
|
||||
ContentEncoding = contentEncodingHeader?.FirstOrDefault(),
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate
|
||||
DecompressGZipAndDeflate = decompressGzipAndDeflate,
|
||||
DeserializeFormUrlEncoded = deserializeFormUrlEncoded
|
||||
};
|
||||
responseMessage.BodyData = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
}
|
||||
@@ -55,7 +57,7 @@ internal static class HttpResponseMessageHelper
|
||||
// If Location header contains absolute redirect URL, and base URL is one that we proxy to,
|
||||
// we need to replace it to original one.
|
||||
if (string.Equals(header.Key, HttpKnownHeaderNames.Location, StringComparison.OrdinalIgnoreCase)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out Uri absoluteLocationUri)
|
||||
&& Uri.TryCreate(header.Value.First(), UriKind.Absolute, out var absoluteLocationUri)
|
||||
&& string.Equals(absoluteLocationUri.Host, requiredUri.Host, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var replacedLocationUri = new Uri(originalUri, absoluteLocationUri.PathAndQuery);
|
||||
|
||||
@@ -120,7 +120,21 @@ public interface IMapping
|
||||
/// <summary>
|
||||
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
bool? UseWebhooksFireAndForget { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
object? Data { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||
/// </summary>
|
||||
double? Probability { get; }
|
||||
|
||||
/// <summary>
|
||||
/// ProvideResponseAsync
|
||||
|
||||
10
src/WireMock.Net/Json/DynamicJsonClassOptions.cs
Normal file
10
src/WireMock.Net/Json/DynamicJsonClassOptions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal class DynamicJsonClassOptions
|
||||
{
|
||||
public IntegerBehavior IntegerConvertBehavior { get; set; } = IntegerBehavior.UseLong;
|
||||
|
||||
public FloatBehavior FloatConvertBehavior { get; set; } = FloatBehavior.UseDouble;
|
||||
}
|
||||
15
src/WireMock.Net/Json/DynamicPropertyWithValue.cs
Normal file
15
src/WireMock.Net/Json/DynamicPropertyWithValue.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
using System.Linq.Dynamic.Core;
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal class DynamicPropertyWithValue : DynamicProperty
|
||||
{
|
||||
public object? Value { get; }
|
||||
|
||||
public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object))
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/Json/FloatBehavior.cs
Normal file
24
src/WireMock.Net/Json/FloatBehavior.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Float in the Json Object.
|
||||
/// </summary>
|
||||
internal enum FloatBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a double. (default)
|
||||
/// </summary>
|
||||
UseDouble = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a float (unless overflow).
|
||||
/// </summary>
|
||||
UseFloat = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a decimal (unless overflow).
|
||||
/// </summary>
|
||||
UseDecimal = 2
|
||||
}
|
||||
20
src/WireMock.Net/Json/IntegerBehavior.cs
Normal file
20
src/WireMock.Net/Json/IntegerBehavior.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Integer in the Json Object.
|
||||
/// </summary>
|
||||
internal enum IntegerBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a int (unless overflow).
|
||||
/// (default)
|
||||
/// </summary>
|
||||
UseInt = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a long.
|
||||
/// </summary>
|
||||
UseLong = 1
|
||||
}
|
||||
202
src/WireMock.Net/Json/JObjectExtensions.cs
Normal file
202
src/WireMock.Net/Json/JObjectExtensions.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal static class JObjectExtensions
|
||||
{
|
||||
private class JTokenResolvers : Dictionary<JTokenType, Func<JToken, DynamicJsonClassOptions?, object?>>
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly JTokenResolvers Resolvers = new()
|
||||
{
|
||||
{ JTokenType.Array, ConvertJTokenArray },
|
||||
{ JTokenType.Boolean, (jToken, _) => jToken.Value<bool>() },
|
||||
{ JTokenType.Bytes, (jToken, _) => jToken.Value<byte[]>() },
|
||||
{ JTokenType.Date, (jToken, _) => jToken.Value<DateTime>() },
|
||||
{ JTokenType.Float, ConvertJTokenFloat },
|
||||
{ JTokenType.Guid, (jToken, _) => jToken.Value<Guid>() },
|
||||
{ JTokenType.Integer, ConvertJTokenInteger },
|
||||
{ JTokenType.None, (_, _) => null },
|
||||
{ JTokenType.Null, (_, _) => null },
|
||||
{ JTokenType.Object, ConvertJObject },
|
||||
{ JTokenType.Property, ConvertJTokenProperty },
|
||||
{ JTokenType.String, (jToken, _) => jToken.Value<string>() },
|
||||
{ JTokenType.TimeSpan, (jToken, _) => jToken.Value<TimeSpan>() },
|
||||
{ JTokenType.Undefined, (_, _) => null },
|
||||
{ JTokenType.Uri, (o, _) => o.Value<Uri>() },
|
||||
};
|
||||
|
||||
internal static DynamicClass? ToDynamicJsonClass(this JObject? src, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (src == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dynamicPropertyWithValues = new List<DynamicPropertyWithValue>();
|
||||
|
||||
foreach (var prop in src.Properties())
|
||||
{
|
||||
var value = Resolvers[prop.Type](prop.Value, options);
|
||||
if (value != null)
|
||||
{
|
||||
dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value));
|
||||
}
|
||||
}
|
||||
|
||||
return CreateInstance(dynamicPropertyWithValues);
|
||||
}
|
||||
|
||||
internal static IEnumerable ToDynamicClassArray(this JArray? src, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (src == null)
|
||||
{
|
||||
return EmptyArray<object?>.Value;
|
||||
}
|
||||
|
||||
return ConvertJTokenArray(src, options);
|
||||
}
|
||||
|
||||
private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg is JObject asJObject)
|
||||
{
|
||||
return asJObject.ToDynamicJsonClass(options);
|
||||
}
|
||||
|
||||
return GetResolverFor(arg)(arg, options);
|
||||
}
|
||||
|
||||
private static object PassThrough(JToken arg, DynamicJsonClassOptions? options)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static Func<JToken, DynamicJsonClassOptions?, object?> GetResolverFor(JToken arg)
|
||||
{
|
||||
return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough;
|
||||
}
|
||||
|
||||
private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg.Type != JTokenType.Float)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float.");
|
||||
}
|
||||
|
||||
if (options?.FloatConvertBehavior == FloatBehavior.UseFloat)
|
||||
{
|
||||
try
|
||||
{
|
||||
return arg.Value<float>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arg.Value<double>();
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal)
|
||||
{
|
||||
try
|
||||
{
|
||||
return arg.Value<decimal>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arg.Value<double>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return arg.Value<double>();
|
||||
}
|
||||
|
||||
private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg.Type != JTokenType.Integer)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int.");
|
||||
}
|
||||
|
||||
var longValue = arg.Value<long>();
|
||||
|
||||
if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt)
|
||||
{
|
||||
if (longValue is >= int.MinValue and <= int.MaxValue)
|
||||
{
|
||||
return Convert.ToInt32(longValue);
|
||||
}
|
||||
}
|
||||
|
||||
return longValue;
|
||||
}
|
||||
|
||||
private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
var resolver = GetResolverFor(arg);
|
||||
if (resolver is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}.");
|
||||
}
|
||||
|
||||
return resolver(arg, options);
|
||||
}
|
||||
|
||||
private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg is not JArray array)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}.");
|
||||
}
|
||||
|
||||
var result = new List<object?>();
|
||||
foreach (var item in array)
|
||||
{
|
||||
result.Add(ConvertJObject(item));
|
||||
}
|
||||
|
||||
var distinctType = FindSameTypeOf(result);
|
||||
return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType);
|
||||
}
|
||||
|
||||
private static Type? FindSameTypeOf(IEnumerable<object?> src)
|
||||
{
|
||||
var types = src.Select(o => o?.GetType()).Distinct().OfType<Type>().ToArray();
|
||||
return types.Length == 1 ? types[0] : null;
|
||||
}
|
||||
|
||||
private static IEnumerable ConvertToTypedArray(IEnumerable<object?> src, Type newType)
|
||||
{
|
||||
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
|
||||
return (IEnumerable)method.Invoke(null, new object[] { src })!;
|
||||
}
|
||||
|
||||
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JObjectExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
private static T[] ConvertToTypedArrayGeneric<T>(IEnumerable<object> src)
|
||||
{
|
||||
return src.Cast<T>().ToArray();
|
||||
}
|
||||
|
||||
public static DynamicClass CreateInstance(IList<DynamicPropertyWithValue> dynamicPropertiesWithValue, bool createParameterCtor = true)
|
||||
{
|
||||
var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast<DynamicProperty>().ToArray(), createParameterCtor);
|
||||
var dynamicClass = (DynamicClass)Activator.CreateInstance(type);
|
||||
foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null))
|
||||
{
|
||||
dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!);
|
||||
}
|
||||
|
||||
return dynamicClass;
|
||||
}
|
||||
}
|
||||
@@ -67,11 +67,17 @@ public class Mapping : IMapping
|
||||
public IWebhook[]? Webhooks { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
public bool? UseWebhooksFireAndForget { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ITimeSettings? TimeSettings { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? Data { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public double? Probability { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||
/// </summary>
|
||||
@@ -91,6 +97,8 @@ public class Mapping : IMapping
|
||||
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||
/// <param name="data">The data object. [Optional]</param>
|
||||
/// <param name="probability">Define the probability when this request should be matched. [Optional]</param>
|
||||
public Mapping(
|
||||
Guid guid,
|
||||
DateTime updatedAt,
|
||||
@@ -107,7 +115,9 @@ public class Mapping : IMapping
|
||||
int? stateTimes,
|
||||
IWebhook[]? webhooks,
|
||||
bool? useWebhooksFireAndForget,
|
||||
ITimeSettings? timeSettings)
|
||||
ITimeSettings? timeSettings,
|
||||
object? data,
|
||||
double? probability)
|
||||
{
|
||||
Guid = guid;
|
||||
UpdatedAt = updatedAt;
|
||||
@@ -125,6 +135,8 @@ public class Mapping : IMapping
|
||||
Webhooks = webhooks;
|
||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||
TimeSettings = timeSettings;
|
||||
Data = data;
|
||||
Probability = probability;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||
|
||||
@@ -69,12 +69,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
/// <inheritdoc />
|
||||
public MappingModel[] GetMappings()
|
||||
{
|
||||
return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray();
|
||||
}
|
||||
|
||||
internal IMapping[] GetMappingsInternal()
|
||||
{
|
||||
return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray();
|
||||
return GetNonAdminMappings().Select(_mappingConverter.ToMappingModel).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -86,7 +81,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
/// <inheritdoc />
|
||||
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
|
||||
{
|
||||
var mapping = GetMappingsInternal().FirstOrDefault(m => m.Guid == guid);
|
||||
var mapping = GetNonAdminMappings().FirstOrDefault(m => m.Guid == guid);
|
||||
if (mapping is null)
|
||||
{
|
||||
return null;
|
||||
@@ -101,7 +96,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
bool addStart = true;
|
||||
foreach (var mapping in GetMappingsInternal())
|
||||
foreach (var mapping in GetNonAdminMappings())
|
||||
{
|
||||
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
||||
|
||||
@@ -123,7 +118,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
/// <inheritdoc />
|
||||
public void SaveMappingsToFolder(string? folder)
|
||||
{
|
||||
foreach (var mapping in GetNonAdminMappings().Where(m => !m.IsAdminInterface))
|
||||
foreach (var mapping in GetNonAdminMappings())
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
||||
}
|
||||
@@ -131,7 +126,7 @@ public class MappingBuilder : IMappingBuilder
|
||||
|
||||
private IMapping[] GetNonAdminMappings()
|
||||
{
|
||||
return _options.Mappings.Values.ToArray();
|
||||
return _options.Mappings.Values.Where(m => !m.IsAdminInterface).OrderBy(m => m.UpdatedAt).ToArray();
|
||||
}
|
||||
|
||||
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Net.Http.Headers;
|
||||
using AnyOfTypes;
|
||||
using JetBrains.Annotations;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -5,8 +5,8 @@ using AnyOfTypes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Json;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -100,38 +100,55 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
double match = MatchScores.Mismatch;
|
||||
|
||||
JObject value;
|
||||
switch (input)
|
||||
JArray jArray;
|
||||
try
|
||||
{
|
||||
case JObject valueAsJObject:
|
||||
value = valueAsJObject;
|
||||
break;
|
||||
|
||||
case { } valueAsObject:
|
||||
value = JObject.FromObject(valueAsObject);
|
||||
break;
|
||||
|
||||
default:
|
||||
return MatchScores.Mismatch;
|
||||
jArray = new JArray { input };
|
||||
}
|
||||
catch
|
||||
{
|
||||
jArray = new JArray { JToken.FromObject(input) };
|
||||
}
|
||||
|
||||
//enumerable = jArray.ToDynamicClassArray();
|
||||
|
||||
//JObject value;
|
||||
//switch (input)
|
||||
//{
|
||||
// case JObject valueAsJObject:
|
||||
// value = valueAsJObject;
|
||||
// break;
|
||||
|
||||
// case { } valueAsObject:
|
||||
// value = JObject.FromObject(valueAsObject);
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// return MatchScores.Mismatch;
|
||||
//}
|
||||
|
||||
// Convert a single object to a Queryable JObject-list with 1 entry.
|
||||
var queryable1 = new[] { value }.AsQueryable();
|
||||
//var queryable1 = new[] { value }.AsQueryable();
|
||||
var queryable = jArray.ToDynamicClassArray().AsQueryable();
|
||||
|
||||
try
|
||||
{
|
||||
// Generate the DynamicLinq select statement.
|
||||
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
|
||||
//string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
|
||||
|
||||
// Execute DynamicLinq Select statement.
|
||||
var queryable2 = queryable1.Select(dynamicSelect);
|
||||
//var queryable2 = queryable1.Select(dynamicSelect);
|
||||
|
||||
// Use the Any(...) method to check if the result matches.
|
||||
match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern)).ToArray(), MatchOperator);
|
||||
|
||||
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
|
||||
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
|
||||
|
||||
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
if (ThrowException)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using WireMock.Models;
|
||||
@@ -62,7 +63,7 @@ public class NotNullOrEmptyMatcher : IObjectMatcher, IStringMatcher
|
||||
/// <inheritdoc cref="IStringMatcher.GetPatterns"/>
|
||||
public AnyOf<string, StringPattern>[] GetPatterns()
|
||||
{
|
||||
return new AnyOf<string, StringPattern>[0];
|
||||
return EmptyArray<AnyOf<string, StringPattern>>.Value;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -33,6 +32,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// </summary>
|
||||
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for FormUrlEncoded
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
@@ -109,6 +113,15 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
BodyDataFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
FormUrlEncodedFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
@@ -144,6 +157,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
{
|
||||
case BodyType.Json:
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
|
||||
case BodyType.Bytes:
|
||||
@@ -158,7 +172,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
{
|
||||
// If the body is a byte array, try to match.
|
||||
var detectedBodyType = requestMessage.BodyData?.DetectedBodyType;
|
||||
if (detectedBodyType is BodyType.Bytes or BodyType.String)
|
||||
if (detectedBodyType is BodyType.Bytes or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return exactObjectMatcher.IsMatch(requestMessage.BodyData?.BodyAsBytes);
|
||||
}
|
||||
@@ -184,7 +198,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
if (matcher is IStringMatcher stringMatcher)
|
||||
{
|
||||
// If the body is a Json or a String, use the BodyAsString to match on.
|
||||
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json || requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
|
||||
if (requestMessage?.BodyData?.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
}
|
||||
@@ -206,6 +220,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
|
||||
}
|
||||
|
||||
if (FormUrlEncodedFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
|
||||
}
|
||||
|
||||
if (JsonFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
@@ -25,9 +24,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
||||
/// </summary>
|
||||
/// <param name="requestMatchers">The request matchers.</param>
|
||||
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</param>
|
||||
protected RequestMessageCompositeMatcher([NotNull] IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
||||
protected RequestMessageCompositeMatcher(IEnumerable<IRequestMatcher> requestMatchers, CompositeMatcherType type = CompositeMatcherType.And)
|
||||
{
|
||||
Guard.NotNull(requestMatchers, nameof(requestMatchers));
|
||||
Guard.NotNull(requestMatchers);
|
||||
|
||||
_type = type;
|
||||
RequestMatchers = requestMatchers;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -14,6 +15,9 @@ public class BodyData : IBodyData
|
||||
/// <inheritdoc />
|
||||
public string? BodyAsString { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IBodyData.BodyAsJson" />
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace WireMock.Models
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// TimeSettingsModel: Start, End and TTL
|
||||
/// </summary>
|
||||
public class TimeSettings : ITimeSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// TimeSettingsModel: Start, End and TTL
|
||||
/// </summary>
|
||||
public class TimeSettings : ITimeSettings
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DateTime? Start { get; set; }
|
||||
/// <inheritdoc />
|
||||
public DateTime? Start { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? End { get; set; }
|
||||
/// <inheritdoc />
|
||||
public DateTime? End { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public int? TTL { get; set; }
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public int? TTL { get; set; }
|
||||
}
|
||||
@@ -1,51 +1,47 @@
|
||||
using System;
|
||||
using System;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.Models
|
||||
namespace WireMock.Models;
|
||||
|
||||
/// <summary>
|
||||
/// UrlDetails
|
||||
/// </summary>
|
||||
public class UrlDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// UrlDetails
|
||||
/// Gets the url (relative).
|
||||
/// </summary>
|
||||
public class UrlDetails
|
||||
public Uri Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AbsoluteUrl.
|
||||
/// </summary>
|
||||
public Uri AbsoluteUrl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public UrlDetails(string url) : this(new Uri(url))
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the url (relative).
|
||||
/// </summary>
|
||||
public Uri Url { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the AbsoluteUrl.
|
||||
/// </summary>
|
||||
public Uri AbsoluteUrl { get; }
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public UrlDetails(Uri url) : this(url, url)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public UrlDetails(string url) : this(new Uri(url))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="url">The URL.</param>
|
||||
public UrlDetails(Uri url) : this(url, url)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="absoluteUrl">The absolute URL.</param>
|
||||
/// <param name="url">The URL (relative).</param>
|
||||
public UrlDetails(Uri absoluteUrl, Uri url)
|
||||
{
|
||||
Guard.NotNull(absoluteUrl, nameof(absoluteUrl));
|
||||
Guard.NotNull(url, nameof(url));
|
||||
|
||||
AbsoluteUrl = absoluteUrl;
|
||||
Url = url;
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="UrlDetails"/> class.
|
||||
/// </summary>
|
||||
/// <param name="absoluteUrl">The absolute URL.</param>
|
||||
/// <param name="url">The URL (relative).</param>
|
||||
public UrlDetails(Uri absoluteUrl, Uri url)
|
||||
{
|
||||
AbsoluteUrl = Guard.NotNull(absoluteUrl);
|
||||
Url = Guard.NotNull(url);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
|
||||
using Stef.Validation;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Owin.Mappers;
|
||||
using WireMock.Services;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Owin
|
||||
@@ -66,6 +67,7 @@ namespace WireMock.Owin
|
||||
{
|
||||
services.AddSingleton(_wireMockMiddlewareOptions);
|
||||
services.AddSingleton<IMappingMatcher, MappingMatcher>();
|
||||
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
|
||||
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
|
||||
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
||||
|
||||
|
||||
@@ -62,11 +62,11 @@ namespace WireMock.Owin.Mappers
|
||||
switch (responseMessage.FaultType)
|
||||
{
|
||||
case FaultType.EMPTY_RESPONSE:
|
||||
bytes = IsFault(responseMessage) ? new byte[0] : GetNormalBody(responseMessage);
|
||||
bytes = IsFault(responseMessage) ? EmptyArray<byte>.Value : GetNormalBody(responseMessage);
|
||||
break;
|
||||
|
||||
case FaultType.MALFORMED_RESPONSE_CHUNK:
|
||||
bytes = GetNormalBody(responseMessage) ?? new byte[0];
|
||||
bytes = GetNormalBody(responseMessage) ?? EmptyArray<byte>.Value;
|
||||
if (IsFault(responseMessage))
|
||||
{
|
||||
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
||||
@@ -87,7 +87,7 @@ namespace WireMock.Owin.Mappers
|
||||
|
||||
case { } typeAsString when typeAsString == typeof(string):
|
||||
// Note: this case will also match on null
|
||||
int.TryParse(responseMessage.StatusCode as string, out int result);
|
||||
int.TryParse(responseMessage.StatusCode as string, out var result);
|
||||
response.StatusCode = MapStatusCode(result);
|
||||
break;
|
||||
|
||||
@@ -130,6 +130,7 @@ namespace WireMock.Owin.Mappers
|
||||
switch (responseMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString!);
|
||||
|
||||
case BodyType.Json:
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using WireMock.Extensions;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Services;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal class MappingMatcher : IMappingMatcher
|
||||
{
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
||||
|
||||
public MappingMatcher(IWireMockMiddlewareOptions options)
|
||||
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
||||
{
|
||||
_options = Guard.NotNull(options);
|
||||
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||
}
|
||||
|
||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||
@@ -21,7 +24,12 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var possibleMappings = new List<MappingMatcherResult>();
|
||||
|
||||
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid()))
|
||||
var mappings = _options.Mappings.Values
|
||||
.Where(m => m.TimeSettings.IsValid())
|
||||
.Where(m => m.Probability is null || m.Probability <= _randomizerDoubleBetween0And1.Generate())
|
||||
.ToArray();
|
||||
|
||||
foreach (var mapping in mappings)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -9,6 +9,9 @@ using JetBrains.Annotations;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Owin.Mappers;
|
||||
using Stef.Validation;
|
||||
using RandomDataGenerator.FieldOptions;
|
||||
using RandomDataGenerator.Randomizers;
|
||||
using WireMock.Services;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
@@ -70,7 +73,7 @@ internal class OwinSelfHost : IOwinSelfHost
|
||||
{
|
||||
var requestMapper = new OwinRequestMapper();
|
||||
var responseMapper = new OwinResponseMapper(_options);
|
||||
var matcher = new MappingMatcher(_options);
|
||||
var matcher = new MappingMatcher(_options, new RandomizerDoubleBetween0And1());
|
||||
|
||||
Action<IAppBuilder> startup = app =>
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace WireMock.Owin
|
||||
public Task Invoke(IContext ctx)
|
||||
#endif
|
||||
{
|
||||
if (_options.HandleRequestsSynchronously.GetValueOrDefault(true))
|
||||
if (_options.HandleRequestsSynchronously.GetValueOrDefault(false))
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
@@ -216,7 +216,12 @@ namespace WireMock.Owin
|
||||
{
|
||||
try
|
||||
{
|
||||
await webhookSender.SendAsync(httpClientForWebhook, mapping, webhookRequest, request, response).ConfigureAwait(false);
|
||||
var result = await webhookSender.SendAsync(httpClientForWebhook, mapping, webhookRequest, request, response).ConfigureAwait(false);
|
||||
if (!result.IsSuccessStatusCode)
|
||||
{
|
||||
var content = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
_options.Logger.Warn($"Sending message to Webhook [{webHookIndex}] from Mapping '{mapping.Guid}' failed. HttpStatusCode: {result.StatusCode} Content: {content}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Http;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Util;
|
||||
@@ -35,7 +37,19 @@ internal class ProxyHelper
|
||||
var requiredUri = new Uri(url);
|
||||
|
||||
// Create HttpRequestMessage
|
||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, url);
|
||||
var replaceSettings = proxyAndRecordSettings.ReplaceSettings;
|
||||
string proxyUrl;
|
||||
if (replaceSettings is not null)
|
||||
{
|
||||
var stringComparison = replaceSettings.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
||||
proxyUrl = url.Replace(replaceSettings.OldValue, replaceSettings.NewValue, stringComparison);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyUrl = url;
|
||||
}
|
||||
|
||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, proxyUrl);
|
||||
|
||||
// Call the URL
|
||||
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
|
||||
@@ -43,16 +57,45 @@ internal class ProxyHelper
|
||||
// Create ResponseMessage
|
||||
bool deserializeJson = !_settings.DisableJsonBodyParsing.GetValueOrDefault(false);
|
||||
bool decompressGzipAndDeflate = !_settings.DisableRequestBodyDecompressing.GetValueOrDefault(false);
|
||||
bool deserializeFormUrlEncoded = !_settings.DisableDeserializeFormUrlEncoded.GetValueOrDefault(false);
|
||||
|
||||
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false);
|
||||
var responseMessage = await HttpResponseMessageHelper.CreateAsync(
|
||||
httpResponseMessage,
|
||||
requiredUri,
|
||||
originalUri,
|
||||
deserializeJson,
|
||||
decompressGzipAndDeflate,
|
||||
deserializeFormUrlEncoded
|
||||
).ConfigureAwait(false);
|
||||
|
||||
IMapping? newMapping = null;
|
||||
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
|
||||
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
|
||||
|
||||
var saveMappingSettings = proxyAndRecordSettings.SaveMappingSettings;
|
||||
|
||||
bool save = true;
|
||||
if (saveMappingSettings != null)
|
||||
{
|
||||
save &= Check(saveMappingSettings.StatusCodePattern,
|
||||
() => saveMappingSettings.StatusCodePattern != null && HttpStatusRangeParser.IsMatch(saveMappingSettings.StatusCodePattern, responseMessage.StatusCode)
|
||||
);
|
||||
|
||||
save &= Check(saveMappingSettings.HttpMethods,
|
||||
() => saveMappingSettings.HttpMethods != null && saveMappingSettings.HttpMethods.Value.Contains(requestMessage.Method, StringComparer.OrdinalIgnoreCase)
|
||||
);
|
||||
}
|
||||
|
||||
if (save && (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
|
||||
{
|
||||
newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage);
|
||||
}
|
||||
|
||||
return (responseMessage, newMapping);
|
||||
}
|
||||
|
||||
private static bool Check<T>(ProxySaveMappingSetting<T>? saveMappingSetting, Func<bool> action)
|
||||
{
|
||||
var isMatch = saveMappingSetting is null || action();
|
||||
var matchBehaviour = saveMappingSetting?.MatchBehaviour ?? MatchBehaviour.AcceptOnMatch;
|
||||
return isMatch == (matchBehaviour == MatchBehaviour.AcceptOnMatch);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
@@ -45,35 +47,60 @@ public interface IBodyRequestBuilder : IRequestMatcher
|
||||
/// WithBody: Body as object
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour [default is AcceptOnMatch].</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Body as a string response based on a object (which will be converted to a JSON string using NewtonSoft.Json).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour [default is AcceptOnMatch].</param>
|
||||
/// <returns>A <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Body as a string response based on a object (which will be converted to a JSON string using the <see cref="IJsonConverter"/>).
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="converter">The JsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <param name="matchBehaviour">The match behaviour [default is AcceptOnMatch].</param>
|
||||
/// <returns>A <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsJson(object body, IJsonConverter converter, JsonConverterOptions? options = null, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (string)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<string, bool> func);
|
||||
IRequestBuilder WithBody(Func<string?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (byte[])
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<byte[], bool> func);
|
||||
IRequestBuilder WithBody(Func<byte[]?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (json object)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<object, bool> func);
|
||||
IRequestBuilder WithBody(Func<object?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (BodyData object)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<IBodyData, bool> func);
|
||||
IRequestBuilder WithBody(Func<IBodyData?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: Body as form-urlencoded values.
|
||||
/// </summary>
|
||||
/// <param name="func">The form-urlencoded values.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func);
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
namespace WireMock.RequestBuilders
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// IRequestBuilder
|
||||
/// </summary>
|
||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// IRequestBuilder
|
||||
/// </summary>
|
||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,57 @@
|
||||
// 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.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JsonConverter.Abstractions;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(string, MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[], MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(object, MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsJson(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
var bodyAsJsonString = JsonConvert.SerializeObject(body);
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, bodyAsJsonString));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsJson(object body, IJsonConverter converter, JsonConverterOptions? options = null, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(converter);
|
||||
|
||||
var bodyAsJsonString = converter.Serialize(body, options);
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, bodyAsJsonString));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(IMatcher matcher)
|
||||
{
|
||||
@@ -46,39 +67,46 @@ public partial class Request
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<string, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<string?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/>
|
||||
public IRequestBuilder WithBody(Func<byte[], bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<byte[]?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<object, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<object?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{IBodyData, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<IBodyData, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<IBodyData?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +1,81 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
public partial class Request
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour)
|
||||
{
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour)
|
||||
{
|
||||
return WithCookie(name, pattern, true, matchBehaviour);
|
||||
}
|
||||
return WithCookie(name, pattern, true, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, bool, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNull(pattern, nameof(pattern));
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, bool, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNull(pattern, nameof(pattern));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, pattern, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour)
|
||||
{
|
||||
return WithCookie(name, patterns, true, matchBehaviour);
|
||||
}
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour)
|
||||
{
|
||||
return WithCookie(name, patterns, true, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], bool, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNull(patterns, nameof(patterns));
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string[], bool, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNull(patterns, nameof(patterns));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, patterns));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, false, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, bool, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, bool, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(MatchBehaviour.AcceptOnMatch, name, ignoreCase, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(name, nameof(name));
|
||||
Guard.NotNullOrEmpty(matchers, nameof(matchers));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(matchBehaviour, name, ignoreCase, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(Func{IDictionary{string, string}, bool}[])"/>
|
||||
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(Func{IDictionary{string, string}, bool}[])"/>
|
||||
public IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageCookieMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -7,86 +7,85 @@ using WireMock.Matchers.Request;
|
||||
using WireMock.Types;
|
||||
using Stef.Validation;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
public partial class Request
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return WithParam(key, false, matchBehaviour);
|
||||
}
|
||||
return WithParam(key, false, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(key);
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, MatchBehaviour)"/>
|
||||
public IRequestBuilder WithParam(string key, bool ignoreCase, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Guard.NotNull(key);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, params string[] values)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values);
|
||||
}
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, params string[] values)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, values);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values);
|
||||
}
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, bool ignoreCase, params string[] values)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, values);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers);
|
||||
}
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, false, matchers);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers);
|
||||
}
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, bool, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithParam(key, MatchBehaviour.AcceptOnMatch, ignoreCase, matchers);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
|
||||
{
|
||||
return WithParam(key, matchBehaviour, false, values);
|
||||
}
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params string[] values)
|
||||
{
|
||||
return WithParam(key, matchBehaviour, false, values);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values)
|
||||
{
|
||||
Guard.NotNull(key);
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, string[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase = false, params string[] values)
|
||||
{
|
||||
Guard.NotNull(key);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, values));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithParam(key, matchBehaviour, false, matchers);
|
||||
}
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithParam(key, matchBehaviour, false, matchers);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(key);
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour, bool, IStringMatcher[])"/>
|
||||
public IRequestBuilder WithParam(string key, MatchBehaviour matchBehaviour, bool ignoreCase, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNull(key);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(matchBehaviour, key, ignoreCase, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
|
||||
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(Func{IDictionary{string, WireMockList{string}}, bool}[])"/>
|
||||
public IRequestBuilder WithParam(params Func<IDictionary<string, WireMockList<string>>, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNullOrEmpty(funcs, nameof(funcs));
|
||||
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
_requestMatchers.Add(new RequestMessageParamMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -3,47 +3,46 @@ using Stef.Validation;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
public partial class Request
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(params IStringMatcher[] matchers)
|
||||
{
|
||||
return WithUrl(MatchOperator.Or, matchers);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNullOrEmpty(matchers);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(params string[] urls)
|
||||
{
|
||||
return WithUrl(MatchOperator.Or, urls);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls)
|
||||
{
|
||||
Guard.NotNullOrEmpty(urls);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUrlAndPathRequestBuilder.WithUrl(Func{string, bool}[])"/>
|
||||
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNullOrEmpty(funcs);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
return WithUrl(MatchOperator.Or, matchers);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(MatchOperator matchOperator, params IStringMatcher[] matchers)
|
||||
{
|
||||
Guard.NotNullOrEmpty(matchers);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, matchers));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(params string[] urls)
|
||||
{
|
||||
return WithUrl(MatchOperator.Or, urls);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithUrl(MatchOperator matchOperator, params string[] urls)
|
||||
{
|
||||
Guard.NotNullOrEmpty(urls);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(MatchBehaviour.AcceptOnMatch, matchOperator, urls));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IUrlAndPathRequestBuilder.WithUrl(Func{string, bool}[])"/>
|
||||
public IRequestBuilder WithUrl(params Func<string, bool>[] funcs)
|
||||
{
|
||||
Guard.NotNullOrEmpty(funcs);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageUrlMatcher(funcs));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -63,5 +63,4 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
||||
{
|
||||
return _requestMatchers.OfType<T>().FirstOrDefault(func);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -180,12 +180,7 @@ public class RequestMessage : IRequestMessage
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a query parameter.
|
||||
/// </summary>
|
||||
/// <param name="key">The key.</param>
|
||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||
/// <returns>The query parameter.</returns>
|
||||
/// <inheritdoc />
|
||||
public WireMockList<string>? GetParameter(string key, bool ignoreCase = false)
|
||||
{
|
||||
if (Query == null)
|
||||
|
||||
@@ -92,7 +92,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="converter">The JsonConverter.</param>
|
||||
/// <param name="options">The IJsonConverterOption [optional].</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody(object body, IJsonConverter converter, JsonConverterOptions? options = null);
|
||||
|
||||
@@ -102,7 +102,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding, can be <c>null</c>.</param>
|
||||
/// <param name="converter">The JsonConverter.</param>
|
||||
/// <param name="options">The IJsonConverterOption [optional].</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter converter, JsonConverterOptions? options = null);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
||||
/// WithProxy using <see cref="X509Certificate2"/>.
|
||||
/// </summary>
|
||||
/// <param name="proxyUrl">The proxy url.</param>
|
||||
/// <param name="certificate"">The X509Certificate2.</param>
|
||||
/// <param name="certificate">The X509Certificate2.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate);
|
||||
}
|
||||
@@ -107,14 +107,12 @@ public partial class Response : IResponseBuilder
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Response"/> class.
|
||||
/// </summary>
|
||||
/// <param name="responseMessage">
|
||||
/// The response.
|
||||
/// </param>
|
||||
/// <param name="responseMessage">The response.</param>
|
||||
private Response(ResponseMessage responseMessage)
|
||||
{
|
||||
ResponseMessage = responseMessage;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithStatusCode(int code)
|
||||
|
||||
@@ -44,6 +44,7 @@ internal class LogEntryMapper
|
||||
switch (logEntry.RequestMessage.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
logRequestModel.Body = logEntry.RequestMessage.BodyData.BodyAsString;
|
||||
break;
|
||||
|
||||
@@ -120,6 +121,7 @@ internal class LogEntryMapper
|
||||
switch (logEntry.ResponseMessage.BodyData!.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
if (!string.IsNullOrEmpty(logEntry.ResponseMessage.BodyData.IsFuncUsed) && _options.DoNotSaveDynamicResponseInLogEntry == true)
|
||||
{
|
||||
logResponseModel.Body = logEntry.ResponseMessage.BodyData.IsFuncUsed;
|
||||
@@ -142,6 +144,9 @@ internal class LogEntryMapper
|
||||
logResponseModel.BodyAsFile = logEntry.ResponseMessage.BodyData.BodyAsFile;
|
||||
logResponseModel.BodyAsFileIsCached = logEntry.ResponseMessage.BodyData.BodyAsFileIsCached;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Constants;
|
||||
@@ -14,7 +16,9 @@ using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
using static WireMock.Util.CSharpFormatter;
|
||||
namespace WireMock.Serialization;
|
||||
|
||||
internal class MappingConverter
|
||||
@@ -33,7 +37,7 @@ internal class MappingConverter
|
||||
settings ??= new MappingConverterSettings();
|
||||
|
||||
var request = (Request)mapping.RequestMatcher;
|
||||
var response = (Response) mapping.Provider;
|
||||
var response = (Response)mapping.Provider;
|
||||
|
||||
var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||
var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||
@@ -103,11 +107,28 @@ internal class MappingConverter
|
||||
|
||||
if (bodyMatcher is { Matchers: { } })
|
||||
{
|
||||
var wildcardMatcher = bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault();
|
||||
if (wildcardMatcher is { } && wildcardMatcher.GetPatterns().Any())
|
||||
if (bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault() is { } wildcardMatcher && wildcardMatcher.GetPatterns().Any())
|
||||
{
|
||||
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
|
||||
}
|
||||
else if (bodyMatcher.Matchers.OfType<JsonPartialMatcher>().FirstOrDefault() is { Value: { } } jsonPartialMatcher)
|
||||
{
|
||||
sb.AppendLine(@$" .WithBody(new JsonPartialMatcher(
|
||||
value: {ToCSharpStringLiteral(jsonPartialMatcher.Value.ToString())},
|
||||
ignoreCase: {ToCSharpBooleanLiteral(jsonPartialMatcher.IgnoreCase)},
|
||||
throwException: {ToCSharpBooleanLiteral(jsonPartialMatcher.ThrowException)},
|
||||
regex: {ToCSharpBooleanLiteral(jsonPartialMatcher.Regex)}
|
||||
))");
|
||||
}
|
||||
else if (bodyMatcher.Matchers.OfType<JsonPartialWildcardMatcher>().FirstOrDefault() is { Value: { } } jsonPartialWildcardMatcher)
|
||||
{
|
||||
sb.AppendLine(@$" .WithBody(new JsonPartialWildcardMatcher(
|
||||
value: {ToCSharpStringLiteral(jsonPartialWildcardMatcher.Value.ToString())},
|
||||
ignoreCase: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.IgnoreCase)},
|
||||
throwException: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.ThrowException)},
|
||||
regex: {ToCSharpBooleanLiteral(jsonPartialWildcardMatcher.Regex)}
|
||||
))");
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine(@" )");
|
||||
@@ -115,23 +136,50 @@ internal class MappingConverter
|
||||
// Guid
|
||||
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
|
||||
|
||||
if (mapping.Probability != null)
|
||||
{
|
||||
sb.AppendLine($" .WithProbability({mapping.Probability.Value.ToString(CultureInfoUtils.CultureInfoEnUS)})");
|
||||
}
|
||||
|
||||
// Response
|
||||
sb.AppendLine(" .RespondWith(Response.Create()");
|
||||
|
||||
if (response.ResponseMessage.StatusCode is int or string)
|
||||
{
|
||||
sb.AppendLine($" .WithStatusCode({JsonConvert.SerializeObject(response.ResponseMessage.StatusCode)})");
|
||||
}
|
||||
else if (response.ResponseMessage.StatusCode is HttpStatusCode httpStatusCode)
|
||||
{
|
||||
sb.AppendLine($" .WithStatusCode({(int)httpStatusCode})");
|
||||
}
|
||||
|
||||
if (response.ResponseMessage.Headers is { })
|
||||
{
|
||||
foreach (var header in response.ResponseMessage.Headers)
|
||||
{
|
||||
sb.AppendLine($" .WithHeader(\"{header.Key})\", {ToValueArguments(header.Value.ToArray())})");
|
||||
sb.AppendLine($" .WithHeader(\"{header.Key}\", {ToValueArguments(header.Value.ToArray())})");
|
||||
}
|
||||
}
|
||||
|
||||
if (response.ResponseMessage.BodyData is { })
|
||||
if (response.ResponseMessage.BodyData is { } bodyData)
|
||||
{
|
||||
switch (response.ResponseMessage.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
sb.AppendLine($" .WithBody(\"{response.ResponseMessage.BodyData.BodyAsString}\")");
|
||||
case BodyType.FormUrlEncoded:
|
||||
sb.AppendLine($" .WithBody({ToCSharpStringLiteral(bodyData.BodyAsString)})");
|
||||
break;
|
||||
case BodyType.Json:
|
||||
if (bodyData.BodyAsJson is string bodyStringValue)
|
||||
{
|
||||
sb.AppendLine($" .WithBody({ToCSharpStringLiteral(bodyStringValue)})");
|
||||
}
|
||||
else if(bodyData.BodyAsJson is {} jsonBody)
|
||||
{
|
||||
var anonymousObjectDefinition = ConvertToAnonymousObjectDefinition(jsonBody);
|
||||
sb.AppendLine($" .WithBodyAsJson({anonymousObjectDefinition})");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +188,7 @@ internal class MappingConverter
|
||||
{
|
||||
sb.AppendLine($" .WithDelay({response.Delay.Value.TotalMilliseconds})");
|
||||
}
|
||||
else if (response.MinimumDelayMilliseconds > 0 && response.MaximumDelayMilliseconds > 0)
|
||||
else if (response is { MinimumDelayMilliseconds: > 0, MaximumDelayMilliseconds: > 0 })
|
||||
{
|
||||
sb.AppendLine($" .WithRandomDelay({response.MinimumDelayMilliseconds}, {response.MaximumDelayMilliseconds})");
|
||||
}
|
||||
@@ -181,6 +229,8 @@ internal class MappingConverter
|
||||
Scenario = mapping.Scenario,
|
||||
WhenStateIs = mapping.ExecutionConditionState,
|
||||
SetStateTo = mapping.NextState,
|
||||
Data = mapping.Data,
|
||||
Probability = mapping.Probability,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
@@ -324,6 +374,7 @@ internal class MappingConverter
|
||||
switch (response.ResponseMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
mappingModel.Response.Body = response.ResponseMessage.BodyData.BodyAsString;
|
||||
break;
|
||||
|
||||
@@ -371,7 +422,7 @@ internal class MappingConverter
|
||||
|
||||
private static string GetString(IStringMatcher stringMatcher)
|
||||
{
|
||||
return stringMatcher.GetPatterns().Select(p => $"\"{p.GetPattern()}\"").First();
|
||||
return stringMatcher.GetPatterns().Select(p => ToCSharpStringLiteral(p.GetPattern())).First();
|
||||
}
|
||||
|
||||
private static string[] GetStringArray(IReadOnlyList<IStringMatcher> stringMatchers)
|
||||
@@ -424,7 +475,7 @@ internal class MappingConverter
|
||||
|
||||
private static string ToValueArguments(string[]? values, string defaultValue = "")
|
||||
{
|
||||
return values is { } ? string.Join(", ", values.Select(v => $"\"{v}\"")) : $"\"{defaultValue}\"";
|
||||
return values is { } ? string.Join(", ", values.Select(ToCSharpStringLiteral)) : ToCSharpStringLiteral(defaultValue);
|
||||
}
|
||||
|
||||
private static WebProxyModel? MapWebProxy(WebProxySettings? settings)
|
||||
@@ -455,4 +506,6 @@ internal class MappingConverter
|
||||
|
||||
return newDictionary;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -26,8 +26,13 @@ internal class ProxyMappingConverter
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
}
|
||||
|
||||
public IMapping ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
public IMapping? ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
{
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
|
||||
var excludedParams = proxyAndRecordSettings.ExcludedParams ?? new string[0];
|
||||
|
||||
var request = (Request?)mapping?.RequestMatcher;
|
||||
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||
var pathMatcher = request?.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||
@@ -37,11 +42,6 @@ internal class ProxyMappingConverter
|
||||
var methodMatcher = request?.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
var bodyMatcher = request?.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
|
||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
|
||||
|
||||
var newRequest = Request.Create();
|
||||
|
||||
// ClientIP
|
||||
@@ -75,12 +75,21 @@ internal class ProxyMappingConverter
|
||||
{
|
||||
foreach (var paramMatcher in paramMatchers)
|
||||
{
|
||||
newRequest.WithParam(paramMatcher.Key, paramMatcher.MatchBehaviour, paramMatcher.Matchers!.ToArray());
|
||||
if (!excludedParams.Contains(paramMatcher.Key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
newRequest.WithParam(paramMatcher.Key, paramMatcher.MatchBehaviour, paramMatcher.Matchers!.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
requestMessage.Query?.Loop((key, value) => newRequest.WithParam(key, false, value.ToArray()));
|
||||
requestMessage.Query?.Loop((key, value) =>
|
||||
{
|
||||
if (!excludedParams.Contains(key, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
newRequest.WithParam(key, false, value.ToArray());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Cookies
|
||||
@@ -142,6 +151,7 @@ internal class ProxyMappingConverter
|
||||
break;
|
||||
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
newRequest.WithBody(new ExactMatcher(MatchBehaviour.AcceptOnMatch, true, throwExceptionWhenMatcherFails, MatchOperator.Or, requestMessage.BodyData.BodyAsString!));
|
||||
break;
|
||||
|
||||
@@ -178,7 +188,9 @@ internal class ProxyMappingConverter
|
||||
stateTimes: null,
|
||||
webhooks: null,
|
||||
useWebhooksFireAndForget: null,
|
||||
timeSettings: null
|
||||
timeSettings: null,
|
||||
data: mapping?.Data,
|
||||
probability: null
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -54,10 +54,12 @@ internal static class SwaggerMapper
|
||||
{
|
||||
operation.Parameters.Add(openApiParameter);
|
||||
}
|
||||
|
||||
foreach (var openApiParameter in MapRequestHeaders(mapping.Request.Headers))
|
||||
{
|
||||
operation.Parameters.Add(openApiParameter);
|
||||
}
|
||||
|
||||
foreach (var openApiParameter in MapRequestCookies(mapping.Request.Cookies))
|
||||
{
|
||||
operation.Parameters.Add(openApiParameter);
|
||||
@@ -94,7 +96,7 @@ internal static class SwaggerMapper
|
||||
return openApiDocument.ToJson(SchemaType.OpenApi3, Formatting.Indented);
|
||||
}
|
||||
|
||||
private static IEnumerable<OpenApiParameter> MapRequestQueryParameters(IList<ParamModel>? queryParameters)
|
||||
private static IReadOnlyList<OpenApiParameter> MapRequestQueryParameters(IList<ParamModel>? queryParameters)
|
||||
{
|
||||
if (queryParameters == null)
|
||||
{
|
||||
@@ -146,7 +148,7 @@ internal static class SwaggerMapper
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static IEnumerable<OpenApiParameter> MapRequestCookies(IList<CookieModel>? cookies)
|
||||
private static IReadOnlyList<OpenApiParameter> MapRequestCookies(IList<CookieModel>? cookies)
|
||||
{
|
||||
if (cookies == null)
|
||||
{
|
||||
|
||||
@@ -97,6 +97,7 @@ internal static class WebhookMapper
|
||||
switch (webhook.Request.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
model.Request.Body = webhook.Request.BodyData.BodyAsString;
|
||||
break;
|
||||
|
||||
|
||||
@@ -166,4 +166,22 @@ public interface IRespondWithAProvider
|
||||
bool useTransformer = true,
|
||||
TransformerType transformerType = TransformerType.Handlebars
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <param name="data">The data dictionary object.</param>
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithData(object data);
|
||||
|
||||
/// <summary>
|
||||
/// Define the probability when this request should be matched. Value is between 0 and 1.
|
||||
/// </summary>
|
||||
/// <param name="probability">The probability when this request should be matched. Value is between 0 and 1.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider WithProbability(double probability);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -17,6 +18,12 @@ namespace WireMock.Server;
|
||||
/// </summary>
|
||||
internal class RespondWithAProvider : IRespondWithAProvider
|
||||
{
|
||||
private readonly RegistrationCallback _registrationCallback;
|
||||
private readonly IRequestMatcher _requestMatcher;
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
private readonly bool _saveToFile;
|
||||
|
||||
private int _priority;
|
||||
private string? _title;
|
||||
private string? _description;
|
||||
@@ -25,13 +32,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
private string? _nextState;
|
||||
private string? _scenario;
|
||||
private int _timesInSameState = 1;
|
||||
private readonly RegistrationCallback _registrationCallback;
|
||||
private readonly IRequestMatcher _requestMatcher;
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
private readonly bool _saveToFile;
|
||||
|
||||
private bool? _useWebhookFireAndForget;
|
||||
private double? _probability;
|
||||
|
||||
public Guid Guid { get; private set; }
|
||||
|
||||
@@ -39,6 +41,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
|
||||
public ITimeSettings? TimeSettings { get; private set; }
|
||||
|
||||
public object? Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
|
||||
/// </summary>
|
||||
@@ -88,10 +92,21 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
_timesInSameState,
|
||||
Webhooks,
|
||||
_useWebhookFireAndForget,
|
||||
TimeSettings);
|
||||
TimeSettings,
|
||||
Data,
|
||||
_probability);
|
||||
|
||||
_registrationCallback(mapping, _saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider WithData(object data)
|
||||
{
|
||||
Data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider WithGuid(string guid)
|
||||
{
|
||||
@@ -272,6 +287,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
return this;
|
||||
}
|
||||
|
||||
public IRespondWithAProvider WithProbability(double probability)
|
||||
{
|
||||
_probability = Guard.Condition(probability, p => p is >= 0 and <= 1.0);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
private static IWebhook InitWebhook(
|
||||
string url,
|
||||
string method,
|
||||
|
||||
@@ -37,6 +37,8 @@ public partial class WireMockServer
|
||||
private const string AdminRequests = "/__admin/requests";
|
||||
private const string AdminSettings = "/__admin/settings";
|
||||
private const string AdminScenarios = "/__admin/scenarios";
|
||||
private const string AdminOpenApi = "/__admin/openapi";
|
||||
|
||||
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
||||
|
||||
private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
||||
@@ -113,6 +115,10 @@ public partial class WireMockServer
|
||||
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileGet));
|
||||
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingHead()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileHead));
|
||||
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete));
|
||||
|
||||
// __admin/openapi
|
||||
Given(Request.Create().WithPath($"{AdminOpenApi}/convert").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiConvertToMappings));
|
||||
Given(Request.Create().WithPath($"{AdminOpenApi}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(OpenApiSaveToMappings));
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -217,10 +223,17 @@ public partial class WireMockServer
|
||||
var model = new SettingsModel
|
||||
{
|
||||
AllowBodyForAllHttpMethods = _settings.AllowBodyForAllHttpMethods,
|
||||
AllowOnlyDefinedHttpStatusCodeInResponse = _settings.AllowOnlyDefinedHttpStatusCodeInResponse,
|
||||
AllowPartialMapping = _settings.AllowPartialMapping,
|
||||
DisableDeserializeFormUrlEncoded = _settings.DisableDeserializeFormUrlEncoded,
|
||||
DisableJsonBodyParsing = _settings.DisableJsonBodyParsing,
|
||||
DisableRequestBodyDecompressing = _settings.DisableRequestBodyDecompressing,
|
||||
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
|
||||
GlobalProcessingDelay = (int?)_options.RequestProcessingDelay?.TotalMilliseconds,
|
||||
HandleRequestsSynchronously = _settings.HandleRequestsSynchronously,
|
||||
HostingScheme = _settings.HostingScheme,
|
||||
MaxRequestLogCount = _settings.MaxRequestLogCount,
|
||||
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
|
||||
ReadStaticMappings = _settings.ReadStaticMappings,
|
||||
RequestLogExpirationDuration = _settings.RequestLogExpirationDuration,
|
||||
SaveUnmatchedRequests = _settings.SaveUnmatchedRequests,
|
||||
@@ -228,14 +241,11 @@ public partial class WireMockServer
|
||||
UseRegexExtended = _settings.UseRegexExtended,
|
||||
WatchStaticMappings = _settings.WatchStaticMappings,
|
||||
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
|
||||
HostingScheme = _settings.HostingScheme,
|
||||
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
|
||||
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString(),
|
||||
AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate,
|
||||
ClientCertificateMode = _settings.ClientCertificateMode,
|
||||
AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate
|
||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -250,10 +260,16 @@ public partial class WireMockServer
|
||||
|
||||
// _settings
|
||||
_settings.AllowBodyForAllHttpMethods = settings.AllowBodyForAllHttpMethods;
|
||||
_settings.AllowOnlyDefinedHttpStatusCodeInResponse = settings.AllowOnlyDefinedHttpStatusCodeInResponse;
|
||||
_settings.AllowPartialMapping = settings.AllowPartialMapping;
|
||||
_settings.DisableDeserializeFormUrlEncoded = settings.DisableDeserializeFormUrlEncoded;
|
||||
_settings.DisableJsonBodyParsing = settings.DisableJsonBodyParsing;
|
||||
_settings.DisableRequestBodyDecompressing = settings.DisableRequestBodyDecompressing;
|
||||
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||
_settings.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
|
||||
_settings.MaxRequestLogCount = settings.MaxRequestLogCount;
|
||||
_settings.ProxyAndRecordSettings = TinyMapperUtils.Instance.Map(settings.ProxyAndRecordSettings);
|
||||
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
_settings.ReadStaticMappings = settings.ReadStaticMappings;
|
||||
_settings.RequestLogExpirationDuration = settings.RequestLogExpirationDuration;
|
||||
_settings.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
||||
@@ -261,8 +277,6 @@ public partial class WireMockServer
|
||||
_settings.UseRegexExtended = settings.UseRegexExtended;
|
||||
_settings.WatchStaticMappings = settings.WatchStaticMappings;
|
||||
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
|
||||
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
|
||||
InitSettings(_settings);
|
||||
|
||||
@@ -729,7 +743,7 @@ public partial class WireMockServer
|
||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
||||
}
|
||||
|
||||
private static ResponseMessage ToJson<T>(T result, bool keepNullValues = false)
|
||||
private static ResponseMessage ToJson<T>(T result, bool keepNullValues = false, object? statusCode = null)
|
||||
{
|
||||
return new ResponseMessage
|
||||
{
|
||||
@@ -738,7 +752,7 @@ public partial class WireMockServer
|
||||
DetectedBodyType = BodyType.String,
|
||||
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues : JsonSerializationConstants.JsonSerializerSettingsDefault)
|
||||
},
|
||||
StatusCode = (int)HttpStatusCode.OK,
|
||||
StatusCode = statusCode ?? (int)HttpStatusCode.OK,
|
||||
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeJson) } }
|
||||
};
|
||||
}
|
||||
@@ -759,14 +773,18 @@ public partial class WireMockServer
|
||||
|
||||
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
|
||||
{
|
||||
return requestMessage.BodyData?.DetectedBodyType switch
|
||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
BodyType.String => JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!),
|
||||
case BodyType.String:
|
||||
case BodyType.FormUrlEncoded:
|
||||
return JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!);
|
||||
|
||||
BodyType.Json when requestMessage.BodyData?.BodyAsJson != null => ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!,
|
||||
case BodyType.Json when requestMessage.BodyData?.BodyAsJson != null:
|
||||
return ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!;
|
||||
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
default:
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
private static T[] DeserializeRequestMessageToArray<T>(IRequestMessage requestMessage)
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace WireMock.Server
|
||||
}
|
||||
};
|
||||
|
||||
if (BytesEncodingUtils.TryGetEncoding(bytes, out Encoding encoding) && FileBodyIsString.Select(x => x.Equals(encoding)).Any())
|
||||
if (BytesEncodingUtils.TryGetEncoding(bytes, out var encoding) && FileBodyIsString.Select(x => x.Equals(encoding)).Any())
|
||||
{
|
||||
response.BodyData.DetectedBodyType = BodyType.String;
|
||||
response.BodyData.BodyAsString = encoding.GetString(bytes);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
@@ -14,7 +15,7 @@ namespace WireMock.Server;
|
||||
|
||||
public partial class WireMockServer
|
||||
{
|
||||
private void ConvertMappingsAndRegisterAsRespondProvider(MappingModel[] mappingModels, string? path = null)
|
||||
private void ConvertMappingsAndRegisterAsRespondProvider(IReadOnlyList<MappingModel> mappingModels, string? path = null)
|
||||
{
|
||||
var duplicateGuids = mappingModels
|
||||
.Where(m => m.Guid != null)
|
||||
@@ -56,6 +57,11 @@ public partial class WireMockServer
|
||||
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.Data != null)
|
||||
{
|
||||
respondProvider = respondProvider.WithData(mappingModel.Data);
|
||||
}
|
||||
|
||||
var timeSettings = TimeSettingsMapper.Map(mappingModel.TimeSettings);
|
||||
if (timeSettings != null)
|
||||
{
|
||||
@@ -102,7 +108,15 @@ public partial class WireMockServer
|
||||
respondProvider = respondProvider.WithWebhook(webhooks);
|
||||
}
|
||||
|
||||
respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget ?? false);
|
||||
if (mappingModel.UseWebhooksFireAndForget == true)
|
||||
{
|
||||
respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.Probability != null)
|
||||
{
|
||||
respondProvider.WithProbability(mappingModel.Probability.Value);
|
||||
}
|
||||
|
||||
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
||||
respondProvider.RespondWith(responseBuilder);
|
||||
@@ -200,11 +214,11 @@ public partial class WireMockServer
|
||||
{
|
||||
foreach (var cookieModel in requestModel.Cookies.Where(c => c.Matchers != null))
|
||||
{
|
||||
requestBuilder = requestBuilder.WithCookie(
|
||||
cookieModel.Name,
|
||||
cookieModel.IgnoreCase == true,
|
||||
cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
|
||||
cookieModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
||||
requestBuilder = requestBuilder.WithCookie(
|
||||
cookieModel.Name,
|
||||
cookieModel.IgnoreCase == true,
|
||||
cookieModel.RejectOnMatch == true ? MatchBehaviour.RejectOnMatch : MatchBehaviour.AcceptOnMatch,
|
||||
cookieModel.Matchers!.Select(_matcherMapper.Map).OfType<IStringMatcher>().ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
54
src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs
Normal file
54
src/WireMock.Net/Server/WireMockServer.OpenApiParser.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
#if OPENAPIPARSER
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using WireMock.Net.OpenApiParser;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Server;
|
||||
|
||||
public partial class WireMockServer
|
||||
{
|
||||
private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage)
|
||||
{
|
||||
#if OPENAPIPARSER
|
||||
try
|
||||
{
|
||||
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
||||
return diagnostic.Errors.Any() ? ToJson(diagnostic, false, HttpStatusCode.BadRequest) : ToJson(mappingModels);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
|
||||
return ResponseMessageBuilder.Create(e.Message, HttpStatusCode.BadRequest);
|
||||
}
|
||||
#else
|
||||
return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400);
|
||||
#endif
|
||||
}
|
||||
|
||||
private IResponseMessage OpenApiSaveToMappings(IRequestMessage requestMessage)
|
||||
{
|
||||
#if OPENAPIPARSER
|
||||
try
|
||||
{
|
||||
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
||||
if (diagnostic.Errors.Any())
|
||||
{
|
||||
return ToJson(diagnostic, false, HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
ConvertMappingsAndRegisterAsRespondProvider(mappingModels);
|
||||
|
||||
return ResponseMessageBuilder.Create("OpenApi document converted to Mappings", HttpStatusCode.Created);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_settings.Logger.Error("HttpStatusCode set to {0} {1}", HttpStatusCode.BadRequest, e);
|
||||
return ResponseMessageBuilder.Create(e.Message, HttpStatusCode.BadRequest);
|
||||
}
|
||||
#else
|
||||
return ResponseMessageBuilder.Create("Not supported for .NETStandard 1.3 and .NET 4.5.2 or lower.", 400);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user