Compare commits

..

15 Commits

Author SHA1 Message Date
Stef Heyenrath
88ff2a72db select id, name, value from table where id in (1, 2, 3, 4, 5) 2022-11-30 18:53:50 +01:00
Stef Heyenrath
d5026e83b5 cf 2022-11-30 15:39:09 +01:00
Stef Heyenrath
b6b17d80cc Merge branch 'master' into stef-849 2022-11-30 15:35:38 +01:00
Stef Heyenrath
c8ad9f49e4 Fix 2022-11-30 15:23:36 +01:00
Stef Heyenrath
0a4aca9ae9 cf 2022-11-30 11:55:31 +01:00
Stef Heyenrath
e5ed6036ee fx 2022-11-30 10:03:41 +01:00
Stef Heyenrath
7b534635b9 fix tests 2022-11-30 08:19:10 +00:00
Stef Heyenrath
1562958172 Add more QueryParameterMultipleValueSupport NoComma tests 2022-11-30 07:36:13 +00:00
Stef Heyenrath
35d42a5c0d Fix Linux CI build + Fix opencover (#851)
* Fix Linux CI Test (opencover.xml)

* 2

* Build & Execute Unit tests

* ,cmd

* cout

* 12

* b

* server

* b =b

* /p:CoverletOutput=./test/WireMock.Net.Tests/WireMock.Net.Tests

* co?

* 2p

* 2?

* failOnStderr: false

* e0

* cc

* pub

* sc

* coverlet

* props

* pt

* coverage.net6.0.opencover.xml
2022-11-25 18:13:02 +01:00
Stef Heyenrath
429d6830ae 1.5.11 2022-11-24 21:47:56 +01:00
Stef Heyenrath
38634ac65a Use try-catch when adding or removing logEntry (#848)
* Use try-catch when removing logEntry

* .

* try catch add

* Add extra check

* ...
2022-11-21 07:30:27 +01:00
Stef Heyenrath
ef5f988786 Add Settings.QueryParameterMultipleValueSupport (#836)
* QueryParameterMultipleValueSupport

* .

* ,

* ,
2022-11-08 19:27:44 +01:00
Stef Heyenrath
1e44f52ad6 1.5.10 2022-11-06 13:29:12 +01:00
Stef Heyenrath
7fd1d30d0e Add WireMockNullLogger as valid commandline logger option (#845)
* Add WireMockNullLogger as valid commandline logger option

* .
2022-11-06 13:25:26 +01:00
Gerhard Gradnig
49b29d74dc Webhook: Use the transformed URL to create the HttpRequestMessage (#843)
Co-authored-by: Gerhard Gradnig <gerhard.gradnig@admiral.at>
2022-11-05 10:54:39 +01:00
57 changed files with 1764 additions and 1465 deletions

View File

@@ -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)

View File

@@ -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=""/>-->

View File

@@ -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%

View File

@@ -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

View File

@@ -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

View File

@@ -1,7 +1,7 @@
<?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">

View File

@@ -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>

View File

@@ -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>

View File

@@ -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; }
} }

View File

@@ -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.

View File

@@ -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
}

View File

@@ -1,11 +1,11 @@
#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))
{ {
@@ -17,5 +17,4 @@ namespace WireMock.Net.OpenApiParser.Extensions
return true; return true;
} }
} }
}
#endif #endif

View File

@@ -1,10 +1,10 @@
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> /// <summary>
@@ -23,7 +23,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
return false; return false;
} }
public static SchemaType GetSchemaType(this OpenApiSchema schema) public static SchemaType GetSchemaType(this OpenApiSchema? schema)
{ {
switch (schema?.Type) switch (schema?.Type)
{ {
@@ -53,7 +53,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
} }
} }
public static SchemaFormat GetSchemaFormat(this OpenApiSchema schema) public static SchemaFormat GetSchemaFormat(this OpenApiSchema? schema)
{ {
switch (schema?.Format) switch (schema?.Format)
{ {
@@ -89,4 +89,3 @@ namespace WireMock.Net.OpenApiParser.Extensions
} }
} }
} }
}

View File

