Create WireMock.Net.ProtoBuf project (#1350)

* Create WireMock.Net.ProtoBuf project

* ok

* Update Directory.Build.props

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Stef Heyenrath
2025-08-26 07:55:02 +02:00
committed by GitHub
parent 124d29c86a
commit e5c4605020
66 changed files with 478 additions and 416 deletions

View File

@@ -1,31 +0,0 @@
using System.Globalization;
namespace WireMock.Extensions;
internal static class StringExtensions
{
// See https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/
public static string GetDeterministicHashCodeAsString(this string str)
{
unchecked
{
int hash1 = (5381 << 16) + 5381;
int hash2 = hash1;
for (int i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
{
break;
}
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
int result = hash1 + hash2 * 1566083941;
return result.ToString(CultureInfo.InvariantCulture).Replace('-', '_');
}
}
}

View File

@@ -1,181 +0,0 @@
// Copyright © WireMock.Net
using System;
using System.Threading.Tasks;
using WireMock.Matchers.Request;
using WireMock.Models;
using WireMock.ResponseProviders;
using WireMock.Settings;
namespace WireMock;
/// <summary>
/// The IMapping interface.
/// </summary>
public interface IMapping
{
/// <summary>
/// Gets the unique identifier.
/// </summary>
Guid Guid { get; }
/// <summary>
/// The datetime when this mapping was created or updated.
/// </summary>
public DateTime? UpdatedAt { get; set; }
/// <summary>
/// Gets the TimeSettings (Start, End and TTL).
/// </summary>
ITimeSettings? TimeSettings { get; }
/// <summary>
/// Gets the unique title.
/// </summary>
string? Title { get; }
/// <summary>
/// Gets the description.
/// </summary>
string? Description { get; }
/// <summary>
/// The full filename path for this mapping (only defined for static mappings).
/// </summary>
string? Path { get; set; }
/// <summary>
/// Gets the priority. (A low value means higher priority.)
/// </summary>
int Priority { get; }
/// <summary>
/// Scenario.
/// </summary>
string? Scenario { get; }
/// <summary>
/// Execution state condition for the current mapping.
/// </summary>
string? ExecutionConditionState { get; }
/// <summary>
/// The next state which will be signaled after the current mapping execution.
/// In case the value is null, state will not be changed.
/// </summary>
string? NextState { get; }
/// <summary>
/// The number of times this match should be matched before the state will be changed to the next state.
/// </summary>
int? TimesInSameState { get; }
/// <summary>
/// The RequestMatcher.
/// </summary>
IRequestMatcher RequestMatcher { get; }
/// <summary>
/// The ResponseProvider.
/// </summary>
IResponseProvider Provider { get; }
/// <summary>
/// The WireMockServerSettings.
/// </summary>
WireMockServerSettings Settings { get; }
/// <summary>
/// Indicates if the state is started or manually set to a value.
/// </summary>
bool IsStartState { get; }
/// <summary>
/// Gets a value indicating whether this mapping is an Admin Interface.
/// </summary>
/// <value>
/// <c>true</c> if this mapping is an Admin Interface; otherwise, <c>false</c>.
/// </value>
bool IsAdminInterface { get; }
/// <summary>
/// Gets a value indicating whether this mapping is a Proxy Mapping.
/// </summary>
/// <value>
/// <c>true</c> if this mapping is a Proxy Mapping; otherwise, <c>false</c>.
/// </value>
bool IsProxy { get; }
/// <summary>
/// Gets a value indicating whether this mapping to be logged.
/// </summary>
/// <value>
/// <c>true</c> if this mapping to be logged; otherwise, <c>false</c>.
/// </value>
bool LogMapping { get; }
/// <summary>
/// The Webhooks.
/// </summary>
IWebhook[]? Webhooks { get; }
/// <summary>
/// Use Fire and Forget for the defined webhook(s). [Optional]
/// </summary>
bool? UseWebhooksFireAndForget { get; }
/// <summary>
/// Data Object which can be used when WithTransformer is used.
/// e.g. lookup an path in this object using
/// <example>
/// lookup data "1"
/// </example>
/// </summary>
object? Data { get; }
/// <summary>
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
/// </summary>
double? Probability { get; }
/// <summary>
/// The Grpc ProtoDefinition which is used for this mapping (request and response). [Optional]
/// </summary>
IdOrTexts? ProtoDefinition { get; }
/// <summary>
/// ProvideResponseAsync
/// </summary>
/// <param name="requestMessage">The request message.</param>
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IRequestMessage requestMessage);
/// <summary>
/// Gets the RequestMatchResult based on the RequestMessage.
/// </summary>
/// <param name="requestMessage">The request message.</param>
/// <param name="nextState">The Next State.</param>
/// <returns>The <see cref="IRequestMatchResult"/>.</returns>
IRequestMatchResult GetRequestMatchResult(IRequestMessage requestMessage, string? nextState);
/// <summary>
/// Define the scenario.
/// </summary>
/// <param name="scenario">The scenario.</param>
/// <returns>The <see cref="IMapping"/>.</returns>
IMapping WithScenario(string scenario);
/// <summary>
/// Define the probability when this request should be matched. [Optional]
/// </summary>
/// <param name="probability">The probability.</param>
/// <returns>The <see cref="IMapping"/>.</returns>
IMapping WithProbability(double probability);
/// <summary>
/// Define a Grpc ProtoDefinition which is used for this mapping (request and response).
/// </summary>
/// <param name="protoDefinition">The proto definitions as id or text.</param>
/// <returns>The <see cref="IMapping"/>.</returns>
IMapping WithProtoDefinition(IdOrTexts protoDefinition);
}

View File

@@ -1,126 +0,0 @@
// Copyright © WireMock.Net
#if PROTOBUF
using System;
using System.Threading;
using System.Threading.Tasks;
using ProtoBufJsonConverter;
using ProtoBufJsonConverter.Models;
using Stef.Validation;
using WireMock.Models;
using WireMock.Util;
namespace WireMock.Matchers;
/// <summary>
/// Grpc ProtoBuf Matcher
/// </summary>
/// <inheritdoc cref="IProtoBufMatcher"/>
public class ProtoBufMatcher : IProtoBufMatcher
{
/// <inheritdoc />
public string Name => nameof(ProtoBufMatcher);
/// <inheritdoc />
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// The Func to define the proto definition as id or texts.
/// </summary>
public Func<IdOrTexts> ProtoDefinition { get; internal set; }
/// <summary>
/// The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".
/// </summary>
public string MessageType { get; }
/// <summary>
/// The Matcher to use (optional).
/// </summary>
public IObjectMatcher? Matcher { get; }
private static readonly Converter ProtoBufToJsonConverter = SingletonFactory<Converter>.GetInstance();
/// <summary>
/// Initializes a new instance of the <see cref="ProtoBufMatcher"/> class.
/// </summary>
/// <param name="protoDefinition">The proto definition as id or text.</param>
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <param name="matcher">The optional jsonMatcher to use to match the ProtoBuf as (json) object.</param>
public ProtoBufMatcher(
Func<IdOrTexts> protoDefinition,
string messageType,
MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch,
IObjectMatcher? matcher = null
)
{
ProtoDefinition = Guard.NotNull(protoDefinition);
MessageType = Guard.NotNullOrWhiteSpace(messageType);
Matcher = matcher;
MatchBehaviour = matchBehaviour;
}
/// <inheritdoc />
public async Task<MatchResult> IsMatchAsync(byte[]? input, CancellationToken cancellationToken = default)
{
var result = new MatchResult();
if (input != null)
{
try
{
var instance = await DecodeAsync(input, true, cancellationToken).ConfigureAwait(false);
result = Matcher?.IsMatch(instance) ?? new MatchResult(MatchScores.Perfect);
}
catch (Exception e)
{
result = new MatchResult(MatchScores.Mismatch, e);
}
}
return MatchBehaviourHelper.Convert(MatchBehaviour, result);
}
/// <inheritdoc />
public Task<object?> DecodeAsync(byte[]? input, CancellationToken cancellationToken = default)
{
return DecodeAsync(input, false, cancellationToken);
}
/// <inheritdoc />
public string GetCSharpCodeArguments()
{
return "NotImplemented";
}
private async Task<object?> DecodeAsync(byte[]? input, bool throwException, CancellationToken cancellationToken)
{
if (input == null)
{
return null;
}
var protoDefinitions = ProtoDefinition().Texts;
var resolver = new WireMockProtoFileResolver(protoDefinitions);
var request = new ConvertToObjectRequest(protoDefinitions[0], MessageType, input)
.WithProtoFileResolver(resolver);
try
{
return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
}
catch
{
if (throwException)
{
throw;
}
return null;
}
}
}
#endif

View File

@@ -1,45 +0,0 @@
// Copyright © WireMock.Net
using System;
using WireMock.Models;
namespace WireMock.Matchers.Request;
/// <summary>
/// The request body Grpc ProtoBuf matcher.
/// </summary>
public class RequestMessageProtoBufMatcher : IRequestMatcher
{
/// <summary>
/// The ProtoBufMatcher.
/// </summary>
public IProtoBufMatcher? Matcher { get; }
/// <summary>
/// Initializes a new instance of the <see cref="RequestMessageProtoBufMatcher"/> class.
/// </summary>
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <param name="protoDefinition">The Func to define the proto definitions as id or text.</param>
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
/// <param name="matcher">The optional matcher to use to match the ProtoBuf as (json) object.</param>
public RequestMessageProtoBufMatcher(MatchBehaviour matchBehaviour, Func<IdOrTexts> protoDefinition, string messageType, IObjectMatcher? matcher = null)
{
#if PROTOBUF
Matcher = new ProtoBufMatcher(protoDefinition, messageType, matchBehaviour, matcher);
#else
throw new System.NotSupportedException("The ProtoBufMatcher can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
#endif
}
/// <inheritdoc />
public double GetMatchingScore(IRequestMessage requestMessage, IRequestMatchResult requestMatchResult)
{
var (score, exception) = GetMatchResult(requestMessage).Expand();
return requestMatchResult.AddScore(GetType(), score, exception);
}
private MatchResult GetMatchResult(IRequestMessage requestMessage)
{
return Matcher?.IsMatchAsync(requestMessage.BodyAsBytes).GetAwaiter().GetResult() ?? default;
}
}

View File

@@ -1,62 +0,0 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using AnyOfTypes;
using Newtonsoft.Json;
using WireMock.Models.GraphQL;
namespace WireMock.Models;
/// <summary>
/// GraphQLSchemaDetails
/// </summary>
public class GraphQLSchemaDetails
{
/// <summary>
/// The GraphQL schema as a string.
/// </summary>
public string? SchemaAsString { get; set; }
/// <summary>
/// The GraphQL schema as a StringPattern.
/// </summary>
public StringPattern? SchemaAsStringPattern { get; set; }
/// <summary>
/// The GraphQL schema as a <seealso cref="ISchemaData"/>.
/// </summary>
public ISchemaData? SchemaAsISchemaData { get; set; }
/// <summary>
/// The GraphQL Schema.
/// </summary>
[JsonIgnore]
public AnyOf<string, StringPattern, ISchemaData>? Schema
{
get
{
if (SchemaAsString != null)
{
return SchemaAsString;
}
if (SchemaAsStringPattern != null)
{
return SchemaAsStringPattern;
}
if (SchemaAsISchemaData != null)
{
return new AnyOf<string, StringPattern, ISchemaData>(SchemaAsISchemaData);
}
return null;
}
}
/// <summary>
/// The custom Scalars to define for this schema.
/// </summary>
public IDictionary<string, Type>? CustomScalars { get; set; }
}

View File

@@ -1,39 +0,0 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.Linq;
using Stef.Validation;
namespace WireMock.Models;
/// <summary>
/// A placeholder class for Proto Definitions.
/// </summary>
public class ProtoDefinitionData
{
private readonly IDictionary<string, string> _filenameMappedToProtoDefinition;
internal ProtoDefinitionData(IDictionary<string, string> filenameMappedToProtoDefinition)
{
_filenameMappedToProtoDefinition = filenameMappedToProtoDefinition;
}
/// <summary>
/// Get all the ProtoDefinitions.
/// Note: the main ProtoDefinition will be the first one in the list.
/// </summary>
/// <param name="mainProtoFilename">The main ProtoDefinition filename.</param>
public IReadOnlyList<string> ToList(string mainProtoFilename)
{
Guard.NotNullOrEmpty(mainProtoFilename);
if (!_filenameMappedToProtoDefinition.TryGetValue(mainProtoFilename, out var mainProtoDefinition))
{
throw new KeyNotFoundException($"The ProtoDefinition with filename '{mainProtoFilename}' was not found.");
}
var list = new List<string> { mainProtoDefinition };
list.AddRange(_filenameMappedToProtoDefinition.Where(kvp => kvp.Key != mainProtoFilename).Select(kvp => kvp.Value));
return list;
}
}

View File

@@ -177,11 +177,10 @@ namespace WireMock.Owin.Mappers
var jsonBody = JsonConvert.SerializeObject(bodyData.BodyAsJson, new JsonSerializerSettings { Formatting = formatting, NullValueHandling = NullValueHandling.Ignore });
return (bodyData.Encoding ?? _utf8NoBom).GetBytes(jsonBody);
#if PROTOBUF
case BodyType.ProtoBuf:
var protoDefinitions = bodyData.ProtoDefinition?.Invoke().Texts;
return await ProtoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false);
#endif
var protoBufUtils = TypeLoader.LoadStaticInstance<IProtoBufUtils>();
return await protoBufUtils.GetProtoBufMessageWithHeaderAsync(protoDefinitions, bodyData.ProtoBufMessageType, bodyData.BodyAsJson).ConfigureAwait(false);
case BodyType.Bytes:
return bodyData.BodyAsBytes;

View File

@@ -1,61 +0,0 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Models;
namespace WireMock.RequestBuilders;
public partial class Request
{
/// <inheritdoc />
public IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return WithBodyAsProtoBuf([protoDefinition], messageType, matchBehaviour);
}
/// <inheritdoc />
public IRequestBuilder WithBodyAsProtoBuf(string protoDefinition, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return WithBodyAsProtoBuf([protoDefinition], messageType, matcher, matchBehaviour);
}
/// <inheritdoc />
public IRequestBuilder WithBodyAsProtoBuf(IReadOnlyList<string> protoDefinitions, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType));
}
/// <inheritdoc />
public IRequestBuilder WithBodyAsProtoBuf(IReadOnlyList<string> protoDefinitions, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType, matcher));
}
/// <inheritdoc />
public IRequestBuilder WithBodyAsProtoBuf(string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(), messageType));
}
/// <inheritdoc />
public IRequestBuilder WithBodyAsProtoBuf(string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(), messageType, matcher));
}
private Func<IdOrTexts> ProtoDefinitionFunc()
{
return () =>
{
if (Mapping.ProtoDefinition == null)
{
throw new InvalidOperationException($"No ProtoDefinition defined on mapping '{Mapping.Guid}'. Please use the WireMockServerSettings to define ProtoDefinitions.");
}
return Mapping.ProtoDefinition.Value;
};
}
}

