Skip to content
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

API improvements for dynamic usages #117

Merged
merged 10 commits into from
Mar 8, 2024

Conversation

jhump
Copy link
Member

@jhump jhump commented Jan 31, 2024

In light of recent experience report using this module with a dynamic schema, this PR proposes several changes to make such work easier. It also puts additional validation into initialization, so we can fail faster and (hopefully) with better error messages.

  1. Don't make user provide a type resolver. If user does not provide a type resolver with a custom schema, use a smarter default that can use dynamic message types and then fallback to the global registry for anything unknown (including both extensions and message types).
  2. Validate during initialization that the given type resolver can actually resolve all of the methods' request and response types.

There is also a small change in here that is not strictly related to dynamic usage but is instead related to proxying an RPC protocol to REST: if a request comes in via RPC protocol for a method that has no HTTP mapping and the target protocol is REST, this will now return a "404 not found" status code. It previously could result in a nil-dereference panic.

@jhump jhump force-pushed the jh/api-improvements-for-dynamic-usages branch from a8f32f0 to 46b21e0 Compare January 31, 2024 04:12
@jhump jhump requested a review from emcfarlane January 31, 2024 04:12
Copy link
Collaborator

@emcfarlane emcfarlane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused on the choice to use the Option. I think this can be done within the register method to fetch the types and error if not available. Would this restrict previous behaviour of using vanguard-go to map rpc protocols (e.g. connectrpc to grpc) without transforming the body?

}
}
if numSupportedMethods == 0 {
return nil, fmt.Errorf("service %s only supports REST target protocol but has no methods with HTTP rules", svcDesc.FullName())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should instead support a default mapping of POST to <service>/<method>. See https://cloud.google.com/endpoints/docs/grpc/transcoding#where_to_configure_transcoding

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you just mean to use the Connect protocol in this case? If not, that's not a thing that actually works today (and smells like an enhancement beyond the scope of this PR).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not Connect but effectively. Think it should be valid to have zero annotations and point towards a REST service, so don't want the handler to error here (not to implement this new behaviour in this PR).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think it should be valid to have zero annotations and point towards a REST service, so don't want the handler to error here

But it's not actually valid yet. As a service to users, I think we should do as much as possible up-front to fail fast and give good error messages. That removes footguns from users and gives them clear information about why things aren't working.

You can always remove this check later, Once this repo actually supports what you're talking about. Though some sort of check seems useful to keep even then, like in case the service only has streaming endpoints (that don't use HttpBody).

vanguard.go Outdated Show resolved Hide resolved
transcoder.go Show resolved Hide resolved
transcoder.go Outdated Show resolved Hide resolved
vanguard.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@emcfarlane emcfarlane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could it instead of creating the fallback resolver instead create the type from the provided resolver or global registry and then fail if not able?

transcoder.go Outdated Show resolved Hide resolved
transcoder.go Outdated Show resolved Hide resolved
@emcfarlane
Copy link
Collaborator

emcfarlane commented Feb 2, 2024

I think you can use dynamicpb.NewMessageType if the resolver fails. e.g.:

func resolveType(messageDesc protoreflect.MessageDescriptor, resolver TypeResolver) (protoreflect.MessageType, error) {
	dynamicOk := resolver == nil
	if dynamicOk {
		resolver = protoregistry.GlobalTypes
	}
	messageType, err := resolver.FindMessageByName(messageDesc.FullName())
	if err != nil {
		if !errors.Is(err, protoregistry.NotFound) || !dynamicOk {
			return nil, fmt.Errorf("failed to resolve message type %s: %w", messageDesc.FullName(), err)
		}
		return dynamicpb.NewMessageType(messageDesc), nil
	}
	return messageType, nil
}

func (t *Transcoder) registerMethod(...) {
        // ...
	requestType, err := resolveType(methodDesc.Input(), opts.resolver)
	if err != nil {
		return fmt.Errorf("failed to resolve input type for method %s: %w", methodDesc.FullName(), err)
	}
	responseType, err := resolveType(methodDesc.Output(), opts.resolver)
	if err != nil {
		return fmt.Errorf("failed to resolve output type for method %s: %w", methodDesc.FullName(), err)
	}
	// ...
}

@jhump jhump force-pushed the jh/api-improvements-for-dynamic-usages branch from 864eb80 to bad91a5 Compare February 5, 2024 18:43
vanguard.go Outdated Show resolved Hide resolved
transcoder.go Outdated Show resolved Hide resolved
transcoder.go Outdated Show resolved Hide resolved
type_resolver_test.go Outdated Show resolved Hide resolved
Copy link
Collaborator

@emcfarlane emcfarlane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

@jhump jhump merged commit 2ccfb59 into main Mar 8, 2024
5 checks passed
@jhump jhump deleted the jh/api-improvements-for-dynamic-usages branch March 8, 2024 20:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants