mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-14 15:43:33 +01:00
Compare commits
26 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b1bd1b21b | ||
|
|
c1b23b615e | ||
|
|
5885324dfb | ||
|
|
e3f3e0a8f2 | ||
|
|
685d28db0b | ||
|
|
f6c5225fe0 | ||
|
|
b9019a2f61 | ||
|
|
b82dad2563 | ||
|
|
45d4e7077d | ||
|
|
19e95325fa | ||
|
|
371bfdc160 | ||
|
|
5c5e104f2c | ||
|
|
068fdf33e3 | ||
|
|
358590918e | ||
|
|
32afea5d30 | ||
|
|
50b3d58a01 | ||
|
|
35bf5e94a5 | ||
|
|
9fcc9ade10 | ||
|
|
865bbf2432 | ||
|
|
560540099e | ||
|
|
e5c4605020 | ||
|
|
124d29c86a | ||
|
|
08117e870b | ||
|
|
ddb181cc52 | ||
|
|
10fdd24fca | ||
|
|
be2ea67b89 |
38
CHANGELOG.md
38
CHANGELOG.md
@@ -1,3 +1,41 @@
|
||||
# 1.15.0 (22 October 2025)
|
||||
- [#1367](https://github.com/wiremock/WireMock.Net/pull/1367) - Fix WithProbability logic [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1370](https://github.com/wiremock/WireMock.Net/pull/1370) - Support Testcontainers 4.8.0 [bug] contributed by [MD-V](https://github.com/MD-V)
|
||||
- [#1126](https://github.com/wiremock/WireMock.Net/issues/1126) - Request matching WithProbability strange behaviour [bug]
|
||||
|
||||
# 1.14.0 (06 October 2025)
|
||||
- [#1362](https://github.com/wiremock/WireMock.Net/pull/1362) - Update ProxyUrlReplaceSettingsModel with TransformTemplate property [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1363](https://github.com/wiremock/WireMock.Net/pull/1363) - Add Tls13 [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1342](https://github.com/wiremock/WireMock.Net/issues/1342) - Facing SSL certificate validation error when using .NET 5 and above framework for some http services during recording [bug]
|
||||
- [#1360](https://github.com/wiremock/WireMock.Net/issues/1360) - ProxyURL path configuration [feature]
|
||||
|
||||
# 1.13.0 (28 September 2025)
|
||||
- [#1358](https://github.com/wiremock/WireMock.Net/pull/1358) - TypeLoader: implement Try methods [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1361](https://github.com/wiremock/WireMock.Net/pull/1361) - ProxyUrlTransformer [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.12.0 (30 August 2025)
|
||||
- [#1357](https://github.com/wiremock/WireMock.Net/pull/1357) - Upgrade Testcontainers to 4.7.0 [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1356](https://github.com/wiremock/WireMock.Net/issues/1356) - WireMock.Net 1.11.2 is not compatible with TestContainers 4.7.0 [bug]
|
||||
|
||||
# 1.11.2 (27 August 2025)
|
||||
- [#1352](https://github.com/wiremock/WireMock.Net/pull/1352) - Add additional try-catch to TypeLoader logic [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1354](https://github.com/wiremock/WireMock.Net/pull/1354) - Add System.Net.Http again to solve CVE warning [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1355](https://github.com/wiremock/WireMock.Net/pull/1355) - Revert JetBrains.Annotations and add System.Text.RegularExpressions t… [bug] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1071](https://github.com/wiremock/WireMock.Net/issues/1071) - Split WireMock into multiple nuget packages depending on features [feature]
|
||||
- [#1351](https://github.com/wiremock/WireMock.Net/issues/1351) - Version 1.11.0 seems to have a dependency to non-existing version of JetBrans.Annotations [bug]
|
||||
- [#1353](https://github.com/wiremock/WireMock.Net/issues/1353) - 1.11.0: NU1903: Warning As Error: Package 'System.Net.Http' 4.3.0 has a known high severity vulnerability, https://github.com/advisories/GHSA-7jgj-8wvc-jh57 [bug]
|
||||
|
||||
# 1.11.0 (26 August 2025)
|
||||
- [#1350](https://github.com/wiremock/WireMock.Net/pull/1350) - Create WireMock.Net.ProtoBuf project [feature] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
# 1.10.1 (22 August 2025)
|
||||
- [#1349](https://github.com/wiremock/WireMock.Net/pull/1349) - Add AtPath and AtAbsolutePath to Assertions projects [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1348](https://github.com/wiremock/WireMock.Net/issues/1348) - Support AtPath and AtAbsolutePath in WireMockAssertions [feature]
|
||||
|
||||
# 1.10.0 (18 August 2025)
|
||||
- [#1344](https://github.com/wiremock/WireMock.Net/pull/1344) - Add new package WireMock.Net.Extensions.Routing which provides minimal-API-style routing for WireMock.Net [feature] contributed by [GennadyGS](https://github.com/GennadyGS)
|
||||
- [#1340](https://github.com/wiremock/WireMock.Net/issues/1340) - Feature Request: Add minimal-API-style routing as Extension Package [feature]
|
||||
|
||||
# 1.9.1 (17 August 2025)
|
||||
- [#1345](https://github.com/wiremock/WireMock.Net/pull/1345) - Add TimesInSameState to MappingModel [feature] contributed by [StefH](https://github.com/StefH)
|
||||
- [#1347](https://github.com/wiremock/WireMock.Net/pull/1347) - Fix generating source code for Scenario and State [bug] contributed by [StefH](https://github.com/StefH)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<VersionPrefix>1.9.1</VersionPrefix>
|
||||
<Version>1.15.0</Version>
|
||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||
<PackageProjectUrl>https://github.com/wiremock/WireMock.Net</PackageProjectUrl>
|
||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||
@@ -58,7 +58,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.32.0.97167">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.15.0.120848">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
rem https://github.com/StefH/GitHubReleaseNotes
|
||||
|
||||
SET version=1.9.1
|
||||
SET version=1.15.0
|
||||
|
||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# 1.9.1 (17 August 2025)
|
||||
- #1345 Add TimesInSameState to MappingModel [feature]
|
||||
- #1347 Fix generating source code for Scenario and State [bug]
|
||||
- #1343 MappingModel allows to configure the times for WillSetStateTo [feature]
|
||||
- #1346 Mapping: generated C# code is missing InScenario and WillSetStateTo [bug]
|
||||
# 1.15.0 (22 October 2025)
|
||||
- #1367 Fix WithProbability logic [bug]
|
||||
- #1370 Support Testcontainers 4.8.0 [bug]
|
||||
- #1126 Request matching WithProbability strange behaviour [bug]
|
||||
|
||||
The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md
|
||||
12
README.md
12
README.md
@@ -1,7 +1,11 @@
|
||||
#  WireMock.Net
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics the functionality from the JAVA based [WireMock](http://wiremock.org).
|
||||
A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) which mimics functionality from the original Java based WireMock.
|
||||
|
||||
For more info, see also this WIKI page: [What is WireMock.Net](https://github.com/wiremock/WireMock.Net/wiki/What-Is-WireMock.Net).
|
||||
---
|
||||
|
||||
:books: <strong>Full documentation can now be found at <a href="https://wiremock.org/dotnet/" title="WireMock.Net docs">wiremock.org</a>
|
||||
|
||||
---
|
||||
|
||||
## :star: Key Features
|
||||
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||
@@ -51,17 +55,19 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
||||
| **WireMock.Net.xUnit** | [](https://www.nuget.org/packages/WireMock.Net.xUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit)
|
||||
| **WireMock.Net.TUnit** | [](https://www.nuget.org/packages/WireMock.Net.TUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.TUnit)
|
||||
| | | |
|
||||
| **WireMock.Net.Extensions.Routing** | [](https://www.nuget.org/packages/WireMock.Net.Extensions.Routing) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Extensions.Routing)
|
||||
| **WireMock.Net.Matchers.CSharpCode** | [](https://www.nuget.org/packages/WireMock.Net.Matchers.CSharpCode) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Matchers.CSharpCode)
|
||||
| **WireMock.Net.OpenApiParser** | [](https://www.nuget.org/packages/WireMock.Net.OpenApiParser) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.OpenApiParser)
|
||||
| **WireMock.Net.MimePart** | [](https://www.nuget.org/packages/WireMock.Net.MimePart) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.MimePart)
|
||||
| **WireMock.Net.GraphQL** | [](https://www.nuget.org/packages/WireMock.Net.GraphQL) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.GraphQL)
|
||||
| **WireMock.Net.ProtoBuf** | [](https://www.nuget.org/packages/WireMock.Net.ProtoBuf) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.ProtoBuf)
|
||||
| | | |
|
||||
| **WireMock.Net.RestClient** | [](https://www.nuget.org/packages/WireMock.Net.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
|
||||
| **WireMock.Org.RestClient** | [](https://www.nuget.org/packages/WireMock.Org.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
|
||||
|
||||
<br />
|
||||
|
||||
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart* and *WireMock.Net.GraphQL*.
|
||||
🔺 **WireMock.Net.Minimal** does not include *WireMock.Net.MimePart*, *WireMock.Net.GraphQL* and *WireMock.Net.ProtoBuf*.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -138,6 +138,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Tests.UsingNuG
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.GraphQL", "src\WireMock.Net.GraphQL\WireMock.Net.GraphQL.csproj", "{B6269AAC-170A-4346-8B9A-444DED3D9A45}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Routing.Tests", "test\WireMock.Net.Extensions.Routing.Tests\WireMock.Net.Extensions.Routing.Tests.csproj", "{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Extensions.Routing", "src\WireMock.Net.Extensions.Routing\WireMock.Net.Extensions.Routing.csproj", "{1E874C8F-08A2-493B-8421-619F9A6E9E77}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.ProtoBuf", "src\WireMock.Net.ProtoBuf\WireMock.Net.ProtoBuf.csproj", "{B47413AA-55D3-49A7-896A-17ADBFF72407}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -332,6 +338,18 @@ Global
|
||||
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B6269AAC-170A-4346-8B9A-444DED3D9A45}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -386,6 +404,9 @@ Global
|
||||
{1F80A6E6-D146-4E40-9EA8-49DB8494239F} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{BBA332C6-28A9-42E7-9C4D-A0816E52A198} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{B6269AAC-170A-4346-8B9A-444DED3D9A45} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{3FCBCA9C-9DB0-4A96-B47E-30470764CC9C} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{1E874C8F-08A2-493B-8421-619F9A6E9E77} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{B47413AA-55D3-49A7-896A-17ADBFF72407} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace WireMock.Net.ConsoleApplication
|
||||
}
|
||||
}
|
||||
|
||||
public static void Run()
|
||||
public static async Task RunAsync()
|
||||
{
|
||||
//RunSse();
|
||||
//RunOnLocal();
|
||||
@@ -290,25 +290,56 @@ namespace WireMock.Net.ConsoleApplication
|
||||
|
||||
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 pX = 0.80;
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.WithPath("todos")
|
||||
.UsingGet()
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyAsJson(todos.Values)
|
||||
);
|
||||
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||
.WithProbability(pX)
|
||||
.RespondWith(Response.Create().WithStatusCode(200).WithBody("X"));
|
||||
|
||||
server
|
||||
.Given(Request.Create()
|
||||
.UsingGet()
|
||||
.WithPath("todos")
|
||||
.WithParam("id")
|
||||
)
|
||||
.RespondWith(Response.Create()
|
||||
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
|
||||
);
|
||||
.Given(Request.Create().UsingGet().WithPath("/p"))
|
||||
.RespondWith(Response.Create().WithStatusCode(200).WithBody("default"));
|
||||
|
||||
// Act
|
||||
var requestUri = new Uri($"http://localhost:{server.Port}/p");
|
||||
var c = server.CreateClient();
|
||||
var xCount = 0;
|
||||
var defaultCount = 0;
|
||||
var tot = 1000;
|
||||
for (var i = 0; i < tot; i++)
|
||||
{
|
||||
var response = await c.GetAsync(requestUri);
|
||||
var value = await response.Content.ReadAsStringAsync();
|
||||
if (value == "X")
|
||||
{
|
||||
xCount++;
|
||||
}
|
||||
else if (value == "default")
|
||||
{
|
||||
defaultCount++;
|
||||
}
|
||||
}
|
||||
System.Console.WriteLine("X = {0} ; default = {1} ; pX = {2:0.00} ; valueX = {3:0.00}", xCount, defaultCount, pX, 1.0 * xCount / tot);
|
||||
return;
|
||||
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
|
||||
{
|
||||
HostingScheme = HostingScheme.HttpAndHttps,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using log4net;
|
||||
using log4net.Config;
|
||||
using log4net.Repository;
|
||||
@@ -14,10 +15,10 @@ static class Program
|
||||
private static readonly ILoggerRepository LogRepository = LogManager.GetRepository(Assembly.GetEntryAssembly());
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Program));
|
||||
|
||||
static void Main(params string[] args)
|
||||
static async Task Main(params string[] args)
|
||||
{
|
||||
XmlConfigurator.Configure(LogRepository, new FileInfo("log4net.config"));
|
||||
|
||||
MainApp.Run();
|
||||
await MainApp.RunAsync();
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Admin.Settings;
|
||||
|
||||
/// <summary>
|
||||
@@ -11,15 +13,25 @@ public class ProxyUrlReplaceSettingsModel
|
||||
/// <summary>
|
||||
/// The old path value to be replaced by the new path value
|
||||
/// </summary>
|
||||
public string OldValue { get; set; } = null!;
|
||||
public string? OldValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The new path value to replace the old value with
|
||||
/// </summary>
|
||||
public string NewValue { get; set; } = null!;
|
||||
public string? NewValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the case should be ignore when replacing.
|
||||
/// Defines if the case should be ignored when replacing.
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Holds the transformation template.
|
||||
/// </summary>
|
||||
public string? TransformTemplate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The transformer type.
|
||||
/// </summary>
|
||||
public TransformerType TransformerType { get; set; } = TransformerType.Handlebars;
|
||||
}
|
||||
@@ -15,12 +15,12 @@ public interface IResponseMessage
|
||||
/// <summary>
|
||||
/// The Body.
|
||||
/// </summary>
|
||||
IBodyData? BodyData { get; }
|
||||
IBodyData? BodyData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the body destination (Null, SameAsSource, String or Bytes).
|
||||
/// </summary>
|
||||
string? BodyDestination { get; }
|
||||
string? BodyDestination { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the body.
|
||||
@@ -30,27 +30,27 @@ public interface IResponseMessage
|
||||
/// <summary>
|
||||
/// Gets the Fault percentage.
|
||||
/// </summary>
|
||||
double? FaultPercentage { get; }
|
||||
double? FaultPercentage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The FaultType.
|
||||
/// </summary>
|
||||
FaultType FaultType { get; }
|
||||
FaultType FaultType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the headers.
|
||||
/// </summary>
|
||||
IDictionary<string, WireMockList<string>>? Headers { get; }
|
||||
IDictionary<string, WireMockList<string>>? Headers { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the trailing headers.
|
||||
/// </summary>
|
||||
IDictionary<string, WireMockList<string>>? TrailingHeaders { get; }
|
||||
IDictionary<string, WireMockList<string>>? TrailingHeaders { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the status code.
|
||||
/// </summary>
|
||||
object? StatusCode { get; }
|
||||
object? StatusCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds the header.
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- CVE-2018-8292 -->
|
||||
<!-- CVE-2018-8292 / https://github.com/advisories/GHSA-7jgj-8wvc-jh57 -->
|
||||
<PackageReference Include="System.Net.Http " Version="4.3.4" />
|
||||
|
||||
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
|
||||
@@ -50,34 +50,15 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<!--<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||
<PackageReference Include="AnyOf" Version="0.4.0" />-->
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="$(TargetFramework.StartsWith('netstandard')) and '$(TargetFramework)' != 'netstandard1.0'">
|
||||
<PackageReference Include="System.Security.Cryptography.X509Certificates" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<!--<ItemGroup>
|
||||
<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>-->
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.3' or '$(TargetFramework)' == 'net45' or '$(TargetFramework)' == 'net451' or '$(TargetFramework)' == 'net461'">
|
||||
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||
|
||||
<!--<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>-->
|
||||
|
||||
<!--<PackageReference Include="PolySharp" Version="1.14.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>-->
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -4,11 +4,11 @@ using Stef.Validation;
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides assertion methods to verify the number of calls made to a WireMock server.
|
||||
/// This class is used in the context of FluentAssertions.
|
||||
/// This class is used in the context of AwesomeAssertions.
|
||||
/// </summary>
|
||||
public class WireMockANumberOfCallsAssertions
|
||||
{
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, string> AtAbsolutePath(string absolutePath, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
_ = AtAbsolutePath(new ExactMatcher(true, absolutePath), because, becauseArgs);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, string>(this, absolutePath);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtAbsolutePath(IStringMatcher absolutePathMatcher, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
var (filter, condition) = BuildFilterAndCondition(request => absolutePathMatcher.IsPerfectMatch(request.AbsolutePath));
|
||||
|
||||
var absolutePath = absolutePathMatcher.GetPatterns().FirstOrDefault().GetPattern();
|
||||
|
||||
_chain
|
||||
.BecauseOf(because, becauseArgs)
|
||||
.Given(() => RequestMessages)
|
||||
.ForCondition(requests => CallsCount == 0 || requests.Any())
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the absolute path {0}{reason}, but no calls were made.",
|
||||
absolutePath
|
||||
)
|
||||
.Then
|
||||
.ForCondition(condition)
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the absolute path {0}{reason}, but didn't find it among the calls to {1}.",
|
||||
_ => absolutePath,
|
||||
requests => requests.Select(request => request.AbsolutePath)
|
||||
);
|
||||
|
||||
FilterRequestMessages(filter);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, absolutePathMatcher);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, string> AtPath(string path, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
_ = AtPath(new ExactMatcher(true, path), because, becauseArgs);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, string>(this, path);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtPath(IStringMatcher pathMatcher, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
var (filter, condition) = BuildFilterAndCondition(request => pathMatcher.IsPerfectMatch(request.Path));
|
||||
|
||||
var path = pathMatcher.GetPatterns().FirstOrDefault().GetPattern();
|
||||
|
||||
_chain
|
||||
.BecauseOf(because, becauseArgs)
|
||||
.Given(() => RequestMessages)
|
||||
.ForCondition(requests => CallsCount == 0 || requests.Any())
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the path {0}{reason}, but no calls were made.",
|
||||
path
|
||||
)
|
||||
.Then
|
||||
.ForCondition(condition)
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the path {0}{reason}, but didn't find it among the calls to {1}.",
|
||||
_ => path,
|
||||
requests => requests.Select(request => request.Path)
|
||||
);
|
||||
|
||||
FilterRequestMessages(filter);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, pathMatcher);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using WireMock.Extensions;
|
||||
using WireMock.Matchers;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
public partial class WireMockAssertions
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using System;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using System;
|
||||
using WireMock.Constants;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@ using WireMock.Matchers;
|
||||
using WireMock.Models;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#pragma warning disable CS1591
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using System;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
|
||||
@@ -7,7 +7,7 @@ using WireMock.Matchers;
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ using AwesomeAssertions.Primitives;
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
namespace WireMock.AwesomeAssertions;
|
||||
|
||||
/// <summary>
|
||||
/// Contains a number of methods to assert that the <see cref="IWireMockServer"/> is in the expected state.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
using WireMock.Server;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions
|
||||
namespace WireMock.AwesomeAssertions
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains extension methods for custom assertions in unit tests.
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Delegates;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a handler for processing WireMock.Net HTTP requests and returning a response asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="requestMessage">The incoming request message.</param>
|
||||
/// <returns>A task that resolves to a <see cref="ResponseMessage"/>.</returns>
|
||||
public delegate Task<IResponseMessage> WireMockHttpRequestHandler(IRequestMessage requestMessage);
|
||||
@@ -0,0 +1,10 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Delegates;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a middleware component for WireMock.Net HTTP request handling.
|
||||
/// </summary>
|
||||
/// <param name="next">The next request handler in the middleware pipeline.</param>
|
||||
/// <returns>A <see cref="WireMockHttpRequestHandler"/> that processes the request.</returns>
|
||||
public delegate WireMockHttpRequestHandler WireMockMiddleware(WireMockHttpRequestHandler next);
|
||||
@@ -0,0 +1,19 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
internal static class DictionaryExtensions
|
||||
{
|
||||
public static IDictionary<TKey, TValue> AddIf<TKey, TValue>(
|
||||
this IDictionary<TKey, TValue> source,
|
||||
bool condition,
|
||||
TKey key,
|
||||
TValue value,
|
||||
IEqualityComparer<TKey>? keyComparer = null)
|
||||
where TKey : notnull =>
|
||||
condition
|
||||
? source.ToImmutableDictionary(keyComparer).Add(key, value)
|
||||
: source;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
internal static class HttpResponseExtensions
|
||||
{
|
||||
public static async Task<ResponseMessage> ToResponseMessageAsync(
|
||||
this HttpResponse response)
|
||||
{
|
||||
var headers = response.Headers.ToDictionary(
|
||||
header => header.Key, header => new WireMockList<string?>(header.Value.ToArray()));
|
||||
return new()
|
||||
{
|
||||
Headers = headers!,
|
||||
BodyData = new BodyData
|
||||
{
|
||||
DetectedBodyType = BodyType.String,
|
||||
BodyAsString = await response.ReadBodyAsStringAsync(),
|
||||
},
|
||||
StatusCode = response.StatusCode,
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task<string> ReadBodyAsStringAsync(this HttpResponse response)
|
||||
{
|
||||
response.Body.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(response.Body);
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JsonConverter.Abstractions;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
internal static class RequestMessageExtensions
|
||||
{
|
||||
public static T? GetBodyAsJson<T>(
|
||||
this IRequestMessage requestMessage,
|
||||
IJsonConverter jsonConverter,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
requestMessage.Body is not null
|
||||
? jsonConverter.Deserialize<T>(requestMessage.Body, jsonOptions)
|
||||
: default;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
internal static class StringExtensions
|
||||
{
|
||||
public static string ToMatchFullStringRegex(this string regex) =>
|
||||
$"^{regex}$";
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Reflection;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
internal static class TaskExtensions
|
||||
{
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage(
|
||||
"Usage",
|
||||
"VSTHRD003:Avoid awaiting foreign Tasks",
|
||||
Justification = "Await is required here to transform base task to generic one.")]
|
||||
public static async Task<object?> ToGenericTaskAsync(this Task task)
|
||||
{
|
||||
await task;
|
||||
var taskType = task.GetType();
|
||||
if (!IsAssignableToGenericTaskType(taskType))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return task
|
||||
.GetType()
|
||||
.GetProperty("Result", BindingFlags.Instance | BindingFlags.Public)!
|
||||
.GetValue(task);
|
||||
}
|
||||
|
||||
private static bool IsAssignableToGenericTaskType(Type type)
|
||||
{
|
||||
if (type.IsGenericType &&
|
||||
type.GetGenericTypeDefinition() == typeof(Task<>) &&
|
||||
type.GetGenericArguments()[0] != Type.GetType("System.Threading.Tasks.VoidTaskResult"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return type.BaseType is not null && IsAssignableToGenericTaskType(type.BaseType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Net.Extensions.Routing.Delegates;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
internal static class WireMockHttpRequestHandlerExtensions
|
||||
{
|
||||
public static WireMockHttpRequestHandler UseMiddleware(
|
||||
this WireMockHttpRequestHandler handler, WireMockMiddleware middleware) =>
|
||||
middleware(handler);
|
||||
|
||||
public static WireMockHttpRequestHandler UseMiddlewareCollection(
|
||||
this WireMockHttpRequestHandler handler,
|
||||
IReadOnlyCollection<WireMockMiddleware> middlewareCollection) =>
|
||||
middlewareCollection.Aggregate(handler, UseMiddleware);
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Net.Extensions.Routing.Models;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for mapping HTTP routes to handlers in <see cref="WireMockRouter"/>.
|
||||
/// </summary>
|
||||
public static class WireMockRouterExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a GET request to a synchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapGet(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, object?> requestHandler) =>
|
||||
source.Map(HttpMethod.Get.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a GET request to an asynchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapGet(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
|
||||
source.Map(HttpMethod.Get.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a POST request to a synchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPost(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, object?> requestHandler) =>
|
||||
source.Map(HttpMethod.Post.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a POST request to an asynchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPost(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
|
||||
source.Map(HttpMethod.Post.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a POST request to a synchronous request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPost<TRequest>(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
source.Map(HttpMethod.Post.Method, pattern, requestHandler, jsonConverter, jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a POST request to an asynchronous request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPost<TRequest>(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, Task<object?>> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
source.Map(HttpMethod.Post.Method, pattern, requestHandler, jsonConverter, jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a PUT request to a synchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPut(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, object?> requestHandler) =>
|
||||
source.Map(HttpMethod.Put.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a PUT request to an asynchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPut(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
|
||||
source.Map(HttpMethod.Put.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a PUT request to a synchronous request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPut<TRequest>(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
source.Map(HttpMethod.Put.Method, pattern, requestHandler, jsonConverter, jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a PUT request to an asynchronous request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapPut<TRequest>(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, Task<object?>> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
source.Map(HttpMethod.Put.Method, pattern, requestHandler, jsonConverter, jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a DELETE request to a synchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapDelete(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, object?> requestHandler) =>
|
||||
source.Map(HttpMethod.Delete.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a DELETE request to an asynchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapDelete(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo, Task<object?>> requestHandler) =>
|
||||
source.Map(HttpMethod.Delete.Method, pattern, requestHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a DELETE request to a synchronous request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapDelete<TRequest>(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
source.Map(HttpMethod.Delete.Method, pattern, requestHandler, jsonConverter, jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Maps a DELETE request to an asynchronous request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="source">The router to extend.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public static WireMockRouter MapDelete<TRequest>(
|
||||
this WireMockRouter source,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, Task<object?>> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null) =>
|
||||
source.Map(HttpMethod.Delete.Method, pattern, requestHandler, jsonConverter, jsonOptions);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Net.Extensions.Routing.Delegates;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.ResponseBuilders;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for mapping HTTP requests to handlers in <see cref="WireMockServer"/>.
|
||||
/// </summary>
|
||||
public static class WireMockServerExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps a request to a WireMock.Net server using the specified method, path matcher, and request handler.
|
||||
/// </summary>
|
||||
/// <param name="source">The WireMock.Net server to extend.</param>
|
||||
/// <param name="method">The HTTP method to match.</param>
|
||||
/// <param name="pathMatcher">The matcher for the request path.</param>
|
||||
/// <param name="httpRequestHandler">The handler to process the request.</param>
|
||||
/// <returns>The current <see cref="WireMockServer"/> instance.</returns>
|
||||
public static WireMockServer Map(
|
||||
this WireMockServer source,
|
||||
string method,
|
||||
IStringMatcher pathMatcher,
|
||||
WireMockHttpRequestHandler httpRequestHandler)
|
||||
{
|
||||
source
|
||||
.Given(Request.Create().WithPath(pathMatcher).UsingMethod(method))
|
||||
.RespondWith(Response.Create().WithCallback(req => httpRequestHandler(req)));
|
||||
return source;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents request information for WireMock.Net routing, including the request message and route arguments.
|
||||
/// </summary>
|
||||
public class WireMockRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockRequestInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The incoming request message.</param>
|
||||
public WireMockRequestInfo(IRequestMessage request)
|
||||
{
|
||||
Request = request;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the incoming request message.
|
||||
/// </summary>
|
||||
public IRequestMessage Request { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or initializes the route arguments extracted from the request path.
|
||||
/// </summary>
|
||||
public IDictionary<string, object> RouteArgs { get; init; } =
|
||||
new Dictionary<string, object>();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents request information with a strongly-typed deserialized body for WireMock.Net routing.
|
||||
/// </summary>
|
||||
/// <typeparam name="TBody">The type of the deserialized request body.</typeparam>
|
||||
public sealed class WireMockRequestInfo<TBody> : WireMockRequestInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockRequestInfo{TBody}"/> class.
|
||||
/// </summary>
|
||||
/// <param name="request">The incoming request message.</param>
|
||||
public WireMockRequestInfo(IRequestMessage request)
|
||||
: base(request)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or initializes the deserialized request body.
|
||||
/// </summary>
|
||||
public TBody? Body { get; init; }
|
||||
}
|
||||
138
src/WireMock.Net.Extensions.Routing/README.md
Normal file
138
src/WireMock.Net.Extensions.Routing/README.md
Normal file
@@ -0,0 +1,138 @@
|
||||
# WireMock.Net.Extensions.Routing
|
||||
|
||||
**WireMock.Net.Extensions.Routing** extends [WireMock.Net](https://github.com/wiremock/wiremock) with modern, minimal-API-style routing for .NET. It provides extension methods for expressive, maintainable, and testable HTTP routing, inspired by [ASP.NET Core Minimal APIs](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-9.0).
|
||||
|
||||
---
|
||||
|
||||
## Motivation
|
||||
|
||||
While [WireMock.Net](https://github.com/WireMock-Net/WireMock.Net) is a powerful tool for HTTP mocking in .NET, its native API for defining routes and request handlers can be verbose and require significant boilerplate. Setting up even simple endpoints often involves multiple chained method calls, manual parsing of request data, and repetitive configuration, which can make tests harder to read and maintain.
|
||||
|
||||
**WireMock.Net.Extensions.Routing** addresses these pain points by introducing a concise, fluent, and minimal-API-inspired approach to routing. This makes your test code:
|
||||
|
||||
- **More readable:** Route definitions are clear and expressive, closely resembling production minimal APIs.
|
||||
- **Easier to maintain:** Less boilerplate means fewer places for errors and easier refactoring.
|
||||
- **Faster to write:** Define routes and handlers in a single line, with strong typing and async support.
|
||||
|
||||
### Example: Native WireMock.Net vs. WireMock.Net.Extensions.Routing
|
||||
|
||||
#### Native WireMock.Net
|
||||
|
||||
```csharp
|
||||
server.Given(
|
||||
Request.Create().WithPath("/hello").UsingGet()
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create().WithBody("Hello, world!")
|
||||
);
|
||||
|
||||
server.Given(
|
||||
Request.Create().WithPath("/user/*").UsingGet()
|
||||
)
|
||||
.RespondWith(
|
||||
Response.Create().WithCallback(request =>
|
||||
{
|
||||
var id = request.PathSegments[1];
|
||||
// ...fetch user by id...
|
||||
return new ResponseMessage { Body = $"User: {id}" };
|
||||
})
|
||||
);
|
||||
```
|
||||
|
||||
#### With WireMock.Net.Extensions.Routing
|
||||
|
||||
```csharp
|
||||
router.MapGet("/hello", _ => "Hello, world!");
|
||||
|
||||
router.MapGet("/user/{id:int}", requestInfo =>
|
||||
{
|
||||
var id = requestInfo.RouteArgs["id"];
|
||||
// ...fetch user by id...
|
||||
return $"User: {id}";
|
||||
});
|
||||
```
|
||||
|
||||
With **WireMock.Net.Extensions.Routing**, you get:
|
||||
|
||||
- Minimal, one-line route definitions
|
||||
- Typed route parameters (e.g., `{id:int}`)
|
||||
- Direct access to parsed route arguments and request bodies
|
||||
- Async handler support
|
||||
|
||||
This leads to more maintainable, scalable, and production-like test code.
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- Minimal API-style route definitions for WireMock.Net
|
||||
- Strongly-typed request handling
|
||||
- Routing parameters with constraints (`int` and `string` are currently supported)
|
||||
- Asynchronous handlers
|
||||
- Fluent, composable routing extensions
|
||||
- Easy integration with existing WireMock.Net servers
|
||||
- .NET 8+ support
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
Install from NuGet:
|
||||
|
||||
```shell
|
||||
dotnet add package WireMock.Net.Extensions.Routing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```csharp
|
||||
using System.Net.Http.Json;
|
||||
using WireMock.Net.Extensions.Routing;
|
||||
using WireMock.Net.Extensions.Routing.Extensions;
|
||||
using WireMock.Server;
|
||||
|
||||
var server = WireMockServer.Start();
|
||||
var router = new WireMockRouter(server);
|
||||
|
||||
router.MapGet("/hello", _ => "Hello, world!");
|
||||
|
||||
using var client = server.CreateClient();
|
||||
var result = await client.GetFromJsonAsync<string>("/hello");
|
||||
// Hello, world!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usage
|
||||
|
||||
### Routing with route parameters
|
||||
|
||||
```csharp
|
||||
router.MapGet("/user/{id:int}", async requestInfo => {
|
||||
var userId = requestInfo.RouteArgs["id"];
|
||||
// var user = await ...
|
||||
return user;
|
||||
});
|
||||
```
|
||||
|
||||
### Strongly-Typed Request Info
|
||||
|
||||
```csharp
|
||||
router.MapPost<Item>("/api/items", requestInfo => {
|
||||
var item = requestInfo.Body!;
|
||||
// process item
|
||||
return Results.Json(new { success = true });
|
||||
});
|
||||
```
|
||||
|
||||
### Supported Methods
|
||||
|
||||
- `MapGet`, `MapPost`, `MapPut`, `MapDelete`
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API Reference](./src/WireMock.Net.Extensions.Routing/)
|
||||
- [WireMock.Net Documentation](https://github.com/WireMock-Net/WireMock.Net)
|
||||
107
src/WireMock.Net.Extensions.Routing/Utils/RoutePattern.cs
Normal file
107
src/WireMock.Net.Extensions.Routing/Utils/RoutePattern.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using WireMock.Net.Extensions.Routing.Extensions;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing.Utils;
|
||||
|
||||
internal static class RoutePattern
|
||||
{
|
||||
private static readonly Regex ArgRegex =
|
||||
new(@"{(?'name'\w+)(?::(?'type'\w+))?}", RegexOptions.Compiled);
|
||||
|
||||
public static IDictionary<string, object> GetArgs(string pattern, string route) =>
|
||||
TryGetArgs(pattern, route, out var args)
|
||||
? args
|
||||
: throw new InvalidOperationException(
|
||||
$"Url {route} does not match route pattern {pattern}");
|
||||
|
||||
public static bool TryGetArgs(
|
||||
string pattern, string route, [NotNullWhen(true)] out IDictionary<string, object>? args)
|
||||
{
|
||||
var regex = new Regex(ToRegex(pattern), RegexOptions.IgnoreCase);
|
||||
var match = regex.Match(route);
|
||||
if (!match.Success)
|
||||
{
|
||||
args = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var routeArgTypeMap = GetArgTypeMap(pattern);
|
||||
args = match.Groups
|
||||
.Cast<Group>()
|
||||
.Where(g => g.Index > 0)
|
||||
.ToDictionary(g => g.Name, g => routeArgTypeMap[g.Name].Parse(g.Value));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string ToRegex(string pattern)
|
||||
{
|
||||
return ArgRegex
|
||||
.Replace(pattern, m => $"(?'{m.Groups["name"].Value}'{GetArgMatchingRegex(m)})")
|
||||
.ToMatchFullStringRegex();
|
||||
|
||||
static string GetArgMatchingRegex(Match match) =>
|
||||
ArgType.GetByName(match.Groups["type"].Value).GetRegex();
|
||||
}
|
||||
|
||||
private static IDictionary<string, ArgType> GetArgTypeMap(string pattern) =>
|
||||
ArgRegex
|
||||
.Matches(pattern)
|
||||
.ToDictionary(
|
||||
m => m.Groups["name"].Value, m => ArgType.GetByName(m.Groups["type"].Value));
|
||||
|
||||
private abstract record ArgType
|
||||
{
|
||||
private ArgType(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public static ArgType String { get; } = new StringArgType();
|
||||
|
||||
public static ArgType Int { get; } = new IntArgType();
|
||||
|
||||
private static IReadOnlyCollection<ArgType> All { get; } =
|
||||
typeof(ArgType)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(p => p.PropertyType.IsAssignableTo(typeof(ArgType)))
|
||||
.Select(p => p.GetValue(null))
|
||||
.Cast<ArgType>()
|
||||
.ToList();
|
||||
|
||||
private static IReadOnlyDictionary<string, ArgType> MapByName { get; } =
|
||||
All.ToDictionary(x => x.Name);
|
||||
|
||||
public static ArgType GetByName(string name) =>
|
||||
GetByNameOrDefault(name)
|
||||
?? throw new InvalidOperationException($"Route argument type {name} is not found");
|
||||
|
||||
public static ArgType? GetByNameOrDefault(string name) =>
|
||||
!string.IsNullOrEmpty(name)
|
||||
? MapByName.GetValueOrDefault(name)
|
||||
: String;
|
||||
|
||||
public abstract object Parse(string input);
|
||||
|
||||
public abstract string GetRegex();
|
||||
|
||||
private sealed record StringArgType() : ArgType("string")
|
||||
{
|
||||
public override object Parse(string input) => input;
|
||||
|
||||
public override string GetRegex() => @".*";
|
||||
}
|
||||
|
||||
private sealed record IntArgType() : ArgType("int")
|
||||
{
|
||||
public override object Parse(string input) => int.Parse(input);
|
||||
|
||||
public override string GetRegex() => @"-?\d+";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<Description>WireMock.Net.Routing extends WireMock.Net with modern, minimal-API-style routing for .NET</Description>
|
||||
<Authors>Gennadii Saltyshchak</Authors>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>tdd;mock;http;wiremock;test;server;unittest;routing;minimalapi</PackageTags>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<!--<TreatWarningsAsErrors>true</TreatWarningsAsErrors>-->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\..\README.md" Link="README.md" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JsonConverter.Newtonsoft.Json" Version="0.7.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.Minimal\WireMock.Net.Minimal.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
153
src/WireMock.Net.Extensions.Routing/WireMockRouter.cs
Normal file
153
src/WireMock.Net.Extensions.Routing/WireMockRouter.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using JsonConverter.Abstractions;
|
||||
using JsonConverter.Newtonsoft.Json;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Net.Extensions.Routing.Delegates;
|
||||
using WireMock.Net.Extensions.Routing.Extensions;
|
||||
using WireMock.Net.Extensions.Routing.Models;
|
||||
using WireMock.Net.Extensions.Routing.Utils;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing;
|
||||
|
||||
/// <summary>
|
||||
/// Provides routing and request mapping functionality for WireMock.Net,
|
||||
/// mimicking ASP.NET Core Minimal APIs routing style.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="WireMockRouter"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="server">The WireMock.Net server instance.</param>
|
||||
public sealed class WireMockRouter(WireMockServer server)
|
||||
{
|
||||
private readonly WireMockServer _server = server;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or initializes the collection of middleware for the router.
|
||||
/// </summary>
|
||||
public IReadOnlyCollection<WireMockMiddleware> MiddlewareCollection { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Gets or initializes the default <see cref="IJsonConverter"/> [optional].
|
||||
/// </summary>
|
||||
public IJsonConverter? DefaultJsonConverter { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or initializes the default <see cref="JsonConverterOptions"/> [optional].
|
||||
/// </summary>
|
||||
public JsonConverterOptions? DefaultJsonOptions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps a route to a synchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="method">The HTTP method.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public WireMockRouter Map(
|
||||
string method, string pattern, Func<WireMockRequestInfo, object?> requestHandler)
|
||||
{
|
||||
return Map(method, pattern, CreateResponse);
|
||||
|
||||
object? CreateResponse(IRequestMessage request) =>
|
||||
requestHandler(CreateRequestInfo(request, pattern));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a route to an asynchronous request handler.
|
||||
/// </summary>
|
||||
/// <param name="method">The HTTP method.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The asynchronous request handler function.</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public WireMockRouter Map(
|
||||
string method, string pattern, Func<WireMockRequestInfo, Task<object?>> requestHandler)
|
||||
{
|
||||
return Map(method, pattern, CreateResponseAsync);
|
||||
|
||||
Task<object?> CreateResponseAsync(IRequestMessage request) =>
|
||||
requestHandler(CreateRequestInfo(request, pattern));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps a route to a request handler with a typed body.
|
||||
/// </summary>
|
||||
/// <typeparam name="TRequest">The type of the request body.</typeparam>
|
||||
/// <param name="method">The HTTP method.</param>
|
||||
/// <param name="pattern">The route pattern.</param>
|
||||
/// <param name="requestHandler">The request handler function.</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="jsonOptions">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>The current <see cref="WireMockRouter"/> instance.</returns>
|
||||
public WireMockRouter Map<TRequest>(
|
||||
string method,
|
||||
string pattern,
|
||||
Func<WireMockRequestInfo<TRequest>, object?> requestHandler,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null)
|
||||
{
|
||||
return Map(method, pattern, CreateBody);
|
||||
|
||||
object? CreateBody(IRequestMessage request) =>
|
||||
requestHandler(CreateRequestInfo<TRequest>(request, pattern, jsonConverter, jsonOptions));
|
||||
}
|
||||
|
||||
private static WireMockRequestInfo CreateRequestInfo(IRequestMessage request, string pattern) =>
|
||||
new(request)
|
||||
{
|
||||
RouteArgs = RoutePattern.GetArgs(pattern, request.Path),
|
||||
};
|
||||
|
||||
private static WireMockHttpRequestHandler CreateHttpRequestHandler(
|
||||
Func<IRequestMessage, object?> requestHandler) =>
|
||||
request => CreateResponseMessageAsync(requestHandler(request));
|
||||
|
||||
private static async Task<IResponseMessage> CreateResponseMessageAsync(object? response)
|
||||
{
|
||||
var awaitedResponse = response is Task task
|
||||
? await task.ToGenericTaskAsync()
|
||||
: response;
|
||||
var result = awaitedResponse as IResult ?? Results.Ok(awaitedResponse);
|
||||
var httpContext = CreateHttpContext();
|
||||
await result.ExecuteAsync(httpContext);
|
||||
return await httpContext.Response.ToResponseMessageAsync();
|
||||
}
|
||||
|
||||
private static HttpContext CreateHttpContext() =>
|
||||
new DefaultHttpContext
|
||||
{
|
||||
RequestServices = new ServiceCollection().AddLogging().BuildServiceProvider(),
|
||||
Response = { Body = new MemoryStream() },
|
||||
};
|
||||
|
||||
private WireMockRequestInfo<TRequest> CreateRequestInfo<TRequest>(
|
||||
IRequestMessage request,
|
||||
string pattern,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? jsonOptions = null)
|
||||
{
|
||||
var requestInfo = CreateRequestInfo(request, pattern);
|
||||
var establishedJsonConverter =
|
||||
jsonConverter ?? DefaultJsonConverter ?? new NewtonsoftJsonConverter();
|
||||
var establishedJsonOptions = jsonOptions ?? DefaultJsonOptions;
|
||||
return new WireMockRequestInfo<TRequest>(requestInfo.Request)
|
||||
{
|
||||
RouteArgs = requestInfo.RouteArgs,
|
||||
Body = requestInfo.Request.GetBodyAsJson<TRequest>(
|
||||
establishedJsonConverter, establishedJsonOptions),
|
||||
};
|
||||
}
|
||||
|
||||
private WireMockRouter Map(
|
||||
string method, string pattern, Func<IRequestMessage, object?> requestHandler)
|
||||
{
|
||||
var matcher = new RegexMatcher(RoutePattern.ToRegex(pattern), ignoreCase: true);
|
||||
var httpRequestHandler =
|
||||
CreateHttpRequestHandler(requestHandler).UseMiddlewareCollection(MiddlewareCollection);
|
||||
_server.Map(method, matcher, httpRequestHandler);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using JsonConverter.Abstractions;
|
||||
using WireMock.Net.Extensions.Routing.Delegates;
|
||||
using WireMock.Server;
|
||||
|
||||
namespace WireMock.Net.Extensions.Routing;
|
||||
|
||||
/// <summary>
|
||||
/// Provides a builder for configuring and creating a <see cref="WireMockRouter"/> with middleware and JSON settings.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="WireMockServerRouterBuilder"/> class.
|
||||
/// </remarks>
|
||||
/// <param name="server">The WireMock.Net server instance.</param>
|
||||
public sealed class WireMockServerRouterBuilder(WireMockServer server)
|
||||
{
|
||||
private readonly WireMockServer _server = server;
|
||||
|
||||
private readonly ConcurrentQueue<WireMockMiddleware> _middlewareCollection = new();
|
||||
|
||||
private IJsonConverter? _defaultJsonConverter;
|
||||
|
||||
private JsonConverterOptions? _defaultJsonOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Builds a <see cref="WireMockRouter"/> with the configured middleware and JSON settings.
|
||||
/// </summary>
|
||||
/// <returns>The configured <see cref="WireMockRouter"/>.</returns>
|
||||
public WireMockRouter Build() =>
|
||||
new(_server)
|
||||
{
|
||||
MiddlewareCollection = _middlewareCollection,
|
||||
DefaultJsonConverter = _defaultJsonConverter,
|
||||
DefaultJsonOptions = _defaultJsonOptions,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Adds a middleware to the router builder.
|
||||
/// </summary>
|
||||
/// <param name="middleware">The middleware to add.</param>
|
||||
/// <returns>The current <see cref="WireMockServerRouterBuilder"/> instance.</returns>
|
||||
public WireMockServerRouterBuilder Use(WireMockMiddleware middleware)
|
||||
{
|
||||
_middlewareCollection.Enqueue(middleware);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default <see cref="IJsonConverter"/>.
|
||||
/// </summary>
|
||||
/// <param name="defaultJsonConverter">the default <see cref="IJsonConverter"/></param>
|
||||
/// <returns>The current <see cref="WireMockServerRouterBuilder"/> instance.</returns>
|
||||
public WireMockServerRouterBuilder WithDefaultJsonConverter(
|
||||
IJsonConverter? defaultJsonConverter)
|
||||
{
|
||||
_defaultJsonConverter = defaultJsonConverter;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default <see cref="JsonConverterOptions"/> [optional].
|
||||
/// </summary>
|
||||
/// <param name="defaultJsonOptions">the default <see cref="JsonConverterOptions"/> [optional]</param>
|
||||
/// <returns>The current <see cref="WireMockServerRouterBuilder"/> instance.</returns>
|
||||
public WireMockServerRouterBuilder WithDefaultJsonOptions(
|
||||
JsonConverterOptions? defaultJsonOptions)
|
||||
{
|
||||
_defaultJsonOptions = defaultJsonOptions;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using WireMock.Extensions;
|
||||
using WireMock.Matchers;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace WireMock.FluentAssertions;
|
||||
|
||||
#pragma warning disable CS1591
|
||||
public partial class WireMockAssertions
|
||||
{
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, string> AtAbsolutePath(string absolutePath, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
_ = AtAbsolutePath(new ExactMatcher(true, absolutePath), because, becauseArgs);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, string>(this, absolutePath);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtAbsolutePath(IStringMatcher absolutePathMatcher, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
var (filter, condition) = BuildFilterAndCondition(request => absolutePathMatcher.IsPerfectMatch(request.AbsolutePath));
|
||||
|
||||
var absolutePath = absolutePathMatcher.GetPatterns().FirstOrDefault().GetPattern();
|
||||
|
||||
Execute.Assertion
|
||||
.BecauseOf(because, becauseArgs)
|
||||
.Given(() => RequestMessages)
|
||||
.ForCondition(requests => CallsCount == 0 || requests.Any())
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the absolute path {0}{reason}, but no calls were made.",
|
||||
absolutePath
|
||||
)
|
||||
.Then
|
||||
.ForCondition(condition)
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the absolute path {0}{reason}, but didn't find it among the calls to {1}.",
|
||||
_ => absolutePath,
|
||||
requests => requests.Select(request => request.AbsolutePath)
|
||||
);
|
||||
|
||||
FilterRequestMessages(filter);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, absolutePathMatcher);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, string> AtPath(string path, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
_ = AtPath(new ExactMatcher(true, path), because, becauseArgs);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, string>(this, path);
|
||||
}
|
||||
|
||||
[CustomAssertion]
|
||||
public AndWhichConstraint<WireMockAssertions, IStringMatcher> AtPath(IStringMatcher pathMatcher, string because = "", params object[] becauseArgs)
|
||||
{
|
||||
var (filter, condition) = BuildFilterAndCondition(request => pathMatcher.IsPerfectMatch(request.Path));
|
||||
|
||||
var path = pathMatcher.GetPatterns().FirstOrDefault().GetPattern();
|
||||
|
||||
Execute.Assertion
|
||||
.BecauseOf(because, becauseArgs)
|
||||
.Given(() => RequestMessages)
|
||||
.ForCondition(requests => CallsCount == 0 || requests.Any())
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the path {0}{reason}, but no calls were made.",
|
||||
path
|
||||
)
|
||||
.Then
|
||||
.ForCondition(condition)
|
||||
.FailWith(
|
||||
"Expected {context:wiremockserver} to have been called at address matching the path {0}{reason}, but didn't find it among the calls to {1}.",
|
||||
_ => path,
|
||||
requests => requests.Select(request => request.Path)
|
||||
);
|
||||
|
||||
FilterRequestMessages(filter);
|
||||
|
||||
return new AndWhichConstraint<WireMockAssertions, IStringMatcher>(this, pathMatcher);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -33,16 +32,25 @@ internal class MimeKitUtils : IMimeKitUtils
|
||||
StartsWithMultiPart(contentTypeHeader)
|
||||
)
|
||||
{
|
||||
var bytes = requestMessage.BodyData?.DetectedBodyType switch
|
||||
byte[] bytes;
|
||||
|
||||
switch (requestMessage.BodyData?.DetectedBodyType)
|
||||
{
|
||||
// If the body is bytes, use the BodyAsBytes to match on.
|
||||
BodyType.Bytes => requestMessage.BodyData.BodyAsBytes!,
|
||||
case BodyType.Bytes:
|
||||
bytes = requestMessage.BodyData.BodyAsBytes!;
|
||||
break;
|
||||
|
||||
// If the body is a String or MultiPart, use the BodyAsString to match on.
|
||||
BodyType.String or BodyType.MultiPart => Encoding.UTF8.GetBytes(requestMessage.BodyData.BodyAsString!),
|
||||
case BodyType.String or BodyType.MultiPart:
|
||||
bytes = Encoding.UTF8.GetBytes(requestMessage.BodyData.BodyAsString!);
|
||||
break;
|
||||
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
// Else return false.
|
||||
default:
|
||||
mimeMessageData = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var fixedBytes = FixBytes(bytes, contentTypeHeader[0]);
|
||||
|
||||
|
||||
@@ -15,8 +15,13 @@ internal static class HttpClientBuilder
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
CheckCertificateRevocationList = false,
|
||||
#if NET5_0_OR_GREATER
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls13 | System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
#else
|
||||
SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls11 | System.Security.Authentication.SslProtocols.Tls,
|
||||
#endif
|
||||
ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true,
|
||||
|
||||
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
|
||||
};
|
||||
#elif NET46
|
||||
@@ -62,7 +67,10 @@ internal static class HttpClientBuilder
|
||||
}
|
||||
}
|
||||
|
||||
#if !NETSTANDARD1_3
|
||||
#if NET5_0_OR_GREATER
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;
|
||||
#elif !NETSTANDARD1_3
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
|
||||
ServicePointManager.ServerCertificateValidationCallback = (message, cert, chain, errors) => true;
|
||||
#endif
|
||||
|
||||
@@ -11,24 +11,17 @@ using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Transformers.Handlebars;
|
||||
using WireMock.Transformers.Scriban;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Http;
|
||||
|
||||
internal class WebhookSender
|
||||
internal class WebhookSender(WireMockServerSettings settings)
|
||||
{
|
||||
private const string ClientIp = "::1";
|
||||
private static readonly ThreadLocal<Random> Random = new(() => new Random(DateTime.UtcNow.Millisecond));
|
||||
|
||||
private readonly WireMockServerSettings _settings;
|
||||
|
||||
public WebhookSender(WireMockServerSettings settings)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
}
|
||||
private readonly WireMockServerSettings _settings = Guard.NotNull(settings);
|
||||
|
||||
public async Task<HttpResponseMessage> SendAsync(
|
||||
HttpClient client,
|
||||
@@ -49,24 +42,7 @@ internal class WebhookSender
|
||||
string requestUrl;
|
||||
if (webhookRequest.UseTransformer == true)
|
||||
{
|
||||
ITransformer transformer;
|
||||
switch (webhookRequest.TransformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
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(_settings, factoryDotLiquid);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotImplementedException($"TransformerType '{webhookRequest.TransformerType}' is not supported.");
|
||||
}
|
||||
|
||||
var transformer = TransformerFactory.Create(webhookRequest.TransformerType, _settings);
|
||||
bodyData = transformer.TransformBody(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.BodyData, webhookRequest.TransformerReplaceNodeOptions);
|
||||
headers = transformer.TransformHeaders(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Headers);
|
||||
requestUrl = transformer.TransformString(mapping, originalRequestMessage, originalResponseMessage, webhookRequest.Url);
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace WireMock.Matchers.Request;
|
||||
/// </summary>
|
||||
public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
{
|
||||
private static readonly IMimeKitUtils MimeKitUtils = TypeLoader.LoadStaticInstance<IMimeKitUtils>();
|
||||
private readonly IMimeKitUtils _mimeKitUtils = LoadMimeKitUtils();
|
||||
|
||||
/// <summary>
|
||||
/// The matchers.
|
||||
@@ -62,7 +62,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
}
|
||||
|
||||
if (!MimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
|
||||
if (!_mimeKitUtils.TryGetMimeMessage(requestMessage, out var message))
|
||||
{
|
||||
return requestMatchResult.AddScore(GetType(), score, null);
|
||||
}
|
||||
@@ -96,4 +96,14 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
|
||||
|
||||
return requestMatchResult.AddScore(GetType(), score, exception);
|
||||
}
|
||||
|
||||
private static IMimeKitUtils LoadMimeKitUtils()
|
||||
{
|
||||
if (TypeLoader.TryLoadStaticInstance<IMimeKitUtils>(out var mimeKitUtils))
|
||||
{
|
||||
return mimeKitUtils;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("MimeKit is required for RequestMessageMultiPartMatcher. Please install the WireMock.Net.MimePart package.");
|
||||
}
|
||||
}
|
||||
@@ -177,11 +177,13 @@ namespace WireMock.Owin.Mappers
|
||||
var jsonBody = JsonConvert.SerializeObject(bodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
|
||||
return (bodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
|
||||
|
||||
#if PROTOBUF
|
||||
case BodyType.ProtoBuf:
|
||||
var protoDefinitions = bodyData.ProtoDefinition?.Invoke().Texts;
|
||||
return await ProtoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false);
|
||||
#endif
|
||||
if (TypeLoader.TryLoadStaticInstance<IProtoBufUtils>(out var protoBufUtils))
|
||||
{
|
||||
var protoDefinitions = bodyData.ProtoDefinition?.Invoke().Texts;
|
||||
return await protoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false);
|
||||
}
|
||||
break;
|
||||
|
||||
case BodyType.Bytes:
|
||||
return bodyData.BodyAsBytes;
|
||||
|
||||
@@ -9,16 +9,10 @@ using WireMock.Services;
|
||||
|
||||
namespace WireMock.Owin;
|
||||
|
||||
internal class MappingMatcher : IMappingMatcher
|
||||
internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1) : IMappingMatcher
|
||||
{
|
||||
private readonly IWireMockMiddlewareOptions _options;
|
||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1;
|
||||
|
||||
public MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDoubleBetween0And1 randomizerDoubleBetween0And1)
|
||||
{
|
||||
_options = Guard.NotNull(options);
|
||||
_randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||
}
|
||||
private readonly IWireMockMiddlewareOptions _options = Guard.NotNull(options);
|
||||
private readonly IRandomizerDoubleBetween0And1 _randomizerDoubleBetween0And1 = Guard.NotNull(randomizerDoubleBetween0And1);
|
||||
|
||||
public (MappingMatcherResult? Match, MappingMatcherResult? Partial) FindBestMatch(RequestMessage request)
|
||||
{
|
||||
@@ -28,7 +22,7 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var mappings = _options.Mappings.Values
|
||||
.Where(m => m.TimeSettings.IsValid())
|
||||
.Where(m => m.Probability is null || m.Probability <= _randomizerDoubleBetween0And1.Generate())
|
||||
.Where(m => m.Probability is null || _randomizerDoubleBetween0And1.Generate() <= m.Probability)
|
||||
.ToArray();
|
||||
|
||||
foreach (var mapping in mappings)
|
||||
@@ -41,10 +35,10 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var exceptions = mappingMatcherResult.RequestMatchResult.MatchDetails
|
||||
.Where(md => md.Exception != null)
|
||||
.Select(md => md.Exception)
|
||||
.Select(md => md.Exception!)
|
||||
.ToArray();
|
||||
|
||||
if (!exceptions.Any())
|
||||
if (exceptions.Length == 0)
|
||||
{
|
||||
possibleMappings.Add(mappingMatcherResult);
|
||||
}
|
||||
@@ -52,7 +46,7 @@ internal class MappingMatcher : IMappingMatcher
|
||||
{
|
||||
foreach (var ex in exceptions)
|
||||
{
|
||||
LogException(mapping, ex!);
|
||||
LogException(mapping, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,14 +56,16 @@ internal class MappingMatcher : IMappingMatcher
|
||||
}
|
||||
}
|
||||
|
||||
var partialMappings = possibleMappings
|
||||
var partialMatches = possibleMappings
|
||||
.Where(pm => (pm.Mapping.IsAdminInterface && pm.RequestMatchResult.IsPerfectMatch) || !pm.Mapping.IsAdminInterface)
|
||||
.OrderBy(m => m.RequestMatchResult)
|
||||
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
||||
.ThenBy(m => m.Mapping.Priority)
|
||||
.ThenByDescending(m => m.Mapping.Probability)
|
||||
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||
.ToList();
|
||||
var partialMatch = partialMappings.FirstOrDefault(pm => pm.RequestMatchResult.AverageTotalScore > 0.0);
|
||||
.Where(pm => pm.RequestMatchResult.AverageTotalScore > 0.0)
|
||||
.ToArray();
|
||||
var partialMatch = partialMatches.FirstOrDefault();
|
||||
|
||||
if (_options.AllowPartialMapping == true)
|
||||
{
|
||||
@@ -78,7 +74,11 @@ internal class MappingMatcher : IMappingMatcher
|
||||
|
||||
var match = possibleMappings
|
||||
.Where(m => m.RequestMatchResult.IsPerfectMatch)
|
||||
.OrderBy(m => m.Mapping.Priority).ThenBy(m => m.RequestMatchResult).ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||
.OrderBy(m => m.Mapping.Priority)
|
||||
.ThenBy(m => m.RequestMatchResult)
|
||||
.ThenBy(m => m.RequestMatchResult.TotalNumber)
|
||||
.ThenByDescending(m => m.Mapping.Probability)
|
||||
.ThenByDescending(m => m.Mapping.UpdatedAt)
|
||||
.FirstOrDefault();
|
||||
|
||||
return (match, partialMatch);
|
||||
|
||||
@@ -13,16 +13,10 @@ using WireMock.Util;
|
||||
|
||||
namespace WireMock.Proxy;
|
||||
|
||||
internal class ProxyHelper
|
||||
internal class ProxyHelper(WireMockServerSettings settings)
|
||||
{
|
||||
private readonly WireMockServerSettings _settings;
|
||||
private readonly ProxyMappingConverter _proxyMappingConverter;
|
||||
|
||||
public ProxyHelper(WireMockServerSettings settings)
|
||||
{
|
||||
_settings = Guard.NotNull(settings);
|
||||
_proxyMappingConverter = new ProxyMappingConverter(settings, new GuidUtils(), new DateTimeUtils());
|
||||
}
|
||||
private readonly WireMockServerSettings _settings = Guard.NotNull(settings);
|
||||
private readonly ProxyMappingConverter _proxyMappingConverter = new(settings, new GuidUtils(), new DateTimeUtils());
|
||||
|
||||
public async Task<(IResponseMessage Message, IMapping? Mapping)> SendAsync(
|
||||
IMapping? mapping,
|
||||
@@ -39,18 +33,7 @@ internal class ProxyHelper
|
||||
var requiredUri = new Uri(url);
|
||||
|
||||
// Create HttpRequestMessage
|
||||
var replaceSettings = proxyAndRecordSettings.ReplaceSettings;
|
||||
string proxyUrl;
|
||||
if (replaceSettings is not null)
|
||||
{
|
||||
var stringComparison = replaceSettings.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
||||
proxyUrl = url.Replace(replaceSettings.OldValue, replaceSettings.NewValue, stringComparison);
|
||||
}
|
||||
else
|
||||
{
|
||||
proxyUrl = url;
|
||||
}
|
||||
|
||||
var proxyUrl = proxyAndRecordSettings.ReplaceSettings != null ? ProxyUrlTransformer.Transform(_settings, proxyAndRecordSettings.ReplaceSettings, url) : url;
|
||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, proxyUrl);
|
||||
|
||||
// Call the URL
|
||||
|
||||
21
src/WireMock.Net.Minimal/Proxy/ProxyUrlTransformer.cs
Normal file
21
src/WireMock.Net.Minimal/Proxy/ProxyUrlTransformer.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers;
|
||||
|
||||
namespace WireMock.Proxy;
|
||||
|
||||
internal static class ProxyUrlTransformer
|
||||
{
|
||||
internal static string Transform(WireMockServerSettings settings, ProxyUrlReplaceSettings replaceSettings, string url)
|
||||
{
|
||||
if (!replaceSettings.UseTransformer)
|
||||
{
|
||||
return url.Replace(replaceSettings.OldValue, replaceSettings.NewValue, replaceSettings.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
var transformer = TransformerFactory.Create(replaceSettings.TransformerType, settings);
|
||||
return transformer.Transform(replaceSettings.TransformTemplate, url);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
public partial class Request
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return WithBodyAsProtoBuf([protoDefinition], messageType, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return WithBodyAsProtoBuf([protoDefinition], messageType, matcher, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsProtoBuf(IReadOnlyList<string> protoDefinitions, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsProtoBuf(IReadOnlyList<string> protoDefinitions, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType, matcher));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsProtoBuf(string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(), messageType));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder WithBodyAsProtoBuf(string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(), messageType, matcher));
|
||||
}
|
||||
|
||||
private Func<IdOrTexts> ProtoDefinitionFunc()
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
if (Mapping.ProtoDefinition == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No ProtoDefinition defined on mapping '{Mapping.Guid}'. Please use the WireMockServerSettings to define ProtoDefinitions.");
|
||||
}
|
||||
|
||||
return Mapping.ProtoDefinition.Value;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -20,9 +20,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
||||
{
|
||||
private readonly IList<IRequestMatcher> _requestMatchers;
|
||||
|
||||
/// <summary>
|
||||
/// The link back to the Mapping.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public IMapping Mapping { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
@@ -73,6 +71,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
|
||||
return _requestMatchers.OfType<T>().FirstOrDefault(func);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IRequestBuilder Add<T>(T requestMatcher) where T : IRequestMatcher
|
||||
{
|
||||
foreach (var existing in _requestMatchers.OfType<T>().ToArray())
|
||||
|
||||
@@ -183,16 +183,9 @@ public class RequestMessage : IRequestMessage
|
||||
#endif
|
||||
|
||||
#if MIMEKIT
|
||||
try
|
||||
if (TypeLoader.TryLoadStaticInstance<IMimeKitUtils>(out var mimeKitUtils) && mimeKitUtils.TryGetMimeMessage(this, out var mimeMessage))
|
||||
{
|
||||
if (TypeLoader.LoadStaticInstance<IMimeKitUtils>().TryGetMimeMessage(this, out var mimeMessage))
|
||||
{
|
||||
BodyAsMimeMessage = mimeMessage;
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore exception from MimeMessage.Load
|
||||
BodyAsMimeMessage = mimeMessage;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The ResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IResponseBuilder : IProxyResponseBuilder
|
||||
{
|
||||
}
|
||||
@@ -1,12 +1,10 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JsonConverter.Abstractions;
|
||||
using Stef.Validation;
|
||||
using WireMock.Exceptions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
@@ -235,72 +233,4 @@ public partial class Response
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsProtoBuf(
|
||||
string protoDefinition,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
return WithBodyAsProtoBuf([protoDefinition], messageType, value, jsonConverter, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsProtoBuf(
|
||||
IReadOnlyList<string> protoDefinitions,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
Guard.NotNullOrEmpty(protoDefinitions);
|
||||
Guard.NotNullOrWhiteSpace(messageType);
|
||||
Guard.NotNull(value);
|
||||
|
||||
#if !PROTOBUF
|
||||
throw new NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
ResponseMessage.BodyDestination = null;
|
||||
ResponseMessage.BodyData = new BodyData
|
||||
{
|
||||
DetectedBodyType = BodyType.ProtoBuf,
|
||||
BodyAsJson = value,
|
||||
ProtoDefinition = () => new IdOrTexts(null, protoDefinitions),
|
||||
ProtoBufMessageType = messageType
|
||||
};
|
||||
|
||||
return this;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsProtoBuf(
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
Guard.NotNullOrWhiteSpace(messageType);
|
||||
Guard.NotNull(value);
|
||||
|
||||
#if !PROTOBUF
|
||||
throw new NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
ResponseMessage.BodyDestination = null;
|
||||
ResponseMessage.BodyData = new BodyData
|
||||
{
|
||||
DetectedBodyType = BodyType.ProtoBuf,
|
||||
BodyAsJson = value,
|
||||
ProtoDefinition = () => Mapping.ProtoDefinition ?? throw new WireMockException("ProtoDefinition cannot be resolved. You probably forgot to call .WithProtoDefinition(...) on the mapping."),
|
||||
ProtoBufMessageType = messageType
|
||||
};
|
||||
|
||||
return this;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -13,13 +13,13 @@ public partial class Response
|
||||
/// A delegate to execute to generate the response.
|
||||
/// </summary>
|
||||
[MemberNotNullWhen(true, nameof(WithCallbackUsed))]
|
||||
public Func<IRequestMessage, ResponseMessage>? Callback { get; private set; }
|
||||
public Func<IRequestMessage, IResponseMessage>? Callback { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// A delegate to execute to generate the response async.
|
||||
/// </summary>
|
||||
[MemberNotNullWhen(true, nameof(WithCallbackUsed))]
|
||||
public Func<IRequestMessage, Task<ResponseMessage>>? CallbackAsync { get; private set; }
|
||||
public Func<IRequestMessage, Task<IResponseMessage>>? CallbackAsync { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the method WithCallback(...) is used.
|
||||
@@ -27,7 +27,7 @@ public partial class Response
|
||||
public bool WithCallbackUsed { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler)
|
||||
public IResponseBuilder WithCallback(Func<IRequestMessage, IResponseMessage> callbackHandler)
|
||||
{
|
||||
Guard.NotNull(callbackHandler);
|
||||
|
||||
@@ -35,14 +35,14 @@ public partial class Response
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
|
||||
public IResponseBuilder WithCallback(Func<IRequestMessage, Task<IResponseMessage>> callbackHandler)
|
||||
{
|
||||
Guard.NotNull(callbackHandler);
|
||||
|
||||
return WithCallbackInternal(true, callbackHandler);
|
||||
}
|
||||
|
||||
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, ResponseMessage> callbackHandler)
|
||||
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, IResponseMessage> callbackHandler)
|
||||
{
|
||||
Guard.NotNull(callbackHandler);
|
||||
|
||||
@@ -52,7 +52,7 @@ public partial class Response
|
||||
return this;
|
||||
}
|
||||
|
||||
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
|
||||
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, Task<IResponseMessage>> callbackHandler)
|
||||
{
|
||||
Guard.NotNull(callbackHandler);
|
||||
|
||||
|
||||
@@ -28,9 +28,7 @@ public partial class Response : IResponseBuilder
|
||||
|
||||
private TimeSpan? _delay;
|
||||
|
||||
/// <summary>
|
||||
/// The link back to the mapping.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public IMapping Mapping { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
@@ -81,10 +79,8 @@ public partial class Response : IResponseBuilder
|
||||
/// </summary>
|
||||
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response message.
|
||||
/// </summary>
|
||||
public ResponseMessage ResponseMessage { get; }
|
||||
/// <inheritdoc />
|
||||
public IResponseMessage ResponseMessage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates this instance.
|
||||
@@ -226,7 +222,7 @@ public partial class Response : IResponseBuilder
|
||||
).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
ResponseMessage responseMessage;
|
||||
IResponseMessage responseMessage;
|
||||
if (!WithCallbackUsed)
|
||||
{
|
||||
responseMessage = ResponseMessage;
|
||||
@@ -276,25 +272,8 @@ public partial class Response : IResponseBuilder
|
||||
}
|
||||
}
|
||||
|
||||
ITransformer responseMessageTransformer;
|
||||
switch (TransformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
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(settings, factoryDotLiquid);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"TransformerType '{TransformerType}' is not supported.");
|
||||
}
|
||||
|
||||
return (responseMessageTransformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
|
||||
var transformer = TransformerFactory.Create(TransformerType, settings);
|
||||
return (transformer.Transform(mapping, requestMessage, responseMessage, UseTransformerForBodyAsFile, TransformerReplaceNodeOptions), null);
|
||||
}
|
||||
|
||||
if (!UseTransformer && ResponseMessage.BodyData?.BodyAsFileIsCached == true && responseMessage.BodyData?.BodyAsFile is not null)
|
||||
|
||||
@@ -126,12 +126,10 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
}
|
||||
}
|
||||
|
||||
#if PROTOBUF
|
||||
if (requestMessageProtoBufMatcher?.Matcher != null)
|
||||
{
|
||||
sb.AppendLine(" // .WithBodyAsProtoBuf() is not yet supported");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (requestMessageBodyMatcher?.Matchers != null)
|
||||
{
|
||||
@@ -188,7 +186,7 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
sb.AppendLine($" .WithStatusCode({(int)httpStatusCode})");
|
||||
}
|
||||
|
||||
if (response.ResponseMessage.Headers is { })
|
||||
if (response.ResponseMessage.Headers != null)
|
||||
{
|
||||
foreach (var header in response.ResponseMessage.Headers)
|
||||
{
|
||||
@@ -196,7 +194,7 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
}
|
||||
}
|
||||
|
||||
if (response.ResponseMessage.TrailingHeaders is { })
|
||||
if (response.ResponseMessage.TrailingHeaders != null)
|
||||
{
|
||||
foreach (var header in response.ResponseMessage.TrailingHeaders)
|
||||
{
|
||||
@@ -400,13 +398,11 @@ internal class MappingConverter(MatcherMapper mapper)
|
||||
{
|
||||
void AfterMap(MatcherModel matcherModel)
|
||||
{
|
||||
#if PROTOBUF
|
||||
// In case the ProtoDefinition is defined at the Mapping level, clear the Pattern at the Matcher level
|
||||
if (bodyMatchers?.OfType<ProtoBufMatcher>().Any() == true && mappingModel.ProtoDefinition != null)
|
||||
if (bodyMatchers?.OfType<IProtoBufMatcher>().Any() == true && mappingModel.ProtoDefinition != null)
|
||||
{
|
||||
matcherModel.Pattern = null;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mappingModel.Request.Body = new BodyModel();
|
||||
|
||||
@@ -55,7 +55,12 @@ internal class MatcherMapper
|
||||
case "CSharpCodeMatcher":
|
||||
if (_settings.AllowCSharpCodeMatcher == true)
|
||||
{
|
||||
return TypeLoader.LoadNewInstance<ICSharpCodeMatcher>(matchBehaviour, matchOperator, stringPatterns);
|
||||
if (TypeLoader.TryLoadNewInstance<ICSharpCodeMatcher>(out var csharpCodeMatcher, matchBehaviour, matchOperator, stringPatterns))
|
||||
{
|
||||
return csharpCodeMatcher;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The 'CSharpCodeMatcher' cannot be loaded. Please install the WireMock.Net.Matchers.CSharpCode package.");
|
||||
}
|
||||
|
||||
throw new NotSupportedException("It's not allowed to use the 'CSharpCodeMatcher' because WireMockServerSettings.AllowCSharpCodeMatcher is not set to 'true'.");
|
||||
@@ -75,15 +80,19 @@ internal class MatcherMapper
|
||||
case "GraphQLMatcher":
|
||||
var patternAsString = stringPatterns[0].GetPattern();
|
||||
var schema = new AnyOf<string, StringPattern, ISchemaData>(patternAsString);
|
||||
return TypeLoader.LoadNewInstance<IGraphQLMatcher>(schema, matcherModel.CustomScalars, matchBehaviour, matchOperator);
|
||||
if (TypeLoader.TryLoadNewInstance<IGraphQLMatcher>(out var graphQLMatcher, schema, matcherModel.CustomScalars, matchBehaviour, matchOperator))
|
||||
{
|
||||
return graphQLMatcher;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The 'GraphQLMatcher' cannot be loaded. Please install the WireMock.Net.GraphQL package.");
|
||||
|
||||
case "MimePartMatcher":
|
||||
return CreateMimePartMatcher(matchBehaviour, matcherModel);
|
||||
|
||||
#if PROTOBUF
|
||||
case nameof(ProtoBufMatcher):
|
||||
case "ProtoBufMatcher":
|
||||
return CreateProtoBufMatcher(matchBehaviour, stringPatterns.GetPatterns(), matcherModel);
|
||||
#endif
|
||||
|
||||
case nameof(RegexMatcher):
|
||||
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, useRegexExtended, matchOperator);
|
||||
|
||||
@@ -210,7 +219,7 @@ internal class MatcherMapper
|
||||
break;
|
||||
|
||||
#if PROTOBUF
|
||||
case ProtoBufMatcher protoBufMatcher:
|
||||
case IProtoBufMatcher protoBufMatcher:
|
||||
protoBufMatcher.ProtoDefinition().Value(id => model.Pattern = id, texts =>
|
||||
{
|
||||
if (texts.Count == 1)
|
||||
@@ -283,20 +292,34 @@ internal class MatcherMapper
|
||||
var contentTransferEncodingMatcher = Map(matcher.ContentTransferEncodingMatcher) as IStringMatcher;
|
||||
var contentMatcher = Map(matcher.ContentMatcher);
|
||||
|
||||
return TypeLoader.LoadNewInstance<IMimePartMatcher>(matchBehaviour, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher);
|
||||
if (TypeLoader.TryLoadNewInstance<IMimePartMatcher>(
|
||||
out var mimePartMatcher,
|
||||
matchBehaviour,
|
||||
contentTypeMatcher,
|
||||
contentDispositionMatcher,
|
||||
contentTransferEncodingMatcher,
|
||||
contentMatcher))
|
||||
{
|
||||
return mimePartMatcher;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The 'MimePartMatcher' cannot be loaded. Please install the WireMock.Net.MimePart package.");
|
||||
}
|
||||
|
||||
#if PROTOBUF
|
||||
private ProtoBufMatcher CreateProtoBufMatcher(MatchBehaviour? matchBehaviour, IReadOnlyList<string> protoDefinitions, MatcherModel matcher)
|
||||
private IProtoBufMatcher CreateProtoBufMatcher(MatchBehaviour? matchBehaviour, IReadOnlyList<string> protoDefinitions, MatcherModel matcher)
|
||||
{
|
||||
var objectMatcher = Map(matcher.ContentMatcher) as IObjectMatcher;
|
||||
|
||||
return new ProtoBufMatcher(
|
||||
() => ProtoDefinitionHelper.GetIdOrTexts(_settings, protoDefinitions.ToArray()),
|
||||
if (TypeLoader.TryLoadNewInstance<IProtoBufMatcher>(
|
||||
out var protobufMatcher,
|
||||
() => ProtoDefinitionUtils.GetIdOrTexts(_settings, protoDefinitions.ToArray()),
|
||||
matcher.ProtoBufMessageType!,
|
||||
matchBehaviour ?? MatchBehaviour.AcceptOnMatch,
|
||||
objectMatcher
|
||||
);
|
||||
objectMatcher))
|
||||
{
|
||||
return protobufMatcher;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("The 'ProtoBufMatcher' cannot be loaded. Please install the WireMock.Net.ProtoBuf package.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -356,12 +356,9 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
||||
{
|
||||
Guard.NotNull(protoDefinitionOrId);
|
||||
|
||||
#if PROTOBUF
|
||||
ProtoDefinition = ProtoDefinitionHelper.GetIdOrTexts(_settings, protoDefinitionOrId);
|
||||
ProtoDefinition = ProtoDefinitionUtils.GetIdOrTexts(_settings, protoDefinitionOrId);
|
||||
|
||||
return this;
|
||||
#else
|
||||
throw new NotSupportedException("The WithProtoDefinition method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -241,13 +241,13 @@ public partial class WireMockServer
|
||||
if (requestModel.Body?.Matcher != null)
|
||||
{
|
||||
var bodyMatcher = _matcherMapper.Map(requestModel.Body.Matcher)!;
|
||||
#if PROTOBUF
|
||||
|
||||
// If the BodyMatcher is a ProtoBufMatcher, and if ProtoDefinition is defined on Mapping-level, set the ProtoDefinition from that Mapping.
|
||||
if (bodyMatcher is ProtoBufMatcher protoBufMatcher && mappingModel?.ProtoDefinition != null)
|
||||
if (bodyMatcher is IProtoBufMatcher protoBufMatcher && mappingModel?.ProtoDefinition != null)
|
||||
{
|
||||
protoBufMatcher.ProtoDefinition = () => ProtoDefinitionHelper.GetIdOrTexts(_settings, mappingModel.ProtoDefinition);
|
||||
protoBufMatcher.ProtoDefinition = () => ProtoDefinitionUtils.GetIdOrTexts(_settings, mappingModel.ProtoDefinition);
|
||||
}
|
||||
#endif
|
||||
|
||||
requestBuilder = requestBuilder.WithBody(bodyMatcher);
|
||||
}
|
||||
else if (requestModel.Body?.Matchers != null)
|
||||
@@ -366,20 +366,19 @@ public partial class WireMockServer
|
||||
}
|
||||
else if (responseModel.BodyAsJson != null)
|
||||
{
|
||||
if (responseModel.ProtoBufMessageType != null)
|
||||
if (responseModel.ProtoBufMessageType != null && TypeLoader.TryLoadStaticInstance<IProtoBufUtils>(out var protoBufUtils))
|
||||
{
|
||||
if (responseModel.ProtoDefinition != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBodyAsProtoBuf(responseModel.ProtoDefinition, responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
|
||||
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson, responseModel.ProtoDefinition);
|
||||
}
|
||||
else if (responseModel.ProtoDefinitions != null)
|
||||
{
|
||||
responseBuilder = responseBuilder.WithBodyAsProtoBuf(responseModel.ProtoDefinitions, responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
|
||||
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson, responseModel.ProtoDefinitions);
|
||||
}
|
||||
else
|
||||
{
|
||||
// ProtoDefinition(s) is/are defined at Mapping/Server level
|
||||
responseBuilder = responseBuilder.WithBodyAsProtoBuf(responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
|
||||
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Settings;
|
||||
|
||||
/// <summary>
|
||||
/// Defines an old path param and a new path param to be replaced when proxying.
|
||||
/// </summary>
|
||||
public class ProxyUrlReplaceSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// The old path value to be replaced by the new path value
|
||||
/// </summary>
|
||||
public string OldValue { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The new path value to replace the old value with
|
||||
/// </summary>
|
||||
public string NewValue { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the case should be ignored when replacing.
|
||||
/// </summary>
|
||||
public bool IgnoreCase { get; set; }
|
||||
}
|
||||
@@ -202,14 +202,27 @@ public static class WireMockServerSettingsParser
|
||||
|
||||
private static void ParseProxyUrlReplaceSettings(ProxyAndRecordSettings settings, SimpleSettingsParser parser)
|
||||
{
|
||||
var proxyUrlReplaceOldValue = parser.GetStringValue("ProxyUrlReplaceOldValue");
|
||||
var proxyUrlReplaceNewValue = parser.GetStringValue("ProxyUrlReplaceNewValue");
|
||||
const string prefix = "ProxyUrlReplace";
|
||||
var proxyUrlReplaceOldValue = parser.GetStringValue($"{prefix}OldValue");
|
||||
var proxyUrlReplaceNewValue = parser.GetStringValue($"{prefix}NewValue");
|
||||
if (!string.IsNullOrEmpty(proxyUrlReplaceOldValue) && proxyUrlReplaceNewValue != null)
|
||||
{
|
||||
settings.ReplaceSettings = new ProxyUrlReplaceSettings
|
||||
{
|
||||
OldValue = proxyUrlReplaceOldValue!,
|
||||
NewValue = proxyUrlReplaceNewValue
|
||||
OldValue = proxyUrlReplaceOldValue,
|
||||
NewValue = proxyUrlReplaceNewValue,
|
||||
IgnoreCase = parser.GetBoolValue($"{prefix}IgnoreCase")
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
var transformTemplate = parser.GetStringValue($"{prefix}TransformTemplate");
|
||||
if (!string.IsNullOrEmpty(transformTemplate))
|
||||
{
|
||||
settings.ReplaceSettings = new ProxyUrlReplaceSettings
|
||||
{
|
||||
TransformTemplate = transformTemplate,
|
||||
TransformerType = parser.GetEnumValue($"{prefix}TransformerType", TransformerType.Handlebars)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using WireMock.Util;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
interface ITransformer
|
||||
internal interface ITransformer
|
||||
{
|
||||
ResponseMessage Transform(IMapping mapping, IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options);
|
||||
|
||||
@@ -15,4 +15,6 @@ interface ITransformer
|
||||
IDictionary<string, WireMockList<string>> TransformHeaders(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, IDictionary<string, WireMockList<string>>? headers);
|
||||
|
||||
string TransformString(IMapping mapping, IRequestMessage originalRequestMessage, IResponseMessage originalResponseMessage, string? value);
|
||||
|
||||
string Transform(string template, object? model);
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.Transformers
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
internal interface ITransformerContextFactory
|
||||
{
|
||||
interface ITransformerContextFactory
|
||||
{
|
||||
ITransformerContext Create();
|
||||
}
|
||||
ITransformerContext Create();
|
||||
}
|
||||
@@ -75,6 +75,11 @@ internal class Transformer : ITransformer
|
||||
return transformerContext.ParseAndRender(value, model);
|
||||
}
|
||||
|
||||
public string Transform(string template, object? model)
|
||||
{
|
||||
return model is null ? string.Empty : _factory.Create().ParseAndRender(template, model);
|
||||
}
|
||||
|
||||
public ResponseMessage Transform(IMapping mapping, IRequestMessage requestMessage, IResponseMessage original, bool useTransformerForBodyAsFile, ReplaceNodeOptions options)
|
||||
{
|
||||
var responseMessage = new ResponseMessage();
|
||||
|
||||
30
src/WireMock.Net.Minimal/Transformers/TransformerFactory.cs
Normal file
30
src/WireMock.Net.Minimal/Transformers/TransformerFactory.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers.Handlebars;
|
||||
using WireMock.Transformers.Scriban;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.Transformers;
|
||||
|
||||
internal static class TransformerFactory
|
||||
{
|
||||
internal static ITransformer Create(TransformerType transformerType, WireMockServerSettings settings)
|
||||
{
|
||||
switch (transformerType)
|
||||
{
|
||||
case TransformerType.Handlebars:
|
||||
var factoryHandlebars = new HandlebarsContextFactory(settings);
|
||||
return new Transformer(settings, factoryHandlebars);
|
||||
|
||||
case TransformerType.Scriban:
|
||||
case TransformerType.ScribanDotLiquid:
|
||||
var factoryDotLiquid = new ScribanContextFactory(settings.FileSystemHandler, transformerType);
|
||||
return new Transformer(settings, factoryDotLiquid);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"{nameof(TransformerType)} '{transformerType}' is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,15 +125,7 @@
|
||||
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' and '$(TargetFramework)' != 'net46' and '$(TargetFramework)' != 'net461'">
|
||||
<PackageReference Include="ProtoBufJsonConverter" Version="0.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||
<!--<PackageReference Include="Nullable" Version="1.3.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>-->
|
||||
<PackageReference Include="System.ComponentModel" Version="4.3.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -155,16 +147,6 @@
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Handlebars.Net.Helpers" Version="2.5.2" />
|
||||
<!--<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.5.2" />-->
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.5.2" />
|
||||
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.5.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' ">
|
||||
<PackageReference Include="Handlebars.Net.Helpers.Xslt" Version="2.5.2" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -24,19 +23,13 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
||||
/// <inheritdoc />
|
||||
public MatchBehaviour MatchBehaviour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Func to define the proto definition as id or texts.
|
||||
/// </summary>
|
||||
public Func<IdOrTexts> ProtoDefinition { get; internal set; }
|
||||
/// <inheritdoc />
|
||||
public Func<IdOrTexts> ProtoDefinition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public string MessageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Matcher to use (optional).
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public IObjectMatcher? Matcher { get; }
|
||||
|
||||
private static readonly Converter ProtoBufToJsonConverter = SingletonFactory<Converter>.GetInstance();
|
||||
@@ -122,5 +115,4 @@ public class ProtoBufMatcher : IProtoBufMatcher
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
8
src/WireMock.Net.ProtoBuf/Properties/AssemblyInfo.cs
Normal file
8
src/WireMock.Net.ProtoBuf/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
|
||||
// Needed for Moq in the UnitTest project
|
||||
// [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
@@ -0,0 +1,108 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// IRequestBuilderExtensions extensions for ProtoBuf.
|
||||
/// </summary>
|
||||
// ReSharper disable once InconsistentNaming
|
||||
public static class IRequestBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
|
||||
/// <param name="protoDefinition">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string protoDefinition, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return requestBuilder.WithBodyAsProtoBuf([protoDefinition], messageType, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
|
||||
/// <param name="protoDefinition">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string protoDefinition, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return requestBuilder.WithBodyAsProtoBuf([protoDefinition], messageType, matcher, matchBehaviour);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
|
||||
/// <param name="protoDefinitions">The proto definitions as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, IReadOnlyList<string> protoDefinitions, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
|
||||
/// <param name="protoDefinitions">The proto definitions as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, IReadOnlyList<string> protoDefinitions, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType, matcher));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(requestBuilder), messageType));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
|
||||
{
|
||||
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(requestBuilder), messageType, matcher));
|
||||
}
|
||||
|
||||
private static Func<IdOrTexts> ProtoDefinitionFunc(IRequestBuilder requestBuilder)
|
||||
{
|
||||
return () =>
|
||||
{
|
||||
if (requestBuilder.Mapping.ProtoDefinition == null)
|
||||
{
|
||||
throw new InvalidOperationException($"No ProtoDefinition defined on mapping '{requestBuilder.Mapping.Guid}'. Please use the WireMockServerSettings to define ProtoDefinitions.");
|
||||
}
|
||||
|
||||
return requestBuilder.Mapping.ProtoDefinition.Value;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using JsonConverter.Abstractions;
|
||||
using Stef.Validation;
|
||||
using WireMock.Exceptions;
|
||||
using WireMock.Models;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="IResponseBuilder"/> to implement WithBodyAsProtoBuf.
|
||||
/// </summary>
|
||||
public static class IResponseBuilderExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
|
||||
/// </summary>
|
||||
/// <param name="responseBuilder">The response builder.</param>
|
||||
/// <param name="protoDefinition">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="value">The object to convert to protobuf byte[].</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public static IResponseBuilder WithBodyAsProtoBuf(
|
||||
this IResponseBuilder responseBuilder,
|
||||
string protoDefinition,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
return responseBuilder.WithBodyAsProtoBuf([protoDefinition], messageType, value, jsonConverter, options);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on proto definitions, message type and the value.
|
||||
/// </summary>
|
||||
/// <param name="responseBuilder">The response builder.</param>
|
||||
/// <param name="protoDefinitions">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="value">The object to convert to protobuf byte[].</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public static IResponseBuilder WithBodyAsProtoBuf(
|
||||
this IResponseBuilder responseBuilder,
|
||||
IReadOnlyList<string> protoDefinitions,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
Guard.NotNullOrEmpty(protoDefinitions);
|
||||
Guard.NotNullOrWhiteSpace(messageType);
|
||||
Guard.NotNull(value);
|
||||
|
||||
responseBuilder.ResponseMessage.BodyDestination = null;
|
||||
responseBuilder.ResponseMessage.BodyData = new BodyData
|
||||
{
|
||||
DetectedBodyType = BodyType.ProtoBuf,
|
||||
BodyAsJson = value,
|
||||
ProtoDefinition = () => new IdOrTexts(null, protoDefinitions),
|
||||
ProtoBufMessageType = messageType
|
||||
};
|
||||
|
||||
return responseBuilder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
|
||||
/// </summary>
|
||||
/// <param name="responseBuilder">The response builder.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="value">The object to convert to protobuf byte[].</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
public static IResponseBuilder WithBodyAsProtoBuf(
|
||||
this IResponseBuilder responseBuilder,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
Guard.NotNullOrWhiteSpace(messageType);
|
||||
Guard.NotNull(value);
|
||||
|
||||
responseBuilder.ResponseMessage.BodyDestination = null;
|
||||
responseBuilder.ResponseMessage.BodyData = new BodyData
|
||||
{
|
||||
DetectedBodyType = BodyType.ProtoBuf,
|
||||
BodyAsJson = value,
|
||||
ProtoDefinition = () => responseBuilder.Mapping.ProtoDefinition ?? throw new WireMockException("ProtoDefinition cannot be resolved. You probably forgot to call .WithProtoDefinition(...) on the mapping."),
|
||||
ProtoBufMessageType = messageType
|
||||
};
|
||||
|
||||
return responseBuilder;
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JsonConverter.Abstractions;
|
||||
using ProtoBufJsonConverter;
|
||||
using ProtoBufJsonConverter.Models;
|
||||
using WireMock.ResponseBuilders;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
internal static class ProtoBufUtils
|
||||
internal class ProtoBufUtils : IProtoBufUtils
|
||||
{
|
||||
internal static async Task<byte[]> GetProtoBufMessageWithHeaderAsync(
|
||||
public async Task<byte[]> GetProtoBufMessageWithHeaderAsync(
|
||||
IReadOnlyList<string>? protoDefinitions,
|
||||
string? messageType,
|
||||
object? value,
|
||||
@@ -33,5 +33,15 @@ internal static class ProtoBufUtils
|
||||
.GetInstance()
|
||||
.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public IResponseBuilder UpdateResponseBuilder(IResponseBuilder responseBuilder, string protoBufMessageType, object bodyAsJson, params string[] protoDefinitions)
|
||||
{
|
||||
if (protoDefinitions.Length > 0)
|
||||
{
|
||||
return responseBuilder.WithBodyAsProtoBuf(protoDefinitions, protoBufMessageType, bodyAsJson);
|
||||
}
|
||||
|
||||
// ProtoDefinition(s) is/are defined at Mapping/Server level
|
||||
return responseBuilder.WithBodyAsProtoBuf(protoBufMessageType, bodyAsJson);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
@@ -9,14 +8,13 @@ using ProtoBufJsonConverter;
|
||||
using ProtoBufJsonConverter.Models;
|
||||
using Stef.Validation;
|
||||
using WireMock.Models;
|
||||
using WireMock.Settings;
|
||||
|
||||
namespace WireMock.Util;
|
||||
|
||||
/// <summary>
|
||||
/// Some helper methods for Proto Definitions.
|
||||
/// </summary>
|
||||
public static class ProtoDefinitionHelper
|
||||
internal static class ProtoDefinitionDataHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a dictionary of ProtoDefinitions from a directory.
|
||||
@@ -42,7 +40,7 @@ public static class ProtoDefinitionHelper
|
||||
|
||||
// Build comment and get content from file.
|
||||
var comment = $"// {protoRelativePath}";
|
||||
#if NETSTANDARD2_0
|
||||
#if NETSTANDARD2_0 || NET462
|
||||
var content = File.ReadAllText(filePath);
|
||||
#else
|
||||
var content = await File.ReadAllTextAsync(filePath, cancellationToken);
|
||||
@@ -79,23 +77,4 @@ public static class ProtoDefinitionHelper
|
||||
|
||||
return new ProtoDefinitionData(fileNameMappedToProtoDefinition);
|
||||
}
|
||||
|
||||
internal static IdOrTexts GetIdOrTexts(WireMockServerSettings settings, params string[] protoDefinitionOrId)
|
||||
{
|
||||
switch (protoDefinitionOrId.Length)
|
||||
{
|
||||
case 1:
|
||||
var idOrText = protoDefinitionOrId[0];
|
||||
if (settings.ProtoDefinitions?.TryGetValue(idOrText, out var protoDefinitions) == true)
|
||||
{
|
||||
return new(idOrText, protoDefinitions);
|
||||
}
|
||||
|
||||
return new(null, protoDefinitionOrId);
|
||||
|
||||
default:
|
||||
return new(null, protoDefinitionOrId);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
#if PROTOBUF
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
@@ -30,10 +29,10 @@ internal class WireMockProtoFileResolver : IProtoFileResolver
|
||||
|
||||
foreach (var extraProtoDefinition in protoDefinitions)
|
||||
{
|
||||
var firstNonEmptyLine = extraProtoDefinition.Split(['\r', '\n']).FirstOrDefault(l => !string.IsNullOrEmpty(l));
|
||||
var firstNonEmptyLine = extraProtoDefinition.Split('\r', '\n').FirstOrDefault(l => !string.IsNullOrEmpty(l));
|
||||
if (firstNonEmptyLine != null)
|
||||
{
|
||||
if (TryGetValidPath(firstNonEmptyLine.TrimStart(['/', ' ']), out var validPath))
|
||||
if (TryGetValidPath(firstNonEmptyLine.TrimStart('/', ' '), out var validPath))
|
||||
{
|
||||
_files.Add(validPath, extraProtoDefinition);
|
||||
}
|
||||
@@ -71,5 +70,4 @@ internal class WireMockProtoFileResolver : IProtoFileResolver
|
||||
validPath = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
36
src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj
Normal file
36
src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj
Normal file
@@ -0,0 +1,36 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Description>ProtoBuf and gRPC support for WireMock.Net</Description>
|
||||
<AssemblyTitle>WireMock.Net.ProtoBuf</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFrameworks>netstandard2.1;net462;net6.0;net8.0</TargetFrameworks>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<PackageTags>wiremock;matchers;matcher;protobuf;grpc</PackageTags>
|
||||
<RootNamespace>WireMock</RootNamespace>
|
||||
<ProjectGuid>{B47413AA-55D3-49A7-896A-17ADBFF72407}</ProjectGuid>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<!--<DelaySign>true</DelaySign>-->
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ProtoBufJsonConverter" Version="0.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -328,7 +328,7 @@ public interface IWireMockAdminApi
|
||||
/// <param name="protoDefinition">The ProtoDefinition as text.</param>
|
||||
/// <param name="cancellationToken">The optional cancellationToken.</param>
|
||||
[Post("protodefinitions/{id}")]
|
||||
Task<StatusModel> AddProtoDefinitionAsync([Path] string id, [Body] string body, CancellationToken cancellationToken = default);
|
||||
Task<StatusModel> AddProtoDefinitionAsync([Path] string id, [Body] string protoDefinition, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Check if a file exists
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using WireMock.Models;
|
||||
|
||||
namespace WireMock.Matchers;
|
||||
|
||||
/// <summary>
|
||||
@@ -7,4 +10,18 @@ namespace WireMock.Matchers;
|
||||
/// </summary>
|
||||
public interface IProtoBufMatcher : IDecodeBytesMatcher, IBytesMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// The Func to define the proto definition as id or texts.
|
||||
/// </summary>
|
||||
Func<IdOrTexts> ProtoDefinition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".
|
||||
/// </summary>
|
||||
string MessageType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The Matcher to use (optional).
|
||||
/// </summary>
|
||||
IObjectMatcher? Matcher { get; }
|
||||
}
|
||||
@@ -100,7 +100,10 @@ public class RequestMessageGraphQLMatcher : IRequestMatcher
|
||||
IDictionary<string, Type>? customScalars
|
||||
)
|
||||
{
|
||||
var graphQLMatcher = TypeLoader.LoadNewInstance<IGraphQLMatcher>(schema, customScalars, matchBehaviour, MatchOperator.Or);
|
||||
return [graphQLMatcher];
|
||||
if (TypeLoader.TryLoadNewInstance<IGraphQLMatcher>(out var graphQLMatcher, schema, customScalars, matchBehaviour, MatchOperator.Or))
|
||||
{
|
||||
return [graphQLMatcher];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using WireMock.Models;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.Matchers.Request;
|
||||
|
||||
@@ -24,11 +25,10 @@ public class RequestMessageProtoBufMatcher : IRequestMatcher
|
||||
/// <param name="matcher">The optional matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
public RequestMessageProtoBufMatcher(MatchBehaviour matchBehaviour, Func<IdOrTexts> protoDefinition, string messageType, IObjectMatcher? matcher = null)
|
||||
{
|
||||
#if PROTOBUF
|
||||
Matcher = new ProtoBufMatcher(protoDefinition, messageType, matchBehaviour, matcher);
|
||||
#else
|
||||
throw new System.NotSupportedException("The ProtoBufMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#endif
|
||||
if (TypeLoader.TryLoadNewInstance<IProtoBufMatcher>(out var protoBufMatcher, protoDefinition, messageType, matchBehaviour, matcher))
|
||||
{
|
||||
Matcher = protoBufMatcher;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -5,6 +5,7 @@ using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Minimal, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.MimePart, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.GraphQL, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.ProtoBuf, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Matchers.CSharpCode, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
// [assembly: InternalsVisibleTo("WireMock.Net.StandAlone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace WireMock.RequestBuilders;
|
||||
/// <summary>
|
||||
/// The BodyRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IBodyRequestBuilder : IProtoBufRequestBuilder
|
||||
public interface IBodyRequestBuilder : IMultiPartRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// WithBody: IMatcher
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Collections.Generic;
|
||||
using WireMock.Matchers;
|
||||
|
||||
namespace WireMock.RequestBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The ProtoBufRequestBuilder interface.
|
||||
/// </summary>
|
||||
public interface IProtoBufRequestBuilder : IMultiPartRequestBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="protoDefinition">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="protoDefinition">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="protoDefinitions">The proto definitions as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsProtoBuf(IReadOnlyList<string> protoDefinitions, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="protoDefinitions">The proto definitions as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsProtoBuf(IReadOnlyList<string> protoDefinitions, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsProtoBuf(string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf
|
||||
/// </summary>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
|
||||
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
|
||||
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
|
||||
IRequestBuilder WithBodyAsProtoBuf(string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch);
|
||||
}
|
||||
@@ -9,5 +9,16 @@ namespace WireMock.RequestBuilders;
|
||||
/// </summary>
|
||||
public interface IRequestBuilder : IClientIPRequestBuilder
|
||||
{
|
||||
public IRequestBuilder Add<T>(T requestMatcher) where T : IRequestMatcher;
|
||||
/// <summary>
|
||||
/// Adds a request matcher to the builder.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the request matcher.</typeparam>
|
||||
/// <param name="requestMatcher">The request matcher to add.</param>
|
||||
/// <returns>The current <see cref="IRequestBuilder"/> instance.</returns>
|
||||
IRequestBuilder Add<T>(T requestMatcher) where T : IRequestMatcher;
|
||||
|
||||
/// <summary>
|
||||
/// The link back to the Mapping.
|
||||
/// </summary>
|
||||
IMapping Mapping { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JsonConverter.Abstractions;
|
||||
@@ -84,7 +83,7 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
IResponseBuilder WithBodyAsJson(Func<IRequestMessage, object> bodyFactory, Encoding? encoding = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsJson : Create a ... response based on a async callback function.
|
||||
/// WithBodyAsJson : Create a ... response based on an async callback function.
|
||||
/// </summary>
|
||||
/// <param name="bodyFactory">The async delegate to build the body.</param>
|
||||
/// <param name="encoding">The body encoding.</param>
|
||||
@@ -117,53 +116,4 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter jsonConverter, JsonConverterOptions? options = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
|
||||
/// </summary>
|
||||
/// <param name="protoDefinition">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="value">The object to convert to protobuf byte[].</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsProtoBuf(
|
||||
string protoDefinition,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on proto definitions, message type and the value.
|
||||
/// </summary>
|
||||
/// <param name="protoDefinitions">The proto definition as text.</param>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="value">The object to convert to protobuf byte[].</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsProtoBuf(
|
||||
IReadOnlyList<string> protoDefinitions,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
|
||||
/// </summary>
|
||||
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
|
||||
/// <param name="value">The object to convert to protobuf byte[].</param>
|
||||
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
|
||||
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
|
||||
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithBodyAsProtoBuf(
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
);
|
||||
}
|
||||
@@ -17,12 +17,12 @@ public interface ICallbackResponseBuilder : IResponseProvider
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler);
|
||||
IResponseBuilder WithCallback(Func<IRequestMessage, IResponseMessage> callbackHandler);
|
||||
|
||||
/// <summary>
|
||||
/// The async callback builder
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
[PublicAPI]
|
||||
IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler);
|
||||
IResponseBuilder WithCallback(Func<IRequestMessage, Task<IResponseMessage>> callbackHandler);
|
||||
}
|
||||
19
src/WireMock.Net.Shared/ResponseBuilders/IResponseBuilder.cs
Normal file
19
src/WireMock.Net.Shared/ResponseBuilders/IResponseBuilder.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
/// <summary>
|
||||
/// The ResponseBuilder interface.
|
||||
/// </summary>
|
||||
public interface IResponseBuilder : IProxyResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The link back to the mapping.
|
||||
/// </summary>
|
||||
IMapping Mapping { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the response message.
|
||||
/// </summary>
|
||||
IResponseMessage ResponseMessage { get; }
|
||||
}
|
||||
@@ -18,6 +18,6 @@ public interface IResponseProvider
|
||||
/// <param name="mapping">The used mapping.</param>
|
||||
/// <param name="requestMessage">The request.</param>
|
||||
/// <param name="settings">The WireMockServerSettings.</param>
|
||||
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
||||
/// <returns>The <see cref="IResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
|
||||
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user