Add WireMock.org RestClient (#631)

* wip...

* x

* .

* .

* .

* r

* 1.4.21-preview-02

* 1.4.21-preview-03

* .

* usings

* wip

* .

* ut

* .

* .

* .

* tests

* .

* comments

* readme
This commit is contained in:
Stef Heyenrath
2021-09-16 14:35:08 +02:00
committed by GitHub
parent fd28ebdffa
commit cb66c04199
34 changed files with 7553 additions and 180 deletions

View File

@@ -4,7 +4,7 @@
</PropertyGroup>
<PropertyGroup>
<VersionPrefix>1.4.20</VersionPrefix>
<VersionPrefix>1.4.21-preview-04</VersionPrefix>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
<PackageIconUrl>https://raw.githubusercontent.com/WireMock-Net/WireMock.Net/master/WireMock.Net-Logo.png</PackageIconUrl>
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>

View File

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

View File

@@ -92,6 +92,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Azure Pipelines", "Azure Pi
azure-pipelines-nuget.yml = azure-pipelines-nuget.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Org.RestClient", "src\WireMock.Org.RestClient\WireMock.Org.RestClient.csproj", "{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Org.Abstractions", "src\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj", "{3BA5109E-5F30-4CC2-B699-02EC82560AA6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -214,6 +218,14 @@ Global
{6580580B-1EFD-4922-B0EC-FF290DB279EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6580580B-1EFD-4922-B0EC-FF290DB279EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6580580B-1EFD-4922-B0EC-FF290DB279EE}.Release|Any CPU.Build.0 = Release|Any CPU
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}.Release|Any CPU.Build.0 = Release|Any CPU
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3BA5109E-5F30-4CC2-B699-02EC82560AA6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -250,6 +262,8 @@ Global
{6580580B-1EFD-4922-B0EC-FF290DB279EE} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{1DAEFF47-D117-4E95-8B3E-4F7C8B92011A} = {7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}
{5B64F6CA-BF6B-4F67-BB2A-9C47E441703E} = {7EFB2C5B-1BB2-4AAF-BC9F-216ED80C594D}
{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
{3BA5109E-5F30-4CC2-B699-02EC82560AA6} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}

View File

@@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="RestEase" Version="1.4.10" />
<PackageReference Include="RestEase" Version="1.5.5" />
</ItemGroup>
<ItemGroup>

View File

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

View File

@@ -59,7 +59,7 @@
<package id="MimeKitLite" version="2.0.7" targetFramework="net461" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net461" />
<package id="Owin" version="1.0" targetFramework="net461" />
<package id="RestEase" version="1.4.10" targetFramework="net461" />
<package id="RestEase" version="1.5.5" targetFramework="net461" />
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net461" />
<package id="System.Buffers" version="4.5.0" targetFramework="net461" />
<package id="System.Collections.Immutable" version="1.5.0" targetFramework="net461" />

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Commonly used interfaces, models, enumerations and types.</Description>
<Description>Commonly used models, enumerations and types.</Description>
<AssemblyTitle>WireMock.Net.Abstractions</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>
@@ -11,7 +11,7 @@
<AssemblyName>WireMock.Net.Abstractions</AssemblyName>
<PackageId>WireMock.Net.Abstractions</PackageId>
<PackageTags>wiremock;interfaces;models;classes;enumerations;types</PackageTags>
<PackageTags>wiremock;wiremock.org;interfaces;models;classes;enumerations;types</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A94}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
@@ -33,11 +33,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" PrivateAssets="All" />
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<!-- See also https://mstack.nl/blog/20210801-source-generators -->
<PackageReference Include="FluentBuilder" Version="0.0.7">
<PackageReference Include="FluentBuilder" Version="0.0.11">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

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

View File

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

View File

@@ -39,6 +39,7 @@ namespace WireMock.Server
private const string ContentTypeJson = "application/json";
private const string AdminFiles = "/__admin/files";
private const string AdminMappings = "/__admin/mappings";
private const string AdminMappingsWireMockOrg = "/__admin/mappings/wiremock.org";
private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings";
private const string AdminScenarios = "/__admin/scenarios";
@@ -58,6 +59,7 @@ namespace WireMock.Server
// __admin/mappings
Given(Request.Create().WithPath(AdminMappings).UsingGet()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsGet));
Given(Request.Create().WithPath(AdminMappings).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPost));
Given(Request.Create().WithPath(AdminMappingsWireMockOrg).UsingPost().WithHeader(HttpKnownHeaderNames.ContentType, _adminRequestContentTypeJson)).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsPostWireMockOrg));
Given(Request.Create().WithPath(AdminMappings).UsingDelete()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(MappingsDelete));
// __admin/mappings/reset

