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

@@ -0,0 +1,118 @@
// Copyright © WireMock.Net
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; }
/// <inheritdoc />
public Func<IdOrTexts> ProtoDefinition { get; set; }
/// <inheritdoc />
public string MessageType { get; }
/// <inheritdoc />
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;
}
}
}

View File

@@ -0,0 +1,39 @@
// 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

@@ -0,0 +1,8 @@
// Copyright © WireMock.Net
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("WireMock.Net.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
// Needed for Moq in the UnitTest project
// [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

View File

@@ -0,0 +1,108 @@
// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using WireMock.Matchers;
using WireMock.Matchers.Request;
using WireMock.Models;
namespace WireMock.RequestBuilders;
/// <summary>
/// IRequestBuilderExtensions extensions for ProtoBuf.
/// </summary>
// ReSharper disable once InconsistentNaming
public static class IRequestBuilderExtensions
{
/// <summary>
/// WithBodyAsProtoBuf
/// </summary>
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
/// <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="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string protoDefinition, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return requestBuilder.WithBodyAsProtoBuf([protoDefinition], messageType, matchBehaviour);
}
/// <summary>
/// WithBodyAsProtoBuf
/// </summary>
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
/// <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="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string protoDefinition, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return requestBuilder.WithBodyAsProtoBuf([protoDefinition], messageType, matcher, matchBehaviour);
}
/// <summary>
/// WithBodyAsProtoBuf
/// </summary>
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
/// <param name="protoDefinitions">The proto definitions 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="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, IReadOnlyList<string> protoDefinitions, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType));
}
/// <summary>
/// WithBodyAsProtoBuf
/// </summary>
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</param>
/// <param name="protoDefinitions">The proto definitions 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="matcher">The matcher to use to match the ProtoBuf as (json) object.</param>
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, IReadOnlyList<string> protoDefinitions, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, () => new IdOrTexts(null, protoDefinitions), messageType, matcher));
}
/// <summary>
/// WithBodyAsProtoBuf
/// </summary>
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</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>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string messageType, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(requestBuilder), messageType));
}
/// <summary>
/// WithBodyAsProtoBuf
/// </summary>
/// <param name="requestBuilder">The <see cref="IRequestBuilder"/>.</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 matcher to use to match the ProtoBuf as (json) object.</param>
/// <param name="matchBehaviour">The match behaviour. (default = "AcceptOnMatch")</param>
/// <returns>The <see cref="IRequestBuilder"/>.</returns>
public static IRequestBuilder WithBodyAsProtoBuf(this IRequestBuilder requestBuilder, string messageType, IObjectMatcher matcher, MatchBehaviour matchBehaviour = MatchBehaviour.AcceptOnMatch)
{
return requestBuilder.Add(new RequestMessageProtoBufMatcher(matchBehaviour, ProtoDefinitionFunc(requestBuilder), messageType, matcher));
}
private static Func<IdOrTexts> ProtoDefinitionFunc(IRequestBuilder requestBuilder)
{
return () =>
{
if (requestBuilder.Mapping.ProtoDefinition == null)
{
throw new InvalidOperationException($"No ProtoDefinition defined on mapping '{requestBuilder.Mapping.Guid}'. Please use the WireMockServerSettings to define ProtoDefinitions.");
}
return requestBuilder.Mapping.ProtoDefinition.Value;
};
}
}

View File