@@ -1,16 +1,14 @@
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> /// <summary>
/// Some extension methods for <see cref="IWireMockServer"/>. /// Some extension methods for <see cref="IWireMockServer"/>.
/// </summary> /// </summary>
@@ -25,7 +23,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
[PublicAPI] [PublicAPI]
public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic) public static IWireMockServer WithMappingFromOpenApiFile(this IWireMockServer server, string path, out OpenApiDiagnostic diagnostic)
{ {
return WithMappingFromOpenApiFile(server, path, null, out diagnostic); return WithMappingFromOpenApiFile(server, path, new WireMockOpenApiParserSettings(), out diagnostic);
} }
/// <summary> /// <summary>
@@ -55,7 +53,7 @@ namespace WireMock.Net.OpenApiParser.Extensions
[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>
@@ -68,9 +66,9 @@ namespace WireMock.Net.OpenApiParser.Extensions
[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);
@@ -84,14 +82,13 @@ namespace WireMock.Net.OpenApiParser.Extensions
/// <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());
} }
} }
}

View File

@@ -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"/>.

View File

@@ -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,8 +16,8 @@ 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 const string HeaderContentType = "Content-Type";
@@ -26,7 +27,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
public OpenApiPathsMapper(WireMockOpenApiParserSettings settings) public OpenApiPathsMapper(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings, nameof(settings)); _settings = Guard.NotNull(settings);
_exampleValueGenerator = new ExampleValueGenerator(settings); _exampleValueGenerator = new ExampleValueGenerator(settings);
} }
@@ -53,7 +54,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
var response = operation.Responses.FirstOrDefault(); var response = operation.Responses.FirstOrDefault();
TryGetContent(response.Value?.Content, out OpenApiMediaType responseContent, out string responseContentType); TryGetContent(response.Value?.Content, out OpenApiMediaType? responseContent, out string? responseContentType);
var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema; var responseSchema = response.Value?.Content?.FirstOrDefault().Value?.Schema;
var responseExample = responseContent?.Example; var responseExample = responseContent?.Example;
var responseSchemaExample = responseContent?.Schema?.Example; var responseSchemaExample = responseContent?.Schema?.Example;
@@ -66,10 +67,10 @@ namespace WireMock.Net.OpenApiParser.Mappers
if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required) if (operation.RequestBody != null && operation.RequestBody.Content != null && operation.RequestBody.Required)
{ {
var request = operation.RequestBody.Content; var request = operation.RequestBody.Content;
TryGetContent(request, out OpenApiMediaType requestContent, out string requestContentType); TryGetContent(request, out OpenApiMediaType? requestContent, out _);
var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema; var requestBodySchema = operation.RequestBody.Content.First().Value?.Schema;
var requestBodyExample = requestContent.Example; var requestBodyExample = requestContent!.Example;
var requestBodySchemaExample = requestContent.Schema?.Example; var requestBodySchemaExample = requestContent.Schema?.Example;
var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) : var requestBodyMapped = requestBodyExample != null ? MapOpenApiAnyToJToken(requestBodyExample) :
@@ -104,7 +105,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private BodyModel MapRequestBody(object requestBody) private BodyModel? MapRequestBody(object? requestBody)
{ {
if (requestBody == null) if (requestBody == null)
{ {
@@ -122,7 +123,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private bool TryGetContent(IDictionary<string, OpenApiMediaType> contents, out OpenApiMediaType openApiMediaType, out string contentType) private bool TryGetContent(IDictionary<string, OpenApiMediaType>? contents, [NotNullWhen(true)] out OpenApiMediaType? openApiMediaType, [NotNullWhen(true)] out string? contentType)
{ {
openApiMediaType = null; openApiMediaType = null;
contentType = null; contentType = null;
@@ -147,7 +148,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return true; return true;
} }
private object MapSchemaToObject(OpenApiSchema schema, string name = null) private object? MapSchemaToObject(OpenApiSchema? schema, string? name = null)
{ {
if (schema == null) if (schema == null)
{ {
@@ -203,6 +204,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key)); propertyAsJObject.Add(MapPropertyAsJObject(schemaProperty.Value, schemaProperty.Key));
} }
if (schema.AllOf.Count > 0) if (schema.AllOf.Count > 0)
{ {
foreach (var property in schema.AllOf) foreach (var property in schema.AllOf)
@@ -214,7 +216,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
} }
} }
return name != null ? new JProperty(name, propertyAsJObject) : (JToken)propertyAsJObject; return name != null ? new JProperty(name, propertyAsJObject) : propertyAsJObject;
default: default:
return null; return null;
@@ -243,19 +245,15 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
return jp; return jp;
} }
else
{
return new JProperty(key, mapped); return new JProperty(key, mapped);
} }
}
else
{
// bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x); // bool propertyIsNullable = openApiSchema.Nullable || (openApiSchema.TryGetXNullable(out bool x) && x);
return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema)); return new JProperty(key, _exampleValueGenerator.GetExampleValue(openApiSchema));
} }
}
private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter> parameters) private string MapPathWithParameters(string path, IEnumerable<OpenApiParameter>? parameters)
{ {
if (parameters == null) if (parameters == null)
{ {
@@ -272,7 +270,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return newPath; return newPath;
} }
private string MapBasePath(IList<OpenApiServer> servers) private string MapBasePath(IList<OpenApiServer>? servers)
{ {
if (servers == null || servers.Count == 0) if (servers == null || servers.Count == 0)
{ {
@@ -288,7 +286,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return string.Empty; return string.Empty;
} }
private JToken MapOpenApiAnyToJToken(IOpenApiAny any) private JToken? MapOpenApiAnyToJToken(IOpenApiAny? any)
{ {
if (any == null) if (any == null)
{ {
@@ -303,17 +301,15 @@ namespace WireMock.Net.OpenApiParser.Mappers
{ {
return JArray.Parse(outputString.ToString()); return JArray.Parse(outputString.ToString());
} }
else
{
return JObject.Parse(outputString.ToString()); return JObject.Parse(outputString.ToString());
} }
}
private IDictionary<string, object> MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers) private IDictionary<string, object?>? MapHeaders(string responseContentType, IDictionary<string, OpenApiHeader> headers)
{ {
var mappedHeaders = headers.ToDictionary( var mappedHeaders = headers.ToDictionary(
item => item.Key, item => item.Key,
item => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern _ => GetExampleMatcherModel(null, _settings.HeaderPatternToUse).Pattern
); );
if (!string.IsNullOrEmpty(responseContentType)) if (!string.IsNullOrEmpty(responseContentType))
@@ -324,7 +320,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
return mappedHeaders.Keys.Any() ? mappedHeaders : null; return mappedHeaders.Keys.Any() ? mappedHeaders : null;
} }
private IList<ParamModel> MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters) private IList<ParamModel>? MapQueryParameters(IEnumerable<OpenApiParameter> queryParameters)
{ {
var list = queryParameters var list = queryParameters
.Where(req => req.Required) .Where(req => req.Required)
@@ -342,7 +338,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
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)
@@ -360,7 +356,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
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
{ {
@@ -370,7 +366,7 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
private string GetExampleValueAsStringForSchemaType(OpenApiSchema schema) private string GetExampleValueAsStringForSchemaType(OpenApiSchema? schema)
{ {
var value = _exampleValueGenerator.GetExampleValue(schema); var value = _exampleValueGenerator.GetExampleValue(schema);
@@ -382,4 +378,3 @@ namespace WireMock.Net.OpenApiParser.Mappers
}; };
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
{
/// <summary> /// <summary>
/// A interface defining the example values to use for the different types. /// A interface defining the example values to use for the different types.
/// </summary> /// </summary>
@@ -12,14 +12,17 @@ namespace WireMock.Net.OpenApiParser.Settings
/// An example value for a Boolean. /// An example value for a Boolean.
/// </summary> /// </summary>
bool Boolean { get; set; } bool Boolean { get; set; }
/// <summary> /// <summary>
/// An example value for an Integer. /// An example value for an Integer.
/// </summary> /// </summary>
int Integer { get; set; } int Integer { get; set; }
/// <summary> /// <summary>
/// An example value for a Float. /// An example value for a Float.
/// </summary> /// </summary>
float Float { get; set; } float Float { get; set; }
/// <summary> /// <summary>
/// An example value for a Double. /// An example value for a Double.
/// </summary> /// </summary>
@@ -53,6 +56,5 @@ namespace WireMock.Net.OpenApiParser.Settings
/// <summary> /// <summary>
/// OpenApi Schema to generate dynamic examples more accurate /// OpenApi Schema to generate dynamic examples more accurate
/// </summary> /// </summary>
OpenApiSchema Schema { get; set; } OpenApiSchema? Schema { get; set; }
}
} }

View File

@@ -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> /// <summary>
/// A class defining the random example values to use for the different types. /// A class defining the random example values to use for the different types.
/// </summary> /// </summary>
public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues public class WireMockOpenApiParserDynamicExampleValues : IWireMockOpenApiParserExampleValues
{ {
/// <inheritdoc /> /// <inheritdoc />
public virtual bool Boolean { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; } set { } } public virtual bool Boolean { get => RandomizerFactory.GetRandomizer(new FieldOptionsBoolean()).Generate() ?? true; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual int Integer { get { return RandomizerFactory.GetRandomizer(new FieldOptionsInteger()).Generate() ?? 42; } set { } } public virtual int Integer { get => 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 float Float { get => RandomizerFactory.GetRandomizer(new FieldOptionsFloat()).Generate() ?? 4.2f; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual double Double { get { return RandomizerFactory.GetRandomizer(new FieldOptionsDouble()).Generate() ?? 4.2d; } set { } } public virtual double Double { get => 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 /> /// <inheritdoc />
public virtual byte[] Bytes { get { return RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); } set { } } public virtual byte[] Bytes { get => RandomizerFactory.GetRandomizer(new FieldOptionsBytes()).Generate(); set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual object Object { get; set; } = "example-object"; 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 string String { get => RandomizerFactory.GetRandomizer(new FieldOptionsTextRegex { Pattern = @"^[0-9]{2}[A-Z]{5}[0-9]{2}" }).Generate() ?? "example-string"; set { } }
/// <inheritdoc /> /// <inheritdoc />
public virtual OpenApiSchema Schema { get; set; } public virtual OpenApiSchema? Schema { get; set; }
}
} }

View File

@@ -1,8 +1,8 @@
using System; using System;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
{
/// <summary> /// <summary>
/// A class defining the example values to use for the different types. /// A class defining the example values to use for the different types.
/// </summary> /// </summary>
@@ -10,23 +10,31 @@ namespace WireMock.Net.OpenApiParser.Settings
{ {
/// <inheritdoc /> /// <inheritdoc />
public virtual bool Boolean { get; set; } = true; public virtual bool Boolean { get; set; } = true;
/// <inheritdoc /> /// <inheritdoc />
public virtual int Integer { get; set; } = 42; public virtual int Integer { get; set; } = 42;
/// <inheritdoc /> /// <inheritdoc />
public virtual float Float { get; set; } = 4.2f; public virtual float Float { get; set; } = 4.2f;
/// <inheritdoc /> /// <inheritdoc />
public virtual double Double { get; set; } = 4.2d; 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 /> /// <inheritdoc />
public virtual byte[] Bytes { get; set; } = { 48, 49, 50 }; public virtual byte[] Bytes { get; set; } = { 48, 49, 50 };
/// <inheritdoc /> /// <inheritdoc />
public virtual object Object { get; set; } = "example-object"; public virtual object Object { get; set; } = "example-object";
/// <inheritdoc /> /// <inheritdoc />
public virtual string String { get; set; } = "example-string"; public virtual string String { get; set; } = "example-string";
/// <inheritdoc /> /// <inheritdoc />
public virtual OpenApiSchema Schema { get; set; } = new OpenApiSchema(); public virtual OpenApiSchema? Schema { get; set; } = new OpenApiSchema();
}
} }

View File

@@ -1,7 +1,7 @@
using WireMock.Net.OpenApiParser.Types; using WireMock.Net.OpenApiParser.Types;
namespace WireMock.Net.OpenApiParser.Settings namespace WireMock.Net.OpenApiParser.Settings;
{
/// <summary> /// <summary>
/// The WireMockOpenApiParser Settings /// The WireMockOpenApiParser Settings
/// </summary> /// </summary>
@@ -34,7 +34,7 @@ namespace WireMock.Net.OpenApiParser.Settings
/// - <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).
@@ -61,4 +61,3 @@ namespace WireMock.Net.OpenApiParser.Settings
/// </summary> /// </summary>
public bool DynamicExamples { get; set; } = false; public bool DynamicExamples { get; set; } = false;
} }
}

View File

@@ -1,5 +1,5 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
{
/// <summary> /// <summary>
/// The example value to use /// The example value to use
/// </summary> /// </summary>
@@ -17,4 +17,3 @@ namespace WireMock.Net.OpenApiParser.Types
/// </summary> /// </summary>
Wildcard Wildcard
} }
}

View File

@@ -1,5 +1,5 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
{
internal enum SchemaFormat internal enum SchemaFormat
{ {
Float, Float,
@@ -22,4 +22,3 @@
Undefined Undefined
} }
}

View File

@@ -1,5 +1,5 @@
namespace WireMock.Net.OpenApiParser.Types namespace WireMock.Net.OpenApiParser.Types;
{
internal enum SchemaType internal enum SchemaType
{ {
Object, Object,
@@ -18,4 +18,3 @@
Unknown Unknown
} }
}

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Globalization; using System.Globalization;
namespace WireMock.Net.OpenApiParser.Utils namespace WireMock.Net.OpenApiParser.Utils;
{
internal static class DateTimeUtils internal static class DateTimeUtils
{ {
public static string ToRfc3339DateTime(DateTime dateTime) public static string ToRfc3339DateTime(DateTime dateTime)
@@ -15,4 +15,3 @@ namespace WireMock.Net.OpenApiParser.Utils
return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo); return dateTime.ToString("yyyy-MM-dd", DateTimeFormatInfo.InvariantInfo);
} }
} }
}

View File

@@ -7,15 +7,15 @@ 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; private readonly WireMockOpenApiParserSettings _settings;
public ExampleValueGenerator(WireMockOpenApiParserSettings settings) public ExampleValueGenerator(WireMockOpenApiParserSettings settings)
{ {
_settings = Guard.NotNull(settings, nameof(settings)); _settings = Guard.NotNull(settings);
// Check if user provided an own implementation // Check if user provided an own implementation
if (settings.ExampleValues is null) if (settings.ExampleValues is null)
@@ -31,7 +31,7 @@ namespace WireMock.Net.OpenApiParser.Utils
} }
} }
public object GetExampleValue(OpenApiSchema schema) public object GetExampleValue(OpenApiSchema? schema)
{ {
var schemaExample = schema?.Example; var schemaExample = schema?.Example;
var schemaEnum = GetRandomEnumValue(schema?.Enum); var schemaEnum = GetRandomEnumValue(schema?.Enum);
@@ -41,7 +41,7 @@ namespace WireMock.Net.OpenApiParser.Utils
switch (schema?.GetSchemaType()) switch (schema?.GetSchemaType())
{ {
case SchemaType.Boolean: case SchemaType.Boolean:
var exampleBoolean = (OpenApiBoolean)schemaExample; var exampleBoolean = schemaExample as OpenApiBoolean;
return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value; return exampleBoolean is null ? _settings.ExampleValues.Boolean : exampleBoolean.Value;
case SchemaType.Integer: case SchemaType.Integer:
@@ -112,7 +112,7 @@ namespace WireMock.Net.OpenApiParser.Utils
} }
} }
private static IOpenApiAny GetRandomEnumValue(IList<IOpenApiAny> schemaEnum) private static IOpenApiAny? GetRandomEnumValue(IList<IOpenApiAny>? schemaEnum)
{ {
if (schemaEnum?.Count > 0) if (schemaEnum?.Count > 0)
{ {
@@ -124,4 +124,3 @@ namespace WireMock.Net.OpenApiParser.Utils
return null; return null;
} }
} }
}

