fullstorydev / grpcurl

Like cURL, but for gRPC: Command-line tool for interacting with gRPC servers
MIT License
10.35k stars 497 forks source link

Server reflection fails when using well-known types (Timestamp) #459

Open ewrogers opened 2 months ago

ewrogers commented 2 months ago

Problem

Server reflection fails when using google.protobuf.* well-known types such as Timestamp or Duration, requiring them to be registered by the server as well for grpcurl to work properly.

Setup

I am using grpc-js with NestJS integration along with @grpc/reflection to register all my .proto files on startup. It works exactly like their example. I also use ts-proto for generating the TypeScript interfaces from my .proto files.

I can grpcurl localhost:5051 list to see my services as expect:

$ grpcurl -plaintext localhost:5051 list
core.Organizations
core.Users

However, once I try to list one that uses a Timestamp message I get the following error:

$ grpcurl -plaintext localhost:5051 list core.Organizations
Failed to list methods for service "core.Organizations": proto: message field "core.Organization.createdAt" cannot resolve type: "*.google.protobuf.Timestamp" not found

Interestingly, if I test it using the -import-path and -proto flags then it works fine!

$ grpcurl -plaintext -import-path ./proto -proto organizations.proto localhost:5051 describe core.Organizations
core.Organizations is a service:
service Organizations {
  rpc GetOrganization ( .core.GetByIdRequest ) returns ( .core.Organization );
  rpc GetOrganizationProfile ( .core.GetByIdRequest ) returns ( .core.OrganizationProfile );
}

$ grpcurl -plaintext -import-path ./proto -proto organizations.proto localhost:5051 describe core.Organization
core.Organization is a message:
message Organization {
  string id = 1;
  string displayName = 2;
  optional string description = 3;
  string type = 4;
  string profileUsername = 5;
  repeated .core.OrganizationProfileInfo profiles = 6;
  .core.OrganizationContact contact = 7;
  .core.OrganizationAddress address = 8;
  optional string countryCode = 9;
  optional string phoneNumber = 10;
  optional string emailAddress = 11;
  .google.protobuf.Timestamp createdAt = 12;
  .google.protobuf.Timestamp updatedAt = 13;
}

I do not have any local copies of the Google well-known type .protos and my import looks like this in organizations.proto:

syntax = "proto3";

import "google/protobuf/timestamp.proto";
import "common.proto";

package core;

Expectation

That grpcurl would have the Google well-known types pre-registered so that server reflection does not need to register them, which seems a bit redundant.

jhump commented 2 months ago

requiring them to be registered by the server as well for grpcurl to work properly

@ewrogers, I'm afraid that this is expected. It seems a little strange to me that registering your service wouldn't automatically register all of its dependencies. I'm not too familiar with grpc-js, but that is certainly how other runtimes work.

The support for builtin versions when using proto source files is only provided to for consistency with protoc: you can generally compile files without providing these "well-known imports" since they are included with the compiler. But they are only there for compiling schemas from source. If you already have the schema in a compiled form (a file descriptor set or server reflection), that other source is authoritative and there should be no need to use them.

BenShelton commented 2 months ago

Just ran into the same issue, again using @grpc/reflection:

> grpcurl -plaintext localhost:5081 describe
Failed to resolve symbol "module.ServiceName": proto: message field "module.MessageName" cannot resolve type: "*.google.protobuf.Timestamp" not found

This is when loading the protoset output as described in https://github.com/fullstorydev/grpcurl?tab=readme-ov-file#protoset-files

Interestingly though I am able to view all the service reflection information using Postman's GRPC Server Reflection feature with no issues, so I'm unclear as to why Postman is able to work with this but grpcurl is not.

ewrogers commented 1 month ago

My guess would be that certain gRPC clients "auto-import" these into their reflection engine.