Open ArcaneTSGK opened 1 month ago
@ArcaneTSGK Thanks for noticing this. This is a new scenario I never tested. Probably I need to fix this here: https://github.com/StefH/ProtoBufJsonConverter/issues/15
I'll keep you informed on the progress...
@ArcaneTSGK I was thinking, are there any other predefined types in google?
And what should actually be the behavior of WireMock.Net if a google.protobuf.Empty is a return value? Is this just an empty response?
@StefH There are a number of them yes,
For Empty return value it's Empty {}
it's because protobuf does not allow you to request/response with null / no body like a typical REST API would.
Converting Empty to JSON would represent an empty JSON object {}
Here are all of Googles Well Known types:
https://protobuf.dev/reference/protobuf/google.protobuf/
https://protobuf.dev/reference/protobuf/google.protobuf/#empty
Returning any of the scalar types in protobuf within a custom response that maps to a C# type is fine except for 'Enum'. I've not tried the 'Any' well-known type as my application doesn't have use for it, but I imagine that one might cause some issues, also when working with nullable types you use 'OneOf', again I haven't had to use these so I do not know if they'll work, but this is what a proto file would look like using those:
Usage of 'Any'
syntax = "proto3";
package tutorial;
import "include/google/protobuf/any.proto"
message Animal {
string name=1;
int32 age=2;
google.protobuf.Any care_giver=3;
}
message Owner {
int32 id=1;
string first_name=2;
string last_name=3;
}
message Foster {
int32 id=1;
string address=2;
}
In that example a care give can be any pre-defined message in the proto, which could be Owner or the Foster, so that might be a test case for Wiremock to see if Any maps back, and lastly OneOf for nullables where you'd need to be able to allow a null return in the response:
And this is OneOf
import "google/protobuf/struct.proto";
package tutorial;
option csharp_namespace = "MyExample.Dog";
message Dog {
google.protobuf.Int32Value id=1;
string name=2;
NullableField profile_picture=3;
}
message NullableField {
oneof kind {
google.protobuf.NullValue null=1;
google.protobuf.StringValue value=2;
}
}
Currently I only have an easy way to support:
@ArcaneTSGK If you have time, you can use this preview version: 1.5.62-ci-19066
@ArcaneTSGK Can you please test preview 1.5.62-ci-19067 , this version supports:
oneof
@ArcaneTSGK Can you please test preview 1.5.62-ci-19067 , this version supports:
- google.protobuf.Empty
- google.protobuf.Duration
- google.protobuf.Timestamp
- google/protobuf/wrappers
- google/protobuf/any
- google/protobuf/struct
- Enum
oneof
I will have some time tomorrow to take a look, I'll let you know my results, thanks @StefH
@ArcaneTSGK Did you have some time to check?
@ArcaneTSGK Did you have some time to check?
Hi @StefH sorry for the delay,
Unfortunately, I can't install your prerelease version 1.5.62-ci-19067 as it does not resolve, are your pre-releases publicly available?
I only have the option (with prereleases enabled) to install 1.5.62 or 1.60 / 1.61
Preview versions are defined on MyGet. See https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions
But it could be that that specific version is automatically deleted because only x versions are kept on MyGet.
I will take a look and maybe I need to build a new preview.
Preview versions are defined on MyGet. See https://github.com/WireMock-Net/WireMock.Net/wiki/MyGet-preview-versions
But it could be that that specific version is automatically deleted because only x versions are kept on MyGet.
I will take a look and maybe I need to build a new preview.
No worries, I've added MyGet as a feed and will await the specific version with the gRPC fix to test
@ArcaneTSGK
Preview version on MyGet for this fix should be : 1.6.1-ci-19109
@StefH
So I attempted to use this build and I get the following exception:
(Status(StatusCode="Internal", Detail="Failed to deserialize response message.")
The proto file looks like this
syntax = "proto3";
import "google/protobuf/empty.proto";
package communication.api.v1;
message SendEmailRequest {
string email_address = 1;
}
service CommunicationService {
rpc SendEmail(SendEmailRequest) returns (google.protobuf.Empty);
}
The test is setup as follows:
public void SetupSendEmail()
{
_server!.AddProtoDefinition(ProtoDefinitionId, CommunicationProtoFile)
.Given(Request.Create()
.UsingPost()
.WithHttpVersion("2")
.WithPath("/communication.api.v1.CommunicationService/SendEmail")
.WithBodyAsProtoBuf("communication.api.v1.SendEmailRequest"))
.WithProtoDefinition(ProtoDefinitionId)
.RespondWith(Response.Create()
.WithHeader("Content-Type", "application/grpc")
.WithTrailingHeader("grpc-status", "0")
.WithBodyAsProtoBuf(
"google.protobuf.Empty",
new { })
.WithTransformer());
}
I did try Duration and Timestamp aswell but those were returning unimplemented for me when I was using google.protobuf.Timestamp
and google.protobuf.Duration
respectively as return types
Let me know if I'm missing anything
I also tried the fully qualified name of Google.Protobuf.WellKnownTypes.Empty as the message type to no avail.
Thanks
Strange, I did add a unit test https://github.com/WireMock-Net/WireMock.Net/blob/bug/1144-protobuf/test/WireMock.Net.Tests/Grpc/WireMockServerTests.Grpc.cs#L104
proto
private const string ProtoDefinitionWithWellKnownTypes = @"
syntax = ""proto3"";
import ""google/protobuf/empty.proto"";
import ""google/protobuf/timestamp.proto"";
import ""google/protobuf/duration.proto"";
service Greeter {
rpc SayNothing (google.protobuf.Empty) returns (google.protobuf.Empty);
}
message MyMessageTimestamp {
google.protobuf.Timestamp ts = 1;
}
message MyMessageDuration {
google.protobuf.Duration du = 1;
}
";
Unit test code:
// Arrange
var bytes = Convert.FromBase64String("CgRzdGVm");
using var server = WireMockServer.Start();
server
.Given(Request.Create()
.UsingPost()
.WithPath("/grpc/Greeter/SayNothing")
.WithBody(new NotNullOrEmptyMatcher())
)
.RespondWith(Response.Create()
.WithBodyAsProtoBuf(ProtoDefinitionWithWellKnownTypes, "google.protobuf.Empty",
new { }
)
.WithTrailingHeader("grpc-status", "0")
.WithTransformer()
);
// Act
var protoBuf = new ByteArrayContent(bytes);
protoBuf.Headers.ContentType = new MediaTypeHeaderValue("application/grpc-web");
var client = server.CreateClient();
var response = await client.PostAsync("/grpc/Greeter/SayNothing", protoBuf);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var responseBytes = await response.Content.ReadAsByteArrayAsync();
Convert.ToBase64String(responseBytes).Should().Be("");
server.Stop();
What is the difference?
I will have to provide a minimal reproduction solution when I have time, I've tried a few different things and for .Empty I always get the unable to deserialize response, and for duration/timestamp I'm getting the Unimplemented error.
I'll provide a link to the repository and throw something together by the end of the week
Using https://protobuf.heyenrath.nl/ with your message and google.protobuf.Empty
does return an empty byte array
When writing a response builder with
google.protobuf.Empty
the mock gRPC client will returnStatusCode=Unimplemented
.Expected behavior:
It should be possible to use googles 'WellKnown' types as the protobuf body in the response builder.
Test to reproduce
I have tried with and without the
.WithTransformer()
, I have also tried withnew Empty()
new Empty {}
andnew()
but none of those match.Other related info
I am able to get the tests to work if the response is a type that I create in my proto, for example lets say that the HelloReply is in the proto file with a
message HelloReply { string reply_message = 1; }
I have also tried creating my own
message EmptyResponse { }
and this also did not workIt only works when the response contains something like
I have also tried creating my own
message EmptyResponse { string status = 1; }
Please can someone either point me in the direction of what I am doing wrong to match an empty response, or confirm that this is a bug
Thanks