View File

@@ -20,9 +20,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
{
private readonly IList<IRequestMatcher> _requestMatchers;
/// <summary>
/// The link back to the Mapping.
/// </summary>
/// <inheritdoc />
public IMapping Mapping { get; set; } = null!;
/// <summary>
@@ -73,6 +71,7 @@ public partial class Request : RequestMessageCompositeMatcher, IRequestBuilder
return _requestMatchers.OfType<T>().FirstOrDefault(func);
}
/// <inheritdoc />
public IRequestBuilder Add<T>(T requestMatcher) where T : IRequestMatcher
{
foreach (var existing in _requestMatchers.OfType<T>().ToArray())

View File

@@ -1,29 +0,0 @@
// Copyright © WireMock.Net
namespace WireMock.ResponseBuilders;
/// <summary>
/// Defines the BodyDestinationFormat
/// </summary>
public static class BodyDestinationFormat
{
/// <summary>
/// Same as source (no conversion)
/// </summary>
public const string SameAsSource = "SameAsSource";
/// <summary>
/// Convert to string
/// </summary>
public const string String = "String";
/// <summary>
/// Convert to bytes
/// </summary>
public const string Bytes = "Bytes";
/// <summary>
/// Convert to Json object
/// </summary>
public const string Json = "Json";
}

View File

@@ -1,169 +0,0 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using JsonConverter.Abstractions;
using WireMock.Models;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The BodyResponseBuilder interface.
/// </summary>
public interface IBodyResponseBuilder : IFaultResponseBuilder
{
/// <summary>
/// WithBody : Create a ... response based on a string.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody(string body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
/// <summary>
/// WithBody : Create a ... response based on a callback function.
/// </summary>
/// <param name="bodyFactory">The delegate to build the body.</param>
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody(Func<IRequestMessage, string> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
/// <summary>
/// WithBody : Create a ... response based on an async callback function.
/// </summary>
/// <param name="bodyFactory">The async delegate to build the body.</param>
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody(Func<IRequestMessage, Task<string>> bodyFactory, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
/// <summary>
/// WithBody : Create a ... response based on an async callback function.
/// </summary>
/// <param name="bodyFactory">The async delegate to build the body.</param>
/// <param name="timeout">The timeout to wait on new items in the queue. Default value is <c>1</c> hour.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithSseBody(Func<IRequestMessage, IBlockingQueue<string?>, Task> bodyFactory, TimeSpan? timeout = null);
/// <summary>
/// WithBody : Create a ... response based on a bytearray.
/// </summary>
/// <param name="body">The body.</param>
/// <param name="destination">The Body Destination format (SameAsSource, String or Bytes).</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody(byte[] body, string? destination = BodyDestinationFormat.SameAsSource, Encoding? encoding = null);
/// <summary>
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
/// </summary>
/// <param name="body">The body.</param>
/// <param name="encoding">The body encoding.</param>
/// <param name="indented">Define whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsJson(object body, Encoding? encoding = null, bool? indented = null);
/// <summary>
/// WithBody : Create a string response based on a object (which will be converted to a JSON string).
/// </summary>
/// <param name="body">The body.</param>
/// <param name="indented">Define whether child objects to be indented according to the Newtonsoft.Json.JsonTextWriter.Indentation and Newtonsoft.Json.JsonTextWriter.IndentChar settings.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsJson(object body, bool indented);
/// <summary>
/// WithBodyAsJson : Create a ... response based on a callback function.
/// </summary>
/// <param name="bodyFactory">The delegate to build the body.</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsJson(Func<IRequestMessage, object> bodyFactory, Encoding? encoding = null);
/// <summary>
/// WithBodyAsJson : Create a ... response based on a async callback function.
/// </summary>
/// <param name="bodyFactory">The async delegate to build the body.</param>
/// <param name="encoding">The body encoding.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsJson(Func<IRequestMessage, Task<object>> bodyFactory, Encoding? encoding = null);
/// <summary>
/// WithBodyFromFile : Create a ... response based on a File.
/// </summary>
/// <param name="filename">The filename.</param>
/// <param name="cache">Defines if this file is cached in memory or retrieved from disk every time the response is created.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyFromFile(string filename, bool cache = true);
/// <summary>
/// WithBody : Create a string response based on a object (which will be converted to a JSON string using the <see cref="IJsonConverter"/>).
/// </summary>
/// <param name="body">The body.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/>.</param>
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody(object body, IJsonConverter jsonConverter, JsonConverterOptions? options = null);
/// <summary>
/// WithBody : Create a string response based on a object (which will be converted to a JSON string using the <see cref="IJsonConverter"/>).
/// </summary>
/// <param name="body">The body.</param>
/// <param name="encoding">The body encoding, can be <c>null</c>.</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/>.</param>
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter jsonConverter, JsonConverterOptions? options = null);
/// <summary>
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
/// </summary>
/// <param name="protoDefinition">The proto definition as text.</param>
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
/// <param name="value">The object to convert to protobuf byte[].</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsProtoBuf(
string protoDefinition,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
);
/// <summary>
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on proto definitions, message type and the value.
/// </summary>
/// <param name="protoDefinitions">The proto definition as text.</param>
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
/// <param name="value">The object to convert to protobuf byte[].</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsProtoBuf(
IReadOnlyList<string> protoDefinitions,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
);
/// <summary>
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
/// </summary>
/// <param name="messageType">The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".</param>
/// <param name="value">The object to convert to protobuf byte[].</param>
/// <param name="jsonConverter">The <see cref="IJsonConverter"/> [optional]. Default value is NewtonsoftJsonConverter.</param>
/// <param name="options">The <see cref="JsonConverterOptions"/> [optional].</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithBodyAsProtoBuf(
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
);
}

