Skip to content

Commit f35a34b

Browse files
committed
Merge branch 'master' into stef-fluent
2 parents 3a96f65 + 275816c commit f35a34b

16 files changed

+552
-65
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.5.62 (27 July 2024)
2+
- [#1147](https://github.com/WireMock-Net/WireMock.Net/pull/1147) - Add FormUrlEncodedMatcher [feature] contributed by [StefH](https://github.com/StefH)
3+
- [#1143](https://github.com/WireMock-Net/WireMock.Net/issues/1143) - FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug]
4+
15
# 1.5.61 (22 July 2024)
26
- [#1122](https://github.com/WireMock-Net/WireMock.Net/pull/1122) - Fix OpenApiPathsMapper [bug] contributed by [StefH](https://github.com/StefH)
37
- [#1135](https://github.com/WireMock-Net/WireMock.Net/pull/1135) - Bump System.Text.Json from 4.7.2 to 8.0.4 in /examples/WireMock.Net.Console.Net472.Classic [dependencies] contributed by [dependabot[bot]](https://github.com/apps/dependabot)

Directory.Build.props

+14-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
</PropertyGroup>
55

66
<PropertyGroup>
7-
<VersionPrefix>1.5.61</VersionPrefix>
7+
<VersionPrefix>1.5.62</VersionPrefix>
88
<PackageIcon>WireMock.Net-Logo.png</PackageIcon>
99
<PackageProjectUrl>https://github.com/WireMock-Net/WireMock.Net</PackageProjectUrl>
1010
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
@@ -42,4 +42,17 @@
4242
</PropertyGroup>
4343
</When>
4444
</Choose>
45+
46+
<ItemGroup>
47+
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.29.0.95321">
48+
<PrivateAssets>all</PrivateAssets>
49+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
50+
</PackageReference>
51+
<!-- <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
52+
<PrivateAssets>all</PrivateAssets>
53+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
54+
</PackageReference> -->
55+
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" PrivateAssets="All" />
56+
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
57+
</ItemGroup>
4558
</Project>

Generate-ReleaseNotes.cmd

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
rem https://github.com/StefH/GitHubReleaseNotes
22

3-
SET version=1.5.61
3+
SET version=1.5.62
44

55
GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels question invalid doc duplicate example environment --version %version% --token %GH_TOKEN%
66

PackageReleaseNotes.txt

+3-11
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
# 1.5.61 (22 July 2024)
2-
- #1122 Fix OpenApiPathsMapper [bug]
3-
- #1135 Bump System.Text.Json from 4.7.2 to 8.0.4 in /examples/WireMock.Net.Console.Net472.Classic [dependencies]
4-
- #1136 Bump System.Text.Json from 8.0.0 to 8.0.4 in /src/dotnet-WireMock.Net [dependencies]
5-
- #1137 Add link to TIOBE Index on main page + fix issues [refactor]
6-
- #1138 Fix some SonarCloud warnings [refactor]
7-
- #1141 Update WireMockContainerBuilder.WithMappings for &quot;includeSubDirectories&quot; [feature]
8-
- #1142 Make property FromConfiguredStub nullable (for WireMock.Org) [bug]
9-
- #1118 Generating mappings from Payroc open-api file gives ArgumentException: Property with the same name already exists on object [bug]
10-
- #1139 Allow WithMappings to support scanning SubDirectories when building a WireMockContainer [feature]
11-
- #1140 WireMock.Org nullable properties and defaults [bug]
1+
# 1.5.62 (27 July 2024)
2+
- #1147 Add FormUrlEncodedMatcher [feature]
3+
- #1143 FormEncoded Request fails (404 Not Found) if key value pairs order in mapping is different from request body order [bug]
124

135
The full release notes can be found here: https://github.com/WireMock-Net/WireMock.Net/blob/master/CHANGELOG.md

WireMock.Net Solution.sln

-3
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
44
VisualStudioVersion = 17.0.31521.260
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8F890C6F-9ACC-438D-928A-AD61CDA862F2}"
7-
ProjectSection(SolutionItems) = preProject
8-
src\Directory.Build.props = src\Directory.Build.props
9-
EndProjectSection
107
EndProject
118
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0BB8B634-407A-4610-A91F-11586990767A}"
129
EndProject

src/Directory.Build.props

-19
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
// Copyright © WireMock.Net
2+
3+
using System.Collections.Generic;
4+
using AnyOfTypes;
5+
using Stef.Validation;
6+
using WireMock.Models;
7+
using WireMock.Util;
8+
9+
namespace WireMock.Matchers;
10+
11+
/// <summary>
12+
/// FormUrl Encoded fields Matcher
13+
/// </summary>
14+
/// <inheritdoc cref="IStringMatcher"/>
15+
/// <inheritdoc cref="IIgnoreCaseMatcher"/>
16+
public class FormUrlEncodedMatcher : IStringMatcher, IIgnoreCaseMatcher
17+
{
18+
private readonly AnyOf<string, StringPattern>[] _patterns;
19+
20+
/// <inheritdoc />
21+
public MatchBehaviour MatchBehaviour { get; }
22+
23+
private readonly List<(WildcardMatcher Key, WildcardMatcher? Value)> _pairs = [];
24+
25+
/// <summary>
26+
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
27+
/// </summary>
28+
/// <param name="pattern">The pattern.</param>
29+
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
30+
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
31+
public FormUrlEncodedMatcher(
32+
AnyOf<string, StringPattern> pattern,
33+
bool ignoreCase = false,
34+
MatchOperator matchOperator = MatchOperator.Or) :
35+
this(MatchBehaviour.AcceptOnMatch, [pattern], ignoreCase, matchOperator)
36+
{
37+
}
38+
39+
/// <summary>
40+
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
41+
/// </summary>
42+
/// <param name="matchBehaviour">The match behaviour.</param>
43+
/// <param name="pattern">The pattern.</param>
44+
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
45+
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
46+
public FormUrlEncodedMatcher(
47+
MatchBehaviour matchBehaviour,
48+
AnyOf<string, StringPattern> pattern,
49+
bool ignoreCase = false,
50+
MatchOperator matchOperator = MatchOperator.Or) :
51+
this(matchBehaviour, [pattern], ignoreCase, matchOperator)
52+
{
53+
}
54+
55+
/// <summary>
56+
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
57+
/// </summary>
58+
/// <param name="patterns">The patterns.</param>
59+
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
60+
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
61+
public FormUrlEncodedMatcher(
62+
AnyOf<string, StringPattern>[] patterns,
63+
bool ignoreCase = false,
64+
MatchOperator matchOperator = MatchOperator.Or) :
65+
this(MatchBehaviour.AcceptOnMatch, patterns, ignoreCase, matchOperator)
66+
{
67+
}
68+
69+
/// <summary>
70+
/// Initializes a new instance of the <see cref="FormUrlEncodedMatcher"/> class.
71+
/// </summary>
72+
/// <param name="matchBehaviour">The match behaviour.</param>
73+
/// <param name="patterns">The patterns.</param>
74+
/// <param name="ignoreCase">Ignore the case from the pattern.</param>
75+
/// <param name="matchOperator">The <see cref="Matchers.MatchOperator"/> to use. (default = "Or")</param>
76+
public FormUrlEncodedMatcher(
77+
MatchBehaviour matchBehaviour,
78+
AnyOf<string, StringPattern>[] patterns,
79+
bool ignoreCase = false,
80+
MatchOperator matchOperator = MatchOperator.Or)
81+
{
82+
_patterns = Guard.NotNull(patterns);
83+
IgnoreCase = ignoreCase;
84+
MatchBehaviour = matchBehaviour;
85+
MatchOperator = matchOperator;
86+
87+
foreach (var pattern in _patterns)
88+
{
89+
if (QueryStringParser.TryParse(pattern, IgnoreCase, out var nameValueCollection))
90+
{
91+
foreach (var nameValue in nameValueCollection)
92+
{
93+
var keyMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Key], ignoreCase, MatchOperator);
94+
var valueMatcher = new WildcardMatcher(MatchBehaviour.AcceptOnMatch, [nameValue.Value], ignoreCase, MatchOperator);
95+
_pairs.Add((keyMatcher, valueMatcher));
96+
}
97+
}
98+
}
99+
}
100+
101+
/// <inheritdoc />
102+
public MatchResult IsMatch(string? input)
103+
{
104+
// Input is null or empty and if no patterns defined, return Perfect match.
105+
if (string.IsNullOrEmpty(input) && _patterns.Length == 0)
106+
{
107+
return new MatchResult(MatchScores.Perfect);
108+
}
109+
110+
if (!QueryStringParser.TryParse(input, IgnoreCase, out var inputNameValueCollection))
111+
{
112+
return new MatchResult(MatchScores.Mismatch);
113+
}
114+
115+
var matches = new List<bool>();
116+
foreach (var inputKeyValuePair in inputNameValueCollection)
117+
{
118+
var match = false;
119+
foreach (var pair in _pairs)
120+
{
121+
var keyMatchResult = pair.Key.IsMatch(inputKeyValuePair.Key).IsPerfect();
122+
if (keyMatchResult)
123+
{
124+
match = pair.Value?.IsMatch(inputKeyValuePair.Value).IsPerfect() ?? false;
125+
if (match)
126+
{
127+
break;
128+
}
129+
}
130+
}
131+
132+
matches.Add(match);
133+
}
134+
135+
var score = MatchScores.ToScore(matches.ToArray(), MatchOperator);
136+
return new MatchResult(MatchBehaviourHelper.Convert(MatchBehaviour, score));
137+
}
138+
139+
/// <inheritdoc />
140+
public virtual AnyOf<string, StringPattern>[] GetPatterns()
141+
{
142+
return _patterns;
143+
}
144+
145+
/// <inheritdoc />
146+
public virtual string Name => nameof(FormUrlEncodedMatcher);
147+
148+
/// <inheritdoc />
149+
public bool IgnoreCase { get; }
150+
151+
/// <inheritdoc />
152+
public MatchOperator MatchOperator { get; }
153+
}

