This commit is contained in:
Stef Heyenrath
2026-01-10 15:00:35 +01:00
parent e0ca9a1223
commit 525f41f23b
15 changed files with 332 additions and 21 deletions

View File

@@ -8,6 +8,12 @@ namespace WireMock.Admin.Mappings;
[FluentBuilder.AutoGenerateBuilder]
public class BodyModel
{
/// <summary>
/// The name of the body matcher.
/// Currently only "MultiPartMatcher" is supported.
/// </summary>
public string? MatcherName { get; set; }
/// <summary>
/// Gets or sets the matcher.
/// </summary>

View File

@@ -0,0 +1,46 @@
// Copyright © WireMock.Net
using System;
namespace WireMock.Matchers;
/// <summary>
/// Represents a matcher that combines multiple matching strategies into a single composite operation.
/// </summary>
public class CompositeMatcher : IMatcher
{
/// <inheritdoc />
public string Name => nameof(CompositeMatcher);
/// <summary>
/// The logical operator used to combine the results of the matchers.
/// </summary>
public MatchOperator MatchOperator { get; }
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// All matchers.
/// </summary>
public IMatcher[] Matchers { get; }
/// <summary>
/// Initializes a new instance of the CompositeMatcher class with the specified matchers, operator, and match behaviour.
/// </summary>
/// <param name="matchers">An array of matchers to be combined. Cannot be null or contain null elements.</param>
/// <param name="matchOperator">The logical operator used to combine the results of the matchers.</param>
/// <param name="matchBehaviour">The behaviour that determines how the composite matcher interprets the combined results.</param>
public CompositeMatcher(IMatcher[] matchers, MatchOperator matchOperator, MatchBehaviour matchBehaviour)
{
Matchers = matchers;
MatchOperator = matchOperator;
MatchBehaviour = matchBehaviour;
}
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
throw new NotImplementedException();
}
}

View File

@@ -12,6 +12,11 @@ namespace WireMock.Matchers.Request;
/// </summary>
public class RequestMessageMultiPartMatcher : IRequestMatcher
{
/// <summary>
/// The name of this matcher.
/// </summary>
public const string MatcherName = "MultiPartMatcher";
private readonly IMimeKitUtils _mimeKitUtils = LoadMimeKitUtils();
/// <summary>
@@ -22,7 +27,7 @@ public class RequestMessageMultiPartMatcher : IRequestMatcher
/// <summary>
/// The <see cref="MatchOperator"/>
/// </summary>
public MatchOperator MatchOperator { get; } = MatchOperator.Or;
public MatchOperator MatchOperator { get; } = MatchOperator.And;
/// <summary>
/// The <see cref="MatchBehaviour"/>

View File

@@ -27,6 +27,11 @@ internal class MappingMatcher(IWireMockMiddlewareOptions options, IRandomizerDou
foreach (var mapping in mappings)
{
if (mapping.Guid == new Guid("b9c82182-e469-41da-bcaf-b6e3157fefdb") || mapping.Guid == new Guid("b9c82182-e469-41da-bcaf-b6e3157fefdc"))
{
int x = 9;
}
try
{
var nextState = GetNextState(mapping);

View File

@@ -15,7 +15,7 @@ public partial class Request
}
/// <inheritdoc />
public IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or)
public IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.And)
{
_requestMatchers.Add(new RequestMessageMultiPartMatcher(matchBehaviour, matchOperator, matchers));
return this;

View File

@@ -25,9 +25,9 @@ internal class MatcherMapper
_settings = Guard.NotNull(settings);
}
public IMatcher[]? Map(IEnumerable<MatcherModel>? matchers)
public IMatcher[] Map(IEnumerable<MatcherModel>? matchers)
{
return matchers?.Select(Map).OfType<IMatcher>().ToArray();
return matchers?.Select(Map).OfType<IMatcher>().ToArray() ?? [];
}
public IMatcher? Map(MatcherModel? matcherModel)

View File

@@ -6,6 +6,7 @@ using System.Linq;
using Stef.Validation;
using WireMock.Admin.Mappings;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Serialization;
@@ -253,7 +254,15 @@ public partial class WireMockServer
else if (requestModel.Body?.Matchers != null)
{
var matchOperator = StringUtils.ParseMatchOperator(requestModel.Body.MatchOperator);
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers)!, matchOperator);
if (requestModel.Body.MatcherName == RequestMessageMultiPartMatcher.MatcherName)
{
requestBuilder = requestBuilder.WithMultiPart(_matcherMapper.Map(requestModel.Body.Matchers), matchOperator: matchOperator);
}
else
{
requestBuilder = requestBuilder.WithBody(_matcherMapper.Map(requestModel.Body.Matchers), matchOperator);
}
}
return requestBuilder;