View File

@@ -0,0 +1,306 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Newtonsoft.Json.Linq;
using WireMock.Matchers;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Util;
using WireMock.Validation;
using OrgMapping = WireMock.Org.Abstractions.Mapping;
namespace WireMock.Server
{
public partial class WireMockServer
{
/// <summary>
/// Read WireMock.org mapping json file.
/// </summary>
/// <param name="path">The path to the WireMock.org mapping json file.</param>
[PublicAPI]
public void ReadStaticWireMockOrgMappingAndAddOrUpdate([NotNull] string path)
{
Check.NotNull(path, nameof(path));
string filenameWithoutExtension = Path.GetFileNameWithoutExtension(path);
if (FileHelper.TryReadMappingFileWithRetryAndDelay(_settings.FileSystemHandler, path, out string value))
{
var mappings = DeserializeJsonToArray<OrgMapping>(value);
foreach (var mapping in mappings)
{
if (mappings.Length == 1 && Guid.TryParse(filenameWithoutExtension, out Guid guidFromFilename))
{
ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mapping, guidFromFilename, path);
}
else
{
ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mapping, null, path);
}
}
}
}
private ResponseMessage MappingsPostWireMockOrg(RequestMessage requestMessage)
{
try
{
var mappingModels = DeserializeRequestMessageToArray<OrgMapping>(requestMessage);
if (mappingModels.Length == 1)
{
Guid? guid = ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModels[0]);
return ResponseMessageBuilder.Create("Mapping added", 201, guid);
}
foreach (var mappingModel in mappingModels)
{
ConvertWireMockOrgMappingAndRegisterAsRespondProvider(mappingModel);
}
return ResponseMessageBuilder.Create("Mappings added", 201);
}
catch (ArgumentException a)
{
_settings.Logger.Error("HttpStatusCode set to 400 {0}", a);
return ResponseMessageBuilder.Create(a.Message, 400);
}
catch (Exception e)
{
_settings.Logger.Error("HttpStatusCode set to 500 {0}", e);
return ResponseMessageBuilder.Create(e.ToString(), 500);
}
}
private Guid? ConvertWireMockOrgMappingAndRegisterAsRespondProvider(OrgMapping mapping, Guid? guid = null, string path = null)
{
var requestBuilder = Request.Create();
var request = mapping.Request;
if (request != null)
{
if (request.Url != null)
{
requestBuilder = requestBuilder.WithUrl(request.Url);
}
else if (request.UrlPattern != null)
{
requestBuilder = requestBuilder.WithUrl(new RegexMatcher(request.UrlPattern));
}
else if (request.UrlPath != null)
{
requestBuilder = requestBuilder.WithPath(request.Url);
}
else if (request.UrlPathPattern != null)
{
requestBuilder = requestBuilder.WithPath(new RegexMatcher(request.UrlPathPattern));
}
if (request.Method != null)
{
requestBuilder = requestBuilder.UsingMethod(request.Method);
}
/*
"headers" : {
"Accept" : {
"contains" : "xml"
}
}
*/
if (request.Headers is JObject headers)
{
ProcessWireMockOrgJObjectAndUseStringMatcher(headers, (key, match) =>
{
requestBuilder = requestBuilder.WithHeader(key, match as IStringMatcher);
});
}
if (request.Cookies is JObject cookies)
{
ProcessWireMockOrgJObjectAndUseStringMatcher(cookies, (key, match) =>
{
requestBuilder = requestBuilder.WithCookie(key, match as IStringMatcher);
});
}
/*
"queryParameters" : {
"search_term" : {
"equalTo" : "WireMock"
}
}
*/
if (request.QueryParameters is JObject queryParameters)
{
ProcessWireMockOrgJObjectAndUseStringMatcher(queryParameters, (key, match) =>
{
requestBuilder = requestBuilder.WithParam(key, match);
});
}
/*
"bodyPatterns" : [ {
"equalToJson" : "{ "cityName": "São Paulo", "cityCode": 5001 },
"ignoreArrayOrder" : true,
"ignoreExtraElements" : true
} ]
*/
if (request.BodyPatterns?.Any() == true)
{
var jObjectArray = request.BodyPatterns.Cast<JObject>();
var bodyPattern = jObjectArray.First();
ProcessWireMockOrgJObjectAndUseIMatcher(bodyPattern, (match) =>
{
requestBuilder = requestBuilder.WithBody(match);
});
}
}
IResponseBuilder responseBuilder = Response.Create();
var response = mapping.Response;
if (response != null)
{
responseBuilder = responseBuilder.WithStatusCode(response.Status);
if (response.Headers is JObject responseHeaders)
{
ProcessWireMockOrgJObjectAndConvertToIDictionary(responseHeaders, (headers) =>
{
responseBuilder = responseBuilder.WithHeaders(headers);
});
}
if (response.Transformers != null)
{
responseBuilder = responseBuilder.WithTransformer();
}
if (response.Body != null)
{
responseBuilder = responseBuilder.WithBody(response.Body);
}
if (response.JsonBody != null)
{
responseBuilder = responseBuilder.WithBodyAsJson(response.JsonBody);
}
if (response.Base64Body != null)
{
responseBuilder = responseBuilder.WithBody(Encoding.UTF8.GetString(Convert.FromBase64String(response.Base64Body)));
}
if (response.BodyFileName != null)
{
responseBuilder = responseBuilder.WithBodyFromFile(response.BodyFileName);
}
}
var respondProvider = Given(requestBuilder);
if (guid != null)
{
respondProvider = respondProvider.WithGuid(guid.Value);
}
else if (!string.IsNullOrEmpty(mapping.Uuid))
{
respondProvider = respondProvider.WithGuid(new Guid(mapping.Uuid));
}
if (mapping.Name != null)
{
respondProvider = respondProvider.WithTitle(mapping.Name);
}
if (path != null)
{
respondProvider = respondProvider.WithPath(path);
}
respondProvider.RespondWith(responseBuilder);
return respondProvider.Guid;
}
private void ProcessWireMockOrgJObjectAndConvertToIDictionary(JObject items, Action<IDictionary<string, string>> action)
{
var dict = new Dictionary<string, string>();
foreach (var item in items)
{
var key = item.Key;
var valueAsString = item.Value.Value<string>();
dict.Add(key, valueAsString);
}
action(dict);
}
private void ProcessWireMockOrgJObjectAndUseStringMatcher(JObject items, Action<string, IStringMatcher> action)
{
foreach (var item in items)
{
var key = item.Key;
var match = item.Value.First as JProperty;
var valueAsString = match?.Value.Value<string>();
if (string.IsNullOrEmpty(valueAsString))
{
continue;
}
var matcher = ProcessAsStringMatcher(match, valueAsString);
if (matcher != null)
{
action(key, matcher);
}
}
}
private void ProcessWireMockOrgJObjectAndUseIMatcher(JObject items, Action<IMatcher> action)
{
IMatcher matcher = null;
var firstItem = items.First as JProperty;
if (firstItem?.Name == "equalToJson")
{
matcher = new JsonMatcher(firstItem.Value);
}
else
{
var valueAsString = (firstItem.Value as JValue)?.Value as string;
if (valueAsString == null)
{
return;
}
matcher = ProcessAsStringMatcher(firstItem, valueAsString);
}
if (matcher != null)
{
action(matcher);
}
}
private static IStringMatcher ProcessAsStringMatcher(JProperty match, string valueAsString)
{
switch (match?.Name)
{
case "contains":
return new WildcardMatcher(valueAsString);
case "matches":
return new RegexMatcher(valueAsString);
case "equalTo":
return new ExactMatcher(valueAsString);
default:
return null;
}
}
}
}

