Grpc: Fix parsing null value for google.protobuf.Timestamp (#1293)

* Add another example for Grpc client + mapping

* <PackageReference Include="ProtoBufJsonConverter" Version="0.9.0" />
This commit is contained in:
Stef Heyenrath
2025-05-10 12:53:18 +02:00
committed by GitHub
parent 56c058fe24
commit 7596967fcc
5 changed files with 240 additions and 64 deletions

View File

@@ -2,22 +2,42 @@
using Greet; using Greet;
using Grpc.Net.Client; using Grpc.Net.Client;
using Policy2;
namespace WireMock.Net.Console.GrpcClient; await TestPolicyAsync();
// await TestGreeterAsync();
return;
internal class Program async Task TestGreeterAsync()
{ {
static async Task Main(string[] args) var channel = GrpcChannel.ForAddress("http://localhost:9093/grpc3", new GrpcChannelOptions
{ {
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" });
Console.WriteLine("Greeting: " + reply.Message);
}
async Task TestPolicyAsync()
{
var channel = GrpcChannel.ForAddress("http://localhost:9093/grpc-policy", new GrpcChannelOptions
{
Credentials = Grpc.Core.ChannelCredentials.Insecure
});
var client = new PolicyService2.PolicyService2Client(channel);
var reply = await client.GetCancellationDetailAsync(new GetCancellationDetailRequest
{
Client = new Client
{ {
Credentials = Grpc.Core.ChannelCredentials.Insecure CorrelationId = "abc"
}); }
});
var client = new Greeter.GreeterClient(channel); Console.WriteLine("PolicyService2:reply.CancellationName " + reply.CancellationName);
var reply = await client.SayHelloAsync(new HelloRequest { Name = "stef" });
System.Console.WriteLine("Greeting: " + reply.Message);
}
} }

View File

@@ -5,6 +5,7 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -18,6 +19,7 @@
<ItemGroup> <ItemGroup>
<Protobuf Include="greet.proto" GrpcServices="Client" /> <Protobuf Include="greet.proto" GrpcServices="Client" />
<Protobuf Include="policy.proto" GrpcServices="Client" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,64 @@
syntax = "proto3";
import "google/protobuf/timestamp.proto";
// option csharp_namespace = "NarrowIntegrationTest.Lookup";
package Policy2;
service PolicyService2 {
rpc GetCancellationDetail (GetCancellationDetailRequest) returns (GetCancellationDetailResponse);
}
message GetCancellationDetailRequest {
Client Client = 1;
LegacyPolicyKey LegacyPolicyKey = 2;
}
message GetCancellationDetailResponse {
ResponseStatus Status = 1;
string CancellationCode = 2;
string CancellationName = 3;
string CancellationDescription = 4;
google.protobuf.Timestamp CancellationEffDate = 5;
string NonRenewalCode = 6;
string NonRenewalName = 7;
string NonRenewalDescription = 8;
google.protobuf.Timestamp NonRenewalEffDate = 9;
google.protobuf.Timestamp LastReinstatementDate = 10;
}
message LegacyPolicyKey {
string Group = 1;
int32 UnitNumber = 2;
int32 Year = 3;
string Suffix = 4;
}
message ResponseStatus {
bool HasErrors = 1;
bool HasWarnings = 2;
repeated string Errors = 3;
repeated string Warnings = 4;
string CorrelationId = 5;
}
message Client {
string CorrelationId = 1;
enum Clients {
Unknown = 0;
QMS = 1;
BillingCenter = 2;
PAS = 3;
Payroll = 4;
Portal = 5;
SFO = 6;
QuoteAndBind = 7;
LegacyConversion = 8;
BindNow = 9;
PaymentPortal = 10 ;
PricingEngine = 11;
}
Clients ClientName = 2;
}

View File

@@ -6,7 +6,6 @@ using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
using WireMock.Logging; using WireMock.Logging;
@@ -45,57 +44,129 @@ namespace WireMock.Net.ConsoleApplication
public static class MainApp public static class MainApp
{ {
private const string ProtoDefinition = @" private const string ProtoDefinitionGreeter =
syntax = ""proto3""; """
syntax = "proto3";
package greet; package greet;
service Greeter { service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply); rpc SayHello (HelloRequest) returns (HelloReply);
} }
message HelloRequest { message HelloRequest {
string name = 1; string name = 1;
} }
message HelloReply { message HelloReply {
string message = 1; string message = 1;
} }
";
private const string TestSchema = @" """;
scalar DateTime
scalar MyCustomScalar
input MessageInput { private const string ProtoDefinitionPolicy =
content: String """
author: String syntax = "proto3";
}
type Message { import "google/protobuf/timestamp.proto";
id: ID!
content: String
author: String
}
type Mutation { // option csharp_namespace = "NarrowIntegrationTest.Lookup";
createMessage(input: MessageInput): Message
createAnotherMessage(x: MyCustomScalar, dt: DateTime): Message
updateMessage(id: ID!, input: MessageInput): Message
}
type Query { package Policy2;
greeting:String
students:[Student]
studentById(id:ID!):Student
}
type Student { service PolicyService2 {
id:ID! rpc GetCancellationDetail (GetCancellationDetailRequest) returns (GetCancellationDetailResponse);
firstName:String }
lastName:String
fullName:String message GetCancellationDetailRequest {
}"; Client Client = 1;
LegacyPolicyKey LegacyPolicyKey = 2;
}
message GetCancellationDetailResponse {
ResponseStatus Status = 1;
string CancellationCode = 2;
string CancellationName = 3;
string CancellationDescription = 4;
google.protobuf.Timestamp CancellationEffDate = 5;
string NonRenewalCode = 6;
string NonRenewalName = 7;
string NonRenewalDescription = 8;
google.protobuf.Timestamp NonRenewalEffDate = 9;
google.protobuf.Timestamp LastReinstatementDate = 10; // Always send the last reinstatement date if present on the policy term.
}
message LegacyPolicyKey {
string Group = 1;
int32 UnitNumber = 2;
int32 Year = 3;
string Suffix = 4;
}
message ResponseStatus {
bool HasErrors = 1;
bool HasWarnings = 2;
repeated string Errors = 3;
repeated string Warnings = 4;
string CorrelationId = 5;
}
message Client {
string CorrelationId = 1;
enum Clients {
Unknown = 0;
QMS = 1;
BillingCenter = 2;
PAS = 3;
Payroll = 4;
Portal = 5;
SFO = 6;
QuoteAndBind = 7;
LegacyConversion = 8;
BindNow = 9;
PaymentPortal = 10 ;
PricingEngine = 11;
}
Clients ClientName = 2;
}
""";
private const string TestSchema =
"""
scalar DateTime
scalar MyCustomScalar
input MessageInput {
content: String
author: String
}
type Message {
id: ID!
content: String
author: String
}
type Mutation {
createMessage(input: MessageInput): Message
createAnotherMessage(x: MyCustomScalar, dt: DateTime): Message
updateMessage(id: ID!, input: MessageInput): Message
}
type Query {
greeting:String
students:[Student]
studentById(id:ID!):Student
}
type Student {
id:ID!
firstName:String
lastName:String
fullName:String
}
""";
private static void RunSse() private static void RunSse()
{ {
@@ -282,11 +353,11 @@ message HelloReply {
.UsingPost() .UsingPost()
.WithHttpVersion("2") .WithHttpVersion("2")
.WithPath("/grpc/greet.Greeter/SayHello") .WithPath("/grpc/greet.Greeter/SayHello")
.WithBodyAsProtoBuf(ProtoDefinition, "greet.HelloRequest", protoBufJsonMatcher) .WithBodyAsProtoBuf(ProtoDefinitionGreeter, "greet.HelloRequest", protoBufJsonMatcher)
) )
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc") .WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf(ProtoDefinition, "greet.HelloReply", .WithBodyAsProtoBuf(ProtoDefinitionGreeter, "greet.HelloReply",
new new
{ {
message = "hello {{request.BodyAsJson.name}}" message = "hello {{request.BodyAsJson.name}}"
@@ -303,7 +374,7 @@ message HelloReply {
.WithPath("/grpc2/greet.Greeter/SayHello") .WithPath("/grpc2/greet.Greeter/SayHello")
.WithBodyAsProtoBuf("greet.HelloRequest", protoBufJsonMatcher) .WithBodyAsProtoBuf("greet.HelloRequest", protoBufJsonMatcher)
) )
.WithProtoDefinition(ProtoDefinition) .WithProtoDefinition(ProtoDefinitionGreeter)
.RespondWith(Response.Create() .RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc") .WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf("greet.HelloReply", .WithBodyAsProtoBuf("greet.HelloReply",
@@ -317,7 +388,7 @@ message HelloReply {
); );
server server
.AddProtoDefinition("my-greeter", ProtoDefinition) .AddProtoDefinition("my-greeter", ProtoDefinitionGreeter)
.Given(Request.Create() .Given(Request.Create()
.UsingPost() .UsingPost()
.WithPath("/grpc3/greet.Greeter/SayHello") .WithPath("/grpc3/greet.Greeter/SayHello")
@@ -335,6 +406,25 @@ message HelloReply {
.WithTrailingHeader("grpc-status", "0") .WithTrailingHeader("grpc-status", "0")
.WithTransformer() .WithTransformer()
); );
var protoBufJsonMatcherForGetCancellationDetailRequest = new JsonPartialWildcardMatcher("{\"Client\":{\"CorrelationId\":\"*\"}}", false, true);
var getCancellationDetailResponseAsJsonObject = JsonConvert.DeserializeObject(
"""{"Status":{"HasErrors":false,"HasWarnings":false,"Errors":[],"Warnings":[],"CorrelationId":"b8ad0d04-ed2f-42e1-ac85-339d91dc9855"},"CancellationCode":"cc123","CancellationName":"cn123","CancellationDescription":"","CancellationEffDate":null,"NonRenewalCode":"","NonRenewalName":"","NonRenewalDescription":"","NonRenewalEffDate":null,"LastReinstatementDate":null}"""
)!;
server
.AddProtoDefinition("grpc-policy", ProtoDefinitionPolicy)
.Given(Request.Create()
.UsingPost()
.WithPath("/Policy2.PolicyService2/GetCancellationDetail")
.WithBodyAsProtoBuf("Policy2.GetCancellationDetailRequest", protoBufJsonMatcherForGetCancellationDetailRequest)
)
.WithProtoDefinition("grpc-policy")
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf("Policy2.GetCancellationDetailResponse", getCancellationDetailResponseAsJsonObject)
.WithTrailingHeader("grpc-status", "0")
.WithTransformer()
);
#endif #endif
#if GRAPHQL #if GRAPHQL
@@ -596,9 +686,9 @@ message HelloReply {
.WithHeader("Content-Type", "application/json") .WithHeader("Content-Type", "application/json")
.WithBodyAsJson(new { result = "data:headers posted with 201" })); .WithBodyAsJson(new { result = "data:headers posted with 201" }));
if (!System.IO.File.Exists(@"c:\temp\x.json")) if (!File.Exists(@"c:\temp\x.json"))
{ {
System.IO.File.WriteAllText(@"c:\temp\x.json", "{ \"hello\": \"world\", \"answer\": 42 }"); File.WriteAllText(@"c:\temp\x.json", "{ \"hello\": \"world\", \"answer\": 42 }");
} }
server server
@@ -925,7 +1015,7 @@ message HelloReply {
BodyData = new BodyData BodyData = new BodyData
{ {
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers), BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
DetectedBodyType = Types.BodyType.String, DetectedBodyType = BodyType.String,
}, },
StatusCode = code StatusCode = code
}; };
@@ -941,7 +1031,7 @@ message HelloReply {
return new ResponseMessage return new ResponseMessage
{ {
BodyData = new BodyData { BodyAsString = "random200or505async:" + code, DetectedBodyType = Types.BodyType.String }, BodyData = new BodyData { BodyAsString = "random200or505async:" + code, DetectedBodyType = BodyType.String },
StatusCode = code StatusCode = code
}; };
})); }));

View File

@@ -150,7 +150,7 @@
<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="GraphQL.NewtonsoftJson" Version="8.2.1" /> <PackageReference Include="GraphQL.NewtonsoftJson" Version="8.2.1" />
<PackageReference Include="MimeKitLite" Version="4.1.0.1" /> <PackageReference Include="MimeKitLite" Version="4.1.0.1" />
<PackageReference Include="ProtoBufJsonConverter" Version="0.8.0" /> <PackageReference Include="ProtoBufJsonConverter" Version="0.9.0" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' "> <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">