View File

@@ -1,28 +0,0 @@
// Copyright © WireMock.Net
using System;
using System.Threading.Tasks;
using JetBrains.Annotations;
using WireMock.ResponseProviders;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The CallbackResponseBuilder interface.
/// </summary>
public interface ICallbackResponseBuilder : IResponseProvider
{
/// <summary>
/// The callback builder
/// </summary>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
[PublicAPI]
IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler);
/// <summary>
/// The async callback builder
/// </summary>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
[PublicAPI]
IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler);
}

View File

@@ -1,33 +0,0 @@
// Copyright © WireMock.Net
using System;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The DelayResponseBuilder interface.
/// </summary>
public interface IDelayResponseBuilder : ICallbackResponseBuilder
{
/// <summary>
/// The delay defined as a <see cref="TimeSpan"/>.
/// </summary>
/// <param name="delay">The TimeSpan to delay.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithDelay(TimeSpan delay);
/// <summary>
/// The delay defined as milliseconds.
/// </summary>
/// <param name="milliseconds">The milliseconds to delay.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithDelay(int milliseconds);
/// <summary>
/// Introduce random delay
/// </summary>
/// <param name="minimumMilliseconds">Minimum milliseconds to delay</param>
/// <param name="maximumMilliseconds">Maximum milliseconds to delay</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithRandomDelay(int minimumMilliseconds = 0, int maximumMilliseconds = 60_000);
}

View File

@@ -1,19 +0,0 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The FaultRequestBuilder interface.
/// </summary>
public interface IFaultResponseBuilder : ITransformResponseBuilder
{
/// <summary>
/// WithBody : Create a fault response.
/// </summary>
/// <param name="faultType">The FaultType.</param>
/// <param name="percentage">The percentage when this fault should occur. When null, it's always a fault.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithFault(FaultType faultType, double? percentage = null);
}

View File

@@ -1,70 +0,0 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using WireMock.Types;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The HeadersResponseBuilder interface.
/// </summary>
public interface IHeadersResponseBuilder : IBodyResponseBuilder
{
/// <summary>
/// The WithHeader.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="values">The values.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeader(string name, params string[] values);
/// <summary>
/// The WithHeaders.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeaders(IDictionary<string, string> headers);
/// <summary>
/// The WithHeaders.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeaders(IDictionary<string, string[]> headers);
/// <summary>
/// The WithHeaders.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers);
/// <summary>
/// The WithTrailingHeader.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="values">The values.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithTrailingHeader(string name, params string[] values);
/// <summary>
/// The WithTrailingHeaders.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithTrailingHeaders(IDictionary<string, string> headers);
/// <summary>
/// The WithTrailingHeaders.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithTrailingHeaders(IDictionary<string, string[]> headers);
/// <summary>
/// The WithTrailingHeaders.
/// </summary>
/// <param name="headers">The headers.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithTrailingHeaders(IDictionary<string, WireMockList<string>> headers);
}

View File

@@ -1,35 +0,0 @@
// Copyright © WireMock.Net
using System.Security.Cryptography.X509Certificates;
using WireMock.Settings;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The ProxyResponseBuilder interface.
/// </summary>
public interface IProxyResponseBuilder : IStatusCodeResponseBuilder
{
/// <summary>
/// WithProxy URL using Client X509Certificate2.
/// </summary>
/// <param name="proxyUrl">The proxy url.</param>
/// <param name="clientX509Certificate2ThumbprintOrSubjectName">The X509Certificate2 file to use for client authentication.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithProxy(string proxyUrl, string? clientX509Certificate2ThumbprintOrSubjectName = null);
/// <summary>
/// WithProxy using <see cref="ProxyAndRecordSettings"/>.
/// </summary>
/// <param name="settings">The ProxyAndRecordSettings.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithProxy(ProxyAndRecordSettings settings);
/// <summary>
/// WithProxy using <see cref="X509Certificate2"/>.
/// </summary>
/// <param name="proxyUrl">The proxy url.</param>
/// <param name="certificate">The X509Certificate2.</param>
/// <returns>A <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithProxy(string proxyUrl, X509Certificate2 certificate);
}

View File

@@ -1,10 +0,0 @@
// Copyright © WireMock.Net
namespace WireMock.ResponseBuilders;
/// <summary>
/// The ResponseBuilder interface.
/// </summary>
public interface IResponseBuilder : IProxyResponseBuilder
{
}

View File

@@ -1,48 +0,0 @@
// Copyright © WireMock.Net
using System.Net;
using WireMock.Settings;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The StatusCodeResponseBuilder interface.
/// </summary>
public interface IStatusCodeResponseBuilder : IHeadersResponseBuilder
{
/// <summary>
/// The with status code.
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
/// </summary>
/// <param name="code">The code.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithStatusCode(int code);
/// <summary>
/// The with status code.
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
/// </summary>
/// <param name="code">The code.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithStatusCode(string code);
/// <summary>
/// The with status code.
/// By default all status codes are allowed, to change this behaviour, see <inheritdoc cref="WireMockServerSettings.AllowOnlyDefinedHttpStatusCodeInResponse"/>.
/// </summary>
/// <param name="code">The code.</param>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithStatusCode(HttpStatusCode code);
/// <summary>
/// The with Success status code (200).
/// </summary>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithSuccess();
/// <summary>
/// The with NotFound status code (404).
/// </summary>
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
IResponseBuilder WithNotFound();
}

View File