View File

@@ -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" />

View File

@@ -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,8 +9,8 @@ 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> /// <summary>
/// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels. /// Parse a OpenApi/Swagger/V2/V3 or Raml to WireMock.Net MappingModels.
/// </summary> /// </summary>
@@ -60,9 +60,8 @@ namespace WireMock.Net.OpenApiParser
/// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" /> /// <inheritdoc cref="IWireMockOpenApiParser.FromDocument(OpenApiDocument, WireMockOpenApiParserSettings)" />
[PublicAPI] [PublicAPI]
public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings settings = null) public IEnumerable<MappingModel> FromDocument(OpenApiDocument openApiDocument, WireMockOpenApiParserSettings? settings = null)
{ {
return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers); return new OpenApiPathsMapper(settings).ToMappingModels(openApiDocument.Paths, openApiDocument.Servers);
} }
} }
}

View File

@@ -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.

View File

@@ -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))

View File

@@ -2,8 +2,8 @@ using Newtonsoft.Json;
using System; using System;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
namespace WireMock.Logging namespace WireMock.Logging;
{
/// <summary> /// <summary>
/// WireMockConsoleLogger which logs to Console /// WireMockConsoleLogger which logs to Console
/// </summary> /// </summary>
@@ -71,4 +71,3 @@ namespace WireMock.Logging
return $"{DateTime.UtcNow} [{level}] : {message}"; return $"{DateTime.UtcNow} [{level}] : {message}";
} }
} }
}

