mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-23 08:48:28 +02:00
Add Grpc ProtoBuf support (request-response) (#1047)
* ProtoBuf
* .
* x
* ---
* x
* fx
* ...
* sc
* ...
* .
* groen
* x
* fix tests
* ok!?
* fix tests
* fix tests
* !
* x
* 6
* .
* x
* ivaluematcher
* transformer
* .
* sc
* .
* mapping
* x
* tra
* com
* ...
* .
* .
* .
* AddProtoDefinition
* .
* set
* grpahj
* .
* .
* IdOrText
* ...
* async
* async2
* .
* t
* nuget
* <PackageReference Include="ProtoBufJsonConverter" Version="0.2.0-preview-04" />
* http version
* tests
* .WithHttpVersion("2")
* <PackageReference Include="ProtoBufJsonConverter" Version="0.2.0" />
* HttpVersionParser
This commit is contained in:
@@ -91,18 +91,50 @@ public interface IBodyResponseBuilder : IFaultResponseBuilder
|
||||
/// 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="converter">The JsonConverter.</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 converter, JsonConverterOptions? options = null);
|
||||
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="converter">The JsonConverter.</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 converter, JsonConverterOptions? options = null);
|
||||
IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter jsonConverter, JsonConverterOptions? options = null);
|
||||
|
||||
/// <summary>
|
||||
/// WithBody : 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>
|
||||
/// WithBody : 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
|
||||
);
|
||||
}
|
||||
@@ -9,7 +9,7 @@ namespace WireMock.ResponseBuilders;
|
||||
public interface IHeadersResponseBuilder : IBodyResponseBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// The with header.
|
||||
/// The WithHeader.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="values">The values.</param>
|
||||
@@ -17,23 +17,52 @@ public interface IHeadersResponseBuilder : IBodyResponseBuilder
|
||||
IResponseBuilder WithHeader(string name, params string[] values);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// The WithHeaders.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeaders(IDictionary<string, string> headers);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// The WithHeaders.
|
||||
/// </summary>
|
||||
/// <param name="headers">The headers.</param>
|
||||
/// <returns>The <see cref="IResponseBuilder"/>.</returns>
|
||||
IResponseBuilder WithHeaders(IDictionary<string, string[]> headers);
|
||||
|
||||
/// <summary>
|
||||
/// The with headers.
|
||||
/// 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);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using JsonConverter.Abstractions;
|
||||
using Stef.Validation;
|
||||
using WireMock.Exceptions;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
@@ -185,25 +186,79 @@ public partial class Response
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBody(object body, IJsonConverter converter, JsonConverterOptions? options = null)
|
||||
public IResponseBuilder WithBody(object body, IJsonConverter jsonConverter, JsonConverterOptions? options = null)
|
||||
{
|
||||
return WithBody(body, null, converter, options);
|
||||
return WithBody(body, null, jsonConverter, options);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter converter, JsonConverterOptions? options = null)
|
||||
public IResponseBuilder WithBody(object body, Encoding? encoding, IJsonConverter jsonConverter, JsonConverterOptions? options = null)
|
||||
{
|
||||
Guard.NotNull(body);
|
||||
Guard.NotNull(converter);
|
||||
Guard.NotNull(jsonConverter);
|
||||
|
||||
ResponseMessage.BodyDestination = null;
|
||||
ResponseMessage.BodyData = new BodyData
|
||||
{
|
||||
Encoding = encoding,
|
||||
DetectedBodyType = BodyType.String,
|
||||
BodyAsString = converter.Serialize(body, options)
|
||||
BodyAsString = jsonConverter.Serialize(body, options)
|
||||
};
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsProtoBuf(
|
||||
string protoDefinition,
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
Guard.NotNullOrWhiteSpace(protoDefinition);
|
||||
Guard.NotNullOrWhiteSpace(messageType);
|
||||
Guard.NotNull(value);
|
||||
|
||||
#if !PROTOBUF
|
||||
throw new System.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 (null, protoDefinition),
|
||||
ProtoBufMessageType = messageType
|
||||
};
|
||||
#endif
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithBodyAsProtoBuf(
|
||||
string messageType,
|
||||
object value,
|
||||
IJsonConverter? jsonConverter = null,
|
||||
JsonConverterOptions? options = null
|
||||
)
|
||||
{
|
||||
Guard.NotNullOrWhiteSpace(messageType);
|
||||
Guard.NotNull(value);
|
||||
|
||||
#if !PROTOBUF
|
||||
throw new System.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
|
||||
};
|
||||
#endif
|
||||
return this;
|
||||
}
|
||||
}
|
||||
101
src/WireMock.Net/ResponseBuilders/Response.WithHeaders.cs
Normal file
101
src/WireMock.Net/ResponseBuilders/Response.WithHeaders.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Stef.Validation;
|
||||
using WireMock.Types;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
public partial class Response
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithHeader(string name, params string[] values)
|
||||
{
|
||||
Guard.NotNull(name);
|
||||
|
||||
ResponseMessage.AddHeader(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
|
||||
{
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string[]> headers)
|
||||
{
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers)
|
||||
{
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.Headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithTrailingHeader(string name, params string[] values)
|
||||
{
|
||||
#if !TRAILINGHEADERS
|
||||
throw new System.NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
|
||||
Guard.NotNull(name);
|
||||
|
||||
ResponseMessage.AddTrailingHeader(name, values);
|
||||
return this;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithTrailingHeaders(IDictionary<string, string> headers)
|
||||
{
|
||||
#if !TRAILINGHEADERS
|
||||
throw new System.NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.TrailingHeaders = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithTrailingHeaders(IDictionary<string, string[]> headers)
|
||||
{
|
||||
#if !TRAILINGHEADERS
|
||||
throw new System.NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.TrailingHeaders = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IResponseBuilder WithTrailingHeaders(IDictionary<string, WireMockList<string>> headers)
|
||||
{
|
||||
#if !TRAILINGHEADERS
|
||||
throw new System.NotSupportedException("The WithBodyAsProtoBuf method can not be used for .NETStandard1.3 or .NET Framework 4.6.1 or lower.");
|
||||
#else
|
||||
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.TrailingHeaders = headers;
|
||||
return this;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using JetBrains.Annotations;
|
||||
using Stef.Validation;
|
||||
using WireMock.Matchers.Request;
|
||||
using WireMock.Proxy;
|
||||
using WireMock.RequestBuilders;
|
||||
using WireMock.Settings;
|
||||
using WireMock.Transformers;
|
||||
using WireMock.Transformers.Handlebars;
|
||||
using WireMock.Transformers.Scriban;
|
||||
using WireMock.Types;
|
||||
using WireMock.Util;
|
||||
|
||||
namespace WireMock.ResponseBuilders;
|
||||
|
||||
@@ -26,6 +27,11 @@ public partial class Response : IResponseBuilder
|
||||
|
||||
private TimeSpan? _delay;
|
||||
|
||||
/// <summary>
|
||||
/// The link back to the mapping.
|
||||
/// </summary>
|
||||
public IMapping Mapping { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum random delay in milliseconds.
|
||||
/// </summary>
|
||||
@@ -112,7 +118,7 @@ public partial class Response : IResponseBuilder
|
||||
{
|
||||
ResponseMessage = responseMessage;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc cref="IStatusCodeResponseBuilder.WithStatusCode(int)"/>
|
||||
[PublicAPI]
|
||||
public IResponseBuilder WithStatusCode(int code)
|
||||
@@ -156,42 +162,6 @@ public partial class Response : IResponseBuilder
|
||||
return WithStatusCode((int)HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeader(string, string[])"/>
|
||||
public IResponseBuilder WithHeader(string name, params string[] values)
|
||||
{
|
||||
Guard.NotNull(name);
|
||||
|
||||
ResponseMessage.AddHeader(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string})"/>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string> headers)
|
||||
{
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, string[]})"/>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, string[]> headers)
|
||||
{
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.Headers = headers.ToDictionary(header => header.Key, header => new WireMockList<string>(header.Value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IHeadersResponseBuilder.WithHeaders(IDictionary{string, WireMockList{string}})"/>
|
||||
public IResponseBuilder WithHeaders(IDictionary<string, WireMockList<string>> headers)
|
||||
{
|
||||
Guard.NotNull(headers);
|
||||
|
||||
ResponseMessage.Headers = headers;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ITransformResponseBuilder.WithTransformer(bool)"/>
|
||||
public IResponseBuilder WithTransformer(bool transformContentFromBodyAsFile)
|
||||
{
|
||||
@@ -304,10 +274,30 @@ public partial class Response : IResponseBuilder
|
||||
{
|
||||
responseMessage.Headers = ResponseMessage.Headers;
|
||||
}
|
||||
|
||||
// Copy TrailingHeaders from ResponseMessage (if defined)
|
||||
if (ResponseMessage.TrailingHeaders?.Count > 0)
|
||||
{
|
||||
responseMessage.TrailingHeaders = ResponseMessage.TrailingHeaders;
|
||||
}
|
||||
}
|
||||
|
||||
if (UseTransformer)
|
||||
{
|
||||
// Check if the body matcher is a RequestMessageProtoBufMatcher and try to to decode the byte-array to a BodyAsJson.
|
||||
if (mapping.RequestMatcher is Request requestMatcher && requestMessage is RequestMessage request)
|
||||
{
|
||||
var protoBufMatcher = requestMatcher.GetRequestMessageMatcher<RequestMessageProtoBufMatcher>()?.Matcher;
|
||||
if (protoBufMatcher != null)
|
||||
{
|
||||
var decoded = await protoBufMatcher.DecodeAsync(request.BodyData?.BodyAsBytes).ConfigureAwait(false);
|
||||
if (decoded != null)
|
||||
{
|
||||
request.BodyAsJson = JsonUtils.ConvertValueToJToken(decoded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ITransformer responseMessageTransformer;
|
||||
switch (TransformerType)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user