-
Notifications
You must be signed in to change notification settings - Fork 349
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
feat: Add ability to generate a generic Rpc
interface and client implementation
#1061
base: main
Are you sure you want to change the base?
Conversation
TODO: * Write tests for async iterable and before/after * Get generic client reaching feature parity with opinionated client for with error handler option
Rpc
interface and client implementationRpc
interface and client implementation
Rpc
interface and client implementationRpc
interface and client implementation
Rpc
interface and client implementationRpc
interface and client implementation
@stephenh Any chance you would be open to reviewing this change? Happy to make any adjustments necessary. |
@stephenh Sorry to keep prodding, but this is a change that we would still really like to contribute if you're open to it. LMK if there's anything we can do to make that possible. Thanks so much! |
Hi @corwinsheahan-wf apologies for not replying sooner; I'd skimmed the PR a few times and couldn't quite succinctly articulate my thoughts/am not super-close to the use case, so just had it easy to keep procrastinating writing up a reply. I think my two main questions, just to understand better, are looking at this interface:
Given they a) are just typing the I.e. maybe just: request(
service: string,
method: string,
request: object,
reqType: MessageType,
respType: MessageType,
): Promise<object>; Hm, I suppose the I dunno, I am probably too tempted to do fancy things, but I'd try something thing: request<ReqType extends MessageType, ResType extends MessageType>(
service: string,
method: string,
request: ReturnType<ReqType["create"]>,
reqType: ReqType,
respType: ResType,
): Promise<ReturnType<ResType["create"]>>; After adding a
And then letting the generics get inferred:
I'd also probably reorder the parameters, just to have "the most static" ones come first: request<ReqType extends MessageType, ResType extends MessageType>(
reqType: ReqType,
respType: ResType,
service: string,
method: string,
request: ReturnType<ReqType["create"]>,
): Promise<ReturnType<ResType["create"]>>; Does this updated
It makes sense there is some impl-specific details in there, but specifically I'd assumed that typically it would just end up calling ...oh wait, I was thinking of the Okay, that makes more sense then! |
No worries @stephenh, I appreciate the response! Apologies for my own delay in getting back to you, I've been out sick.
At first pass it looks like an improvement for sure 👍. I'll work on the implementation and raise any issues should they come up.
Yep, our impl of As far as our use case, it's a bit unique. We are using protobuf to define interactions between microfrontends, so we're technically not even leaving the browser for our |
@stephenh I took a look at what an implementation of the below proposed typing of request<ReqType extends MessageType, ResType extends MessageType>(
reqType: ReqType,
respType: ResType,
service: string,
method: string,
request: ReturnType<ReqType["create"]>,
): Promise<ReturnType<ResType["create"]>>; Unfortunately I'm not able to get something that satisfies the typing on the return type in the implementation. Here is a simple example: Another option is to use interface Rpc {
request<ReqType extends MessageType, ResType extends MessageType>(
service: string,
method: string,
request: UnknownMessage,
reqType: ReqType,
respType: ResType,
): Promise<UnknownMessage>;
} This allows the For the response, however, // For service method `foo` which takes a `FooRequest` and returns a `FooResponse`:
async foo(request: FooRequest): Promise<FooResponse> {
return await this.rpc.request(
this.service,
"Foo",
request,
FooRequest,
FooResponse,
) as FooResponse;
} Without the cast, we get a type error if Do you have an opinion on an approach? I think I lean towards typing the request/response as |
@stephenh Please let me know your thoughts and any changes we can make to get this PR through. Thanks! |
@stephenh Is there anything we can do to move this along? Sorry to keep pestering 😬 |
This PR introduces a way to generate a generic
Rpc
interface, as well as client implementations to match. The inspiration for this change comes from this comment.This change allows the implementer of the
Rpc
interface to serialize the data sent over the wire however they wish.Summary of changes:
ts_proto_opts=outputClientImpl="generic"
is set, the plugin outputs anRpc
interface that looks like this for the basic unaryrequest
(other methods on Rpc are similarly updated):ts_proto_opts=outputClientImpl="generic"
setsoutputTypeRegistry=true
, since theMessageType
gives us a super interface for the serialization instances.ts_proto_opts=outputClientImpl="generic"
is set, outputs a generated client implementation which uses the generic Rpc interface and support the existing options of the client implementation (rpc before/after, handleError, asyncIterable, batching).