@@ -0,0 +1,106 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using JsonConverter.Abstractions;
using Stef.Validation;
using WireMock.Exceptions;
using WireMock.Models;
using WireMock.Types;
using WireMock.Util;
namespace WireMock.ResponseBuilders;
/// <summary>
/// Extensions for <see cref="IResponseBuilder"/> to implement WithBodyAsProtoBuf.
/// </summary>
public static class IResponseBuilderExtensions
{
/// <summary>
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
/// </summary>
/// <param name="responseBuilder">The response builder.</param>
/// <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>
public static IResponseBuilder WithBodyAsProtoBuf(
this IResponseBuilder responseBuilder,
string protoDefinition,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
)
{
return responseBuilder.WithBodyAsProtoBuf([protoDefinition], messageType, value, jsonConverter, options);
}
/// <summary>
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on proto definitions, message type and the value.
/// </summary>
/// <param name="responseBuilder">The response builder.</param>
/// <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>
public static IResponseBuilder WithBodyAsProtoBuf(
this IResponseBuilder responseBuilder,
IReadOnlyList<string> protoDefinitions,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
)
{
Guard.NotNullOrEmpty(protoDefinitions);
Guard.NotNullOrWhiteSpace(messageType);
Guard.NotNull(value);
responseBuilder.ResponseMessage.BodyDestination = null;
responseBuilder.ResponseMessage.BodyData = new BodyData
{
DetectedBodyType = BodyType.ProtoBuf,
BodyAsJson = value,
ProtoDefinition = () => new IdOrTexts(null, protoDefinitions),
ProtoBufMessageType = messageType
};
return responseBuilder;
}
/// <summary>
/// WithBodyAsProtoBuf : Create a ProtoBuf byte[] response based on a proto definition, message type and the value.
/// </summary>
/// <param name="responseBuilder">The response builder.</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>
public static IResponseBuilder WithBodyAsProtoBuf(
this IResponseBuilder responseBuilder,
string messageType,
object value,
IJsonConverter? jsonConverter = null,
JsonConverterOptions? options = null
)
{
Guard.NotNullOrWhiteSpace(messageType);
Guard.NotNull(value);
responseBuilder.ResponseMessage.BodyDestination = null;
responseBuilder.ResponseMessage.BodyData = new BodyData
{
DetectedBodyType = BodyType.ProtoBuf,
BodyAsJson = value,
ProtoDefinition = () => responseBuilder.Mapping.ProtoDefinition ?? throw new WireMockException("ProtoDefinition cannot be resolved. You probably forgot to call .WithProtoDefinition(...) on the mapping."),
ProtoBufMessageType = messageType
};
return responseBuilder;
}
}

View File

@@ -0,0 +1,47 @@
// Copyright © WireMock.Net
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using JsonConverter.Abstractions;
using ProtoBufJsonConverter;
using ProtoBufJsonConverter.Models;
using WireMock.ResponseBuilders;
namespace WireMock.Util;
internal class ProtoBufUtils : IProtoBufUtils
{
public 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);
}
public IResponseBuilder UpdateResponseBuilder(IResponseBuilder responseBuilder, string protoBufMessageType, object bodyAsJson, params string[] protoDefinitions)
{
if (protoDefinitions.Length > 0)
{
return responseBuilder.WithBodyAsProtoBuf(protoDefinitions, protoBufMessageType, bodyAsJson);
}
// ProtoDefinition(s) is/are defined at Mapping/Server level
return responseBuilder.WithBodyAsProtoBuf(protoBufMessageType, bodyAsJson);
}
}

View File

@@ -0,0 +1,80 @@
// Copyright © WireMock.Net
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;
namespace WireMock.Util;
/// <summary>
/// Some helper methods for Proto Definitions.
/// </summary>
internal static class ProtoDefinitionDataHelper
{
/// <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 || NET462
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);
}
}

View File

@@ -0,0 +1,73 @@
// Copyright © WireMock.Net
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;
}
}

View File

@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.10.1-preview-00</Version>
<Description>ProtoBuf and gRPC support for WireMock.Net</Description>
<AssemblyTitle>WireMock.Net.ProtoBuf</AssemblyTitle>
<Authors>Stef Heyenrath</Authors>
<TargetFrameworks>netstandard2.1;net462;net6.0;net8.0</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PackageTags>wiremock;matchers;matcher;protobuf;grpc</PackageTags>
<RootNamespace>WireMock</RootNamespace>
<ProjectGuid>{B47413AA-55D3-49A7-896A-17ADBFF72407}</ProjectGuid>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<!--<DelaySign>true</DelaySign>-->
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ProtoBufJsonConverter" Version="0.10.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WireMock.Net.Shared\WireMock.Net.Shared.csproj" />
</ItemGroup>
</Project>