mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-13 22:03:35 +01:00
Compare commits
32 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52d2109c7e | ||
|
|
30064b922b | ||
|
|
78b94d2ebc | ||
|
|
19701f5260 | ||
|
|
1269fb178f | ||
|
|
7426bf76ee | ||
|
|
36c9d95abb | ||
|
|
674fa89c3e | ||
|
|
61cdc13fae | ||
|
|
c344b73f45 | ||
|
|
2ac9ca207a | ||
|
|
f099f3a288 | ||
|
|
02b607cc95 | ||
|
|
7ac89e85b7 | ||
|
|
cc4cf27101 | ||
|
|
6839b11d35 | ||
|
|
1000f4409f | ||
|
|
7fe2c8af78 | ||
|
|
0fc664b404 | ||
|
|
770a670e53 | ||
|
|
b4c8779d68 | ||
|
|
c85eaf1072 | ||
|
|
b2a8178161 | ||
|
|
20eb37b0c8 | ||
|
|
742f1d1f0a | ||
|
|
d8927b88c8 | ||
|
|
7ab136557a | ||
|
|
3d17913f35 | ||
|
|
9ed6a75384 | ||
|
|
9606fee8cb | ||
|
|
6b03dfaa8c | ||
|
|
e2f3ffd33a |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -255,3 +255,5 @@ paket-files/
|
||||
/test/WireMock.Net.Tests/coverage.opencover.xml
|
||||
/test/WireMock.Net.Tests/coverage.netcoreapp3.1.opencover.xml
|
||||
/test/WireMock.Net.Tests/coverage.net5.0.opencover.xml
|
||||
|
||||
*.received.*
|
||||
48
CHANGELOG.md
48
CHANGELOG.md
@@ -1,3 +1,46 @@
|
||||
# 1.5.19 (17 March 2023)
|
||||
- [#903](https://github.com/WireMock-Net/WireMock.Net/pull/903) - Add WithBody with IDictionary (form-urlencoded values) [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#904](https://github.com/WireMock-Net/WireMock.Net/pull/904) - Update Handlebars.Net.Helpers to 2.3.15 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#901](https://github.com/WireMock-Net/WireMock.Net/issues/901) - Matching one form-urlencoded value [feature]
|
||||
|
||||
# 1.5.18 (09 March 2023)
|
||||
- [#893](https://github.com/WireMock-Net/WireMock.Net/pull/893) - Add 'Data' to response which can be used during transforming the response [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#896](https://github.com/WireMock-Net/WireMock.Net/pull/896) - Bump Microsoft.Owin from 2.0.2 to 4.2.2 in /examples/WireMock.Net.Service [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)
|
||||
- [#900](https://github.com/WireMock-Net/WireMock.Net/pull/900) - ProxySettings : Add logic to not save some requests depending on HttpMethods [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#897](https://github.com/WireMock-Net/WireMock.Net/issues/897) - WebHostBuilder.ConfigureServices method not found when using nunit3testadapter 4.4.0 [bug]
|
||||
- [#899](https://github.com/WireMock-Net/WireMock.Net/issues/899) - Ignore OPTIONS request when using proxyandrecord [feature]
|
||||
|
||||
# 1.5.17 (25 February 2023)
|
||||
- [#881](https://github.com/WireMock-Net/WireMock.Net/pull/881) - Add WithBodyAsJson builder method with accepts a Func [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#882](https://github.com/WireMock-Net/WireMock.Net/pull/882) - Add example code to test HTTP Status 400 and 500 [test] contributed by [StefH](https://github.com/StefH)
|
||||
- [#890](https://github.com/WireMock-Net/WireMock.Net/pull/890) - AdminApiMappingBuilder [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.16 (01 February 2023)
|
||||
- [#880](https://github.com/WireMock-Net/WireMock.Net/pull/880) - Add `WithProxy(string proxyUrl, X509Certificate2 certificate)` [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#879](https://github.com/WireMock-Net/WireMock.Net/issues/879) - Possibility to pass a X509Certificate2 to WithProxy() or specifiy certificate loading options [feature]
|
||||
|
||||
# 1.5.15 (29 January 2023)
|
||||
- [#878](https://github.com/WireMock-Net/WireMock.Net/pull/878) - Update REST Admin interface to support "Get Mapping(s) as C# Code" [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.5.14 (24 January 2023)
|
||||
- [#842](https://github.com/WireMock-Net/WireMock.Net/pull/842) - Generate C# code from Mapping [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#869](https://github.com/WireMock-Net/WireMock.Net/pull/869) - Add MappingBuilder to build mappings in code and export to Models or JSON [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#871](https://github.com/WireMock-Net/WireMock.Net/pull/871) - Add UseWebhooksFireAndForget to Server ConvertMapping [bug] contributed by [ggradnig](https://github.com/ggradnig)
|
||||
- [#872](https://github.com/WireMock-Net/WireMock.Net/pull/872) - Fix unsubscribe from LogEntriesChanged event handler [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#875](https://github.com/WireMock-Net/WireMock.Net/pull/875) - Fix Self referencing loop detected for property [bug] contributed by [eseneckiy](https://github.com/eseneckiy)
|
||||
- [#877](https://github.com/WireMock-Net/WireMock.Net/pull/877) - Add unit test example for Transformer Handlebars String.Append String.Join [test] contributed by [StefH](https://github.com/StefH)
|
||||
- [#701](https://github.com/WireMock-Net/WireMock.Net/issues/701) - Allow to create MappingModel from c# to be able to configure local and remote mocks similarly [feature]
|
||||
- [#867](https://github.com/WireMock-Net/WireMock.Net/issues/867) - Can I build mappings with code and save them to JSON-file without starting server [feature]
|
||||
- [#870](https://github.com/WireMock-Net/WireMock.Net/issues/870) - Can not unsubscribe from LogEntriesChanged event. [bug]
|
||||
|
||||
# 1.5.13 (11 December 2022)
|
||||
- [#858](https://github.com/WireMock-Net/WireMock.Net/pull/858) - Update Transformer functionality to return value instead of string [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#859](https://github.com/WireMock-Net/WireMock.Net/pull/859) - Add UpdatedAt property to Mapping [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#861](https://github.com/WireMock-Net/WireMock.Net/pull/861) - Add extra functionality for issue 55 contributed by [StefH](https://github.com/StefH)
|
||||
- [#862](https://github.com/WireMock-Net/WireMock.Net/pull/862) - Add client certificate support [feature] contributed by [billybraga](https://github.com/billybraga)
|
||||
- [#863](https://github.com/WireMock-Net/WireMock.Net/pull/863) - Update WireMockServer.CreateClient/CreateClients to include handlers [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#856](https://github.com/WireMock-Net/WireMock.Net/issues/856) - Inconsistent result with overlapping (duplicate) request [bug]
|
||||
|
||||
# 1.5.12 (03 December 2022)
|
||||
- [#851](https://github.com/WireMock-Net/WireMock.Net/pull/851) - Fix Linux CI build + Fix opencover [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#853](https://github.com/WireMock-Net/WireMock.Net/pull/853) - Add .Net 7 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
@@ -625,8 +668,8 @@
|
||||
- [#263](https://github.com/WireMock-Net/WireMock.Net/issues/263) - Content-Type multipart/form-data is not serialized in proxy and recording mode [bug]
|
||||
|
||||
# 1.0.11.0 (30 March 2019)
|
||||
- [#261](https://github.com/WireMock-Net/WireMock.Net/pull/261) - Fix BodyAsJson transform bug in ResponseMessageTransformer contributed by [psypilat](https://github.com/psypilat)
|
||||
- [#262](https://github.com/WireMock-Net/WireMock.Net/pull/262) - Add ProvideResponse_WithJsonBodyAndTransform test contributed by [psypilat](https://github.com/psypilat)
|
||||
- [#261](https://github.com/WireMock-Net/WireMock.Net/pull/261) - Fix BodyAsJson transform bug in ResponseMessageTransformer contributed by [ghost](https://github.com/ghost)
|
||||
- [#262](https://github.com/WireMock-Net/WireMock.Net/pull/262) - Add ProvideResponse_WithJsonBodyAndTransform test contributed by [ghost](https://github.com/ghost)
|
||||
|
||||
# 1.0.10.0 (27 March 2019)
|
||||
- [#260](https://github.com/WireMock-Net/WireMock.Net/pull/260) - Fix Response.Delay property serialization [bug] contributed by [StefH](https://github.com/StefH)
|
||||
@@ -915,6 +958,7 @@
|
||||
- [#45](https://github.com/WireMock-Net/WireMock.Net/pull/45) - Add RequestLogExpirationDuration and MaxRequestLogCount (#43) contributed by [StefH](https://github.com/StefH)
|
||||
- [#51](https://github.com/WireMock-Net/WireMock.Net/pull/51) - Observable logs contributed by [dmtrrk](https://github.com/dmtrrk)
|
||||
- [#15](https://github.com/WireMock-Net/WireMock.Net/issues/15) - New feature: Proxying [feature]
|
||||
- [#20](https://github.com/WireMock-Net/WireMock.Net/issues/20) - Add client certificate authentication [feature]
|
||||
- [#31](https://github.com/WireMock-Net/WireMock.Net/issues/31) - Feature request: Nuget package for standalone version [feature]
|
||||
- [#33](https://github.com/WireMock-Net/WireMock.Net/issues/33) - Issue with launching sample code (StandAlone server) [bug]
|
||||
- [#38](https://github.com/WireMock-Net/WireMock.Net/issues/38) - Bug: support also listening on *:{port}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.5.12</VersionPrefix>
|
||||
<VersionPrefix>1.5.19</VersionPrefix>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=1.5.12
|
||||
SET version=1.5.19
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# 1.5.12 (03 December 2022)
|
||||
- #851 Fix Linux CI build + Fix opencover [feature]
|
||||
- #853 Add .Net 7 [feature]
|
||||
- #854 Fix logic for QueryParameterMultipleValueSupport [bug]
|
||||
- #857 Update some dependencies [feature]
|
||||
# 1.5.19 (17 March 2023)
|
||||
- #903 Add WithBody with IDictionary (form-urlencoded values) [feature]
|
||||
- #904 Update Handlebars.Net.Helpers to 2.3.15 [feature]
|
||||
- #901 Matching one form-urlencoded value [feature]
|
||||
|
||||
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
||||
@@ -33,6 +33,7 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scriban/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sigil/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=templated/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Victoor/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhook/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Webhooks/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
@@ -1,78 +1,125 @@
|
||||
using Newtonsoft.Json;
|
||||
using RestEase;
|
||||
using System;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
using RestEase;
|
||||
using WireMock.Admin.Settings;
|
||||
using WireMock.Client;
|
||||
using WireMock.Client.Extensions;
|
||||
|
||||
namespace WireMock.Net.Client
|
||||
namespace WireMock.Net.Client;
|
||||
|
||||
class Program
|
||||
{
|
||||
class Program
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
// Create an implementation of the IWireMockAdminApi and pass in the base URL for the API.
|
||||
var api = RestClient.For<IWireMockAdminApi>("http://localhost:9091");
|
||||
|
||||
// await api.ResetMappingsAsync().ConfigureAwait(false);
|
||||
|
||||
var mappingBuilder = api.GetMappingBuilder();
|
||||
mappingBuilder.Given(m => m
|
||||
.WithTitle("This is my title 1")
|
||||
.WithRequest(req => req
|
||||
.UsingGet()
|
||||
.WithPath("/bla1")
|
||||
)
|
||||
.WithResponse(rsp => rsp
|
||||
.WithBody("x1")
|
||||
.WithHeaders(h => h.Add("h1", "v1"))
|
||||
)
|
||||
);
|
||||
|
||||
mappingBuilder.Given(m => m
|
||||
.WithTitle("This is my title 2")
|
||||
.WithRequest(req => req
|
||||
.UsingGet()
|
||||
.WithPath("/bla2")
|
||||
)
|
||||
.WithResponse(rsp => rsp
|
||||
.WithBody("x2")
|
||||
.WithHeaders(h => h.Add("h2", "v2"))
|
||||
)
|
||||
);
|
||||
|
||||
mappingBuilder.Given(m => m
|
||||
.WithTitle("This is my title 3")
|
||||
.WithRequest(req => req
|
||||
.UsingGet()
|
||||
.WithPath("/bla3")
|
||||
)
|
||||
.WithResponse(rsp => rsp
|
||||
.WithBodyAsJson(new
|
||||
{
|
||||
x = "test"
|
||||
}, true)
|
||||
)
|
||||
);
|
||||
|
||||
var result = await mappingBuilder.BuildAndPostAsync().ConfigureAwait(false);
|
||||
Console.WriteLine($"result = {JsonConvert.SerializeObject(result)}");
|
||||
|
||||
var mappings = await api.GetMappingsAsync();
|
||||
Console.WriteLine($"mappings = {JsonConvert.SerializeObject(mappings)}");
|
||||
|
||||
// Set BASIC Auth
|
||||
var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b"));
|
||||
api.Authorization = new AuthenticationHeaderValue("Basic", value);
|
||||
|
||||
var settings1 = await api.GetSettingsAsync();
|
||||
Console.WriteLine($"settings1 = {JsonConvert.SerializeObject(settings1)}");
|
||||
|
||||
var settingsViaBuilder = new SettingsModelBuilder()
|
||||
.WithGlobalProcessingDelay(1077)
|
||||
.WithoutGlobalProcessingDelay()
|
||||
.Build();
|
||||
|
||||
settings1.GlobalProcessingDelay = 1077;
|
||||
api.PostSettingsAsync(settings1).Wait();
|
||||
|
||||
var settings2 = await api.GetSettingsAsync();
|
||||
Console.WriteLine($"settings2 = {JsonConvert.SerializeObject(settings2)}");
|
||||
|
||||
mappings = await api.GetMappingsAsync();
|
||||
Console.WriteLine($"mappings = {JsonConvert.SerializeObject(mappings)}");
|
||||
|
||||
try
|
||||
{
|
||||
// Create an implementation of the IWireMockAdminApi and pass in the base URL for the API.
|
||||
var api = RestClient.For<IWireMockAdminApi>("http://localhost:9091");
|
||||
|
||||
// Set BASIC Auth
|
||||
var value = Convert.ToBase64String(Encoding.ASCII.GetBytes("a:b"));
|
||||
api.Authorization = new AuthenticationHeaderValue("Basic", value);
|
||||
|
||||
var settings1 = await api.GetSettingsAsync();
|
||||
Console.WriteLine($"settings1 = {JsonConvert.SerializeObject(settings1)}");
|
||||
|
||||
var settingsViaBuilder = new SettingsModelBuilder()
|
||||
.WithGlobalProcessingDelay(1077)
|
||||
.WithoutGlobalProcessingDelay()
|
||||
.Build();
|
||||
|
||||
settings1.GlobalProcessingDelay = 1077;
|
||||
api.PostSettingsAsync(settings1).Wait();
|
||||
|
||||
var settings2 = await api.GetSettingsAsync();
|
||||
Console.WriteLine($"settings2 = {JsonConvert.SerializeObject(settings2)}");
|
||||
|
||||
var mappings = await api.GetMappingsAsync();
|
||||
Console.WriteLine($"mappings = {JsonConvert.SerializeObject(mappings)}");
|
||||
|
||||
try
|
||||
{
|
||||
var guid = Guid.Parse("11111110-a633-40e8-a244-5cb80bc0ab66");
|
||||
var mapping = await api.GetMappingAsync(guid);
|
||||
Console.WriteLine($"mapping = {JsonConvert.SerializeObject(mapping)}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
var request = await api.GetRequestsAsync();
|
||||
Console.WriteLine($"request = {JsonConvert.SerializeObject(request)}");
|
||||
|
||||
//var deleteRequestsAsync = api.DeleteRequestsAsync().Result;
|
||||
//Console.WriteLine($"DeleteRequestsAsync = {deleteRequestsAsync.Status}");
|
||||
|
||||
//var resetRequestsAsync = api.ResetRequestsAsync().Result;
|
||||
//Console.WriteLine($"ResetRequestsAsync = {resetRequestsAsync.Status}");
|
||||
|
||||
var scenarioStates = await api.GetScenariosAsync();
|
||||
Console.WriteLine($"GetScenariosAsync = {JsonConvert.SerializeObject(scenarioStates)}");
|
||||
|
||||
var postFileResult = await api.PostFileAsync("1.cs", "C# Hello");
|
||||
Console.WriteLine($"postFileResult = {JsonConvert.SerializeObject(postFileResult)}");
|
||||
|
||||
var getFileResult = await api.GetFileAsync("1.cs");
|
||||
Console.WriteLine($"getFileResult = {getFileResult}");
|
||||
|
||||
var resetMappingsAsync = await api.ResetMappingsAsync();
|
||||
Console.WriteLine($"resetMappingsAsync = {resetMappingsAsync.Status}");
|
||||
|
||||
var resetMappingsAndReloadStaticMappingsAsync = await api.ResetMappingsAsync(true);
|
||||
Console.WriteLine($"resetMappingsAndReloadStaticMappingsAsync = {resetMappingsAndReloadStaticMappingsAsync.Status}");
|
||||
|
||||
Console.WriteLine("Press any key to quit");
|
||||
Console.ReadKey();
|
||||
var guid = Guid.Parse("11111110-a633-40e8-a244-5cb80bc0ab66");
|
||||
var mapping = await api.GetMappingAsync(guid);
|
||||
Console.WriteLine($"mapping = {JsonConvert.SerializeObject(mapping)}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
var request = await api.GetRequestsAsync();
|
||||
Console.WriteLine($"request = {JsonConvert.SerializeObject(request)}");
|
||||
|
||||
//var deleteRequestsAsync = api.DeleteRequestsAsync().Result;
|
||||
//Console.WriteLine($"DeleteRequestsAsync = {deleteRequestsAsync.Status}");
|
||||
|
||||
//var resetRequestsAsync = api.ResetRequestsAsync().Result;
|
||||
//Console.WriteLine($"ResetRequestsAsync = {resetRequestsAsync.Status}");
|
||||
|
||||
var scenarioStates = await api.GetScenariosAsync();
|
||||
Console.WriteLine($"GetScenariosAsync = {JsonConvert.SerializeObject(scenarioStates)}");
|
||||
|
||||
var postFileResult = await api.PostFileAsync("1.cs", "C# Hello");
|
||||
Console.WriteLine($"postFileResult = {JsonConvert.SerializeObject(postFileResult)}");
|
||||
|
||||
var getFileResult = await api.GetFileAsync("1.cs");
|
||||
Console.WriteLine($"getFileResult = {getFileResult}");
|
||||
|
||||
var resetMappingsAsync = await api.ResetMappingsAsync();
|
||||
Console.WriteLine($"resetMappingsAsync = {resetMappingsAsync.Status}");
|
||||
|
||||
var resetMappingsAndReloadStaticMappingsAsync = await api.ResetMappingsAsync(true);
|
||||
Console.WriteLine($"resetMappingsAndReloadStaticMappingsAsync = {resetMappingsAndReloadStaticMappingsAsync.Status}");
|
||||
|
||||
Console.WriteLine("Press any key to quit");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ApplicationIcon>../../resources/WireMock.Net-Logo.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="RestEase" Version="1.5.7" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
|
||||
<!--<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />-->
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -21,10 +21,10 @@
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.*" />
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -33,10 +33,10 @@
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.*" />
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
@@ -30,10 +30,10 @@
|
||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.3.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.10" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.3.*" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.3.*" />
|
||||
<PackageReference Include="log4net" Version="2.0.15" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
@@ -33,12 +35,54 @@ namespace WireMock.Net.ConsoleApplication
|
||||
}
|
||||
}
|
||||
|
||||
public class Todo
|
||||
{
|
||||
public int Id { get; set; }
|
||||
}
|
||||
|
||||
public static class MainApp
|
||||
{
|
||||
public static void Run()
|
||||
{
|
||||
var s = WireMockServer.Start();
|
||||
s.Stop();
|
||||
var mappingBuilder = new MappingBuilder();
|
||||
mappingBuilder
|
||||
.Given(Request
|
||||
.Create()
|
||||
.WithPath(new WildcardMatcher("/param2", true))
|
||||
.WithParam("key", "test")
|
||||
.UsingGet())
|
||||
.RespondWith(Response.Create()
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBodyAsJson(new { result = "param2" }));
|
||||
|
||||
var json = mappingBuilder.ToJson();
|
||||
System.Console.WriteLine("mappingBuilder : Json = {0}", json);
|
||||
|
||||
var todos = new Dictionary<int, Todo>();
|
||||
|
||||
var server = WireMockServer.Start();
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("todos")
|
||||
.UsingGet()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyAsJson(todos.Values)
|
||||
);
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.UsingGet()
|
||||
.WithPath("todos")
|
||||
.WithParam("id")
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
|
||||
);
|
||||
|
||||
var httpClient = server.CreateClient();
|
||||
//server.Stop();
|
||||
|
||||
var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
@@ -57,7 +101,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
string url2 = "http://localhost:9092/";
|
||||
string url3 = "https://localhost:9443/";
|
||||
|
||||
var server = WireMockServer.Start(new WireMockServerSettings
|
||||
server = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
AllowCSharpCodeMatcher = true,
|
||||
Urls = new[] { url1, url2, url3 },
|
||||
@@ -93,7 +137,43 @@ namespace WireMock.Net.ConsoleApplication
|
||||
|
||||
// server.AllowPartialMapping();
|
||||
|
||||
// 400 ms
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/slow/400")
|
||||
.UsingPost())
|
||||
.RespondWith(
|
||||
Response.Create()
|
||||
.WithStatusCode(400)
|
||||
.WithBody("return 400")
|
||||
.WithHeader("Content-Type", "text/plain")
|
||||
);
|
||||
// 4 sec
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("/slow/500")
|
||||
.UsingPost())
|
||||
.RespondWith(
|
||||
Response.Create()
|
||||
.WithStatusCode(500)
|
||||
.WithBody("return 500")
|
||||
.WithHeader("Content-Type", "text/plain")
|
||||
);
|
||||
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.UsingMethod("GET")
|
||||
.WithPath("/foo1")
|
||||
.WithParam("p1", "xyz")
|
||||
)
|
||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||
.RespondWith(Response.Create()
|
||||
.WithBody("Hello World")
|
||||
);
|
||||
|
||||
server.Given(Request.Create().WithPath(MatchOperator.Or, "/mypath", "/mypath1", "/mypath2").UsingPost())
|
||||
.WithGuid("86984b0e-2516-4935-a2ef-b45bf4820d7d")
|
||||
.RespondWith(Response.Create()
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBodyAsJson("{{JsonPath.SelectToken request.body \"..name\"}}")
|
||||
@@ -366,14 +446,14 @@ namespace WireMock.Net.ConsoleApplication
|
||||
// http://localhost:9091/trans?start=1000&stop=1&stop=2
|
||||
server
|
||||
.Given(Request.Create().WithPath("/trans").UsingGet())
|
||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f05")
|
||||
.WithGuid("90356dba-b36c-469a-a17e-669cd84f1f06")
|
||||
.RespondWith(Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithHeader("Transformed-Postman-Token", "token is {{request.headers.Postman-Token}}")
|
||||
.WithHeader("xyz_{{request.headers.Postman-Token}}", "token is {{request.headers.Postman-Token}}")
|
||||
.WithBody(@"{""msg"": ""Hello world CATCH-ALL on /*, {{request.path}}, add={{Math.Add request.query.start.[0] 42}} bykey={{request.query.start}}, bykey={{request.query.stop}}, byidx0={{request.query.stop.[0]}}, byidx1={{request.query.stop.[1]}}"" }")
|
||||
.WithTransformer(TransformerType.Handlebars, true, ReplaceNodeOptions.None)
|
||||
.WithTransformer(TransformerType.Handlebars, true, ReplaceNodeOptions.EvaluateAndTryToConvert)
|
||||
.WithDelay(TimeSpan.FromMilliseconds(100))
|
||||
);
|
||||
|
||||
@@ -563,6 +643,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "application/json")
|
||||
.WithBodyAsJson(new { Id = "5bdf076c-5654-4b3e-842c-7caf1fabf8c9" }));
|
||||
|
||||
server
|
||||
.Given(Request.Create().WithPath("/random200or505").UsingGet())
|
||||
.RespondWith(Response.Create().WithCallback(request =>
|
||||
@@ -570,7 +651,11 @@ namespace WireMock.Net.ConsoleApplication
|
||||
int code = new Random().Next(1, 2) == 1 ? 505 : 200;
|
||||
return new ResponseMessage
|
||||
{
|
||||
BodyData = new BodyData { BodyAsString = "random200or505:" + code, DetectedBodyType = Types.BodyType.String },
|
||||
BodyData = new BodyData
|
||||
{
|
||||
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
|
||||
DetectedBodyType = Types.BodyType.String,
|
||||
},
|
||||
StatusCode = code
|
||||
};
|
||||
}));
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
<Reference Include="Handlebars, Version=2.1.2.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.2.1.2\lib\net452\Handlebars.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Handlebars.Net.Helpers, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.2.3.10\lib\net452\Handlebars.Net.Helpers.dll</HintPath>
|
||||
<Reference Include="Handlebars.Net.Helpers, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.2.3.12\lib\net452\Handlebars.Net.Helpers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Core, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Core.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Core.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Core, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Core.2.3.12\lib\net452\HandlebarsDotNet.Helpers.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=2.0.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<packages>
|
||||
<package id="AnyOf" version="0.3.0" targetFramework="net452" />
|
||||
<package id="Handlebars.Net" version="2.1.2" targetFramework="net452" />
|
||||
<package id="Handlebars.Net.Helpers" version="2.3.10" targetFramework="net452" />
|
||||
<package id="Handlebars.Net.Helpers.Core" version="2.3.10" targetFramework="net452" />
|
||||
<package id="Handlebars.Net.Helpers" version="2.3.12" targetFramework="net452" />
|
||||
<package id="Handlebars.Net.Helpers.Core" version="2.3.12" targetFramework="net452" />
|
||||
<package id="log4net" version="2.0.15" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="3.1.0" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
|
||||
|
||||
@@ -41,11 +41,11 @@
|
||||
<Reference Include="Handlebars, Version=2.1.2.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.2.1.2\lib\net46\Handlebars.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Handlebars.Net.Helpers, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.2.3.10\lib\net452\Handlebars.Net.Helpers.dll</HintPath>
|
||||
<Reference Include="Handlebars.Net.Helpers, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.2.3.12\lib\net46\Handlebars.Net.Helpers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Core, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Core.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Core.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Core, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Core.2.3.12\lib\net46\HandlebarsDotNet.Helpers.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="log4net, Version=2.0.15.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\log4net.2.0.15\lib\net45\log4net.dll</HintPath>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<packages>
|
||||
<package id="AnyOf" version="0.3.0" targetFramework="net461" />
|
||||
<package id="Handlebars.Net" version="2.1.2" targetFramework="net461" />
|
||||
<package id="Handlebars.Net.Helpers" version="2.3.10" targetFramework="net461" />
|
||||
<package id="Handlebars.Net.Helpers.Core" version="2.3.10" targetFramework="net461" />
|
||||
<package id="Handlebars.Net.Helpers" version="2.3.12" targetFramework="net461" />
|
||||
<package id="Handlebars.Net.Helpers.Core" version="2.3.12" targetFramework="net461" />
|
||||
<package id="log4net" version="2.0.15" targetFramework="net461" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="2.2.0" targetFramework="net461" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net461" />
|
||||
|
||||
@@ -49,29 +49,29 @@
|
||||
<Reference Include="Handlebars, Version=2.1.2.0, Culture=neutral, PublicKeyToken=22225d0bf33cd661, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.2.1.2\lib\net46\Handlebars.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Handlebars.Net.Helpers, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.2.3.10\lib\net452\Handlebars.Net.Helpers.dll</HintPath>
|
||||
<Reference Include="Handlebars.Net.Helpers, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.2.3.12\lib\net46\Handlebars.Net.Helpers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Core, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Core.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Core.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Core, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Core.2.3.12\lib\net46\HandlebarsDotNet.Helpers.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.DynamicLinq, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.DynamicLinq.2.3.10\lib\net452\HandlebarsDotNet.Helpers.DynamicLinq.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.DynamicLinq, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.DynamicLinq.2.3.12\lib\net46\HandlebarsDotNet.Helpers.DynamicLinq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Humanizer, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Humanizer.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Humanizer.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Humanizer, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Humanizer.2.3.12\lib\net46\HandlebarsDotNet.Helpers.Humanizer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Json, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Json.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Json.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Json, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Json.2.3.12\lib\net46\HandlebarsDotNet.Helpers.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Random, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Random.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Random.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Random, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Random.2.3.12\lib\net46\HandlebarsDotNet.Helpers.Random.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Xeger, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Xeger.2.3.10\lib\net452\HandlebarsDotNet.Helpers.Xeger.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.Xeger, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.Xeger.2.3.12\lib\net46\HandlebarsDotNet.Helpers.Xeger.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.XPath, Version=2.3.10.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.XPath.2.3.10\lib\net452\HandlebarsDotNet.Helpers.XPath.dll</HintPath>
|
||||
<Reference Include="HandlebarsDotNet.Helpers.XPath, Version=2.3.12.0, Culture=neutral, PublicKeyToken=00d131fae0c250bc, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Handlebars.Net.Helpers.XPath.2.3.12\lib\net46\HandlebarsDotNet.Helpers.XPath.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Humanizer, Version=2.14.0.0, Culture=neutral, PublicKeyToken=979442b78dfc278e, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\Humanizer.Core.2.14.1\lib\netstandard2.0\Humanizer.dll</HintPath>
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<package id="AnyOf" version="0.3.0" targetFramework="net472" />
|
||||
<package id="Fare" version="2.2.1" targetFramework="net472" />
|
||||
<package id="Handlebars.Net" version="2.1.2" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Core" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.DynamicLinq" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Humanizer" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Json" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Random" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Xeger" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.XPath" version="2.3.10" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Core" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.DynamicLinq" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Humanizer" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Json" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Random" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.Xeger" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Handlebars.Net.Helpers.XPath" version="2.3.12" targetFramework="net472" />
|
||||
<package id="Humanizer" version="2.14.1" targetFramework="net472" />
|
||||
<package id="Humanizer.Core" version="2.14.1" targetFramework="net472" />
|
||||
<package id="Humanizer.Core.af" version="2.14.1" targetFramework="net472" />
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net.Http;
|
||||
using Newtonsoft.Json;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
|
||||
@@ -26,10 +26,8 @@ namespace WireMock.Net.Console.Proxy.Net452
|
||||
}
|
||||
});
|
||||
|
||||
server.LogEntriesChanged += (sender, eventRecordArgs) =>
|
||||
{
|
||||
System.Console.WriteLine(JsonConvert.SerializeObject(eventRecordArgs.NewItems, Formatting.Indented));
|
||||
};
|
||||
System.Console.WriteLine("Subscribing to LogEntriesChanged");
|
||||
server.LogEntriesChanged += Server_LogEntriesChanged;
|
||||
|
||||
var uri = new Uri(urls[0]);
|
||||
var form = new MultipartFormDataContent
|
||||
@@ -38,9 +36,23 @@ namespace WireMock.Net.Console.Proxy.Net452
|
||||
};
|
||||
new HttpClient().PostAsync(uri, form).GetAwaiter().GetResult();
|
||||
|
||||
System.Console.WriteLine("Unsubscribing to LogEntriesChanged");
|
||||
server.LogEntriesChanged -= Server_LogEntriesChanged;
|
||||
|
||||
form = new MultipartFormDataContent
|
||||
{
|
||||
{ new StringContent("data2"), "test2", "test2.txt" }
|
||||
};
|
||||
new HttpClient().PostAsync(uri, form).GetAwaiter().GetResult();
|
||||
|
||||
System.Console.WriteLine("Press any key to stop the server");
|
||||
System.Console.ReadKey();
|
||||
server.Stop();
|
||||
}
|
||||
|
||||
private static void Server_LogEntriesChanged(object sender, NotifyCollectionChangedEventArgs eventRecordArgs)
|
||||
{
|
||||
System.Console.WriteLine("Server_LogEntriesChanged : {0}", eventRecordArgs.NewItems.Count);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.AspNet.WebApi.OwinSelfHost" version="5.2.3" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin" version="4.2.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Host.HttpListener" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Microsoft.Owin.Hosting" version="2.0.2" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using log4net.Repository;
|
||||
@@ -10,71 +11,112 @@ using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Net.StandAlone.NETCoreApp
|
||||
namespace WireMock.Net.StandAlone.NETCoreApp;
|
||||
|
||||
static class Program
|
||||
{
|
||||
static class Program
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||
// private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
|
||||
private static int sleepTime = 30000;
|
||||
private static WireMockServer _server;
|
||||
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||
// private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
await TestAsync().ConfigureAwait(false);
|
||||
return;
|
||||
|
||||
private static int sleepTime = 30000;
|
||||
private static WireMockServer _server;
|
||||
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||
|
||||
static void Main(string[] args)
|
||||
if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger()))
|
||||
{
|
||||
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||
|
||||
if (!WireMockServerSettingsParser.TryParseArguments(args, out var settings, new WireMockLog4NetLogger()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
|
||||
|
||||
_server = WireMockServer.Start(settings);
|
||||
|
||||
//_server.Given(Request.Create().WithPath("/api/sap")
|
||||
// .UsingPost()
|
||||
// .WithBody((IBodyData xmlData) =>
|
||||
// {
|
||||
// //xmlData is always null
|
||||
// return true;
|
||||
// }))
|
||||
// .RespondWith(Response.Create().WithStatusCode(System.Net.HttpStatusCode.OK));
|
||||
|
||||
//_server
|
||||
// .Given(Request.Create()
|
||||
// .UsingAnyMethod())
|
||||
// .RespondWith(Response.Create()
|
||||
// .WithTransformer()
|
||||
// .WithBody("{{Random Type=\"Integer\" Min=100 Max=999999}} {{DateTime.Now}} {{DateTime.Now \"yyyy-MMM\"}} {{String.Format (DateTime.Now) \"MMM-dd\"}}"));
|
||||
|
||||
Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
|
||||
|
||||
Console.CancelKeyPress += (s, e) =>
|
||||
{
|
||||
Stop("CancelKeyPress");
|
||||
};
|
||||
|
||||
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
|
||||
{
|
||||
Stop("AssemblyLoadContext.Default.Unloading");
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running : {_server.IsStarted}");
|
||||
Thread.Sleep(sleepTime);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
private static void Stop(string why)
|
||||
settings.Logger.Debug("WireMock.Net server arguments [{0}]", string.Join(", ", args.Select(a => $"'{a}'")));
|
||||
|
||||
_server = WireMockServer.Start(settings);
|
||||
|
||||
//_server.Given(Request.Create().WithPath("/api/sap")
|
||||
// .UsingPost()
|
||||
// .WithBody((IBodyData xmlData) =>
|
||||
// {
|
||||
// //xmlData is always null
|
||||
// return true;
|
||||
// }))
|
||||
// .RespondWith(Response.Create().WithStatusCode(System.Net.HttpStatusCode.OK));
|
||||
|
||||
//_server
|
||||
// .Given(Request.Create()
|
||||
// .UsingAnyMethod())
|
||||
// .RespondWith(Response.Create()
|
||||
// .WithTransformer()
|
||||
// .WithBody("{{Random Type=\"Integer\" Min=100 Max=999999}} {{DateTime.Now}} {{DateTime.Now \"yyyy-MMM\"}} {{String.Format (DateTime.Now) \"MMM-dd\"}}"));
|
||||
|
||||
Console.WriteLine($"{DateTime.UtcNow} Press Ctrl+C to shut down");
|
||||
|
||||
Console.CancelKeyPress += (s, e) =>
|
||||
{
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
|
||||
_server.Stop();
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
|
||||
Stop("CancelKeyPress");
|
||||
};
|
||||
|
||||
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
|
||||
{
|
||||
Stop("AssemblyLoadContext.Default.Unloading");
|
||||
};
|
||||
|
||||
while (true)
|
||||
{
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server running : {_server.IsStarted}");
|
||||
Thread.Sleep(sleepTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Stop(string why)
|
||||
{
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopping because '{why}'");
|
||||
_server.Stop();
|
||||
Console.WriteLine($"{DateTime.UtcNow} WireMock.Net server stopped");
|
||||
}
|
||||
|
||||
private static async Task TestAsync()
|
||||
{
|
||||
for (var i = 0; i < 20; i++)
|
||||
{
|
||||
var server = WireMockServer.Start();
|
||||
|
||||
server
|
||||
.Given(
|
||||
Request.Create().WithPath("/some/thing").UsingGet()
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "text/plain")
|
||||
.WithBody("Hello world! : " + i)
|
||||
);
|
||||
|
||||
server
|
||||
.Given(
|
||||
Request.Create().WithPath("/some/thing").UsingGet()
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create()
|
||||
.WithStatusCode(200)
|
||||
.WithHeader("Content-Type", "text/plain")
|
||||
.WithBody("Hello world duplicate! : " + i)
|
||||
);
|
||||
|
||||
var client = server.CreateClient();
|
||||
|
||||
var response = await client.GetAsync($"{server.Url}/some/thing").ConfigureAwait(false);
|
||||
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
Console.WriteLine($"counter {i} value:{content}");
|
||||
|
||||
server.Reset();
|
||||
server.Dispose();
|
||||
server.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Admin.Mappings;
|
||||
@@ -14,6 +15,11 @@ public class MappingModel
|
||||
/// </summary>
|
||||
public Guid? Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The datetime when this mapping was created or updated.
|
||||
/// </summary>
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the TimeSettings when which this mapping should be used.
|
||||
/// </summary>
|
||||
@@ -79,4 +85,13 @@ public class MappingModel
|
||||
/// Fire and forget for webhooks.
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
public object? Data { get; set; }
|
||||
}
|
||||
@@ -1,121 +1,120 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace WireMock.Admin.Mappings
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// ResponseModel
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
[FluentBuilder.AutoGenerateBuilder]
|
||||
public class ResponseModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP status.
|
||||
/// </summary>
|
||||
public object? StatusCode { get; set; }
|
||||
public object? StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string? BodyDestination { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body destination (SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
public string? BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string? Body { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
/// </summary>
|
||||
public string? Body { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
public object? BodyAsJson { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as JSON object).
|
||||
/// </summary>
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.
|
||||
/// </summary>
|
||||
public bool? BodyAsJsonIndented { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body (as bytearray).
|
||||
/// </summary>
|
||||
public byte[]? BodyAsBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string? BodyAsFile { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body as a file.
|
||||
/// </summary>
|
||||
public string? BodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
/// <summary>
|
||||
/// Is the body as file cached?
|
||||
/// </summary>
|
||||
public bool? BodyAsFileIsCached { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public EncodingModel? BodyEncoding { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the body encoding.
|
||||
/// </summary>
|
||||
public EncodingModel? BodyEncoding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use ResponseMessage Transformer.
|
||||
/// </summary>
|
||||
public bool? UseTransformer { get; set; }
|
||||
/// <summary>
|
||||
/// Use ResponseMessage Transformer.
|
||||
/// </summary>
|
||||
public bool? UseTransformer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the transformer.
|
||||
/// </summary>
|
||||
public string? TransformerType { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the type of the transformer.
|
||||
/// </summary>
|
||||
public string? TransformerType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||
/// </summary>
|
||||
public bool? UseTransformerForBodyAsFile { get; set; }
|
||||
/// <summary>
|
||||
/// Use the Handlebars transformer for the content from the referenced BodyAsFile.
|
||||
/// </summary>
|
||||
public bool? UseTransformerForBodyAsFile { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
/// <summary>
|
||||
/// The ReplaceNodeOptions to use when transforming a JSON node.
|
||||
/// </summary>
|
||||
public string? TransformerReplaceNodeOptions { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? Headers { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the headers.
|
||||
/// </summary>
|
||||
public IDictionary<string, object>? Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
public string? HeadersRaw { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Headers (Raw).
|
||||
/// </summary>
|
||||
public string? HeadersRaw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? Delay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MinimumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the maximum random delay in milliseconds.
|
||||
/// </summary>
|
||||
public int? MaximumRandomDelay { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
public string? ProxyUrl { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the Proxy URL.
|
||||
/// </summary>
|
||||
public string? ProxyUrl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
/// <summary>
|
||||
/// The client X509Certificate2 Thumbprint or SubjectName to use.
|
||||
/// </summary>
|
||||
public string? X509Certificate2ThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the fault.
|
||||
/// </summary>
|
||||
public FaultModel? Fault { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the fault.
|
||||
/// </summary>
|
||||
public FaultModel? Fault { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the WebProxy settings.
|
||||
/// </summary>
|
||||
public WebProxyModel? WebProxy { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets or sets the WebProxy settings.
|
||||
/// </summary>
|
||||
public WebProxyModel? WebProxy { get; set; }
|
||||
}
|
||||
@@ -96,4 +96,16 @@ public class SettingsModel
|
||||
/// Default value = "All".
|
||||
/// </summary>
|
||||
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||
|
||||
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||
/// <summary>
|
||||
/// Server client certificate mode
|
||||
/// </summary>
|
||||
public ClientCertificateMode ClientCertificateMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to accept any client certificate
|
||||
/// </summary>
|
||||
public bool AcceptAnyClientCertificate { get; set; }
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// RequestModelBuilder
|
||||
/// </summary>
|
||||
public partial class RequestModelBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// UsingConnect: add HTTP Method matching on `CONNECT`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingConnect() => WithMethods("CONNECT");
|
||||
|
||||
/// <summary>
|
||||
/// UsingDelete: add HTTP Method matching on `DELETE`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingDelete() => WithMethods("DELETE");
|
||||
|
||||
/// <summary>
|
||||
/// UsingGet: add HTTP Method matching on `GET`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingGet() => WithMethods("GET");
|
||||
|
||||
/// <summary>
|
||||
/// UsingHead: Add HTTP Method matching on `HEAD`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingHead() => WithMethods("HEAD");
|
||||
|
||||
/// <summary>
|
||||
/// UsingPost: add HTTP Method matching on `POST`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingPost() => WithMethods("POST");
|
||||
|
||||
/// <summary>
|
||||
/// UsingPatch: add HTTP Method matching on `PATCH`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingPatch() => WithMethods("PATCH");
|
||||
|
||||
/// <summary>
|
||||
/// UsingPut: add HTTP Method matching on `OPTIONS`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingOptions() => WithMethods("OPTIONS");
|
||||
|
||||
/// <summary>
|
||||
/// UsingPut: add HTTP Method matching on `PUT`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingPut() => WithMethods("PUT");
|
||||
|
||||
/// <summary>
|
||||
/// UsingTrace: add HTTP Method matching on `TRACE`.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingTrace() => WithMethods("TRACE");
|
||||
|
||||
/// <summary>
|
||||
/// UsingAnyMethod: add HTTP Method matching on any method.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="RequestModelBuilder"/>.</returns>
|
||||
public RequestModelBuilder UsingAnyMethod() => this;
|
||||
|
||||
/// <summary>
|
||||
/// Set the ClientIP.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithClientIP(string value) => WithClientIP(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the ClientIP.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithClientIP(ClientIPModel value) => WithClientIP(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the ClientIP.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithClientIP(Action<ClientIPModelBuilder> action)
|
||||
{
|
||||
return WithClientIP(() =>
|
||||
{
|
||||
var builder = new ClientIPModelBuilder();
|
||||
action(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Path.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithPath(string value) => WithPath(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the Path.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithPath(PathModel value) => WithPath(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the Path.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithPath(Action<PathModelBuilder> action)
|
||||
{
|
||||
return WithPath(() =>
|
||||
{
|
||||
var builder = new PathModelBuilder();
|
||||
action(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the Url.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithUrl(string value) => WithUrl(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the Url.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithUrl(UrlModel value) => WithUrl(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the Url.
|
||||
/// </summary>
|
||||
public RequestModelBuilder WithUrl(Action<UrlModelBuilder> action)
|
||||
{
|
||||
return WithUrl(() =>
|
||||
{
|
||||
var builder = new UrlModelBuilder();
|
||||
action(builder);
|
||||
return builder.Build();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.Admin.Mappings;
|
||||
|
||||
/// <summary>
|
||||
/// ResponseModelBuilder
|
||||
/// </summary>
|
||||
public partial class ResponseModelBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Set the StatusCode.
|
||||
/// </summary>
|
||||
public ResponseModelBuilder WithStatusCode(int value) => WithStatusCode(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the StatusCode.
|
||||
/// </summary>
|
||||
public ResponseModelBuilder WithStatusCode(HttpStatusCode value) => WithStatusCode(() => value);
|
||||
|
||||
/// <summary>
|
||||
/// Set the Delay.
|
||||
/// </summary>
|
||||
public ResponseModelBuilder WithDelay(TimeSpan value) => WithDelay((int) value.TotalMilliseconds);
|
||||
|
||||
/// <summary>
|
||||
/// Set the MinimumRandomDelay.
|
||||
/// </summary>
|
||||
public ResponseModelBuilder WithMinimumRandomDelay(TimeSpan value) => WithMinimumRandomDelay((int)value.TotalMilliseconds);
|
||||
|
||||
/// <summary>
|
||||
/// Set the MaximumRandomDelay.
|
||||
/// </summary>
|
||||
public ResponseModelBuilder WithMaximumRandomDelay(TimeSpan value) => WithMaximumRandomDelay((int)value.TotalMilliseconds);
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -75,6 +78,11 @@ public interface IRequestMessage
|
||||
/// </summary>
|
||||
IDictionary<string, WireMockList<string>>? Query { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the query.
|
||||
/// </summary>
|
||||
IDictionary<string, WireMockList<string>>? QueryIgnoreCase { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw query.
|
||||
/// </summary>
|
||||
@@ -134,4 +142,11 @@ public interface IRequestMessage
|
||||
/// Gets the origin
|
||||
/// </summary>
|
||||
string Origin { get; }
|
||||
|
||||
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||
/// <summary>
|
||||
/// Gets the connection's client certificate
|
||||
/// </summary>
|
||||
X509Certificate2? ClientCertificate { get; }
|
||||
#endif
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public interface IResponseMessage
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
object? StatusCode { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds the header.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -38,6 +39,11 @@ public interface IBodyData
|
||||
/// </summary>
|
||||
string? BodyAsString { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The body as Form UrlEncoded dictionary.
|
||||
/// </summary>
|
||||
IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The detected body type (detection based on body content).
|
||||
/// </summary>
|
||||
|
||||
@@ -3,212 +3,227 @@ using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Server
|
||||
namespace WireMock.Server;
|
||||
|
||||
/// <summary>
|
||||
/// The fluent mock server interface.
|
||||
/// </summary>
|
||||
public interface IWireMockServer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// The fluent mock server interface.
|
||||
/// Gets a value indicating whether this server is started.
|
||||
/// </summary>
|
||||
public interface IWireMockServer : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this server is started.
|
||||
/// </summary>
|
||||
bool IsStarted { get; }
|
||||
bool IsStarted { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the request logs.
|
||||
/// </summary>
|
||||
IEnumerable<ILogEntry> LogEntries { get; }
|
||||
/// <summary>
|
||||
/// Gets the request logs.
|
||||
/// </summary>
|
||||
IEnumerable<ILogEntry> LogEntries { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mappings as MappingModels.
|
||||
/// </summary>
|
||||
IEnumerable<MappingModel> MappingModels { get; }
|
||||
/// <summary>
|
||||
/// Gets the mappings as MappingModels.
|
||||
/// </summary>
|
||||
IEnumerable<MappingModel> MappingModels { get; }
|
||||
|
||||
// <summary>
|
||||
// Gets the mappings.
|
||||
// </summary>
|
||||
//[PublicAPI]
|
||||
//IEnumerable<IMapping> Mappings { get; }
|
||||
// <summary>
|
||||
// Gets the mappings.
|
||||
// </summary>
|
||||
//[PublicAPI]
|
||||
//IEnumerable<IMapping> Mappings { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
List<int> Ports { get; }
|
||||
/// <summary>
|
||||
/// Gets the ports.
|
||||
/// </summary>
|
||||
List<int> Ports { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first port.
|
||||
/// </summary>
|
||||
int Port { get; }
|
||||
/// <summary>
|
||||
/// Gets the first port.
|
||||
/// </summary>
|
||||
int Port { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
string[] Urls { get; }
|
||||
/// <summary>
|
||||
/// Gets the urls.
|
||||
/// </summary>
|
||||
string[] Urls { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first url.
|
||||
/// </summary>
|
||||
string? Url { get; }
|
||||
/// <summary>
|
||||
/// Gets the first url.
|
||||
/// </summary>
|
||||
string? Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the consumer.
|
||||
/// </summary>
|
||||
string? Consumer { get; }
|
||||
/// <summary>
|
||||
/// Gets the consumer.
|
||||
/// </summary>
|
||||
string? Consumer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider.
|
||||
/// </summary>
|
||||
string? Provider { get; }
|
||||
/// <summary>
|
||||
/// Gets the provider.
|
||||
/// </summary>
|
||||
string? Provider { get; }
|
||||
|
||||
//ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
|
||||
//ConcurrentDictionary<string, ScenarioState> Scenarios { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when [log entries changed].
|
||||
/// </summary>
|
||||
event NotifyCollectionChangedEventHandler LogEntriesChanged;
|
||||
/// <summary>
|
||||
/// Occurs when [log entries changed].
|
||||
/// </summary>
|
||||
event NotifyCollectionChangedEventHandler LogEntriesChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a 'catch all mapping'
|
||||
///
|
||||
/// - matches all Paths and any Methods
|
||||
/// - priority is set to 1000
|
||||
/// - responds with a 404 "No matching mapping found"
|
||||
/// </summary>
|
||||
void AddCatchAllMapping();
|
||||
/// <summary>
|
||||
/// Adds a 'catch all mapping'
|
||||
///
|
||||
/// - matches all Paths and any Methods
|
||||
/// - priority is set to 1000
|
||||
/// - responds with a 404 "No matching mapping found"
|
||||
/// </summary>
|
||||
void AddCatchAllMapping();
|
||||
|
||||
/// <summary>
|
||||
/// The add request processing delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">The delay.</param>
|
||||
void AddGlobalProcessingDelay(TimeSpan delay);
|
||||
/// <summary>
|
||||
/// The add request processing delay.
|
||||
/// </summary>
|
||||
/// <param name="delay">The delay.</param>
|
||||
void AddGlobalProcessingDelay(TimeSpan delay);
|
||||
|
||||
/// <summary>
|
||||
/// Allows the partial mapping.
|
||||
/// </summary>
|
||||
void AllowPartialMapping(bool allow = true);
|
||||
/// <summary>
|
||||
/// Allows the partial mapping.
|
||||
/// </summary>
|
||||
void AllowPartialMapping(bool allow = true);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a LogEntry.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
bool DeleteLogEntry(Guid guid);
|
||||
/// <summary>
|
||||
/// Deletes a LogEntry.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
bool DeleteLogEntry(Guid guid);
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
bool DeleteMapping(Guid guid);
|
||||
/// <summary>
|
||||
/// Deletes the mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
bool DeleteMapping(Guid guid);
|
||||
|
||||
//IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers);
|
||||
//IEnumerable<LogEntry> FindLogEntries([NotNull] params IRequestMatcher[] matchers);
|
||||
|
||||
// IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
|
||||
// IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
|
||||
|
||||
/// <summary>
|
||||
/// Reads a static mapping file and adds or updates a single mapping.
|
||||
///
|
||||
/// Calling this method manually forces WireMock.Net to read and apply the specified static mapping file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the static mapping file.</param>
|
||||
bool ReadStaticMappingAndAddOrUpdate(string path);
|
||||
/// <summary>
|
||||
/// Reads a static mapping file and adds or updates a single mapping.
|
||||
///
|
||||
/// Calling this method manually forces WireMock.Net to read and apply the specified static mapping file.
|
||||
/// </summary>
|
||||
/// <param name="path">The path to the static mapping file.</param>
|
||||
bool ReadStaticMappingAndAddOrUpdate(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Reads the static mappings from a folder.
|
||||
/// (This method is also used when WireMockServerSettings.ReadStaticMappings is set to true.
|
||||
///
|
||||
/// Calling this method manually forces WireMock.Net to read and apply all static mapping files in the specified folder.
|
||||
/// </summary>
|
||||
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
|
||||
void ReadStaticMappings(string? folder = null);
|
||||
/// <summary>
|
||||
/// Reads the static mappings from a folder.
|
||||
/// (This method is also used when WireMockServerSettings.ReadStaticMappings is set to true.
|
||||
///
|
||||
/// Calling this method manually forces WireMock.Net to read and apply all static mapping files in the specified folder.
|
||||
/// </summary>
|
||||
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
|
||||
void ReadStaticMappings(string? folder = null);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the authentication.
|
||||
/// </summary>
|
||||
void RemoveAuthentication();
|
||||
/// <summary>
|
||||
/// Removes the authentication.
|
||||
/// </summary>
|
||||
void RemoveAuthentication();
|
||||
|
||||
/// <summary>
|
||||
/// Resets LogEntries and Mappings.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
/// <summary>
|
||||
/// Resets LogEntries and Mappings.
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// Resets the Mappings.
|
||||
/// </summary>
|
||||
void ResetMappings();
|
||||
/// <summary>
|
||||
/// Resets the Mappings.
|
||||
/// </summary>
|
||||
void ResetMappings();
|
||||
|
||||
/// <summary>
|
||||
/// Resets all Scenarios.
|
||||
/// </summary>
|
||||
void ResetScenarios();
|
||||
/// <summary>
|
||||
/// Resets all Scenarios.
|
||||
/// </summary>
|
||||
void ResetScenarios();
|
||||
|
||||
/// <summary>
|
||||
/// Resets a specific Scenario by the name.
|
||||
/// </summary>
|
||||
bool ResetScenario(string name);
|
||||
/// <summary>
|
||||
/// Resets a specific Scenario by the name.
|
||||
/// </summary>
|
||||
bool ResetScenario(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Resets the LogEntries.
|
||||
/// </summary>
|
||||
void ResetLogEntries();
|
||||
/// <summary>
|
||||
/// Resets the LogEntries.
|
||||
/// </summary>
|
||||
void ResetLogEntries();
|
||||
|
||||
/// <summary>
|
||||
/// Saves the static mappings.
|
||||
/// </summary>
|
||||
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
|
||||
void SaveStaticMappings(string? folder = null);
|
||||
/// <summary>
|
||||
/// Saves the static mappings.
|
||||
/// </summary>
|
||||
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
|
||||
void SaveStaticMappings(string? folder = null);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="tenant">The Tenant.</param>
|
||||
/// <param name="audience">The Audience or Resource.</param>
|
||||
void SetAzureADAuthentication(string tenant, string audience);
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="tenant">The Tenant.</param>
|
||||
/// <param name="audience">The Audience or Resource.</param>
|
||||
void SetAzureADAuthentication(string tenant, string audience);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
void SetBasicAuthentication(string username, string password);
|
||||
/// <summary>
|
||||
/// Sets the basic authentication.
|
||||
/// </summary>
|
||||
/// <param name="username">The username.</param>
|
||||
/// <param name="password">The password.</param>
|
||||
void SetBasicAuthentication(string username, string password);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the maximum RequestLog count.
|
||||
/// </summary>
|
||||
/// <param name="maxRequestLogCount">The maximum RequestLog count.</param>
|
||||
void SetMaxRequestLogCount(int? maxRequestLogCount);
|
||||
/// <summary>
|
||||
/// Sets the maximum RequestLog count.
|
||||
/// </summary>
|
||||
/// <param name="maxRequestLogCount">The maximum RequestLog count.</param>
|
||||
void SetMaxRequestLogCount(int? maxRequestLogCount);
|
||||
|
||||
/// <summary>
|
||||
/// Sets RequestLog expiration in hours.
|
||||
/// </summary>
|
||||
/// <param name="requestLogExpirationDuration">The RequestLog expiration in hours.</param>
|
||||
void SetRequestLogExpirationDuration(int? requestLogExpirationDuration);
|
||||
/// <summary>
|
||||
/// Sets RequestLog expiration in hours.
|
||||
/// </summary>
|
||||
/// <param name="requestLogExpirationDuration">The RequestLog expiration in hours.</param>
|
||||
void SetRequestLogExpirationDuration(int? requestLogExpirationDuration);
|
||||
|
||||
/// <summary>
|
||||
/// Stop this server.
|
||||
/// </summary>
|
||||
void Stop();
|
||||
/// <summary>
|
||||
/// Stop this server.
|
||||
/// </summary>
|
||||
void Stop();
|
||||
|
||||
/// <summary>
|
||||
/// Watches the static mappings for changes.
|
||||
/// </summary>
|
||||
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
|
||||
void WatchStaticMappings(string? folder = null);
|
||||
/// <summary>
|
||||
/// Watches the static mappings for changes.
|
||||
/// </summary>
|
||||
/// <param name="folder">The optional folder. If not defined, use {CurrentFolder}/__admin/mappings</param>
|
||||
void WatchStaticMappings(string? folder = null);
|
||||
|
||||
/// <summary>
|
||||
/// Register the mappings (via <see cref="MappingModel"/>).
|
||||
///
|
||||
/// This can be used if you have 1 or more <see cref="MappingModel"/> defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
|
||||
/// </summary>
|
||||
/// <param name="mappings">The MappingModels</param>
|
||||
IWireMockServer WithMapping(params MappingModel[] mappings);
|
||||
/// <summary>
|
||||
/// Register the mappings (via <see cref="MappingModel"/>).
|
||||
///
|
||||
/// This can be used if you have 1 or more <see cref="MappingModel"/> defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
|
||||
/// </summary>
|
||||
/// <param name="mappings">The MappingModels</param>
|
||||
IWireMockServer WithMapping(params MappingModel[] mappings);
|
||||
|
||||
/// <summary>
|
||||
/// Register the mappings (via json string).
|
||||
///
|
||||
/// This can be used if you the mappings as json string defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
|
||||
/// </summary>
|
||||
/// <param name="mappings">The mapping(s) as json string.</param>
|
||||
IWireMockServer WithMapping(string mappings);
|
||||
}
|
||||
/// <summary>
|
||||
/// Register the mappings (via json string).
|
||||
///
|
||||
/// This can be used if you the mappings as json string defined and want to register these in WireMock.Net directly instead of using the fluent syntax.
|
||||
/// </summary>
|
||||
/// <param name="mappings">The mapping(s) as json string.</param>
|
||||
IWireMockServer WithMapping(string mappings);
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code for a mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Mapping Guid.</param>
|
||||
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
|
||||
/// <returns>C# code (null in case the mapping is not found)</returns>
|
||||
string? MappingToCSharpCode(Guid guid, MappingConverterType converterType);
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code for all mappings.
|
||||
/// </summary>
|
||||
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
|
||||
/// <returns>C# code</returns>
|
||||
public string MappingsToCSharpCode(MappingConverterType converterType);
|
||||
}
|
||||
@@ -1,38 +1,42 @@
|
||||
namespace WireMock.Types
|
||||
namespace WireMock.Types;
|
||||
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// The BodyType
|
||||
/// No body present
|
||||
/// </summary>
|
||||
public enum BodyType
|
||||
{
|
||||
/// <summary>
|
||||
/// No body present
|
||||
/// </summary>
|
||||
None,
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
/// <summary>
|
||||
/// Body is a String
|
||||
/// </summary>
|
||||
String,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
/// <summary>
|
||||
/// Body is a Json object
|
||||
/// </summary>
|
||||
Json,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
/// <summary>
|
||||
/// Body is a Byte array
|
||||
/// </summary>
|
||||
Bytes,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File,
|
||||
/// <summary>
|
||||
/// Body is a File
|
||||
/// </summary>
|
||||
File,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a MultiPart
|
||||
/// </summary>
|
||||
MultiPart
|
||||
}
|
||||
/// <summary>
|
||||
/// Body is a MultiPart
|
||||
/// </summary>
|
||||
MultiPart,
|
||||
|
||||
/// <summary>
|
||||
/// Body is a String which is x-www-form-urlencoded.
|
||||
/// </summary>
|
||||
FormUrlEncoded
|
||||
}
|
||||
31
src/WireMock.Net.Abstractions/Types/ClientCertificateMode.cs
Normal file
31
src/WireMock.Net.Abstractions/Types/ClientCertificateMode.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
namespace WireMock.Types;
|
||||
|
||||
#if NETSTANDARD1_3_OR_GREATER || NET461
|
||||
/// <summary>
|
||||
/// Describes the client certificate requirements for a HTTPS connection.
|
||||
/// This enum is the same as https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.server.kestrel.https.clientcertificatemode
|
||||
/// </summary>
|
||||
public enum ClientCertificateMode
|
||||
{
|
||||
/// <summary>
|
||||
/// A client certificate is not required and will not be requested from clients.
|
||||
/// </summary>
|
||||
NoCertificate,
|
||||
|
||||
/// <summary>
|
||||
/// A client certificate will be requested; however, authentication will not fail if a certificate is not provided by the client.
|
||||
/// </summary>
|
||||
AllowCertificate,
|
||||
|
||||
/// <summary>
|
||||
/// A client certificate will be requested, and the client must provide a valid certificate for authentication to succeed.
|
||||
/// </summary>
|
||||
RequireCertificate,
|
||||
|
||||
/// <summary>
|
||||
/// A client certificate is not required and will not be requested from clients at the start of the connection.
|
||||
/// It may be requested by the application later.
|
||||
/// </summary>
|
||||
DelayCertificate,
|
||||
}
|
||||
#endif
|
||||
11
src/WireMock.Net.Abstractions/Types/MappingConverterType.cs
Normal file
11
src/WireMock.Net.Abstractions/Types/MappingConverterType.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace WireMock.Types;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum MappingConverterType
|
||||
{
|
||||
Server,
|
||||
|
||||
Builder
|
||||
}
|
||||
@@ -1,36 +1,25 @@
|
||||
using System;
|
||||
namespace WireMock.Types;
|
||||
|
||||
namespace WireMock.Types
|
||||
/// <summary>
|
||||
/// Logic to use when replace a JSON node using the Transformer.
|
||||
/// </summary>
|
||||
public enum ReplaceNodeOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Flags to use when replace a JSON node using the Transformer.
|
||||
/// Try to evaluate a templated value.
|
||||
/// In case this is valid, return the value and if the value can be converted to a primitive type, use that value.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum ReplaceNodeOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Default
|
||||
/// </summary>
|
||||
None = 0
|
||||
EvaluateAndTryToConvert = 0,
|
||||
|
||||
///// <summary>
|
||||
///// Replace boolean string value to a real boolean value. (This is used by default to maintain backward compatibility.)
|
||||
///// </summary>
|
||||
//Bool = 0b00000001,
|
||||
/// <summary>
|
||||
/// Try to evaluate a templated value.
|
||||
/// In case this is valid, return the value, else fallback to the parse behavior.
|
||||
/// </summary>
|
||||
Evaluate = 1,
|
||||
|
||||
///// <summary>
|
||||
///// Replace integer string value to a real integer value.
|
||||
///// </summary>
|
||||
//Integer = 0b00000010,
|
||||
|
||||
///// <summary>
|
||||
///// Replace long string value to a real long value.
|
||||
///// </summary>
|
||||
//Long = 0b00000100,
|
||||
|
||||
///// <summary>
|
||||
///// Replace all string values to a real values.
|
||||
///// </summary>
|
||||
//All = Bool | Integer | Long
|
||||
}
|
||||
/// <summary>
|
||||
/// Parse templated string to a templated string.
|
||||
/// (keep a templated string value as string value).
|
||||
/// </summary>
|
||||
Parse = 2
|
||||
}
|
||||
@@ -1,23 +1,22 @@
|
||||
namespace WireMock.Types
|
||||
namespace WireMock.Types;
|
||||
|
||||
/// <summary>
|
||||
/// The ResponseMessage Transformers
|
||||
/// </summary>
|
||||
public enum TransformerType
|
||||
{
|
||||
/// <summary>
|
||||
/// The ResponseMessage Transformers
|
||||
/// https://github.com/Handlebars-Net/Handlebars.Net
|
||||
/// </summary>
|
||||
public enum TransformerType
|
||||
{
|
||||
/// <summary>
|
||||
/// https://github.com/Handlebars-Net/Handlebars.Net
|
||||
/// </summary>
|
||||
Handlebars,
|
||||
Handlebars,
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/scriban/scriban : default
|
||||
/// </summary>
|
||||
Scriban,
|
||||
/// <summary>
|
||||
/// https://github.com/scriban/scriban : default
|
||||
/// </summary>
|
||||
Scriban,
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/scriban/scriban : DotLiquid
|
||||
/// </summary>
|
||||
ScribanDotLiquid
|
||||
}
|
||||
/// <summary>
|
||||
/// https://github.com/scriban/scriban : DotLiquid
|
||||
/// </summary>
|
||||
ScribanDotLiquid
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Types;
|
||||
|
||||
@@ -63,7 +64,8 @@ public class WireMockList<T> : List<T>
|
||||
return this[0]?.ToString();
|
||||
|
||||
default:
|
||||
return base.ToString();
|
||||
var strings = this.Select(x => x as string ?? x?.ToString());
|
||||
return string.Join(", ", strings);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<Description>Commonly used models, enumerations and types.</Description>
|
||||
<AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>net45;net451;netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<TargetFrameworks>net45;net451;net461;netstandard1.0;netstandard1.3;netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<NoWarn>$(NoWarn);1591;8603</NoWarn>
|
||||
<AssemblyName>WireMock.Net.Abstractions</AssemblyName>
|
||||
@@ -41,11 +41,15 @@
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<!--<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
|
||||
<PackageReference Include="Nullable" Version="1.2.1">
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetFramework)' != 'netstandard1.0'">
|
||||
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
|
||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net46" Version="1.0.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>-->
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
|
||||
namespace WireMock.Client.Builders;
|
||||
|
||||
/// <summary>
|
||||
/// AdminApiMappingBuilder
|
||||
/// </summary>
|
||||
public class AdminApiMappingBuilder
|
||||
{
|
||||
private readonly List<Action<MappingModelBuilder>> _mappingModelBuilderActions = new();
|
||||
|
||||
private readonly IWireMockAdminApi _api;
|
||||
|
||||
/// <summary>
|
||||
/// AdminApiMappingBuilder
|
||||
/// </summary>
|
||||
/// <param name="api">The <see cref="IWireMockAdminApi"/>.</param>
|
||||
public AdminApiMappingBuilder(IWireMockAdminApi api)
|
||||
{
|
||||
_api = Guard.NotNull(api);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Given
|
||||
/// </summary>
|
||||
/// <param name="mappingModelBuilderAction">The action.</param>
|
||||
public void Given(Action<MappingModelBuilder> mappingModelBuilderAction)
|
||||
{
|
||||
_mappingModelBuilderActions.Add(Guard.NotNull(mappingModelBuilderAction));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build the mappings and post these using the <see cref="IWireMockAdminApi"/> to the WireMock.Net server.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional CancellationToken.</param>
|
||||
/// <returns><see cref="StatusModel"/></returns>
|
||||
public Task<StatusModel> BuildAndPostAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
var modelMappings = new List<MappingModel>();
|
||||
|
||||
foreach (var mappingModelBuilderAction in _mappingModelBuilderActions)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var mappingModelBuilder = new MappingModelBuilder();
|
||||
mappingModelBuilderAction(mappingModelBuilder);
|
||||
|
||||
modelMappings.Add(mappingModelBuilder.Build());
|
||||
}
|
||||
|
||||
return _api.PostMappingsAsync(modelMappings, cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Text;
|
||||
using JsonConverter.Abstractions;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using WireMock.Admin.Mappings;
|
||||
|
||||
namespace WireMock.Client.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// ResponseModelBuilder
|
||||
/// </summary>
|
||||
public static class ResponseModelBuilderExtensions
|
||||
{
|
||||
private static readonly Encoding Utf8NoBom = new UTF8Encoding(false);
|
||||
private static readonly IJsonConverter JsonConverter = new NewtonsoftJsonConverter();
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsJson
|
||||
/// </summary>
|
||||
/// <param name="builder">The ResponseModelBuilder.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <param name="indented">Define whether child objects to be indented.</param>
|
||||
public static ResponseModelBuilder WithBodyAsJson(this ResponseModelBuilder builder, object body, Encoding? encoding = null, bool? indented = null)
|
||||
{
|
||||
return builder.WithBodyAsBytes(() =>
|
||||
{
|
||||
var options = new JsonConverterOptions
|
||||
{
|
||||
WriteIndented = indented == true
|
||||
};
|
||||
var jsonBody = JsonConverter.Serialize(body, options);
|
||||
return (encoding ?? Utf8NoBom).GetBytes(jsonBody);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsJson
|
||||
/// </summary>
|
||||
/// <param name="builder">The ResponseModelBuilder.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="indented">Define whether child objects to be indented.</param>
|
||||
public static ResponseModelBuilder WithBodyAsJson(this ResponseModelBuilder builder, object body, bool indented)
|
||||
{
|
||||
return builder.WithBodyAsJson(body, null, indented);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using WireMock.Client.Builders;
|
||||
|
||||
namespace WireMock.Client.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Some extensions for <see cref="IWireMockAdminApi"/>.
|
||||
/// </summary>
|
||||
public static class WireMockAdminApiExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a new <see cref="AdminApiMappingBuilder"/> for the <see cref="IWireMockAdminApi"/>.
|
||||
/// </summary>
|
||||
/// <param name="api">See <see cref="IWireMockAdminApi"/>.</param>
|
||||
/// <returns></returns>
|
||||
public static AdminApiMappingBuilder GetMappingBuilder(this IWireMockAdminApi api)
|
||||
{
|
||||
return new AdminApiMappingBuilder(api);
|
||||
}
|
||||
}
|
||||
@@ -1,233 +1,283 @@
|
||||
using RestEase;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using RestEase;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Admin.Requests;
|
||||
using WireMock.Admin.Scenarios;
|
||||
using WireMock.Admin.Settings;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Client
|
||||
namespace WireMock.Client;
|
||||
|
||||
/// <summary>
|
||||
/// The RestEase interface which defines all admin commands.
|
||||
/// </summary>
|
||||
[BasePath("__admin")]
|
||||
public interface IWireMockAdminApi
|
||||
{
|
||||
/// <summary>
|
||||
/// The RestEase interface which defines all admin commands.
|
||||
/// Authentication header
|
||||
/// </summary>
|
||||
[BasePath("__admin")]
|
||||
public interface IWireMockAdminApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Authentication header
|
||||
/// </summary>
|
||||
[Header("Authorization")]
|
||||
AuthenticationHeaderValue Authorization { get; set; }
|
||||
[Header("Authorization")]
|
||||
AuthenticationHeaderValue Authorization { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Get the settings.
|
||||
/// </summary>
|
||||
/// <returns>SettingsModel</returns>
|
||||
[Get("settings")]
|
||||
Task<SettingsModel> GetSettingsAsync();
|
||||
/// <summary>
|
||||
/// Get the settings.
|
||||
/// </summary>
|
||||
/// <returns>SettingsModel</returns>
|
||||
[Get("settings")]
|
||||
Task<SettingsModel> GetSettingsAsync();
|
||||
|
||||
/// <summary>
|
||||
/// Update the settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">SettingsModel</param>
|
||||
[Put("settings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PutSettingsAsync([Body] SettingsModel settings);
|
||||
/// <summary>
|
||||
/// Update the settings.
|
||||
/// </summary>
|
||||
/// <param name="settings">SettingsModel</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("settings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PutSettingsAsync([Body] SettingsModel settings, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Update the settings
|
||||
/// </summary>
|
||||
/// <param name="settings">SettingsModel</param>
|
||||
[Post("settings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PostSettingsAsync([Body] SettingsModel settings);
|
||||
/// <summary>
|
||||
/// Update the settings
|
||||
/// </summary>
|
||||
/// <param name="settings">SettingsModel</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("settings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PostSettingsAsync([Body] SettingsModel settings, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the mappings.
|
||||
/// </summary>
|
||||
/// <returns>MappingModels</returns>
|
||||
[Get("mappings")]
|
||||
Task<IList<MappingModel>> GetMappingsAsync();
|
||||
/// <summary>
|
||||
/// Get the mappings.
|
||||
/// </summary>
|
||||
/// <returns>MappingModels</returns>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("mappings")]
|
||||
Task<IList<MappingModel>> GetMappingsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">MappingModel</param>
|
||||
[Post("mappings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PostMappingAsync([Body] MappingModel mapping);
|
||||
/// <summary>
|
||||
/// Get the C# code from all mappings
|
||||
/// </summary>
|
||||
/// <returns>C# code</returns>
|
||||
/// <param name="mappingConverterType">The <see cref="MappingConverterType"/>, default is Server.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("mappings/code")]
|
||||
Task<string> GetMappingsCodeAsync([Query] MappingConverterType mappingConverterType = MappingConverterType.Server, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Add new mappings.
|
||||
/// </summary>
|
||||
/// <param name="mappings">MappingModels</param>
|
||||
[Post("mappings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PostMappingsAsync([Body] IList<MappingModel> mappings);
|
||||
/// <summary>
|
||||
/// Add a new mapping.
|
||||
/// </summary>
|
||||
/// <param name="mapping">MappingModel</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("mappings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PostMappingAsync([Body] MappingModel mapping, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete all mappings.
|
||||
/// </summary>
|
||||
[Delete("mappings")]
|
||||
Task<StatusModel> DeleteMappingsAsync();
|
||||
/// <summary>
|
||||
/// Add new mappings.
|
||||
/// </summary>
|
||||
/// <param name="mappings">MappingModels</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("mappings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PostMappingsAsync([Body] IList<MappingModel> mappings, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete mappings according to GUIDs
|
||||
/// </summary>
|
||||
/// <param name="mappings">MappingModels</param>
|
||||
[Delete("mappings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> DeleteMappingsAsync([Body] IList<MappingModel> mappings);
|
||||
/// <summary>
|
||||
/// Delete all mappings.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("mappings")]
|
||||
Task<StatusModel> DeleteMappingsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all mappings.
|
||||
/// </summary>
|
||||
/// <param name="reloadStaticMappings">A value indicating whether to reload the static mappings after the reset.</param>
|
||||
[Post("mappings/reset")]
|
||||
Task<StatusModel> ResetMappingsAsync(bool? reloadStaticMappings = false);
|
||||
/// <summary>
|
||||
/// Delete mappings according to GUIDs
|
||||
/// </summary>
|
||||
/// <param name="mappings">MappingModels</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("mappings")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> DeleteMappingsAsync([Body] IList<MappingModel> mappings, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
[Get("mappings/{guid}")]
|
||||
Task<MappingModel> GetMappingAsync([Path] Guid guid);
|
||||
/// <summary>
|
||||
/// Delete (reset) all mappings.
|
||||
/// </summary>
|
||||
/// <param name="reloadStaticMappings">A value indicating whether to reload the static mappings after the reset.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("mappings/reset")]
|
||||
Task<StatusModel> ResetMappingsAsync(bool? reloadStaticMappings = false, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Update a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <param name="mapping">MappingModel</param>
|
||||
[Put("mappings/{guid}")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping);
|
||||
/// <summary>
|
||||
/// Get a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("mappings/{guid}")]
|
||||
Task<MappingModel> GetMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
[Delete("mappings/{guid}")]
|
||||
Task<StatusModel> DeleteMappingAsync([Path] Guid guid);
|
||||
/// <summary>
|
||||
/// Get the C# code from a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <param name="mappingConverterType">The optional mappingConverterType (can be Server or Builder)</param>
|
||||
/// <returns>C# code</returns>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("mappings/code/{guid}")]
|
||||
Task<string> GetMappingCodeAsync([Path] Guid guid, [Query] MappingConverterType mappingConverterType = MappingConverterType.Server, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Save the mappings
|
||||
/// </summary>
|
||||
[Post("mappings/save")]
|
||||
Task<StatusModel> SaveMappingAsync();
|
||||
/// <summary>
|
||||
/// Update a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <param name="mapping">MappingModel</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("mappings/{guid}")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<StatusModel> PutMappingAsync([Path] Guid guid, [Body] MappingModel mapping, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the requests.
|
||||
/// </summary>
|
||||
/// <returns>LogRequestModels</returns>
|
||||
[Get("requests")]
|
||||
Task<IList<LogEntryModel>> GetRequestsAsync();
|
||||
/// <summary>
|
||||
/// Delete a mapping based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("mappings/{guid}")]
|
||||
Task<StatusModel> DeleteMappingAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete all requests.
|
||||
/// </summary>
|
||||
[Delete("requests")]
|
||||
Task<StatusModel> DeleteRequestsAsync();
|
||||
/// <summary>
|
||||
/// Save the mappings
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("mappings/save")]
|
||||
Task<StatusModel> SaveMappingAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all requests.
|
||||
/// </summary>
|
||||
[Post("requests/reset")]
|
||||
Task<StatusModel> ResetRequestsAsync();
|
||||
/// <summary>
|
||||
/// Get the requests.
|
||||
/// </summary>
|
||||
/// <returns>LogRequestModels</returns>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("requests")]
|
||||
Task<IList<LogEntryModel>> GetRequestsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get a request based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
[Get("requests/{guid}")]
|
||||
Task<LogEntryModel> GetRequestAsync([Path] Guid guid);
|
||||
/// <summary>
|
||||
/// Delete all requests.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("requests")]
|
||||
Task<StatusModel> DeleteRequestsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete a request based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
[Delete("requests/{guid}")]
|
||||
Task<StatusModel> DeleteRequestAsync([Path] Guid guid);
|
||||
/// <summary>
|
||||
/// Delete (reset) all requests.
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("requests/reset")]
|
||||
Task<StatusModel> ResetRequestsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Find a request based on the criteria
|
||||
/// </summary>
|
||||
/// <param name="model">The RequestModel</param>
|
||||
[Post("requests/find")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<IList<LogEntryModel>> FindRequestsAsync([Body] RequestModel model);
|
||||
/// <summary>
|
||||
/// Get a request based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <returns>MappingModel</returns>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("requests/{guid}")]
|
||||
Task<LogEntryModel> GetRequestAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get all scenarios
|
||||
/// </summary>
|
||||
[Get("scenarios")]
|
||||
Task<IList<ScenarioStateModel>> GetScenariosAsync();
|
||||
/// <summary>
|
||||
/// Delete a request based on the guid
|
||||
/// </summary>
|
||||
/// <param name="guid">The Guid</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("requests/{guid}")]
|
||||
Task<StatusModel> DeleteRequestAsync([Path] Guid guid, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
[Delete("scenarios")]
|
||||
Task<StatusModel> DeleteScenariosAsync();
|
||||
/// <summary>
|
||||
/// Find a request based on the criteria
|
||||
/// </summary>
|
||||
/// <param name="model">The RequestModel</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("requests/find")]
|
||||
[Header("Content-Type", "application/json")]
|
||||
Task<IList<LogEntryModel>> FindRequestsAsync([Body] RequestModel model, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
[Post("scenarios")]
|
||||
Task<StatusModel> ResetScenariosAsync();
|
||||
/// <summary>
|
||||
/// Get all scenarios
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("scenarios")]
|
||||
Task<IList<ScenarioStateModel>> GetScenariosAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) a specific scenario
|
||||
/// </summary>
|
||||
[Delete("scenarios/{name}")]
|
||||
[AllowAnyStatusCode]
|
||||
Task<StatusModel> DeleteScenarioAsync([Path] string name);
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("scenarios")]
|
||||
Task<StatusModel> DeleteScenariosAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
[Post("scenarios/{name}/reset")]
|
||||
[AllowAnyStatusCode]
|
||||
Task<StatusModel> ResetScenarioAsync([Path] string name);
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("scenarios")]
|
||||
Task<StatusModel> ResetScenariosAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Create a new File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="body">The body</param>
|
||||
[Post("files/{filename}")]
|
||||
Task<StatusModel> PostFileAsync([Path] string filename, [Body] string body);
|
||||
/// <summary>
|
||||
/// Delete (reset) a specific scenario
|
||||
/// </summary>
|
||||
/// <param name="name">Scenario name.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("scenarios/{name}")]
|
||||
[AllowAnyStatusCode]
|
||||
Task<StatusModel> DeleteScenarioAsync([Path] string name, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Update an existing File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="body">The body</param>
|
||||
[Put("files/{filename}")]
|
||||
Task<StatusModel> PutFileAsync([Path] string filename, [Body] string body);
|
||||
/// <summary>
|
||||
/// Delete (reset) all scenarios
|
||||
/// </summary>
|
||||
/// <param name="name">Scenario name.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("scenarios/{name}/reset")]
|
||||
[AllowAnyStatusCode]
|
||||
Task<StatusModel> ResetScenarioAsync([Path] string name, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Get the content of an existing File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
[Get("files/{filename}")]
|
||||
Task<string> GetFileAsync([Path] string filename);
|
||||
/// <summary>
|
||||
/// Create a new File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="body">The body</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("files/{filename}")]
|
||||
Task<StatusModel> PostFileAsync([Path] string filename, [Body] string body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an existing File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
[Delete("files/{filename}")]
|
||||
Task<StatusModel> DeleteFileAsync([Path] string filename);
|
||||
/// <summary>
|
||||
/// Update an existing File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="body">The body</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Put("files/{filename}")]
|
||||
Task<StatusModel> PutFileAsync([Path] string filename, [Body] string body, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a file exists
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
[Head("files/{filename}")]
|
||||
Task FileExistsAsync([Path] string filename);
|
||||
}
|
||||
/// <summary>
|
||||
/// Get the content of an existing File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Get("files/{filename}")]
|
||||
Task<string> GetFileAsync([Path] string filename, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Delete an existing File
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Delete("files/{filename}")]
|
||||
Task<StatusModel> DeleteFileAsync([Path] string filename, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a file exists
|
||||
/// </summary>
|
||||
/// <param name="filename">The filename</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Head("files/{filename}")]
|
||||
Task FileExistsAsync([Path] string filename, CancellationToken cancellationToken = default);
|
||||
}
|
||||
@@ -30,8 +30,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.3.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||
<PackageReference Include="RestEase" Version="1.5.7" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -7,4 +7,5 @@ internal static class WireMockConstants
|
||||
public const int ProxyPriority = -2_000_000;
|
||||
|
||||
public const string ContentTypeJson = "application/json";
|
||||
public const string ContentTypeTextPlain = "text/plain";
|
||||
}
|
||||
20
src/WireMock.Net/Extensions/EnumExtensions.cs
Normal file
20
src/WireMock.Net/Extensions/EnumExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
namespace WireMock.Extensions;
|
||||
|
||||
internal static class EnumExtensions
|
||||
{
|
||||
public static string GetFullyQualifiedEnumValue<T>(this T enumValue)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
var type = typeof(T);
|
||||
|
||||
if (!type.GetTypeInfo().IsEnum)
|
||||
{
|
||||
throw new ArgumentException("T must be an enum");
|
||||
}
|
||||
|
||||
return $"{type.Namespace}.{type.Name}.{enumValue}";
|
||||
}
|
||||
}
|
||||
@@ -35,9 +35,14 @@ internal static class HttpClientBuilder
|
||||
{
|
||||
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
|
||||
|
||||
var x509Certificate2 = CertificateLoader.LoadCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName);
|
||||
var x509Certificate2 = CertificateLoader.LoadCertificate(settings.ClientX509Certificate2ThumbprintOrSubjectName!);
|
||||
handler.ClientCertificates.Add(x509Certificate2);
|
||||
}
|
||||
else if (settings.Certificate != null)
|
||||
{
|
||||
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
|
||||
handler.ClientCertificates.Add(settings.Certificate);
|
||||
}
|
||||
|
||||
handler.AllowAutoRedirect = settings.AllowAutoRedirect == true;
|
||||
|
||||
|
||||
@@ -51,14 +51,14 @@ internal class WebhookSender
|
||||
switch (webhookRequest.TransformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
var factoryHandlebars = new HandlebarsContextFactory(_settings.FileSystemHandler, _settings.HandlebarsRegistrationCallback);
|
||||
transformer = new Transformer(factoryHandlebars);
|
||||
var factoryHandlebars = new HandlebarsContextFactory(_settings);
|
||||
transformer = new Transformer(_settings, factoryHandlebars);
|
||||
break;
|
||||
|
||||
case TransformerType.Scriban:
|
||||
case TransformerType.ScribanDotLiquid:
|
||||
var factoryDotLiquid = new ScribanContextFactory(_settings.FileSystemHandler, webhookRequest.TransformerType);
|
||||
transformer = new Transformer(factoryDotLiquid);
|
||||
transformer = new Transformer(_settings, factoryDotLiquid);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -17,6 +18,11 @@ public interface IMapping
|
||||
/// </summary>
|
||||
Guid Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The datetime when this mapping was created or updated.
|
||||
/// </summary>
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the TimeSettings (Start, End and TTL).
|
||||
/// </summary>
|
||||
@@ -115,7 +121,16 @@ public interface IMapping
|
||||
/// <summary>
|
||||
/// Use Fire and Forget for the defined webhook(s). [Optional]
|
||||
/// </summary>
|
||||
public bool? UseWebhooksFireAndForget { get; set; }
|
||||
bool? UseWebhooksFireAndForget { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ProvideResponseAsync
|
||||
|
||||
60
src/WireMock.Net/IMappingBuilder.cs
Normal file
60
src/WireMock.Net/IMappingBuilder.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Server;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
/// <summary>
|
||||
/// IMappingBuilder
|
||||
/// </summary>
|
||||
public interface IMappingBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The given.
|
||||
/// </summary>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
|
||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||
IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false);
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the mappings as a list.
|
||||
/// </summary>
|
||||
/// <returns>A list from <see cref="MappingModel"/>s.</returns>
|
||||
MappingModel[] GetMappings();
|
||||
|
||||
/// <summary>
|
||||
/// Convert all mappings to JSON.
|
||||
/// </summary>
|
||||
/// <returns>JSON</returns>
|
||||
string ToJson();
|
||||
|
||||
/// <summary>
|
||||
/// Save all mappings as a single JSON to a file.
|
||||
/// </summary>
|
||||
/// <param name="path">The file to write to.</param>
|
||||
void SaveMappingsToFile(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Save all mappings as multiple JSON files (each file is 1 mapping).
|
||||
/// </summary>
|
||||
/// <param name="folder">The folder to write the files to.</param>
|
||||
void SaveMappingsToFolder(string folder);
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code for a mapping.
|
||||
/// </summary>
|
||||
/// <param name="guid">The Mapping Guid.</param>
|
||||
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
|
||||
/// <returns>C# code (null in case the mapping is not found)</returns>
|
||||
string? ToCSharpCode(Guid guid, MappingConverterType converterType);
|
||||
|
||||
/// <summary>
|
||||
/// Get the C# code for all mappings.
|
||||
/// </summary>
|
||||
/// <param name="converterType">The <see cref="MappingConverterType"/></param>
|
||||
/// <returns>C# code</returns>
|
||||
public string ToCSharpCode(MappingConverterType converterType);
|
||||
}
|
||||
10
src/WireMock.Net/Json/DynamicJsonClassOptions.cs
Normal file
10
src/WireMock.Net/Json/DynamicJsonClassOptions.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal class DynamicJsonClassOptions
|
||||
{
|
||||
public IntegerBehavior IntegerConvertBehavior { get; set; } = IntegerBehavior.UseLong;
|
||||
|
||||
public FloatBehavior FloatConvertBehavior { get; set; } = FloatBehavior.UseDouble;
|
||||
}
|
||||
15
src/WireMock.Net/Json/DynamicPropertyWithValue.cs
Normal file
15
src/WireMock.Net/Json/DynamicPropertyWithValue.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
using System.Linq.Dynamic.Core;
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
public class DynamicPropertyWithValue : DynamicProperty
|
||||
{
|
||||
public object? Value { get; }
|
||||
|
||||
public DynamicPropertyWithValue(string name, object? value) : base(name, value?.GetType() ?? typeof(object))
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
24
src/WireMock.Net/Json/FloatBehavior.cs
Normal file
24
src/WireMock.Net/Json/FloatBehavior.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Float in the Json Object.
|
||||
/// </summary>
|
||||
internal enum FloatBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a double. (default)
|
||||
/// </summary>
|
||||
UseDouble = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a float (unless overflow).
|
||||
/// </summary>
|
||||
UseFloat = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Float types in the Json Object to a decimal (unless overflow).
|
||||
/// </summary>
|
||||
UseDecimal = 2
|
||||
}
|
||||
20
src/WireMock.Net/Json/IntegerBehavior.cs
Normal file
20
src/WireMock.Net/Json/IntegerBehavior.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
/// <summary>
|
||||
/// Enum to define how to convert an Integer in the Json Object.
|
||||
/// </summary>
|
||||
internal enum IntegerBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a int (unless overflow).
|
||||
/// (default)
|
||||
/// </summary>
|
||||
UseInt = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Convert all Integer types in the Json Object to a long.
|
||||
/// </summary>
|
||||
UseLong = 1
|
||||
}
|
||||
202
src/WireMock.Net/Json/JObjectExtensions.cs
Normal file
202
src/WireMock.Net/Json/JObjectExtensions.cs
Normal file
@@ -0,0 +1,202 @@
|
||||
// Copied from https://github.com/Handlebars-Net/Handlebars.Net.Helpers/blob/master/src/Handlebars.Net.Helpers.DynamicLinq
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Dynamic.Core;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace WireMock.Json;
|
||||
|
||||
internal static class JObjectExtensions
|
||||
{
|
||||
private class JTokenResolvers : Dictionary<JTokenType, Func<JToken, DynamicJsonClassOptions?, object?>>
|
||||
{
|
||||
}
|
||||
|
||||
private static readonly JTokenResolvers Resolvers = new()
|
||||
{
|
||||
{ JTokenType.Array, ConvertJTokenArray },
|
||||
{ JTokenType.Boolean, (jToken, _) => jToken.Value<bool>() },
|
||||
{ JTokenType.Bytes, (jToken, _) => jToken.Value<byte[]>() },
|
||||
{ JTokenType.Date, (jToken, _) => jToken.Value<DateTime>() },
|
||||
{ JTokenType.Float, ConvertJTokenFloat },
|
||||
{ JTokenType.Guid, (jToken, _) => jToken.Value<Guid>() },
|
||||
{ JTokenType.Integer, ConvertJTokenInteger },
|
||||
{ JTokenType.None, (_, _) => null },
|
||||
{ JTokenType.Null, (_, _) => null },
|
||||
{ JTokenType.Object, ConvertJObject },
|
||||
{ JTokenType.Property, ConvertJTokenProperty },
|
||||
{ JTokenType.String, (jToken, _) => jToken.Value<string>() },
|
||||
{ JTokenType.TimeSpan, (jToken, _) => jToken.Value<TimeSpan>() },
|
||||
{ JTokenType.Undefined, (_, _) => null },
|
||||
{ JTokenType.Uri, (o, _) => o.Value<Uri>() },
|
||||
};
|
||||
|
||||
internal static DynamicClass? ToDynamicJsonClass(this JObject? src, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (src == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dynamicPropertyWithValues = new List<DynamicPropertyWithValue>();
|
||||
|
||||
foreach (var prop in src.Properties())
|
||||
{
|
||||
var value = Resolvers[prop.Type](prop.Value, options);
|
||||
if (value != null)
|
||||
{
|
||||
dynamicPropertyWithValues.Add(new DynamicPropertyWithValue(prop.Name, value));
|
||||
}
|
||||
}
|
||||
|
||||
return CreateInstance(dynamicPropertyWithValues);
|
||||
}
|
||||
|
||||
internal static IEnumerable ToDynamicClassArray(this JArray? src, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (src == null)
|
||||
{
|
||||
return new object?[0];
|
||||
}
|
||||
|
||||
return ConvertJTokenArray(src, options);
|
||||
}
|
||||
|
||||
private static object? ConvertJObject(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg is JObject asJObject)
|
||||
{
|
||||
return asJObject.ToDynamicJsonClass(options);
|
||||
}
|
||||
|
||||
return GetResolverFor(arg)(arg, options);
|
||||
}
|
||||
|
||||
private static object PassThrough(JToken arg, DynamicJsonClassOptions? options)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static Func<JToken, DynamicJsonClassOptions?, object?> GetResolverFor(JToken arg)
|
||||
{
|
||||
return Resolvers.TryGetValue(arg.Type, out var result) ? result : PassThrough;
|
||||
}
|
||||
|
||||
private static object ConvertJTokenFloat(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg.Type != JTokenType.Float)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to double or float.");
|
||||
}
|
||||
|
||||
if (options?.FloatConvertBehavior == FloatBehavior.UseFloat)
|
||||
{
|
||||
try
|
||||
{
|
||||
return arg.Value<float>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arg.Value<double>();
|
||||
}
|
||||
}
|
||||
|
||||
if (options?.FloatConvertBehavior == FloatBehavior.UseDecimal)
|
||||
{
|
||||
try
|
||||
{
|
||||
return arg.Value<decimal>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return arg.Value<double>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return arg.Value<double>();
|
||||
}
|
||||
|
||||
private static object ConvertJTokenInteger(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg.Type != JTokenType.Integer)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to long or int.");
|
||||
}
|
||||
|
||||
var longValue = arg.Value<long>();
|
||||
|
||||
if (options is null || options.IntegerConvertBehavior == IntegerBehavior.UseInt)
|
||||
{
|
||||
if (longValue is >= int.MinValue and <= int.MaxValue)
|
||||
{
|
||||
return Convert.ToInt32(longValue);
|
||||
}
|
||||
}
|
||||
|
||||
return longValue;
|
||||
}
|
||||
|
||||
private static object? ConvertJTokenProperty(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
var resolver = GetResolverFor(arg);
|
||||
if (resolver is null)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to handle {nameof(JToken)} of type: {arg.Type}.");
|
||||
}
|
||||
|
||||
return resolver(arg, options);
|
||||
}
|
||||
|
||||
private static IEnumerable ConvertJTokenArray(JToken arg, DynamicJsonClassOptions? options = null)
|
||||
{
|
||||
if (arg is not JArray array)
|
||||
{
|
||||
throw new InvalidOperationException($"Unable to convert {nameof(JToken)} of type: {arg.Type} to {nameof(JArray)}.");
|
||||
}
|
||||
|
||||
var result = new List<object?>();
|
||||
foreach (var item in array)
|
||||
{
|
||||
result.Add(ConvertJObject(item));
|
||||
}
|
||||
|
||||
var distinctType = FindSameTypeOf(result);
|
||||
return distinctType == null ? result.ToArray() : ConvertToTypedArray(result, distinctType);
|
||||
}
|
||||
|
||||
private static Type? FindSameTypeOf(IEnumerable<object?> src)
|
||||
{
|
||||
var types = src.Select(o => o?.GetType()).Distinct().OfType<Type>().ToArray();
|
||||
return types.Length == 1 ? types[0] : null;
|
||||
}
|
||||
|
||||
private static IEnumerable ConvertToTypedArray(IEnumerable<object?> src, Type newType)
|
||||
{
|
||||
var method = ConvertToTypedArrayGenericMethod.MakeGenericMethod(newType);
|
||||
return (IEnumerable)method.Invoke(null, new object[] { src })!;
|
||||
}
|
||||
|
||||
private static readonly MethodInfo ConvertToTypedArrayGenericMethod = typeof(JObjectExtensions).GetMethod(nameof(ConvertToTypedArrayGeneric), BindingFlags.NonPublic | BindingFlags.Static)!;
|
||||
|
||||
private static T[] ConvertToTypedArrayGeneric<T>(IEnumerable<object> src)
|
||||
{
|
||||
return src.Cast<T>().ToArray();
|
||||
}
|
||||
|
||||
public static DynamicClass CreateInstance(IList<DynamicPropertyWithValue> dynamicPropertiesWithValue, bool createParameterCtor = true)
|
||||
{
|
||||
var type = DynamicClassFactory.CreateType(dynamicPropertiesWithValue.Cast<DynamicProperty>().ToArray(), createParameterCtor);
|
||||
var dynamicClass = (DynamicClass)Activator.CreateInstance(type);
|
||||
foreach (var dynamicPropertyWithValue in dynamicPropertiesWithValue.Where(p => p.Value != null))
|
||||
{
|
||||
dynamicClass.SetDynamicPropertyValue(dynamicPropertyWithValue.Name, dynamicPropertyWithValue.Value!);
|
||||
}
|
||||
|
||||
return dynamicClass;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@ public class Mapping : IMapping
|
||||
/// <inheritdoc />
|
||||
public Guid Guid { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? Title { get; }
|
||||
|
||||
@@ -69,10 +72,14 @@ public class Mapping : IMapping
|
||||
/// <inheritdoc />
|
||||
public ITimeSettings? TimeSettings { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public object? Data { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Mapping"/> class.
|
||||
/// </summary>
|
||||
/// <param name="guid">The unique identifier.</param>
|
||||
/// <param name="updatedAt">The datetime when this mapping was created.</param>
|
||||
/// <param name="title">The unique title (can be null).</param>
|
||||
/// <param name="description">The description (can be null).</param>
|
||||
/// <param name="path">The full file path from this mapping title (can be null).</param>
|
||||
@@ -87,8 +94,10 @@ public class Mapping : IMapping
|
||||
/// <param name="webhooks">The Webhooks. [Optional]</param>
|
||||
/// <param name="useWebhooksFireAndForget">Use Fire and Forget for the defined webhook(s). [Optional]</param>
|
||||
/// <param name="timeSettings">The TimeSettings. [Optional]</param>
|
||||
/// <param name="data">The data object. [Optional]</param>
|
||||
public Mapping(
|
||||
Guid guid,
|
||||
DateTime updatedAt,
|
||||
string? title,
|
||||
string? description,
|
||||
string? path,
|
||||
@@ -102,9 +111,11 @@ public class Mapping : IMapping
|
||||
int? stateTimes,
|
||||
IWebhook[]? webhooks,
|
||||
bool? useWebhooksFireAndForget,
|
||||
ITimeSettings? timeSettings)
|
||||
ITimeSettings? timeSettings,
|
||||
object? data)
|
||||
{
|
||||
Guid = guid;
|
||||
UpdatedAt = updatedAt;
|
||||
Title = title;
|
||||
Description = description;
|
||||
Path = path;
|
||||
@@ -119,6 +130,7 @@ public class Mapping : IMapping
|
||||
Webhooks = webhooks;
|
||||
UseWebhooksFireAndForget = useWebhooksFireAndForget;
|
||||
TimeSettings = timeSettings;
|
||||
Data = data;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IMapping.ProvideResponseAsync" />
|
||||
|
||||
160
src/WireMock.Net/MappingBuilder.cs
Normal file
160
src/WireMock.Net/MappingBuilder.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Owin;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Server;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock;
|
||||
|
||||
/// <summary>
|
||||
/// MappingBuilder
|
||||
/// </summary>
|
||||
public class MappingBuilder : IMappingBuilder
|
||||
{
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly MappingConverter _mappingConverter;
|
||||
private readonly MappingToFileSaver _mappingToFileSaver;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
|
||||
/// <summary>
|
||||
/// Create a MappingBuilder
|
||||
/// </summary>
|
||||
/// <param name="settings">The optional <see cref="WireMockServerSettings"/>.</param>
|
||||
public MappingBuilder(WireMockServerSettings? settings = null)
|
||||
{
|
||||
_settings = settings ?? new WireMockServerSettings();
|
||||
_options = WireMockMiddlewareOptionsHelper.InitFromSettings(_settings);
|
||||
|
||||
var matcherMapper = new MatcherMapper(_settings);
|
||||
_mappingConverter = new MappingConverter(matcherMapper);
|
||||
_mappingToFileSaver = new MappingToFileSaver(_settings, _mappingConverter);
|
||||
|
||||
_guidUtils = new GuidUtils();
|
||||
_dateTimeUtils = new DateTimeUtils();
|
||||
}
|
||||
|
||||
internal MappingBuilder(
|
||||
WireMockServerSettings settings,
|
||||
IWireMockMiddlewareOptions options,
|
||||
MappingConverter mappingConverter,
|
||||
MappingToFileSaver mappingToFileSaver,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils
|
||||
)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
_options = Guard.NotNull(options);
|
||||
_mappingConverter = Guard.NotNull(mappingConverter);
|
||||
_mappingToFileSaver = Guard.NotNull(mappingToFileSaver);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, Guard.NotNull(requestMatcher), _settings, _guidUtils, _dateTimeUtils, saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public MappingModel[] GetMappings()
|
||||
{
|
||||
return GetMappingsInternal().Select(_mappingConverter.ToMappingModel).ToArray();
|
||||
}
|
||||
|
||||
internal IMapping[] GetMappingsInternal()
|
||||
{
|
||||
return _options.Mappings.Values.ToArray().Where(m => !m.IsAdminInterface).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ToJson()
|
||||
{
|
||||
return ToJson(GetMappings());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string? ToCSharpCode(Guid guid, MappingConverterType converterType)
|
||||
{
|
||||
var mapping = GetMappingsInternal().FirstOrDefault(m => m.Guid == guid);
|
||||
if (mapping is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var settings = new MappingConverterSettings { AddStart = true, ConverterType = converterType };
|
||||
return _mappingConverter.ToCSharpCode(mapping, settings);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ToCSharpCode(MappingConverterType converterType)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
bool addStart = true;
|
||||
foreach (var mapping in GetMappingsInternal())
|
||||
{
|
||||
sb.AppendLine(_mappingConverter.ToCSharpCode(mapping, new MappingConverterSettings { AddStart = addStart, ConverterType = converterType }));
|
||||
|
||||
if (addStart)
|
||||
{
|
||||
addStart = false;
|
||||
}
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveMappingsToFile(string path)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingsToFile(GetNonAdminMappings(), path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SaveMappingsToFolder(string? folder)
|
||||
{
|
||||
foreach (var mapping in GetNonAdminMappings().Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
||||
}
|
||||
}
|
||||
|
||||
private IMapping[] GetNonAdminMappings()
|
||||
{
|
||||
return _options.Mappings.Values.ToArray();
|
||||
}
|
||||
|
||||
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
||||
{
|
||||
// Check a mapping exists with the same Guid. If so, update the datetime and replace it.
|
||||
if (_options.Mappings.ContainsKey(mapping.Guid))
|
||||
{
|
||||
mapping.UpdatedAt = _dateTimeUtils.UtcNow;
|
||||
_options.Mappings[mapping.Guid] = mapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
_options.Mappings.TryAdd(mapping.Guid, mapping);
|
||||
}
|
||||
|
||||
if (saveToFile)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
private static string ToJson(object value)
|
||||
{
|
||||
return JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,8 @@ using AnyOfTypes;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Json;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -100,38 +100,55 @@ public class LinqMatcher : IObjectMatcher, IStringMatcher
|
||||
{
|
||||
double match = MatchScores.Mismatch;
|
||||
|
||||
JObject value;
|
||||
switch (input)
|
||||
JArray jArray;
|
||||
try
|
||||
{
|
||||
case JObject valueAsJObject:
|
||||
value = valueAsJObject;
|
||||
break;
|
||||
|
||||
case { } valueAsObject:
|
||||
value = JObject.FromObject(valueAsObject);
|
||||
break;
|
||||
|
||||
default:
|
||||
return MatchScores.Mismatch;
|
||||
jArray = new JArray { input };
|
||||
}
|
||||
catch
|
||||
{
|
||||
jArray = new JArray { JToken.FromObject(input) };
|
||||
}
|
||||
|
||||
//enumerable = jArray.ToDynamicClassArray();
|
||||
|
||||
//JObject value;
|
||||
//switch (input)
|
||||
//{
|
||||
// case JObject valueAsJObject:
|
||||
// value = valueAsJObject;
|
||||
// break;
|
||||
|
||||
// case { } valueAsObject:
|
||||
// value = JObject.FromObject(valueAsObject);
|
||||
// break;
|
||||
|
||||
// default:
|
||||
// return MatchScores.Mismatch;
|
||||
//}
|
||||
|
||||
// Convert a single object to a Queryable JObject-list with 1 entry.
|
||||
var queryable1 = new[] { value }.AsQueryable();
|
||||
//var queryable1 = new[] { value }.AsQueryable();
|
||||
var queryable = jArray.ToDynamicClassArray().AsQueryable();
|
||||
|
||||
try
|
||||
{
|
||||
// Generate the DynamicLinq select statement.
|
||||
string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
|
||||
//string dynamicSelect = JsonUtils.GenerateDynamicLinqStatement(value);
|
||||
|
||||
// Execute DynamicLinq Select statement.
|
||||
var queryable2 = queryable1.Select(dynamicSelect);
|
||||
//var queryable2 = queryable1.Select(dynamicSelect);
|
||||
|
||||
// Use the Any(...) method to check if the result matches.
|
||||
match = MatchScores.ToScore(_patterns.Select(pattern => queryable2.Any(pattern)).ToArray(), MatchOperator);
|
||||
|
||||
var patternsAsStringArray = _patterns.Select(p => p.GetPattern()).ToArray();
|
||||
var scores = patternsAsStringArray.Select(p => queryable.Any(p)).ToArray();
|
||||
|
||||
match = MatchScores.ToScore(_patterns.Select(pattern => queryable.Any(pattern.GetPattern())).ToArray(), MatchOperator);
|
||||
|
||||
return MatchBehaviourHelper.Convert(MatchBehaviour, match);
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
if (ThrowException)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -33,6 +32,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
/// </summary>
|
||||
public Func<IBodyData?, bool>? BodyDataFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The body data function for FormUrlEncoded
|
||||
/// </summary>
|
||||
public Func<IDictionary<string, string>?, bool>? FormUrlEncodedFunc { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
/// </summary>
|
||||
@@ -109,6 +113,15 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
BodyDataFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
public RequestMessageBodyMatcher(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
FormUrlEncodedFunc = Guard.NotNull(func);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||
/// </summary>
|
||||
@@ -184,7 +197,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
if (matcher is IStringMatcher stringMatcher)
|
||||
{
|
||||
// If the body is a Json or a String, use the BodyAsString to match on.
|
||||
if (requestMessage?.BodyData?.DetectedBodyType == BodyType.Json || requestMessage?.BodyData?.DetectedBodyType == BodyType.String)
|
||||
if (requestMessage?.BodyData?.DetectedBodyType is BodyType.Json or BodyType.String)
|
||||
{
|
||||
return stringMatcher.IsMatch(requestMessage.BodyData.BodyAsString);
|
||||
}
|
||||
@@ -206,6 +219,11 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
||||
return MatchScores.ToScore(Func(requestMessage.BodyData?.BodyAsString));
|
||||
}
|
||||
|
||||
if (FormUrlEncodedFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(FormUrlEncodedFunc(requestMessage.BodyData?.BodyAsFormUrlEncoded));
|
||||
}
|
||||
|
||||
if (JsonFunc != null)
|
||||
{
|
||||
return MatchScores.ToScore(JsonFunc(requestMessage.BodyData?.BodyAsJson));
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using WireMock.Types;
|
||||
|
||||
@@ -14,6 +15,9 @@ public class BodyData : IBodyData
|
||||
/// <inheritdoc />
|
||||
public string? BodyAsString { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, string>? BodyAsFormUrlEncoded { get; set; }
|
||||
|
||||
/// <inheritdoc cref="IBodyData.BodyAsJson" />
|
||||
public object? BodyAsJson { get; set; }
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WireMock.HttpsCertificate;
|
||||
using CertificateLoader = WireMock.HttpsCertificate.CertificateLoader;
|
||||
|
||||
namespace WireMock.Owin
|
||||
{
|
||||
@@ -26,21 +27,25 @@ namespace WireMock.Owin
|
||||
{
|
||||
kestrelOptions.ListenAnyIP(urlDetail.Port, listenOptions =>
|
||||
{
|
||||
if (wireMockMiddlewareOptions.CustomCertificateDefined)
|
||||
listenOptions.UseHttps(options =>
|
||||
{
|
||||
listenOptions.UseHttps(CertificateLoader.LoadCertificate(
|
||||
wireMockMiddlewareOptions.X509StoreName,
|
||||
wireMockMiddlewareOptions.X509StoreLocation,
|
||||
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
|
||||
wireMockMiddlewareOptions.X509CertificateFilePath,
|
||||
wireMockMiddlewareOptions.X509CertificatePassword,
|
||||
urlDetail.Host)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
listenOptions.UseHttps();
|
||||
}
|
||||
if (wireMockMiddlewareOptions.CustomCertificateDefined)
|
||||
{
|
||||
options.ServerCertificate = CertificateLoader.LoadCertificate(
|
||||
wireMockMiddlewareOptions.X509StoreName,
|
||||
wireMockMiddlewareOptions.X509StoreLocation,
|
||||
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
|
||||
wireMockMiddlewareOptions.X509CertificateFilePath,
|
||||
wireMockMiddlewareOptions.X509CertificatePassword,
|
||||
urlDetail.Host);
|
||||
}
|
||||
|
||||
options.ClientCertificateMode = (ClientCertificateMode) wireMockMiddlewareOptions.ClientCertificateMode;
|
||||
if (wireMockMiddlewareOptions.AcceptAnyClientCertificate)
|
||||
{
|
||||
options.ClientCertificateValidation = (_, _, _) => true;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WireMock.HttpsCertificate;
|
||||
@@ -23,21 +24,22 @@ internal partial class AspNetCoreSelfHost
|
||||
{
|
||||
if (urlDetail.IsHttps)
|
||||
{
|
||||
if (wireMockMiddlewareOptions.CustomCertificateDefined)
|
||||
options.UseHttps(new HttpsConnectionFilterOptions
|
||||
{
|
||||
options.UseHttps(CertificateLoader.LoadCertificate(
|
||||
wireMockMiddlewareOptions.X509StoreName,
|
||||
wireMockMiddlewareOptions.X509StoreLocation,
|
||||
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
|
||||
wireMockMiddlewareOptions.X509CertificateFilePath,
|
||||
wireMockMiddlewareOptions.X509CertificatePassword,
|
||||
urlDetail.Host)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
options.UseHttps(PublicCertificateHelper.GetX509Certificate2());
|
||||
}
|
||||
ServerCertificate = wireMockMiddlewareOptions.CustomCertificateDefined
|
||||
? CertificateLoader.LoadCertificate(
|
||||
wireMockMiddlewareOptions.X509StoreName,
|
||||
wireMockMiddlewareOptions.X509StoreLocation,
|
||||
wireMockMiddlewareOptions.X509ThumbprintOrSubjectName,
|
||||
wireMockMiddlewareOptions.X509CertificateFilePath,
|
||||
wireMockMiddlewareOptions.X509CertificatePassword,
|
||||
urlDetail.Host)
|
||||
: PublicCertificateHelper.GetX509Certificate2(),
|
||||
ClientCertificateMode = (ClientCertificateMode) wireMockMiddlewareOptions.ClientCertificateMode,
|
||||
ClientCertificateValidation = wireMockMiddlewareOptions.AcceptAnyClientCertificate
|
||||
? (_, _, _) => true
|
||||
: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,10 @@ internal interface IWireMockMiddlewareOptions
|
||||
Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
|
||||
|
||||
CorsPolicyOptions? CorsPolicyOptions { get; set; }
|
||||
|
||||
ClientCertificateMode ClientCertificateMode { get; set; }
|
||||
|
||||
bool AcceptAnyClientCertificate { get; set; }
|
||||
#endif
|
||||
|
||||
IFileSystemHandler? FileSystemHandler { get; set; }
|
||||
|
||||
@@ -68,7 +68,21 @@ namespace WireMock.Owin.Mappers
|
||||
body = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return new RequestMessage(options, urlDetails, method, clientIP, body, headers, cookies) { DateTime = DateTime.UtcNow };
|
||||
return new RequestMessage(
|
||||
options,
|
||||
urlDetails,
|
||||
method,
|
||||
clientIP,
|
||||
body,
|
||||
headers,
|
||||
cookies
|
||||
#if USE_ASPNETCORE
|
||||
, await request.HttpContext.Connection.GetClientCertificateAsync()
|
||||
#endif
|
||||
)
|
||||
{
|
||||
DateTime = DateTime.UtcNow
|
||||
};
|
||||
}
|
||||
|
||||
private static (UrlDetails UrlDetails, string ClientIP) ParseRequest(IRequest request)
|
||||
|
||||
@@ -12,13 +12,13 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
public MappingMatcher(IWireMockMiddlewareOptions options)
|
||||
{
|
||||
Guard.NotNull(options, nameof(options));
|
||||
|
||||
_options = options;
|
||||
_options = Guard.NotNull(options);
|
||||
}
|
||||
|
||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||
{
|
||||
Guard.NotNull(request);
|
||||
|
||||
var possibleMappings = new List<MappingMatcherResult>();
|
||||
|
||||
foreach (var mapping in _options.Mappings.Values.Where(m => m.TimeSettings.IsValid()))
|
||||
@@ -41,8 +41,7 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var partialMappings = possibleMappings
|
||||
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.RequestMatchResult)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.OrderBy(m => m.RequestMatchResult).ThenBy(m => m.Mapping.Priority).ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||
.ToList();
|
||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
||||
|
||||
@@ -53,7 +52,7 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var match = possibleMappings
|
||||
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
||||
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult)
|
||||
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult).ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||
.FirstOrDefault();
|
||||
|
||||
return (match, partialMatch);
|
||||
|
||||
@@ -42,6 +42,11 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
|
||||
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
|
||||
|
||||
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
|
||||
|
||||
public ClientCertificateMode ClientCertificateMode { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool AcceptAnyClientCertificate { get; set; }
|
||||
#endif
|
||||
|
||||
/// <inheritdoc cref="IWireMockMiddlewareOptions.FileSystemHandler"/>
|
||||
|
||||
35
src/WireMock.Net/Owin/WireMockMiddlewareOptionsHelper.cs
Normal file
35
src/WireMock.Net/Owin/WireMockMiddlewareOptionsHelper.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Stef.Validation;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal static class WireMockMiddlewareOptionsHelper
|
||||
{
|
||||
public static IWireMockMiddlewareOptions InitFromSettings(WireMockServerSettings settings, IWireMockMiddlewareOptions? options = null)
|
||||
{
|
||||
Guard.NotNull(settings);
|
||||
|
||||
options ??= new WireMockMiddlewareOptions();
|
||||
|
||||
options.FileSystemHandler = settings.FileSystemHandler;
|
||||
options.PreWireMockMiddlewareInit = settings.PreWireMockMiddlewareInit;
|
||||
options.PostWireMockMiddlewareInit = settings.PostWireMockMiddlewareInit;
|
||||
options.Logger = settings.Logger;
|
||||
options.DisableJsonBodyParsing = settings.DisableJsonBodyParsing;
|
||||
options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
|
||||
options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
||||
options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||
options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
|
||||
if (settings.CustomCertificateDefined)
|
||||
{
|
||||
options.X509StoreName = settings.CertificateSettings!.X509StoreName;
|
||||
options.X509StoreLocation = settings.CertificateSettings.X509StoreLocation;
|
||||
options.X509ThumbprintOrSubjectName = settings.CertificateSettings.X509StoreThumbprintOrSubjectName;
|
||||
options.X509CertificateFilePath = settings.CertificateSettings.X509CertificateFilePath;
|
||||
options.X509CertificatePassword = settings.CertificateSettings.X509CertificatePassword;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Stef.Validation;
|
||||
using WireMock.Http;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Util;
|
||||
@@ -17,7 +19,7 @@ internal class ProxyHelper
|
||||
public ProxyHelper(WireMockServerSettings settings)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
_proxyMappingConverter = new ProxyMappingConverter(settings, new GuidUtils());
|
||||
_proxyMappingConverter = new ProxyMappingConverter(settings, new GuidUtils(), new DateTimeUtils());
|
||||
}
|
||||
|
||||
public async Task<(IResponseMessage Message, IMapping? Mapping)> SendAsync(
|
||||
@@ -47,12 +49,33 @@ internal class ProxyHelper
|
||||
var responseMessage = await HttpResponseMessageHelper.CreateAsync(httpResponseMessage, requiredUri, originalUri, deserializeJson, decompressGzipAndDeflate).ConfigureAwait(false);
|
||||
|
||||
IMapping? newMapping = null;
|
||||
if (HttpStatusRangeParser.IsMatch(proxyAndRecordSettings.SaveMappingForStatusCodePattern, responseMessage.StatusCode) &&
|
||||
(proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
|
||||
|
||||
var saveMappingSettings = proxyAndRecordSettings.SaveMappingSettings;
|
||||
|
||||
bool save = true;
|
||||
if (saveMappingSettings != null)
|
||||
{
|
||||
save &= Check(saveMappingSettings.StatusCodePattern,
|
||||
() => HttpStatusRangeParser.IsMatch(saveMappingSettings.StatusCodePattern, responseMessage.StatusCode)
|
||||
);
|
||||
|
||||
save &= Check(saveMappingSettings.HttpMethods,
|
||||
() => saveMappingSettings.HttpMethods.Value.Contains(requestMessage.Method, StringComparer.OrdinalIgnoreCase)
|
||||
);
|
||||
}
|
||||
|
||||
if (save && (proxyAndRecordSettings.SaveMapping || proxyAndRecordSettings.SaveMappingToFile))
|
||||
{
|
||||
newMapping = _proxyMappingConverter.ToMapping(mapping, proxyAndRecordSettings, requestMessage, responseMessage);
|
||||
}
|
||||
|
||||
return (responseMessage, newMapping);
|
||||
}
|
||||
|
||||
private static bool Check<T>(ProxySaveMappingSetting<T>? saveMappingSetting, Func<bool> action)
|
||||
{
|
||||
var isMatch = saveMappingSetting is null || action();
|
||||
var matchBehaviour = saveMappingSetting?.MatchBehaviour ?? MatchBehaviour.AcceptOnMatch;
|
||||
return isMatch == (matchBehaviour == MatchBehaviour.AcceptOnMatch);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
@@ -54,26 +55,33 @@ public interface IBodyRequestBuilder : IRequestMatcher
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<string, bool> func);
|
||||
IRequestBuilder WithBody(Func<string?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (byte[])
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<byte[], bool> func);
|
||||
IRequestBuilder WithBody(Func<byte[]?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (json object)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<object, bool> func);
|
||||
IRequestBuilder WithBody(Func<object?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: func (BodyData object)
|
||||
/// </summary>
|
||||
/// <param name="func">The function.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<IBodyData, bool> func);
|
||||
IRequestBuilder WithBody(Func<IBodyData?, bool> func);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody: Body as form-urlencoded values.
|
||||
/// </summary>
|
||||
/// <param name="func">The form-urlencoded values.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func);
|
||||
}
|
||||
@@ -1,85 +1,83 @@
|
||||
using JetBrains.Annotations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.RequestBuilders
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The CookieRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface ICookiesRequestBuilder : IParamsRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The CookieRequestBuilder interface.
|
||||
/// WithCookie: matching based on name, pattern and matchBehaviour.
|
||||
/// </summary>
|
||||
public interface ICookiesRequestBuilder : IParamsRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, pattern and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, string pattern, MatchBehaviour matchBehaviour);
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, string pattern, MatchBehaviour matchBehaviour);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, pattern, ignoreCase and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, pattern, ignoreCase and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, string pattern, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, patterns and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, string[] patterns, MatchBehaviour matchBehaviour);
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, patterns and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, string[] patterns, MatchBehaviour matchBehaviour);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, patterns, ignoreCase and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, patterns, ignoreCase and matchBehaviour.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="patterns">The patterns.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, string[] patterns, bool ignoreCase = true, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name and IStringMatcher[].
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, [NotNull] params IStringMatcher[] matchers);
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name and IStringMatcher[].
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, params IStringMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, ignoreCase and IStringMatcher[].
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the cookie-keys.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, bool ignoreCase, [NotNull] params IStringMatcher[] matchers);
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, ignoreCase and IStringMatcher[].
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the cookie-keys.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, bool ignoreCase, params IStringMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, ignoreCase, matchBehaviour and IStringMatcher[].
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the cookie-keys.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] string name, bool ignoreCase, MatchBehaviour matchBehaviour, [NotNull] params IStringMatcher[] matchers);
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on name, ignoreCase, matchBehaviour and IStringMatcher[].
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="ignoreCase">Ignore the case from the cookie-keys.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour.</param>
|
||||
/// <param name="matchers">The matchers.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(string name, bool ignoreCase, MatchBehaviour matchBehaviour, params IStringMatcher[] matchers);
|
||||
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on functions.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The cookies funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie([NotNull] params Func<IDictionary<string, string>, bool>[] funcs);
|
||||
}
|
||||
/// <summary>
|
||||
/// WithCookie: matching based on functions.
|
||||
/// </summary>
|
||||
/// <param name="funcs">The cookies funcs.</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithCookie(params Func<IDictionary<string, string>, bool>[] funcs);
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
|
||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Util;
|
||||
@@ -10,21 +11,21 @@ namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(string, MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(string body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(byte[], MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(byte[] body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(object, MatchBehaviour)"/>
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(object body, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(matchBehaviour, body));
|
||||
@@ -46,39 +47,46 @@ public partial class Request
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{string, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<string, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<string?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{byte[], bool})"/>
|
||||
public IRequestBuilder WithBody(Func<byte[], bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<byte[]?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{object, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<object, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<object?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyRequestBuilder.WithBody(Func{IBodyData, bool})"/>
|
||||
public IRequestBuilder WithBody(Func<IBodyData, bool> func)
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<IBodyData?, bool> func)
|
||||
{
|
||||
Guard.NotNull(func, nameof(func));
|
||||
Guard.NotNull(func);
|
||||
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(func));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBody(Func<IDictionary<string, string>?, bool> func)
|
||||
{
|
||||
_requestMatchers.Add(new RequestMessageBodyMatcher(Guard.NotNull(func)));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
#if USE_ASPNETCORE
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
#endif
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Owin;
|
||||
@@ -53,9 +56,12 @@ public class RequestMessage : IRequestMessage
|
||||
/// <inheritdoc cref="IRequestMessage.Cookies" />
|
||||
public IDictionary<string, string>? Cookies { get; }
|
||||
|
||||
/// <inheritdoc cref="IRequestMessage.Query" />
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, WireMockList<string>>? Query { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<string, WireMockList<string>>? QueryIgnoreCase { get; }
|
||||
|
||||
/// <inheritdoc cref="IRequestMessage.RawQuery" />
|
||||
public string RawQuery { get; }
|
||||
|
||||
@@ -92,6 +98,11 @@ public class RequestMessage : IRequestMessage
|
||||
/// <inheritdoc cref="IRequestMessage.Origin" />
|
||||
public string Origin { get; }
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
/// <inheritdoc cref="IRequestMessage.ClientCertificate" />
|
||||
public X509Certificate2? ClientCertificate { get; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Used for Unit Testing
|
||||
/// </summary>
|
||||
@@ -115,13 +126,20 @@ public class RequestMessage : IRequestMessage
|
||||
/// <param name="bodyData">The BodyData.</param>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <param name="cookies">The cookies.</param>
|
||||
#if USE_ASPNETCORE
|
||||
/// <param name="clientCertificate">The client certificate</param>
|
||||
#endif
|
||||
internal RequestMessage(
|
||||
IWireMockMiddlewareOptions? options,
|
||||
UrlDetails urlDetails, string method,
|
||||
string clientIP,
|
||||
IBodyData? bodyData = null,
|
||||
IDictionary<string, string[]>? headers = null,
|
||||
IDictionary<string, string>? cookies = null)
|
||||
IDictionary<string, string>? cookies = null
|
||||
#if USE_ASPNETCORE
|
||||
, X509Certificate2? clientCertificate = null
|
||||
#endif
|
||||
)
|
||||
{
|
||||
Guard.NotNull(urlDetails, nameof(urlDetails));
|
||||
Guard.NotNull(method, nameof(method));
|
||||
@@ -156,6 +174,10 @@ public class RequestMessage : IRequestMessage
|
||||
Cookies = cookies;
|
||||
RawQuery = urlDetails.Url.Query;
|
||||
Query = QueryStringParser.Parse(RawQuery, options?.QueryParameterMultipleValueSupport);
|
||||
QueryIgnoreCase = new Dictionary<string, WireMockList<string>>(Query, StringComparer.OrdinalIgnoreCase);
|
||||
#if USE_ASPNETCORE
|
||||
ClientCertificate = clientCertificate;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -29,7 +29,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : Create a ... response based on a callback function.
|
||||
/// WithBody : Create a ... response based on a async callback function.
|
||||
/// </summary>
|
||||
/// <param name="bodyFactory">The async delegate to build the body.</param>
|
||||
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
|
||||
@@ -51,7 +51,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// </summary>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <param name="indented">Use JSON indented.</param>
|
||||
/// <param name="indented">Define whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsJson(object body, Encoding? encoding = null, bool? indented = null);
|
||||
|
||||
@@ -63,6 +63,22 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsJson(object body, bool indented);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsJson : Create a ... response based on a callback function.
|
||||
/// </summary>
|
||||
/// <param name="bodyFactory">The delegate to build the body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsJson(Func<IRequestMessage, object> bodyFactory, Encoding? encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsJson : Create a ... response based on a async callback function.
|
||||
/// </summary>
|
||||
/// <param name="bodyFactory">The async delegate to build the body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsJson(Func<IRequestMessage, Task<object>> bodyFactory, Encoding? encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyFromFile : Create a ... response based on a File.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using JetBrains.Annotations;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
@@ -17,9 +17,17 @@ public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
|
||||
IResponseBuilder WithProxy(string proxyUrl, string? clientX509Certificate2ThumbprintOrSubjectName = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithProxy using IProxyAndRecordSettings.
|
||||
/// WithProxy using <see cref="ProxyAndRecordSettings"/>.
|
||||
/// </summary>
|
||||
/// <param name="settings">The IProxyAndRecordSettings.</param>
|
||||
/// <param name="settings">The ProxyAndRecordSettings.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithProxy([NotNull] ProxyAndRecordSettings settings);
|
||||
IResponseBuilder WithProxy(ProxyAndRecordSettings settings);
|
||||
|
||||
/// <summary>
|
||||
/// WithProxy using <see cref="X509Certificate2"/>.
|
||||
/// </summary>
|
||||
/// <param name="proxyUrl">The proxy url.</param>
|
||||
/// <param name="certificate"">The X509Certificate2.</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate);
|
||||
}
|
||||
@@ -29,5 +29,5 @@ public interface ITransformResponseBuilder : IDelayResponseBuilder
|
||||
/// <returns>
|
||||
/// The <see cref="IResponseBuilder"/>.
|
||||
/// </returns>
|
||||
IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None);
|
||||
IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.Evaluate);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ public partial class Response
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||
{
|
||||
Guard.NotNull(bodyFactory, nameof(bodyFactory));
|
||||
Guard.NotNull(bodyFactory);
|
||||
|
||||
return WithCallbackInternal(true, req => new ResponseMessage
|
||||
{
|
||||
@@ -30,7 +30,7 @@ public partial class Response
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBody(Func<IRequestMessage, Task<string>> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null)
|
||||
{
|
||||
Guard.NotNull(bodyFactory, nameof(bodyFactory));
|
||||
Guard.NotNull(bodyFactory);
|
||||
|
||||
return WithCallbackInternal(true, async req => new ResponseMessage
|
||||
{
|
||||
@@ -70,7 +70,7 @@ public partial class Response
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyFromFile"/>
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyFromFile(string filename, bool cache = true)
|
||||
{
|
||||
Guard.NotNull(filename);
|
||||
@@ -127,7 +127,7 @@ public partial class Response
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson(object, Encoding, bool?)"/>
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsJson(object body, Encoding? encoding = null, bool? indented = null)
|
||||
{
|
||||
Guard.NotNull(body);
|
||||
@@ -144,12 +144,46 @@ public partial class Response
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IBodyResponseBuilder.WithBodyAsJson(object, bool)"/>
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsJson(object body, bool indented)
|
||||
{
|
||||
return WithBodyAsJson(body, null, indented);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsJson(Func<IRequestMessage, object> bodyFactory, Encoding? encoding = null)
|
||||
{
|
||||
Guard.NotNull(bodyFactory);
|
||||
|
||||
return WithCallbackInternal(true, req => new ResponseMessage
|
||||
{
|
||||
BodyData = new BodyData
|
||||
{
|
||||
Encoding = encoding ?? Encoding.UTF8,
|
||||
DetectedBodyType = BodyType.Json,
|
||||
BodyAsJson = bodyFactory(req),
|
||||
IsFuncUsed = "Func<IRequestMessage, object>"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsJson(Func<IRequestMessage, Task<object>> bodyFactory, Encoding? encoding = null)
|
||||
{
|
||||
Guard.NotNull(bodyFactory);
|
||||
|
||||
return WithCallbackInternal(true, async req => new ResponseMessage
|
||||
{
|
||||
BodyData = new BodyData
|
||||
{
|
||||
Encoding = encoding ?? Encoding.UTF8,
|
||||
DetectedBodyType = BodyType.Json,
|
||||
BodyAsJson = await bodyFactory(req).ConfigureAwait(false),
|
||||
IsFuncUsed = "Func<IRequestMessage, Task<object>>"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBody(object body, IJsonConverter converter, JsonConverterOptions? options = null)
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Net.Http;
|
||||
using WireMock.Http;
|
||||
using WireMock.Settings;
|
||||
using Stef.Validation;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
@@ -38,4 +39,19 @@ public partial class Response
|
||||
_httpClientForProxy = HttpClientBuilder.Build(settings);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate)
|
||||
{
|
||||
Guard.NotNullOrEmpty(proxyUrl);
|
||||
Guard.NotNull(certificate);
|
||||
|
||||
var settings = new ProxyAndRecordSettings
|
||||
{
|
||||
Url = proxyUrl,
|
||||
Certificate = certificate
|
||||
};
|
||||
|
||||
return WithProxy(settings);
|
||||
}
|
||||
}
|
||||
@@ -107,14 +107,12 @@ public partial class Response : IResponseBuilder
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Response"/> class.
|
||||
/// </summary>
|
||||
/// <param name="responseMessage">
|
||||
/// The response.
|
||||
/// </param>
|
||||
/// <param name="responseMessage">The response.</param>
|
||||
private Response(ResponseMessage responseMessage)
|
||||
{
|
||||
ResponseMessage = responseMessage;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithStatusCode(int code)
|
||||
@@ -207,7 +205,7 @@ public partial class Response : IResponseBuilder
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithTransformer(TransformerType transformerType, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.None)
|
||||
public IResponseBuilder WithTransformer(TransformerType transformerType, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.Evaluate)
|
||||
{
|
||||
UseTransformer = true;
|
||||
TransformerType = transformerType;
|
||||
@@ -314,14 +312,14 @@ public partial class Response : IResponseBuilder
|
||||
switch (TransformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
var factoryHandlebars = new HandlebarsContextFactory(settings.FileSystemHandler, settings.HandlebarsRegistrationCallback);
|
||||
responseMessageTransformer = new Transformer(factoryHandlebars);
|
||||
var factoryHandlebars = new HandlebarsContextFactory(settings);
|
||||
responseMessageTransformer = new Transformer(settings, factoryHandlebars);
|
||||
break;
|
||||
|
||||
case TransformerType.Scriban:
|
||||
case TransformerType.ScribanDotLiquid:
|
||||
var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, TransformerType);
|
||||
responseMessageTransformer = new Transformer(factoryDotLiquid);
|
||||
responseMessageTransformer = new Transformer(settings, factoryDotLiquid);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -16,6 +19,8 @@ namespace WireMock.Serialization;
|
||||
|
||||
internal class MappingConverter
|
||||
{
|
||||
private static readonly string AcceptOnMatch = MatchBehaviour.AcceptOnMatch.GetFullyQualifiedEnumValue();
|
||||
|
||||
private readonly MatcherMapper _mapper;
|
||||
|
||||
public MappingConverter(MatcherMapper mapper)
|
||||
@@ -23,6 +28,133 @@ internal class MappingConverter
|
||||
_mapper = Guard.NotNull(mapper);
|
||||
}
|
||||
|
||||
public string ToCSharpCode(IMapping mapping, MappingConverterSettings? settings = null)
|
||||
{
|
||||
settings ??= new MappingConverterSettings();
|
||||
|
||||
var request = (Request)mapping.RequestMatcher;
|
||||
var response = (Response) mapping.Provider;
|
||||
|
||||
var clientIPMatcher = request.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||
var pathMatcher = request.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||
var urlMatcher = request.GetRequestMessageMatcher<RequestMessageUrlMatcher>();
|
||||
var headerMatchers = request.GetRequestMessageMatchers<RequestMessageHeaderMatcher>();
|
||||
var cookieMatchers = request.GetRequestMessageMatchers<RequestMessageCookieMatcher>();
|
||||
var paramsMatchers = request.GetRequestMessageMatchers<RequestMessageParamMatcher>();
|
||||
var methodMatcher = request.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
var bodyMatcher = request.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (settings.ConverterType == MappingConverterType.Server)
|
||||
{
|
||||
if (settings.AddStart)
|
||||
{
|
||||
sb.AppendLine("var server = WireMockServer.Start();");
|
||||
}
|
||||
sb.AppendLine("server");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (settings.AddStart)
|
||||
{
|
||||
sb.AppendLine("var builder = new MappingBuilder();");
|
||||
}
|
||||
sb.AppendLine("builder");
|
||||
}
|
||||
|
||||
// Request
|
||||
sb.AppendLine(" .Given(Request.Create()");
|
||||
sb.AppendLine($" .UsingMethod({To1Or2Or3Arguments(methodMatcher?.MatchBehaviour, methodMatcher?.MatchOperator, methodMatcher?.Methods, HttpRequestMethod.GET)})");
|
||||
|
||||
if (pathMatcher is { Matchers: { } })
|
||||
{
|
||||
sb.AppendLine($" .WithPath({To1Or2Arguments(pathMatcher.MatchOperator, pathMatcher.Matchers)})");
|
||||
}
|
||||
else if (urlMatcher is { Matchers: { } })
|
||||
{
|
||||
sb.AppendLine($" .WithUrl({To1Or2Arguments(urlMatcher.MatchOperator, urlMatcher.Matchers)})");
|
||||
}
|
||||
|
||||
foreach (var paramsMatcher in paramsMatchers)
|
||||
{
|
||||
sb.AppendLine($" .WithParam({To1Or2Or3Arguments(paramsMatcher.Key, paramsMatcher.MatchBehaviour, paramsMatcher.Matchers!)})");
|
||||
}
|
||||
|
||||
if (clientIPMatcher is { Matchers: { } })
|
||||
{
|
||||
sb.AppendLine($" .WithClientIP({ToValueArguments(GetStringArray(clientIPMatcher.Matchers))})");
|
||||
}
|
||||
|
||||
foreach (var headerMatcher in headerMatchers.Where(h => h.Matchers is { }))
|
||||
{
|
||||
var headerBuilder = new StringBuilder($"\"{headerMatcher.Name}\", {ToValueArguments(GetStringArray(headerMatcher.Matchers!))}, true");
|
||||
if (headerMatcher.MatchOperator != MatchOperator.Or)
|
||||
{
|
||||
headerBuilder.Append($"{AcceptOnMatch}, {headerMatcher.MatchOperator.GetFullyQualifiedEnumValue()}");
|
||||
}
|
||||
sb.AppendLine($" .WithHeader({headerBuilder})");
|
||||
}
|
||||
|
||||
foreach (var cookieMatcher in cookieMatchers.Where(h => h.Matchers is { }))
|
||||
{
|
||||
sb.AppendLine($" .WithCookie(\"{cookieMatcher.Name}\", {ToValueArguments(GetStringArray(cookieMatcher.Matchers!))}, true)");
|
||||
}
|
||||
|
||||
if (bodyMatcher is { Matchers: { } })
|
||||
{
|
||||
var wildcardMatcher = bodyMatcher.Matchers.OfType<WildcardMatcher>().FirstOrDefault();
|
||||
if (wildcardMatcher is { } && wildcardMatcher.GetPatterns().Any())
|
||||
{
|
||||
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine(@" )");
|
||||
|
||||
// Guid
|
||||
sb.AppendLine($" .WithGuid(\"{mapping.Guid}\")");
|
||||
|
||||
// Response
|
||||
sb.AppendLine(" .RespondWith(Response.Create()");
|
||||
|
||||
if (response.ResponseMessage.Headers is { })
|
||||
{
|
||||
foreach (var header in response.ResponseMessage.Headers)
|
||||
{
|
||||
sb.AppendLine($" .WithHeader(\"{header.Key})\", {ToValueArguments(header.Value.ToArray())})");
|
||||
}
|
||||
}
|
||||
|
||||
if (response.ResponseMessage.BodyData is { })
|
||||
{
|
||||
switch (response.ResponseMessage.BodyData.DetectedBodyType)
|
||||
{
|
||||
case BodyType.String:
|
||||
sb.AppendLine($" .WithBody(\"{response.ResponseMessage.BodyData.BodyAsString}\")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (response.Delay is { })
|
||||
{
|
||||
sb.AppendLine($" .WithDelay({response.Delay.Value.TotalMilliseconds})");
|
||||
}
|
||||
else if (response.MinimumDelayMilliseconds > 0 && response.MaximumDelayMilliseconds > 0)
|
||||
{
|
||||
sb.AppendLine($" .WithRandomDelay({response.MinimumDelayMilliseconds}, {response.MaximumDelayMilliseconds})");
|
||||
}
|
||||
|
||||
if (response.UseTransformer)
|
||||
{
|
||||
var transformerArgs = response.TransformerType != TransformerType.Handlebars ? response.TransformerType.GetFullyQualifiedEnumValue() : string.Empty;
|
||||
sb.AppendLine($" .WithTransformer({transformerArgs})");
|
||||
}
|
||||
sb.AppendLine(@" );");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public MappingModel ToMappingModel(IMapping mapping)
|
||||
{
|
||||
var request = (Request)mapping.RequestMatcher;
|
||||
@@ -40,6 +172,7 @@ internal class MappingConverter
|
||||
var mappingModel = new MappingModel
|
||||
{
|
||||
Guid = mapping.Guid,
|
||||
UpdatedAt = mapping.UpdatedAt,
|
||||
TimeSettings = TimeSettingsMapper.Map(mapping.TimeSettings),
|
||||
Title = mapping.Title,
|
||||
Description = mapping.Description,
|
||||
@@ -48,6 +181,7 @@ internal class MappingConverter
|
||||
Scenario = mapping.Scenario,
|
||||
WhenStateIs = mapping.ExecutionConditionState,
|
||||
SetStateTo = mapping.NextState,
|
||||
Data = mapping.Data,
|
||||
Request = new RequestModel
|
||||
{
|
||||
Headers = headerMatchers.Any() ? headerMatchers.Select(hm => new HeaderModel
|
||||
@@ -236,6 +370,64 @@ internal class MappingConverter
|
||||
return mappingModel;
|
||||
}
|
||||
|
||||
private static string GetString(IStringMatcher stringMatcher)
|
||||
{
|
||||
return stringMatcher.GetPatterns().Select(p => $"\"{p.GetPattern()}\"").First();
|
||||
}
|
||||
|
||||
private static string[] GetStringArray(IReadOnlyList<IStringMatcher> stringMatchers)
|
||||
{
|
||||
return stringMatchers.SelectMany(m => m.GetPatterns()).Select(p => p.GetPattern()).ToArray();
|
||||
}
|
||||
|
||||
private static string To1Or2Or3Arguments(string key, MatchBehaviour? matchBehaviour, IReadOnlyList<IStringMatcher> matchers)
|
||||
{
|
||||
var sb = new StringBuilder($"\"{key}\", ");
|
||||
|
||||
if (matchBehaviour.HasValue && matchBehaviour != MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
sb.AppendFormat("{0}, ", matchBehaviour.Value.GetFullyQualifiedEnumValue());
|
||||
}
|
||||
|
||||
sb.AppendFormat("{0}", ToValueArguments(GetStringArray(matchers), string.Empty));
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static string To1Or2Or3Arguments(MatchBehaviour? matchBehaviour, MatchOperator? matchOperator, string[]? values, string defaultValue)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (matchBehaviour.HasValue && matchBehaviour != MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
sb.AppendFormat("{0}, ", matchBehaviour.Value.GetFullyQualifiedEnumValue());
|
||||
}
|
||||
|
||||
return To1Or2Arguments(matchOperator, values, defaultValue);
|
||||
}
|
||||
|
||||
private static string To1Or2Arguments(MatchOperator? matchOperator, IReadOnlyList<IStringMatcher> matchers)
|
||||
{
|
||||
return To1Or2Arguments(matchOperator, GetStringArray(matchers), string.Empty);
|
||||
}
|
||||
|
||||
private static string To1Or2Arguments(MatchOperator? matchOperator, string[]? values, string defaultValue)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
|
||||
if (matchOperator.HasValue && matchOperator != MatchOperator.Or)
|
||||
{
|
||||
sb.AppendFormat("{0}, ", matchOperator.Value.GetFullyQualifiedEnumValue());
|
||||
}
|
||||
|
||||
return sb.Append(ToValueArguments(values, defaultValue)).ToString();
|
||||
}
|
||||
|
||||
private static string ToValueArguments(string[]? values, string defaultValue = "")
|
||||
{
|
||||
return values is { } ? string.Join(", ", values.Select(v => $"\"{v}\"")) : $"\"{defaultValue}\"";
|
||||
}
|
||||
|
||||
private static WebProxyModel? MapWebProxy(WebProxySettings? settings)
|
||||
{
|
||||
return settings != null ? new WebProxyModel
|
||||
|
||||
25
src/WireMock.Net/Serialization/MappingConverterSettings.cs
Normal file
25
src/WireMock.Net/Serialization/MappingConverterSettings.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// MappingConverterSettings
|
||||
/// </summary>
|
||||
public class MappingConverterSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Use 'Server' or 'Builder'.
|
||||
///
|
||||
/// Default is Server
|
||||
/// </summary>
|
||||
public MappingConverterType ConverterType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Add "var server = WireMockServer.Start();"
|
||||
/// or
|
||||
/// Add "var builder = new MappingBuilder();"
|
||||
///
|
||||
/// Default it's false.
|
||||
/// </summary>
|
||||
public bool AddStart { get; set; }
|
||||
}
|
||||
@@ -17,6 +17,20 @@ internal class MappingToFileSaver
|
||||
_mappingConverter = Guard.NotNull(mappingConverter);
|
||||
}
|
||||
|
||||
public void SaveMappingsToFile(IMapping[] mappings, string? folder = null)
|
||||
{
|
||||
folder ??= _settings.FileSystemHandler.GetMappingFolder();
|
||||
|
||||
if (!_settings.FileSystemHandler.FolderExists(folder))
|
||||
{
|
||||
_settings.FileSystemHandler.CreateFolder(folder);
|
||||
}
|
||||
|
||||
var models = mappings.Select(_mappingConverter.ToMappingModel).ToArray();
|
||||
|
||||
Save(models, folder);
|
||||
}
|
||||
|
||||
public void SaveMappingToFile(IMapping mapping, string? folder = null)
|
||||
{
|
||||
folder ??= _settings.FileSystemHandler.GetMappingFolder();
|
||||
@@ -31,9 +45,14 @@ internal class MappingToFileSaver
|
||||
var filename = BuildSanitizedFileName(mapping);
|
||||
var path = Path.Combine(folder, filename);
|
||||
|
||||
Save(model, path);
|
||||
}
|
||||
|
||||
private void Save(object value, string path)
|
||||
{
|
||||
_settings.Logger.Info("Saving Mapping file {0}", path);
|
||||
|
||||
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(model, JsonSerializationConstants.JsonSerializerSettingsDefault));
|
||||
_settings.FileSystemHandler.WriteMappingFile(path, JsonConvert.SerializeObject(value, JsonSerializationConstants.JsonSerializerSettingsDefault));
|
||||
}
|
||||
|
||||
private string BuildSanitizedFileName(IMapping mapping, char replaceChar = '_')
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using AnyOfTypes;
|
||||
using SimMetrics.Net;
|
||||
using Stef.Validation;
|
||||
using WireMock.Admin.Mappings;
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers;
|
||||
@@ -19,7 +20,7 @@ internal class MatcherMapper
|
||||
|
||||
public MatcherMapper(WireMockServerSettings settings)
|
||||
{
|
||||
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
|
||||
_settings = Guard.NotNull(settings);
|
||||
}
|
||||
|
||||
public IMatcher[]? Map(IEnumerable<MatcherModel>? matchers)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using Stef.Validation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
@@ -17,15 +17,21 @@ internal class ProxyMappingConverter
|
||||
{
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly IGuidUtils _guidUtils;
|
||||
private readonly IDateTimeUtils _dateTimeUtils;
|
||||
|
||||
public ProxyMappingConverter(WireMockServerSettings settings, IGuidUtils guidUtils)
|
||||
public ProxyMappingConverter(WireMockServerSettings settings, IGuidUtils guidUtils, IDateTimeUtils dateTimeUtils)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
_guidUtils = Guard.NotNull(guidUtils);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
}
|
||||
|
||||
public IMapping ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
public IMapping? ToMapping(IMapping? mapping, ProxyAndRecordSettings proxyAndRecordSettings, IRequestMessage requestMessage, ResponseMessage responseMessage)
|
||||
{
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[0];
|
||||
|
||||
var request = (Request?)mapping?.RequestMatcher;
|
||||
var clientIPMatcher = request?.GetRequestMessageMatcher<RequestMessageClientIPMatcher>();
|
||||
var pathMatcher = request?.GetRequestMessageMatcher<RequestMessagePathMatcher>();
|
||||
@@ -35,11 +41,6 @@ internal class ProxyMappingConverter
|
||||
var methodMatcher = request?.GetRequestMessageMatcher<RequestMessageMethodMatcher>();
|
||||
var bodyMatcher = request?.GetRequestMessageMatcher<RequestMessageBodyMatcher>();
|
||||
|
||||
var useDefinedRequestMatchers = proxyAndRecordSettings.UseDefinedRequestMatchers;
|
||||
|
||||
var excludedHeaders = new List<string>(proxyAndRecordSettings.ExcludedHeaders ?? new string[] { }) { "Cookie" };
|
||||
var excludedCookies = proxyAndRecordSettings.ExcludedCookies ?? new string[] { };
|
||||
|
||||
var newRequest = Request.Create();
|
||||
|
||||
// ClientIP
|
||||
@@ -162,6 +163,7 @@ internal class ProxyMappingConverter
|
||||
return new Mapping
|
||||
(
|
||||
guid: _guidUtils.NewGuid(),
|
||||
updatedAt: _dateTimeUtils.UtcNow,
|
||||
title: title,
|
||||
description: description,
|
||||
path: null,
|
||||
@@ -175,7 +177,8 @@ internal class ProxyMappingConverter
|
||||
stateTimes: null,
|
||||
webhooks: null,
|
||||
useWebhooksFireAndForget: null,
|
||||
timeSettings: null
|
||||
timeSettings: null,
|
||||
data: mapping?.Data
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,7 @@ internal static class WebhookMapper
|
||||
|
||||
if (!Enum.TryParse<ReplaceNodeOptions>(model.Request.TransformerReplaceNodeOptions, out var option))
|
||||
{
|
||||
option = ReplaceNodeOptions.None;
|
||||
option = ReplaceNodeOptions.Evaluate;
|
||||
}
|
||||
webhook.Request.TransformerReplaceNodeOptions = option;
|
||||
}
|
||||
|
||||
@@ -166,4 +166,14 @@ public interface IRespondWithAProvider
|
||||
bool useTransformer = true,
|
||||
TransformerType transformerType = TransformerType.Handlebars
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Data Object which can be used when WithTransformer is used.
|
||||
/// e.g. lookup an path in this object using
|
||||
/// <param name="data">The data dictionary object.</param>
|
||||
/// <example>
|
||||
/// lookup data "1"
|
||||
/// </example>
|
||||
/// </summary>
|
||||
IRespondWithAProvider WithData(object data);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
@@ -28,29 +29,44 @@ 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 bool _useWebhookFireAndForget;
|
||||
private bool? _useWebhookFireAndForget;
|
||||
|
||||
public Guid Guid { get; private set; } = Guid.NewGuid();
|
||||
public Guid Guid { get; private set; }
|
||||
|
||||
public IWebhook[]? Webhooks { get; private set; }
|
||||
|
||||
public ITimeSettings? TimeSettings { get; private set; }
|
||||
|
||||
public object? Data { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RespondWithAProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="registrationCallback">The registration callback.</param>
|
||||
/// <param name="requestMatcher">The request matcher.</param>
|
||||
/// <param name="settings">The WireMockServerSettings.</param>
|
||||
/// <param name="guidUtils">GuidUtils to make unit testing possible.</param>
|
||||
/// <param name="dateTimeUtils">DateTimeUtils to make unit testing possible.</param>
|
||||
/// <param name="saveToFile">Optional boolean to indicate if this mapping should be saved as static mapping file.</param>
|
||||
public RespondWithAProvider(RegistrationCallback registrationCallback, IRequestMatcher requestMatcher, WireMockServerSettings settings, bool saveToFile = false)
|
||||
public RespondWithAProvider(
|
||||
RegistrationCallback registrationCallback,
|
||||
IRequestMatcher requestMatcher,
|
||||
WireMockServerSettings settings,
|
||||
IGuidUtils guidUtils,
|
||||
IDateTimeUtils dateTimeUtils,
|
||||
bool saveToFile = false
|
||||
)
|
||||
{
|
||||
_registrationCallback = registrationCallback;
|
||||
_requestMatcher = requestMatcher;
|
||||
_settings = settings;
|
||||
_registrationCallback = Guard.NotNull(registrationCallback);
|
||||
_requestMatcher = Guard.NotNull(requestMatcher);
|
||||
_settings = Guard.NotNull(settings);
|
||||
_dateTimeUtils = Guard.NotNull(dateTimeUtils);
|
||||
_saveToFile = saveToFile;
|
||||
|
||||
Guid = guidUtils.NewGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -59,7 +75,34 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
/// <param name="provider">The provider.</param>
|
||||
public void RespondWith(IResponseProvider provider)
|
||||
{
|
||||
_registrationCallback(new Mapping(Guid, _title, _description, _path, _settings, _requestMatcher, provider, _priority, _scenario, _executionConditionState, _nextState, _timesInSameState, Webhooks, _useWebhookFireAndForget, TimeSettings), _saveToFile);
|
||||
var mapping = new Mapping(
|
||||
Guid,
|
||||
_dateTimeUtils.UtcNow,
|
||||
_title,
|
||||
_description,
|
||||
_path,
|
||||
_settings,
|
||||
_requestMatcher,
|
||||
provider,
|
||||
_priority,
|
||||
_scenario,
|
||||
_executionConditionState,
|
||||
_nextState,
|
||||
_timesInSameState,
|
||||
Webhooks,
|
||||
_useWebhookFireAndForget,
|
||||
TimeSettings,
|
||||
Data);
|
||||
|
||||
_registrationCallback(mapping, _saveToFile);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider WithData(object data)
|
||||
{
|
||||
Data = data;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -32,6 +32,7 @@ public partial class WireMockServer
|
||||
private const int EnhancedFileSystemWatcherTimeoutMs = 1000;
|
||||
private const string AdminFiles = "/__admin/files";
|
||||
private const string AdminMappings = "/__admin/mappings";
|
||||
private const string AdminMappingsCode = "/__admin/mappings/code";
|
||||
private const string AdminMappingsWireMockOrg = "/__admin/mappings/wiremock.org";
|
||||
private const string AdminRequests = "/__admin/requests";
|
||||
private const string AdminSettings = "/__admin/settings";
|
||||
@@ -41,6 +42,7 @@ public partial class WireMockServer
|
||||
private static readonly Guid ProxyMappingGuid = new("e59914fd-782e-428e-91c1-4810ffb86567");
|
||||
private static readonly RegexMatcher AdminRequestContentTypeJson = new ContentTypeMatcher(WireMockConstants.ContentTypeJson, true);
|
||||
private static readonly RegexMatcher AdminMappingsGuidPathMatcher = new(@"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||
private static readonly RegexMatcher AdminMappingsCodeGuidPathMatcher = new(@"^\/__admin\/mappings\/code\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||
private static readonly RegexMatcher AdminRequestsGuidPathMatcher = new(@"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
|
||||
private static readonly RegexMatcher AdminScenariosNameMatcher = new(@"^\/__admin\/scenarios\/.+$");
|
||||
private static readonly RegexMatcher AdminScenariosNameWithResetMatcher = new(@"^\/__admin\/scenarios\/.+\/reset$");
|
||||
@@ -57,9 +59,14 @@ public partial class WireMockServer
|
||||
// __admin/mappings
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
|
||||
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
|
||||
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
|
||||
|
||||
// __admin/mappings/code
|
||||
Given(Request.Create().WithPath(AdminMappingsCode).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsCodeGet));
|
||||
|
||||
// __admin/mappings/wiremock.org
|
||||
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
|
||||
|
||||
// __admin/mappings/reset
|
||||
Given(Request.Create().WithPath(AdminMappings + "/reset").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsReset));
|
||||
|
||||
@@ -68,6 +75,9 @@ public partial class WireMockServer
|
||||
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingPut().WithHeader(HttpKnownHeaderNames.ContentType, AdminRequestContentTypeJson)).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingPut));
|
||||
Given(Request.Create().WithPath(AdminMappingsGuidPathMatcher).UsingDelete()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingDelete));
|
||||
|
||||
// __admin/mappings/code/{guid}
|
||||
Given(Request.Create().WithPath(AdminMappingsCodeGuidPathMatcher).UsingGet()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingCodeGet));
|
||||
|
||||
// __admin/mappings/save
|
||||
Given(Request.Create().WithPath($"{AdminMappings}/save").UsingPost()).AtPriority(WireMockConstants.AdminPriority).RespondWith(new DynamicResponseProvider(MappingsSave));
|
||||
|
||||
@@ -111,10 +121,7 @@ public partial class WireMockServer
|
||||
[PublicAPI]
|
||||
public void SaveStaticMappings(string? folder = null)
|
||||
{
|
||||
foreach (var mapping in Mappings.Where(m => !m.IsAdminInterface))
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping, folder);
|
||||
}
|
||||
_mappingBuilder.SaveMappingsToFolder(folder);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockServer.ReadStaticMappings" />
|
||||
@@ -226,7 +233,9 @@ public partial class WireMockServer
|
||||
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
|
||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString(),
|
||||
ClientCertificateMode = _settings.ClientCertificateMode,
|
||||
AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -275,6 +284,9 @@ public partial class WireMockServer
|
||||
_settings.CorsPolicyOptions = corsPolicyOptions;
|
||||
_options.CorsPolicyOptions = corsPolicyOptions;
|
||||
}
|
||||
|
||||
_options.ClientCertificateMode = _settings.ClientCertificateMode;
|
||||
_options.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate;
|
||||
#endif
|
||||
|
||||
return ResponseMessageBuilder.Create("Settings updated");
|
||||
@@ -284,13 +296,11 @@ public partial class WireMockServer
|
||||
#region Mapping/{guid}
|
||||
private IResponseMessage MappingGet(IRequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = ParseGuidFromRequestMessage(requestMessage);
|
||||
var mapping = Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid);
|
||||
|
||||
var mapping = FindMappingByGuid(requestMessage);
|
||||
if (mapping == null)
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create("Mapping not found", 404);
|
||||
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var model = _mappingConverter.ToMappingModel(mapping);
|
||||
@@ -298,31 +308,71 @@ public partial class WireMockServer
|
||||
return ToJson(model);
|
||||
}
|
||||
|
||||
private IResponseMessage MappingCodeGet(IRequestMessage requestMessage)
|
||||
{
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
|
||||
{
|
||||
var code = _mappingBuilder.ToCSharpCode(guid, GetMappingConverterType(requestMessage));
|
||||
if (code is null)
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return ToResponseMessage(code);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 400");
|
||||
return ResponseMessageBuilder.Create("GUID is missing", HttpStatusCode.BadRequest);
|
||||
}
|
||||
|
||||
private static MappingConverterType GetMappingConverterType(IRequestMessage requestMessage)
|
||||
{
|
||||
var mappingConverterType = MappingConverterType.Server;
|
||||
|
||||
if (requestMessage.QueryIgnoreCase?.TryGetValue(nameof(MappingConverterType), out var values) == true &&
|
||||
Enum.TryParse(values.FirstOrDefault(), true, out MappingConverterType parsed))
|
||||
{
|
||||
mappingConverterType = parsed;
|
||||
}
|
||||
|
||||
return mappingConverterType;
|
||||
}
|
||||
|
||||
private IMapping? FindMappingByGuid(IRequestMessage requestMessage)
|
||||
{
|
||||
return TryParseGuidFromRequestMessage(requestMessage, out var guid) ? Mappings.FirstOrDefault(m => !m.IsAdminInterface && m.Guid == guid) : null;
|
||||
}
|
||||
|
||||
private IResponseMessage MappingPut(IRequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = ParseGuidFromRequestMessage(requestMessage);
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
|
||||
{
|
||||
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
|
||||
var guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
|
||||
|
||||
var mappingModel = DeserializeObject<MappingModel>(requestMessage);
|
||||
Guid? guidFromPut = ConvertMappingAndRegisterAsRespondProvider(mappingModel, guid);
|
||||
return ResponseMessageBuilder.Create("Mapping added or updated", HttpStatusCode.OK, guidFromPut);
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create("Mapping added or updated", HttpStatusCode.OK, guidFromPut);
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
private IResponseMessage MappingDelete(IRequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = ParseGuidFromRequestMessage(requestMessage);
|
||||
|
||||
if (DeleteMapping(guid))
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteMapping(guid))
|
||||
{
|
||||
return ResponseMessageBuilder.Create("Mapping removed", HttpStatusCode.OK, guid);
|
||||
}
|
||||
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Mapping not found");
|
||||
return ResponseMessageBuilder.Create("Mapping not found", HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
private static Guid ParseGuidFromRequestMessage(IRequestMessage requestMessage)
|
||||
private static bool TryParseGuidFromRequestMessage(IRequestMessage requestMessage, out Guid guid)
|
||||
{
|
||||
return Guid.Parse(requestMessage.Path.Substring(AdminMappings.Length + 1));
|
||||
var lastPart = requestMessage.Path.Split('/').LastOrDefault();
|
||||
return Guid.TryParse(lastPart, out guid);
|
||||
}
|
||||
#endregion Mapping/{guid}
|
||||
|
||||
@@ -348,9 +398,9 @@ public partial class WireMockServer
|
||||
return ResponseMessageBuilder.Create("Mappings saved to disk");
|
||||
}
|
||||
|
||||
private IEnumerable<MappingModel> ToMappingModels()
|
||||
private MappingModel[] ToMappingModels()
|
||||
{
|
||||
return Mappings.Where(m => !m.IsAdminInterface).Select(_mappingConverter.ToMappingModel);
|
||||
return _mappingBuilder.GetMappings();
|
||||
}
|
||||
|
||||
private IResponseMessage MappingsGet(IRequestMessage requestMessage)
|
||||
@@ -358,6 +408,15 @@ public partial class WireMockServer
|
||||
return ToJson(ToMappingModels());
|
||||
}
|
||||
|
||||
private IResponseMessage MappingsCodeGet(IRequestMessage requestMessage)
|
||||
{
|
||||
var converterType = GetMappingConverterType(requestMessage);
|
||||
|
||||
var code = _mappingBuilder.ToCSharpCode(converterType);
|
||||
|
||||
return ToResponseMessage(code);
|
||||
}
|
||||
|
||||
private IResponseMessage MappingsPost(IRequestMessage requestMessage)
|
||||
{
|
||||
try
|
||||
@@ -413,18 +472,15 @@ public partial class WireMockServer
|
||||
try
|
||||
{
|
||||
var mappingModels = DeserializeRequestMessageToArray<MappingModel>(requestMessage);
|
||||
foreach (var mappingModel in mappingModels)
|
||||
foreach (var guid in mappingModels.Where(mm => mm.Guid.HasValue).Select(mm => mm.Guid!.Value))
|
||||
{
|
||||
if (mappingModel.Guid.HasValue)
|
||||
if (DeleteMapping(guid))
|
||||
{
|
||||
if (DeleteMapping(mappingModel.Guid.Value))
|
||||
{
|
||||
deletedGuids.Add(mappingModel.Guid.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
_settings.Logger.Debug($"Did not find/delete mapping with GUID: {mappingModel.Guid.Value}.");
|
||||
}
|
||||
deletedGuids.Add(guid);
|
||||
}
|
||||
else
|
||||
{
|
||||
_settings.Logger.Debug($"Did not find/delete mapping with GUID: {guid}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -465,30 +521,29 @@ public partial class WireMockServer
|
||||
#region Request/{guid}
|
||||
private IResponseMessage RequestGet(IRequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = ParseGuidFromRequestMessage(requestMessage);
|
||||
var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
|
||||
|
||||
if (entry == null)
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid))
|
||||
{
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create("Request not found", 404);
|
||||
var entry = LogEntries.FirstOrDefault(r => !r.RequestMessage.Path.StartsWith("/__admin/") && r.Guid == guid);
|
||||
if (entry is { })
|
||||
{
|
||||
var model = new LogEntryMapper(_options).Map(entry);
|
||||
return ToJson(model);
|
||||
}
|
||||
}
|
||||
|
||||
var model = new LogEntryMapper(_options).Map(entry);
|
||||
|
||||
return ToJson(model);
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create("Request not found", HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
private IResponseMessage RequestDelete(IRequestMessage requestMessage)
|
||||
{
|
||||
Guid guid = ParseGuidFromRequestMessage(requestMessage);
|
||||
|
||||
if (DeleteLogEntry(guid))
|
||||
if (TryParseGuidFromRequestMessage(requestMessage, out var guid) && DeleteLogEntry(guid))
|
||||
{
|
||||
return ResponseMessageBuilder.Create("Request removed");
|
||||
}
|
||||
|
||||
return ResponseMessageBuilder.Create("Request not found", 404);
|
||||
_settings.Logger.Warn("HttpStatusCode set to 404 : Request not found");
|
||||
return ResponseMessageBuilder.Create("Request not found", HttpStatusCode.NotFound);
|
||||
}
|
||||
#endregion Request/{guid}
|
||||
|
||||
@@ -565,7 +620,7 @@ public partial class WireMockServer
|
||||
|
||||
return ResetScenario(name) ?
|
||||
ResponseMessageBuilder.Create("Scenario reset") :
|
||||
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", 404);
|
||||
ResponseMessageBuilder.Create($"No scenario found by name '{name}'.", HttpStatusCode.NotFound);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -688,11 +743,25 @@ public partial class WireMockServer
|
||||
};
|
||||
}
|
||||
|
||||
private static ResponseMessage ToResponseMessage(string text)
|
||||
{
|
||||
return new ResponseMessage
|
||||
{
|
||||
BodyData = new BodyData
|
||||
{
|
||||
DetectedBodyType = BodyType.String,
|
||||
BodyAsString = text
|
||||
},
|
||||
StatusCode = (int)HttpStatusCode.OK,
|
||||
Headers = new Dictionary<string, WireMockList<string>> { { HttpKnownHeaderNames.ContentType, new WireMockList<string>(WireMockConstants.ContentTypeTextPlain) } }
|
||||
};
|
||||
}
|
||||
|
||||
private static T DeserializeObject<T>(IRequestMessage requestMessage) where T : new()
|
||||
{
|
||||
return requestMessage.BodyData?.DetectedBodyType switch
|
||||
{
|
||||
BodyType.String => JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString),
|
||||
BodyType.String => JsonUtils.DeserializeObject<T>(requestMessage.BodyData.BodyAsString!),
|
||||
|
||||
BodyType.Json when requestMessage.BodyData?.BodyAsJson != null => ((JObject)requestMessage.BodyData.BodyAsJson).ToObject<T>()!,
|
||||
|
||||
@@ -721,7 +790,7 @@ public partial class WireMockServer
|
||||
{
|
||||
if (value is JArray jArray)
|
||||
{
|
||||
return jArray.ToObject<T[]>();
|
||||
return jArray.ToObject<T[]>()!;
|
||||
}
|
||||
|
||||
var singleResult = ((JObject)value).ToObject<T>();
|
||||
|
||||
@@ -46,7 +46,7 @@ public partial class WireMockServer
|
||||
}
|
||||
|
||||
var respondProvider = Given(requestBuilder, mappingModel.SaveToFile == true);
|
||||
|
||||
|
||||
if (guid != null)
|
||||
{
|
||||
respondProvider = respondProvider.WithGuid(guid.Value);
|
||||
@@ -56,6 +56,11 @@ public partial class WireMockServer
|
||||
respondProvider = respondProvider.WithGuid(mappingModel.Guid.Value);
|
||||
}
|
||||
|
||||
if (mappingModel.Data != null)
|
||||
{
|
||||
respondProvider = respondProvider.WithData(mappingModel.Data);
|
||||
}
|
||||
|
||||
var timeSettings = TimeSettingsMapper.Map(mappingModel.TimeSettings);
|
||||
if (timeSettings != null)
|
||||
{
|
||||
@@ -102,6 +107,8 @@ public partial class WireMockServer
|
||||
respondProvider = respondProvider.WithWebhook(webhooks);
|
||||
}
|
||||
|
||||
respondProvider.WithWebhookFireAndForget(mappingModel.UseWebhooksFireAndForget ?? false);
|
||||
|
||||
var responseBuilder = InitResponseBuilder(mappingModel.Response);
|
||||
respondProvider.RespondWith(responseBuilder);
|
||||
|
||||
@@ -250,7 +257,7 @@ public partial class WireMockServer
|
||||
|
||||
if (!Enum.TryParse<ReplaceNodeOptions>(responseModel.TransformerReplaceNodeOptions, out var option))
|
||||
{
|
||||
option = ReplaceNodeOptions.None;
|
||||
option = ReplaceNodeOptions.Evaluate;
|
||||
}
|
||||
responseBuilder = responseBuilder.WithTransformer(
|
||||
transformerType,
|
||||
|
||||
@@ -14,26 +14,12 @@ namespace WireMock.Server;
|
||||
|
||||
public partial class WireMockServer
|
||||
{
|
||||
/// <inheritdoc cref="IWireMockServer.LogEntriesChanged" />
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public event NotifyCollectionChangedEventHandler LogEntriesChanged
|
||||
{
|
||||
add
|
||||
{
|
||||
_options.LogEntries.CollectionChanged += (sender, eventRecordArgs) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
value(sender, eventRecordArgs);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_options.Logger.Error("Error calling the LogEntriesChanged event handler: {0}", exception.Message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
remove => _options.LogEntries.CollectionChanged -= value;
|
||||
add => _logEntriesChanged += value;
|
||||
remove => _logEntriesChanged -= value;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IWireMockServer.LogEntries" />
|
||||
@@ -90,4 +76,24 @@ public partial class WireMockServer
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private NotifyCollectionChangedEventHandler? _logEntriesChanged;
|
||||
|
||||
private void LogEntries_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (_logEntriesChanged is { })
|
||||
{
|
||||
foreach (var handler in _logEntriesChanged.GetInvocationList())
|
||||
{
|
||||
try
|
||||
{
|
||||
handler.DynamicInvoke(this, e);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
_options.Logger.Error("Error calling the LogEntriesChanged event handler: {0}", exception.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@ using WireMock.ResponseProviders;
|
||||
using WireMock.Serialization;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Server;
|
||||
|
||||
@@ -38,6 +39,9 @@ public partial class WireMockServer : IWireMockServer
|
||||
private readonly MappingConverter _mappingConverter;
|
||||
private readonly MatcherMapper _matcherMapper;
|
||||
private readonly MappingToFileSaver _mappingToFileSaver;
|
||||
private readonly MappingBuilder _mappingBuilder;
|
||||
private readonly IGuidUtils _guidUtils = new GuidUtils();
|
||||
private readonly IDateTimeUtils _dateTimeUtils = new DateTimeUtils();
|
||||
|
||||
/// <inheritdoc cref="IWireMockServer.IsStarted" />
|
||||
[PublicAPI]
|
||||
@@ -89,6 +93,8 @@ public partial class WireMockServer : IWireMockServer
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_options.LogEntries.CollectionChanged -= LogEntries_CollectionChanged;
|
||||
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
@@ -107,25 +113,42 @@ public partial class WireMockServer : IWireMockServer
|
||||
#region HttpClient
|
||||
/// <summary>
|
||||
/// Create a <see cref="HttpClient"/> which can be used to call this instance.
|
||||
/// <param name="handlers">
|
||||
/// An ordered list of System.Net.Http.DelegatingHandler instances to be invoked
|
||||
/// as an System.Net.Http.HttpRequestMessage travels from the System.Net.Http.HttpClient
|
||||
/// to the network and an System.Net.Http.HttpResponseMessage travels from the network
|
||||
/// back to System.Net.Http.HttpClient. The handlers are invoked in a top-down fashion.
|
||||
/// That is, the first entry is invoked first for an outbound request message but
|
||||
/// last for an inbound response message.
|
||||
/// </param>
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public HttpClient CreateClient()
|
||||
public HttpClient CreateClient(params DelegatingHandler[] handlers)
|
||||
{
|
||||
if (!IsStarted)
|
||||
{
|
||||
throw new InvalidOperationException("Unable to create HttpClient because the service is not started.");
|
||||
}
|
||||
|
||||
var client = HttpClientFactory2.Create();
|
||||
var client = HttpClientFactory2.Create(handlers);
|
||||
client.BaseAddress = new Uri(Url!);
|
||||
return client;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create <see cref="HttpClient"/>s (one for each URL) which can be used to call this instance.
|
||||
/// <param name="innerHandler">The inner handler represents the destination of the HTTP message channel.</param>
|
||||
/// <param name="handlers">
|
||||
/// An ordered list of System.Net.Http.DelegatingHandler instances to be invoked
|
||||
/// as an System.Net.Http.HttpRequestMessage travels from the System.Net.Http.HttpClient
|
||||
/// to the network and an System.Net.Http.HttpResponseMessage travels from the network
|
||||
/// back to System.Net.Http.HttpClient. The handlers are invoked in a top-down fashion.
|
||||
/// That is, the first entry is invoked first for an outbound request message but
|
||||
/// last for an inbound response message.
|
||||
/// </param>
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public HttpClient[] CreateClients()
|
||||
public HttpClient[] CreateClients(HttpMessageHandler innerHandler, params DelegatingHandler[] handlers)
|
||||
{
|
||||
if (!IsStarted)
|
||||
{
|
||||
@@ -134,7 +157,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
|
||||
return Urls.Select(url =>
|
||||
{
|
||||
var client = HttpClientFactory2.Create();
|
||||
var client = HttpClientFactory2.Create(innerHandler, handlers);
|
||||
client.BaseAddress = new Uri(url);
|
||||
return client;
|
||||
}).ToArray();
|
||||
@@ -229,7 +252,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
[PublicAPI]
|
||||
public static WireMockServer StartWithAdminInterfaceAndReadStaticMappings(params string[] urls)
|
||||
{
|
||||
Guard.NotNullOrEmpty(urls, nameof(urls));
|
||||
Guard.NotNullOrEmpty(urls);
|
||||
|
||||
return new WireMockServer(new WireMockServerSettings
|
||||
{
|
||||
@@ -251,7 +274,7 @@ public partial class WireMockServer : IWireMockServer
|
||||
/// <exception cref="TimeoutException">Service start timed out after {TimeSpan.FromMilliseconds(settings.StartTimeout)}</exception>
|
||||
protected WireMockServer(WireMockServerSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
_settings = Guard.NotNull(settings);
|
||||
|
||||
// Set default values if not provided
|
||||
_settings.Logger = settings.Logger ?? new WireMockNullLogger();
|
||||
@@ -288,32 +311,27 @@ public partial class WireMockServer : IWireMockServer
|
||||
}
|
||||
}
|
||||
|
||||
_options.FileSystemHandler = _settings.FileSystemHandler;
|
||||
_options.PreWireMockMiddlewareInit = _settings.PreWireMockMiddlewareInit;
|
||||
_options.PostWireMockMiddlewareInit = _settings.PostWireMockMiddlewareInit;
|
||||
_options.Logger = _settings.Logger;
|
||||
_options.DisableJsonBodyParsing = _settings.DisableJsonBodyParsing;
|
||||
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
|
||||
_options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
||||
_options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||
_options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||
WireMockMiddlewareOptionsHelper.InitFromSettings(settings, _options);
|
||||
|
||||
if (settings.CustomCertificateDefined)
|
||||
{
|
||||
_options.X509StoreName = settings.CertificateSettings!.X509StoreName;
|
||||
_options.X509StoreLocation = settings.CertificateSettings.X509StoreLocation;
|
||||
_options.X509ThumbprintOrSubjectName = settings.CertificateSettings.X509StoreThumbprintOrSubjectName;
|
||||
_options.X509CertificateFilePath = settings.CertificateSettings.X509CertificateFilePath;
|
||||
_options.X509CertificatePassword = settings.CertificateSettings.X509CertificatePassword;
|
||||
}
|
||||
_options.LogEntries.CollectionChanged += LogEntries_CollectionChanged;
|
||||
|
||||
_matcherMapper = new MatcherMapper(_settings);
|
||||
_mappingConverter = new MappingConverter(_matcherMapper);
|
||||
_mappingToFileSaver = new MappingToFileSaver(_settings, _mappingConverter);
|
||||
_mappingBuilder = new MappingBuilder(
|
||||
settings,
|
||||
_options,
|
||||
_mappingConverter,
|
||||
_mappingToFileSaver,
|
||||
_guidUtils,
|
||||
_dateTimeUtils
|
||||
);
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
_options.AdditionalServiceRegistration = _settings.AdditionalServiceRegistration;
|
||||
_options.CorsPolicyOptions = _settings.CorsPolicyOptions;
|
||||
_options.ClientCertificateMode = _settings.ClientCertificateMode;
|
||||
_options.AcceptAnyClientCertificate = _settings.AcceptAnyClientCertificate;
|
||||
|
||||
_httpServer = new AspNetCoreSelfHost(_options, urlOptions);
|
||||
#else
|
||||
@@ -519,25 +537,21 @@ public partial class WireMockServer : IWireMockServer
|
||||
[PublicAPI]
|
||||
public IRespondWithAProvider Given(IRequestMatcher requestMatcher, bool saveToFile = false)
|
||||
{
|
||||
return new RespondWithAProvider(RegisterMapping, requestMatcher, _settings, saveToFile);
|
||||
return _mappingBuilder.Given(requestMatcher, saveToFile);
|
||||
}
|
||||
|
||||
private void RegisterMapping(IMapping mapping, bool saveToFile)
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public string? MappingToCSharpCode(Guid guid, MappingConverterType converterType)
|
||||
{
|
||||
// Check a mapping exists with the same Guid, if so, replace it.
|
||||
if (_options.Mappings.ContainsKey(mapping.Guid))
|
||||
{
|
||||
_options.Mappings[mapping.Guid] = mapping;
|
||||
}
|
||||
else
|
||||
{
|
||||
_options.Mappings.TryAdd(mapping.Guid, mapping);
|
||||
}
|
||||
return _mappingBuilder.ToCSharpCode(guid, converterType);
|
||||
}
|
||||
|
||||
if (saveToFile)
|
||||
{
|
||||
_mappingToFileSaver.SaveMappingToFile(mapping);
|
||||
}
|
||||
/// <inheritdoc />
|
||||
[PublicAPI]
|
||||
public string MappingsToCSharpCode(MappingConverterType converterType)
|
||||
{
|
||||
return _mappingBuilder.ToCSharpCode(converterType);
|
||||
}
|
||||
|
||||
private void InitSettings(WireMockServerSettings settings)
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// HttpClientSettings
|
||||
/// </summary>
|
||||
public class HttpClientSettings
|
||||
public abstract class HttpClientSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The clientCertificate thumbprint or subject name fragment to use.
|
||||
@@ -20,4 +22,9 @@ public class HttpClientSettings
|
||||
/// Proxy requests should follow redirection (30x).
|
||||
/// </summary>
|
||||
public bool? AllowAutoRedirect { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="X509Certificate2"/> to use.
|
||||
/// </summary>
|
||||
public X509Certificate2? Certificate { get; set; }
|
||||
}
|
||||
@@ -28,9 +28,28 @@ public class ProxyAndRecordSettings : HttpClientSettings
|
||||
/// <summary>
|
||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||
///
|
||||
/// Deprecated : use SaveMappingSettings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string SaveMappingForStatusCodePattern { get; set; } = "*";
|
||||
public string SaveMappingForStatusCodePattern
|
||||
{
|
||||
set
|
||||
{
|
||||
if (SaveMappingSettings is null)
|
||||
{
|
||||
SaveMappingSettings = new ProxySaveMappingSettings();
|
||||
}
|
||||
|
||||
SaveMappingSettings.StatusCodePattern = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Additional SaveMappingSettings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines a list from headers which will be excluded from the saved mappings.
|
||||
|
||||
20
src/WireMock.Net/Settings/ProxySaveMappingSetting.cs
Normal file
20
src/WireMock.Net/Settings/ProxySaveMappingSetting.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
public class ProxySaveMappingSetting<T>
|
||||
{
|
||||
public MatchBehaviour MatchBehaviour { get; } = MatchBehaviour.AcceptOnMatch;
|
||||
|
||||
public T Value { get; private set; }
|
||||
|
||||
public ProxySaveMappingSetting(T value, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
Value = value;
|
||||
MatchBehaviour = matchBehaviour;
|
||||
}
|
||||
|
||||
public static implicit operator ProxySaveMappingSetting<T>(T value) => new ProxySaveMappingSetting<T>(value);
|
||||
|
||||
public static implicit operator T(ProxySaveMappingSetting<T> @this) => @this.Value;
|
||||
}
|
||||
22
src/WireMock.Net/Settings/ProxySaveMappingSettings.cs
Normal file
22
src/WireMock.Net/Settings/ProxySaveMappingSettings.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// ProxySaveMappingSettings
|
||||
/// </summary>
|
||||
public class ProxySaveMappingSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
|
||||
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxySaveMappingSetting<string>? StatusCodePattern { get; set; } = "*";
|
||||
|
||||
/// <summary>
|
||||
/// Only save these Http Methods. (Note that SaveMapping must also be set to true.)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxySaveMappingSetting<string[]>? HttpMethods { get; set; }
|
||||
}
|
||||
@@ -1,54 +1,53 @@
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace WireMock.Settings
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
|
||||
///
|
||||
/// X509StoreName and X509StoreLocation should be defined
|
||||
/// OR
|
||||
/// X509CertificateFilePath and X509CertificatePassword should be defined
|
||||
/// </summary>
|
||||
public class WireMockCertificateSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
|
||||
///
|
||||
/// X509 StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509StoreName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509 StoreLocation (CurrentUser or LocalMachine)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509StoreLocation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509 Thumbprint or SubjectName (if not defined, the 'host' is used)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509StoreThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509Certificate FilePath
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509CertificateFilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509Certificate Password
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509CertificatePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509StoreName and X509StoreLocation should be defined
|
||||
/// OR
|
||||
/// X509CertificateFilePath and X509CertificatePassword should be defined
|
||||
/// </summary>
|
||||
public class WireMockCertificateSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// X509 StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509StoreName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509 StoreLocation (CurrentUser or LocalMachine)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509StoreLocation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509 Thumbprint or SubjectName (if not defined, the 'host' is used)
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509StoreThumbprintOrSubjectName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509Certificate FilePath
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509CertificateFilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509Certificate Password
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? X509CertificatePassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// X509StoreName and X509StoreLocation should be defined
|
||||
/// OR
|
||||
/// X509CertificateFilePath and X509CertificatePassword should be defined
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool IsDefined =>
|
||||
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
|
||||
!string.IsNullOrEmpty(X509CertificateFilePath);
|
||||
}
|
||||
[PublicAPI]
|
||||
public bool IsDefined =>
|
||||
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
|
||||
!string.IsNullOrEmpty(X509CertificateFilePath);
|
||||
}
|
||||
@@ -10,272 +10,293 @@ using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.RegularExpressions;
|
||||
using WireMock.Types;
|
||||
using System.Globalization;
|
||||
#if USE_ASPNETCORE
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
#endif
|
||||
|
||||
namespace WireMock.Settings
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// WireMockServerSettings
|
||||
/// </summary>
|
||||
public class WireMockServerSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// WireMockServerSettings
|
||||
/// Gets or sets the http port.
|
||||
/// </summary>
|
||||
public class WireMockServerSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the http port.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? Port { get; set; }
|
||||
[PublicAPI]
|
||||
public int? Port { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the use SSL.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
[PublicAPI]
|
||||
public bool? UseSSL { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the use SSL.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
[PublicAPI]
|
||||
public bool? UseSSL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public HostingScheme? HostingScheme { get; set; }
|
||||
/// <summary>
|
||||
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public HostingScheme? HostingScheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to start admin interface.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? StartAdminInterface { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets whether to start admin interface.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? StartAdminInterface { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the static mappings should be read at startup.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? ReadStaticMappings { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets if the static mappings should be read at startup.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? ReadStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Watch the static mapping files + folder for changes when running.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? WatchStaticMappings { get; set; }
|
||||
/// <summary>
|
||||
/// Watch the static mapping files + folder for changes when running.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? WatchStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A value indicating whether subdirectories within the static mappings path should be monitored.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? WatchStaticMappingsInSubdirectories { get; set; }
|
||||
/// <summary>
|
||||
/// A value indicating whether subdirectories within the static mappings path should be monitored.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? WatchStaticMappingsInSubdirectories { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the proxy and record settings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxyAndRecordSettings? ProxyAndRecordSettings { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets if the proxy and record settings.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public ProxyAndRecordSettings? ProxyAndRecordSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the urls.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string[]? Urls { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the urls.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string[]? Urls { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// StartTimeout
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int StartTimeout { get; set; } = 10000;
|
||||
/// <summary>
|
||||
/// StartTimeout
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int StartTimeout { get; set; } = 10000;
|
||||
|
||||
/// <summary>
|
||||
/// Allow Partial Mapping (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowPartialMapping { get; set; }
|
||||
/// <summary>
|
||||
/// Allow Partial Mapping (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowPartialMapping { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The username needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminUsername { get; set; }
|
||||
/// <summary>
|
||||
/// The username needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminUsername { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The password needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminPassword { get; set; }
|
||||
/// <summary>
|
||||
/// The password needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The AzureAD Tenant needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminAzureADTenant { get; set; }
|
||||
/// <summary>
|
||||
/// The AzureAD Tenant needed for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminAzureADTenant { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The AzureAD Audience / Resource for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminAzureADAudience { get; set; }
|
||||
/// <summary>
|
||||
/// The AzureAD Audience / Resource for __admin access.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public string? AdminAzureADAudience { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The RequestLog expiration in hours (optional).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? RequestLogExpirationDuration { get; set; }
|
||||
/// <summary>
|
||||
/// The RequestLog expiration in hours (optional).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? RequestLogExpirationDuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The MaxRequestLog count (optional).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
/// <summary>
|
||||
/// The MaxRequestLog count (optional).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public int? MaxRequestLogCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<object>? PreWireMockMiddlewareInit { get; set; }
|
||||
/// <summary>
|
||||
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<object>? PreWireMockMiddlewareInit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<object>? PostWireMockMiddlewareInit { get; set; }
|
||||
/// <summary>
|
||||
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<object>? PostWireMockMiddlewareInit { get; set; }
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
/// <summary>
|
||||
/// Action which is called with IServiceCollection when ASP.NET Core DI is being configured. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The IWireMockLogger which logs Debug, Info, Warning or Error
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public IWireMockLogger Logger { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Handler to interact with the file system to read and write static mapping files.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public IFileSystemHandler FileSystemHandler { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Action which can be used to add additional Handlebars registrations. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow the usage of CSharpCodeMatcher (default is not allowed).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowCSharpCodeMatcher { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow a Body for all HTTP Methods. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowBodyForAllHttpMethods { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow only a HttpStatus Code in the response which is defined. (default set to false).
|
||||
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
|
||||
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable Json deserialization when processing requests. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableJsonBodyParsing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disable support for GZip and Deflate request body decompression. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableRequestBodyDecompressing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? HandleRequestsSynchronously { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Throw an exception when the <see cref="IMatcher"/> fails because of invalid input. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
|
||||
///
|
||||
/// X509StoreName and X509StoreLocation should be defined
|
||||
/// OR
|
||||
/// X509CertificateFilePath and X509CertificatePassword should be defined
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public WireMockCertificateSettings? CertificateSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines if custom CertificateSettings are defined
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
/// <summary>
|
||||
/// Action which is called with IServiceCollection when ASP.NET Core DI is being configured. [Optional]
|
||||
/// Client certificate mode for the server
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
|
||||
public ClientCertificateMode ClientCertificateMode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
|
||||
/// Whether to accept any client certificate
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
|
||||
public bool AcceptAnyClientCertificate { get; set; }
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The IWireMockLogger which logs Debug, Info, Warning or Error
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public IWireMockLogger Logger { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Defines the global IWebhookSettings to use.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public WebhookSettings? WebhookSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handler to interact with the file system to read and write static mapping files.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public IFileSystemHandler FileSystemHandler { get; set; } = null!;
|
||||
/// <summary>
|
||||
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to true).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? UseRegexExtended { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Action which can be used to add additional Handlebars registrations. [Optional]
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
[JsonIgnore]
|
||||
public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; }
|
||||
/// <summary>
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? SaveUnmatchedRequests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow the usage of CSharpCodeMatcher (default is not allowed).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowCSharpCodeMatcher { get; set; }
|
||||
/// <summary>
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow a Body for all HTTP Methods. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowBodyForAllHttpMethods { get; set; }
|
||||
/// <summary>
|
||||
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
|
||||
///
|
||||
/// Default value = "All".
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Allow only a HttpStatus Code in the response which is defined. (default set to false).
|
||||
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
|
||||
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
|
||||
/// <summary>
|
||||
/// Custom matcher mappings for static mappings
|
||||
/// </summary>
|
||||
[PublicAPI, JsonIgnore]
|
||||
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Set to true to disable Json deserialization when processing requests. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableJsonBodyParsing { get; set; }
|
||||
/// <summary>
|
||||
/// The <see cref="JsonSerializerSettings"/> used when the a JSON response is generated.
|
||||
/// </summary>
|
||||
[PublicAPI, JsonIgnore]
|
||||
public JsonSerializerSettings? JsonSerializerSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Disable support for GZip and Deflate request body decompression. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DisableRequestBodyDecompressing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Handle all requests synchronously. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? HandleRequestsSynchronously { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Throw an exception when the <see cref="IMatcher"/> fails because of invalid input. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? ThrowExceptionWhenMatcherFails { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
|
||||
///
|
||||
/// X509StoreName and X509StoreLocation should be defined
|
||||
/// OR
|
||||
/// X509CertificateFilePath and X509CertificatePassword should be defined
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public WireMockCertificateSettings? CertificateSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines if custom CertificateSettings are defined
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
|
||||
|
||||
/// <summary>
|
||||
/// Defines the global IWebhookSettings to use.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public WebhookSettings? WebhookSettings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to true).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? UseRegexExtended { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? SaveUnmatchedRequests { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
|
||||
///
|
||||
/// Default value = "All".
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Custom matcher mappings for static mappings
|
||||
/// </summary>
|
||||
[PublicAPI, JsonIgnore]
|
||||
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="JsonSerializerSettings"/> used when the a JSON response is generated.
|
||||
/// </summary>
|
||||
[PublicAPI, JsonIgnore]
|
||||
public JsonSerializerSettings? JsonSerializerSettings { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// The Culture to use.
|
||||
/// Currently used for:
|
||||
/// - Handlebars Transformer
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Logging;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
@@ -55,11 +59,14 @@ public static class WireMockServerSettingsParser
|
||||
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
|
||||
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
|
||||
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
|
||||
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport))
|
||||
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport)),
|
||||
Culture = parser.GetValue(nameof(WireMockServerSettings.Culture), strings => CultureInfoUtils.Parse(strings.FirstOrDefault()), CultureInfo.CurrentCulture)
|
||||
};
|
||||
|
||||
#if USE_ASPNETCORE
|
||||
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
|
||||
settings.ClientCertificateMode = parser.GetEnumValue(nameof(WireMockServerSettings.ClientCertificateMode), ClientCertificateMode.NoCertificate);
|
||||
settings.AcceptAnyClientCertificate = parser.GetBoolValue(nameof(WireMockServerSettings.AcceptAnyClientCertificate));
|
||||
#endif
|
||||
|
||||
var loggerType = parser.GetStringValue("WireMockLogger");
|
||||
@@ -105,7 +112,12 @@ public static class WireMockServerSettingsParser
|
||||
SaveMappingToFile = parser.GetBoolValue("SaveMappingToFile"),
|
||||
UseDefinedRequestMatchers = parser.GetBoolValue(nameof(ProxyAndRecordSettings.UseDefinedRequestMatchers)),
|
||||
AppendGuidToSavedMappingFile = parser.GetBoolValue(nameof(ProxyAndRecordSettings.AppendGuidToSavedMappingFile)),
|
||||
Url = proxyUrl!
|
||||
Url = proxyUrl!,
|
||||
SaveMappingSettings = new ProxySaveMappingSettings
|
||||
{
|
||||
StatusCodePattern = parser.GetStringValue("SaveMappingForStatusCodePattern", "*"),
|
||||
// HttpMethods = new ProxySaveMappingSetting<string[]>(parser.GetValues("DoNotSaveMappingForHttpMethods", new string[0]), MatchBehaviour.RejectOnMatch)
|
||||
}
|
||||
};
|
||||
|
||||
string? proxyAddress = parser.GetStringValue("WebProxyAddress");
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
using System;
|
||||
using HandlebarsDotNet;
|
||||
using HandlebarsDotNet.Helpers.Attributes;
|
||||
using HandlebarsDotNet.Helpers.Enums;
|
||||
using HandlebarsDotNet.Helpers.Helpers;
|
||||
using Stef.Validation;
|
||||
using WireMock.Handlers;
|
||||
|
||||
namespace WireMock.Transformers.Handlebars
|
||||
namespace WireMock.Transformers.Handlebars;
|
||||
|
||||
internal class FileHelpers : BaseHelpers, IHelpers
|
||||
{
|
||||
internal class FileHelpers : BaseHelpers, IHelpers
|
||||
private readonly IFileSystemHandler _fileSystemHandler;
|
||||
|
||||
public FileHelpers(IHandlebars context, IFileSystemHandler fileSystemHandler) : base(context)
|
||||
{
|
||||
private readonly IFileSystemHandler _fileSystemHandler;
|
||||
_fileSystemHandler = Guard.NotNull(fileSystemHandler);
|
||||
}
|
||||
|
||||
public FileHelpers(IHandlebars context, IFileSystemHandler fileSystemHandler) : base(context)
|
||||
{
|
||||
_fileSystemHandler = fileSystemHandler ?? throw new ArgumentNullException(nameof(fileSystemHandler));
|
||||
}
|
||||
|
||||
[HandlebarsWriter(WriterType.String, usage: HelperUsage.Both, passContext: true, name: "File")]
|
||||
public string Read(Context context, string path)
|
||||
{
|
||||
var templateFunc = Context.Compile(path);
|
||||
string transformed = templateFunc(context.Value);
|
||||
return _fileSystemHandler.ReadResponseBodyAsString(transformed);
|
||||
}
|
||||
[HandlebarsWriter(WriterType.String, usage: HelperUsage.Both, passContext: true, name: "File")]
|
||||
public string Read(Context context, string path)
|
||||
{
|
||||
var templateFunc = Context.Compile(path);
|
||||
string transformed = templateFunc(context.Value);
|
||||
return _fileSystemHandler.ReadResponseBodyAsString(transformed);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,35 @@
|
||||
using HandlebarsDotNet;
|
||||
using HandlebarsDotNet.Helpers.Extensions;
|
||||
using Stef.Validation;
|
||||
using WireMock.Handlers;
|
||||
|
||||
namespace WireMock.Transformers.Handlebars
|
||||
namespace WireMock.Transformers.Handlebars;
|
||||
|
||||
internal class HandlebarsContext : IHandlebarsContext
|
||||
{
|
||||
internal class HandlebarsContext : IHandlebarsContext
|
||||
public IHandlebars Handlebars { get; }
|
||||
|
||||
public IFileSystemHandler FileSystemHandler { get; }
|
||||
|
||||
public HandlebarsContext(IHandlebars handlebars, IFileSystemHandler fileSystemHandler)
|
||||
{
|
||||
public IHandlebars Handlebars { get; set; }
|
||||
Handlebars = Guard.NotNull(handlebars);
|
||||
FileSystemHandler = Guard.NotNull(fileSystemHandler);
|
||||
}
|
||||
|
||||
public IFileSystemHandler FileSystemHandler { get; set; }
|
||||
public string ParseAndRender(string text, object model)
|
||||
{
|
||||
var template = Handlebars.Compile(text);
|
||||
return template(model);
|
||||
}
|
||||
|
||||
public string ParseAndRender(string text, object model)
|
||||
public object? ParseAndEvaluate(string text, object model)
|
||||
{
|
||||
if (Handlebars.TryEvaluate(text, model, out var result) && result is not UndefinedBindingResult)
|
||||
{
|
||||
var template = Handlebars.Compile(text);
|
||||
return template(model);
|
||||
return result;
|
||||
}
|
||||
|
||||
return ParseAndRender(text, model);
|
||||
}
|
||||
}
|
||||
@@ -1,33 +1,30 @@
|
||||
using System;
|
||||
using HandlebarsDotNet;
|
||||
using Stef.Validation;
|
||||
using WireMock.Handlers;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Transformers.Handlebars;
|
||||
|
||||
internal class HandlebarsContextFactory : ITransformerContextFactory
|
||||
{
|
||||
private readonly IFileSystemHandler _fileSystemHandler;
|
||||
private readonly Action<IHandlebars, IFileSystemHandler>? _action;
|
||||
private readonly WireMockServerSettings _settings;
|
||||
|
||||
public HandlebarsContextFactory(IFileSystemHandler fileSystemHandler, Action<IHandlebars, IFileSystemHandler>? action)
|
||||
public HandlebarsContextFactory(WireMockServerSettings settings)
|
||||
{
|
||||
_fileSystemHandler = Guard.NotNull(fileSystemHandler);
|
||||
_action = action;
|
||||
_settings = Guard.NotNull(settings);
|
||||
}
|
||||
|
||||
public ITransformerContext Create()
|
||||
{
|
||||
var handlebars = HandlebarsDotNet.Handlebars.Create();
|
||||
|
||||
WireMockHandlebarsHelpers.Register(handlebars, _fileSystemHandler);
|
||||
|
||||
_action?.Invoke(handlebars, _fileSystemHandler);
|
||||
|
||||
return new HandlebarsContext
|
||||
var config = new HandlebarsConfiguration
|
||||
{
|
||||
Handlebars = handlebars,
|
||||
FileSystemHandler = _fileSystemHandler
|
||||
FormatProvider = _settings.Culture
|
||||
};
|
||||
var handlebars = HandlebarsDotNet.Handlebars.Create(config);
|
||||
|
||||
WireMockHandlebarsHelpers.Register(handlebars, _settings.FileSystemHandler);
|
||||
|
||||
_settings.HandlebarsRegistrationCallback?.Invoke(handlebars, _settings.FileSystemHandler);
|
||||
|
||||
return new HandlebarsContext(handlebars, _settings.FileSystemHandler);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user