@@ -1,35 +0,0 @@
// Copyright © WireMock.Net
using WireMock.Types;
namespace WireMock.ResponseBuilders;
/// <summary>
/// The TransformResponseBuilder interface.
/// </summary>
public interface ITransformResponseBuilder : IDelayResponseBuilder
{
/// <summary>
/// Use the Handlebars.Net ResponseMessage transformer.
/// </summary>
/// <returns>
/// The <see cref="IResponseBuilder"/>.
/// </returns>
IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile);
/// <summary>
/// Use the Handlebars.Net ResponseMessage transformer.
/// </summary>
/// <returns>
/// The <see cref="IResponseBuilder"/>.
/// </returns>
IResponseBuilder WithTransformer(ReplaceNodeOptions options);
/// <summary>
/// Use a specific ResponseMessage transformer.
/// </summary>
/// <returns>
/// The <see cref="IResponseBuilder"/>.
/// </returns>
IResponseBuilder WithTransformer(TransformerType transformerType = TransformerType.Handlebars, bool transformContentFromBodyAsFile = false, ReplaceNodeOptions options = ReplaceNodeOptions.EvaluateAndTryToConvert);
}

View File

@@ -1,12 +1,10 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using JsonConverter.Abstractions;
using Stef.Validation;
using WireMock.Exceptions;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
@@ -235,72 +233,4 @@ public partial class Response
return this;
}
/// <inheritdoc />
public IResponseBuilder WithBodyAsProtoBuf(
string protoDefinition,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
)
{
return WithBodyAsProtoBuf([protoDefinition], messageType, value, jsonConverter, options);
}
/// <inheritdoc />
public IResponseBuilder WithBodyAsProtoBuf(
IReadOnlyList<string> protoDefinitions,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
)
{
Guard.NotNullOrEmpty(protoDefinitions);
Guard.NotNullOrWhiteSpace(messageType);
Guard.NotNull(value);
#if !PROTOBUF
throw new NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
#else
ResponseMessage.BodyDestination = null;
ResponseMessage.BodyData = new BodyData
{
DetectedBodyType = BodyType.ProtoBuf,
BodyAsJson = value,
ProtoDefinition = () => new IdOrTexts(null, protoDefinitions),
ProtoBufMessageType = messageType
};
return this;
#endif
}
/// <inheritdoc />
public IResponseBuilder WithBodyAsProtoBuf(
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
)
{
Guard.NotNullOrWhiteSpace(messageType);
Guard.NotNull(value);
#if !PROTOBUF
throw new NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
#else
ResponseMessage.BodyDestination = null;
ResponseMessage.BodyData = new BodyData
{
DetectedBodyType = BodyType.ProtoBuf,
BodyAsJson = value,
ProtoDefinition = () => Mapping.ProtoDefinition ?? throw new WireMockException("ProtoDefinition cannot be resolved. You probably forgot to call .WithProtoDefinition(...) on the mapping."),
ProtoBufMessageType = messageType
};
return this;
#endif
}
}

View File