src/WireMock.Net/Serialization/MappingConverter.cs

+14-11
Original file line numberDiff line numberDiff line change
@@ -147,19 +147,22 @@ public string ToCSharpCode(IMapping mapping, MappingConverterSettings? settings
147147
{
148148
var firstMatcher = requestMessageBodyMatcher.Matchers.FirstOrDefault();
149149

150-
if (firstMatcher is WildcardMatcher wildcardMatcher && wildcardMatcher.GetPatterns().Any())
150+
switch (firstMatcher)
151151
{
152-
sb.AppendLine($" .WithBody({GetString(wildcardMatcher)})");
153-
}
152+
case IStringMatcher stringMatcher when stringMatcher.GetPatterns().Length > 0:
153+
sb.AppendLine($" .WithBody({GetString(stringMatcher)})");
154+
break;
154155

155-
if (firstMatcher is JsonMatcher jsonMatcher)
156-
{
157-
var matcherType = jsonMatcher.GetType().Name;
158-
sb.AppendLine($" .WithBody(new {matcherType}(");
159-
sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
160-
sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
161-
sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}");
162-
sb.AppendLine(@" ))");
156+
case JsonMatcher jsonMatcher:
157+
{
158+
var matcherType = jsonMatcher.GetType().Name;
159+
sb.AppendLine($" .WithBody(new {matcherType}(");
160+
sb.AppendLine($" value: {ConvertToAnonymousObjectDefinition(jsonMatcher.Value, 3)},");
161+
sb.AppendLine($" ignoreCase: {ToCSharpBooleanLiteral(jsonMatcher.IgnoreCase)},");
162+
sb.AppendLine($" regex: {ToCSharpBooleanLiteral(jsonMatcher.Regex)}");
163+
sb.AppendLine(@" ))");
164+
break;
165+
}
163166
}
164167
}
165168

src/WireMock.Net/Serialization/MatcherMapper.cs

+5-2
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ public MatcherMapper(WireMockServerSettings settings)
111111
case nameof(ContentTypeMatcher):
112112
return new ContentTypeMatcher(matchBehaviour, stringPatterns, ignoreCase);
113113

114+
case nameof(FormUrlEncodedMatcher):
115+
return new FormUrlEncodedMatcher(matchBehaviour, stringPatterns, ignoreCase);
116+
114117
case nameof(SimMetricsMatcher):
115118
SimMetricType type = SimMetricType.Levenstein;
116119
if (!string.IsNullOrEmpty(matcherType) && !Enum.TryParse(matcherType, out type))
@@ -224,7 +227,7 @@ private AnyOf<string, StringPattern>[] ParseStringPatterns(MatcherModel matcher)
224227
{
225228
if (matcher.Pattern is string patternAsString)
226229
{
227-
return new[] { new AnyOf<string, StringPattern>(patternAsString) };
230+
return [new AnyOf<string, StringPattern>(patternAsString)];
228231
}
229232

230233
if (matcher.Pattern is IEnumerable<string> patternAsStringArray)
@@ -241,7 +244,7 @@ private AnyOf<string, StringPattern>[] ParseStringPatterns(MatcherModel matcher)
241244
{
242245
var patternAsFile = matcher.PatternAsFile!;
243246
var pattern = _settings.FileSystemHandler.ReadFileAsString(patternAsFile);
244-
return new[] { new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile }) };
247+
return [new AnyOf<string, StringPattern>(new StringPattern { Pattern = pattern, PatternAsFile = patternAsFile })];
245248
}
246249

247250
return EmptyArray<AnyOf<string, StringPattern>>.Value;

0 commit comments

Comments
 (0)