View File

@@ -8,26 +8,26 @@ namespace WireMock.Matchers.Helpers;
internal static class BodyDataMatchScoreCalculator
{
internal static MatchResult CalculateMatchScore(IBodyData? requestMessage, IMatcher matcher)
internal static MatchResult CalculateMatchScore(IBodyData? bodyData, IMatcher matcher)
{
Guard.NotNull(matcher);
if (requestMessage == null)
if (bodyData == null)
{
return default;
}
if (matcher is NotNullOrEmptyMatcher notNullOrEmptyMatcher)
{
switch (requestMessage.DetectedBodyType)
switch (bodyData.DetectedBodyType)
{
case BodyType.Json:
case BodyType.String:
case BodyType.FormUrlEncoded:
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsString);
return notNullOrEmptyMatcher.IsMatch(bodyData.BodyAsString);
case BodyType.Bytes:
return notNullOrEmptyMatcher.IsMatch(requestMessage.BodyAsBytes);
return notNullOrEmptyMatcher.IsMatch(bodyData.BodyAsBytes);
default:
return default;
@@ -37,37 +37,37 @@ internal static class BodyDataMatchScoreCalculator
if (matcher is ExactObjectMatcher { Value: byte[] } exactObjectMatcher)
{
// If the body is a byte array, try to match.
return exactObjectMatcher.IsMatch(requestMessage.BodyAsBytes);
return exactObjectMatcher.IsMatch(bodyData.BodyAsBytes);
}
// Check if the matcher is a IObjectMatcher
if (matcher is IObjectMatcher objectMatcher)
{
// If the body is a JSON object, try to match.
if (requestMessage.DetectedBodyType == BodyType.Json)
if (bodyData.DetectedBodyType == BodyType.Json)
{
return objectMatcher.IsMatch(requestMessage.BodyAsJson);
return objectMatcher.IsMatch(bodyData.BodyAsJson);
}
// If the body is a byte array, try to match.
if (requestMessage.DetectedBodyType == BodyType.Bytes)
if (bodyData.DetectedBodyType == BodyType.Bytes)
{
return objectMatcher.IsMatch(requestMessage.BodyAsBytes);
return objectMatcher.IsMatch(bodyData.BodyAsBytes);
}
}
// In case the matcher is a IStringMatcher and If body is a Json or a String, use the BodyAsString to match on.
if (matcher is IStringMatcher stringMatcher && requestMessage.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
// In case the matcher is a IStringMatcher and if body is a Json or a String, use the BodyAsString to match on.
if (matcher is IStringMatcher stringMatcher && bodyData.DetectedBodyType is BodyType.Json or BodyType.String or BodyType.FormUrlEncoded)
{
return stringMatcher.IsMatch(requestMessage.BodyAsString);
return stringMatcher.IsMatch(bodyData.BodyAsString);
}
// In case the matcher is a IProtoBufMatcher, use the BodyAsBytes to match on.
if (matcher is IProtoBufMatcher protoBufMatcher)
{
return protoBufMatcher.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult();
return protoBufMatcher.IsMatchAsync(bodyData.BodyAsBytes).GetAwaiter().GetResult();
}
return default;
}
}
}

View File

@@ -23,7 +23,7 @@ public interface IMultiPartRequestBuilder : IHttpVersionBuilder
/// <param name="matchBehaviour">The <see cref="MatchBehaviour"/> to use.</param>
/// <param name="matchOperator">The <see cref="MatchOperator"/> to use.</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.Or);
IRequestBuilder WithMultiPart(IMatcher[] matchers, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch, MatchOperator matchOperator = MatchOperator.And);
/// <summary>
/// WithMultiPart: MatchBehaviour and IMatcher[]