mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-11 11:47:12 +02:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88ff2a72db | ||
|
|
d5026e83b5 | ||
|
|
b6b17d80cc | ||
|
|
c8ad9f49e4 | ||
|
|
0a4aca9ae9 | ||
|
|
e5ed6036ee | ||
|
|
7b534635b9 | ||
|
|
1562958172 | ||
|
|
35d42a5c0d | ||
|
|
429d6830ae | ||
|
|
38634ac65a | ||
|
|
ef5f988786 | ||
|
|
1e44f52ad6 | ||
|
|
7fd1d30d0e | ||
|
|
49b29d74dc |
@@ -1,3 +1,12 @@
|
|||||||
|
# 1.5.11 (24 November 2022)
|
||||||
|
- [#836](https://github.com/WireMock-Net/WireMock.Net/pull/836) - Add Settings.QueryParameterMultipleValueSupport [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#848](https://github.com/WireMock-Net/WireMock.Net/pull/848) - Use try-catch when adding or removing logEntry [bug] contributed by [StefH](https://github.com/StefH)
|
||||||
|
- [#846](https://github.com/WireMock-Net/WireMock.Net/issues/846) - Exception ArgumentOutOfRangeException [bug]
|
||||||
|
|
||||||
|
# 1.5.10 (06 November 2022)
|
||||||
|
- [#843](https://github.com/WireMock-Net/WireMock.Net/pull/843) - Webhook Templating: Use the transformed URL to create the HttpRequestMessage contributed by [ggradnig](https://github.com/ggradnig)
|
||||||
|
- [#845](https://github.com/WireMock-Net/WireMock.Net/pull/845) - Add WireMockNullLogger as valid commandline logger option [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
|
||||||
# 1.5.9 (29 October 2022)
|
# 1.5.9 (29 October 2022)
|
||||||
- [#828](https://github.com/WireMock-Net/WireMock.Net/pull/828) - Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature] contributed by [StefH](https://github.com/StefH)
|
- [#828](https://github.com/WireMock-Net/WireMock.Net/pull/828) - Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
- [#832](https://github.com/WireMock-Net/WireMock.Net/pull/832) - Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature] contributed by [StefH](https://github.com/StefH)
|
- [#832](https://github.com/WireMock-Net/WireMock.Net/pull/832) - Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature] contributed by [StefH](https://github.com/StefH)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<VersionPrefix>1.5.9</VersionPrefix>
|
<VersionPrefix>1.5.11</VersionPrefix>
|
||||||
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
|
||||||
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
|
||||||
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
|
||||||
@@ -20,6 +20,13 @@
|
|||||||
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- https://github.com/coverlet-coverage/coverlet/issues/1391 -->
|
||||||
|
<PropertyGroup Condition="$(MSBuildProjectName.Contains('.Tests'))">
|
||||||
|
<CollectCoverage>true</CollectCoverage>
|
||||||
|
<ExcludeByAttribute>GeneratedCodeAttribute</ExcludeByAttribute>
|
||||||
|
<CoverletOutputFormat>opencover</CoverletOutputFormat>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
|
<None Include="../../resources/WireMock.Net-Logo.png" Pack="true" PackagePath="" />
|
||||||
<!--<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>-->
|
<!--<None Include="../../PackageReadme.md" Pack="true" PackagePath=""/>-->
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
rem https://github.com/StefH/GitHubReleaseNotes
|
rem https://github.com/StefH/GitHubReleaseNotes
|
||||||
|
|
||||||
SET version=1.5.9
|
SET version=1.5.11
|
||||||
|
|
||||||
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate --version %version% --token %GH_TOKEN%
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
# 1.5.9 (29 October 2022)
|
# 1.5.11 (24 November 2022)
|
||||||
- #828 Add setting to skip saving the string-response in the logging when using WithBody(Func...) [feature]
|
- #836 Add Settings.QueryParameterMultipleValueSupport [feature]
|
||||||
- #832 Fixes for WireMock.Net.FluentAssertions (callcount behaviour) [feature]
|
- #848 Use try-catch when adding or removing logEntry [bug]
|
||||||
- #834 Support deleting / resetting a single scenario [feature]
|
- #846 Exception ArgumentOutOfRangeException [bug]
|
||||||
- #837 Bump Microsoft.AspNetCore.Server.Kestrel.Core from 2.1.7 to 2.1.25 in /examples/WireMock.Net.StandAlone.Net461 [dependencies]
|
|
||||||
- #838 Add option to ProxySettings to append guid to mapping file
|
|
||||||
- #826 Dynamic Body not to be cached when a Func is used to created the body [feature]
|
|
||||||
|
|
||||||
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md
|
||||||
@@ -44,12 +44,10 @@ jobs:
|
|||||||
projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj'
|
projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj'
|
||||||
arguments: '--configuration Debug --framework net6.0'
|
arguments: '--configuration Debug --framework net6.0'
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
- task: CmdLine@2
|
||||||
displayName: 'Execute Unit tests'
|
|
||||||
inputs:
|
inputs:
|
||||||
command: 'test'
|
script: 'dotnet test ./test/WireMock.Net.Tests/WireMock.Net.Tests.csproj --no-build --configuration Debug --framework net6.0'
|
||||||
projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj'
|
displayName: 'Execute Unit Tests with Coverage'
|
||||||
arguments: '--no-build --configuration Debug --framework net6.0 --collect:"XPlat Code Coverage" --logger trx /p:CollectCoverage=true /p:CoverletOutputFormat=opencover'
|
|
||||||
|
|
||||||
- task: SonarCloudAnalyze@1
|
- task: SonarCloudAnalyze@1
|
||||||
displayName: 'SonarCloud: Run Code Analysis'
|
displayName: 'SonarCloud: Run Code Analysis'
|
||||||
@@ -76,7 +74,7 @@ jobs:
|
|||||||
- task: PublishBuildArtifacts@1
|
- task: PublishBuildArtifacts@1
|
||||||
displayName: Publish coverage file
|
displayName: Publish coverage file
|
||||||
inputs:
|
inputs:
|
||||||
PathtoPublish: '/home/vsts/work/1/s/test/WireMock.Net.Tests/coverage.net6.0.opencover.xml'
|
PathtoPublish: './test/WireMock.Net.Tests/coverage.net6.0.opencover.xml'
|
||||||
|
|
||||||
- job: Windows_Build_Test
|
- job: Windows_Build_Test
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
|
||||||
</startup>
|
</startup>
|
||||||
<runtime>
|
<runtime>
|
||||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||||
<dependentAssembly>
|
<dependentAssembly>
|
||||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral"/>
|
||||||
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
|
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0"/>
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
|
|||||||
@@ -8,9 +8,10 @@
|
|||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>WireMock.Net.Service</RootNamespace>
|
<RootNamespace>WireMock.Net.Service</RootNamespace>
|
||||||
<AssemblyName>WireMock.Net.Service</AssemblyName>
|
<AssemblyName>WireMock.Net.Service</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
|||||||
@@ -11,6 +11,6 @@
|
|||||||
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
|
<package id="Newtonsoft.Json" version="13.0.1" targetFramework="net452" />
|
||||||
<package id="Owin" version="1.0" targetFramework="net452" />
|
<package id="Owin" version="1.0" targetFramework="net452" />
|
||||||
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net452" />
|
<package id="SimMetrics.Net" version="1.0.5" targetFramework="net452" />
|
||||||
<package id="System.Net.Http" version="4.3.4" targetFramework="net452" />
|
<package id="System.Net.Http" version="4.3.4" targetFramework="net452" requireReinstallation="true" />
|
||||||
<package id="XPath2" version="1.1.3" targetFramework="net452" />
|
<package id="XPath2" version="1.1.3" targetFramework="net452" />
|
||||||
</packages>
|
</packages>
|
||||||
@@ -89,4 +89,11 @@ public class SettingsModel
|
|||||||
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
/// Don't save the response-string in the LogEntry when WithBody(Func{IRequestMessage, string}) or WithBody(Func{IRequestMessage, Task{string}}) is used. (default set to false).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
|
||||||
|
///
|
||||||
|
/// Default value = "All".
|
||||||
|
/// </summary>
|
||||||
|
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ public interface IRequestMessage
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The ProxyUrl (if a proxy is used).
|
/// The ProxyUrl (if a proxy is used).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string ProxyUrl { get; set; }
|
string? ProxyUrl { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the DateTime.
|
/// Gets the DateTime.
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace WireMock.Types;
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
public enum QueryParameterMultipleValueSupport
|
||||||
|
{
|
||||||
|
// Support none
|
||||||
|
None = 0x0,
|
||||||
|
|
||||||
|
// Support "&" as multi-value-separator --> "key=value&key=anotherValue"
|
||||||
|
Ampersand = 0x1,
|
||||||
|
|
||||||
|
// Support ";" as multi-value-separator --> "key=value;key=anotherValue"
|
||||||
|
SemiColon = 0x2,
|
||||||
|
|
||||||
|
// Support "," as multi-value-separator --> "key=1,2"
|
||||||
|
Comma = 0x4,
|
||||||
|
|
||||||
|
// Support "&" and ";" as multi-value-separator --> "key=value&key=anotherValue" and also "key=value;key=anotherValue"
|
||||||
|
AmpersandAndSemiColon = Ampersand | SemiColon,
|
||||||
|
|
||||||
|
// Support "&" and ";" as multi-value-separator
|
||||||
|
NoComma = AmpersandAndSemiColon,
|
||||||
|
|
||||||
|
// Support all multi-value-separators ("&" and ";" and ",")
|
||||||
|
All = Ampersand | SemiColon | Comma
|
||||||
|
}
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
#if NET46 || NETSTANDARD2_0
|
#if NET46 || NETSTANDARD2_0
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Extensions
|
namespace WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
|
||||||
|
internal static class DictionaryExtensions
|
||||||
{
|
{
|
||||||
internal static class DictionaryExtensions
|
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue>? dictionary, TKey key, TValue value)
|
||||||
{
|
{
|
||||||
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
|
if (dictionary is null || dictionary.ContainsKey(key))
|
||||||
{
|
{
|
||||||
if (dictionary is null || dictionary.ContainsKey(key))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dictionary[key] = value;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dictionary[key] = value;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -1,92 +1,91 @@
|
|||||||
using Microsoft.OpenApi.Any;
|
using Microsoft.OpenApi.Any;
|
||||||
using Microsoft.OpenApi.Interfaces;
|
using Microsoft.OpenApi.Interfaces;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Extensions
|
namespace WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
|
||||||
|
internal static class OpenApiSchemaExtensions
|
||||||
{
|
{
|
||||||
internal static class OpenApiSchemaExtensions
|
/// <summary>
|
||||||
|
/// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
|
||||||
|
/// </summary>
|
||||||
|
public static bool TryGetXNullable(this OpenApiSchema schema, out bool value)
|
||||||
{
|
{
|
||||||
/// <summary>
|
value = false;
|
||||||
/// https://stackoverflow.com/questions/48111459/how-to-define-a-property-that-can-be-string-or-null-in-openapi-swagger
|
|
||||||
/// </summary>
|
if (schema.Extensions.TryGetValue("x-nullable", out IOpenApiExtension e) && e is OpenApiBoolean openApiBoolean)
|
||||||
public static bool TryGetXNullable(this OpenApiSchema schema, out bool value)
|
|
||||||
{
|
{
|
||||||
value = false;
|
value = openApiBoolean.Value;
|
||||||
|
return true;
|
||||||
if (schema.Extensions.TryGetValue("x-nullable", out IOpenApiExtension e) && e is OpenApiBoolean openApiBoolean)
|
|
||||||
{
|
|
||||||
value = openApiBoolean.Value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SchemaType GetSchemaType(this OpenApiSchema schema)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SchemaType GetSchemaType(this OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
switch (schema?.Type)
|
||||||
{
|
{
|
||||||
switch (schema?.Type)
|
case "object":
|
||||||
{
|
return SchemaType.Object;
|
||||||
case "object":
|
|
||||||
return SchemaType.Object;
|
|
||||||
|
|
||||||
case "array":
|
case "array":
|
||||||
return SchemaType.Array;
|
return SchemaType.Array;
|
||||||
|
|
||||||
case "integer":
|
case "integer":
|
||||||
return SchemaType.Integer;
|
return SchemaType.Integer;
|
||||||
|
|
||||||
case "number":
|
case "number":
|
||||||
return SchemaType.Number;
|
return SchemaType.Number;
|
||||||
|
|
||||||
case "boolean":
|
case "boolean":
|
||||||
return SchemaType.Boolean;
|
return SchemaType.Boolean;
|
||||||
|
|
||||||
case "string":
|
case "string":
|
||||||
return SchemaType.String;
|
return SchemaType.String;
|
||||||
|
|
||||||
case "file":
|
case "file":
|
||||||
return SchemaType.File;
|
return SchemaType.File;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return SchemaType.Unknown;
|
return SchemaType.Unknown;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static SchemaFormat GetSchemaFormat(this OpenApiSchema schema)
|
public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
switch (schema?.Format)
|
||||||
{
|
{
|
||||||
switch (schema?.Format)
|
case "float":
|
||||||
{
|
return SchemaFormat.Float;
|
||||||
case "float":
|
|
||||||
return SchemaFormat.Float;
|
|
||||||
|
|
||||||
case "double":
|
case "double":
|
||||||
return SchemaFormat.Double;
|
return SchemaFormat.Double;
|
||||||
|
|
||||||
case "int32":
|
case "int32":
|
||||||
return SchemaFormat.Int32;
|
return SchemaFormat.Int32;
|
||||||
|
|
||||||
case "int64":
|
case "int64":
|
||||||
return SchemaFormat.Int64;
|
return SchemaFormat.Int64;
|
||||||
|
|
||||||
case "date":
|
case "date":
|
||||||
return SchemaFormat.Date;
|
return SchemaFormat.Date;
|
||||||
|
|
||||||
case "date-time":
|
case "date-time":
|
||||||
return SchemaFormat.DateTime;
|
return SchemaFormat.DateTime;
|
||||||
|
|
||||||
case "password":
|
case "password":
|
||||||
return SchemaFormat.Password;
|
return SchemaFormat.Password;
|
||||||
|
|
||||||
case "byte":
|
case "byte":
|
||||||
return SchemaFormat.Byte;
|
return SchemaFormat.Byte;
|
||||||
|
|
||||||
case "binary":
|
case "binary":
|
||||||
return SchemaFormat.Binary;
|
return SchemaFormat.Binary;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return SchemaFormat.Undefined;
|
return SchemaFormat.Undefined;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,97 +1,94 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices.ComTypes;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Microsoft.OpenApi.Readers;
|
using Microsoft.OpenApi.Readers;
|
||||||
using SharpYaml.Model;
|
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Extensions
|
namespace WireMock.Net.OpenApiParser.Extensions;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Some extension methods for <see cref="IWireMockServer"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class WireMockServerExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Some extension methods for <see cref="IWireMockServer"/>.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class WireMockServerExtensions
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
|
[PublicAPI]
|
||||||
|
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
/// <summary>
|
return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
}
|
||||||
/// </summary>
|
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
|
||||||
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
|
||||||
[PublicAPI]
|
|
||||||
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
|
|
||||||
{
|
|
||||||
return WithMappingFromOpenApiFile(server, path, null, out diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
/// <param name="path">Path containing OpenAPI file to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
Guard.NotNull(server, nameof(server));
|
Guard.NotNull(server, nameof(server));
|
||||||
Guard.NotNullOrEmpty(path, nameof(path));
|
Guard.NotNullOrEmpty(path, nameof(path));
|
||||||
|
|
||||||
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
var mappings = new WireMockOpenApiParser().FromFile(path, settings, out diagnostic);
|
||||||
|
|
||||||
return server.WithMapping(mappings.ToArray());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
|
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return WithMappingFromOpenApiStream(server, stream, null, out diagnostic);
|
return WithMappingFromOpenApiStream(server, stream, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 stream.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
/// <param name="stream">Stream containing OpenAPI description to parse and use the mappings.</param>
|
||||||
/// <param name="settings">Additional settings</param>
|
/// <param name="settings">Additional settings</param>
|
||||||
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
/// <param name="diagnostic">Returns diagnostic object containing errors detected during parsing</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
public static IWireMockServer WithMappingFromOpenApiStream(this IWireMockServer server, Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
Guard.NotNull(server, nameof(server));
|
Guard.NotNull(server);
|
||||||
Guard.NotNull(stream, nameof(stream));
|
Guard.NotNull(stream);
|
||||||
Guard.NotNull(settings, nameof(settings));
|
Guard.NotNull(settings);
|
||||||
|
|
||||||
var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
|
var mappings = new WireMockOpenApiParser().FromStream(stream, settings, out diagnostic);
|
||||||
|
|
||||||
return server.WithMapping(mappings.ToArray());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register the mappings via an OpenAPI (swagger) V2 or V3 document.
|
/// Register the mappings via an OpenAPI (swagger) V2 or V3 document.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="server">The WireMockServer instance</param>
|
/// <param name="server">The WireMockServer instance</param>
|
||||||
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
/// <param name="document">The OpenAPI document to use as mappings.</param>
|
||||||
/// <param name="settings">Additional settings [optional]</param>
|
/// <param name="settings">Additional settings [optional]</param>
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings = null)
|
public static IWireMockServer WithMappingFromOpenApiDocument(this IWireMockServer server, OpenApiDocument document, WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
Guard.NotNull(server, nameof(server));
|
Guard.NotNull(server);
|
||||||
Guard.NotNull(document, nameof(document));
|
Guard.NotNull(document);
|
||||||
|
|
||||||
var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
|
var mappings = new WireMockOpenApiParser().FromDocument(document, settings);
|
||||||
|
|
||||||
return server.WithMapping(mappings.ToArray());
|
return server.WithMapping(mappings.ToArray());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using Microsoft.OpenApi.Readers;
|
using Microsoft.OpenApi.Readers;
|
||||||
@@ -35,7 +35,7 @@ namespace WireMock.Net.OpenApiParser
|
|||||||
/// <param name="document">The source OpenApiDocument</param>
|
/// <param name="document">The source OpenApiDocument</param>
|
||||||
/// <param name="settings">Additional settings [optional]</param>
|
/// <param name="settings">Additional settings [optional]</param>
|
||||||
/// <returns>MappingModel</returns>
|
/// <returns>MappingModel</returns>
|
||||||
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings settings = null);
|
IEnumerable<MappingModel> FromDocument(OpenApiDocument document, WireMockOpenApiParserSettings? settings = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
/// Generate <see cref="IEnumerable{MappingModel}"/> from a <seealso cref="Stream"/>.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.OpenApi;
|
using Microsoft.OpenApi;
|
||||||
@@ -15,371 +16,365 @@ using WireMock.Net.OpenApiParser.Settings;
|
|||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
using WireMock.Net.OpenApiParser.Utils;
|
using WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Mappers
|
namespace WireMock.Net.OpenApiParser.Mappers;
|
||||||
|
|
||||||
|
internal class OpenApiPathsMapper
|
||||||
{
|
{
|
||||||
internal class OpenApiPathsMapper
|
private const string HeaderContentType = "Content-Type";
|
||||||
|
|
||||||
|
private readonly WireMockOpenApiParserSettings _settings;
|
||||||
|
private readonly ExampleValueGenerator _exampleValueGenerator;
|
||||||
|
|
||||||
|
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
private const string HeaderContentType = "Content-Type";
|
_settings = Guard.NotNull(settings);
|
||||||
|
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
||||||
|
}
|
||||||
|
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths, IList<OpenApiServer> servers)
|
||||||
private readonly ExampleValueGenerator _exampleValueGenerator;
|
{
|
||||||
|
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
|
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths, IList<OpenApiServer> servers)
|
||||||
|
{
|
||||||
|
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
||||||
|
{
|
||||||
|
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
|
||||||
|
}
|
||||||
|
|
||||||
|
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
||||||
|
{
|
||||||
|
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
|
||||||
|
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
|
||||||
|
var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header);
|
||||||
|
|
||||||
|
var response = operation.Responses.FirstOrDefault();
|
||||||
|
|
||||||
|
TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType);
|
||||||
|
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
|
||||||
|
var responseExample = responseContent?.Example;
|
||||||
|
var responseSchemaExample = responseContent?.Schema?.Example;
|
||||||
|
|
||||||
|
var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) :
|
||||||
|
responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) :
|
||||||
|
MapSchemaToObject(responseSchema);
|
||||||
|
|
||||||
|
var requestBodyModel = new BodyModel();
|
||||||
|
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings, nameof(settings));
|
var request = operation.RequestBody.Content;
|
||||||
_exampleValueGenerator = new ExampleValueGenerator(settings);
|
TryGetContent(request, out OpenApiMediaType? requestContent, out _);
|
||||||
|
|
||||||
|
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
|
||||||
|
var requestBodyExample = requestContent!.Example;
|
||||||
|
var requestBodySchemaExample = requestContent.Schema?.Example;
|
||||||
|
|
||||||
|
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
|
||||||
|
requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) :
|
||||||
|
MapSchemaToObject(requestBodySchema);
|
||||||
|
|
||||||
|
requestBodyModel = MapRequestBody(requestBodyMapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<MappingModel> ToMappingModels(OpenApiPaths paths, IList<OpenApiServer> servers)
|
if (!int.TryParse(response.Key, out var httpStatusCode))
|
||||||
{
|
{
|
||||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
httpStatusCode = 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<MappingModel> MapPaths(OpenApiPaths paths, IList<OpenApiServer> servers)
|
return new MappingModel
|
||||||
{
|
{
|
||||||
return paths.Select(p => MapPath(p.Key, p.Value, servers)).SelectMany(x => x);
|
Guid = Guid.NewGuid(),
|
||||||
}
|
Request = new RequestModel
|
||||||
|
|
||||||
private IEnumerable<MappingModel> MapPath(string path, OpenApiPathItem pathItem, IList<OpenApiServer> servers)
|
|
||||||
{
|
|
||||||
return pathItem.Operations.Select(o => MapOperationToMappingModel(path, o.Key.ToString().ToUpperInvariant(), o.Value, servers));
|
|
||||||
}
|
|
||||||
|
|
||||||
private MappingModel MapOperationToMappingModel(string path, string httpMethod, OpenApiOperation operation, IList<OpenApiServer> servers)
|
|
||||||
{
|
|
||||||
var queryParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Query);
|
|
||||||
var pathParameters = operation.Parameters.Where(p => p.In == ParameterLocation.Path);
|
|
||||||
var headers = operation.Parameters.Where(p => p.In == ParameterLocation.Header);
|
|
||||||
|
|
||||||
var response = operation.Responses.FirstOrDefault();
|
|
||||||
|
|
||||||
TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType);
|
|
||||||
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
|
|
||||||
var responseExample = responseContent?.Example;
|
|
||||||
var responseSchemaExample = responseContent?.Schema?.Example;
|
|
||||||
|
|
||||||
var body = responseExample != null ? MapOpenApiAnyToJToken(responseExample) :
|
|
||||||
responseSchemaExample != null ? MapOpenApiAnyToJToken(responseSchemaExample) :
|
|
||||||
MapSchemaToObject(responseSchema);
|
|
||||||
|
|
||||||
var requestBodyModel = new BodyModel();
|
|
||||||
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
|
|
||||||
{
|
{
|
||||||
var request = operation.RequestBody.Content;
|
Methods = new[] { httpMethod },
|
||||||
TryGetContent(request, out OpenApiMediaType requestContent, out string requestContentType);
|
Path = MapBasePath(servers) + MapPathWithParameters(path, pathParameters),
|
||||||
|
Params = MapQueryParameters(queryParameters),
|
||||||
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
|
Headers = MapRequestHeaders(headers),
|
||||||
var requestBodyExample = requestContent.Example;
|
Body = requestBodyModel
|
||||||
var requestBodySchemaExample = requestContent.Schema?.Example;
|
},
|
||||||
|
Response = new ResponseModel
|
||||||
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
|
{
|
||||||
requestBodySchemaExample != null ? MapOpenApiAnyToJToken(requestBodySchemaExample) :
|
StatusCode = httpStatusCode,
|
||||||
MapSchemaToObject(requestBodySchema);
|
Headers = MapHeaders(responseContentType, response.Value?.Headers),
|
||||||
|
BodyAsJson = body
|
||||||
requestBodyModel = MapRequestBody(requestBodyMapped);
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (!int.TryParse(response.Key, out var httpStatusCode))
|
private BodyModel? MapRequestBody(object? requestBody)
|
||||||
|
{
|
||||||
|
if (requestBody == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BodyModel
|
||||||
|
{
|
||||||
|
Matcher = new MatcherModel
|
||||||
{
|
{
|
||||||
httpStatusCode = 200;
|
Name = "JsonMatcher",
|
||||||
|
Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented),
|
||||||
|
IgnoreCase = _settings.RequestBodyIgnoreCase
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new MappingModel
|
private bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
|
||||||
{
|
{
|
||||||
Guid = Guid.NewGuid(),
|
openApiMediaType = null;
|
||||||
Request = new RequestModel
|
contentType = null;
|
||||||
|
|
||||||
|
if (contents == null || contents.Values.Count == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contents.TryGetValue("application/json", out var content))
|
||||||
|
{
|
||||||
|
openApiMediaType = content;
|
||||||
|
contentType = "application/json";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var first = contents.FirstOrDefault();
|
||||||
|
openApiMediaType = first.Value;
|
||||||
|
contentType = first.Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null)
|
||||||
|
{
|
||||||
|
if (schema == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (schema.GetSchemaType())
|
||||||
|
{
|
||||||
|
case SchemaType.Array:
|
||||||
|
var jArray = new JArray();
|
||||||
|
for (int i = 0; i < _settings.NumberOfArrayItems; i++)
|
||||||
{
|
{
|
||||||
Methods = new[] { httpMethod },
|
if (schema.Items.Properties.Count > 0)
|
||||||
Path = MapBasePath(servers) + MapPathWithParameters(path, pathParameters),
|
|
||||||
Params = MapQueryParameters(queryParameters),
|
|
||||||
Headers = MapRequestHeaders(headers),
|
|
||||||
Body = requestBodyModel
|
|
||||||
},
|
|
||||||
Response = new ResponseModel
|
|
||||||
{
|
|
||||||
StatusCode = httpStatusCode,
|
|
||||||
Headers = MapHeaders(responseContentType, response.Value?.Headers),
|
|
||||||
BodyAsJson = body
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private BodyModel MapRequestBody(object requestBody)
|
|
||||||
{
|
|
||||||
if (requestBody == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BodyModel
|
|
||||||
{
|
|
||||||
Matcher = new MatcherModel
|
|
||||||
{
|
|
||||||
Name = "JsonMatcher",
|
|
||||||
Pattern = JsonConvert.SerializeObject(requestBody, Formatting.Indented),
|
|
||||||
IgnoreCase = _settings.RequestBodyIgnoreCase
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryGetContent(IDictionary<string, OpenApiMediaType> contents, out OpenApiMediaType openApiMediaType, out string contentType)
|
|
||||||
{
|
|
||||||
openApiMediaType = null;
|
|
||||||
contentType = null;
|
|
||||||
|
|
||||||
if (contents == null || contents.Values.Count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (contents.TryGetValue("application/json", out var content))
|
|
||||||
{
|
|
||||||
openApiMediaType = content;
|
|
||||||
contentType = "application/json";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var first = contents.FirstOrDefault();
|
|
||||||
openApiMediaType = first.Value;
|
|
||||||
contentType = first.Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object MapSchemaToObject(OpenApiSchema schema, string name = null)
|
|
||||||
{
|
|
||||||
if (schema == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (schema.GetSchemaType())
|
|
||||||
{
|
|
||||||
case SchemaType.Array:
|
|
||||||
var jArray = new JArray();
|
|
||||||
for (int i = 0; i < _settings.NumberOfArrayItems; i++)
|
|
||||||
{
|
{
|
||||||
if (schema.Items.Properties.Count > 0)
|
var arrayItem = new JObject();
|
||||||
|
foreach (var property in schema.Items.Properties)
|
||||||
{
|
{
|
||||||
var arrayItem = new JObject();
|
var objectValue = MapSchemaToObject(property.Value, property.Key);
|
||||||
foreach (var property in schema.Items.Properties)
|
if (objectValue is JProperty jp)
|
||||||
{
|
{
|
||||||
var objectValue = MapSchemaToObject(property.Value, property.Key);
|
arrayItem.Add(jp);
|
||||||
if (objectValue is JProperty jp)
|
|
||||||
{
|
|
||||||
arrayItem.Add(jp);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
arrayItem.Add(new JProperty(property.Key, objectValue));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
jArray.Add(arrayItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
jArray.Add(MapSchemaToObject(schema.Items, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schema.AllOf.Count > 0)
|
|
||||||
{
|
|
||||||
jArray.Add(MapSchemaAllOfToObject(schema));
|
|
||||||
}
|
|
||||||
|
|
||||||
return jArray;
|
|
||||||
|
|
||||||
case SchemaType.Boolean:
|
|
||||||
case SchemaType.Integer:
|
|
||||||
case SchemaType.Number:
|
|
||||||
case SchemaType.String:
|
|
||||||
return _exampleValueGenerator.GetExampleValue(schema);
|
|
||||||
|
|
||||||
case SchemaType.Object:
|
|
||||||
var propertyAsJObject = new JObject();
|
|
||||||
foreach (var schemaProperty in schema.Properties)
|
|
||||||
{
|
|
||||||
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
|
|
||||||
}
|
|
||||||
if (schema.AllOf.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var property in schema.AllOf)
|
|
||||||
{
|
|
||||||
foreach (var item in property.Properties)
|
|
||||||
{
|
{
|
||||||
propertyAsJObject.Add(MapPropertyAsJObject(item.Value, item.Key));
|
arrayItem.Add(new JProperty(property.Key, objectValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jArray.Add(arrayItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jArray.Add(MapSchemaToObject(schema.Items, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JObject MapSchemaAllOfToObject(OpenApiSchema schema)
|
|
||||||
{
|
|
||||||
var arrayItem = new JObject();
|
|
||||||
foreach (var property in schema.AllOf)
|
|
||||||
{
|
|
||||||
foreach (var item in property.Properties)
|
|
||||||
{
|
|
||||||
arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return arrayItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key)
|
if (schema.AllOf.Count > 0)
|
||||||
{
|
|
||||||
if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array)
|
|
||||||
{
|
|
||||||
var mapped = MapSchemaToObject(openApiSchema, key);
|
|
||||||
if (mapped is JProperty jp)
|
|
||||||
{
|
{
|
||||||
return jp;
|
jArray.Add(MapSchemaAllOfToObject(schema));
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
return jArray;
|
||||||
|
|
||||||
|
case SchemaType.Boolean:
|
||||||
|
case SchemaType.Integer:
|
||||||
|
case SchemaType.Number:
|
||||||
|
case SchemaType.String:
|
||||||
|
return _exampleValueGenerator.GetExampleValue(schema);
|
||||||
|
|
||||||
|
case SchemaType.Object:
|
||||||
|
var propertyAsJObject = new JObject();
|
||||||
|
foreach (var schemaProperty in schema.Properties)
|
||||||
{
|
{
|
||||||
return new JProperty(key, mapped);
|
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
if (schema.AllOf.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var property in schema.AllOf)
|
||||||
|
{
|
||||||
|
foreach (var item in property.Properties)
|
||||||
|
{
|
||||||
|
propertyAsJObject.Add(MapPropertyAsJObject(item.Value, item.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JObject MapSchemaAllOfToObject(OpenApiSchema schema)
|
||||||
|
{
|
||||||
|
var arrayItem = new JObject();
|
||||||
|
foreach (var property in schema.AllOf)
|
||||||
|
{
|
||||||
|
foreach (var item in property.Properties)
|
||||||
{
|
{
|
||||||
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
|
arrayItem.Add(MapPropertyAsJObject(item.Value, item.Key));
|
||||||
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return arrayItem;
|
||||||
|
}
|
||||||
|
|
||||||
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters)
|
private object MapPropertyAsJObject(OpenApiSchema openApiSchema, string key)
|
||||||
|
{
|
||||||
|
if (openApiSchema.GetSchemaType() == SchemaType.Object || openApiSchema.GetSchemaType() == SchemaType.Array)
|
||||||
{
|
{
|
||||||
if (parameters == null)
|
var mapped = MapSchemaToObject(openApiSchema, key);
|
||||||
|
if (mapped is JProperty jp)
|
||||||
{
|
{
|
||||||
return path;
|
return jp;
|
||||||
}
|
}
|
||||||
|
|
||||||
string newPath = path;
|
return new JProperty(key, mapped);
|
||||||
foreach (var parameter in parameters)
|
|
||||||
{
|
|
||||||
var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse);
|
|
||||||
newPath = newPath.Replace($"{{{parameter.Name}}}", exampleMatcherModel.Pattern as string);
|
|
||||||
}
|
|
||||||
|
|
||||||
return newPath;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string MapBasePath(IList<OpenApiServer> servers)
|
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
|
||||||
|
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter>? parameters)
|
||||||
|
{
|
||||||
|
if (parameters == null)
|
||||||
{
|
{
|
||||||
if (servers == null || servers.Count == 0)
|
return path;
|
||||||
{
|
}
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
OpenApiServer server = servers.First();
|
string newPath = path;
|
||||||
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
foreach (var parameter in parameters)
|
||||||
{
|
{
|
||||||
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
var exampleMatcherModel = GetExampleMatcherModel(parameter.Schema, _settings.PathPatternToUse);
|
||||||
}
|
newPath = newPath.Replace($"{{{parameter.Name}}}", exampleMatcherModel.Pattern as string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string MapBasePath(IList<OpenApiServer>? servers)
|
||||||
|
{
|
||||||
|
if (servers == null || servers.Count == 0)
|
||||||
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JToken MapOpenApiAnyToJToken(IOpenApiAny any)
|
OpenApiServer server = servers.First();
|
||||||
|
if (Uri.TryCreate(server.Url, UriKind.RelativeOrAbsolute, out Uri uriResult))
|
||||||
{
|
{
|
||||||
if (any == null)
|
return uriResult.IsAbsoluteUri ? uriResult.AbsolutePath : uriResult.ToString();
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
using var outputString = new StringWriter();
|
|
||||||
var writer = new OpenApiJsonWriter(outputString);
|
|
||||||
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
|
||||||
|
|
||||||
if (any.AnyType == AnyType.Array)
|
|
||||||
{
|
|
||||||
return JArray.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return JObject.Parse(outputString.ToString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
|
||||||
|
{
|
||||||
|
if (any == null)
|
||||||
{
|
{
|
||||||
var mappedHeaders = headers.ToDictionary(
|
return null;
|
||||||
item => item.Key,
|
|
||||||
item => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(responseContentType))
|
|
||||||
{
|
|
||||||
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
|
using var outputString = new StringWriter();
|
||||||
|
var writer = new OpenApiJsonWriter(outputString);
|
||||||
|
any.Write(writer, OpenApiSpecVersion.OpenApi3_0);
|
||||||
|
|
||||||
|
if (any.AnyType == AnyType.Array)
|
||||||
{
|
{
|
||||||
var list = queryParameters
|
return JArray.Parse(outputString.ToString());
|
||||||
.Where(req => req.Required)
|
}
|
||||||
.Select(qp => new ParamModel
|
|
||||||
|
return JObject.Parse(outputString.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
|
||||||
|
{
|
||||||
|
var mappedHeaders = headers.ToDictionary(
|
||||||
|
item => item.Key,
|
||||||
|
_ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(responseContentType))
|
||||||
|
{
|
||||||
|
mappedHeaders.TryAdd(HeaderContentType, responseContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mappedHeaders.Keys.Any() ? mappedHeaders : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
|
||||||
|
{
|
||||||
|
var list = queryParameters
|
||||||
|
.Where(req => req.Required)
|
||||||
|
.Select(qp => new ParamModel
|
||||||
|
{
|
||||||
|
Name = qp.Name,
|
||||||
|
IgnoreCase = _settings.QueryParameterPatternIgnoreCase,
|
||||||
|
Matchers = new[]
|
||||||
{
|
{
|
||||||
Name = qp.Name,
|
GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse)
|
||||||
IgnoreCase = _settings.QueryParameterPatternIgnoreCase,
|
}
|
||||||
Matchers = new[]
|
})
|
||||||
{
|
.ToList();
|
||||||
GetExampleMatcherModel(qp.Schema, _settings.QueryParameterPatternToUse)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return list.Any() ? list : null;
|
return list.Any() ? list : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<HeaderModel> MapRequestHeaders(IEnumerable<OpenApiParameter> headers)
|
private IList<HeaderModel>? MapRequestHeaders(IEnumerable<OpenApiParameter> headers)
|
||||||
{
|
{
|
||||||
var list = headers
|
var list = headers
|
||||||
.Where(req => req.Required)
|
.Where(req => req.Required)
|
||||||
.Select(qp => new HeaderModel
|
.Select(qp => new HeaderModel
|
||||||
|
{
|
||||||
|
Name = qp.Name,
|
||||||
|
IgnoreCase = _settings.HeaderPatternIgnoreCase,
|
||||||
|
Matchers = new[]
|
||||||
{
|
{
|
||||||
Name = qp.Name,
|
GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse)
|
||||||
IgnoreCase = _settings.HeaderPatternIgnoreCase,
|
}
|
||||||
Matchers = new[]
|
})
|
||||||
{
|
.ToList();
|
||||||
GetExampleMatcherModel(qp.Schema, _settings.HeaderPatternToUse)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return list.Any() ? list : null;
|
return list.Any() ? list : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatcherModel GetExampleMatcherModel(OpenApiSchema schema, ExampleValueType type)
|
private MatcherModel GetExampleMatcherModel(OpenApiSchema? schema, ExampleValueType type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
{
|
{
|
||||||
return type switch
|
ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues },
|
||||||
{
|
|
||||||
ExampleValueType.Value => new MatcherModel { Name = "ExactMatcher", Pattern = GetExampleValueAsStringForSchemaType(schema), IgnoreCase = _settings.IgnoreCaseExampleValues },
|
|
||||||
|
|
||||||
_ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" }
|
_ => new MatcherModel { Name = "WildcardMatcher", Pattern = "*" }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetExampleValueAsStringForSchemaType(OpenApiSchema schema)
|
private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
|
||||||
|
{
|
||||||
|
var value = _exampleValueGenerator.GetExampleValue(schema);
|
||||||
|
|
||||||
|
return value switch
|
||||||
{
|
{
|
||||||
var value = _exampleValueGenerator.GetExampleValue(schema);
|
string valueAsString => valueAsString,
|
||||||
|
|
||||||
return value switch
|
_ => value.ToString(),
|
||||||
{
|
};
|
||||||
string valueAsString => valueAsString,
|
|
||||||
|
|
||||||
_ => value.ToString(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,58 +1,60 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Settings
|
namespace WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A interface defining the example values to use for the different types.
|
||||||
|
/// </summary>
|
||||||
|
public interface IWireMockOpenApiParserExampleValues
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A interface defining the example values to use for the different types.
|
/// An example value for a Boolean.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IWireMockOpenApiParserExampleValues
|
bool Boolean { get; set; }
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for a Boolean.
|
|
||||||
/// </summary>
|
|
||||||
bool Boolean { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for an Integer.
|
|
||||||
/// </summary>
|
|
||||||
int Integer { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for a Float.
|
|
||||||
/// </summary>
|
|
||||||
float Float { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// An example value for a Double.
|
|
||||||
/// </summary>
|
|
||||||
double Double { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a Date.
|
/// An example value for an Integer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Func<DateTime> Date { get; set; }
|
int Integer { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a DateTime.
|
/// An example value for a Float.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Func<DateTime> DateTime { get; set; }
|
float Float { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for Bytes.
|
/// An example value for a Double.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
byte[] Bytes { get; set; }
|
double Double { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a Object.
|
/// An example value for a Date.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object Object { get; set; }
|
Func<DateTime> Date { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An example value for a String.
|
/// An example value for a DateTime.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
string String { get; set; }
|
Func<DateTime> DateTime { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// OpenApi Schema to generate dynamic examples more accurate
|
/// An example value for Bytes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
OpenApiSchema Schema { get; set; }
|
byte[] Bytes { get; set; }
|
||||||
}
|
|
||||||
|
/// <summary>
|
||||||
|
/// An example value for a Object.
|
||||||
|
/// </summary>
|
||||||
|
object Object { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An example value for a String.
|
||||||
|
/// </summary>
|
||||||
|
string String { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// OpenApi Schema to generate dynamic examples more accurate
|
||||||
|
/// </summary>
|
||||||
|
OpenApiSchema? Schema { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,34 +1,42 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using RandomDataGenerator.FieldOptions;
|
using RandomDataGenerator.FieldOptions;
|
||||||
using RandomDataGenerator.Randomizers;
|
using RandomDataGenerator.Randomizers;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Settings
|
namespace WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A class defining the random example values to use for the different types.
|
||||||
|
/// </summary>
|
||||||
|
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// A class defining the random example values to use for the different types.
|
public virtual bool Boolean { get => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; set { } }
|
||||||
/// </summary>
|
|
||||||
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
|
/// <inheritdoc />
|
||||||
{
|
public virtual int Integer { get => RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; set { } }
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } }
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual float Float { get => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; set { } }
|
||||||
public virtual int Integer { get { return RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; } set { } }
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual float Float { get { return RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; } set { } }
|
public virtual double Double { get => RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; set { } }
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual double Double { get { return RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; } set { } }
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual Func<DateTime> Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } }
|
||||||
public virtual Func<DateTime> Date { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow.Date; } set { } }
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual Func<DateTime> DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } }
|
public virtual Func<DateTime> DateTime { get { return () => RandomizerFactory.GetRandomizer(new FieldOptionsDateTime()).Generate() ?? System.DateTime.UtcNow; } set { } }
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } }
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual byte[] Bytes { get => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); set { } }
|
||||||
public virtual object Object { get; set; } = "example-object";
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual string String { get { return RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; } set { } }
|
public virtual object Object { get; set; } = "example-object";
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual OpenApiSchema Schema { get; set; }
|
/// <inheritdoc />
|
||||||
}
|
public virtual string String { get => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; set { } }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual OpenApiSchema? Schema { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,32 +1,40 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Settings
|
namespace WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A class defining the example values to use for the different types.
|
||||||
|
/// </summary>
|
||||||
|
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// A class defining the example values to use for the different types.
|
public virtual bool Boolean { get; set; } = true;
|
||||||
/// </summary>
|
|
||||||
public class WireMockOpenApiParserExampleValues : IWireMockOpenApiParserExampleValues
|
/// <inheritdoc />
|
||||||
{
|
public virtual int Integer { get; set; } = 42;
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual bool Boolean { get; set; } = true;
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual float Float { get; set; } = 4.2f;
|
||||||
public virtual int Integer { get; set; } = 42;
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual float Float { get; set; } = 4.2f;
|
public virtual double Double { get; set; } = 4.2d;
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual double Double { get; set; } = 4.2d;
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
|
||||||
public virtual Func<DateTime> Date { get; set; } = () => System.DateTime.UtcNow.Date;
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow;
|
public virtual Func<DateTime> DateTime { get; set; } = () => System.DateTime.UtcNow;
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
|
/// <inheritdoc />
|
||||||
/// <inheritdoc />
|
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
|
||||||
public virtual object Object { get; set; } = "example-object";
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public virtual string String { get; set; } = "example-string";
|
public virtual object Object { get; set; } = "example-object";
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual OpenApiSchema Schema { get; set; } = new OpenApiSchema();
|
/// <inheritdoc />
|
||||||
}
|
public virtual string String { get; set; } = "example-string";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
|
||||||
}
|
}
|
||||||
@@ -1,64 +1,63 @@
|
|||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Settings
|
namespace WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The WireMockOpenApiParser Settings
|
||||||
|
/// </summary>
|
||||||
|
public class WireMockOpenApiParserSettings
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The WireMockOpenApiParser Settings
|
/// The number of array items to generate (default is 3).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class WireMockOpenApiParserSettings
|
public int NumberOfArrayItems { get; set; } = 3;
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The number of array items to generate (default is 3).
|
|
||||||
/// </summary>
|
|
||||||
public int NumberOfArrayItems { get; set; } = 3;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value type to use when generating a Path
|
/// The example value type to use when generating a Path
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value;
|
public ExampleValueType PathPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value type to use when generating a Header
|
/// The example value type to use when generating a Header
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value;
|
public ExampleValueType HeaderPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value type to use when generating a Query Parameter
|
/// The example value type to use when generating a Query Parameter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExampleValueType QueryParameterPatternToUse { get; set; } = ExampleValueType.Value;
|
public ExampleValueType QueryParameterPatternToUse { get; set; } = ExampleValueType.Value;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example values to use.
|
/// The example values to use.
|
||||||
///
|
///
|
||||||
/// Default implementations are:
|
/// Default implementations are:
|
||||||
/// - <see cref="WireMockOpenApiParserExampleValues"/>
|
/// - <see cref="WireMockOpenApiParserExampleValues"/>
|
||||||
/// - <see cref="WireMockOpenApiParserDynamicExampleValues"/>
|
/// - <see cref="WireMockOpenApiParserDynamicExampleValues"/>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IWireMockOpenApiParserExampleValues ExampleValues { get; set; }
|
public IWireMockOpenApiParserExampleValues? ExampleValues { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a Header match case insensitive? (default is true).
|
/// Is a Header match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HeaderPatternIgnoreCase { get; set; } = true;
|
public bool HeaderPatternIgnoreCase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a Query Parameter match case insensitive? (default is true).
|
/// Is a Query Parameter match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool QueryParameterPatternIgnoreCase { get; set; } = true;
|
public bool QueryParameterPatternIgnoreCase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a Request Body match case insensitive? (default is true).
|
/// Is a Request Body match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequestBodyIgnoreCase { get; set; } = true;
|
public bool RequestBodyIgnoreCase { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Is a ExampleValue match case insensitive? (default is true).
|
/// Is a ExampleValue match case insensitive? (default is true).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IgnoreCaseExampleValues { get; set; } = true;
|
public bool IgnoreCaseExampleValues { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Are examples generated dynamically? (default is false).
|
/// Are examples generated dynamically? (default is false).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DynamicExamples { get; set; } = false;
|
public bool DynamicExamples { get; set; } = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,20 +1,19 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The example value to use
|
||||||
|
/// </summary>
|
||||||
|
public enum ExampleValueType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The example value to use
|
/// 1. Use a generated example value based on the SchemaType (default).
|
||||||
|
/// 2. If there is no example value defined in the schema,
|
||||||
|
/// then the <see cref="Settings.IWireMockOpenApiParserExampleValues"/> will be used (custom, fixed or dynamic).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ExampleValueType
|
Value,
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 1. Use a generated example value based on the SchemaType (default).
|
|
||||||
/// 2. If there is no example value defined in the schema,
|
|
||||||
/// then the <see cref="Settings.IWireMockOpenApiParserExampleValues"/> will be used (custom, fixed or dynamic).
|
|
||||||
/// </summary>
|
|
||||||
Value,
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Just use a Wildcard (*) character.
|
/// Just use a Wildcard (*) character.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Wildcard
|
Wildcard
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,24 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
internal enum SchemaFormat
|
||||||
{
|
{
|
||||||
internal enum SchemaFormat
|
Float,
|
||||||
{
|
|
||||||
Float,
|
|
||||||
|
|
||||||
Double,
|
Double,
|
||||||
|
|
||||||
Int32,
|
Int32,
|
||||||
|
|
||||||
Int64,
|
Int64,
|
||||||
|
|
||||||
Date,
|
Date,
|
||||||
|
|
||||||
DateTime,
|
DateTime,
|
||||||
|
|
||||||
Password,
|
Password,
|
||||||
|
|
||||||
Byte,
|
Byte,
|
||||||
|
|
||||||
Binary,
|
Binary,
|
||||||
|
|
||||||
Undefined
|
Undefined
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,20 @@
|
|||||||
namespace WireMock.Net.OpenApiParser.Types
|
namespace WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
|
internal enum SchemaType
|
||||||
{
|
{
|
||||||
internal enum SchemaType
|
Object,
|
||||||
{
|
|
||||||
Object,
|
|
||||||
|
|
||||||
Array,
|
Array,
|
||||||
|
|
||||||
String,
|
String,
|
||||||
|
|
||||||
Integer,
|
Integer,
|
||||||
|
|
||||||
Number,
|
Number,
|
||||||
|
|
||||||
Boolean,
|
Boolean,
|
||||||
|
|
||||||
File,
|
File,
|
||||||
|
|
||||||
Unknown
|
Unknown
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Utils
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
{
|
|
||||||
internal static class DateTimeUtils
|
|
||||||
{
|
|
||||||
public static string ToRfc3339DateTime(DateTime dateTime)
|
|
||||||
{
|
|
||||||
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToRfc3339Date(DateTime dateTime)
|
internal static class DateTimeUtils
|
||||||
{
|
{
|
||||||
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
|
public static string ToRfc3339DateTime(DateTime dateTime)
|
||||||
}
|
{
|
||||||
|
return dateTime.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToRfc3339Date(DateTime dateTime)
|
||||||
|
{
|
||||||
|
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,121 +7,120 @@ using WireMock.Net.OpenApiParser.Extensions;
|
|||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
using WireMock.Net.OpenApiParser.Types;
|
using WireMock.Net.OpenApiParser.Types;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser.Utils
|
namespace WireMock.Net.OpenApiParser.Utils;
|
||||||
|
|
||||||
|
internal class ExampleValueGenerator
|
||||||
{
|
{
|
||||||
internal class ExampleValueGenerator
|
private readonly WireMockOpenApiParserSettings _settings;
|
||||||
|
|
||||||
|
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
||||||
{
|
{
|
||||||
private readonly WireMockOpenApiParserSettings _settings;
|
_settings = Guard.NotNull(settings);
|
||||||
|
|
||||||
public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
|
// Check if user provided an own implementation
|
||||||
|
if (settings.ExampleValues is null)
|
||||||
{
|
{
|
||||||
_settings = Guard.NotNull(settings, nameof(settings));
|
if (_settings.DynamicExamples)
|
||||||
|
|
||||||
// Check if user provided an own implementation
|
|
||||||
if (settings.ExampleValues is null)
|
|
||||||
{
|
{
|
||||||
if (_settings.DynamicExamples)
|
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
||||||
{
|
|
||||||
_settings.ExampleValues = new WireMockOpenApiParserDynamicExampleValues();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
else
|
||||||
|
|
||||||
public object GetExampleValue(OpenApiSchema schema)
|
|
||||||
{
|
|
||||||
var schemaExample = schema?.Example;
|
|
||||||
var schemaEnum = GetRandomEnumValue(schema?.Enum);
|
|
||||||
|
|
||||||
_settings.ExampleValues.Schema = schema;
|
|
||||||
|
|
||||||
switch (schema?.GetSchemaType())
|
|
||||||
{
|
{
|
||||||
case SchemaType.Boolean:
|
_settings.ExampleValues = new WireMockOpenApiParserExampleValues();
|
||||||
var exampleBoolean = (OpenApiBoolean)schemaExample;
|
|
||||||
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
|
|
||||||
|
|
||||||
case SchemaType.Integer:
|
|
||||||
switch (schema?.GetSchemaFormat())
|
|
||||||
{
|
|
||||||
case SchemaFormat.Int64:
|
|
||||||
var exampleLong = (OpenApiLong)schemaExample;
|
|
||||||
var enumLong = (OpenApiLong)schemaEnum;
|
|
||||||
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
|
|
||||||
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var exampleInteger = (OpenApiInteger)schemaExample;
|
|
||||||
var enumInteger = (OpenApiInteger)schemaEnum;
|
|
||||||
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
|
|
||||||
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SchemaType.Number:
|
|
||||||
switch (schema?.GetSchemaFormat())
|
|
||||||
{
|
|
||||||
case SchemaFormat.Float:
|
|
||||||
var exampleFloat = (OpenApiFloat)schemaExample;
|
|
||||||
var enumFloat = (OpenApiFloat)schemaEnum;
|
|
||||||
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
|
|
||||||
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var exampleDouble = (OpenApiDouble)schemaExample;
|
|
||||||
var enumDouble = (OpenApiDouble)schemaEnum;
|
|
||||||
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
|
|
||||||
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
switch (schema?.GetSchemaFormat())
|
|
||||||
{
|
|
||||||
case SchemaFormat.Date:
|
|
||||||
var exampleDate = (OpenApiDate)schemaExample;
|
|
||||||
var enumDate = (OpenApiDate)schemaEnum;
|
|
||||||
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
|
|
||||||
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
|
|
||||||
|
|
||||||
case SchemaFormat.DateTime:
|
|
||||||
var exampleDateTime = (OpenApiDateTime)schemaExample;
|
|
||||||
var enumDateTime = (OpenApiDateTime)schemaEnum;
|
|
||||||
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
|
|
||||||
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
|
|
||||||
|
|
||||||
case SchemaFormat.Byte:
|
|
||||||
var exampleByte = (OpenApiByte)schemaExample;
|
|
||||||
var enumByte = (OpenApiByte)schemaEnum;
|
|
||||||
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
|
|
||||||
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
|
|
||||||
|
|
||||||
case SchemaFormat.Binary:
|
|
||||||
var exampleBinary = (OpenApiBinary)schemaExample;
|
|
||||||
var enumBinary = (OpenApiBinary)schemaEnum;
|
|
||||||
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
|
|
||||||
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var exampleString = (OpenApiString)schemaExample;
|
|
||||||
var enumString = (OpenApiString)schemaEnum;
|
|
||||||
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
|
|
||||||
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private static IOpenApiAny GetRandomEnumValue(IList<IOpenApiAny> schemaEnum)
|
|
||||||
{
|
public object GetExampleValue(OpenApiSchema? schema)
|
||||||
if (schemaEnum?.Count > 0)
|
{
|
||||||
{
|
var schemaExample = schema?.Example;
|
||||||
int maxValue = schemaEnum.Count - 1;
|
var schemaEnum = GetRandomEnumValue(schema?.Enum);
|
||||||
int randomEnum = new Random().Next(0, maxValue);
|
|
||||||
return schemaEnum[randomEnum];
|
_settings.ExampleValues.Schema = schema;
|
||||||
}
|
|
||||||
|
switch (schema?.GetSchemaType())
|
||||||
return null;
|
{
|
||||||
}
|
case SchemaType.Boolean:
|
||||||
|
var exampleBoolean = schemaExample as OpenApiBoolean;
|
||||||
|
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
|
||||||
|
|
||||||
|
case SchemaType.Integer:
|
||||||
|
switch (schema?.GetSchemaFormat())
|
||||||
|
{
|
||||||
|
case SchemaFormat.Int64:
|
||||||
|
var exampleLong = (OpenApiLong)schemaExample;
|
||||||
|
var enumLong = (OpenApiLong)schemaEnum;
|
||||||
|
var valueLongEnumOrExample = enumLong is null ? exampleLong?.Value : enumLong?.Value;
|
||||||
|
return valueLongEnumOrExample ?? _settings.ExampleValues.Integer;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var exampleInteger = (OpenApiInteger)schemaExample;
|
||||||
|
var enumInteger = (OpenApiInteger)schemaEnum;
|
||||||
|
var valueIntegerEnumOrExample = enumInteger is null ? exampleInteger?.Value : enumInteger?.Value;
|
||||||
|
return valueIntegerEnumOrExample ?? _settings.ExampleValues.Integer;
|
||||||
|
}
|
||||||
|
|
||||||
|
case SchemaType.Number:
|
||||||
|
switch (schema?.GetSchemaFormat())
|
||||||
|
{
|
||||||
|
case SchemaFormat.Float:
|
||||||
|
var exampleFloat = (OpenApiFloat)schemaExample;
|
||||||
|
var enumFloat = (OpenApiFloat)schemaEnum;
|
||||||
|
var valueFloatEnumOrExample = enumFloat is null ? exampleFloat?.Value : enumFloat?.Value;
|
||||||
|
return valueFloatEnumOrExample ?? _settings.ExampleValues.Float;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var exampleDouble = (OpenApiDouble)schemaExample;
|
||||||
|
var enumDouble = (OpenApiDouble)schemaEnum;
|
||||||
|
var valueDoubleEnumOrExample = enumDouble is null ? exampleDouble?.Value : enumDouble?.Value;
|
||||||
|
return valueDoubleEnumOrExample ?? _settings.ExampleValues.Double;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
switch (schema?.GetSchemaFormat())
|
||||||
|
{
|
||||||
|
case SchemaFormat.Date:
|
||||||
|
var exampleDate = (OpenApiDate)schemaExample;
|
||||||
|
var enumDate = (OpenApiDate)schemaEnum;
|
||||||
|
var valueDateEnumOrExample = enumDate is null ? exampleDate?.Value : enumDate?.Value;
|
||||||
|
return DateTimeUtils.ToRfc3339Date(valueDateEnumOrExample ?? _settings.ExampleValues.Date());
|
||||||
|
|
||||||
|
case SchemaFormat.DateTime:
|
||||||
|
var exampleDateTime = (OpenApiDateTime)schemaExample;
|
||||||
|
var enumDateTime = (OpenApiDateTime)schemaEnum;
|
||||||
|
var valueDateTimeEnumOrExample = enumDateTime is null ? exampleDateTime?.Value : enumDateTime?.Value;
|
||||||
|
return DateTimeUtils.ToRfc3339DateTime(valueDateTimeEnumOrExample?.DateTime ?? _settings.ExampleValues.DateTime());
|
||||||
|
|
||||||
|
case SchemaFormat.Byte:
|
||||||
|
var exampleByte = (OpenApiByte)schemaExample;
|
||||||
|
var enumByte = (OpenApiByte)schemaEnum;
|
||||||
|
var valueByteEnumOrExample = enumByte is null ? exampleByte?.Value : enumByte?.Value;
|
||||||
|
return valueByteEnumOrExample ?? _settings.ExampleValues.Bytes;
|
||||||
|
|
||||||
|
case SchemaFormat.Binary:
|
||||||
|
var exampleBinary = (OpenApiBinary)schemaExample;
|
||||||
|
var enumBinary = (OpenApiBinary)schemaEnum;
|
||||||
|
var valueBinaryEnumOrExample = enumBinary is null ? exampleBinary?.Value : enumBinary?.Value;
|
||||||
|
return valueBinaryEnumOrExample ?? _settings.ExampleValues.Object;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var exampleString = (OpenApiString)schemaExample;
|
||||||
|
var enumString = (OpenApiString)schemaEnum;
|
||||||
|
var valueStringEnumOrExample = enumString is null ? exampleString?.Value : enumString?.Value;
|
||||||
|
return valueStringEnumOrExample ?? _settings.ExampleValues.String;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IOpenApiAny? GetRandomEnumValue(IList<IOpenApiAny>? schemaEnum)
|
||||||
|
{
|
||||||
|
if (schemaEnum?.Count > 0)
|
||||||
|
{
|
||||||
|
int maxValue = schemaEnum.Count - 1;
|
||||||
|
int randomEnum = new Random().Next(0, maxValue);
|
||||||
|
return schemaEnum[randomEnum];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
<LangVersion>8.0</LangVersion>
|
<LangVersion>10</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" />
|
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.2.3" />
|
||||||
|
<PackageReference Include="Nullable" Version="1.3.1">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
<PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" />
|
<PackageReference Include="RamlToOpenApiConverter" Version="0.6.1" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
|
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" PrivateAssets="All" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
@@ -9,60 +9,59 @@ using WireMock.Admin.Mappings;
|
|||||||
using WireMock.Net.OpenApiParser.Mappers;
|
using WireMock.Net.OpenApiParser.Mappers;
|
||||||
using WireMock.Net.OpenApiParser.Settings;
|
using WireMock.Net.OpenApiParser.Settings;
|
||||||
|
|
||||||
namespace WireMock.Net.OpenApiParser
|
namespace WireMock.Net.OpenApiParser;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
|
||||||
|
/// </summary>
|
||||||
|
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
||||||
{
|
{
|
||||||
/// <summary>
|
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
||||||
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
|
|
||||||
/// </summary>
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
||||||
public class WireMockOpenApiParser : IWireMockOpenApiParser
|
[PublicAPI]
|
||||||
|
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
private readonly OpenApiStreamReader _reader = new OpenApiStreamReader();
|
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, out OpenApiDiagnostic)" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromFile(string path, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
OpenApiDocument document;
|
||||||
|
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return FromFile(path, new WireMockOpenApiParserSettings(), out diagnostic);
|
diagnostic = new OpenApiDiagnostic();
|
||||||
|
document = new RamlConverter().ConvertToOpenApiDocument(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var reader = new OpenApiStreamReader();
|
||||||
|
document = reader.Read(File.OpenRead(path), out diagnostic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromFile(string, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
return FromDocument(document, settings);
|
||||||
[PublicAPI]
|
}
|
||||||
public IEnumerable<MappingModel> FromFile(string path, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
|
||||||
{
|
|
||||||
OpenApiDocument document;
|
|
||||||
if (Path.GetExtension(path).EndsWith("raml", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
diagnostic = new OpenApiDiagnostic();
|
|
||||||
document = new RamlConverter().ConvertToOpenApiDocument(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var reader = new OpenApiStreamReader();
|
|
||||||
document = reader.Read(File.OpenRead(path), out diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
return FromDocument(document, settings);
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
||||||
}
|
[PublicAPI]
|
||||||
|
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
return FromDocument(_reader.Read(stream, out diagnostic));
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, out OpenApiDiagnostic)" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromStream(Stream stream, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
||||||
{
|
{
|
||||||
return FromDocument(_reader.Read(stream, out diagnostic));
|
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromStream(Stream, WireMockOpenApiParserSettings, out OpenApiDiagnostic)" />
|
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public IEnumerable<MappingModel> FromStream(Stream stream, WireMockOpenApiParserSettings settings, out OpenApiDiagnostic diagnostic)
|
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
|
||||||
{
|
{
|
||||||
return FromDocument(_reader.Read(stream, out diagnostic), settings);
|
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
|
|
||||||
[PublicAPI]
|
|
||||||
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null)
|
|
||||||
{
|
|
||||||
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -15,7 +15,7 @@ namespace WireMock.Net.StandAlone;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class StandAloneApp
|
public static class StandAloneApp
|
||||||
{
|
{
|
||||||
private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version.ToString();
|
private static readonly string Version = typeof(StandAloneApp).GetTypeInfo().Assembly.GetName().Version!.ToString();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Start WireMock.Net standalone Server based on the WireMockServerSettings.
|
/// Start WireMock.Net standalone Server based on the WireMockServerSettings.
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ internal class WebhookSender
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Create HttpRequestMessage
|
// Create HttpRequestMessage
|
||||||
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequest.Url);
|
var httpRequestMessage = HttpRequestMessageHelper.Create(requestMessage, webhookRequestUrl);
|
||||||
|
|
||||||
// Delay (if required)
|
// Delay (if required)
|
||||||
if (TryGetDelay(webhookRequest, out var delay))
|
if (TryGetDelay(webhookRequest, out var delay))
|
||||||
|
|||||||
@@ -2,73 +2,72 @@ using Newtonsoft.Json;
|
|||||||
using System;
|
using System;
|
||||||
using WireMock.Admin.Requests;
|
using WireMock.Admin.Requests;
|
||||||
|
|
||||||
namespace WireMock.Logging
|
namespace WireMock.Logging;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WireMockConsoleLogger which logs to Console
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="IWireMockLogger" />
|
||||||
|
public class WireMockConsoleLogger : IWireMockLogger
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// WireMockConsoleLogger which logs to Console
|
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="IWireMockLogger" />
|
public WireMockConsoleLogger()
|
||||||
public class WireMockConsoleLogger : IWireMockLogger
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
||||||
/// Initializes a new instance of the <see cref="WireMockConsoleLogger"/> class.
|
}
|
||||||
/// </summary>
|
|
||||||
public WireMockConsoleLogger()
|
|
||||||
{
|
|
||||||
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Debug"/>
|
/// <see cref="IWireMockLogger.Debug"/>
|
||||||
public void Debug(string formatString, params object[] args)
|
public void Debug(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Debug", formatString, args));
|
Console.WriteLine(Format("Debug", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Info"/>
|
/// <see cref="IWireMockLogger.Info"/>
|
||||||
public void Info(string formatString, params object[] args)
|
public void Info(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Info", formatString, args));
|
Console.WriteLine(Format("Info", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Warn"/>
|
/// <see cref="IWireMockLogger.Warn"/>
|
||||||
public void Warn(string formatString, params object[] args)
|
public void Warn(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Warn", formatString, args));
|
Console.WriteLine(Format("Warn", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||||
public void Error(string formatString, params object[] args)
|
public void Error(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Error", formatString, args));
|
Console.WriteLine(Format("Error", formatString, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||||
public void Error(string formatString, Exception exception)
|
public void Error(string formatString, Exception exception)
|
||||||
{
|
{
|
||||||
Console.WriteLine(Format("Error", formatString, exception.Message));
|
Console.WriteLine(Format("Error", formatString, exception.Message));
|
||||||
|
|
||||||
if (exception is AggregateException ae)
|
if (exception is AggregateException ae)
|
||||||
|
{
|
||||||
|
ae.Handle(ex =>
|
||||||
{
|
{
|
||||||
ae.Handle(ex =>
|
Console.WriteLine(Format("Error", "Exception {0}", ex.Message));
|
||||||
{
|
return true;
|
||||||
Console.WriteLine(Format("Error", "Exception {0}", ex.Message));
|
});
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
|
||||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
|
||||||
{
|
|
||||||
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
|
||||||
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string Format(string level, string formatString, params object[] args)
|
|
||||||
{
|
|
||||||
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
|
||||||
|
|
||||||
return $"{DateTime.UtcNow} [{level}] : {message}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||||
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
|
{
|
||||||
|
string message = JsonConvert.SerializeObject(logEntryModel, Formatting.Indented);
|
||||||
|
Console.WriteLine(Format("DebugRequestResponse", "Admin[{0}] {1}", isAdminRequest, message));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Format(string level, string formatString, params object[] args)
|
||||||
|
{
|
||||||
|
var message = args.Length > 0 ? string.Format(formatString, args) : formatString;
|
||||||
|
|
||||||
|
return $"{DateTime.UtcNow} [{level}] : {message}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,48 +1,47 @@
|
|||||||
using System;
|
using System;
|
||||||
using WireMock.Admin.Requests;
|
using WireMock.Admin.Requests;
|
||||||
|
|
||||||
namespace WireMock.Logging
|
namespace WireMock.Logging;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WireMockNullLogger which does not log.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="IWireMockLogger" />
|
||||||
|
public class WireMockNullLogger : IWireMockLogger
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <see cref="IWireMockLogger.Debug"/>
|
||||||
/// WireMockNullLogger which does not log.
|
public void Debug(string formatString, params object[] args)
|
||||||
/// </summary>
|
|
||||||
/// <seealso cref="IWireMockLogger" />
|
|
||||||
public class WireMockNullLogger : IWireMockLogger
|
|
||||||
{
|
{
|
||||||
/// <see cref="IWireMockLogger.Debug"/>
|
// Log nothing
|
||||||
public void Debug(string formatString, params object[] args)
|
}
|
||||||
{
|
|
||||||
// Log nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Info"/>
|
/// <see cref="IWireMockLogger.Info"/>
|
||||||
public void Info(string formatString, params object[] args)
|
public void Info(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Warn"/>
|
/// <see cref="IWireMockLogger.Warn"/>
|
||||||
public void Warn(string formatString, params object[] args)
|
public void Warn(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||||
public void Error(string formatString, params object[] args)
|
public void Error(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||||
public void Error(string formatString, Exception exception)
|
public void Error(string formatString, Exception exception)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
{
|
{
|
||||||
// Log nothing
|
// Log nothing
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ public class RequestMessageBodyMatcher : IRequestMatcher
|
|||||||
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
/// Initializes a new instance of the <see cref="RequestMessageBodyMatcher"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="func">The function.</param>
|
/// <param name="func">The function.</param>
|
||||||
public RequestMessageBodyMatcher(Func<IBodyData, bool> func)
|
public RequestMessageBodyMatcher(Func<IBodyData?, bool> func)
|
||||||
{
|
{
|
||||||
BodyDataFunc = Guard.NotNull(func);
|
BodyDataFunc = Guard.NotNull(func);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
|||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||||
/// <param name="values">The values.</param>
|
/// <param name="values">The values.</param>
|
||||||
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, string[]? values) : this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, false, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
|
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params string[]? values) :
|
||||||
|
this(matchBehaviour, key, ignoreCase, values?.Select(value => new ExactMatcher(matchBehaviour, ignoreCase, false, MatchOperator.And, value)).Cast<IStringMatcher>().ToArray())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +65,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
|||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
/// <param name="ignoreCase">Defines if the key should be matched using case-ignore.</param>
|
||||||
/// <param name="matchers">The matchers.</param>
|
/// <param name="matchers">The matchers.</param>
|
||||||
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, IStringMatcher[]? matchers)
|
public RequestMessageParamMatcher(MatchBehaviour matchBehaviour, string key, bool ignoreCase, params IStringMatcher[]? matchers)
|
||||||
{
|
{
|
||||||
MatchBehaviour = matchBehaviour;
|
MatchBehaviour = matchBehaviour;
|
||||||
Key = Guard.NotNull(key);
|
Key = Guard.NotNull(key);
|
||||||
@@ -95,7 +96,7 @@ public class RequestMessageParamMatcher : IRequestMatcher
|
|||||||
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
|
return MatchScores.ToScore(requestMessage.Query != null && Funcs.Any(f => f(requestMessage.Query)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var valuesPresentInRequestMessage = ((RequestMessage)requestMessage).GetParameter(Key!, IgnoreCase ?? false);
|
var valuesPresentInRequestMessage = ((RequestMessage)requestMessage).GetParameter(Key, IgnoreCase ?? false);
|
||||||
if (valuesPresentInRequestMessage == null)
|
if (valuesPresentInRequestMessage == null)
|
||||||
{
|
{
|
||||||
// Key is not present at all, just return Mismatch
|
// Key is not present at all, just return Mismatch
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ using System.Collections.Concurrent;
|
|||||||
using WireMock.Handlers;
|
using WireMock.Handlers;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
#if !USE_ASPNETCORE
|
#if !USE_ASPNETCORE
|
||||||
using Owin;
|
using Owin;
|
||||||
#else
|
#else
|
||||||
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
|
using IAppBuilder = Microsoft.AspNetCore.Builder.IApplicationBuilder;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using WireMock.Types;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace WireMock.Owin;
|
namespace WireMock.Owin;
|
||||||
@@ -70,5 +70,7 @@ internal interface IWireMockMiddlewareOptions
|
|||||||
|
|
||||||
bool? SaveUnmatchedRequests { get; set; }
|
bool? SaveUnmatchedRequests { get; set; }
|
||||||
|
|
||||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||||
|
|
||||||
|
QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
body = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
body = await BodyParser.ParseAsync(bodyParserSettings).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RequestMessage(urlDetails, method, clientIP, body, headers, cookies) { DateTime = DateTime.UtcNow };
|
return new RequestMessage(options, urlDetails, method, clientIP, body, headers, cookies) { DateTime = DateTime.UtcNow };
|
||||||
}
|
}
|
||||||
|
|
||||||
private static (UrlDetails UrlDetails, string ClientIP) ParseRequest(IRequest request)
|
private static (UrlDetails UrlDetails, string ClientIP) ParseRequest(IRequest request)
|
||||||
|
|||||||
@@ -81,11 +81,11 @@ namespace WireMock.Owin.Mappers
|
|||||||
var statusCodeType = responseMessage.StatusCode?.GetType();
|
var statusCodeType = responseMessage.StatusCode?.GetType();
|
||||||
switch (statusCodeType)
|
switch (statusCodeType)
|
||||||
{
|
{
|
||||||
case Type typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
|
case { } typeAsIntOrEnum when typeAsIntOrEnum == typeof(int) || typeAsIntOrEnum == typeof(int?) || typeAsIntOrEnum.GetTypeInfo().IsEnum:
|
||||||
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
|
response.StatusCode = MapStatusCode((int)responseMessage.StatusCode!);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Type typeAsString when typeAsString == typeof(string):
|
case { } typeAsString when typeAsString == typeof(string):
|
||||||
// Note: this case will also match on null
|
// Note: this case will also match on null
|
||||||
int.TryParse(responseMessage.StatusCode as string, out int result);
|
int.TryParse(responseMessage.StatusCode as string, out int result);
|
||||||
response.StatusCode = MapStatusCode(result);
|
response.StatusCode = MapStatusCode(result);
|
||||||
@@ -130,7 +130,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
switch (responseMessage.BodyData?.DetectedBodyType)
|
switch (responseMessage.BodyData?.DetectedBodyType)
|
||||||
{
|
{
|
||||||
case BodyType.String:
|
case BodyType.String:
|
||||||
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString);
|
return (responseMessage.BodyData.Encoding ?? _utf8NoBom).GetBytes(responseMessage.BodyData.BodyAsString!);
|
||||||
|
|
||||||
case BodyType.Json:
|
case BodyType.Json:
|
||||||
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true
|
var formatting = responseMessage.BodyData.BodyAsJsonIndented == true
|
||||||
@@ -143,7 +143,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
return responseMessage.BodyData.BodyAsBytes;
|
return responseMessage.BodyData.BodyAsBytes;
|
||||||
|
|
||||||
case BodyType.File:
|
case BodyType.File:
|
||||||
return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile);
|
return _options.FileSystemHandler?.ReadResponseBodyAsFile(responseMessage.BodyData.BodyAsFile!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -161,7 +161,7 @@ namespace WireMock.Owin.Mappers
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Set other headers
|
// Set other headers
|
||||||
foreach (var item in responseMessage.Headers)
|
foreach (var item in responseMessage.Headers!)
|
||||||
{
|
{
|
||||||
var headerName = item.Key;
|
var headerName = item.Key;
|
||||||
var value = item.Value;
|
var value = item.Value;
|
||||||
|
|||||||
@@ -269,29 +269,55 @@ namespace WireMock.Owin
|
|||||||
{
|
{
|
||||||
_options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/"));
|
_options.Logger.DebugRequestResponse(_logEntryMapper.Map(entry), entry.RequestMessage.Path.StartsWith("/__admin/"));
|
||||||
|
|
||||||
if (addRequest)
|
// If addRequest is set to true and MaxRequestLogCount is null or does have a value greater than 0, try to add a new request log.
|
||||||
|
if (addRequest && _options.MaxRequestLogCount is null or > 0)
|
||||||
{
|
{
|
||||||
_options.LogEntries.Add(entry);
|
TryAddLogEntry(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_options.MaxRequestLogCount != null)
|
// In case MaxRequestLogCount has a value greater than 0, try to delete existing request logs based on the count.
|
||||||
|
if (_options.MaxRequestLogCount is > 0)
|
||||||
{
|
{
|
||||||
var logEntries = _options.LogEntries.ToList();
|
var logEntries = _options.LogEntries.ToList();
|
||||||
foreach (var logEntry in logEntries.OrderBy(le => le.RequestMessage.DateTime).Take(logEntries.Count - _options.MaxRequestLogCount.Value))
|
foreach (var logEntry in logEntries.OrderBy(le => le.RequestMessage.DateTime).Take(logEntries.Count - _options.MaxRequestLogCount.Value))
|
||||||
{
|
{
|
||||||
_options.LogEntries.Remove(logEntry);
|
TryRemoveLogEntry(logEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_options.RequestLogExpirationDuration != null)
|
// In case RequestLogExpirationDuration has a value greater than 0, try to delete existing request logs based on the date.
|
||||||
|
if (_options.RequestLogExpirationDuration is > 0)
|
||||||
{
|
{
|
||||||
var checkTime = DateTime.UtcNow.AddHours(-_options.RequestLogExpirationDuration.Value);
|
var checkTime = DateTime.UtcNow.AddHours(-_options.RequestLogExpirationDuration.Value);
|
||||||
|
|
||||||
foreach (var logEntry in _options.LogEntries.ToList().Where(le => le.RequestMessage.DateTime < checkTime))
|
foreach (var logEntry in _options.LogEntries.ToList().Where(le => le.RequestMessage.DateTime < checkTime))
|
||||||
{
|
{
|
||||||
_options.LogEntries.Remove(logEntry);
|
TryRemoveLogEntry(logEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TryAddLogEntry(LogEntry logEntry)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_options.LogEntries.Add(logEntry);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore exception (can happen during stress testing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryRemoveLogEntry(LogEntry logEntry)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_options.LogEntries.Remove(logEntry);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore exception (can happen during stress testing)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,4 +87,7 @@ internal class WireMockMiddlewareOptions : IWireMockMiddlewareOptions
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
using WireMock.Owin;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ public class RequestMessage : IRequestMessage
|
|||||||
public string AbsoluteUrl { get; }
|
public string AbsoluteUrl { get; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.ProxyUrl" />
|
/// <inheritdoc cref="IRequestMessage.ProxyUrl" />
|
||||||
public string ProxyUrl { get; set; }
|
public string? ProxyUrl { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc cref="IRequestMessage.DateTime" />
|
/// <inheritdoc cref="IRequestMessage.DateTime" />
|
||||||
public DateTime DateTime { get; set; }
|
public DateTime DateTime { get; set; }
|
||||||
@@ -91,16 +92,36 @@ public class RequestMessage : IRequestMessage
|
|||||||
/// <inheritdoc cref="IRequestMessage.Origin" />
|
/// <inheritdoc cref="IRequestMessage.Origin" />
|
||||||
public string Origin { get; }
|
public string Origin { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for Unit Testing
|
||||||
|
/// </summary>
|
||||||
|
public RequestMessage(
|
||||||
|
UrlDetails urlDetails,
|
||||||
|
string method,
|
||||||
|
string clientIP,
|
||||||
|
IBodyData? bodyData = null,
|
||||||
|
IDictionary<string, string[]>? headers = null,
|
||||||
|
IDictionary<string, string>? cookies = null) : this(null, urlDetails, method, clientIP, bodyData, headers, cookies)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
|
/// Initializes a new instance of the <see cref="RequestMessage"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="options">The<seealso cref="IWireMockMiddlewareOptions"/>.</param>
|
||||||
/// <param name="urlDetails">The original url details.</param>
|
/// <param name="urlDetails">The original url details.</param>
|
||||||
/// <param name="method">The HTTP method.</param>
|
/// <param name="method">The HTTP method.</param>
|
||||||
/// <param name="clientIP">The client IP Address.</param>
|
/// <param name="clientIP">The client IP Address.</param>
|
||||||
/// <param name="bodyData">The BodyData.</param>
|
/// <param name="bodyData">The BodyData.</param>
|
||||||
/// <param name="headers">The headers.</param>
|
/// <param name="headers">The headers.</param>
|
||||||
/// <param name="cookies">The cookies.</param>
|
/// <param name="cookies">The cookies.</param>
|
||||||
public RequestMessage(UrlDetails urlDetails, string method, string clientIP, IBodyData? bodyData = null, IDictionary<string, string[]>? headers = null, IDictionary<string, string>? cookies = null)
|
internal RequestMessage(
|
||||||
|
IWireMockMiddlewareOptions? options,
|
||||||
|
UrlDetails urlDetails, string method,
|
||||||
|
string clientIP,
|
||||||
|
IBodyData? bodyData = null,
|
||||||
|
IDictionary<string, string[]>? headers = null,
|
||||||
|
IDictionary<string, string>? cookies = null)
|
||||||
{
|
{
|
||||||
Guard.NotNull(urlDetails, nameof(urlDetails));
|
Guard.NotNull(urlDetails, nameof(urlDetails));
|
||||||
Guard.NotNull(method, nameof(method));
|
Guard.NotNull(method, nameof(method));
|
||||||
@@ -134,7 +155,7 @@ public class RequestMessage : IRequestMessage
|
|||||||
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
Headers = headers?.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||||
Cookies = cookies;
|
Cookies = cookies;
|
||||||
RawQuery = urlDetails.Url.Query;
|
RawQuery = urlDetails.Url.Query;
|
||||||
Query = QueryStringParser.Parse(RawQuery);
|
Query = QueryStringParser.Parse(RawQuery, options?.QueryParameterMultipleValueSupport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -125,9 +125,9 @@ public interface IRespondWithAProvider
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Support FireAndForget for any configured Webhooks
|
/// Support FireAndForget for any configured Webhooks
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="UseWebhooksFireAndForget"></param>
|
/// <param name="useWebhooksFireAndForget"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
IRespondWithAProvider WithWebhookFireAndForget(bool UseWebhooksFireAndForget);
|
IRespondWithAProvider WithWebhookFireAndForget(bool useWebhooksFireAndForget);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a Webhook to call after the response has been generated.
|
/// Add a Webhook to call after the response has been generated.
|
||||||
@@ -141,7 +141,7 @@ public interface IRespondWithAProvider
|
|||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithWebhook(
|
IRespondWithAProvider WithWebhook(
|
||||||
string url,
|
string url,
|
||||||
string? method = "post",
|
string method = "post",
|
||||||
IDictionary<string, WireMockList<string>>? headers = null,
|
IDictionary<string, WireMockList<string>>? headers = null,
|
||||||
string? body = null,
|
string? body = null,
|
||||||
bool useTransformer = true,
|
bool useTransformer = true,
|
||||||
@@ -160,7 +160,7 @@ public interface IRespondWithAProvider
|
|||||||
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
/// <returns>The <see cref="IRespondWithAProvider"/>.</returns>
|
||||||
IRespondWithAProvider WithWebhook(
|
IRespondWithAProvider WithWebhook(
|
||||||
string url,
|
string url,
|
||||||
string? method = "post",
|
string method = "post",
|
||||||
IDictionary<string, WireMockList<string>>? headers = null,
|
IDictionary<string, WireMockList<string>>? headers = null,
|
||||||
object? body = null,
|
object? body = null,
|
||||||
bool useTransformer = true,
|
bool useTransformer = true,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ internal class RespondWithAProvider : IRespondWithAProvider
|
|||||||
private readonly WireMockServerSettings _settings;
|
private readonly WireMockServerSettings _settings;
|
||||||
private readonly bool _saveToFile;
|
private readonly bool _saveToFile;
|
||||||
|
|
||||||
private bool _useWebhookFireAndForget = false;
|
private bool _useWebhookFireAndForget;
|
||||||
|
|
||||||
public Guid Guid { get; private set; } = Guid.NewGuid();
|
public Guid Guid { get; private set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ public partial class WireMockServer
|
|||||||
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
|
WatchStaticMappingsInSubdirectories = _settings.WatchStaticMappingsInSubdirectories,
|
||||||
HostingScheme = _settings.HostingScheme,
|
HostingScheme = _settings.HostingScheme,
|
||||||
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
|
DoNotSaveDynamicResponseInLogEntry = _settings.DoNotSaveDynamicResponseInLogEntry,
|
||||||
|
QueryParameterMultipleValueSupport = _settings.QueryParameterMultipleValueSupport,
|
||||||
|
|
||||||
#if USE_ASPNETCORE
|
#if USE_ASPNETCORE
|
||||||
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
|
CorsPolicyOptions = _settings.CorsPolicyOptions?.ToString()
|
||||||
@@ -252,6 +253,7 @@ public partial class WireMockServer
|
|||||||
_settings.WatchStaticMappings = settings.WatchStaticMappings;
|
_settings.WatchStaticMappings = settings.WatchStaticMappings;
|
||||||
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
|
_settings.WatchStaticMappingsInSubdirectories = settings.WatchStaticMappingsInSubdirectories;
|
||||||
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
_settings.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||||
|
_settings.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||||
|
|
||||||
InitSettings(_settings);
|
InitSettings(_settings);
|
||||||
|
|
||||||
|
|||||||
@@ -296,6 +296,7 @@ public partial class WireMockServer : IWireMockServer
|
|||||||
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
|
_options.HandleRequestsSynchronously = settings.HandleRequestsSynchronously;
|
||||||
_options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
_options.SaveUnmatchedRequests = settings.SaveUnmatchedRequests;
|
||||||
_options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
_options.DoNotSaveDynamicResponseInLogEntry = settings.DoNotSaveDynamicResponseInLogEntry;
|
||||||
|
_options.QueryParameterMultipleValueSupport = settings.QueryParameterMultipleValueSupport;
|
||||||
|
|
||||||
if (settings.CustomCertificateDefined)
|
if (settings.CustomCertificateDefined)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -258,6 +258,14 @@ namespace WireMock.Settings
|
|||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
|
||||||
|
///
|
||||||
|
/// Default value = "All".
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Custom matcher mappings for static mappings
|
/// Custom matcher mappings for static mappings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Stef.Validation;
|
using Stef.Validation;
|
||||||
@@ -55,20 +54,31 @@ public static class WireMockServerSettingsParser
|
|||||||
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
|
WatchStaticMappings = parser.GetBoolValue("WatchStaticMappings"),
|
||||||
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
|
WatchStaticMappingsInSubdirectories = parser.GetBoolValue("WatchStaticMappingsInSubdirectories"),
|
||||||
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
|
HostingScheme = parser.GetEnumValue<HostingScheme>(nameof(WireMockServerSettings.HostingScheme)),
|
||||||
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry))
|
DoNotSaveDynamicResponseInLogEntry = parser.GetBoolValue(nameof(WireMockServerSettings.DoNotSaveDynamicResponseInLogEntry)),
|
||||||
|
QueryParameterMultipleValueSupport = parser.GetEnumValue<QueryParameterMultipleValueSupport>(nameof(WireMockServerSettings.QueryParameterMultipleValueSupport))
|
||||||
};
|
};
|
||||||
|
|
||||||
#if USE_ASPNETCORE
|
#if USE_ASPNETCORE
|
||||||
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
|
settings.CorsPolicyOptions = parser.GetEnumValue(nameof(WireMockServerSettings.CorsPolicyOptions), CorsPolicyOptions.None);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (logger != null)
|
var loggerType = parser.GetStringValue("WireMockLogger");
|
||||||
|
switch (loggerType)
|
||||||
{
|
{
|
||||||
settings.Logger = logger;
|
case nameof(WireMockConsoleLogger):
|
||||||
}
|
settings.Logger = new WireMockConsoleLogger();
|
||||||
else if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger")
|
break;
|
||||||
{
|
|
||||||
settings.Logger = new WireMockConsoleLogger();
|
case nameof(WireMockNullLogger):
|
||||||
|
settings.Logger = new WireMockNullLogger();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (logger != null)
|
||||||
|
{
|
||||||
|
settings.Logger = logger;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
if (parser.Contains(nameof(WireMockServerSettings.Port)))
|
||||||
|
|||||||
@@ -11,25 +11,41 @@ namespace WireMock.Util;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class QueryStringParser
|
internal static class QueryStringParser
|
||||||
{
|
{
|
||||||
public static IDictionary<string, WireMockList<string>> Parse(string queryString)
|
private static readonly Dictionary<string, WireMockList<string>> Empty = new();
|
||||||
|
|
||||||
|
public static IDictionary<string, WireMockList<string>> Parse(string? queryString, QueryParameterMultipleValueSupport? support = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(queryString))
|
if (string.IsNullOrEmpty(queryString))
|
||||||
{
|
{
|
||||||
return new Dictionary<string, WireMockList<string>>();
|
return Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var queryParameterMultipleValueSupport = support ?? QueryParameterMultipleValueSupport.All;
|
||||||
|
|
||||||
string[] JoinParts(string[] parts)
|
string[] JoinParts(string[] parts)
|
||||||
{
|
{
|
||||||
if (parts.Length > 1)
|
if (parts.Length > 1)
|
||||||
{
|
{
|
||||||
return parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); // support "?key=1,2"
|
return queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.Comma) ?
|
||||||
|
parts[1].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) : // Support "?key=1,2"
|
||||||
|
new[] { parts[1] };
|
||||||
}
|
}
|
||||||
|
|
||||||
return new string[0];
|
return new string[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryString.TrimStart('?')
|
var splitOn = new List<string>();
|
||||||
.Split(new[] { '&', ';' }, StringSplitOptions.RemoveEmptyEntries) // Support "?key=value;key=anotherValue" and "?key=value&key=anotherValue"
|
if (queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.Ampersand))
|
||||||
|
{
|
||||||
|
splitOn.Add("&"); // Support "?key=value&key=anotherValue"
|
||||||
|
}
|
||||||
|
if (queryParameterMultipleValueSupport.HasFlag(QueryParameterMultipleValueSupport.SemiColon))
|
||||||
|
{
|
||||||
|
splitOn.Add(";"); // Support "?key=value;key=anotherValue"
|
||||||
|
}
|
||||||
|
|
||||||
|
return queryString!.TrimStart('?')
|
||||||
|
.Split(splitOn.ToArray(), StringSplitOptions.RemoveEmptyEntries)
|
||||||
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
|
.Select(parameter => parameter.Split(new[] { '=' }, 2, StringSplitOptions.RemoveEmptyEntries))
|
||||||
.GroupBy(parts => parts[0], JoinParts)
|
.GroupBy(parts => parts[0], JoinParts)
|
||||||
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));
|
.ToDictionary(grouping => grouping.Key, grouping => new WireMockList<string>(grouping.SelectMany(x => x).Select(WebUtility.UrlDecode)));
|
||||||
|
|||||||
@@ -21,10 +21,6 @@
|
|||||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!--<ItemGroup>
|
|
||||||
<None Include="WireMock.Net-Logo.png" Pack="true" PackagePath="../../"/>
|
|
||||||
</ItemGroup>-->
|
|
||||||
|
|
||||||
<!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 -->
|
<!-- https://github.com/aspnet/RoslynCodeDomProvider/issues/51 -->
|
||||||
<!-- This is needed else we cannot build net452 in Azure DevOps Pipeline -->
|
<!-- This is needed else we cannot build net452 in Azure DevOps Pipeline -->
|
||||||
<!--<Target Name="CheckIfShouldKillVBCSCompiler" />-->
|
<!--<Target Name="CheckIfShouldKillVBCSCompiler" />-->
|
||||||
@@ -145,7 +141,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
|
||||||
<!--<PackageReference Include="System.Diagnostics.CodeAnalysis" Version="4.3.0" />-->
|
|
||||||
<PackageReference Include="Nullable" Version="1.3.0" />
|
<PackageReference Include="Nullable" Version="1.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -5,56 +5,55 @@ using WireMock.Logging;
|
|||||||
using WireMock.Net.StandAlone;
|
using WireMock.Net.StandAlone;
|
||||||
using WireMock.Server;
|
using WireMock.Server;
|
||||||
|
|
||||||
namespace WireMock.Net
|
namespace WireMock.Net;
|
||||||
|
|
||||||
|
public class Program
|
||||||
{
|
{
|
||||||
public class Program
|
private static readonly int SleepTime = 30000;
|
||||||
|
private static readonly ILogger XLogger = LoggerFactory.Create(o =>
|
||||||
{
|
{
|
||||||
private static readonly int SleepTime = 30000;
|
o.SetMinimumLevel(LogLevel.Debug);
|
||||||
private static readonly ILogger xLogger = LoggerFactory.Create(o =>
|
o.AddSimpleConsole(options =>
|
||||||
{
|
{
|
||||||
o.SetMinimumLevel(LogLevel.Debug);
|
options.IncludeScopes = true;
|
||||||
o.AddSimpleConsole(options =>
|
options.SingleLine = false;
|
||||||
{
|
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
|
||||||
options.IncludeScopes = true;
|
});
|
||||||
options.SingleLine = false;
|
}).CreateLogger("WireMock.Net");
|
||||||
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
|
private static readonly IWireMockLogger Logger = new WireMockLogger(XLogger);
|
||||||
});
|
|
||||||
}).CreateLogger("WireMock.Net");
|
|
||||||
private static readonly IWireMockLogger Logger = new WireMockLogger(xLogger);
|
|
||||||
|
|
||||||
private static WireMockServer Server;
|
private static WireMockServer _server = null!;
|
||||||
|
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
if (!StandAloneApp.TryStart(args, out _server!, Logger))
|
||||||
{
|
{
|
||||||
if (!StandAloneApp.TryStart(args, out Server, Logger))
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info("Press Ctrl+C to shut down");
|
|
||||||
|
|
||||||
Console.CancelKeyPress += (s, e) =>
|
|
||||||
{
|
|
||||||
Stop("CancelKeyPress");
|
|
||||||
};
|
|
||||||
|
|
||||||
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
|
|
||||||
{
|
|
||||||
Stop("AssemblyLoadContext.Default.Unloading");
|
|
||||||
};
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
Logger.Info("Server running : {IsStarted}", Server.IsStarted);
|
|
||||||
await Task.Delay(SleepTime).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void Stop(string why)
|
Logger.Info("Press Ctrl+C to shut down");
|
||||||
|
|
||||||
|
Console.CancelKeyPress += (s, e) =>
|
||||||
{
|
{
|
||||||
Logger.Info("Server stopping because '{why}'", why);
|
Stop("CancelKeyPress");
|
||||||
Server.Stop();
|
};
|
||||||
Logger.Info("Server stopped");
|
|
||||||
|
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += ctx =>
|
||||||
|
{
|
||||||
|
Stop("AssemblyLoadContext.Default.Unloading");
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Logger.Info("Server running : {0}", _server.IsStarted);
|
||||||
|
await Task.Delay(SleepTime).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void Stop(string why)
|
||||||
|
{
|
||||||
|
Logger.Info("Server stopping because '{0}'", why);
|
||||||
|
_server.Stop();
|
||||||
|
Logger.Info("Server stopped");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"dotnet-WireMock.Net": {
|
"dotnet-WireMock.Net": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": ""
|
"commandLineArgs": "--WireMockLogger s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,58 +4,57 @@ using Microsoft.Extensions.Logging;
|
|||||||
using WireMock.Admin.Requests;
|
using WireMock.Admin.Requests;
|
||||||
using WireMock.Logging;
|
using WireMock.Logging;
|
||||||
|
|
||||||
namespace WireMock.Net
|
namespace WireMock.Net;
|
||||||
|
|
||||||
|
public class WireMockLogger : IWireMockLogger
|
||||||
{
|
{
|
||||||
public class WireMockLogger : IWireMockLogger
|
private readonly JsonSerializerOptions _options = new()
|
||||||
{
|
{
|
||||||
private readonly JsonSerializerOptions options = new JsonSerializerOptions
|
WriteIndented = true
|
||||||
{
|
};
|
||||||
WriteIndented = true
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
public WireMockLogger(ILogger logger)
|
public WireMockLogger(ILogger logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Debug"/>
|
/// <see cref="IWireMockLogger.Debug"/>
|
||||||
public void Debug(string formatString, params object[] args)
|
public void Debug(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
_logger.LogDebug(formatString, args);
|
_logger.LogDebug(formatString, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Info"/>
|
/// <see cref="IWireMockLogger.Info"/>
|
||||||
public void Info(string formatString, params object[] args)
|
public void Info(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
_logger.LogInformation(formatString, args);
|
_logger.LogInformation(formatString, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Warn"/>
|
/// <see cref="IWireMockLogger.Warn"/>
|
||||||
public void Warn(string formatString, params object[] args)
|
public void Warn(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(formatString, args);
|
_logger.LogWarning(formatString, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
/// <see cref="IWireMockLogger.Error(string, object[])"/>
|
||||||
public void Error(string formatString, params object[] args)
|
public void Error(string formatString, params object[] args)
|
||||||
{
|
{
|
||||||
_logger.LogError(formatString, args);
|
_logger.LogError(formatString, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
/// <see cref="IWireMockLogger.Error(string, Exception)"/>
|
||||||
public void Error(string formatString, Exception exception)
|
public void Error(string formatString, Exception exception)
|
||||||
{
|
{
|
||||||
_logger.LogError(formatString, exception);
|
_logger.LogError(formatString, exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
/// <see cref="IWireMockLogger.DebugRequestResponse"/>
|
||||||
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
public void DebugRequestResponse(LogEntryModel logEntryModel, bool isAdminRequest)
|
||||||
{
|
{
|
||||||
string message = JsonSerializer.Serialize(logEntryModel, options);
|
string message = JsonSerializer.Serialize(logEntryModel, _options);
|
||||||
|
|
||||||
_logger.LogDebug("Admin[{IsAdmin}] {Message}", isAdminRequest, message);
|
_logger.LogDebug("Admin[{IsAdmin}] {Message}", isAdminRequest, message);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
|
using FluentAssertions;
|
||||||
using NFluent;
|
using NFluent;
|
||||||
using WireMock.Matchers;
|
using WireMock.Matchers;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
using WireMock.Owin;
|
||||||
|
using WireMock.Types;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.RequestMatchers
|
namespace WireMock.Net.Tests.RequestMatchers
|
||||||
@@ -172,5 +175,25 @@ namespace WireMock.Net.Tests.RequestMatchers
|
|||||||
// Assert
|
// Assert
|
||||||
Check.That(score).IsEqualTo(1.0d);
|
Check.That(score).IsEqualTo(1.0d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue #849
|
||||||
|
[Fact]
|
||||||
|
public void RequestMessageParamMatcher_With1ParamContainingComma_Using_QueryParameterMultipleValueSupport_NoComma()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var options = new WireMockMiddlewareOptions
|
||||||
|
{
|
||||||
|
QueryParameterMultipleValueSupport = QueryParameterMultipleValueSupport.NoComma
|
||||||
|
};
|
||||||
|
var requestMessage = new RequestMessage(options, new UrlDetails("http://localhost?query=SELECT id, value FROM table WHERE id = 1&test=42"), "GET", "127.0.0.1");
|
||||||
|
var matcher = new RequestMessageParamMatcher(MatchBehaviour.AcceptOnMatch, "query", false, "SELECT id, value FROM table WHERE id = 1");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = new RequestMatchResult();
|
||||||
|
double score = matcher.GetMatchingScore(requestMessage, result);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
score.Should().Be(1.0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,243 +1,261 @@
|
|||||||
using NFluent;
|
using NFluent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Matchers.Request;
|
using WireMock.Matchers.Request;
|
||||||
using WireMock.Models;
|
using WireMock.Models;
|
||||||
|
using WireMock.Owin;
|
||||||
using WireMock.RequestBuilders;
|
using WireMock.RequestBuilders;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests
|
namespace WireMock.Net.Tests;
|
||||||
|
|
||||||
|
public class RequestTests
|
||||||
{
|
{
|
||||||
public class RequestTests
|
private const string ClientIp = "::1";
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_exclude_requests_matching_given_http_method_but_not_url()
|
||||||
{
|
{
|
||||||
private const string ClientIp = "::1";
|
// given
|
||||||
|
var spec = Request.Create().WithPath("/bar").UsingPut();
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_exclude_requests_matching_given_http_method_but_not_url()
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp);
|
||||||
|
|
||||||
|
// then
|
||||||
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_exclude_requests_not_matching_given_headers()
|
||||||
|
{
|
||||||
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "tatata");
|
||||||
|
|
||||||
|
// when
|
||||||
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().WithPath("/bar").UsingPut();
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp);
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_exclude_requests_not_matching_given_headers_ignorecase()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "abc", false);
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_exclude_requests_not_matching_given_headers()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "tatata");
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "ABC" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_matching_given_header_prefix()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "tata*");
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_exclude_requests_not_matching_given_headers_ignorecase()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "abc", false);
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "ABC" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_matching_given_wildcard_header()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*");
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_specify_requests_matching_given_header_prefix()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "tata*");
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_not_matching_given_wildcard_header2()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*");
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_specify_requests_matching_given_wildcard_header()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*");
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-tata", new[] { "ToTo" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_matching_given_wildcard_header_rejectonmatch()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*", WireMock.Matchers.MatchBehaviour.RejectOnMatch);
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_specify_requests_not_matching_given_wildcard_header2()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*");
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-tata", new[] { "ToTo" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-tata", new[] { "ToTo" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_not_matching_given_wildcard_header_rejectonmatch()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*", WireMock.Matchers.MatchBehaviour.RejectOnMatch);
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_specify_requests_matching_given_wildcard_header_rejectonmatch()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "whatever",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*", WireMock.Matchers.MatchBehaviour.RejectOnMatch);
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-tata", new[] { "ToTo" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_matching_given_body()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithBody("Hello world!");
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_specify_requests_not_matching_given_wildcard_header_rejectonmatch()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "Hello world!",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithHeader("X-toto", "*", WireMock.Matchers.MatchBehaviour.RejectOnMatch);
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body);
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
BodyAsString = "whatever",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "TaTa" } } });
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_exclude_requests_not_matching_given_body()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(0.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().UsingAnyMethod().WithBody(" Hello world! ");
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_specify_requests_matching_given_body()
|
var body = new BodyData
|
||||||
{
|
{
|
||||||
// given
|
BodyAsString = "xxx",
|
||||||
var spec = Request.Create().UsingAnyMethod().WithBody("Hello world!");
|
DetectedBodyType = BodyType.String
|
||||||
|
};
|
||||||
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
|
||||||
|
|
||||||
// when
|
// then
|
||||||
var body = new BodyData
|
var requestMatchResult = new RequestMatchResult();
|
||||||
{
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
||||||
BodyAsString = "Hello world!",
|
}
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body);
|
|
||||||
|
|
||||||
// then
|
[Fact]
|
||||||
var requestMatchResult = new RequestMatchResult();
|
public void Should_specify_requests_matching_given_param()
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
{
|
||||||
}
|
// given
|
||||||
|
var spec = Request.Create().WithParam("bar", "1", "2");
|
||||||
|
|
||||||
[Fact]
|
// when
|
||||||
public void Should_exclude_requests_not_matching_given_body()
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo?bar=1&bar=2"), "PUT", ClientIp);
|
||||||
|
|
||||||
|
// then
|
||||||
|
var requestMatchResult = new RequestMatchResult();
|
||||||
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_specify_requests_matching_given_param_WithComma()
|
||||||
|
{
|
||||||
|
// given
|
||||||
|
var options = new WireMockMiddlewareOptions
|
||||||
{
|
{
|
||||||
// given
|
QueryParameterMultipleValueSupport = QueryParameterMultipleValueSupport.NoComma
|
||||||
var spec = Request.Create().UsingAnyMethod().WithBody(" Hello world! ");
|
};
|
||||||
|
var spec = Request.Create().WithParam("$filter", "startswith(name,'testName')");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
var body = new BodyData
|
var request = new RequestMessage(options, new UrlDetails("http://localhost/?$filter=startswith(name,'testName')"), "PUT", ClientIp);
|
||||||
{
|
|
||||||
BodyAsString = "xxx",
|
|
||||||
DetectedBodyType = BodyType.String
|
|
||||||
};
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "PUT", ClientIp, body, new Dictionary<string, string[]> { { "X-toto", new[] { "tata" } } });
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
var requestMatchResult = new RequestMatchResult();
|
var requestMatchResult = new RequestMatchResult();
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_specify_requests_matching_given_param()
|
public void Should_specify_requests_matching_given_param_func()
|
||||||
{
|
{
|
||||||
// given
|
// given
|
||||||
var spec = Request.Create().WithParam("bar", "1", "2");
|
var spec = Request.Create().UsingAnyMethod().WithParam(p => p.ContainsKey("bar"));
|
||||||
|
|
||||||
// when
|
// when
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo?bar=1&bar=2"), "PUT", ClientIp);
|
var request = new RequestMessage(new UrlDetails("http://localhost/foo?bar=1&bar=2"), "PUT", ClientIp);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
var requestMatchResult = new RequestMatchResult();
|
var requestMatchResult = new RequestMatchResult();
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_specify_requests_matching_given_param_func()
|
public void Should_exclude_requests_not_matching_given_params()
|
||||||
{
|
{
|
||||||
// given
|
// given
|
||||||
var spec = Request.Create().UsingAnyMethod().WithParam(p => p.ContainsKey("bar"));
|
var spec = Request.Create().WithParam("bar", "1");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/foo?bar=1&bar=2"), "PUT", ClientIp);
|
var request = new RequestMessage(new UrlDetails("http://localhost/test=7"), "PUT", ClientIp);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
var requestMatchResult = new RequestMatchResult();
|
var requestMatchResult = new RequestMatchResult();
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
|
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Should_exclude_requests_not_matching_given_params()
|
|
||||||
{
|
|
||||||
// given
|
|
||||||
var spec = Request.Create().WithParam("bar", "1");
|
|
||||||
|
|
||||||
// when
|
|
||||||
var request = new RequestMessage(new UrlDetails("http://localhost/test=7"), "PUT", ClientIp);
|
|
||||||
|
|
||||||
// then
|
|
||||||
var requestMatchResult = new RequestMatchResult();
|
|
||||||
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,267 +1,324 @@
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using WireMock.Types;
|
using WireMock.Types;
|
||||||
using WireMock.Util;
|
using WireMock.Util;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace WireMock.Net.Tests.Util
|
namespace WireMock.Net.Tests.Util;
|
||||||
|
|
||||||
|
public class QueryStringParserTests
|
||||||
{
|
{
|
||||||
public class QueryStringParserTests
|
[Fact]
|
||||||
|
public void Parse_WithNullString()
|
||||||
{
|
{
|
||||||
[Fact]
|
// Assign
|
||||||
public void Parse_WithNullString()
|
string? query = null;
|
||||||
{
|
|
||||||
// Assign
|
|
||||||
string query = null;
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().Equal(new Dictionary<string, WireMockList<string>>());
|
result.Should().Equal(new Dictionary<string, WireMockList<string>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_WithEmptyString()
|
public void Parse_WithEmptyString()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "";
|
string query = "";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().Equal(new Dictionary<string, WireMockList<string>>());
|
result.Should().Equal(new Dictionary<string, WireMockList<string>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_WithQuestionMark()
|
public void Parse_WithQuestionMark()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?";
|
string query = "?";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Should().Equal(new Dictionary<string, WireMockList<string>>());
|
result.Should().Equal(new Dictionary<string, WireMockList<string>>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1Param()
|
public void Parse_With1Param()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=bla/blub.xml";
|
string query = "?key=bla/blub.xml";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>("bla/blub.xml"));
|
result["key"].Should().Equal(new WireMockList<string>("bla/blub.xml"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With2Params()
|
public void Parse_With2Params()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?x=1&y=2";
|
string query = "?x=1&y=2";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(2);
|
result.Count.Should().Be(2);
|
||||||
result["x"].Should().Equal(new WireMockList<string>("1"));
|
result["x"].Should().Equal(new WireMockList<string>("1"));
|
||||||
result["y"].Should().Equal(new WireMockList<string>("2"));
|
result["y"].Should().Equal(new WireMockList<string>("2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamNoValue()
|
public void Parse_With1ParamNoValue()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?empty";
|
string query = "?empty";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["empty"].Should().Equal(new WireMockList<string>());
|
result["empty"].Should().Equal(new WireMockList<string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamNoValueWithEqualSign()
|
public void Parse_With1ParamNoValueWithEqualSign()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?empty=";
|
string query = "?empty=";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["empty"].Should().Equal(new WireMockList<string>());
|
result["empty"].Should().Equal(new WireMockList<string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamAndJustAndSign()
|
public void Parse_With1ParamAndJustAndSign()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=1&";
|
string query = "?key=1&";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>("1"));
|
result["key"].Should().Equal(new WireMockList<string>("1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With2ParamsAndWhereOneHasAQuestion()
|
public void Parse_With2ParamsAndWhereOneHasAQuestion()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=value?&b=c";
|
string query = "?key=value?&b=c";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(2);
|
result.Count.Should().Be(2);
|
||||||
result["key"].Should().Equal(new WireMockList<string>("value?"));
|
result["key"].Should().Equal(new WireMockList<string>("value?"));
|
||||||
result["b"].Should().Equal(new WireMockList<string>("c"));
|
result["b"].Should().Equal(new WireMockList<string>("c"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamWithEqualSign()
|
public void Parse_With1ParamWithEqualSign()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=value=what";
|
string query = "?key=value=what";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>("value=what"));
|
result["key"].Should().Equal(new WireMockList<string>("value=what"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamWithTwoEqualSigns()
|
public void Parse_With1ParamWithTwoEqualSigns()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=value==what";
|
string query = "?key=value==what";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>("value==what"));
|
result["key"].Should().Equal(new WireMockList<string>("value==what"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_WithMultipleParamWithSameKeySeparatedBySemiColon()
|
public void Parse_WithMultipleParamWithSameKeySeparatedBySemiColon()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=value;key=anotherValue";
|
string query = "?key=value;key=anotherValue";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>(new[] { "value", "anotherValue" }));
|
result["key"].Should().Equal(new WireMockList<string>("value", "anotherValue"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamContainingComma()
|
public void Parse_WithMultipleParamWithSameKeySeparatedByAmp()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=1,2&key=3";
|
string query = "?key=1&key=2";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>(new[] { "1", "2", "3" }));
|
result["key"].Should().Equal(new WireMockList<string>("1", "2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamContainingEscapedAnd()
|
public void Parse_With1ParamContainingComma_When_SupportMultiValueUsingComma_Is_True()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?winkel=C%26A";
|
string query = "?key=1,2,3";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["winkel"].Should().Equal(new WireMockList<string>(new[] { "C&A" }));
|
result["key"].Should().Equal(new WireMockList<string>("1", "2", "3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamContainingParentheses()
|
public void Parse_With1ParamContainingCommaAndAmpCombined_When_SupportMultiValueUsingComma_Is_Comma()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?Transaction=(123)";
|
string query = "?key=1,2&key=3";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["Transaction"].Should().Equal(new WireMockList<string>(new[] { "(123)" }));
|
result["key"].Should().Equal(new WireMockList<string>("1", "2", "3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_WithMultipleParamWithSameKey()
|
public void Parse_With1ParamContainingComma_SupportMultiValueUsingComma_Is_AmpersandAndSemiColon()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?key=value&key=anotherValue";
|
string query = "?$filter=startswith(name,'testName')";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query, QueryParameterMultipleValueSupport.AmpersandAndSemiColon);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["key"].Should().Equal(new WireMockList<string>(new[] { "value", "anotherValue" }));
|
result["$filter"].Should().Equal(new WireMockList<string>("startswith(name,'testName')"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_With1ParamContainingSpacesAndEqualSign()
|
public void Parse_With1ParamContainingEscapedAnd()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?q=SELECT Id from User where username='user@gmail.com'";
|
string query = "?winkel=C%26A";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(1);
|
result.Count.Should().Be(1);
|
||||||
result["q"].Should().Equal(new WireMockList<string>("SELECT Id from User where username='user@gmail.com'"));
|
result["winkel"].Should().Equal(new WireMockList<string>("C&A"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Parse_WithComplex()
|
public void Parse_With1ParamContainingParentheses()
|
||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
string query = "?q=energy+edge&rls=com.microsoft:en-au&ie=UTF-8&oe=UTF-8&startIndex=&startPage=1%22";
|
string query = "?Transaction=(123)";
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var result = QueryStringParser.Parse(query);
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
result.Count.Should().Be(6);
|
result.Count.Should().Be(1);
|
||||||
result["q"].Should().Equal(new WireMockList<string>("energy edge"));
|
result["Transaction"].Should().Equal(new WireMockList<string>("(123)"));
|
||||||
result["rls"].Should().Equal(new WireMockList<string>("com.microsoft:en-au"));
|
}
|
||||||
result["ie"].Should().Equal(new WireMockList<string>("UTF-8"));
|
|
||||||
result["oe"].Should().Equal(new WireMockList<string>("UTF-8"));
|
[Fact]
|
||||||
result["startIndex"].Should().Equal(new WireMockList<string>());
|
public void Parse_WithMultipleParamWithSameKey()
|
||||||
result["startPage"].Should().Equal(new WireMockList<string>("1\""));
|
{
|
||||||
}
|
// Assign
|
||||||
|
string query = "?key=value&key=anotherValue";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Count.Should().Be(1);
|
||||||
|
result["key"].Should().Equal(new WireMockList<string>("value", "anotherValue"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Parse_With1ParamContainingSpacesSingleQuoteAndEqualSign()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
string query = "?q=SELECT Id from User where username='user@gmail.com'";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Count.Should().Be(1);
|
||||||
|
result["q"].Should().Equal(new WireMockList<string>("SELECT Id from User where username='user@gmail.com'"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issue #849
|
||||||
|
[Fact]
|
||||||
|
public void Parse_With1ParamContainingComma_Using_QueryParameterMultipleValueSupport_NoComma()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
string query = "?query=SELECT id, value FROM table WHERE id = 1&test=42";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = QueryStringParser.Parse(query, QueryParameterMultipleValueSupport.NoComma);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Count.Should().Be(2);
|
||||||
|
result["query"].Should().Equal(new WireMockList<string>("SELECT id, value FROM table WHERE id = 1"));
|
||||||
|
result["test"].Should().Equal(new WireMockList<string>("42"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Parse_WithComplex()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
string query = "?q=energy+edge&rls=com.microsoft:en-au&ie=UTF-8&oe=UTF-8&startIndex=&startPage=1%22";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = QueryStringParser.Parse(query);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
result.Count.Should().Be(6);
|
||||||
|
result["q"].Should().Equal(new WireMockList<string>("energy edge"));
|
||||||
|
result["rls"].Should().Equal(new WireMockList<string>("com.microsoft:en-au"));
|
||||||
|
result["ie"].Should().Equal(new WireMockList<string>("UTF-8"));
|
||||||
|
result["oe"].Should().Equal(new WireMockList<string>("UTF-8"));
|
||||||
|
result["startIndex"].Should().Equal(new WireMockList<string>());
|
||||||
|
result["startPage"].Should().Equal(new WireMockList<string>("1\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,17 +31,17 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Codecov" Version="1.13.0" />
|
<PackageReference Include="Codecov" Version="1.13.0" />
|
||||||
<PackageReference Include="coverlet.msbuild" Version="3.1.2">
|
<PackageReference Include="coverlet.msbuild" Version="3.2.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
<PackageReference Include="coverlet.collector" Version="3.2.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Net;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using NFluent;
|
using NFluent;
|
||||||
@@ -374,6 +375,26 @@ public class WireMockServerAdminTests
|
|||||||
server.Stop();
|
server.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WireMockServer_Admin_Logging_SetMaxRequestLogCount_To_0_Should_Not_AddLogging()
|
||||||
|
{
|
||||||
|
// Assign
|
||||||
|
var client = new HttpClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var server = WireMockServer.Start();
|
||||||
|
server.SetMaxRequestLogCount(0);
|
||||||
|
|
||||||
|
await client.GetAsync("http://localhost:" + server.Port + "/foo1").ConfigureAwait(false);
|
||||||
|
await client.GetAsync("http://localhost:" + server.Port + "/foo2").ConfigureAwait(false);
|
||||||
|
await client.GetAsync("http://localhost:" + server.Port + "/foo3").ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
server.LogEntries.Should().BeEmpty();
|
||||||
|
|
||||||
|
server.Stop();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WireMockServer_Admin_WatchStaticMappings()
|
public void WireMockServer_Admin_WatchStaticMappings()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public class WireMockServerWebhookTests
|
|||||||
{
|
{
|
||||||
// Assign
|
// Assign
|
||||||
var serverReceivingTheWebhook = WireMockServer.Start();
|
var serverReceivingTheWebhook = WireMockServer.Start();
|
||||||
serverReceivingTheWebhook.Given(Request.Create().WithPath("x").UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
|
serverReceivingTheWebhook.Given(Request.Create().WithPath("/x").UsingPost()).RespondWith(Response.Create().WithStatusCode(200));
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var server = WireMockServer.Start();
|
var server = WireMockServer.Start();
|
||||||
@@ -126,6 +126,7 @@ public class WireMockServerWebhookTests
|
|||||||
content.Should().Be("a-response");
|
content.Should().Be("a-response");
|
||||||
|
|
||||||
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
|
serverReceivingTheWebhook.LogEntries.Should().HaveCount(1);
|
||||||
|
serverReceivingTheWebhook.LogEntries.First().MappingGuid.Should().NotBeNull();
|
||||||
|
|
||||||
server.Dispose();
|
server.Dispose();
|
||||||
serverReceivingTheWebhook.Dispose();
|
serverReceivingTheWebhook.Dispose();
|
||||||
|
|||||||
46
test/WireMock.Net.Tests/WireMockServerTests.WithParam.cs
Normal file
46
test/WireMock.Net.Tests/WireMockServerTests.WithParam.cs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using WireMock.RequestBuilders;
|
||||||
|
using WireMock.ResponseBuilders;
|
||||||
|
using WireMock.Server;
|
||||||
|
using WireMock.Settings;
|
||||||
|
using WireMock.Types;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Tests;
|
||||||
|
|
||||||
|
public partial class WireMockServerTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData("SELECT id, value FROM table WHERE id = 1")]
|
||||||
|
[InlineData("select id, name, value from table where id in (1, 2, 3, 4, 5)")]
|
||||||
|
public async Task WireMockServer_WithParam_QueryParameterMultipleValueSupport_NoComma_Should_Ignore_Comma(string queryValue)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var settings = new WireMockServerSettings
|
||||||
|
{
|
||||||
|
QueryParameterMultipleValueSupport = QueryParameterMultipleValueSupport.NoComma
|
||||||
|
};
|
||||||
|
var server = WireMockServer.Start(settings);
|
||||||
|
server.Given(
|
||||||
|
Request.Create()
|
||||||
|
.UsingGet()
|
||||||
|
.WithPath("/foo")
|
||||||
|
.WithParam("query", queryValue)
|
||||||
|
)
|
||||||
|
.RespondWith(
|
||||||
|
Response.Create().WithStatusCode(200)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var requestUri = new Uri($"http://localhost:{server.Port}/foo?query={queryValue}");
|
||||||
|
var response = await server.CreateClient().GetAsync(requestUri).ConfigureAwait(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||||
|
|
||||||
|
server.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user