mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-05-17 13:37:02 +02:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dabe3a2a10 | |||
| 1f1bc05f00 | |||
| c107e38e3b | |||
| b609191095 | |||
| b1ae9aaf46 | |||
| a77c4fe1ac | |||
| ad3ef83c5e | |||
| 35ffbbc7d9 | |||
| 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 |
+38
-1
@@ -1,3 +1,41 @@
|
|||||||
|
# 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)
|
# 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)
|
- [#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)
|
- [#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)
|
||||||
@@ -6,7 +44,6 @@
|
|||||||
# 1.5.19 (17 March 2023)
|
# 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)
|
- [#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)
|
- [#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)
|
||||||
- [#901](https://github.com/WireMock-Net/WireMock.Net/issues/901) - Matching one form-urlencoded value [feature]
|
|
||||||
|
|
||||||
# 1.5.18 (09 March 2023)
|
# 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)
|
- [#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)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.5.20</VersionPrefix>
|
<VersionPrefix>1.5.27</VersionPrefix>
|
||||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
<RepositoryUrl>https://github.com/WireMock-Net/WireMock.Net</RepositoryUrl>
|
||||||
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
||||||
|
<PackageReadmeFile>PackageReadme.md</PackageReadmeFile>
|
||||||
<LangVersion>Latest</LangVersion>
|
<LangVersion>Latest</LangVersion>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -29,7 +30,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
|
<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>
|
</ItemGroup>
|
||||||
|
|
||||||
<Choose>
|
<Choose>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
rem https://github.com/StefH/GitHubReleaseNotes
|
rem https://github.com/StefH/GitHubReleaseNotes
|
||||||
|
|
||||||
SET version=1.5.20
|
SET version=1.5.27
|
||||||
|
|
||||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
||||||
|
|
||||||
|
|||||||
@@ -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,7 @@
|
|||||||
# 1.5.20 (19 March 2023)
|
# 1.5.27 (03 June 2023)
|
||||||
- #905 Add DeserializeFormUrl Encoded to the settings [feature]
|
- #946 Add warning logging when sending a request to a Webhook does not return status 200 [feature]
|
||||||
- #907 Fix issue with application/x-www-form-urlencoded and ExactMatcher [bug]
|
- #949 Add ".NET Framework 4.7" to WireMock.Net.FluentAssertions [feature]
|
||||||
- #906 Upgrade to 1.5.19 breaks a form data test [bug]
|
- #928 TypeLoadException when using WithHeader method. [bug]
|
||||||
|
- #945 Webhook logging [feature]
|
||||||
|
|
||||||
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
||||||
@@ -1,24 +1,28 @@
|
|||||||
# WireMock.Net
|
# 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).
|
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
|
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||||
* Library can be used in unit tests and integration tests
|
* Library can be used in unit tests and integration tests
|
||||||
* Runs as a standalone process, as windows service, as Azure/IIS or as docker
|
* 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)
|
* Record/playback of stubs (proxying)
|
||||||
* Per-request conditional proxying
|
* Per-request conditional proxying
|
||||||
* Stateful behaviour simulation
|
* Stateful behaviour simulation
|
||||||
* Response templating / transformation using Handlebars and extensions
|
* Response templating / transformation using Handlebars and extensions
|
||||||
* Can be used locally or in CI/CD scenarios
|
* 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*** | |
|
| ***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) |
|
| **Issues** | [](https://github.com/WireMock-Net/WireMock.Net/issues) |
|
||||||
| | |
|
| | |
|
||||||
| ***Quality*** | |
|
| ***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) |
|
| **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)|
|
| **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) |
|
| | Official | Preview [:information_source:](https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions) |
|
||||||
| - | - | - |
|
| - | - | - |
|
||||||
@@ -41,23 +45,23 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
|||||||
| **WireMock.Org.RestClient** | [](https://www.nuget.org/packages/WireMock.Org.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
|
| **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.
|
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.
|
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).
|
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).
|
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).
|
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).
|
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:
|
WireMock.Net can be used in several ways:
|
||||||
|
|
||||||
### UnitTesting
|
### UnitTesting
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
|||||||
.github\FUNDING.yml = .github\FUNDING.yml
|
.github\FUNDING.yml = .github\FUNDING.yml
|
||||||
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
|
Generate-ReleaseNotes.cmd = Generate-ReleaseNotes.cmd
|
||||||
nuget.config = nuget.config
|
nuget.config = nuget.config
|
||||||
|
PackageReadme.md = PackageReadme.md
|
||||||
PackageReleaseNotes.template = PackageReleaseNotes.template
|
PackageReleaseNotes.template = PackageReleaseNotes.template
|
||||||
PackageReleaseNotes.txt = PackageReleaseNotes.txt
|
PackageReleaseNotes.txt = PackageReleaseNotes.txt
|
||||||
README.md = README.md
|
README.md = README.md
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Guids/@EntryIndexedValue">True</s:Boolean>
|
<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/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=openapi/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pacticipant/@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/=Raml/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public class DynamicDataGeneration : WireMockOpenApiParserDynamicExampleValues
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
// Since you have your Schema, you can get if max-length is set. You can generate accurate examples with this settings
|
// 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
|
return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
|
|
||||||
@@ -17,8 +18,8 @@ class Program
|
|||||||
|
|
||||||
private static void RunMockServerWithDynamicExampleGeneration()
|
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);
|
var serverCustomer_V2_json = Run.RunServer(Path.Combine(Folder, "Swagger_Customer_V2.0.json"), "http://localhost:8090/", true, new DynamicDataGeneration(), ExampleValueType.Value, ExampleValueType.Value);
|
||||||
Console.WriteLine("Press any key to stop the servers");
|
Console.WriteLine("Press any key to stop the servers");
|
||||||
|
|
||||||
Console.ReadKey();
|
Console.ReadKey();
|
||||||
@@ -27,15 +28,15 @@ class Program
|
|||||||
|
|
||||||
private static void RunOthersOpenApiParserExample()
|
private static void RunOthersOpenApiParserExample()
|
||||||
{
|
{
|
||||||
var serverOpenAPIExamples = Run.RunServer(Path.Combine(Folder, "openAPIExamples.yaml"), "https://localhost:9091/");
|
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"), "https://localhost:9092/");
|
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"), "https://localhost:9093/");
|
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"), "https://localhost:9094/");
|
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"), "https://localhost:9095/");
|
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"), "https://localhost:9096/");
|
var testopenapifile_json = Run.RunServer(Path.Combine(Folder, "testopenapifile.json"), "http://localhost:9096/");
|
||||||
var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "https://localhost:9097/");
|
var file_errorYaml = Run.RunServer(Path.Combine(Folder, "file_error.yaml"), "http://localhost:9097/");
|
||||||
var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "https://localhost:9098/");
|
var file_petJson = Run.RunServer(Path.Combine(Folder, "pet.json"), "http://localhost:9098/");
|
||||||
var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "https://localhost:9099/");
|
var refsYaml = Run.RunServer(Path.Combine(Folder, "refs.yaml"), "http://localhost:9099/");
|
||||||
|
|
||||||
testopenapifile_json
|
testopenapifile_json
|
||||||
.Given(Request.Create().WithPath("/x").UsingGet())
|
.Given(Request.Create().WithPath("/x").UsingGet())
|
||||||
|
|||||||
@@ -9,11 +9,18 @@ using WireMock.Net.OpenApiParser.Types;
|
|||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
using WireMock.Settings;
|
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
|
||||||
{
|
{
|
||||||
@@ -69,4 +76,3 @@ namespace WireMock.Net.OpenApiParser.ConsoleApp
|
|||||||
server.Stop();
|
server.Stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -24,8 +24,8 @@ static class Program
|
|||||||
|
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
await TestAsync().ConfigureAwait(false);
|
//await TestAsync().ConfigureAwait(false);
|
||||||
return;
|
//return;
|
||||||
|
|
||||||
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using log4net;
|
using log4net;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using WireMock.Admin.Requests;
|
using WireMock.Admin.Requests;
|
||||||
using WireMock.Logging;
|
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));
|
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||||
@@ -41,4 +41,3 @@ namespace WireMock.Net.StandAlone.NETCoreApp
|
|||||||
Log.DebugFormat("Admin[{0}] {1}", isAdminRequest, message);
|
Log.DebugFormat("Admin[{0}] {1}", isAdminRequest, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -19,12 +19,12 @@ public class CookieModel
|
|||||||
public IList<MatcherModel>? Matchers { get; set; }
|
public IList<MatcherModel>? Matchers { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ignore case.
|
/// Gets or sets the ignore case for the Cookie Name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? IgnoreCase { get; set; }
|
public bool? IgnoreCase { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reject on match.
|
/// Gets or sets the Reject on match for the Cookie Name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? RejectOnMatch { get; set; }
|
public bool? RejectOnMatch { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
namespace WireMock.Admin.Mappings
|
namespace WireMock.Admin.Mappings;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fault Model
|
/// Fault Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -7,13 +7,12 @@
|
|||||||
public class FaultModel
|
public class FaultModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE, MALFORMED_RESPONSE_CHUNK or RANDOM_DATA_THEN_CLOSE.
|
/// Gets or sets the fault. Can be null, "", NONE, EMPTY_RESPONSE or MALFORMED_RESPONSE_CHUNK.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Type { get; set; }
|
public string? Type { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the fault percentage.
|
/// Gets or sets the fault percentage.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? Percentage { get; set; }
|
public double? Percentage { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ namespace WireMock.Admin.Mappings;
|
|||||||
public class HeaderModel
|
public class HeaderModel
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the name.
|
/// Gets or sets the name (key).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; } = null!;
|
public string Name { get; set; } = null!;
|
||||||
|
|
||||||
@@ -19,12 +19,12 @@ public class HeaderModel
|
|||||||
public IList<MatcherModel>? Matchers { get; set; }
|
public IList<MatcherModel>? Matchers { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the ignore case.
|
/// Gets or sets the ignore case for the Header Key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? IgnoreCase { get; set; }
|
public bool? IgnoreCase { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reject on match.
|
/// Gets or sets the Reject on match for the Header Key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? RejectOnMatch { get; set; }
|
public bool? RejectOnMatch { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Admin.Mappings;
|
namespace WireMock.Admin.Mappings;
|
||||||
@@ -94,4 +93,9 @@ public class MappingModel
|
|||||||
/// </example>
|
/// </example>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Data { get; set; }
|
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; }
|
||||||
}
|
}
|
||||||
@@ -68,4 +68,9 @@ public class ProxyAndRecordSettingsModel
|
|||||||
/// Append an unique GUID to the filename from the saved mapping file.
|
/// Append an unique GUID to the filename from the saved mapping file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AppendGuidToSavedMappingFile { get; set; }
|
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; }
|
||||||
|
}
|
||||||
@@ -143,6 +143,14 @@ public interface IRequestMessage
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
string Origin { get; }
|
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
|
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the connection's client certificate
|
/// Gets the connection's client certificate
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace WireMock.ResponseBuilders
|
// ReSharper disable InconsistentNaming
|
||||||
{
|
namespace WireMock.ResponseBuilders;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The FaultType enumeration
|
/// The FaultType enumeration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -20,4 +21,3 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
MALFORMED_RESPONSE_CHUNK
|
MALFORMED_RESPONSE_CHUNK
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -17,6 +17,11 @@ public interface IWireMockServer : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsStarted { get; }
|
bool IsStarted { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this server is started with the admin interface enabled.
|
||||||
|
/// </summary>
|
||||||
|
bool IsStartedWithAdminInterface { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the request logs.
|
/// Gets the request logs.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
|
|
||||||
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
|
<!-- 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>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
<Description>FluentAssertions extensions for WireMock.Net</Description>
|
||||||
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
<AssemblyTitle>WireMock.Net.FluentAssertions</AssemblyTitle>
|
||||||
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
<Authors>Mahmoud Ali;Stef Heyenrath</Authors>
|
||||||
<TargetFrameworks>net451;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>net451;net47;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
<AssemblyName>WireMock.Net.FluentAssertions</AssemblyName>
|
||||||
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
<PackageId>WireMock.Net.FluentAssertions</PackageId>
|
||||||
|
|||||||
@@ -31,13 +31,13 @@ public static class WireMockServerExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
Guard.NotNull(server, nameof(server));
|
Guard.NotNull(server);
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
||||||
|
|
||||||
@@ -80,9 +80,9 @@ public static class WireMockServerExtensions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
||||||
/// <param name="settings">Additional settings [optional]</param>
|
/// <param name="settings">Additional settings [optional].</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings)
|
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings? settings = null)
|
||||||
{
|
{
|
||||||
Guard.NotNull(server);
|
Guard.NotNull(server);
|
||||||
Guard.NotNull(document);
|
Guard.NotNull(document);
|
||||||
|
|||||||
@@ -5,53 +5,69 @@ using Microsoft.OpenApi.Readers;
|
|||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser
|
namespace WireMock.Net.OpenApiParser;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
|
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock MappingModels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IWireMockOpenApiParser
|
public interface IWireMockOpenApiParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
|
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a file-path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
||||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic);
|
IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a file-path.
|
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a file-path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
/// <param name="path">The path to read the OpenApi/Swagger/V2/V3 or Raml file.</param>
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
IReadOnlyList<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
|
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from an <seealso cref="OpenApiDocument"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="document">The source OpenApiDocument</param>
|
/// <param name="document">The source OpenApiDocument</param>
|
||||||
/// <param name="settings">Additional settings [optional]</param>
|
/// <param name="settings">Additional settings [optional]</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
|
IReadOnlyList<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="Stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The source stream</param>
|
/// <param name="stream">The source stream</param>
|
||||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
|
IReadOnlyList<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
/// Generate <see cref="IReadOnlyList{MappingModel}"/> from a <seealso cref="Stream"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="stream">The source stream</param>
|
/// <param name="stream">The source stream</param>
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
/// <param name="diagnostic">OpenApiDiagnostic output</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic);
|
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);
|
||||||
}
|
}
|
||||||
@@ -23,27 +23,28 @@ internal class OpenApiPathsMapper
|
|||||||
private const string HeaderContentType = "Content-Type";
|
private const string HeaderContentType = "Content-Type";
|
||||||
|
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
private readonly WireMockOpenApiParserSettings _settings;
|
||||||
private readonly ExampleValueGenerator _exampleValueGenerator;
|
private readonly IExampleValueGenerator _exampleValueGenerator;
|
||||||
|
private readonly IExampleValueGenerator _regexExampleValueGenerator;
|
||||||
|
|
||||||
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings);
|
_settings = Guard.NotNull(settings);
|
||||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
||||||
|
_regexExampleValueGenerator = new RegexExampleValueGenerator(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> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||||
{
|
{
|
||||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers)).ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
|
||||||
{
|
|
||||||
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
||||||
@@ -123,7 +124,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;
|
openApiMediaType = null;
|
||||||
contentType = null;
|
contentType = null;
|
||||||
@@ -270,54 +271,19 @@ internal class OpenApiPathsMapper
|
|||||||
return newPath;
|
return newPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MapBasePath(IList<OpenApiServer>? servers)
|
private IDictionary<string, object>? MapHeaders(string? responseContentType, IDictionary<string, OpenApiHeader>? headers)
|
||||||
{
|
{
|
||||||
if (servers == null || servers.Count == 0)
|
var mappedHeaders = headers?.ToDictionary(
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenApiServer server = servers.First();
|
|
||||||
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
|
||||||
{
|
|
||||||
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
|
|
||||||
{
|
|
||||||
if (any == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var outputString = new StringWriter();
|
|
||||||
var writer = new OpenApiJsonWriter(outputString);
|
|
||||||
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
|
||||||
|
|
||||||
if (any.AnyType == AnyType.Array)
|
|
||||||
{
|
|
||||||
return JArray.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return JObject.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
|
|
||||||
{
|
|
||||||
var mappedHeaders = headers.ToDictionary(
|
|
||||||
item => item.Key,
|
item => item.Key,
|
||||||
_ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
|
_ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern!
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(responseContentType))
|
if (!string.IsNullOrEmpty(responseContentType))
|
||||||
{
|
{
|
||||||
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
mappedHeaders.TryAdd(HeaderContentType, responseContentType!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
return mappedHeaders?.Keys.Any() == true ? mappedHeaders : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
|
private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
|
||||||
@@ -356,19 +322,38 @@ internal class OpenApiPathsMapper
|
|||||||
return list.Any() ? list : null;
|
return list.Any() ? list : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type)
|
private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType exampleValueType)
|
||||||
{
|
{
|
||||||
return type switch
|
return exampleValueType switch
|
||||||
{
|
{
|
||||||
ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues },
|
ExampleValueType.Value => new MatcherModel
|
||||||
|
{
|
||||||
|
Name = "ExactMatcher",
|
||||||
|
Pattern = GetExampleValueAsStringForSchemaType(schema, exampleValueType),
|
||||||
|
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||||
|
},
|
||||||
|
|
||||||
_ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" }
|
ExampleValueType.Regex => new MatcherModel
|
||||||
|
{
|
||||||
|
Name = "RegexMatcher",
|
||||||
|
Pattern = GetExampleValueAsStringForSchemaType(schema, exampleValueType),
|
||||||
|
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => new MatcherModel
|
||||||
|
{
|
||||||
|
Name = "WildcardMatcher",
|
||||||
|
Pattern = "*",
|
||||||
|
IgnoreCase = _settings.IgnoreCaseExampleValues
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
|
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema, ExampleValueType exampleValueType)
|
||||||
{
|
{
|
||||||
var value = _exampleValueGenerator.GetExampleValue(schema);
|
var value = exampleValueType == ExampleValueType.Regex ?
|
||||||
|
_regexExampleValueGenerator.GetExampleValue(schema) :
|
||||||
|
_exampleValueGenerator.GetExampleValue(schema);
|
||||||
|
|
||||||
return value switch
|
return value switch
|
||||||
{
|
{
|
||||||
@@ -377,4 +362,39 @@ internal class OpenApiPathsMapper
|
|||||||
_ => value.ToString(),
|
_ => value.ToString(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string MapBasePath(IList<OpenApiServer>? servers)
|
||||||
|
{
|
||||||
|
if (servers == null || servers.Count == 0)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
var server = servers.First();
|
||||||
|
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
||||||
|
{
|
||||||
|
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
|
||||||
|
{
|
||||||
|
if (any == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var outputString = new StringWriter();
|
||||||
|
var writer = new OpenApiJsonWriter(outputString);
|
||||||
|
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
||||||
|
|
||||||
|
if (any.AnyType == AnyType.Array)
|
||||||
|
{
|
||||||
|
return JArray.Parse(outputString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return JObject.Parse(outputString.ToString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -36,5 +36,5 @@ public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleV
|
|||||||
public virtual string String { get; set; } = "example-string";
|
public virtual string String { get; set; } = "example-string";
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
|
public virtual OpenApiSchema? Schema { get; set; } = new();
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types;
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value to use
|
/// The (example) value pattern to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ExampleValueType
|
public enum ExampleValueType
|
||||||
{
|
{
|
||||||
@@ -12,6 +12,11 @@ public enum ExampleValueType
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Value,
|
Value,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build a Regex based on the SchemaType.
|
||||||
|
/// </summary>
|
||||||
|
Regex,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Just use a Wildcard (*) character.
|
/// Just use a Wildcard (*) character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.OpenApi.Any;
|
using Microsoft.OpenApi.Any;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
@@ -9,118 +8,110 @@ using WireMock.Net.OpenApiParser.Types;
|
|||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Utils;
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
internal class ExampleValueGenerator
|
internal class ExampleValueGenerator : IExampleValueGenerator
|
||||||
{
|
{
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
private readonly IWireMockOpenApiParserExampleValues _exampleValues;
|
||||||
|
|
||||||
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings);
|
Guard.NotNull(settings);
|
||||||
|
|
||||||
// Check if user provided an own implementation
|
// Check if user provided an own implementation
|
||||||
if (settings.ExampleValues is null)
|
if (settings.ExampleValues is null)
|
||||||
{
|
{
|
||||||
if (_settings.DynamicExamples)
|
if (settings.DynamicExamples)
|
||||||
{
|
{
|
||||||
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
_exampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
|
_exampleValues = new WireMockOpenApiParserExampleValues();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_exampleValues = settings.ExampleValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public object GetExampleValue(OpenApiSchema? schema)
|
public object GetExampleValue(OpenApiSchema? schema)
|
||||||
{
|
{
|
||||||
var schemaExample = schema?.Example;
|
var schemaExample = schema?.Example;
|
||||||
var schemaEnum = GetRandomEnumValue(schema?.Enum);
|
var schemaEnum = schema?.Enum?.FirstOrDefault();
|
||||||
|
|
||||||
_settings.ExampleValues.Schema = schema;
|
_exampleValues.Schema = schema;
|
||||||
|
|
||||||
switch (schema?.GetSchemaType())
|
switch (schema?.GetSchemaType())
|
||||||
{
|
{
|
||||||
case SchemaType.Boolean:
|
case SchemaType.Boolean:
|
||||||
var exampleBoolean = schemaExample as OpenApiBoolean;
|
var exampleBoolean = schemaExample as OpenApiBoolean;
|
||||||
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
|
return exampleBoolean?.Value ?? _exampleValues.Boolean;
|
||||||
|
|
||||||
case SchemaType.Integer:
|
case SchemaType.Integer:
|
||||||
switch (schema?.GetSchemaFormat())
|
switch (schema?.GetSchemaFormat())
|
||||||
{
|
{
|
||||||
case SchemaFormat.Int64:
|
case SchemaFormat.Int64:
|
||||||
var exampleLong = (OpenApiLong)schemaExample;
|
var exampleLong = schemaExample as OpenApiLong;
|
||||||
var enumLong = (OpenApiLong)schemaEnum;
|
var enumLong = schemaEnum as OpenApiLong;
|
||||||
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
|
var valueLongEnumOrExample = enumLong?.Value ?? exampleLong?.Value;
|
||||||
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
|
return valueLongEnumOrExample ?? _exampleValues.Integer;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var exampleInteger = (OpenApiInteger)schemaExample;
|
var exampleInteger = schemaExample as OpenApiInteger;
|
||||||
var enumInteger = (OpenApiInteger)schemaEnum;
|
var enumInteger = schemaEnum as OpenApiInteger;
|
||||||
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
|
var valueIntegerEnumOrExample = enumInteger?.Value ?? exampleInteger?.Value;
|
||||||
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
|
return valueIntegerEnumOrExample ?? _exampleValues.Integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SchemaType.Number:
|
case SchemaType.Number:
|
||||||
switch (schema?.GetSchemaFormat())
|
switch (schema?.GetSchemaFormat())
|
||||||
{
|
{
|
||||||
case SchemaFormat.Float:
|
case SchemaFormat.Float:
|
||||||
var exampleFloat = (OpenApiFloat)schemaExample;
|
var exampleFloat = schemaExample as OpenApiFloat;
|
||||||
var enumFloat = (OpenApiFloat)schemaEnum;
|
var enumFloat = schemaEnum as OpenApiFloat;
|
||||||
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
|
var valueFloatEnumOrExample = enumFloat?.Value ?? exampleFloat?.Value;
|
||||||
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
|
return valueFloatEnumOrExample ?? _exampleValues.Float;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var exampleDouble = (OpenApiDouble)schemaExample;
|
var exampleDouble = schemaExample as OpenApiDouble;
|
||||||
var enumDouble = (OpenApiDouble)schemaEnum;
|
var enumDouble = schemaEnum as OpenApiDouble;
|
||||||
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
|
var valueDoubleEnumOrExample = enumDouble?.Value ?? exampleDouble?.Value;
|
||||||
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
|
return valueDoubleEnumOrExample ?? _exampleValues.Double;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
switch (schema?.GetSchemaFormat())
|
switch (schema?.GetSchemaFormat())
|
||||||
{
|
{
|
||||||
case SchemaFormat.Date:
|
case SchemaFormat.Date:
|
||||||
var exampleDate = (OpenApiDate)schemaExample;
|
var exampleDate = schemaExample as OpenApiDate;
|
||||||
var enumDate = (OpenApiDate)schemaEnum;
|
var enumDate = schemaEnum as OpenApiDate;
|
||||||
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
|
var valueDateEnumOrExample = enumDate?.Value ?? exampleDate?.Value;
|
||||||
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
|
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _exampleValues.Date());
|
||||||
|
|
||||||
case SchemaFormat.DateTime:
|
case SchemaFormat.DateTime:
|
||||||
var exampleDateTime = (OpenApiDateTime)schemaExample;
|
var exampleDateTime = schemaExample as OpenApiDateTime;
|
||||||
var enumDateTime = (OpenApiDateTime)schemaEnum;
|
var enumDateTime = schemaEnum as OpenApiDateTime;
|
||||||
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
|
var valueDateTimeEnumOrExample = enumDateTime?.Value ?? exampleDateTime?.Value;
|
||||||
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
|
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _exampleValues.DateTime());
|
||||||
|
|
||||||
case SchemaFormat.Byte:
|
case SchemaFormat.Byte:
|
||||||
var exampleByte = (OpenApiByte)schemaExample;
|
var exampleByte = schemaExample as OpenApiByte;
|
||||||
var enumByte = (OpenApiByte)schemaEnum;
|
var enumByte = schemaEnum as OpenApiByte;
|
||||||
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
|
var valueByteEnumOrExample = enumByte?.Value ?? exampleByte?.Value;
|
||||||
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
|
return valueByteEnumOrExample ?? _exampleValues.Bytes;
|
||||||
|
|
||||||
case SchemaFormat.Binary:
|
case SchemaFormat.Binary:
|
||||||
var exampleBinary = (OpenApiBinary)schemaExample;
|
var exampleBinary = schemaExample as OpenApiBinary;
|
||||||
var enumBinary = (OpenApiBinary)schemaEnum;
|
var enumBinary = schemaEnum as OpenApiBinary;
|
||||||
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
|
var valueBinaryEnumOrExample = enumBinary?.Value ?? exampleBinary?.Value;
|
||||||
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
|
return valueBinaryEnumOrExample ?? _exampleValues.Object;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
var exampleString = (OpenApiString)schemaExample;
|
var exampleString = schemaExample as OpenApiString;
|
||||||
var enumString = (OpenApiString)schemaEnum;
|
var enumString = schemaEnum as OpenApiString;
|
||||||
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
|
var valueStringEnumOrExample = enumString?.Value ?? exampleString?.Value;
|
||||||
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
|
internal interface IExampleValueGenerator
|
||||||
|
{
|
||||||
|
object GetExampleValue(OpenApiSchema? schema);
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Stef.Validation;
|
||||||
|
using WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
|
internal class RegexExampleValueGenerator : IExampleValueGenerator
|
||||||
|
{
|
||||||
|
public RegexExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
||||||
|
{
|
||||||
|
Guard.NotNull(settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetExampleValue(OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
switch (schema?.GetSchemaType())
|
||||||
|
{
|
||||||
|
case SchemaType.Boolean:
|
||||||
|
return @"(true|false)";
|
||||||
|
|
||||||
|
case SchemaType.Integer:
|
||||||
|
return @"-?\d+";
|
||||||
|
|
||||||
|
case SchemaType.Number:
|
||||||
|
return @"[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return schema?.GetSchemaFormat() switch
|
||||||
|
{
|
||||||
|
SchemaFormat.Date => @"(\d{4})-([01]\d)-([0-3]\d)",
|
||||||
|
SchemaFormat.DateTime => @"(\d{4})-([01]\d)-([0-3]\d)T([0-2]\d):([0-5]\d):([0-5]\d)(\.\d+)?(Z|[+-][0-2]\d:[0-5]\d)",
|
||||||
|
SchemaFormat.Byte => @"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)",
|
||||||
|
SchemaFormat.Binary => @"[a-zA-Z0-9\+/]*={0,3}",
|
||||||
|
_ => ".*"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<Description>An OpenApi (swagger) parser to generate MappingModel or mapping.json file.</Description>
|
<Description>An OpenApi (swagger) parser to generate MappingModel or mapping.json file.</Description>
|
||||||
<TargetFrameworks>net46;netstandard2.0;netstandard2.1</TargetFrameworks>
|
<TargetFrameworks>net46;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<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>
|
<ProjectGuid>{D3804228-91F4-4502-9595-39584E5AADAD}</ProjectGuid>
|
||||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Microsoft.OpenApi.Readers;
|
using Microsoft.OpenApi.Readers;
|
||||||
@@ -8,6 +9,7 @@ using RamlToOpenApiConverter;
|
|||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Net.OpenApiParser.Mappers;
|
using WireMock.Net.OpenApiParser.Mappers;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser;
|
namespace WireMock.Net.OpenApiParser;
|
||||||
|
|
||||||
@@ -16,18 +18,25 @@ namespace WireMock.Net.OpenApiParser;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
||||||
{
|
{
|
||||||
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
private readonly WireMockOpenApiParserSettings _wireMockOpenApiParserSettings = new WireMockOpenApiParserSettings
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
|
||||||
[PublicAPI]
|
|
||||||
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
|
||||||
{
|
{
|
||||||
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
HeaderPatternToUse = ExampleValueType.Regex,
|
||||||
|
QueryParameterPatternToUse = ExampleValueType.Regex,
|
||||||
|
PathPatternToUse = ExampleValueType.Regex
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly OpenApiStreamReader _reader = new();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[PublicAPI]
|
||||||
|
public IReadOnlyList<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
return FromFile(path, _wireMockOpenApiParserSettings, out diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[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;
|
OpenApiDocument document;
|
||||||
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -44,24 +53,38 @@ public class WireMockOpenApiParser : IWireMockOpenApiParser
|
|||||||
return FromDocument(document, settings);
|
return FromDocument(document, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
public IReadOnlyList<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
|
||||||
|
{
|
||||||
|
return new OpenApiPathsMapper(settings ?? _wireMockOpenApiParserSettings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[PublicAPI]
|
||||||
|
public IReadOnlyList<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return FromDocument(_reader.Read(stream, out diagnostic));
|
return FromDocument(_reader.Read(stream, out diagnostic));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[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);
|
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[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>
|
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||||
[Head("files/{filename}")]
|
[Head("files/{filename}")]
|
||||||
Task FileExistsAsync([Path] string filename, CancellationToken cancellationToken = default);
|
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);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"help": "https://go.microsoft.com/fwlink/?linkid=866610",
|
||||||
|
"root": true,
|
||||||
|
|
||||||
|
"dependentFileProviders": {
|
||||||
|
"add": {
|
||||||
|
"addedExtension": {},
|
||||||
|
"pathSegment": {
|
||||||
|
"add": {
|
||||||
|
".*": [
|
||||||
|
".cs"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
namespace WireMock.Exceptions;
|
||||||
|
|
||||||
namespace WireMock.Exceptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WireMockException
|
/// WireMockException
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -32,4 +32,3 @@ namespace WireMock.Exceptions
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -3,8 +3,8 @@ using System.IO;
|
|||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Handlers
|
namespace WireMock.Handlers;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
/// Default implementation for a handler to interact with the local file system to read and write static mapping files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -34,7 +34,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
/// <inheritdoc cref="IFileSystemHandler.FolderExists"/>
|
||||||
public virtual bool FolderExists(string path)
|
public virtual bool FolderExists(string path)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
return Directory.Exists(path);
|
return Directory.Exists(path);
|
||||||
}
|
}
|
||||||
@@ -42,7 +42,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
/// <inheritdoc cref="IFileSystemHandler.CreateFolder"/>
|
||||||
public virtual void CreateFolder(string path)
|
public virtual void CreateFolder(string path)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
Directory.CreateDirectory(path);
|
Directory.CreateDirectory(path);
|
||||||
}
|
}
|
||||||
@@ -50,7 +50,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
/// <inheritdoc cref="IFileSystemHandler.EnumerateFiles"/>
|
||||||
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
public virtual IEnumerable<string> EnumerateFiles(string path, bool includeSubdirectories)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
return includeSubdirectories ? Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories) : Directory.EnumerateFiles(path);
|
||||||
}
|
}
|
||||||
@@ -64,7 +64,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
/// <inheritdoc cref="IFileSystemHandler.ReadMappingFile"/>
|
||||||
public virtual string ReadMappingFile(string path)
|
public virtual string ReadMappingFile(string path)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
return File.ReadAllText(path);
|
return File.ReadAllText(path);
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsFile"/>
|
||||||
public virtual byte[] ReadResponseBodyAsFile(string path)
|
public virtual byte[] ReadResponseBodyAsFile(string path)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
path = PathUtils.CleanPath(path);
|
path = PathUtils.CleanPath(path);
|
||||||
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||||
// Else the path will just be as-is.
|
// Else the path will just be as-is.
|
||||||
@@ -91,7 +91,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||||
public virtual string ReadResponseBodyAsString(string path)
|
public virtual string ReadResponseBodyAsString(string path)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path);
|
||||||
path = PathUtils.CleanPath(path);
|
path = PathUtils.CleanPath(path);
|
||||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||||
// Else the path will just be as-is.
|
// Else the path will just be as-is.
|
||||||
@@ -101,7 +101,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||||
public virtual bool FileExists(string filename)
|
public virtual bool FileExists(string filename)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
|
||||||
return File.Exists(AdjustPathForMappingFolder(filename));
|
return File.Exists(AdjustPathForMappingFolder(filename));
|
||||||
}
|
}
|
||||||
@@ -109,8 +109,8 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual void WriteFile(string filename, byte[] bytes)
|
public virtual void WriteFile(string filename, byte[] bytes)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
Guard.NotNullOrEmpty(filename);
|
||||||
Guard.NotNull(bytes, nameof(bytes));
|
Guard.NotNull(bytes);
|
||||||
|
|
||||||
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
File.WriteAllBytes(AdjustPathForMappingFolder(filename), bytes);
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
/// <inheritdoc cref="IFileSystemHandler.DeleteFile"/>
|
||||||
public virtual void DeleteFile(string filename)
|
public virtual void DeleteFile(string filename)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
|
||||||
File.Delete(AdjustPathForMappingFolder(filename));
|
File.Delete(AdjustPathForMappingFolder(filename));
|
||||||
}
|
}
|
||||||
@@ -136,7 +136,7 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
/// <inheritdoc cref="IFileSystemHandler.ReadFile"/>
|
||||||
public virtual byte[] ReadFile(string filename)
|
public virtual byte[] ReadFile(string filename)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
Guard.NotNullOrEmpty(filename);
|
||||||
|
|
||||||
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
return File.ReadAllBytes(AdjustPathForMappingFolder(filename));
|
||||||
}
|
}
|
||||||
@@ -156,8 +156,8 @@ namespace WireMock.Handlers
|
|||||||
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
/// <inheritdoc cref="IFileSystemHandler.WriteUnmatchedRequest"/>
|
||||||
public virtual void WriteUnmatchedRequest(string filename, string text)
|
public virtual void WriteUnmatchedRequest(string filename, string text)
|
||||||
{
|
{
|
||||||
Guard.NotNullOrEmpty(filename, nameof(filename));
|
Guard.NotNullOrEmpty(filename);
|
||||||
Guard.NotNull(text, nameof(text));
|
Guard.NotNull(text);
|
||||||
|
|
||||||
var folder = GetUnmatchedRequestsFolder();
|
var folder = GetUnmatchedRequestsFolder();
|
||||||
if (!FolderExists(folder))
|
if (!FolderExists(folder))
|
||||||
@@ -178,4 +178,3 @@ namespace WireMock.Handlers
|
|||||||
return Path.Combine(GetMappingFolder(), filename);
|
return Path.Combine(GetMappingFolder(), filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
@@ -121,7 +120,7 @@ public interface IMapping
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool? UseWebhooksFireAndForget { get; set; }
|
bool? UseWebhooksFireAndForget { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Data Object which can be used when WithTransformer is used.
|
/// Data Object which can be used when WithTransformer is used.
|
||||||
@@ -130,7 +129,12 @@ public interface IMapping
|
|||||||
/// lookup data "1"
|
/// lookup data "1"
|
||||||
/// </example>
|
/// </example>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object? Data { get; set; }
|
object? Data { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
|
||||||
|
/// </summary>
|
||||||
|
double? Probability { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ProvideResponseAsync
|
/// ProvideResponseAsync
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using System.Linq.Dynamic.Core;
|
|||||||
|
|
||||||
namespace WireMock.Json;
|
namespace WireMock.Json;
|
||||||
|
|
||||||
public class DynamicPropertyWithValue : DynamicProperty
|
internal class DynamicPropertyWithValue : DynamicProperty
|
||||||
{
|
{
|
||||||
public object? Value { get; }
|
public object? Value { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -67,13 +67,16 @@ public class Mapping : IMapping
|
|||||||
public IWebhook[]? Webhooks { get; }
|
public IWebhook[]? Webhooks { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool? UseWebhooksFireAndForget { get; set; }
|
public bool? UseWebhooksFireAndForget { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ITimeSettings? TimeSettings { get; }
|
public ITimeSettings? TimeSettings { get; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public object? Data { get; set; }
|
public object? Data { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public double? Probability { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||||
@@ -95,6 +98,7 @@ public class Mapping : IMapping
|
|||||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||||
/// <param name="data">The data object. [Optional]</param>
|
/// <param name="data">The data object. [Optional]</param>
|
||||||
|
/// <param name="probability">Define the probability when this request should be matched. [Optional]</param>
|
||||||
public Mapping(
|
public Mapping(
|
||||||
Guid guid,
|
Guid guid,
|
||||||
DateTime updatedAt,
|
DateTime updatedAt,
|
||||||
@@ -112,7 +116,8 @@ public class Mapping : IMapping
|
|||||||
IWebhook[]? webhooks,
|
IWebhook[]? webhooks,
|
||||||
bool? useWebhooksFireAndForget,
|
bool? useWebhooksFireAndForget,
|
||||||
ITimeSettings? timeSettings,
|
ITimeSettings? timeSettings,
|
||||||
object? data)
|
object? data,
|
||||||
|
double? probability)
|
||||||
{
|
{
|
||||||
Guid = guid;
|
Guid = guid;
|
||||||
UpdatedAt = updatedAt;
|
UpdatedAt = updatedAt;
|
||||||
@@ -131,6 +136,7 @@ public class Mapping : IMapping
|
|||||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||||
TimeSettings = timeSettings;
|
TimeSettings = timeSettings;
|
||||||
Data = data;
|
Data = data;
|
||||||
|
Probability = probability;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||||
|
|||||||
@@ -69,12 +69,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MappingModel[] GetMappings()
|
public MappingModel[] GetMappings()
|
||||||
{
|
{
|
||||||
return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray();
|
return GetNonAdminMappings().Select(_mappingConverter.ToMappingModel).ToArray();
|
||||||
}
|
|
||||||
|
|
||||||
internal IMapping[] GetMappingsInternal()
|
|
||||||
{
|
|
||||||
return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -86,7 +81,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
|
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)
|
if (mapping is null)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
@@ -101,7 +96,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
{
|
{
|
||||||
var sb = new StringBuilder();
|
var sb = new StringBuilder();
|
||||||
bool addStart = true;
|
bool addStart = true;
|
||||||
foreach (var mapping in GetMappingsInternal())
|
foreach (var mapping in GetNonAdminMappings())
|
||||||
{
|
{
|
||||||
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
||||||
|
|
||||||
@@ -123,7 +118,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void SaveMappingsToFolder(string? folder)
|
public void SaveMappingsToFolder(string? folder)
|
||||||
{
|
{
|
||||||
foreach (var mapping in GetNonAdminMappings().Where(m => !m.IsAdminInterface))
|
foreach (var mapping in GetNonAdminMappings())
|
||||||
{
|
{
|
||||||
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
||||||
}
|
}
|
||||||
@@ -131,7 +126,7 @@ public class MappingBuilder : IMappingBuilder
|
|||||||
|
|
||||||
private IMapping[] GetNonAdminMappings()
|
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)
|
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using AnyOfTypes;
|
using AnyOfTypes;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
|
||||||
namespace WireMock.Matchers;
|
namespace WireMock.Matchers;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Matchers.Request;
|
namespace WireMock.Matchers.Request;
|
||||||
@@ -25,9 +24,9 @@ public abstract class RequestMessageCompositeMatcher : IRequestMatcher
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestMatchers">The request matchers.</param>
|
/// <param name="requestMatchers">The request matchers.</param>
|
||||||
/// <param name="type">The CompositeMatcherType type (Defaults to 'And')</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;
|
_type = type;
|
||||||
RequestMatchers = requestMatchers;
|
RequestMatchers = requestMatchers;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace WireMock.Models
|
namespace WireMock.Models;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// TimeSettingsModel: Start, End and TTL
|
/// TimeSettingsModel: Start, End and TTL
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -16,4 +16,3 @@ namespace WireMock.Models
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public int? TTL { get; set; }
|
public int? TTL { get; set; }
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Models
|
namespace WireMock.Models;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// UrlDetails
|
/// UrlDetails
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -41,11 +41,7 @@ namespace WireMock.Models
|
|||||||
/// <param name="url">The URL (relative).</param>
|
/// <param name="url">The URL (relative).</param>
|
||||||
public UrlDetails(Uri absoluteUrl, Uri url)
|
public UrlDetails(Uri absoluteUrl, Uri url)
|
||||||
{
|
{
|
||||||
Guard.NotNull(absoluteUrl, nameof(absoluteUrl));
|
AbsoluteUrl = Guard.NotNull(absoluteUrl);
|
||||||
Guard.NotNull(url, nameof(url));
|
Url = Guard.NotNull(url);
|
||||||
|
|
||||||
AbsoluteUrl = absoluteUrl;
|
|
||||||
Url = url;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.Mappers;
|
using WireMock.Owin.Mappers;
|
||||||
|
using WireMock.Services;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
namespace WireMock.Owin
|
namespace WireMock.Owin
|
||||||
@@ -66,6 +67,7 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
services.AddSingleton(_wireMockMiddlewareOptions);
|
services.AddSingleton(_wireMockMiddlewareOptions);
|
||||||
services.AddSingleton<IMappingMatcher, MappingMatcher>();
|
services.AddSingleton<IMappingMatcher, MappingMatcher>();
|
||||||
|
services.AddSingleton<IRandomizerDoubleBetween0And1, RandomizerDoubleBetween0And1>();
|
||||||
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
|
services.AddSingleton<IOwinRequestMapper, OwinRequestMapper>();
|
||||||
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
services.AddSingleton<IOwinResponseMapper, OwinResponseMapper>();
|
||||||
|
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ namespace WireMock.Owin.Mappers
|
|||||||
switch (responseMessage.FaultType)
|
switch (responseMessage.FaultType)
|
||||||
{
|
{
|
||||||
case FaultType.EMPTY_RESPONSE:
|
case FaultType.EMPTY_RESPONSE:
|
||||||
bytes = IsFault(responseMessage) ? new byte[0] : GetNormalBody(responseMessage);
|
bytes = IsFault(responseMessage) ? EmptyArray<byte>.Value : GetNormalBody(responseMessage);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FaultType.MALFORMED_RESPONSE_CHUNK:
|
case FaultType.MALFORMED_RESPONSE_CHUNK:
|
||||||
bytes = GetNormalBody(responseMessage) ?? new byte[0];
|
bytes = GetNormalBody(responseMessage) ?? EmptyArray<byte>.Value;
|
||||||
if (IsFault(responseMessage))
|
if (IsFault(responseMessage))
|
||||||
{
|
{
|
||||||
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
bytes = bytes.Take(bytes.Length / 2).Union(_randomizerBytes.Generate()).ToArray();
|
||||||
@@ -87,7 +87,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
|
|
||||||
case { } typeAsString when typeAsString == typeof(string):
|
case { } typeAsString when typeAsString == typeof(string):
|
||||||
// Note: this case will also match on null
|
// Note: this case will also match on null
|
||||||
int.TryParse(responseMessage.StatusCode as string, out int result);
|
int.TryParse(responseMessage.StatusCode as string, out var result);
|
||||||
response.StatusCode = MapStatusCode(result);
|
response.StatusCode = MapStatusCode(result);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using WireMock.Extensions;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using WireMock.Extensions;
|
||||||
|
using WireMock.Services;
|
||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
internal class MappingMatcher : IMappingMatcher
|
internal class MappingMatcher : IMappingMatcher
|
||||||
{
|
{
|
||||||
private readonly IWireMockMiddlewareOptions _options;
|
private readonly IWireMockMiddlewareOptions _options;
|
||||||
|
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
||||||
|
|
||||||
public MappingMatcher(IWireMockMiddlewareOptions options)
|
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
||||||
{
|
{
|
||||||
_options = Guard.NotNull(options);
|
_options = Guard.NotNull(options);
|
||||||
|
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||||
@@ -21,7 +24,12 @@ internal class MappingMatcher : IMappingMatcher
|
|||||||
|
|
||||||
var possibleMappings = new List<MappingMatcherResult>();
|
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
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ using JetBrains.Annotations;
|
|||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Owin.Mappers;
|
using WireMock.Owin.Mappers;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
using RandomDataGenerator.FieldOptions;
|
||||||
|
using RandomDataGenerator.Randomizers;
|
||||||
|
using WireMock.Services;
|
||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
|
|
||||||
@@ -70,7 +73,7 @@ internal class OwinSelfHost : IOwinSelfHost
|
|||||||
{
|
{
|
||||||
var requestMapper = new OwinRequestMapper();
|
var requestMapper = new OwinRequestMapper();
|
||||||
var responseMapper = new OwinResponseMapper(_options);
|
var responseMapper = new OwinResponseMapper(_options);
|
||||||
var matcher = new MappingMatcher(_options);
|
var matcher = new MappingMatcher(_options, new RandomizerDoubleBetween0And1());
|
||||||
|
|
||||||
Action<IAppBuilder> startup = app =>
|
Action<IAppBuilder> startup = app =>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace WireMock.Owin
|
|||||||
public Task Invoke(IContext ctx)
|
public Task Invoke(IContext ctx)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (_options.HandleRequestsSynchronously.GetValueOrDefault(true))
|
if (_options.HandleRequestsSynchronously.GetValueOrDefault(false))
|
||||||
{
|
{
|
||||||
lock (_lock)
|
lock (_lock)
|
||||||
{
|
{
|
||||||
@@ -216,7 +216,12 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
try
|
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)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -37,7 +37,19 @@ internal class ProxyHelper
|
|||||||
var requiredUri = new Uri(url);
|
var requiredUri = new Uri(url);
|
||||||
|
|
||||||
// Create HttpRequestMessage
|
// 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
|
// Call the URL
|
||||||
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
|
var httpResponseMessage = await client.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseContentRead).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
@@ -46,10 +47,28 @@ public interface IBodyRequestBuilder : IRequestMatcher
|
|||||||
/// WithBody: Body as object
|
/// WithBody: Body as object
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="body">The body.</param>
|
/// <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>
|
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||||
IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
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>
|
/// <summary>
|
||||||
/// WithBody: func (string)
|
/// WithBody: func (string)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IRequestBuilder
|
/// IRequestBuilder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -2,10 +2,12 @@
|
|||||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Stef.Validation;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Stef.Validation;
|
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders;
|
namespace WireMock.RequestBuilders;
|
||||||
|
|
||||||
@@ -32,6 +34,24 @@ public partial class Request
|
|||||||
return this;
|
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 />
|
/// <inheritdoc />
|
||||||
public IRequestBuilder WithBody(IMatcher matcher)
|
public IRequestBuilder WithBody(IMatcher matcher)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
{
|
|
||||||
public partial class Request
|
public partial class Request
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
|
/// <inheritdoc cref="ICookiesRequestBuilder.WithCookie(string, string, MatchBehaviour)"/>
|
||||||
@@ -79,4 +79,3 @@ namespace WireMock.RequestBuilders
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -7,8 +7,8 @@ using WireMock.Matchers.Request;
|
|||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
{
|
|
||||||
public partial class Request
|
public partial class Request
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
|
/// <inheritdoc cref="IParamsRequestBuilder.WithParam(string, MatchBehaviour)"/>
|
||||||
@@ -89,4 +89,3 @@ namespace WireMock.RequestBuilders
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -3,8 +3,8 @@ using Stef.Validation;
|
|||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
|
|
||||||
namespace WireMock.RequestBuilders
|
namespace WireMock.RequestBuilders;
|
||||||
{
|
|
||||||
public partial class Request
|
public partial class Request
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -46,4 +46,3 @@ namespace WireMock.RequestBuilders
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -63,5 +63,4 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
|||||||
{
|
{
|
||||||
return _requestMatchers.OfType<T>().FirstOrDefault(func);
|
return _requestMatchers.OfType<T>().FirstOrDefault(func);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -180,12 +180,7 @@ public class RequestMessage : IRequestMessage
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// 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>
|
|
||||||
public WireMockList<string>? GetParameter(string key, bool ignoreCase = false)
|
public WireMockList<string>? GetParameter(string key, bool ignoreCase = false)
|
||||||
{
|
{
|
||||||
if (Query == null)
|
if (Query == null)
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="body">The body.</param>
|
/// <param name="body">The body.</param>
|
||||||
/// <param name="converter">The JsonConverter.</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>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBody(object body, IJsonConverter converter, JsonConverterOptions? options = null);
|
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="body">The body.</param>
|
||||||
/// <param name="encoding">The body encoding, can be <c>null</c>.</param>
|
/// <param name="encoding">The body encoding, can be <c>null</c>.</param>
|
||||||
/// <param name="converter">The JsonConverter.</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>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter converter, JsonConverterOptions? options = null);
|
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter converter, JsonConverterOptions? options = null);
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
|||||||
/// WithProxy using <see cref="X509Certificate2"/>.
|
/// WithProxy using <see cref="X509Certificate2"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="proxyUrl">The proxy url.</param>
|
/// <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>
|
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||||
IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate);
|
IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate);
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
using WireMock.Constants;
|
using WireMock.Constants;
|
||||||
@@ -14,7 +16,9 @@ using WireMock.RequestBuilders;
|
|||||||
using WireMock.ResponseBuilders;
|
using WireMock.ResponseBuilders;
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
|
using WireMock.Util;
|
||||||
|
|
||||||
|
using static WireMock.Util.CSharpFormatter;
|
||||||
namespace WireMock.Serialization;
|
namespace WireMock.Serialization;
|
||||||
|
|
||||||
internal class MappingConverter
|
internal class MappingConverter
|
||||||
@@ -103,11 +107,28 @@ internal class MappingConverter
|
|||||||
|
|
||||||
if (bodyMatcher is { Matchers: { } })
|
if (bodyMatcher is { Matchers: { } })
|
||||||
{
|
{
|
||||||
var wildcardMatcher = bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault();
|
if (bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault() is { } wildcardMatcher && wildcardMatcher.GetPatterns().Any())
|
||||||
if (wildcardMatcher is { } && wildcardMatcher.GetPatterns().Any())
|
|
||||||
{
|
{
|
||||||
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
|
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(@" )");
|
sb.AppendLine(@" )");
|
||||||
@@ -115,24 +136,50 @@ internal class MappingConverter
|
|||||||
// Guid
|
// Guid
|
||||||
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
|
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
|
||||||
|
|
||||||
|
if (mapping.Probability != null)
|
||||||
|
{
|
||||||
|
sb.AppendLine($" .WithProbability({mapping.Probability.Value.ToString(CultureInfoUtils.CultureInfoEnUS)})");
|
||||||
|
}
|
||||||
|
|
||||||
// Response
|
// Response
|
||||||
sb.AppendLine(" .RespondWith(Response.Create()");
|
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 { })
|
if (response.ResponseMessage.Headers is { })
|
||||||
{
|
{
|
||||||
foreach (var header in response.ResponseMessage.Headers)
|
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)
|
switch (response.ResponseMessage.BodyData.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
case BodyType.FormUrlEncoded:
|
case BodyType.FormUrlEncoded:
|
||||||
sb.AppendLine($" .WithBody(\"{response.ResponseMessage.BodyData.BodyAsString}\")");
|
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;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,6 +230,7 @@ internal class MappingConverter
|
|||||||
WhenStateIs = mapping.ExecutionConditionState,
|
WhenStateIs = mapping.ExecutionConditionState,
|
||||||
SetStateTo = mapping.NextState,
|
SetStateTo = mapping.NextState,
|
||||||
Data = mapping.Data,
|
Data = mapping.Data,
|
||||||
|
Probability = mapping.Probability,
|
||||||
Request = new RequestModel
|
Request = new RequestModel
|
||||||
{
|
{
|
||||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||||
@@ -374,7 +422,7 @@ internal class MappingConverter
|
|||||||
|
|
||||||
private static string GetString(IStringMatcher stringMatcher)
|
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)
|
private static string[] GetStringArray(IReadOnlyList<IStringMatcher> stringMatchers)
|
||||||
@@ -427,7 +475,7 @@ internal class MappingConverter
|
|||||||
|
|
||||||
private static string ToValueArguments(string[]? values, string defaultValue = "")
|
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)
|
private static WebProxyModel? MapWebProxy(WebProxySettings? settings)
|
||||||
@@ -458,4 +506,6 @@ internal class MappingConverter
|
|||||||
|
|
||||||
return newDictionary;
|
return newDictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -31,6 +31,7 @@ internal class ProxyMappingConverter
|
|||||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
|
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
|
||||||
|
var excludedParams = proxyAndRecordSettings.ExcludedParams ?? new string[0];
|
||||||
|
|
||||||
var request = (Request?)mapping?.RequestMatcher;
|
var request = (Request?)mapping?.RequestMatcher;
|
||||||
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||||
@@ -73,13 +74,22 @@ internal class ProxyMappingConverter
|
|||||||
if (useDefinedRequestMatchers && paramMatchers is not null)
|
if (useDefinedRequestMatchers && paramMatchers is not null)
|
||||||
{
|
{
|
||||||
foreach (var paramMatcher in paramMatchers)
|
foreach (var paramMatcher in paramMatchers)
|
||||||
|
{
|
||||||
|
if (!excludedParams.Contains(paramMatcher.Key, StringComparer.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
newRequest.WithParam(paramMatcher.Key, paramMatcher.MatchBehaviour, paramMatcher.Matchers!.ToArray());
|
newRequest.WithParam(paramMatcher.Key, paramMatcher.MatchBehaviour, paramMatcher.Matchers!.ToArray());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
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
|
// Cookies
|
||||||
@@ -179,7 +189,8 @@ internal class ProxyMappingConverter
|
|||||||
webhooks: null,
|
webhooks: null,
|
||||||
useWebhooksFireAndForget: null,
|
useWebhooksFireAndForget: null,
|
||||||
timeSettings: null,
|
timeSettings: null,
|
||||||
data: mapping?.Data
|
data: mapping?.Data,
|
||||||
|
probability: null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,10 +54,12 @@ internal static class SwaggerMapper
|
|||||||
{
|
{
|
||||||
operation.Parameters.Add(openApiParameter);
|
operation.Parameters.Add(openApiParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var openApiParameter in MapRequestHeaders(mapping.Request.Headers))
|
foreach (var openApiParameter in MapRequestHeaders(mapping.Request.Headers))
|
||||||
{
|
{
|
||||||
operation.Parameters.Add(openApiParameter);
|
operation.Parameters.Add(openApiParameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var openApiParameter in MapRequestCookies(mapping.Request.Cookies))
|
foreach (var openApiParameter in MapRequestCookies(mapping.Request.Cookies))
|
||||||
{
|
{
|
||||||
operation.Parameters.Add(openApiParameter);
|
operation.Parameters.Add(openApiParameter);
|
||||||
@@ -94,7 +96,7 @@ internal static class SwaggerMapper
|
|||||||
return openApiDocument.ToJson(SchemaType.OpenApi3, Formatting.Indented);
|
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)
|
if (queryParameters == null)
|
||||||
{
|
{
|
||||||
@@ -146,7 +148,7 @@ internal static class SwaggerMapper
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<OpenApiParameter> MapRequestCookies(IList<CookieModel>? cookies)
|
private static IReadOnlyList<OpenApiParameter> MapRequestCookies(IList<CookieModel>? cookies)
|
||||||
{
|
{
|
||||||
if (cookies == null)
|
if (cookies == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -175,5 +175,13 @@ public interface IRespondWithAProvider
|
|||||||
/// lookup data "1"
|
/// lookup data "1"
|
||||||
/// </example>
|
/// </example>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithData(object data);
|
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);
|
||||||
}
|
}
|
||||||
@@ -18,6 +18,12 @@ namespace WireMock.Server;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class RespondWithAProvider : IRespondWithAProvider
|
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 int _priority;
|
||||||
private string? _title;
|
private string? _title;
|
||||||
private string? _description;
|
private string? _description;
|
||||||
@@ -26,13 +32,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
private string? _nextState;
|
private string? _nextState;
|
||||||
private string? _scenario;
|
private string? _scenario;
|
||||||
private int _timesInSameState = 1;
|
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 bool? _useWebhookFireAndForget;
|
||||||
|
private double? _probability;
|
||||||
|
|
||||||
public Guid Guid { get; private set; }
|
public Guid Guid { get; private set; }
|
||||||
|
|
||||||
@@ -92,7 +93,8 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
Webhooks,
|
Webhooks,
|
||||||
_useWebhookFireAndForget,
|
_useWebhookFireAndForget,
|
||||||
TimeSettings,
|
TimeSettings,
|
||||||
Data);
|
Data,
|
||||||
|
_probability);
|
||||||
|
|
||||||
_registrationCallback(mapping, _saveToFile);
|
_registrationCallback(mapping, _saveToFile);
|
||||||
}
|
}
|
||||||
@@ -285,6 +287,13 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IRespondWithAProvider WithProbability(double probability)
|
||||||
|
{
|
||||||
|
_probability = Guard.Condition(probability, p => p is >= 0 and <= 1.0);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private static IWebhook InitWebhook(
|
private static IWebhook InitWebhook(
|
||||||
string url,
|
string url,
|
||||||
string method,
|
string method,
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ public partial class WireMockServer
|
|||||||
private const string AdminRequests = "/__admin/requests";
|
private const string AdminRequests = "/__admin/requests";
|
||||||
private const string AdminSettings = "/__admin/settings";
|
private const string AdminSettings = "/__admin/settings";
|
||||||
private const string AdminScenarios = "/__admin/scenarios";
|
private const string AdminScenarios = "/__admin/scenarios";
|
||||||
|
private const string AdminOpenApi = "/__admin/openapi";
|
||||||
|
|
||||||
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
private const string QueryParamReloadStaticMappings = "reloadStaticMappings";
|
||||||
|
|
||||||
private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
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).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).UsingHead()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileHead));
|
||||||
Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete));
|
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
|
#endregion
|
||||||
|
|
||||||
@@ -737,7 +743,7 @@ public partial class WireMockServer
|
|||||||
return encodingModel != null ? Encoding.GetEncoding(encodingModel.CodePage) : null;
|
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
|
return new ResponseMessage
|
||||||
{
|
{
|
||||||
@@ -746,7 +752,7 @@ public partial class WireMockServer
|
|||||||
DetectedBodyType = BodyType.String,
|
DetectedBodyType = BodyType.String,
|
||||||
BodyAsString = JsonConvert.SerializeObject(result, keepNullValues ? JsonSerializationConstants.JsonSerializerSettingsIncludeNullValues : JsonSerializationConstants.JsonSerializerSettingsDefault)
|
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) } }
|
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeJson) } }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Admin.Mappings;
|
using WireMock.Admin.Mappings;
|
||||||
@@ -14,7 +15,7 @@ namespace WireMock.Server;
|
|||||||
|
|
||||||
public partial class WireMockServer
|
public partial class WireMockServer
|
||||||
{
|
{
|
||||||
private void ConvertMappingsAndRegisterAsRespondProvider(MappingModel[] mappingModels, string? path = null)
|
private void ConvertMappingsAndRegisterAsRespondProvider(IReadOnlyList<MappingModel> mappingModels, string? path = null)
|
||||||
{
|
{
|
||||||
var duplicateGuids = mappingModels
|
var duplicateGuids = mappingModels
|
||||||
.Where(m => m.Guid != null)
|
.Where(m => m.Guid != null)
|
||||||
@@ -107,7 +108,15 @@ public partial class WireMockServer
|
|||||||
respondProvider = respondProvider.WithWebhook(webhooks);
|
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);
|
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
||||||
respondProvider.RespondWith(responseBuilder);
|
respondProvider.RespondWith(responseBuilder);
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
#if OPENAPIPARSER
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using WireMock.Net.OpenApiParser;
|
||||||
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
using WireMock.Serialization;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace WireMock.Server;
|
||||||
|
|
||||||
|
public partial class WireMockServer
|
||||||
|
{
|
||||||
|
#if OPENAPIPARSER
|
||||||
|
private readonly WireMockOpenApiParserSettings _openApiParserSettings = new WireMockOpenApiParserSettings
|
||||||
|
{
|
||||||
|
PathPatternToUse = ExampleValueType.Regex,
|
||||||
|
HeaderPatternToUse = ExampleValueType.Regex,
|
||||||
|
QueryParameterPatternToUse = ExampleValueType.Regex
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private IResponseMessage OpenApiConvertToMappings(IRequestMessage requestMessage)
|
||||||
|
{
|
||||||
|
#if OPENAPIPARSER
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var mappingModels = new WireMockOpenApiParser().FromText(requestMessage.Body, out var diagnostic);
|
||||||
|
if (diagnostic.Errors.Any())
|
||||||
|
{
|
||||||
|
var diagnosticAsJson = JsonConvert.SerializeObject(diagnostic, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
||||||
|
_settings.Logger.Warn("OpenApiError(s) while converting OpenAPI specification to MappingModel(s) : {0}", diagnosticAsJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 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())
|
||||||
|
{
|
||||||
|
var diagnosticAsJson = JsonConvert.SerializeObject(diagnostic, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
||||||
|
_settings.Logger.Warn("OpenApiError(s) while converting OpenAPI specification to MappingModel(s) : {0}", diagnosticAsJson);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,10 +43,14 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
||||||
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockServer.IsStarted" />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public bool IsStarted => _httpServer is { IsStarted: true };
|
public bool IsStarted => _httpServer is { IsStarted: true };
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
[PublicAPI]
|
||||||
|
public bool IsStartedWithAdminInterface => IsStarted && _settings.StartAdminInterface.GetValueOrDefault();
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public List<int> Ports { get; }
|
public List<int> Ports { get; }
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
namespace WireMock.Services;
|
||||||
|
|
||||||
|
internal interface IRandomizerDoubleBetween0And1
|
||||||
|
{
|
||||||
|
double Generate();
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using RandomDataGenerator.FieldOptions;
|
||||||
|
using RandomDataGenerator.Randomizers;
|
||||||
|
|
||||||
|
namespace WireMock.Services;
|
||||||
|
|
||||||
|
internal class RandomizerDoubleBetween0And1 : IRandomizerDoubleBetween0And1
|
||||||
|
{
|
||||||
|
private readonly IRandomizerNumber<double> _randomizerDoubleBetween0And1 = RandomizerFactory.GetRandomizer(new FieldOptionsDouble { Min = 0, Max = 1 });
|
||||||
|
|
||||||
|
public double Generate()
|
||||||
|
{
|
||||||
|
return _randomizerDoubleBetween0And1.Generate() ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,12 +57,24 @@ public class ProxyAndRecordSettings : HttpClientSettings
|
|||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string[]? ExcludedHeaders { get; set; }
|
public string[]? ExcludedHeaders { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a list of params which will be excluded from the saved mappings.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public string[]? ExcludedParams { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines a list of cookies which will be excluded from the saved mappings.
|
/// Defines a list of cookies which will be excluded from the saved mappings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public string[]? ExcludedCookies { get; set; }
|
public string[]? ExcludedCookies { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replace Settings
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public ProxyUrlReplaceSettings? ReplaceSettings { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
namespace WireMock.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines an old path param and a new path param to be replaced when proxying.
|
||||||
|
/// </summary>
|
||||||
|
public class ProxyUrlReplaceSettings
|
||||||
|
{
|
||||||
|
/// <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; }
|
||||||
|
}
|
||||||
@@ -21,7 +21,8 @@ public static class WireMockServerSettingsParser
|
|||||||
/// <param name="logger">The logger (optional, can be null)</param>
|
/// <param name="logger">The logger (optional, can be null)</param>
|
||||||
/// <param name="settings">The parsed settings</param>
|
/// <param name="settings">The parsed settings</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings, IWireMockLogger? logger = null)
|
public static bool TryParseArguments(string[] args, [NotNullWhen(true)] out WireMockServerSettings? settings,
|
||||||
|
IWireMockLogger? logger = null)
|
||||||
{
|
{
|
||||||
Guard.HasNoNulls(args);
|
Guard.HasNoNulls(args);
|
||||||
|
|
||||||
@@ -70,6 +71,17 @@ public static class WireMockServerSettingsParser
|
|||||||
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
|
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
ParseLoggerSettings(settings, logger, parser);
|
||||||
|
ParsePortSettings(settings, parser);
|
||||||
|
ParseProxyAndRecordSettings(settings, parser);
|
||||||
|
ParseCertificateSettings(settings, parser);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseLoggerSettings(WireMockServerSettings settings, IWireMockLogger? logger,
|
||||||
|
SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
var loggerType = parser.GetStringValue("WireMockLogger");
|
var loggerType = parser.GetStringValue("WireMockLogger");
|
||||||
switch (loggerType)
|
switch (loggerType)
|
||||||
{
|
{
|
||||||
@@ -86,22 +98,17 @@ public static class WireMockServerSettingsParser
|
|||||||
{
|
{
|
||||||
settings.Logger = logger;
|
settings.Logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
|
||||||
{
|
|
||||||
settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
|
|
||||||
}
|
|
||||||
else if (settings.HostingScheme is null)
|
|
||||||
{
|
|
||||||
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ParseProxyAndRecordSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
|
var proxyUrl = parser.GetStringValue("ProxyURL") ?? parser.GetStringValue("ProxyUrl");
|
||||||
if (!string.IsNullOrEmpty(proxyUrl))
|
if (!string.IsNullOrEmpty(proxyUrl))
|
||||||
{
|
{
|
||||||
settings.ProxyAndRecordSettings = new ProxyAndRecordSettings
|
var proxyAndRecordSettings = new ProxyAndRecordSettings
|
||||||
{
|
{
|
||||||
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
|
AllowAutoRedirect = parser.GetBoolValue("AllowAutoRedirect"),
|
||||||
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
|
ClientX509Certificate2ThumbprintOrSubjectName = parser.GetStringValue("ClientX509Certificate2ThumbprintOrSubjectName"),
|
||||||
@@ -121,18 +128,27 @@ public static class WireMockServerSettingsParser
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
ParseWebProxyAddressSettings(proxyAndRecordSettings, parser);
|
||||||
if (!string.IsNullOrEmpty(proxyAddress))
|
ParseProxyUrlReplaceSettings(proxyAndRecordSettings, parser);
|
||||||
{
|
|
||||||
settings.ProxyAndRecordSettings.WebProxySettings = new WebProxySettings
|
settings.ProxyAndRecordSettings = proxyAndRecordSettings;
|
||||||
{
|
|
||||||
Address = proxyAddress!,
|
|
||||||
UserName = parser.GetStringValue("WebProxyUserName"),
|
|
||||||
Password = parser.GetStringValue("WebProxyPassword")
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ParsePortSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
|
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
||||||
|
{
|
||||||
|
settings.Port = parser.GetIntValue(nameof(WireMockServerSettings.Port));
|
||||||
|
}
|
||||||
|
else if (settings.HostingScheme is null)
|
||||||
|
{
|
||||||
|
settings.Urls = parser.GetValues("Urls", new[] { "http://*:9091/" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseCertificateSettings(WireMockServerSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
var certificateSettings = new WireMockCertificateSettings
|
var certificateSettings = new WireMockCertificateSettings
|
||||||
{
|
{
|
||||||
X509StoreName = parser.GetStringValue("X509StoreName"),
|
X509StoreName = parser.GetStringValue("X509StoreName"),
|
||||||
@@ -145,7 +161,33 @@ public static class WireMockServerSettingsParser
|
|||||||
{
|
{
|
||||||
settings.CertificateSettings = certificateSettings;
|
settings.CertificateSettings = certificateSettings;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
private static void ParseWebProxyAddressSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
|
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
||||||
|
if (!string.IsNullOrEmpty(proxyAddress))
|
||||||
|
{
|
||||||
|
settings.WebProxySettings = new WebProxySettings
|
||||||
|
{
|
||||||
|
Address = proxyAddress!,
|
||||||
|
UserName = parser.GetStringValue("WebProxyUserName"),
|
||||||
|
Password = parser.GetStringValue("WebProxyPassword")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleCommandLineParser parser)
|
||||||
|
{
|
||||||
|
var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue");
|
||||||
|
var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue");
|
||||||
|
if (!string.IsNullOrEmpty(proxyUrlReplaceOldValue) && proxyUrlReplaceNewValue != null)
|
||||||
|
{
|
||||||
|
settings.ReplaceSettings = new ProxyUrlReplaceSettings
|
||||||
|
{
|
||||||
|
OldValue = proxyUrlReplaceOldValue!,
|
||||||
|
NewValue = proxyUrlReplaceNewValue!
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@ namespace WireMock.Util;
|
|||||||
/// http://www.unicode.org/versions/corrigendum1.html
|
/// http://www.unicode.org/versions/corrigendum1.html
|
||||||
/// http://www.ietf.org/rfc/rfc2279.txt
|
/// http://www.ietf.org/rfc/rfc2279.txt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BytesEncodingUtils
|
internal static class BytesEncodingUtils
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries the get the Encoding from an array of bytes.
|
/// Tries the get the Encoding from an array of bytes.
|
||||||
@@ -108,7 +108,7 @@ public static class BytesEncodingUtils
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch >= 0xc2 && ch <= 0xdf)
|
if (ch is >= 0xc2 and <= 0xdf)
|
||||||
{
|
{
|
||||||
if (position >= length - 2)
|
if (position >= length - 2)
|
||||||
{
|
{
|
||||||
@@ -145,7 +145,7 @@ public static class BytesEncodingUtils
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch >= 0xe1 && ch <= 0xef)
|
if (ch is >= 0xe1 and <= 0xef)
|
||||||
{
|
{
|
||||||
if (position >= length - 3)
|
if (position >= length - 3)
|
||||||
{
|
{
|
||||||
@@ -204,7 +204,7 @@ public static class BytesEncodingUtils
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ch >= 0xf1 && ch <= 0xf3)
|
if (ch is >= 0xf1 and <= 0xf3)
|
||||||
{
|
{
|
||||||
if (position >= length - 4)
|
if (position >= length - 4)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace WireMock.Util;
|
||||||
|
|
||||||
|
internal static class CSharpFormatter
|
||||||
|
{
|
||||||
|
#region Reserved Keywords
|
||||||
|
private static readonly HashSet<string> CSharpReservedKeywords = new(new[]
|
||||||
|
{
|
||||||
|
"abstract",
|
||||||
|
"as",
|
||||||
|
"base",
|
||||||
|
"bool",
|
||||||
|
"break",
|
||||||
|
"byte",
|
||||||
|
"case",
|
||||||
|
"catch",
|
||||||
|
"char",
|
||||||
|
"checked",
|
||||||
|
"class",
|
||||||
|
"const",
|
||||||
|
"continue",
|
||||||
|
"decimal",
|
||||||
|
"default",
|
||||||
|
"delegate",
|
||||||
|
"do",
|
||||||
|
"double",
|
||||||
|
"else",
|
||||||
|
"enum",
|
||||||
|
"event",
|
||||||
|
"explicit",
|
||||||
|
"extern",
|
||||||
|
"false",
|
||||||
|
"finally",
|
||||||
|
"fixed",
|
||||||
|
"float",
|
||||||
|
"for",
|
||||||
|
"foreach",
|
||||||
|
"goto",
|
||||||
|
"if",
|
||||||
|
"implicit",
|
||||||
|
"in",
|
||||||
|
"int",
|
||||||
|
"interface",
|
||||||
|
"internal",
|
||||||
|
"is",
|
||||||
|
"lock",
|
||||||
|
"long",
|
||||||
|
"namespace",
|
||||||
|
"new",
|
||||||
|
"null",
|
||||||
|
"object",
|
||||||
|
"operator",
|
||||||
|
"out",
|
||||||
|
"override",
|
||||||
|
"params",
|
||||||
|
"private",
|
||||||
|
"protected",
|
||||||
|
"public",
|
||||||
|
"readonly",
|
||||||
|
"ref",
|
||||||
|
"return",
|
||||||
|
"sbyte",
|
||||||
|
"sealed",
|
||||||
|
"short",
|
||||||
|
"sizeof",
|
||||||
|
"stackalloc",
|
||||||
|
"static",
|
||||||
|
"string",
|
||||||
|
"struct",
|
||||||
|
"switch",
|
||||||
|
"this",
|
||||||
|
"throw",
|
||||||
|
"true",
|
||||||
|
"try",
|
||||||
|
"typeof",
|
||||||
|
"uint",
|
||||||
|
"ulong",
|
||||||
|
"unchecked",
|
||||||
|
"unsafe",
|
||||||
|
"ushort",
|
||||||
|
"using",
|
||||||
|
"virtual",
|
||||||
|
"void",
|
||||||
|
"volatile",
|
||||||
|
"while"
|
||||||
|
});
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private const string Null = "null";
|
||||||
|
|
||||||
|
public static object ConvertToAnonymousObjectDefinition(object jsonBody)
|
||||||
|
{
|
||||||
|
var serializedBody = JsonConvert.SerializeObject(jsonBody);
|
||||||
|
using var jsonReader = new JsonTextReader(new StringReader(serializedBody));
|
||||||
|
jsonReader.DateParseHandling = DateParseHandling.None;
|
||||||
|
var deserializedBody = JObject.Load(jsonReader);
|
||||||
|
|
||||||
|
return ConvertJsonToAnonymousObjectDefinition(deserializedBody, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ConvertJsonToAnonymousObjectDefinition(JToken token, int ind = 0)
|
||||||
|
{
|
||||||
|
return token switch
|
||||||
|
{
|
||||||
|
JArray jArray => FormatArray(jArray, ind),
|
||||||
|
JObject jObject => FormatObject(jObject, ind),
|
||||||
|
JProperty jProperty => $"{FormatPropertyName(jProperty.Name)} = {ConvertJsonToAnonymousObjectDefinition(jProperty.Value, ind)}",
|
||||||
|
JValue jValue => jValue.Type switch
|
||||||
|
{
|
||||||
|
JTokenType.None => Null,
|
||||||
|
JTokenType.Integer => jValue.Value != null ? string.Format(CultureInfo.InvariantCulture, "{0}", jValue.Value) : Null,
|
||||||
|
JTokenType.Float => jValue.Value != null ? string.Format(CultureInfo.InvariantCulture, "{0}", jValue.Value) : Null,
|
||||||
|
JTokenType.String => ToCSharpStringLiteral(jValue.Value?.ToString()),
|
||||||
|
JTokenType.Boolean => jValue.Value != null ? string.Format(CultureInfo.InvariantCulture, "{0}", jValue.Value).ToLower() : Null,
|
||||||
|
JTokenType.Null => Null,
|
||||||
|
JTokenType.Undefined => Null,
|
||||||
|
JTokenType.Date when jValue.Value is DateTime dateValue =>
|
||||||
|
$"DateTime.Parse({ToCSharpStringLiteral(dateValue.ToString("s"))})",
|
||||||
|
_ => $"UNHANDLED_CASE: {jValue.Type}"
|
||||||
|
},
|
||||||
|
_ => $"UNHANDLED_CASE: {token}"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToCSharpBooleanLiteral(bool value) => value ? "true" : "false";
|
||||||
|
|
||||||
|
public static string ToCSharpStringLiteral(string? value)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(value))
|
||||||
|
{
|
||||||
|
return "\"\"";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.Contains("\n"))
|
||||||
|
{
|
||||||
|
var escapedValue = value?.Replace("\"", "\"\"") ?? string.Empty;
|
||||||
|
return $"@\"{escapedValue}\"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var escapedValue = value?.Replace("\"", "\\\"") ?? string.Empty;
|
||||||
|
return $"\"{escapedValue}\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatPropertyName(string propertyName)
|
||||||
|
{
|
||||||
|
return CSharpReservedKeywords.Contains(propertyName) ? "@" + propertyName : propertyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatObject(JObject jObject, int ind)
|
||||||
|
{
|
||||||
|
var indStr = new string(' ', 4 * ind);
|
||||||
|
var indStrSub = new string(' ', 4 * (ind + 1));
|
||||||
|
var items = jObject.Properties().Select(x => ConvertJsonToAnonymousObjectDefinition(x, ind + 1));
|
||||||
|
|
||||||
|
return $"new\r\n{indStr}{{\r\n{indStrSub}{string.Join($",\r\n{indStrSub}", items)}\r\n{indStr}}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatArray(JArray jArray, int ind)
|
||||||
|
{
|
||||||
|
var hasComplexItems = jArray.FirstOrDefault() is JObject or JArray;
|
||||||
|
var items = jArray.Select(x => ConvertJsonToAnonymousObjectDefinition(x, hasComplexItems ? ind + 1 : ind));
|
||||||
|
if (hasComplexItems)
|
||||||
|
{
|
||||||
|
var indStr = new string(' ', 4 * ind);
|
||||||
|
var indStrSub = new string(' ', 4 * (ind + 1));
|
||||||
|
return $"new []\r\n{indStr}{{\r\n{indStrSub}{string.Join($",\r\n{indStrSub}", items)}\r\n{indStr}}}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $"new [] {{ {string.Join(", ", items)} }}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe.
|
/// A special Collection that overrides methods of <see cref="ObservableCollection{T}"/> to make them thread safe.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -83,4 +83,3 @@ namespace WireMock.Util
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -5,6 +5,8 @@ namespace WireMock.Util;
|
|||||||
|
|
||||||
internal static class CultureInfoUtils
|
internal static class CultureInfoUtils
|
||||||
{
|
{
|
||||||
|
public static readonly CultureInfo CultureInfoEnUS = new("en-US");
|
||||||
|
|
||||||
public static CultureInfo Parse(string? value)
|
public static CultureInfo Parse(string? value)
|
||||||
{
|
{
|
||||||
if (value is null)
|
if (value is null)
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ using System.IO;
|
|||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
|
|
||||||
namespace WireMock.Util
|
namespace WireMock.Util;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file.
|
/// An EnhancedFileSystemWatcher, which can be used to suppress duplicate events that fire on a single change to the file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -251,4 +251,3 @@ namespace WireMock.Util
|
|||||||
#endregion
|
#endregion
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
|
||||||
@@ -9,7 +9,7 @@ namespace WireMock.Util;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Port Utility class
|
/// Port Utility class
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class PortUtils
|
internal static class PortUtils
|
||||||
{
|
{
|
||||||
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
|
private static readonly Regex UrlDetailsRegex = new(@"^((?<proto>\w+)://)(?<host>[^/]+?):(?<port>\d+)\/?$", RegexOptions.Compiled);
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ internal static class QueryStringParser
|
|||||||
{
|
{
|
||||||
if (part.Length == 2)
|
if (part.Length == 2)
|
||||||
{
|
{
|
||||||
nameValueCollection.Add(part[0], part[1]);
|
nameValueCollection.Add(part[0], WebUtility.UrlDecode(part[1]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ internal static class RegexUtils
|
|||||||
return namedGroupsDictionary;
|
return namedGroupsDictionary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static (bool IsValid, bool Result) MatchRegex(string pattern, string input, bool useRegexExtended = true)
|
public static (bool IsValid, bool Result) MatchRegex(string? pattern, string input, bool useRegexExtended = true)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(pattern))
|
if (string.IsNullOrEmpty(pattern))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -12,9 +12,11 @@ internal sealed class TinyMapperUtils
|
|||||||
{
|
{
|
||||||
TinyMapper.Bind<ProxyAndRecordSettings, ProxyAndRecordSettingsModel>();
|
TinyMapper.Bind<ProxyAndRecordSettings, ProxyAndRecordSettingsModel>();
|
||||||
TinyMapper.Bind<WebProxySettings, WebProxySettingsModel>();
|
TinyMapper.Bind<WebProxySettings, WebProxySettingsModel>();
|
||||||
|
TinyMapper.Bind<ProxyUrlReplaceSettings, ProxyUrlReplaceSettingsModel>();
|
||||||
|
|
||||||
TinyMapper.Bind<ProxyAndRecordSettingsModel, ProxyAndRecordSettings>();
|
TinyMapper.Bind<ProxyAndRecordSettingsModel, ProxyAndRecordSettings>();
|
||||||
TinyMapper.Bind<WebProxySettingsModel, WebProxySettings>();
|
TinyMapper.Bind<WebProxySettingsModel, WebProxySettings>();
|
||||||
|
TinyMapper.Bind<ProxyUrlReplaceSettingsModel, ProxyUrlReplaceSettings>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
|
public ProxyAndRecordSettingsModel? Map(ProxyAndRecordSettings? instance)
|
||||||
|
|||||||
@@ -35,15 +35,19 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'netstandard2.0' or '$(TargetFramework)' == 'netstandard2.1'">
|
||||||
<DefineConstants>NETSTANDARD;USE_ASPNETCORE</DefineConstants>
|
<DefineConstants>$(DefineConstants);NETSTANDARD;USE_ASPNETCORE</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1' or '$(TargetFramework)' == 'netcoreapp2.2' or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0'or '$(TargetFramework)' == 'net7.0' ">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1' or '$(TargetFramework)' == 'netcoreapp2.2' or '$(TargetFramework)' == 'netcoreapp3.1' or '$(TargetFramework)' == 'net5.0' or '$(TargetFramework)' == 'net6.0'or '$(TargetFramework)' == 'net7.0' ">
|
||||||
<DefineConstants>USE_ASPNETCORE</DefineConstants>
|
<DefineConstants>$(DefineConstants);USE_ASPNETCORE</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(TargetFramework)' == 'net461'">
|
<PropertyGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||||
<DefineConstants>USE_ASPNETCORE;NET46</DefineConstants>
|
<DefineConstants>$(DefineConstants);USE_ASPNETCORE;NET46</DefineConstants>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452'">
|
||||||
|
<DefineConstants>$(DefineConstants);OPENAPIPARSER</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -189,4 +193,8 @@
|
|||||||
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||||
<ProjectReference Include="..\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj" />
|
<ProjectReference Include="..\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452'">
|
||||||
|
<ProjectReference Include="..\WireMock.Net.OpenApiParser\WireMock.Net.OpenApiParser.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,16 +1,11 @@
|
|||||||
namespace WireMock.Org.Abstractions
|
namespace WireMock.Org.Abstractions;
|
||||||
{
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The fault to apply (instead of a full, valid response).
|
/// The fault to apply (instead of a full, valid response).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class MappingsResponseFaultConstants
|
public static class MappingsResponseFaultConstants
|
||||||
{
|
{
|
||||||
public const string CONNECTIONRESETBYPEER = "CONNECTION_RESET_BY_PEER";
|
|
||||||
|
|
||||||
public const string EMPTYRESPONSE = "EMPTY_RESPONSE";
|
public const string EMPTYRESPONSE = "EMPTY_RESPONSE";
|
||||||
|
|
||||||
public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK";
|
public const string MALFORMEDRESPONSECHUNK = "MALFORMED_RESPONSE_CHUNK";
|
||||||
|
|
||||||
public const string RANDOMDATATHENCLOSE = "RANDOM_DATA_THEN_CLOSE";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,840 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {
|
||||||
|
"title": "Swagger Petstore - OpenAPI 3.0",
|
||||||
|
"description": "This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about\nSwagger at [http://swagger.io](http://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!\nYou can now help us improve the API whether it's by making changes to the definition itself or to the code.\nThat way, with time, we can improve the API in general, and expose some of the new features in OAS3.\n\nSome useful links:\n- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)\n- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) ",
|
||||||
|
"termsOfService": "http://swagger.io/terms/",
|
||||||
|
"contact": { "email": "apiteam@swagger.io" },
|
||||||
|
"license": {
|
||||||
|
"name": "Apache 2.0",
|
||||||
|
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||||
|
},
|
||||||
|
"version": "1.0.4"
|
||||||
|
},
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more about Swagger",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
},
|
||||||
|
"servers": [ { "url": "/api/v3" } ],
|
||||||
|
"tags": [
|
||||||
|
{
|
||||||
|
"name": "pet",
|
||||||
|
"description": "Everything about your Pets",
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "store",
|
||||||
|
"description": "Operations about user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "user",
|
||||||
|
"description": "Access to Petstore orders",
|
||||||
|
"externalDocs": {
|
||||||
|
"description": "Find out more about our store",
|
||||||
|
"url": "http://swagger.io"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/pet": {
|
||||||
|
"put": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Update an existing pet",
|
||||||
|
"description": "Update an existing pet by Id",
|
||||||
|
"operationId": "updatePet",
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Update an existent pet in the store",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid ID supplied" },
|
||||||
|
"404": { "description": "Pet not found" },
|
||||||
|
"405": { "description": "Validation exception" }
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Add a new pet to the store",
|
||||||
|
"description": "Add a new pet to the store",
|
||||||
|
"operationId": "addPet",
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Create a new pet in the store",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
},
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"405": { "description": "Invalid input" }
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/pet/findByStatus": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Finds Pets by status",
|
||||||
|
"description": "Multiple status values can be provided with comma separated strings",
|
||||||
|
"operationId": "findPetsByStatus",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Status values that need to be considered for filter",
|
||||||
|
"required": false,
|
||||||
|
"explode": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "available",
|
||||||
|
"enum": [ "available", "pending", "sold" ]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid status value" }
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/pet/findByTags": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Finds Pets by tags",
|
||||||
|
"description": "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
|
||||||
|
"operationId": "findPetsByTags",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "tags",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Tags to filter by",
|
||||||
|
"required": false,
|
||||||
|
"explode": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/Pet" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid tag value" }
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/pet/{petId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Find pet by ID",
|
||||||
|
"description": "Returns a single pet",
|
||||||
|
"operationId": "getPetById",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "petId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet to return",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid ID supplied" },
|
||||||
|
"404": { "description": "Pet not found" }
|
||||||
|
},
|
||||||
|
"security": [
|
||||||
|
{ "api_key": [] },
|
||||||
|
{ "petstore_auth": [ "write:pets", "read:pets" ] }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Updates a pet in the store with form data",
|
||||||
|
"description": "",
|
||||||
|
"operationId": "updatePetWithForm",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "petId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet that needs to be updated",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "name",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Name of pet that needs to be updated",
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "status",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Status of pet that needs to be updated",
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": { "405": { "description": "Invalid input" } },
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "Deletes a pet",
|
||||||
|
"description": "",
|
||||||
|
"operationId": "deletePet",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "header",
|
||||||
|
"description": "",
|
||||||
|
"required": false,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "petId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "Pet id to delete",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": { "400": { "description": "Invalid pet value" } },
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/pet/{petId}/uploadImage": {
|
||||||
|
"post": {
|
||||||
|
"tags": [ "pet" ],
|
||||||
|
"summary": "uploads an image",
|
||||||
|
"description": "",
|
||||||
|
"operationId": "uploadFile",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "petId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of pet to update",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "additionalMetadata",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Additional Metadata",
|
||||||
|
"required": false,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/octet-stream": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/ApiResponse" } } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [ { "petstore_auth": [ "write:pets", "read:pets" ] } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/store/inventory": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "store" ],
|
||||||
|
"summary": "Returns pet inventories by status",
|
||||||
|
"description": "Returns a map of status codes to quantities",
|
||||||
|
"operationId": "getInventory",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [ { "api_key": [] } ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/store/order": {
|
||||||
|
"post": {
|
||||||
|
"tags": [ "store" ],
|
||||||
|
"summary": "Place an order for a pet",
|
||||||
|
"description": "Place a new order in the store",
|
||||||
|
"operationId": "placeOrder",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Order" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Order" } },
|
||||||
|
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/Order" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": { "application/json": { "schema": { "$ref": "#/components/schemas/Order" } } }
|
||||||
|
},
|
||||||
|
"405": { "description": "Invalid input" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/store/order/{orderId}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "store" ],
|
||||||
|
"summary": "Find purchase order by ID",
|
||||||
|
"description": "For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",
|
||||||
|
"operationId": "getOrderById",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "orderId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of order that needs to be fetched",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Order" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Order" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid ID supplied" },
|
||||||
|
"404": { "description": "Order not found" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [ "store" ],
|
||||||
|
"summary": "Delete purchase order by ID",
|
||||||
|
"description": "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
|
||||||
|
"operationId": "deleteOrder",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "orderId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "ID of the order that needs to be deleted",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"400": { "description": "Invalid ID supplied" },
|
||||||
|
"404": { "description": "Order not found" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/user": {
|
||||||
|
"post": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Create user",
|
||||||
|
"description": "This can only be done by the logged in user.",
|
||||||
|
"operationId": "createUser",
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Created user object",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/User" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"default": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/User" } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/user/createWithList": {
|
||||||
|
"post": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Creates list of users with given input array",
|
||||||
|
"description": "Creates list of users with given input array",
|
||||||
|
"operationId": "createUsersWithListInput",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/User" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/User" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"default": { "description": "successful operation" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/user/login": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Logs user into the system",
|
||||||
|
"description": "",
|
||||||
|
"operationId": "loginUser",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "query",
|
||||||
|
"description": "The user name for login",
|
||||||
|
"required": false,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "password",
|
||||||
|
"in": "query",
|
||||||
|
"description": "The password for login in clear text",
|
||||||
|
"required": false,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"headers": {
|
||||||
|
"X-Rate-Limit": {
|
||||||
|
"description": "calls per hour allowed by the user",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"X-Expires-After": {
|
||||||
|
"description": "date in UTC when toekn expires",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "type": "string" } },
|
||||||
|
"application/json": { "schema": { "type": "string" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid username/password supplied" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/user/logout": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Logs out current logged in user session",
|
||||||
|
"description": "",
|
||||||
|
"operationId": "logoutUser",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": { "default": { "description": "successful operation" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/user/{username}": {
|
||||||
|
"get": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Get user by user name",
|
||||||
|
"description": "",
|
||||||
|
"operationId": "getUserByName",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"description": "The name that needs to be fetched. Use user1 for testing. ",
|
||||||
|
"required": true,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "successful operation",
|
||||||
|
"content": {
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/User" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": { "description": "Invalid username supplied" },
|
||||||
|
"404": { "description": "User not found" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Update user",
|
||||||
|
"description": "This can only be done by the logged in user.",
|
||||||
|
"operationId": "updateUser",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"description": "name that need to be deleted",
|
||||||
|
"required": true,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "Update an existent user in the store",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/User" } },
|
||||||
|
"application/x-www-form-urlencoded": { "schema": { "$ref": "#/components/schemas/User" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": { "default": { "description": "successful operation" } }
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"tags": [ "user" ],
|
||||||
|
"summary": "Delete user",
|
||||||
|
"description": "This can only be done by the logged in user.",
|
||||||
|
"operationId": "deleteUser",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"description": "The name that needs to be deleted",
|
||||||
|
"required": true,
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"400": { "description": "Invalid username supplied" },
|
||||||
|
"404": { "description": "User not found" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Order": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 10
|
||||||
|
},
|
||||||
|
"petId": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 198772
|
||||||
|
},
|
||||||
|
"quantity": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32",
|
||||||
|
"example": 7
|
||||||
|
},
|
||||||
|
"shipDate": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "date-time"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Order Status",
|
||||||
|
"example": "approved",
|
||||||
|
"enum": [ "placed", "approved", "delivered" ]
|
||||||
|
},
|
||||||
|
"complete": { "type": "boolean" }
|
||||||
|
},
|
||||||
|
"xml": { "name": "order" }
|
||||||
|
},
|
||||||
|
"Customer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 100000
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "fehguy"
|
||||||
|
},
|
||||||
|
"address": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": {
|
||||||
|
"name": "addresses",
|
||||||
|
"wrapped": true
|
||||||
|
},
|
||||||
|
"items": { "$ref": "#/components/schemas/Address" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "customer" }
|
||||||
|
},
|
||||||
|
"Address": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"street": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "437 Lytton"
|
||||||
|
},
|
||||||
|
"city": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Palo Alto"
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "CA"
|
||||||
|
},
|
||||||
|
"zip": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "94301"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "address" }
|
||||||
|
},
|
||||||
|
"Category": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 1
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "Dogs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "category" }
|
||||||
|
},
|
||||||
|
"User": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 10
|
||||||
|
},
|
||||||
|
"username": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "theUser"
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "John"
|
||||||
|
},
|
||||||
|
"lastName": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "James"
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "john@email.com"
|
||||||
|
},
|
||||||
|
"password": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "12345"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "12345"
|
||||||
|
},
|
||||||
|
"userStatus": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "User Status",
|
||||||
|
"format": "int32",
|
||||||
|
"example": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "user" }
|
||||||
|
},
|
||||||
|
"Tag": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"name": { "type": "string" }
|
||||||
|
},
|
||||||
|
"xml": { "name": "tag" }
|
||||||
|
},
|
||||||
|
"Pet": {
|
||||||
|
"required": [ "name", "photoUrls" ],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int64",
|
||||||
|
"example": 10
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"example": "doggie"
|
||||||
|
},
|
||||||
|
"category": { "$ref": "#/components/schemas/Category" },
|
||||||
|
"photoUrls": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": { "wrapped": true },
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"xml": { "name": "photoUrl" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"xml": { "wrapped": true },
|
||||||
|
"items": { "$ref": "#/components/schemas/Tag" }
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "pet status in the store",
|
||||||
|
"enum": [ "available", "pending", "sold" ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"xml": { "name": "pet" }
|
||||||
|
},
|
||||||
|
"ApiResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
},
|
||||||
|
"type": { "type": "string" },
|
||||||
|
"message": { "type": "string" }
|
||||||
|
},
|
||||||
|
"xml": { "name": "##default" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requestBodies": {
|
||||||
|
"Pet": {
|
||||||
|
"description": "Pet object that needs to be added to the store",
|
||||||
|
"content": {
|
||||||
|
"application/json": { "schema": { "$ref": "#/components/schemas/Pet" } },
|
||||||
|
"application/xml": { "schema": { "$ref": "#/components/schemas/Pet" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"UserArray": {
|
||||||
|
"description": "List of user object",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": { "$ref": "#/components/schemas/User" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"securitySchemes": {
|
||||||
|
"petstore_auth": {
|
||||||
|
"type": "oauth2",
|
||||||
|
"flows": {
|
||||||
|
"implicit": {
|
||||||
|
"authorizationUrl": "https://petstore.swagger.io/oauth/authorize",
|
||||||
|
"scopes": {
|
||||||
|
"write:pets": "modify pets in your account",
|
||||||
|
"read:pets": "read your pets"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"api_key": {
|
||||||
|
"type": "apiKey",
|
||||||
|
"name": "api_key",
|
||||||
|
"in": "header"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,730 @@
|
|||||||
|
swagger: '2.0'
|
||||||
|
info:
|
||||||
|
description: 'This is a sample server Petstore server. Copied from https://github.com/swagger-api/swagger-codegen/blob/master/modules/swagger-codegen/src/test/resources/2_0/petstore.yaml.'
|
||||||
|
version: 1.0.0
|
||||||
|
title: Swagger Petstore
|
||||||
|
termsOfService: 'http://swagger.io/terms/'
|
||||||
|
contact:
|
||||||
|
email: apiteam@swagger.io
|
||||||
|
license:
|
||||||
|
name: Apache-2.0
|
||||||
|
url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
|
||||||
|
host: petstore.swagger.io
|
||||||
|
basePath: /v2
|
||||||
|
tags:
|
||||||
|
- name: pet
|
||||||
|
description: Everything about your Pets
|
||||||
|
externalDocs:
|
||||||
|
description: Find out more
|
||||||
|
url: 'http://swagger.io'
|
||||||
|
- name: store
|
||||||
|
description: Access to Petstore orders
|
||||||
|
- name: user
|
||||||
|
description: Operations about user
|
||||||
|
externalDocs:
|
||||||
|
description: Find out more about our store
|
||||||
|
url: 'http://swagger.io'
|
||||||
|
schemes:
|
||||||
|
- http
|
||||||
|
paths:
|
||||||
|
/pet:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Add a new pet to the store
|
||||||
|
description: ''
|
||||||
|
operationId: addPet
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
- application/xml
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Pet object that needs to be added to the store
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Pet'
|
||||||
|
responses:
|
||||||
|
'405':
|
||||||
|
description: Invalid input
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Update an existing pet
|
||||||
|
description: ''
|
||||||
|
operationId: updatePet
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
- application/xml
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Pet object that needs to be added to the store
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Pet'
|
||||||
|
responses:
|
||||||
|
'400':
|
||||||
|
description: Invalid ID supplied
|
||||||
|
'404':
|
||||||
|
description: Pet not found
|
||||||
|
'405':
|
||||||
|
description: Validation exception
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
/pet/findByStatus:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Finds Pets by status
|
||||||
|
description: Multiple status values can be provided with comma separated strings
|
||||||
|
operationId: findPetsByStatus
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: status
|
||||||
|
in: query
|
||||||
|
description: Status values that need to be considered for filter
|
||||||
|
required: true
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
enum:
|
||||||
|
- available
|
||||||
|
- pending
|
||||||
|
- sold
|
||||||
|
default: available
|
||||||
|
collectionFormat: csv
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Pet'
|
||||||
|
'400':
|
||||||
|
description: Invalid status value
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
/pet/findByTags:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Finds Pets by tags
|
||||||
|
description: 'Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.'
|
||||||
|
operationId: findPetsByTags
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: tags
|
||||||
|
in: query
|
||||||
|
description: Tags to filter by
|
||||||
|
required: true
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
collectionFormat: csv
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Pet'
|
||||||
|
'400':
|
||||||
|
description: Invalid tag value
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
deprecated: true
|
||||||
|
'/pet/{petId}':
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Find pet by ID
|
||||||
|
description: Returns a single pet
|
||||||
|
operationId: getPetById
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: petId
|
||||||
|
in: path
|
||||||
|
description: ID of pet to return
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Pet'
|
||||||
|
'400':
|
||||||
|
description: Invalid ID supplied
|
||||||
|
'404':
|
||||||
|
description: Pet not found
|
||||||
|
security:
|
||||||
|
- api_key: []
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Updates a pet in the store with form data
|
||||||
|
description: ''
|
||||||
|
operationId: updatePetWithForm
|
||||||
|
consumes:
|
||||||
|
- application/x-www-form-urlencoded
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: petId
|
||||||
|
in: path
|
||||||
|
description: ID of pet that needs to be updated
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
- name: name
|
||||||
|
in: formData
|
||||||
|
description: Updated name of the pet
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
- name: status
|
||||||
|
in: formData
|
||||||
|
description: Updated status of the pet
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'405':
|
||||||
|
description: Invalid input
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: Deletes a pet
|
||||||
|
description: ''
|
||||||
|
operationId: deletePet
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: api_key
|
||||||
|
in: header
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
- name: petId
|
||||||
|
in: path
|
||||||
|
description: Pet id to delete
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'400':
|
||||||
|
description: Invalid pet value
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
'/pet/{petId}/uploadImage':
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- pet
|
||||||
|
summary: uploads an image
|
||||||
|
description: ''
|
||||||
|
operationId: uploadFile
|
||||||
|
consumes:
|
||||||
|
- multipart/form-data
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: petId
|
||||||
|
in: path
|
||||||
|
description: ID of pet to update
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
- name: additionalMetadata
|
||||||
|
in: formData
|
||||||
|
description: Additional data to pass to server
|
||||||
|
required: false
|
||||||
|
type: string
|
||||||
|
- name: file
|
||||||
|
in: formData
|
||||||
|
description: file to upload
|
||||||
|
required: false
|
||||||
|
type: file
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/ApiResponse'
|
||||||
|
security:
|
||||||
|
- petstore_auth:
|
||||||
|
- 'write:pets'
|
||||||
|
- 'read:pets'
|
||||||
|
/store/inventory:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
summary: Returns pet inventories by status
|
||||||
|
description: Returns a map of status codes to quantities
|
||||||
|
operationId: getInventory
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
additionalProperties:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
security:
|
||||||
|
- api_key: []
|
||||||
|
/store/order:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
summary: Place an order for a pet
|
||||||
|
description: ''
|
||||||
|
operationId: placeOrder
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: order placed for purchasing the pet
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Order'
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Order'
|
||||||
|
'400':
|
||||||
|
description: Invalid Order
|
||||||
|
'/store/order/{orderId}':
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
summary: Find purchase order by ID
|
||||||
|
description: 'For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions'
|
||||||
|
operationId: getOrderById
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: orderId
|
||||||
|
in: path
|
||||||
|
description: ID of pet that needs to be fetched
|
||||||
|
required: true
|
||||||
|
type: integer
|
||||||
|
maximum: 5
|
||||||
|
minimum: 1
|
||||||
|
format: int64
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/Order'
|
||||||
|
'400':
|
||||||
|
description: Invalid ID supplied
|
||||||
|
'404':
|
||||||
|
description: Order not found
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- store
|
||||||
|
summary: Delete purchase order by ID
|
||||||
|
description: For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors
|
||||||
|
operationId: deleteOrder
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: orderId
|
||||||
|
in: path
|
||||||
|
description: ID of the order that needs to be deleted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'400':
|
||||||
|
description: Invalid ID supplied
|
||||||
|
'404':
|
||||||
|
description: Order not found
|
||||||
|
/user:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Create user
|
||||||
|
description: This can only be done by the logged in user.
|
||||||
|
operationId: createUser
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Created user object
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/User'
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
description: successful operation
|
||||||
|
/user/createWithArray:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Creates list of users with given input array
|
||||||
|
description: ''
|
||||||
|
operationId: createUsersWithArrayInput
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: List of user object
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/User'
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
description: successful operation
|
||||||
|
/user/createWithList:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Creates list of users with given input array
|
||||||
|
description: ''
|
||||||
|
operationId: createUsersWithListInput
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: List of user object
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/User'
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
description: successful operation
|
||||||
|
/user/login:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Logs user into the system
|
||||||
|
description: ''
|
||||||
|
operationId: loginUser
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: username
|
||||||
|
in: query
|
||||||
|
description: The user name for login
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- name: password
|
||||||
|
in: query
|
||||||
|
description: The password for login in clear text
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
headers:
|
||||||
|
X-Rate-Limit:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
description: calls per hour allowed by the user
|
||||||
|
X-Expires-After:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
description: date in UTC when toekn expires
|
||||||
|
'400':
|
||||||
|
description: Invalid username/password supplied
|
||||||
|
/user/logout:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Logs out current logged in user session
|
||||||
|
description: ''
|
||||||
|
operationId: logoutUser
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters: []
|
||||||
|
responses:
|
||||||
|
default:
|
||||||
|
description: successful operation
|
||||||
|
'/user/{username}':
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Get user by user name
|
||||||
|
description: ''
|
||||||
|
operationId: getUserByName
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: username
|
||||||
|
in: path
|
||||||
|
description: 'The name that needs to be fetched. Use user1 for testing.'
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: successful operation
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/User'
|
||||||
|
'400':
|
||||||
|
description: Invalid username supplied
|
||||||
|
'404':
|
||||||
|
description: User not found
|
||||||
|
put:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Updated user
|
||||||
|
description: This can only be done by the logged in user.
|
||||||
|
operationId: updateUser
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: username
|
||||||
|
in: path
|
||||||
|
description: name that need to be deleted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: Updated user object
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/User'
|
||||||
|
responses:
|
||||||
|
'400':
|
||||||
|
description: Invalid user supplied
|
||||||
|
'404':
|
||||||
|
description: User not found
|
||||||
|
delete:
|
||||||
|
tags:
|
||||||
|
- user
|
||||||
|
summary: Delete user
|
||||||
|
description: This can only be done by the logged in user.
|
||||||
|
operationId: deleteUser
|
||||||
|
produces:
|
||||||
|
- application/xml
|
||||||
|
- application/json
|
||||||
|
parameters:
|
||||||
|
- name: username
|
||||||
|
in: path
|
||||||
|
description: The name that needs to be deleted
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
'400':
|
||||||
|
description: Invalid username supplied
|
||||||
|
'404':
|
||||||
|
description: User not found
|
||||||
|
securityDefinitions:
|
||||||
|
petstore_auth:
|
||||||
|
type: oauth2
|
||||||
|
authorizationUrl: 'http://petstore.swagger.io/api/oauth/dialog'
|
||||||
|
flow: implicit
|
||||||
|
scopes:
|
||||||
|
'write:pets': modify pets in your account
|
||||||
|
'read:pets': read your pets
|
||||||
|
api_key:
|
||||||
|
type: apiKey
|
||||||
|
name: api_key
|
||||||
|
in: header
|
||||||
|
definitions:
|
||||||
|
Order:
|
||||||
|
title: Pet Order
|
||||||
|
description: An order for a pets from the pet store
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
petId:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
quantity:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
shipDate:
|
||||||
|
type: string
|
||||||
|
format: date-time
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
description: Order Status
|
||||||
|
enum:
|
||||||
|
- placed
|
||||||
|
- approved
|
||||||
|
- delivered
|
||||||
|
complete:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
xml:
|
||||||
|
name: Order
|
||||||
|
Category:
|
||||||
|
title: Pet category
|
||||||
|
description: A category for a pet
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
xml:
|
||||||
|
name: Category
|
||||||
|
User:
|
||||||
|
title: a User
|
||||||
|
description: A User who is purchasing from the pet store
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
firstName:
|
||||||
|
type: string
|
||||||
|
lastName:
|
||||||
|
type: string
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
|
password:
|
||||||
|
type: string
|
||||||
|
phone:
|
||||||
|
type: string
|
||||||
|
userStatus:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
description: User Status
|
||||||
|
xml:
|
||||||
|
name: User
|
||||||
|
Tag:
|
||||||
|
title: Pet Tag
|
||||||
|
description: A tag for a pet
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
xml:
|
||||||
|
name: Tag
|
||||||
|
Pet:
|
||||||
|
title: a Pet
|
||||||
|
description: A pet for sale in the pet store
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- name
|
||||||
|
- photoUrls
|
||||||
|
properties:
|
||||||
|
id:
|
||||||
|
type: integer
|
||||||
|
format: int64
|
||||||
|
category:
|
||||||
|
$ref: '#/definitions/Category'
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
example: doggie
|
||||||
|
photoUrls:
|
||||||
|
type: array
|
||||||
|
xml:
|
||||||
|
name: photoUrl
|
||||||
|
wrapped: true
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
tags:
|
||||||
|
type: array
|
||||||
|
xml:
|
||||||
|
name: tag
|
||||||
|
wrapped: true
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/Tag'
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
description: pet status in the store
|
||||||
|
enum:
|
||||||
|
- available
|
||||||
|
- pending
|
||||||
|
- sold
|
||||||
|
xml:
|
||||||
|
name: Pet
|
||||||
|
ApiResponse:
|
||||||
|
title: An uploaded response
|
||||||
|
description: Describes the result of uploading an image resource
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
code:
|
||||||
|
type: integer
|
||||||
|
format: int32
|
||||||
|
type:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
#issue: https://github.com/swagger-api/swagger-codegen/issues/7980
|
||||||
|
Amount:
|
||||||
|
type: object
|
||||||
|
description: >
|
||||||
|
some description
|
||||||
|
properties:
|
||||||
|
value:
|
||||||
|
format: double
|
||||||
|
type: number
|
||||||
|
minimum: 0.01
|
||||||
|
maximum: 1000000000000000
|
||||||
|
description: >
|
||||||
|
some description
|
||||||
|
currency:
|
||||||
|
$ref: '#/definitions/Currency'
|
||||||
|
required:
|
||||||
|
- value
|
||||||
|
- currency
|
||||||
|
Currency:
|
||||||
|
type: string
|
||||||
|
pattern: '^[A-Z]{3,3}$'
|
||||||
|
description: >
|
||||||
|
some description
|
||||||
|
externalDocs:
|
||||||
|
description: Find out more about Swagger
|
||||||
|
url: 'http://swagger.io'
|
||||||
@@ -6,6 +6,7 @@ using WireMock.Logging;
|
|||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
using WireMock.Owin;
|
using WireMock.Owin;
|
||||||
|
using WireMock.Services;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@@ -14,6 +15,8 @@ namespace WireMock.Net.Tests.Owin;
|
|||||||
public class MappingMatcherTests
|
public class MappingMatcherTests
|
||||||
{
|
{
|
||||||
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
private readonly Mock<IWireMockMiddlewareOptions> _optionsMock;
|
||||||
|
private readonly Mock<IRandomizerDoubleBetween0And1> _randomizerDoubleBetween0And1Mock;
|
||||||
|
|
||||||
private readonly MappingMatcher _sut;
|
private readonly MappingMatcher _sut;
|
||||||
|
|
||||||
public MappingMatcherTests()
|
public MappingMatcherTests()
|
||||||
@@ -29,7 +32,10 @@ public class MappingMatcherTests
|
|||||||
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
loggerMock.Setup(l => l.Error(It.IsAny<string>()));
|
||||||
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
_optionsMock.Setup(o => o.Logger).Returns(loggerMock.Object);
|
||||||
|
|
||||||
_sut = new MappingMatcher(_optionsMock.Object);
|
_randomizerDoubleBetween0And1Mock = new Mock<IRandomizerDoubleBetween0And1>();
|
||||||
|
_randomizerDoubleBetween0And1Mock.Setup(r => r.Generate()).Returns(0.0);
|
||||||
|
|
||||||
|
_sut = new MappingMatcher(_optionsMock.Object, _randomizerDoubleBetween0And1Mock.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -76,8 +82,8 @@ public class MappingMatcherTests
|
|||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 0.1 }),
|
(guid1, new[] { 0.1 }, null),
|
||||||
(guid2, new[] { 1.0 })
|
(guid2, new[] { 1.0 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -104,8 +110,8 @@ public class MappingMatcherTests
|
|||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings
|
var mappings = InitMappings
|
||||||
(
|
(
|
||||||
(guid1, new[] { 0.1 }),
|
(guid1, new[] { 0.1 }, null),
|
||||||
(guid2, new[] { 0.9 })
|
(guid2, new[] { 0.9 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -131,8 +137,8 @@ public class MappingMatcherTests
|
|||||||
|
|
||||||
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
_optionsMock.SetupGet(o => o.AllowPartialMapping).Returns(true);
|
||||||
var mappings = InitMappings(
|
var mappings = InitMappings(
|
||||||
(guid1, new[] { 0.1 }),
|
(guid1, new[] { 0.1 }, null),
|
||||||
(guid2, new[] { 0.9 })
|
(guid2, new[] { 0.9 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -158,8 +164,8 @@ public class MappingMatcherTests
|
|||||||
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
var mappings = InitMappings(
|
var mappings = InitMappings(
|
||||||
(guid1, new[] { 1.0 }),
|
(guid1, new[] { 1.0 }, null),
|
||||||
(guid2, new[] { 1.0, 1.0 })
|
(guid2, new[] { 1.0, 1.0 }, null)
|
||||||
);
|
);
|
||||||
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
@@ -178,7 +184,31 @@ public class MappingMatcherTests
|
|||||||
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
result.Partial.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores)[] matches)
|
[Fact]
|
||||||
|
public void MappingMatcher_FindBestMatch_WhenProbabilityFailsFirst_ShouldReturnSecondMatch()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var guid1 = Guid.Parse("00000000-0000-0000-0000-000000000001");
|
||||||
|
var guid2 = Guid.Parse("00000000-0000-0000-0000-000000000002");
|
||||||
|
var mappings = InitMappings
|
||||||
|
(
|
||||||
|
(guid1, new[] { 1.0 }, 1.0),
|
||||||
|
(guid2, new[] { 1.0 }, null)
|
||||||
|
);
|
||||||
|
_optionsMock.Setup(o => o.Mappings).Returns(mappings);
|
||||||
|
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "GET", "::1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = _sut.FindBestMatch(request);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Match.Should().NotBeNull();
|
||||||
|
result.Match!.Mapping.Guid.Should().Be(guid2);
|
||||||
|
result.Match.RequestMatchResult.AverageTotalScore.Should().Be(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ConcurrentDictionary<Guid, IMapping> InitMappings(params (Guid guid, double[] scores, double? probability)[] matches)
|
||||||
{
|
{
|
||||||
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
var mappings = new ConcurrentDictionary<Guid, IMapping>();
|
||||||
|
|
||||||
@@ -193,6 +223,8 @@ public class MappingMatcherTests
|
|||||||
requestMatchResult.AddScore(typeof(object), score);
|
requestMatchResult.AddScore(typeof(object), score);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mappingMock.SetupGet(m => m.Probability).Returns(match.probability);
|
||||||
|
|
||||||
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
mappingMock.Setup(m => m.GetRequestMatchResult(It.IsAny<RequestMessage>(), It.IsAny<string>())).Returns(requestMatchResult);
|
||||||
|
|
||||||
mappings.TryAdd(match.guid, mappingMock.Object);
|
mappings.TryAdd(match.guid, mappingMock.Object);
|
||||||
|
|||||||
@@ -177,7 +177,7 @@ public class WireMockMiddlewareTests
|
|||||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||||
|
|
||||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null);
|
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, null, null);
|
||||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||||
|
|
||||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||||
@@ -231,7 +231,7 @@ public class WireMockMiddlewareTests
|
|||||||
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
_mappingMock.SetupGet(m => m.Provider).Returns(responseBuilder);
|
||||||
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
_mappingMock.SetupGet(m => m.Settings).Returns(settings);
|
||||||
|
|
||||||
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null);
|
var newMappingFromProxy = new Mapping(Guid.NewGuid(), _updatedAt, "my-title", "my-description", null, settings, Request.Create(), Response.Create(), 0, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
_mappingMock.Setup(m => m.ProvideResponseAsync(It.IsAny<RequestMessage>())).ReturnsAsync((new ResponseMessage(), newMappingFromProxy));
|
||||||
|
|
||||||
var requestBuilder = Request.Create().UsingAnyMethod();
|
var requestBuilder = Request.Create().UsingAnyMethod();
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using JsonConverter.Abstractions;
|
||||||
|
using Moq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NFluent;
|
using NFluent;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
@@ -134,7 +136,7 @@ namespace WireMock.Net.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Request_BodyAsString_Using_WildcardMatcher()
|
public void Request_WithBody_BodyDataAsString_Using_WildcardMatcher()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithBody(new WildcardMatcher("H*o*"));
|
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithBody(new WildcardMatcher("H*o*"));
|
||||||
@@ -153,7 +155,7 @@ namespace WireMock.Net.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Request_BodyAsJson_Using_WildcardMatcher()
|
public void Request_WithBody_BodyDataAsJson_Using_WildcardMatcher()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithBody(new WildcardMatcher("*Hello*"));
|
var spec = Request.Create().WithPath("/foo").UsingAnyMethod().WithBody(new WildcardMatcher("*Hello*"));
|
||||||
@@ -349,6 +351,56 @@ namespace WireMock.Net.Tests
|
|||||||
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Request_WithBodyAsJson_UsingObject()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
object body = new
|
||||||
|
{
|
||||||
|
Test = "abc"
|
||||||
|
};
|
||||||
|
var requestBuilder = Request.Create().UsingAnyMethod().WithBodyAsJson(body);
|
||||||
|
|
||||||
|
var bodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = JsonConvert.SerializeObject(body),
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Request_WithBodyAsJson_WithIJsonConverter_UsingObject()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var jsonConverterMock = new Mock<IJsonConverter>();
|
||||||
|
jsonConverterMock.Setup(j => j.Serialize(It.IsAny<object>(), It.IsAny<JsonConverterOptions>())).Returns("test");
|
||||||
|
object body = new
|
||||||
|
{
|
||||||
|
Any = "key"
|
||||||
|
};
|
||||||
|
var requestBuilder = Request.Create().UsingAnyMethod().WithBodyAsJson(body, jsonConverterMock.Object);
|
||||||
|
|
||||||
|
var bodyData = new BodyData
|
||||||
|
{
|
||||||
|
BodyAsString = "test",
|
||||||
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(requestBuilder.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(new byte[] { 1 }, BodyType.Bytes)]
|
[InlineData(new byte[] { 1 }, BodyType.Bytes)]
|
||||||
[InlineData(new byte[] { 48, 49, 50 }, BodyType.Bytes)]
|
[InlineData(new byte[] { 48, 49, 50 }, BodyType.Bytes)]
|
||||||
|
|||||||
@@ -136,7 +136,8 @@ public partial class MappingConverterTests
|
|||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
null,
|
null,
|
||||||
data: null
|
data: null,
|
||||||
|
probability: 0.3
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -9,8 +9,9 @@
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
+2
-1
@@ -10,8 +10,9 @@ builder
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
+2
-1
@@ -9,8 +9,9 @@
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
+2
-1
@@ -10,8 +10,9 @@ server
|
|||||||
.WithBody("b")
|
.WithBody("b")
|
||||||
)
|
)
|
||||||
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
.WithGuid("8e7b9ab7-e18e-4502-8bc9-11e6679811cc")
|
||||||
|
.WithProbability(0.3)
|
||||||
.RespondWith(Response.Create()
|
.RespondWith(Response.Create()
|
||||||
.WithHeader("Keep-Alive)", "test")
|
.WithHeader("Keep-Alive", "test")
|
||||||
.WithBody("bbb")
|
.WithBody("bbb")
|
||||||
.WithDelay(12345)
|
.WithDelay(12345)
|
||||||
.WithTransformer()
|
.WithTransformer()
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
Guid: Guid_1,
|
||||||
|
UpdatedAt: DateTime_1,
|
||||||
|
Title: ,
|
||||||
|
Description: ,
|
||||||
|
Priority: 42,
|
||||||
|
Request: {},
|
||||||
|
Response: {},
|
||||||
|
UseWebhooksFireAndForget: false,
|
||||||
|
Probability: 0.4
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ public partial class MappingConverterTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -130,7 +130,7 @@ public partial class MappingConverterTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 0, null, null, null, null, webhooks, true, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -168,7 +168,7 @@ public partial class MappingConverterTests
|
|||||||
var description = "my-description";
|
var description = "my-description";
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create();
|
var response = Response.Create();
|
||||||
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, title, description, null, _settings, request, response, 0, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -188,7 +188,7 @@ public partial class MappingConverterTests
|
|||||||
// Assign
|
// Assign
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
|
var response = Response.Create().WithBodyAsJson(new { x = "x" }).WithTransformer();
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -217,7 +217,7 @@ public partial class MappingConverterTests
|
|||||||
End = end,
|
End = end,
|
||||||
TTL = ttl
|
TTL = ttl
|
||||||
};
|
};
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, timeSettings, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -248,7 +248,7 @@ public partial class MappingConverterTests
|
|||||||
{
|
{
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithDelay(test.Delay);
|
var response = Response.Create().WithDelay(test.Delay);
|
||||||
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(Guid.NewGuid(), _updatedAt, string.Empty, string.Empty, string.Empty, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -266,7 +266,7 @@ public partial class MappingConverterTests
|
|||||||
var delay = 1000;
|
var delay = 1000;
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithDelay(delay);
|
var response = Response.Create().WithDelay(delay);
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -286,7 +286,7 @@ public partial class MappingConverterTests
|
|||||||
int minimumDelay = 1000;
|
int minimumDelay = 1000;
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithRandomDelay(minimumDelay);
|
var response = Response.Create().WithRandomDelay(minimumDelay);
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -309,7 +309,7 @@ public partial class MappingConverterTests
|
|||||||
int maximumDelay = 2000;
|
int maximumDelay = 2000;
|
||||||
var request = Request.Create();
|
var request = Request.Create();
|
||||||
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
|
var response = Response.Create().WithRandomDelay(minimumDelay, maximumDelay);
|
||||||
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null);
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: null);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var model = _sut.ToMappingModel(mapping);
|
var model = _sut.ToMappingModel(mapping);
|
||||||
@@ -323,5 +323,25 @@ public partial class MappingConverterTests
|
|||||||
// Verify
|
// Verify
|
||||||
return Verifier.Verify(model);
|
return Verifier.Verify(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public Task ToMappingModel_WithProbability_ReturnsCorrectModel()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
double probability = 0.4;
|
||||||
|
var request = Request.Create();
|
||||||
|
var response = Response.Create();
|
||||||
|
var mapping = new Mapping(_guid, _updatedAt, string.Empty, string.Empty, null, _settings, request, response, 42, null, null, null, null, null, false, null, data: null, probability: probability);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var model = _sut.ToMappingModel(mapping);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
model.Should().NotBeNull();
|
||||||
|
model.Probability.Should().Be(0.4);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
return Verifier.Verify(model);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user