View File

@@ -1,8 +1,8 @@
using System; using System;
using WireMock.Admin.Requests; using WireMock.Admin.Requests;
namespace WireMock.Logging namespace WireMock.Logging;
{
/// <summary> /// <summary>
/// WireMockNullLogger which does not log. /// WireMockNullLogger which does not log.
/// </summary> /// </summary>
@@ -45,4 +45,3 @@ namespace WireMock.Logging
// Log nothing // Log nothing
} }
} }
}

View File

@@ -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);
} }

View File

@@ -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

View File

@@ -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; }
} }

View File

@@ -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)

View File

@@ -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;

View File

@@ -269,28 +269,54 @@ 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))
{
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); _options.LogEntries.Remove(logEntry);
} }
catch
{
// Ignore exception (can happen during stress testing)
} }
} }
} }

View File

@@ -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; }
} }

View File

@@ -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>

View File

@@ -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,

View File

@@ -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();

View File

@@ -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);

View File

@@ -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)
{ {

View File

@@ -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>

View File

@@ -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
var loggerType = parser.GetStringValue("WireMockLogger");
switch (loggerType)
{
case nameof(WireMockConsoleLogger):
settings.Logger = new WireMockConsoleLogger();
break;
case nameof(WireMockNullLogger):
settings.Logger = new WireMockNullLogger();
break;
default:
if (logger != null) if (logger != null)
{ {
settings.Logger = logger; settings.Logger = logger;
} }
else if (parser.GetStringValue("WireMockLogger") == "WireMockConsoleLogger") break;
{
settings.Logger = new WireMockConsoleLogger();
} }
if (parser.Contains(nameof(WireMockServerSettings.Port))) if (parser.Contains(nameof(WireMockServerSettings.Port)))

View File

@@ -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)));

