Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Grpc ProtoBuf support (request-response) #1047

Merged
merged 56 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
80ba43e
ProtoBuf
StefH Dec 23, 2023
d3ef6cc
Merge branch 'master' into stef-protobuf
StefH Dec 23, 2023
1f4fb94
.
StefH Dec 23, 2023
6cbef09
x
StefH Dec 23, 2023
d4d1b7b
---
StefH Dec 23, 2023
ce63663
x
StefH Dec 24, 2023
d2bee04
fx
StefH Dec 24, 2023
1c01e7a
...
StefH Dec 25, 2023
f4f4c31
sc
StefH Dec 25, 2023
8b85698
Merge branch 'master' into stef-protobuf
StefH Dec 27, 2023
1ac1698
...
StefH Dec 27, 2023
3c63a8d
.
StefH Dec 27, 2023
0679f58
groen
StefH Dec 27, 2023
c0c0fa9
x
StefH Dec 27, 2023
4c433f6
fix tests
StefH Dec 27, 2023
995f89b
ok!?
StefH Dec 27, 2023
ad45b69
fix tests
StefH Dec 27, 2023
199ba9f
fix tests
StefH Dec 27, 2023
a47d4de
!
StefH Dec 28, 2023
9b7dee5
x
StefH Dec 28, 2023
980595c
6
StefH Dec 28, 2023
eec149d
.
StefH Dec 28, 2023
b5d90b4
x
StefH Dec 28, 2023
17c7a66
ivaluematcher
StefH Dec 28, 2023
3adc428
transformer
StefH Dec 29, 2023
d4d0e91
.
StefH Dec 29, 2023
90ca499
sc
StefH Dec 29, 2023
8436a84
.
StefH Dec 29, 2023
84deb58
mapping
StefH Dec 29, 2023
49d1877
x
StefH Dec 29, 2023
2d49b7d
tra
StefH Dec 30, 2023
7e3d5c5
com
StefH Dec 30, 2023
f9c9be2
...
StefH Dec 30, 2023
01a8e3d
.
StefH Dec 30, 2023
5ecb619
.
StefH Dec 30, 2023
e88f4aa
.
StefH Dec 30, 2023
61fa7dc
AddProtoDefinition
StefH Dec 30, 2023
8b83f44
.
StefH Dec 30, 2023
e2fbfba
set
StefH Dec 30, 2023
d7495b4
grpahj
StefH Dec 31, 2023
9ecad4a
.
StefH Dec 31, 2023
2ba66b9
.
StefH Jan 1, 2024
c638509
IdOrText
StefH Jan 1, 2024
d047884
...
StefH Jan 1, 2024
f8e9efd
async
StefH Jan 6, 2024
d49301f
async2
StefH Jan 6, 2024
a2b3a86
.
StefH Jan 15, 2024
a18e4be
Merge branch 'master' into stef-protobuf
StefH Jan 25, 2024
8fbd630
t
StefH Jan 25, 2024
ddeb8b0
nuget
StefH Jan 27, 2024
5ea7033
<PackageReference Include="ProtoBufJsonConverter" Version="0.2.0-prev…
StefH Feb 3, 2024
d46f1fb
http version
StefH Feb 5, 2024
12daf9c
tests
StefH Feb 7, 2024
a946d6b
.WithHttpVersion("2")
StefH Feb 7, 2024
3acda7c
<PackageReference Include="ProtoBufJsonConverter" Version="0.2.0" />
StefH Feb 9, 2024
e083a9b
HttpVersionParser
StefH Feb 16, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions WireMock.Net Solution.sln
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.NET8",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{7FC0B409-2682-40EE-B3B9-3930D6769D01}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.GrpcClient", "examples\WireMock.Net.Console.GrpcClient\WireMock.Net.Console.GrpcClient.csproj", "{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -262,6 +264,10 @@ Global
{7FC0B409-2682-40EE-B3B9-3930D6769D01}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7FC0B409-2682-40EE-B3B9-3930D6769D01}.Release|Any CPU.Build.0 = Release|Any CPU
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -305,6 +311,7 @@ Global
{941229D6-191B-4B5E-AC81-0905EBF4F19D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{7FC0B409-2682-40EE-B3B9-3930D6769D01} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
Expand Down
2 changes: 2 additions & 0 deletions WireMock.Net Solution.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=XUA/@EntryIndexedValue">XUA</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Flurl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=funcs/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Grpc/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=guidb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Guids/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Heyenrath/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jmes/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=openapi/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pacticipant/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=protobuf/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Raml/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scriban/@EntryIndexedValue">True</s:Boolean>
Expand Down
21 changes: 21 additions & 0 deletions examples/WireMock.Net.Console.GrpcClient/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Greet;
using Grpc.Net.Client;

namespace WireMock.Net.Console.GrpcClient;

internal class Program
{
static async Task Main(string[] args)
{
var channel = GrpcChannel.ForAddress("http://localhost:9093/grpc3", new GrpcChannelOptions
{
Credentials = Grpc.Core.ChannelCredentials.Insecure
});

var client = new Greeter.GreeterClient(channel);

var reply = await client.SayHelloAsync(new HelloRequest { Name = "stef" });

System.Console.WriteLine("Greeting: " + reply.Message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Google.Protobuf" Version="3.25.1" />
<PackageReference Include="Grpc.Net.Client" Version="2.59.0" />
<PackageReference Include="Grpc.Tools" Version="2.60.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Protobuf Include="greet.proto" GrpcServices="Client" />
</ItemGroup>

</Project>
33 changes: 33 additions & 0 deletions examples/WireMock.Net.Console.GrpcClient/greet.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2019 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

package greet;

// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply);
}

// The request message containing the user's name.
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<DefineConstants>$(DefineConstants);GRAPHQL;MIMEKIT</DefineConstants>
<DefineConstants>$(DefineConstants);GRAPHQL;MIMEKIT;PROTOBUF</DefineConstants>
</PropertyGroup>

<ItemGroup>
Expand Down
110 changes: 101 additions & 9 deletions examples/WireMock.Net.Console.Net452.Classic/MainApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ public class Todo

public static class MainApp
{
private const string ProtoDefinition = @"
syntax = ""proto3"";

package greet;

service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
string name = 1;
}

message HelloReply {
string message = 1;
}
";

private const string TestSchema = @"
scalar DateTime
scalar MyCustomScalar
Expand Down Expand Up @@ -115,17 +133,14 @@ public static void Run()
.WithBodyAsJson(rm => todos[int.Parse(rm.Query!["id"].ToString())])
);

var httpClient = server.CreateClient();
//server.Stop();

var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
using var httpAndHttpsWithPort = WireMockServer.Start(new WireMockServerSettings
{
HostingScheme = HostingScheme.HttpAndHttps,
Port = 12399
});
httpAndHttpsWithPort.Stop();

var httpAndHttpsFree = WireMockServer.Start(new WireMockServerSettings
using var httpAndHttpsFree = WireMockServer.Start(new WireMockServerSettings
{
HostingScheme = HostingScheme.HttpAndHttps
});
Expand All @@ -134,11 +149,14 @@ public static void Run()
string url1 = "http://localhost:9091/";
string url2 = "http://localhost:9092/";
string url3 = "https://localhost:9443/";
string urlGrpc = "grpc://localhost:9093/";
string urlGrpcSSL = "grpcs://localhost:9094/";

server = WireMockServer.Start(new WireMockServerSettings
{
// CorsPolicyOptions = CorsPolicyOptions.AllowAll,
AllowCSharpCodeMatcher = true,
Urls = new[] { url1, url2, url3 },
Urls = new[] { url1, url2, url3, urlGrpc, urlGrpcSSL },
StartAdminInterface = true,
ReadStaticMappings = true,
SaveUnmatchedRequests = true,
Expand Down Expand Up @@ -171,17 +189,91 @@ public static void Run()
//server.SetAzureADAuthentication("6c2a4722-f3b9-4970-b8fc-fac41e29stef", "8587fde1-7824-42c7-8592-faf92b04stef");

// server.AllowPartialMapping();

#if PROTOBUF
var protoBufJsonMatcher = new JsonPartialWildcardMatcher(new { name = "*" });
server
.Given(Request.Create()
.UsingPost()
.WithHttpVersion("2")
.WithPath("/grpc/greet.Greeter/SayHello")
.WithBodyAsProtoBuf(ProtoDefinition, "greet.HelloRequest", protoBufJsonMatcher)
)
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf(ProtoDefinition, "greet.HelloReply",
new
{
message = "hello {{request.BodyAsJson.name}}"
}
)
.WithTrailingHeader("grpc-status", "0")
.WithTransformer()
);

server
.Given(Request.Create()
.UsingPost()
.WithHttpVersion("2")
.WithPath("/grpc2/greet.Greeter/SayHello")
.WithBodyAsProtoBuf("greet.HelloRequest", protoBufJsonMatcher)
)
.WithProtoDefinition(ProtoDefinition)
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf("greet.HelloReply",
new
{
message = "hello {{request.BodyAsJson.name}}"
}
)
.WithTrailingHeader("grpc-status", "0")
.WithTransformer()
);

server
.AddProtoDefinition("my-greeter", ProtoDefinition)
.Given(Request.Create()
.UsingPost()
.WithPath("/grpc3/greet.Greeter/SayHello")
.WithBodyAsProtoBuf("greet.HelloRequest", protoBufJsonMatcher)
)
.WithProtoDefinition("my-greeter")
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf("greet.HelloReply",
new
{
message = "hello {{request.BodyAsJson.name}}"
}
)
.WithTrailingHeader("grpc-status", "0")
.WithTransformer()
);
#endif

#if GRAPHQL
var customScalars = new Dictionary<string, Type> { { "MyCustomScalar", typeof(int) } };
server
.Given(Request.Create()
.WithPath("/graphql")
.UsingPost()
.WithGraphQLSchema(TestSchema, customScalars)
.WithBodyAsGraphQL(TestSchema, customScalars)
)
.RespondWith(Response.Create()
.WithBody("GraphQL is ok")
);

//server
// .AddGraphQLSchema("my-graphql", TestSchema, customScalars)
// .Given(Request.Create()
// .WithPath("/graphql2")
// .UsingPost()
// )
// .WithGraphQLSchema("my-graphql")
// .RespondWith(Response.Create()
// .WithBody("GraphQL is ok")
// );
#endif

#if MIMEKIT
Expand Down Expand Up @@ -336,8 +428,8 @@ public static void Run()
Url = "http://localhost:9999",
ReplaceSettings = new ProxyUrlReplaceSettings
{
OldValue = "old",
NewValue = "new"
OldValue = "old",
NewValue = "new"
}
})
);
Expand Down
5 changes: 5 additions & 0 deletions src/WireMock.Net.Abstractions/Admin/Mappings/MappingModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,9 @@ public class MappingModel
/// The probability when this request should be matched. Value is between 0 and 1. [Optional]
/// </summary>
public double? Probability { get; set; }

