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,13 +2,14 @@
using Greet;
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
{
Credentials = Grpc.Core.ChannelCredentials.Insecure
@@ -18,6 +19,25 @@ internal class Program
var reply = await client.SayHelloAsync(new HelloRequest { Name = "stef" });
System.Console.WriteLine("Greeting: " + reply.Message);
}
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
{
CorrelationId = "abc"
}
});
Console.WriteLine("PolicyService2:reply.CancellationName " + reply.CancellationName);
}

View File

@@ -5,6 +5,7 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
@@ -18,6 +19,7 @@
<ItemGroup>
<Protobuf Include="greet.proto" GrpcServices="Client" />
<Protobuf Include="policy.proto" GrpcServices="Client" />
</ItemGroup>
</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.Linq;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Newtonsoft.Json;
using WireMock.Logging;
@@ -45,25 +44,96 @@ namespace WireMock.Net.ConsoleApplication
public static class MainApp
{
private const string ProtoDefinition = @"
syntax = ""proto3"";
private const string ProtoDefinitionGreeter =
"""
syntax = "proto3";
package greet;
package greet;
service Greeter {
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
}
message HelloRequest {
message HelloRequest {
string name = 1;
}
}
message HelloReply {
message HelloReply {
string message = 1;
}
";
}
private const string TestSchema = @"
""";
private const string ProtoDefinitionPolicy =
"""
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; // 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
@@ -95,7 +165,8 @@ message HelloReply {
firstName:String
lastName:String
fullName:String
}";
}
""";
private static void RunSse()
{
@@ -282,11 +353,11 @@ message HelloReply {
.UsingPost()
.WithHttpVersion("2")
.WithPath("/grpc/greet.Greeter/SayHello")
.WithBodyAsProtoBuf(ProtoDefinition, "greet.HelloRequest", protoBufJsonMatcher)
.WithBodyAsProtoBuf(ProtoDefinitionGreeter, "greet.HelloRequest", protoBufJsonMatcher)
)
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf(ProtoDefinition, "greet.HelloReply",
.WithBodyAsProtoBuf(ProtoDefinitionGreeter, "greet.HelloReply",
new
{
message = "hello {{request.BodyAsJson.name}}"
@@ -303,7 +374,7 @@ message HelloReply {
.WithPath("/grpc2/greet.Greeter/SayHello")
.WithBodyAsProtoBuf("greet.HelloRequest", protoBufJsonMatcher)
)
.WithProtoDefinition(ProtoDefinition)
.WithProtoDefinition(ProtoDefinitionGreeter)
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithBodyAsProtoBuf("greet.HelloReply",
@@ -317,7 +388,7 @@ message HelloReply {
);
server
.AddProtoDefinition("my-greeter", ProtoDefinition)
.AddProtoDefinition("my-greeter", ProtoDefinitionGreeter)
.Given(Request.Create()
.UsingPost()
.WithPath("/grpc3/greet.Greeter/SayHello")
@@ -335,6 +406,25 @@ message HelloReply {
.WithTrailingHeader("grpc-status", "0")
.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
#if GRAPHQL
@@ -596,9 +686,9 @@ message HelloReply {
.WithHeader("Content-Type", "application/json")
.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
@@ -925,7 +1015,7 @@ message HelloReply {
BodyData = new BodyData
{
BodyAsString = "random200or505:" + code + ", HeadersFromRequest = " + string.Join(",", request.Headers),
DetectedBodyType = Types.BodyType.String,
DetectedBodyType = BodyType.String,
},
StatusCode = code
};
@@ -941,7 +1031,7 @@ message HelloReply {
return new ResponseMessage
{
BodyData = new BodyData { BodyAsString = "random200or505async:" + code, DetectedBodyType = Types.BodyType.String },
BodyData = new BodyData { BodyAsString = "random200or505async:" + code, DetectedBodyType = BodyType.String },
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'">
<PackageReference Include="GraphQL.NewtonsoftJson" Version="8.2.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 Condition=" '$(TargetFramework)' == 'netcoreapp3.1' ">