View File

@@ -132,5 +132,6 @@
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
<ProjectReference Include="..\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,12 @@
namespace WireMock.Org.Abstractions
{
/// <summary>
/// Pre-emptive basic auth credentials to match against
/// </summary>
public class BasicAuthCredentials
{
public string Password { get; set; }
public string Username { get; set; }
}
}

View File

@@ -0,0 +1,9 @@
namespace WireMock.Org.Abstractions
{
public class GetAdminMappingsResponse
{
public Mapping[] Mappings { get; set; }
public Meta Meta { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Org.Abstractions
{
public class GetAdminRecordingsStatusResponse
{
public string Status { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Org.Abstractions
{
public class GetAdminRequestsUnmatchedNearMissesResponse
{
public NearMiss[] NearMisses { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Org.Abstractions
{
public class GetAdminScenariosResponse
{
public Scenario[] Scenarios { get; set; }
}
}

View File

@@ -0,0 +1,59 @@
namespace WireMock.Org.Abstractions
{
public class Mapping
{
/// <summary>
/// This stub mapping's unique identifier
/// </summary>
public string Id { get; set; }
/// <summary>
/// Alias for the id
/// </summary>
public string Uuid { get; set; }
/// <summary>
/// The stub mapping's name
/// </summary>
public string Name { get; set; }
public Request Request { get; set; }
public Response Response { get; set; }
/// <summary>
/// Indicates that the stub mapping should be persisted immediately on create/update/delete and survive resets to default.
/// </summary>
public bool Persistent { get; set; }
/// <summary>
/// This stub mapping's priority relative to others. 1 is highest.
/// </summary>
public int Priority { get; set; }
/// <summary>
/// The name of the scenario that this stub mapping is part of
/// </summary>
public string ScenarioName { get; set; }
/// <summary>
/// The required state of the scenario in order for this stub to be matched.
/// </summary>
public string RequiredScenarioState { get; set; }
/// <summary>
/// The new state for the scenario to be updated to after this stub is served.
/// </summary>
public string NewScenarioState { get; set; }
/// <summary>
/// A map of the names of post serve action extensions to trigger and their parameters.
/// </summary>
public object PostServeActions { get; set; }
/// <summary>
/// Arbitrary metadata to be used for e.g. tagging, documentation. Can also be used to find and remove stubs.
/// </summary>
public object Metadata { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Org.Abstractions
{
public class Meta
{
public int Total { get; set; }
}
}

View File

@@ -0,0 +1,35 @@
namespace WireMock.Org.Abstractions
{
public class NearMiss
{
/// <summary>
/// The HTTP request method
/// </summary>
public string Method { get; set; }
/// <summary>
/// The path and query to match exactly against
/// </summary>
public string Url { get; set; }
/// <summary>
/// The full URL to match against
/// </summary>
public string AbsoluteUrl { get; set; }
/// <summary>
/// Header patterns to match against in the &lt;key&gt;: { "&lt;predicate&gt;": "&lt;value&gt;" } form
/// </summary>
public object Headers { get; set; }
/// <summary>
/// Cookie patterns to match against in the &lt;key&gt;: { "&lt;predicate&gt;": "&lt;value&gt;" } form
/// </summary>
public object Cookies { get; set; }
/// <summary>
/// Body string to match against
/// </summary>
public string Body { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace WireMock.Org.Abstractions
{
public class PostAdminRequestsCountResponse
{
public int Count { get; set; }
}
}

View File

@@ -0,0 +1,55 @@
namespace WireMock.Org.Abstractions
{
public class Request
{
/// <summary>
/// The HTTP request method e.g. GET
/// </summary>
public string Method { get; set; }
/// <summary>
/// The path and query to match exactly against. Only one of url, urlPattern, urlPath or urlPathPattern may be specified.
/// </summary>
public string Url { get; set; }
/// <summary>
/// The path to match exactly against. Only one of url, urlPattern, urlPath or urlPathPattern may be specified.
/// </summary>
public string UrlPath { get; set; }
/// <summary>
/// The path regex to match against. Only one of url, urlPattern, urlPath or urlPathPattern may be specified.
/// </summary>
public string UrlPathPattern { get; set; }
/// <summary>
/// The path and query regex to match against. Only one of url, urlPattern, urlPath or urlPathPattern may be specified.
/// </summary>
public string UrlPattern { get; set; }
/// <summary>
/// Query parameter patterns to match against in the &lt;key&gt;: { "&lt;predicate&gt;": "&lt;value&gt;" } form
/// </summary>
public object QueryParameters { get; set; }
/// <summary>
/// Header patterns to match against in the &lt;key&gt;: { "&lt;predicate&gt;": "&lt;value&gt;" } form
/// </summary>
public object Headers { get; set; }
/// <summary>
/// Pre-emptive basic auth credentials to match against
/// </summary>
public BasicAuthCredentials BasicAuthCredentials { get; set; }
/// <summary>
/// Cookie patterns to match against in the &lt;key&gt;: { "&lt;predicate&gt;": "&lt;value&gt;" } form
/// </summary>
public object Cookies { get; set; }
/// <summary>
/// Request body patterns to match against in the &lt;key&gt;: { "&lt;predicate&gt;": "&lt;value&gt;" } form
/// </summary>
public object[] BodyPatterns { get; set; }
}
}

View File

@@ -0,0 +1,75 @@
namespace WireMock.Org.Abstractions
{
public class Response
{
/// <summary>
/// The HTTP status code to be returned
/// </summary>
public int Status { get; set; }
/// <summary>
/// The HTTP status message to be returned
/// </summary>
public string StatusMessage { get; set; }
/// <summary>
/// Map of response headers to send
/// </summary>
public object Headers { get; set; }
/// <summary>
/// Extra request headers to send when proxying to another host.
/// </summary>
public object AdditionalProxyRequestHeaders { get; set; }
/// <summary>
/// The response body as a string. Only one of body, base64Body, jsonBody or bodyFileName may be specified.
/// </summary>
public string Body { get; set; }
/// <summary>
/// The response body as a base64 encoded string (useful for binary content). Only one of body, base64Body, jsonBody or bodyFileName may be specified.
/// </summary>
public string Base64Body { get; set; }
/// <summary>
/// The response body as a JSON object. Only one of body, base64Body, jsonBody or bodyFileName may be specified.
/// </summary>
public object JsonBody { get; set; }
/// <summary>
/// The path to the file containing the response body, relative to the configured file root. Only one of body, base64Body, jsonBody or bodyFileName may be specified.
/// </summary>
public string BodyFileName { get; set; }
/// <summary>
/// The fault to apply (instead of a full, valid response).
/// </summary>
public string Fault { get; set; }
/// <summary>
/// Number of milliseconds to delay be before sending the response.
/// </summary>
public int FixedDelayMilliseconds { get; set; }
/// <summary>
/// Read-only flag indicating false if this was the default, unmatched response. Not present otherwise.
/// </summary>
public bool FromConfiguredStub { get; set; }
/// <summary>
/// The base URL of the target to proxy matching requests to.
/// </summary>
public string ProxyBaseUrl { get; set; }
/// <summary>
/// Parameters to apply to response transformers.
/// </summary>
public object TransformerParameters { get; set; }
/// <summary>
/// List of names of transformers to apply to this response.
/// </summary>
public string Transformers { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
namespace WireMock.Org.Abstractions
{
/// <summary>
/// Log normal randomly distributed response delay.
/// </summary>
public class ResponseLogNormal
{
public int Median { get; set; }
public double Sigma { get; set; }
public string Type { get; set; }
}
}

View File

@@ -0,0 +1,14 @@
namespace WireMock.Org.Abstractions
{
/// <summary>
/// Uniformly distributed random response delay.
/// </summary>
public class ResponseLogUniformlyDistributed
{
public int Lower { get; set; }
public string Type { get; set; }
public int Upper { get; set; }
}
}

View File

@@ -0,0 +1,22 @@
namespace WireMock.Org.Abstractions
{
public class Scenario
{
/// <summary>
/// The scenario ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// The scenario name
/// </summary>
public string Name { get; set; }
public string PossibleStates { get; set; }
/// <summary>
/// The current state of this scenario
/// </summary>
public string State { get; set; }
}
}

View File

@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Commonly used interfaces, models, enumerations and types.</Description>
<AssemblyTitle>WireMock.Org.Abstractions</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;netstandard1.0;netstandard2.0;netstandard2.1</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591;8603</NoWarn>
<AssemblyName>WireMock.Org.Abstractions</AssemblyName>
<PackageId>WireMock.Org.Abstractions</PackageId>
<PackageTags>wiremock;wiremock.org;interfaces;models;classes;enumerations;types</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{3BA5109E-5F30-4CC2-B699-02EC82560AA6}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<!--<PackageReference Include="AnyOf" Version="0.0.23" />-->
<PackageReference Include="JetBrains.Annotations" Version="2021.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
</ItemGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,232 @@
using System.IO;
using System.Threading.Tasks;
using RestEase;
using WireMock.Org.Abstractions;
namespace WireMock.Org.RestClient
{
/// <summary>
/// WireMockOrg
/// </summary>
public interface IWireMockOrgApi
{
/// <summary>
/// Get all stub mappings
/// </summary>
/// <param name="limit">The maximum number of results to return</param>
/// <param name="offset">The start index of the results to return</param>
[Get("/__admin/mappings")]
Task<GetAdminMappingsResponse> GetAdminMappingsAsync([Query] int? limit, [Query] int? offset);
/// <summary>
/// Create a new stub mapping
/// </summary>
/// <param name="request"></param>
[Post("/__admin/mappings")]
[Header("Content-Type", "application/json")]
Task<Mapping> PostAdminMappingsAsync([Body] Mapping request);
/// <summary>
/// Delete all stub mappings
/// </summary>
[Delete("/__admin/mappings")]
Task DeleteAdminMappingsAsync();
/// <summary>
/// Reset stub mappings
/// </summary>
[Post("/__admin/mappings/reset")]
Task PostAdminMappingsResetAsync();
/// <summary>
/// Persist stub mappings
/// </summary>
[Post("/__admin/mappings/save")]
Task PostAdminMappingsSaveAsync();
/// <summary>
/// Get stub mapping by ID
/// </summary>
[Get("/__admin/mappings/{stubMappingId}")]
Task<Mapping> GetAdminMappingsByStubMappingIdAsync();
/// <summary>
/// Update a stub mapping
/// </summary>
/// <param name="request"></param>
[Put("/__admin/mappings/{stubMappingId}")]
[Header("Content-Type", "application/json")]
Task<Mapping> PutAdminMappingsByStubMappingIdAsync([Body] Mapping request);
/// <summary>
/// Delete a stub mapping
/// </summary>
[Delete("/__admin/mappings/{stubMappingId}")]
Task DeleteAdminMappingsByStubMappingIdAsync();
/// <summary>
/// PostAdminMappingsFindByMetadata (/__admin/mappings/find-by-metadata)
/// </summary>
[Post("/__admin/mappings/find-by-metadata")]
[Header("Content-Type", "application/json")]
Task<GetAdminMappingsResponse> PostAdminMappingsFindByMetadataAsync();
/// <summary>
/// Delete stub mappings matching metadata
/// </summary>
[Post("/__admin/mappings/remove-by-metadata")]
[Header("Content-Type", "application/json")]
Task PostAdminMappingsRemoveByMetadataAsync();
/// <summary>
/// Get all requests in journal
/// </summary>
/// <param name="limit">The maximum number of results to return</param>
/// <param name="since">Only return logged requests after this date</param>
[Get("/__admin/requests")]
Task GetAdminRequestsAsync([Query] string limit, [Query] string since);
/// <summary>
/// Delete all requests in journal
/// </summary>
[Delete("/__admin/requests")]
Task DeleteAdminRequestsAsync();
/// <summary>
/// Get request by ID
/// </summary>
/// <param name="requestId">The UUID of the logged request</param>
[Get("/__admin/requests/{requestId}")]
Task GetAdminRequestsByRequestIdAsync([Path] string requestId);
/// <summary>
/// Delete request by ID
/// </summary>
/// <param name="requestId">The UUID of the logged request</param>
[Delete("/__admin/requests/{requestId}")]
Task DeleteAdminRequestsByRequestIdAsync([Path] string requestId);
/// <summary>
/// Empty the request journal
/// </summary>
[Post("/__admin/requests/reset")]
Task PostAdminRequestsResetAsync();
/// <summary>
/// Count requests by criteria
/// </summary>
/// <param name="request"></param>
[Post("/__admin/requests/count")]
[Header("Content-Type", "application/json")]
Task<PostAdminRequestsCountResponse> PostAdminRequestsCountAsync([Body] Request request);
/// <summary>
/// Remove requests by criteria
/// </summary>
/// <param name="request"></param>
[Post("/__admin/requests/remove")]
[Header("Content-Type", "application/json")]
Task PostAdminRequestsRemoveAsync([Body] Request request);
/// <summary>
/// Delete requests mappings matching metadata
/// </summary>
[Post("/__admin/requests/remove-by-metadata")]
[Header("Content-Type", "application/json")]
Task PostAdminRequestsRemoveByMetadataAsync();
/// <summary>
/// Find requests by criteria
/// </summary>
/// <param name="request"></param>
[Post("/__admin/requests/find")]
[Header("Content-Type", "application/json")]
Task PostAdminRequestsFindAsync([Body] Request request);
/// <summary>
/// Find unmatched requests
/// </summary>
[Get("/__admin/requests/unmatched")]
Task GetAdminRequestsUnmatchedAsync();
/// <summary>
/// GetAdminRequestsUnmatchedNearMisses (/__admin/requests/unmatched/near-misses)
/// </summary>
[Get("/__admin/requests/unmatched/near-misses")]
Task<GetAdminRequestsUnmatchedNearMissesResponse> GetAdminRequestsUnmatchedNearMissesAsync();
/// <summary>
/// Find near misses matching specific request
/// </summary>
/// <param name="request"></param>
[Post("/__admin/near-misses/request")]
[Header("Content-Type", "application/json")]
Task<GetAdminRequestsUnmatchedNearMissesResponse> PostAdminNearMissesRequestAsync([Body] NearMiss request);
/// <summary>
/// Find near misses matching request pattern
/// </summary>
/// <param name="request"></param>
[Post("/__admin/near-misses/request-pattern")]
[Header("Content-Type", "application/json")]
Task<GetAdminRequestsUnmatchedNearMissesResponse> PostAdminNearMissesRequestPatternAsync([Body] Request request);
/// <summary>
/// Start recording
/// </summary>
[Post("/__admin/recordings/start")]
[Header("Content-Type", "application/json")]
Task PostAdminRecordingsStartAsync();
/// <summary>
/// Stop recording
/// </summary>
[Post("/__admin/recordings/stop")]
Task<GetAdminMappingsResponse> PostAdminRecordingsStopAsync();
/// <summary>
/// Get recording status
/// </summary>
[Get("/__admin/recordings/status")]
Task<GetAdminRecordingsStatusResponse> GetAdminRecordingsStatusAsync();
/// <summary>
/// Take a snapshot recording
/// </summary>
/// <param name="request"></param>
[Post("/__admin/recordings/snapshot")]
[Header("Content-Type", "application/json")]
Task<GetAdminMappingsResponse> PostAdminRecordingsSnapshotAsync([Body] object request);
/// <summary>
/// Get all scenarios
/// </summary>
[Get("/__admin/scenarios")]
Task<GetAdminScenariosResponse> GetAdminScenariosAsync();
/// <summary>
/// Reset the state of all scenarios
/// </summary>
[Post("/__admin/scenarios/reset")]
Task PostAdminScenariosResetAsync();
/// <summary>
/// Update global settings
/// </summary>
[Post("/__admin/settings")]
[Header("Content-Type", "application/json")]
Task PostAdminSettingsAsync();
/// <summary>
/// Reset mappings and request journal
/// </summary>
[Post("/__admin/reset")]
Task PostAdminResetAsync();
/// <summary>
/// PostAdminShutdown (/__admin/shutdown)
/// </summary>
[Post("/__admin/shutdown")]
Task PostAdminShutdownAsync();
}
}

View File

@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A WireMock.org RestClient using RestEase to access the admin interface.</Description>
<AssemblyTitle>WireMock.Org.RestClient</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>net45;netstandard1.1;netstandard2.0;netstandard2.1</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>WireMock.Org.RestClient</AssemblyName>
<PackageId>WireMock.Org.RestClient</PackageId>
<PackageTags>wiremock;wiremock.org;rest;client;restclient;restease;rest;json</PackageTags>
<RootNamespace>WireMock.Org.Client</RootNamespace>
<ProjectGuid>{08B29DB1-FEFE-408A-AD0A-6BA6DDC8D70F}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<!--<PathMap>$(MSBuildProjectDirectory)=/</PathMap>-->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="RestEase" Version="1.5.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Org.Abstractions\WireMock.Org.Abstractions.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Authors>Stef Heyenrath</Authors>
@@ -51,7 +51,7 @@
<PackageReference Include="FluentAssertions" Version="5.10.3" />
<PackageReference Include="Moq" Version="4.16.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
<PackageReference Include="RestEase" Version="1.4.10" />
<PackageReference Include="RestEase" Version="1.5.5" />
<PackageReference Include="RandomDataGenerator.Net" Version="1.0.12" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
@@ -72,6 +72,9 @@
<None Update="responsebody.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings.org\mapping1.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="__admin\mappings\00000002-ee28-4f29-ae63-1ac9b0802d86.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -0,0 +1,31 @@
using System.IO;
using System.Linq;
using FluentAssertions;
using WireMock.Server;
using Xunit;
namespace WireMock.Net.Tests
{
public class WireMockServerImportWireMockOrgTests
{
// For for AppVeyor + OpenCover
private string GetCurrentFolder()
{
return Directory.GetCurrentDirectory();
}
[Fact]
public void WireMockServer_Admin_ReadStaticWireMockOrgMappingAndAddOrUpdate()
{
var server = WireMockServer.Start();
string path = Path.Combine(GetCurrentFolder(), "__admin", "mappings.org", "mapping1.json");
server.ReadStaticWireMockOrgMappingAndAddOrUpdate(path);
var mappings = server.Mappings.ToArray();
mappings.Should().HaveCount(1);
server.Stop();
}
}
}

View File

@@ -62,7 +62,7 @@ namespace WireMock.Net.Tests
// Assert
var mappings = server.Mappings;
Check.That(mappings.Count()).IsEqualTo(24);
Check.That(mappings.Count()).IsEqualTo(25);
Check.That(mappings.All(m => m.Priority == int.MinValue)).IsTrue();
}
@@ -81,8 +81,8 @@ namespace WireMock.Net.Tests
// Assert
var mappings = server.Mappings;
Check.That(mappings.Count()).IsEqualTo(25);
Check.That(mappings.Count(m => m.Priority == int.MinValue)).IsEqualTo(24);
Check.That(mappings.Count()).IsEqualTo(26);
Check.That(mappings.Count(m => m.Priority == int.MinValue)).IsEqualTo(25);
Check.That(mappings.Count(m => m.Priority == 1000)).IsEqualTo(1);
}

View File

@@ -0,0 +1,38 @@
{
"id": "ef53ea56-f118-4b3a-8c69-a9484851d99a",
"name": "weatherforecast",
"request": {
"path": "/WeatherForecast/register-city",
"method": "POST",
"headers": {
"XXX": {
"contains": "xml"
}
},
"queryParameters": {
"search_term": {
"equalTo": "WireMock"
}
},
"bodyPatterns": [
{
"equalToJson": {
"cityName": "São Paulo",
"cityCode": 5001
},
"ignoreArrayOrder": true,
"ignoreExtraElements": true
}
]
},
"response": {
"status": 200,
"body": "[{\"date\":\"2021-09-09T20:44:48.0992639-03:00\",\"temperatureC\":51,\"temperatureF\":123,\"summary\":\"Hot\"},{\"date\":\"2021-09-10T20:44:48.0992692-03:00\",\"temperatureC\":34,\"temperatureF\":93,\"summary\":\"Mild\"},{\"date\":\"2021-09-11T20:44:48.0992696-03:00\",\"temperatureC\":43,\"temperatureF\":109,\"summary\":\"Sweltering\"},{\"date\":\"2021-09-12T20:44:48.0992698-03:00\",\"temperatureC\":46,\"temperatureF\":114,\"summary\":\"Cool\"},{\"date\":\"2021-09-13T20:44:48.0992701-03:00\",\"temperatureC\":3,\"temperatureF\":37,\"summary\":\"Freezing\"}]",
"headers": {
"Content-Type": "application/json; charset=utf-8"
}
},
"uuid": "ef53ea56-f118-4b3a-8c69-a9484851d99a",
"persistent": true,
"insertionIndex": 1
}