/// <summary>
/// The Grpc ProtoDefinition which is used for this mapping (request and response). [Optional]
/// </summary>
public string? ProtoDefinition { get; set; }
}
11 changes: 10 additions & 1 deletion src/WireMock.Net.Abstractions/Admin/Mappings/MatcherModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,22 @@ public class MatcherModel
/// ContentTransferEncoding Matcher (base64)
/// </summary>
public MatcherModel? ContentTransferEncodingMatcher { get; set; }
#endregion

#region MimePartMatcher + ProtoBufMatcher
/// <summary>
/// Content Matcher
/// </summary>
public MatcherModel? ContentMatcher { get; set; }
#endregion

#region ProtoBufMatcher
/// <summary>
/// The full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".
/// </summary>
public string? ProtoBufMessageType { get; set; }
#endregion

#region XPathMatcher
/// <summary>
/// Array of namespace prefix and uri. (optional)
Expand All @@ -86,7 +95,7 @@ public class MatcherModel

#region GraphQLMatcher
/// <summary>
/// Mapping of custom GraphQL Scalar name to ClrType. (optional)
/// Mapping of custom GraphQL Scalar name to ClrType. (optional)
/// </summary>
public IDictionary<string, Type>? CustomScalars { get; set; }
#endregion
Expand Down
5 changes: 5 additions & 0 deletions src/WireMock.Net.Abstractions/Admin/Mappings/RequestModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public class RequestModel
/// </summary>
public string[]? Methods { get; set; }