View File

@@ -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>

View File

@@ -5,12 +5,12 @@ 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 int SleepTime = 30000;
private static readonly ILogger xLogger = LoggerFactory.Create(o => private static readonly ILogger XLogger = LoggerFactory.Create(o =>
{ {
o.SetMinimumLevel(LogLevel.Debug); o.SetMinimumLevel(LogLevel.Debug);
o.AddSimpleConsole(options => o.AddSimpleConsole(options =>
@@ -20,13 +20,13 @@ namespace WireMock.Net
options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss "; options.TimestampFormat = "yyyy-MM-ddTHH:mm:ss ";
}); });
}).CreateLogger("WireMock.Net"); }).CreateLogger("WireMock.Net");
private static readonly IWireMockLogger Logger = new WireMockLogger(xLogger); 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;
} }
@@ -45,16 +45,15 @@ namespace WireMock.Net
while (true) while (true)
{ {
Logger.Info("Server running : {IsStarted}", Server.IsStarted); Logger.Info("Server running : {0}", _server.IsStarted);
await Task.Delay(SleepTime).ConfigureAwait(false); await Task.Delay(SleepTime).ConfigureAwait(false);
} }
} }
private static void Stop(string why) private static void Stop(string why)
{ {
Logger.Info("Server stopping because '{why}'", why); Logger.Info("Server stopping because '{0}'", why);
Server.Stop(); _server.Stop();
Logger.Info("Server stopped"); Logger.Info("Server stopped");
} }
} }
}

View File

@@ -2,7 +2,7 @@
"profiles": { "profiles": {
"dotnet-WireMock.Net": { "dotnet-WireMock.Net": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "" "commandLineArgs": "--WireMockLogger s"
} }
} }
} }

View File

@@ -4,11 +4,11 @@ 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 JsonSerializerOptions private readonly JsonSerializerOptions _options = new()
{ {
WriteIndented = true WriteIndented = true
}; };
@@ -53,9 +53,8 @@ namespace WireMock.Net
/// <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);
} }
} }
}

View File

@@ -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);
}
} }
} }

View File