@@ -13,13 +13,13 @@ public partial class Response
/// A delegate to execute to generate the response.
/// </summary>
[MemberNotNullWhen(true, nameof(WithCallbackUsed))]
public Func<IRequestMessage, ResponseMessage>? Callback { get; private set; }
public Func<IRequestMessage, IResponseMessage>? Callback { get; private set; }
/// <summary>
/// A delegate to execute to generate the response async.
/// </summary>
[MemberNotNullWhen(true, nameof(WithCallbackUsed))]
public Func<IRequestMessage, Task<ResponseMessage>>? CallbackAsync { get; private set; }
public Func<IRequestMessage, Task<IResponseMessage>>? CallbackAsync { get; private set; }
/// <summary>
/// Defines if the method WithCallback(...) is used.
@@ -27,7 +27,7 @@ public partial class Response
public bool WithCallbackUsed { get; private set; }
/// <inheritdoc />
public IResponseBuilder WithCallback(Func<IRequestMessage, ResponseMessage> callbackHandler)
public IResponseBuilder WithCallback(Func<IRequestMessage, IResponseMessage> callbackHandler)
{
Guard.NotNull(callbackHandler);
@@ -35,14 +35,14 @@ public partial class Response
}
/// <inheritdoc />
public IResponseBuilder WithCallback(Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
public IResponseBuilder WithCallback(Func<IRequestMessage, Task<IResponseMessage>> callbackHandler)
{
Guard.NotNull(callbackHandler);
return WithCallbackInternal(true, callbackHandler);
}
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, ResponseMessage> callbackHandler)
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, IResponseMessage> callbackHandler)
{
Guard.NotNull(callbackHandler);
@@ -52,7 +52,7 @@ public partial class Response
return this;
}
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, Task<ResponseMessage>> callbackHandler)
private IResponseBuilder WithCallbackInternal(bool withCallbackUsed, Func<IRequestMessage, Task<IResponseMessage>> callbackHandler)
{
Guard.NotNull(callbackHandler);

View File

@@ -28,9 +28,7 @@ public partial class Response : IResponseBuilder
private TimeSpan? _delay;
/// <summary>
/// The link back to the mapping.
/// </summary>
/// <inheritdoc />
public IMapping Mapping { get; set; } = null!;
/// <summary>
@@ -81,10 +79,8 @@ public partial class Response : IResponseBuilder
/// </summary>
public ReplaceNodeOptions TransformerReplaceNodeOptions { get; private set; }
/// <summary>
/// Gets the response message.
/// </summary>
public ResponseMessage ResponseMessage { get; }
/// <inheritdoc />
public IResponseMessage ResponseMessage { get; }
/// <summary>
/// Creates this instance.
@@ -226,7 +222,7 @@ public partial class Response : IResponseBuilder
).ConfigureAwait(false);
}
ResponseMessage responseMessage;
IResponseMessage responseMessage;
if (!WithCallbackUsed)
{
responseMessage = ResponseMessage;

View File

@@ -1,23 +0,0 @@
// Copyright © WireMock.Net and mock4net by Alexandre Victoor
// This source file is based on mock4net by Alexandre Victoor which is licensed under the Apache 2.0 License.
// For more details see 'mock4net/LICENSE.txt' and 'mock4net/readme.md' in this project root.
using System.Threading.Tasks;
using WireMock.Settings;
namespace WireMock.ResponseProviders;
/// <summary>
/// The Response Provider interface.
/// </summary>
public interface IResponseProvider
{
/// <summary>
/// The provide response.
/// </summary>
/// <param name="mapping">The used mapping.</param>
/// <param name="requestMessage">The request.</param>
/// <param name="settings">The WireMockServerSettings.</param>
/// <returns>The <see cref="ResponseMessage"/> including a new (optional) <see cref="IMapping"/>.</returns>
Task<(IResponseMessage Message, IMapping? Mapping)> ProvideResponseAsync(IMapping mapping, IRequestMessage requestMessage, WireMockServerSettings settings);
}

View File

@@ -126,12 +126,10 @@ internal class MappingConverter(MatcherMapper mapper)
}
}
#if PROTOBUF
if (requestMessageProtoBufMatcher?.Matcher != null)
{
sb.AppendLine(" // .WithBodyAsProtoBuf() is not yet supported");
}
#endif
if (requestMessageBodyMatcher?.Matchers != null)
{
@@ -188,7 +186,7 @@ internal class MappingConverter(MatcherMapper mapper)
sb.AppendLine($" .WithStatusCode({(int)httpStatusCode})");
}
if (response.ResponseMessage.Headers is { })
if (response.ResponseMessage.Headers != null)
{
foreach (var header in response.ResponseMessage.Headers)
{
@@ -196,7 +194,7 @@ internal class MappingConverter(MatcherMapper mapper)
}
}
if (response.ResponseMessage.TrailingHeaders is { })
if (response.ResponseMessage.TrailingHeaders != null)
{
foreach (var header in response.ResponseMessage.TrailingHeaders)
{
@@ -400,13 +398,11 @@ internal class MappingConverter(MatcherMapper mapper)
{
void AfterMap(MatcherModel matcherModel)
{
#if PROTOBUF
// In case the ProtoDefinition is defined at the Mapping level, clear the Pattern at the Matcher level
if (bodyMatchers?.OfType<ProtoBufMatcher>().Any() == true && mappingModel.ProtoDefinition != null)
if (bodyMatchers?.OfType<IProtoBufMatcher>().Any() == true && mappingModel.ProtoDefinition != null)
{
matcherModel.Pattern = null;
}
#endif
}
mappingModel.Request.Body = new BodyModel();

View File

@@ -80,10 +80,9 @@ internal class MatcherMapper
case "MimePartMatcher":
return CreateMimePartMatcher(matchBehaviour, matcherModel);
#if PROTOBUF
case nameof(ProtoBufMatcher):
case "ProtoBufMatcher":
return CreateProtoBufMatcher(matchBehaviour, stringPatterns.GetPatterns(), matcherModel);
#endif
case nameof(RegexMatcher):
return new RegexMatcher(matchBehaviour, stringPatterns, ignoreCase, useRegexExtended, matchOperator);
@@ -210,7 +209,7 @@ internal class MatcherMapper
break;
#if PROTOBUF
case ProtoBufMatcher protoBufMatcher:
case IProtoBufMatcher protoBufMatcher:
protoBufMatcher.ProtoDefinition().Value(id => model.Pattern = id, texts =>
{
if (texts.Count == 1)
@@ -286,17 +285,15 @@ internal class MatcherMapper
return TypeLoader.LoadNewInstance<IMimePartMatcher>(matchBehaviour, contentTypeMatcher, contentDispositionMatcher, contentTransferEncodingMatcher, contentMatcher);
}
#if PROTOBUF
private ProtoBufMatcher CreateProtoBufMatcher(MatchBehaviour? matchBehaviour, IReadOnlyList<string> protoDefinitions, MatcherModel matcher)
private IProtoBufMatcher CreateProtoBufMatcher(MatchBehaviour? matchBehaviour, IReadOnlyList<string> protoDefinitions, MatcherModel matcher)
{
var objectMatcher = Map(matcher.ContentMatcher) as IObjectMatcher;
return new ProtoBufMatcher(
() => ProtoDefinitionHelper.GetIdOrTexts(_settings, protoDefinitions.ToArray()),
return TypeLoader.LoadNewInstance<IProtoBufMatcher>(
() => ProtoDefinitionUtils.GetIdOrTexts(_settings, protoDefinitions.ToArray()),
matcher.ProtoBufMessageType!,
matchBehaviour ?? MatchBehaviour.AcceptOnMatch,
objectMatcher
);
}
#endif
}

View File

@@ -356,12 +356,9 @@ internal class RespondWithAProvider : IRespondWithAProvider
{
Guard.NotNull(protoDefinitionOrId);
#if PROTOBUF
ProtoDefinition = ProtoDefinitionHelper.GetIdOrTexts(_settings, protoDefinitionOrId);
ProtoDefinition = ProtoDefinitionUtils.GetIdOrTexts(_settings, protoDefinitionOrId);
return this;
#else
throw new NotSupportedException("The WithProtoDefinition method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
#endif
}
/// <inheritdoc />

View File

@@ -241,13 +241,13 @@ public partial class WireMockServer
if (requestModel.Body?.Matcher != null)
{
var bodyMatcher = _matcherMapper.Map(requestModel.Body.Matcher)!;
#if PROTOBUF
// If the BodyMatcher is a ProtoBufMatcher, and if ProtoDefinition is defined on Mapping-level, set the ProtoDefinition from that Mapping.
if (bodyMatcher is ProtoBufMatcher protoBufMatcher && mappingModel?.ProtoDefinition != null)
if (bodyMatcher is IProtoBufMatcher protoBufMatcher && mappingModel?.ProtoDefinition != null)
{
protoBufMatcher.ProtoDefinition = () => ProtoDefinitionHelper.GetIdOrTexts(_settings, mappingModel.ProtoDefinition);
protoBufMatcher.ProtoDefinition = () => ProtoDefinitionUtils.GetIdOrTexts(_settings, mappingModel.ProtoDefinition);
}
#endif
requestBuilder = requestBuilder.WithBody(bodyMatcher);
}
else if (requestModel.Body?.Matchers != null)
@@ -368,18 +368,19 @@ public partial class WireMockServer
{
if (responseModel.ProtoBufMessageType != null)
{
var protoBufUtils = TypeLoader.LoadStaticInstance<IProtoBufUtils>();
if (responseModel.ProtoDefinition != null)
{
responseBuilder = responseBuilder.WithBodyAsProtoBuf(responseModel.ProtoDefinition, responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson, responseModel.ProtoDefinition);
}
else if (responseModel.ProtoDefinitions != null)
{
responseBuilder = responseBuilder.WithBodyAsProtoBuf(responseModel.ProtoDefinitions, responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson, responseModel.ProtoDefinitions);
}
else
{
// ProtoDefinition(s) is/are defined at Mapping/Server level
responseBuilder = responseBuilder.WithBodyAsProtoBuf(responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
responseBuilder = protoBufUtils.UpdateResponseBuilder(responseBuilder, responseModel.ProtoBufMessageType, responseModel.BodyAsJson);
}
}
else

View File

@@ -1,50 +0,0 @@
// Copyright © WireMock.Net
using HandlebarsDotNet.Helpers.Enums;
using JetBrains.Annotations;
using WireMock.Types;
namespace WireMock.Settings;
/// <summary>
/// HandlebarsSettings
/// </summary>
[PublicAPI]
public class HandlebarsSettings
{
internal static readonly Category[] DefaultAllowedHandlebarsHelpers =
[
Category.Boolean,
Category.Constants,
Category.DateTime,
Category.Enumerable,
Category.Humanizer,
Category.JsonPath,
Category.Math,
Category.Object,
Category.Random,
Category.Regex,
Category.String,
Category.Url,
Category.Xeger,
Category.XPath,
Category.Xslt
];
/// <summary>
/// Defines the allowed custom HandlebarsHelpers which can be used. Possible values are:
/// - <see cref="CustomHandlebarsHelpers.None"/> (Default)
/// - <see cref="CustomHandlebarsHelpers.File"/>
/// - <see cref="CustomHandlebarsHelpers.All"/>
/// </summary>
[PublicAPI]
public CustomHandlebarsHelpers AllowedCustomHandlebarsHelpers { get; set; } = CustomHandlebarsHelpers.None;
/// <summary>
/// Defines the allowed HandlebarHelpers which can be used.
///
/// By default, all categories except <see cref="Category.DynamicLinq"/> and <see cref="Category.Environment"/> are registered.
/// </summary>
[PublicAPI]
public Category[] AllowedHandlebarsHelpers { get; set; } = DefaultAllowedHandlebarsHelpers;
}

View File

@@ -1,32 +0,0 @@
// Copyright © WireMock.Net
using System.Security.Cryptography.X509Certificates;
namespace WireMock.Settings;
/// <summary>
/// HttpClientSettings
/// </summary>
public abstract class HttpClientSettings
{
/// <summary>
/// The clientCertificate thumbprint or subject name fragment to use.
/// Example thumbprint : "D2DBF135A8D06ACCD0E1FAD9BFB28678DF7A9818". Example subject name: "www.google.com""
/// </summary>
public string? ClientX509Certificate2ThumbprintOrSubjectName { get; set; }
/// <summary>
/// Defines the WebProxySettings.
/// </summary>
public WebProxySettings? WebProxySettings { get; set; }
/// <summary>
/// Proxy requests should follow redirection (30x).
/// </summary>
public bool? AllowAutoRedirect { get; set; }
/// <summary>
/// The <see cref="X509Certificate2"/> to use.
/// </summary>
public X509Certificate2? Certificate { get; set; }
}

View File

@@ -1,115 +0,0 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// ProxyAndRecordSettings
/// </summary>
public class ProxyAndRecordSettings : HttpClientSettings
{
/// <summary>
/// Default prefix value for saved mapping file
/// </summary>
public const string DefaultPrefixForSavedMappingFile = "Proxy Mapping for ";
/// <summary>
/// The URL to proxy.
/// </summary>
[PublicAPI]
public string Url { get; set; } = null!;
/// <summary>
/// Save the mapping for each request/response to the internal Mappings.
/// </summary>
[PublicAPI]
public bool SaveMapping { get; set; }
/// <summary>
/// Save the mapping for each request/response also to a file. (Note that SaveMapping must also be set to true.)
/// </summary>
[PublicAPI]
public bool SaveMappingToFile { get; set; }
/// <summary>
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
///
/// Deprecated : use SaveMappingSettings.
/// </summary>
[PublicAPI]
public string SaveMappingForStatusCodePattern
{
set
{
if (SaveMappingSettings is null)
{
SaveMappingSettings = new ProxySaveMappingSettings();
}
SaveMappingSettings.StatusCodePattern = value;
}
}
/// <summary>
/// Additional SaveMappingSettings.
/// </summary>
[PublicAPI]
public ProxySaveMappingSettings? SaveMappingSettings { get; set; }
/// <summary>
/// Defines a list from headers which will be excluded from the saved mappings.
/// </summary>
[PublicAPI]
public string[]? ExcludedHeaders { get; set; }
/// <summary>
/// Defines a list of params which will be excluded from the saved mappings.
/// </summary>
[PublicAPI]
public string[]? ExcludedParams { get; set; }
/// <summary>
/// Defines a list of cookies which will be excluded from the saved mappings.
/// </summary>
[PublicAPI]
public string[]? ExcludedCookies { get; set; }
/// <summary>
/// Replace Settings
/// </summary>
[PublicAPI]
public ProxyUrlReplaceSettings? ReplaceSettings { get; set; }
/// <summary>
/// Prefer the Proxy Mapping over the saved Mapping (in case SaveMapping is set to <c>true</c>).
/// </summary>
//[PublicAPI]
//public bool PreferProxyMapping { get; set; }
/// <summary>
/// When SaveMapping is set to <c>true</c>, this setting can be used to control the behavior of the generated request matchers for the new mapping.
/// - <c>false</c>, the default matchers will be used.
/// - <c>true</c>, the defined mappings in the request wil be used for the new mapping.
///
/// Default value is false.
/// </summary>
public bool UseDefinedRequestMatchers { get; set; }
/// <summary>
/// Append a unique GUID to the filename from the saved mapping file.
/// </summary>
public bool AppendGuidToSavedMappingFile { get; set; }
/// <summary>
/// Set prefix for saved mapping file.
/// </summary>
public string PrefixForSavedMappingFile { get; set; } = DefaultPrefixForSavedMappingFile;
/// <summary>
/// Proxy all Api calls, irrespective of any condition
/// </summary>
[PublicAPI]
public bool ProxyAll { get; set; }
}

View File

@@ -1,50 +0,0 @@
// Copyright © WireMock.Net
using System;
using Stef.Validation;
using WireMock.Matchers;
namespace WireMock.Settings;
/// <summary>
/// Represents settings for saving a proxy mapping with a non-nullable value and a specific match behaviour.
/// </summary>
/// <typeparam name="T">The non-nullable type of the value associated with the proxy save mapping setting.</typeparam>
public class ProxySaveMappingSetting<T> where T : notnull
{
/// <summary>
/// Gets the match behaviour for the proxy save mapping setting.
/// </summary>
/// <value>The match behaviour which determines how matches are evaluated.</value>
public MatchBehaviour MatchBehaviour { get; }
/// <summary>
/// Gets the non-nullable value associated with the proxy save mapping setting.
/// </summary>
/// <value>The value of type <typeparamref name="T"/>.</value>
public T Value { get; }
/// <summary>
/// Initializes a new instance of the <see cref="ProxySaveMappingSetting{T}"/> class with specified non-nullable value and match behaviour.
/// </summary>
/// <param name="value">The non-nullable value of type <typeparamref name="T"/>.</param>
/// <param name="matchBehaviour">The match behaviour (optional, default is <c>MatchBehaviour.AcceptOnMatch</c>.</param>
/// <exception cref="ArgumentNullException">Thrown if the <paramref name="value"/> is null.</exception>
public ProxySaveMappingSetting(T value, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
Value = Guard.NotNull(value);
MatchBehaviour = matchBehaviour;
}
/// <summary>
/// Converts a non-nullable value of type <typeparamref name="T"/> to a <see cref="ProxySaveMappingSetting{T}"/> implicitly.
/// </summary>
/// <param name="value">The non-nullable value to convert.</param>
public static implicit operator ProxySaveMappingSetting<T>(T value) => new(value);
/// <summary>
/// Converts a <see cref="ProxySaveMappingSetting{T}"/> to its underlying non-nullable value of type <typeparamref name="T"/> implicitly.
/// </summary>
/// <param name="instance">The <see cref="ProxySaveMappingSetting{T}"/> to convert.</param>
public static implicit operator T(ProxySaveMappingSetting<T> instance) => instance.Value;
}

View File

@@ -1,24 +0,0 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// ProxySaveMappingSettings
/// </summary>
public class ProxySaveMappingSettings
{
/// <summary>
/// Only save request/response to the internal Mappings if the status code is included in this pattern. (Note that SaveMapping must also be set to true.)
/// The pattern can contain a single value like "200", but also ranges like "2xx", "100,300,600" or "100-299,6xx" are supported.
/// </summary>
[PublicAPI]
public ProxySaveMappingSetting<string>? StatusCodePattern { get; set; } = "*";
/// <summary>
/// Only save these Http Methods. (Note that SaveMapping must also be set to true.)
/// </summary>
[PublicAPI]
public ProxySaveMappingSetting<string[]>? HttpMethods { get; set; }
}

View File

@@ -1,24 +0,0 @@
// Copyright © WireMock.Net
namespace WireMock.Settings;
/// <summary>
/// Defines an old path param and a new path param to be replaced when proxying.
/// </summary>
public class ProxyUrlReplaceSettings
{
/// <summary>
/// The old path value to be replaced by the new path value
/// </summary>
public string OldValue { get; set; } = null!;
/// <summary>
/// The new path value to replace the old value with
/// </summary>
public string NewValue { get; set; } = null!;
/// <summary>
/// Defines if the case should be ignored when replacing.
/// </summary>
public bool IgnoreCase { get; set; }
}

View File

@@ -1,29 +0,0 @@
// Copyright © WireMock.Net
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// WebProxySettings
/// </summary>
public class WebProxySettings
{
/// <summary>
/// A string instance that contains the address of the proxy server.
/// </summary>
[PublicAPI]
public string Address { get; set; } = null!;
/// <summary>
/// The username associated with the credentials.
/// </summary>
[PublicAPI]
public string? UserName { get; set; }
/// <summary>
/// The password for the username associated with the credentials.
/// </summary>
[PublicAPI]
public string? Password { get; set; }
}

View File

@@ -1,24 +0,0 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.Settings;
/// <summary>
/// WebhookSettings
/// </summary>
public class WebhookSettings : HttpClientSettings
{
/// <summary>
/// Executes an action after the transformation of the request body.
/// </summary>
/// <param name="mapping">The mapping used for the request.</param>
/// <param name="requestUrl">The request Url.</param>
/// <param name="bodyData">The body data of the request. [Optional]</param>
/// <param name="headers">The headers of the request. [Optional]</param>
public virtual void PostTransform(IMapping mapping, string requestUrl, IBodyData? bodyData = null, IDictionary<string, WireMockList<string>>? headers = null)
{
}
}

View File

@@ -1,67 +0,0 @@
// Copyright © WireMock.Net
using System.Security.Cryptography.X509Certificates;
using JetBrains.Annotations;
namespace WireMock.Settings;
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath should be defined
/// OR
/// X509Certificate should be defined
/// </summary>
public class WireMockCertificateSettings
{
/// <summary>
/// X.509 certificate StoreName (AddressBook, AuthRoot, CertificateAuthority, My, Root, TrustedPeople or TrustedPublisher)
/// </summary>
[PublicAPI]
public string? X509StoreName { get; set; }
/// <summary>
/// X.509 certificate StoreLocation (CurrentUser or LocalMachine)
/// </summary>
[PublicAPI]
public string? X509StoreLocation { get; set; }
/// <summary>
/// X.509 certificate Thumbprint or SubjectName (if not defined, the 'host' is used)
/// </summary>
[PublicAPI]
public string? X509StoreThumbprintOrSubjectName { get; set; }
/// <summary>
/// X.509 certificate FilePath
/// </summary>
[PublicAPI]
public string? X509CertificateFilePath { get; set; }
/// <summary>
/// A X.509 certificate instance.
/// </summary>
[PublicAPI]
public X509Certificate2? X509Certificate { get; set; }
/// <summary>
/// X.509 certificate Password
/// </summary>
[PublicAPI]
public string? X509CertificatePassword { get; set; }
/// <summary>
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath should be defined
/// OR
/// X509Certificate should be defined
/// </summary>
[PublicAPI]
public bool IsDefined =>
!string.IsNullOrEmpty(X509StoreName) && !string.IsNullOrEmpty(X509StoreLocation) ||
!string.IsNullOrEmpty(X509CertificateFilePath) ||
X509Certificate != null;
}

View File

@@ -1,340 +0,0 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using HandlebarsDotNet;
using JetBrains.Annotations;
using Newtonsoft.Json;
using WireMock.Admin.Mappings;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Matchers;
using WireMock.RegularExpressions;
using WireMock.Types;
using System.Globalization;
using WireMock.Models;
#if USE_ASPNETCORE
using Microsoft.Extensions.DependencyInjection;
#endif
namespace WireMock.Settings;
/// <summary>
/// WireMockServerSettings
/// </summary>
public class WireMockServerSettings
{
internal const int DefaultStartTimeout = 10000;
/// <summary>
/// Gets or sets the http port.
/// </summary>
[PublicAPI]
public int? Port { get; set; }
/// <summary>
/// Gets or sets the use SSL.
/// </summary>
// ReSharper disable once InconsistentNaming
[PublicAPI]
public bool? UseSSL { get; set; }
/// <summary>
/// Defines on which scheme (http/https) to host. (This overrides the <c>UseSSL</c> value).
/// </summary>
[PublicAPI]
public HostingScheme? HostingScheme { get; set; }
/// <summary>
/// Gets or sets to use HTTP 2 (used for Grpc).
/// </summary>
[PublicAPI]
public bool? UseHttp2 { get; set; }
/// <summary>
/// Gets or sets whether to start admin interface.
/// </summary>
[PublicAPI]
public bool? StartAdminInterface { get; set; }
/// <summary>
/// Gets or sets if the static mappings should be read at startup.
/// </summary>
[PublicAPI]
public bool? ReadStaticMappings { get; set; }
/// <summary>
/// Watch the static mapping files + folder for changes when running.
/// </summary>
[PublicAPI]
public bool? WatchStaticMappings { get; set; }
/// <summary>
/// A value indicating whether subdirectories within the static mappings path should be monitored.
/// </summary>
[PublicAPI]
public bool? WatchStaticMappingsInSubdirectories { get; set; }
/// <summary>
/// Gets or sets if the proxy and record settings.
/// </summary>
[PublicAPI]
public ProxyAndRecordSettings? ProxyAndRecordSettings { get; set; }
/// <summary>
/// Gets or sets the urls.
/// </summary>
[PublicAPI]
public string[]? Urls { get; set; }
/// <summary>
/// StartTimeout
/// </summary>
[PublicAPI]
public int StartTimeout { get; set; } = DefaultStartTimeout;
/// <summary>
/// Allow Partial Mapping (default set to false).
/// </summary>
[PublicAPI]
public bool? AllowPartialMapping { get; set; }
/// <summary>
/// The username needed for __admin access.
/// </summary>
[PublicAPI]
public string? AdminUsername { get; set; }
/// <summary>
/// The password needed for __admin access.
/// </summary>
[PublicAPI]
public string? AdminPassword { get; set; }
/// <summary>
/// The AzureAD Tenant needed for __admin access.
/// </summary>
[PublicAPI]
public string? AdminAzureADTenant { get; set; }
/// <summary>
/// The AzureAD Audience / Resource for __admin access.
/// </summary>
[PublicAPI]
public string? AdminAzureADAudience { get; set; }
/// <summary>
/// The RequestLog expiration in hours (optional).
/// </summary>
[PublicAPI]
public int? RequestLogExpirationDuration { get; set; }
/// <summary>
/// The MaxRequestLog count (optional).
/// </summary>
[PublicAPI]
public int? MaxRequestLogCount { get; set; }
/// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) before the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<object>? PreWireMockMiddlewareInit { get; set; }
/// <summary>
/// Action which is called (with the IAppBuilder or IApplicationBuilder) after the internal WireMockMiddleware is initialized. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<object>? PostWireMockMiddlewareInit { get; set; }
#if USE_ASPNETCORE
/// <summary>
/// Action which is called with IServiceCollection when ASP.NET Core DI is being configured. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<IServiceCollection>? AdditionalServiceRegistration { get; set; }
/// <summary>
/// Policies to use when using CORS. By default CORS is disabled. [Optional]
/// </summary>
[PublicAPI]
public CorsPolicyOptions? CorsPolicyOptions { get; set; }
#endif
/// <summary>
/// The IWireMockLogger which logs Debug, Info, Warning or Error
/// </summary>
[PublicAPI]
[JsonIgnore]
public IWireMockLogger Logger { get; set; } = null!;
/// <summary>
/// Handler to interact with the file system to read and write static mapping files.
/// </summary>
[PublicAPI]
[JsonIgnore]
public IFileSystemHandler FileSystemHandler { get; set; } = null!;
/// <summary>
/// Action which can be used to add additional Handlebars registrations. [Optional]
/// </summary>
[PublicAPI]
[JsonIgnore]
public Action<IHandlebars, IFileSystemHandler>? HandlebarsRegistrationCallback { get; set; }
/// <summary>
/// Allow the usage of CSharpCodeMatcher (default is not allowed).
/// </summary>
[PublicAPI]
public bool? AllowCSharpCodeMatcher { get; set; }
/// <summary>
/// Allow a Body for all HTTP Methods. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? AllowBodyForAllHttpMethods { get; set; }
/// <summary>
/// Allow only a HttpStatus Code in the response which is defined. (default set to <c>false</c>).
/// - false : also null, 0, empty or invalid HttpStatus codes are allowed.
/// - true : only codes defined in <see cref="System.Net.HttpStatusCode"/> are allowed.
/// </summary>
[PublicAPI]
public bool? AllowOnlyDefinedHttpStatusCodeInResponse { get; set; }
/// <summary>
/// Set to true to disable Json deserialization when processing requests. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DisableJsonBodyParsing { get; set; }
/// <summary>
/// Disable support for GZip and Deflate request body decompression. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DisableRequestBodyDecompressing { get; set; }
/// <summary>
/// Set to true to disable FormUrlEncoded deserializing when processing requests. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DisableDeserializeFormUrlEncoded { get; set; }
/// <summary>
/// Handle all requests synchronously. (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? HandleRequestsSynchronously { get; set; }
/// <summary>
/// If https is used, these settings can be used to configure the CertificateSettings in case a custom certificate instead the default .NET certificate should be used.
///
/// X509StoreName and X509StoreLocation should be defined
/// OR
/// X509CertificateFilePath should be defined
/// OR
/// X509Certificate should be defined
/// </summary>
[PublicAPI]
public WireMockCertificateSettings? CertificateSettings { get; set; }
/// <summary>
/// Defines if custom CertificateSettings are defined
/// </summary>
[PublicAPI]
public bool CustomCertificateDefined => CertificateSettings?.IsDefined == true;
#if USE_ASPNETCORE
/// <summary>
/// Client certificate mode for the server
/// </summary>
[PublicAPI]
public ClientCertificateMode ClientCertificateMode { get; set; }
/// <summary>
/// Whether to accept any client certificate
/// </summary>
public bool AcceptAnyClientCertificate { get; set; }
#endif
/// <summary>
/// Defines the global IWebhookSettings to use.
/// </summary>
[PublicAPI]
public WebhookSettings? WebhookSettings { get; set; }
/// <summary>
/// Use the <see cref="RegexExtended"/> instead of the default <see cref="Regex"/> (default set to <c>true</c>).
/// </summary>
[PublicAPI]
public bool? UseRegexExtended { get; set; } = true;
/// <summary>
/// Save unmatched requests to a file using the <see cref="IFileSystemHandler"/> (default set to <c>false</c>).
/// </summary>
[PublicAPI]
public bool? SaveUnmatchedRequests { get; set; }
/// <summary>
/// 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 <c>false</c>).
/// </summary>
[PublicAPI]
public bool? DoNotSaveDynamicResponseInLogEntry { get; set; }
/// <summary>
/// See <seealso cref="QueryParameterMultipleValueSupport"/>.
///
/// Default value = "All".
/// </summary>
[PublicAPI]
public QueryParameterMultipleValueSupport? QueryParameterMultipleValueSupport { get; set; }
/// <summary>
/// Custom matcher mappings for static mappings
/// </summary>
[PublicAPI, JsonIgnore]
public IDictionary<string, Func<MatcherModel, IMatcher>>? CustomMatcherMappings { get; set; }
/// <summary>
/// The <see cref="JsonSerializerSettings"/> used when the JSON response is generated.
/// </summary>
[PublicAPI, JsonIgnore]
public JsonSerializerSettings? JsonSerializerSettings { get; set; }
/// <summary>
/// The Culture to use.
/// Currently used for:
/// - Handlebars Transformer
/// </summary>
[JsonIgnore]
public CultureInfo Culture { get; set; } = CultureInfo.CurrentCulture;
/// <summary>
/// A list of Grpc ProtoDefinitions which can be used.
/// </summary>
[PublicAPI]
public Dictionary<string, string[]>? ProtoDefinitions { get; set; }
/// <summary>
/// A list of GraphQL Schemas which can be used.
/// </summary>
[PublicAPI]
public Dictionary<string, GraphQLSchemaDetails>? GraphQLSchemas { get; set; }
/// <summary>
/// The admin path to use for accessing the Admin REST interface.
/// If not set <c>__/admin</c> is used.
/// </summary>
[PublicAPI]
public string? AdminPath { get; set; }
/// <summary>
/// Defines the additional Handlebars Settings.
/// </summary>
[PublicAPI]
public HandlebarsSettings? HandlebarsSettings { get; set; }
}

View File

@@ -1,86 +0,0 @@
// Copyright © WireMock.Net
using System.IO;
using Stef.Validation;
namespace WireMock.Util;
internal static class FilePathUtils
{
/// <summary>
/// Robust handling of the user defined path.
/// Also supports Unix and Windows platforms
/// </summary>
/// <param name="path">The path to clean</param>
public static string? CleanPath(string? path)
{
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
}
/// <summary>
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
/// </summary>
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
public static string? RemoveLeadingDirectorySeparators(string? path)
{
return path?.TrimStart(Path.DirectorySeparatorChar);
}
/// <summary>
/// Combine two paths
/// </summary>
/// <param name="root">The root path</param>
/// <param name="path">The path</param>
public static string Combine(string root, string? path)
{
Guard.NotNull(root);
var result = RemoveLeadingDirectorySeparators(path);
return result == null ? root : Path.Combine(root, result);
}
/// <summary>
/// Returns a relative path from one path to another.
/// </summary>
/// <param name="relativeTo">The source path the result should be relative to. This path is always considered to be a directory..</param>
/// <param name="path">The destination path.</param>
/// <returns>The relative path, or path if the paths don't share the same root.</returns>
public static string GetRelativePath(string relativeTo, string path)
{
#if NETCOREAPP3_1 || NET5_0_OR_GREATER || NETSTANDARD2_1
return Path.GetRelativePath(relativeTo, path);
#else
Guard.NotNull(relativeTo);
Guard.NotNull(path);
static string AppendDirectorySeparatorChar(string path)
{
// Append a slash only if the path is a directory and does not have a slash.
if (!Path.HasExtension(path) && !path.EndsWith(Path.DirectorySeparatorChar.ToString()))
{
return path + Path.DirectorySeparatorChar;
}
return path;
}
var fromUri = new System.Uri(AppendDirectorySeparatorChar(relativeTo));
var toUri = new System.Uri(AppendDirectorySeparatorChar(path));
if (fromUri.Scheme != toUri.Scheme)
{
return path;
}
var relativeUri = fromUri.MakeRelativeUri(toUri);
var relativePath = System.Uri.UnescapeDataString(relativeUri.ToString());
if (string.Equals(toUri.Scheme, "FILE", System.StringComparison.OrdinalIgnoreCase))
{
relativePath = relativePath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
}
return relativePath;
#endif
}
}

View File

@@ -1,37 +0,0 @@
// Copyright © WireMock.Net
#if PROTOBUF
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using JsonConverter.Abstractions;
using ProtoBufJsonConverter;
using ProtoBufJsonConverter.Models;
namespace WireMock.Util;
internal static class ProtoBufUtils
{
internal static async Task<byte[]> GetProtoBufMessageWithHeaderAsync(
IReadOnlyList<string>? protoDefinitions,
string? messageType,
object? value,
IJsonConverter? jsonConverter = null,
CancellationToken cancellationToken = default
)
{
if (protoDefinitions == null || string.IsNullOrWhiteSpace(messageType) || value is null)
{
return [];
}
var resolver = new WireMockProtoFileResolver(protoDefinitions);
var request = new ConvertToProtoBufRequest(protoDefinitions[0], messageType!, value, true)
.WithProtoFileResolver(resolver);
return await SingletonFactory<Converter>
.GetInstance()
.ConvertAsync(request, cancellationToken).ConfigureAwait(false);
}
}
#endif

View File

@@ -1,101 +0,0 @@
// Copyright © WireMock.Net
#if PROTOBUF
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using ProtoBufJsonConverter;
using ProtoBufJsonConverter.Models;
using Stef.Validation;
using WireMock.Models;
using WireMock.Settings;
namespace WireMock.Util;
/// <summary>
/// Some helper methods for Proto Definitions.
/// </summary>
public static class ProtoDefinitionHelper
{
/// <summary>
/// Builds a dictionary of ProtoDefinitions from a directory.
/// - The key will be the filename without extension.
/// - The value will be the ProtoDefinition with an extra comment with the relative path to each <c>.proto</c> file so it can be used by the WireMockProtoFileResolver.
/// </summary>
/// <param name="directory">The directory to start from.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is <c>System.Threading.CancellationToken.None</c>.</param>
public static async Task<ProtoDefinitionData> FromDirectory(string directory, CancellationToken cancellationToken = default)
{
Guard.NotNullOrEmpty(directory);
var fileNameMappedToProtoDefinition = new Dictionary<string, string>();
var filePaths = Directory.EnumerateFiles(directory, "*.proto", SearchOption.AllDirectories);
foreach (var filePath in filePaths)
{
// Get the relative path to the directory (note that this will be OS specific).
var relativePath = FilePathUtils.GetRelativePath(directory, filePath);
// Make it a valid proto import path
var protoRelativePath = relativePath.Replace(Path.DirectorySeparatorChar, '/');
// Build comment and get content from file.
var comment = $"// {protoRelativePath}";
#if NETSTANDARD2_0
var content = File.ReadAllText(filePath);
#else
var content = await File.ReadAllTextAsync(filePath, cancellationToken);
#endif
// Only add the comment if it's not already defined.
var modifiedContent = !content.StartsWith(comment) ? $"{comment}\n{content}" : content;
var key = Path.GetFileNameWithoutExtension(filePath);
fileNameMappedToProtoDefinition.Add(key, modifiedContent);
}
var converter = SingletonFactory<Converter>.GetInstance();
var resolver = new WireMockProtoFileResolver(fileNameMappedToProtoDefinition.Values);
var messageTypeMappedToWithProtoDefinition = new Dictionary<string, string>();
foreach (var protoDefinition in fileNameMappedToProtoDefinition.Values)
{
var infoRequest = new GetInformationRequest(protoDefinition, resolver);
try
{
var info = await converter.GetInformationAsync(infoRequest, cancellationToken);
foreach (var messageType in info.MessageTypes)
{
messageTypeMappedToWithProtoDefinition[messageType.Key] = protoDefinition;
}
}
catch
{
// Ignore
}
}
return new ProtoDefinitionData(fileNameMappedToProtoDefinition);
}
internal static IdOrTexts GetIdOrTexts(WireMockServerSettings settings, params string[] protoDefinitionOrId)
{
switch (protoDefinitionOrId.Length)
{
case 1:
var idOrText = protoDefinitionOrId[0];
if (settings.ProtoDefinitions?.TryGetValue(idOrText, out var protoDefinitions) == true)
{
return new(idOrText, protoDefinitions);
}
return new(null, protoDefinitionOrId);
default:
return new(null, protoDefinitionOrId);
}
}
}
#endif

View File

@@ -1,26 +0,0 @@
// Copyright © WireMock.Net
namespace WireMock.Util;
internal static class SingletonLock
{
public static readonly object Lock = new();
}
internal static class SingletonFactory<T> where T : class, new()
{
private static T? _instance;
public static T GetInstance()
{
if (_instance == null)
{
lock (SingletonLock.Lock)
{
_instance ??= new T();
}
}
return _instance;
}
}

View File

@@ -1,75 +0,0 @@
// Copyright © WireMock.Net
#if PROTOBUF
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using ProtoBufJsonConverter;
using Stef.Validation;
using WireMock.Extensions;
namespace WireMock.Util;
/// <summary>
/// This resolver is used to resolve the extra ProtoDefinition files.
///
/// It assumes that:
/// - The first commented line of each ProtoDefinition file is the filepath which is used in the import of the other ProtoDefinition file(s).
/// </summary>
internal class WireMockProtoFileResolver : IProtoFileResolver
{
private readonly Dictionary<string, string> _files = [];
public WireMockProtoFileResolver(IReadOnlyCollection<string> protoDefinitions)
{
if (Guard.NotNullOrEmpty(protoDefinitions).Count() <= 1)
{
return;
}
foreach (var extraProtoDefinition in protoDefinitions)
{
var firstNonEmptyLine = extraProtoDefinition.Split(['\r', '\n']).FirstOrDefault(l => !string.IsNullOrEmpty(l));
if (firstNonEmptyLine != null)
{
if (TryGetValidPath(firstNonEmptyLine.TrimStart(['/', ' ']), out var validPath))
{
_files.Add(validPath, extraProtoDefinition);
}
else
{
_files.Add(extraProtoDefinition.GetDeterministicHashCodeAsString(), extraProtoDefinition);
}
}
}
}
public bool Exists(string path)
{
return _files.ContainsKey(path);
}
public TextReader OpenText(string path)
{
if (_files.TryGetValue(path, out var extraProtoDefinition))
{
return new StringReader(extraProtoDefinition);
}
throw new FileNotFoundException($"The ProtoDefinition '{path}' was not found.");
}
private static bool TryGetValidPath(string path, [NotNullWhen(true)] out string? validPath)
{
if (!path.Any(c => Path.GetInvalidPathChars().Contains(c)))
{
validPath = path;
return true;
}
validPath = null;
return false;
}
}
#endif

View File

@@ -125,9 +125,9 @@
<PackageReference Include="Scriban.Signed" Version="5.5.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' and '$(TargetFramework)' != 'net46' and '$(TargetFramework)' != 'net461'">
<!--<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' and '$(TargetFramework)' != 'net46' and '$(TargetFramework)' != 'net461'">
<PackageReference Include="ProtoBufJsonConverter" Version="0.10.0" />
</ItemGroup>
</ItemGroup>-->
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">
<!--<PackageReference Include="Nullable" Version="1.3.1">
@@ -155,16 +155,6 @@
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Handlebars.Net.Helpers" Version="2.5.2" />
<!--<PackageReference Include="Handlebars.Net.Helpers.DynamicLinq" Version="2.5.2" />-->
<PackageReference Include="Handlebars.Net.Helpers.Humanizer" Version="2.5.2" />
<PackageReference Include="Handlebars.Net.Helpers.Json" Version="2.5.2" />
<PackageReference Include="Handlebars.Net.Helpers.Random" Version="2.5.2" />
<PackageReference Include="Handlebars.Net.Helpers.Xeger" Version="2.5.2" />
<PackageReference Include="Handlebars.Net.Helpers.XPath" Version="2.5.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'netstandard1.3' and '$(TargetFramework)' != 'net451' and '$(TargetFramework)' != 'net452' ">
<PackageReference Include="Handlebars.Net.Helpers.Xslt" Version="2.5.2" />
</ItemGroup>