/// <summary>
/// The HTTP Version
/// </summary>
public string? HttpVersion { get; set; }

/// <summary>
/// Reject on match for Methods.
/// </summary>
Expand Down
19 changes: 18 additions & 1 deletion src/WireMock.Net.Abstractions/Admin/Mappings/ResponseModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class ResponseModel
public bool? BodyAsJsonIndented { get; set; }

/// <summary>
/// Gets or sets the body (as bytearray).
/// Gets or sets the body (as byte array).
/// </summary>
public byte[]? BodyAsBytes { get; set; }

Expand Down Expand Up @@ -84,6 +84,11 @@ public class ResponseModel
/// </summary>
public string? HeadersRaw { get; set; }

/// <summary>
/// Gets or sets the Trailing Headers.
/// </summary>
public IDictionary<string, object>? TrailingHeaders { get; set; }

/// <summary>
/// Gets or sets the delay in milliseconds.
/// </summary>
Expand Down Expand Up @@ -123,4 +128,16 @@ public class ResponseModel
/// Gets or sets the WebProxy settings.
/// </summary>
public WebProxyModel? WebProxy { get; set; }

#region ProtoBuf
/// <summary>
/// Gets or sets the proto definition.
/// </summary>
public string? ProtoDefinition { get; set; }

/// <summary>
/// Gets or sets the full type of the protobuf (request/response) message object. Format is "{package-name}.{type-name}".
/// </summary>
public string? ProtoBufMessageType { get; set; }
#endregion
}
Loading
Loading