@@ -1,14 +1,15 @@
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"; private const string ClientIp = "::1";
@@ -212,6 +213,24 @@ namespace WireMock.Net.Tests
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
} }
[Fact]
public void Should_specify_requests_matching_given_param_WithComma()
{
// given
var options = new WireMockMiddlewareOptions
{
QueryParameterMultipleValueSupport = QueryParameterMultipleValueSupport.NoComma
};
var spec = Request.Create().WithParam("$filter", "startswith(name,'testName')");
// when
var request = new RequestMessage(options, new UrlDetails("http://localhost/?$filter=startswith(name,'testName')"), "PUT", ClientIp);
// then
var requestMatchResult = new RequestMatchResult();
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsEqualTo(1.0);
}
[Fact] [Fact]
public void Should_specify_requests_matching_given_param_func() public void Should_specify_requests_matching_given_param_func()
{ {
@@ -240,4 +259,3 @@ namespace WireMock.Net.Tests
Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0); Check.That(spec.GetMatchingScore(request, requestMatchResult)).IsNotEqualTo(1.0);
} }
} }
}

View File

@@ -1,18 +1,18 @@
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] [Fact]
public void Parse_WithNullString() public void Parse_WithNullString()
{ {
// Assign // Assign
string query = null; string? query = null;
// Act // Act
var result = QueryStringParser.Parse(query); var result = QueryStringParser.Parse(query);
@@ -172,11 +172,39 @@ namespace WireMock.Net.Tests.Util
// 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
string query = "?key=1&key=2";
// Act
var result = QueryStringParser.Parse(query);
// Assert
result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>("1", "2"));
}
[Fact]
public void Parse_With1ParamContainingComma_When_SupportMultiValueUsingComma_Is_True()
{
// Assign
string query = "?key=1,2,3";
// Act
var result = QueryStringParser.Parse(query);
// Assert
result.Count.Should().Be(1);
result["key"].Should().Equal(new WireMockList<string>("1", "2", "3"));
}
[Fact]
public void Parse_With1ParamContainingCommaAndAmpCombined_When_SupportMultiValueUsingComma_Is_Comma()
{ {
// Assign // Assign
string query = "?key=1,2&key=3"; string query = "?key=1,2&key=3";
@@ -186,7 +214,21 @@ namespace WireMock.Net.Tests.Util
// 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", "3"));
}
[Fact]
public void Parse_With1ParamContainingComma_SupportMultiValueUsingComma_Is_AmpersandAndSemiColon()
{
// Assign
string query = "?$filter=startswith(name,'testName')";
// Act
var result = QueryStringParser.Parse(query, QueryParameterMultipleValueSupport.AmpersandAndSemiColon);
// Assert
result.Count.Should().Be(1);
result["$filter"].Should().Equal(new WireMockList<string>("startswith(name,'testName')"));
} }
[Fact] [Fact]
@@ -200,7 +242,7 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["winkel"].Should().Equal(new WireMockList<string>(new[] { "C&A" })); result["winkel"].Should().Equal(new WireMockList<string>("C&A"));
} }
[Fact] [Fact]
@@ -214,7 +256,7 @@ namespace WireMock.Net.Tests.Util
// Assert // Assert
result.Count.Should().Be(1); result.Count.Should().Be(1);
result["Transaction"].Should().Equal(new WireMockList<string>(new[] { "(123)" })); result["Transaction"].Should().Equal(new WireMockList<string>("(123)"));
} }
[Fact] [Fact]
@@ -228,11 +270,11 @@ namespace WireMock.Net.Tests.Util
// 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_With1ParamContainingSpacesAndEqualSign() public void Parse_With1ParamContainingSpacesSingleQuoteAndEqualSign()
{ {
// Assign // Assign
string query = "?q=SELECT Id from User where username='user@gmail.com'"; string query = "?q=SELECT Id from User where username='user@gmail.com'";
@@ -245,6 +287,22 @@ namespace WireMock.Net.Tests.Util
result["q"].Should().Equal(new WireMockList<string>("SELECT Id from User where username='user@gmail.com'")); 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] [Fact]
public void Parse_WithComplex() public void Parse_WithComplex()
{ {
@@ -264,4 +322,3 @@ namespace WireMock.Net.Tests.Util
result["startPage"].Should().Equal(new WireMockList<string>("1\"")); result["startPage"].Should().Equal(new WireMockList<string>("1\""));
} }
} }
}

View File

@@ -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>

View File

@@ -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()
{ {

View File

@@ -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();

View 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();
}
}