translation

package
v0.6.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 11, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package translation rewrites one gRPC method call into another on the hop to the upstream.

A Translation pairs an inbound method with the upstream method that stands in for it, plus the conversions between their request and response types. A Registry holds the set of them, and DialOptions installs them on a connection: a call whose method is registered is converted, sent under the upstream method, and converted back before the caller sees a reply. Everything else is passed straight through.

It lives under internal/cloud because Temporal Cloud is what needs it - Cloud serves some methods only from its control plane, under another service - and because "translation" unqualified already means namespace translation elsewhere in the proxy. That one rewrites names inside a message and leaves the method alone; this one replaces the call. The two compose: install this innermost, so namespace translation, payload codecs, and the reflective forwarder all keep seeing the method and message types the caller asked for.

The one translation the proxy ships is WorkflowService.ListNamespaces onto CloudService.GetNamespaces, since Temporal Cloud serves the namespace list from its own control plane rather than from a frontend.

The mechanism itself knows nothing about Cloud, and is kept separate from the parent package so that using [cloud.IsEndpoint] or [cloud.ValidateNamespace] does not pull gRPC and protobuf into a caller that only wanted to check a name.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DialOptions

func DialOptions(r *Registry, opts ...Option) []grpc.DialOption

DialOptions returns the dial options that install method translation on an outbound connection. Callers fold them into the dial options for the upstream connection, last, so translation is the innermost interceptor: every other interceptor on the chain then sees the method and message types the caller asked for rather than the substitute sent upstream.

The result is a slice though it holds a single option today. A Translation substitutes a unary method, so a unary interceptor is all there is to install; translating a streaming method would add a stream interceptor beside it, the way the namespace and Cloud-namespace helpers in internal/proxy already pair the two. Keeping the slice means that arrives without changing this signature or the call sites, which already spread the result.

Types

type Option

type Option func(*options)

Option configures the interceptor DialOptions installs.

func Via

Via sends a translated call over cc instead of continuing down the chain to the connection the interceptor is installed on.

The upstream method belongs to a different service, which the connection that received the call does not serve: the caller asked a Temporal Service for ListNamespaces, and only Temporal Cloud's control plane can answer it. Where that service lives is as fixed as the conversions themselves, so the translation carries the connection rather than the request being routed to it, and a request reaching any upstream is answered the same way.

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry is the set of translations an interceptor consults, keyed by the inbound full method. It is fixed once built and safe for concurrent use; a nil Registry translates nothing, so a caller with none to install can pass one straight through.

func Default

func Default() (*Registry, error)

Default returns the registry of translations the proxy ships with, built once and shared. It fails only if a compiled-in mapping is malformed, which is a build-time mistake rather than anything configuration can cause; callers still propagate it so the process refuses to start rather than silently forwarding a method it was meant to translate.

func NewRegistry

func NewRegistry(ts ...*Translation) (*Registry, error)

NewRegistry indexes ts by the method each translates from. It rejects a nil entry, a method name that is not a gRPC full method, a translation onto itself, and two translations of the same inbound method, so a mapping mistake surfaces at construction rather than on the first request that hits it.

func (*Registry) Lookup

func (r *Registry) Lookup(fullMethod string) (*Translation, bool)

Lookup returns the translation registered for fullMethod, reporting false when there is none and the call should be forwarded unchanged. A nil Registry, or one built from no translations, always reports false.

func (*Registry) Methods

func (r *Registry) Methods() []string

Methods returns the inbound methods the registry translates, in canonical "/pkg.Service/Method" form. Order is not significant.

type Translation

type Translation struct {
	// contains filtered or unexported fields
}

Translation stands one unary method in for another: it converts the caller's request into the upstream's request type, allocates the reply the upstream will fill, and folds that reply back into the message type the caller is waiting on. Build one with Adapt rather than by hand, so the conversions are written against concrete message types. A Translation holds no per-call state and is safe for concurrent use.

func Adapt

func Adapt[Req, UpReq, UpResp, Resp proto.Message](
	from, to string,
	request func(Req) (UpReq, error),
	response func(Req, UpResp, Resp) error,
) *Translation

Adapt builds a Translation from from onto to out of two typed conversions. request converts the caller's request into the upstream's; response folds the upstream's reply into the caller's, and is given the original request too, since a field the upstream has no equivalent for (a filter, say) can only be honoured on the way back. Both message types are inferred from the conversions, so a mapping never asserts on proto.Message itself.

func (*Translation) From

func (t *Translation) From() string

From is the inbound method this translation replaces.

func (*Translation) To

func (t *Translation) To() string

To is the upstream method that stands in for it.

func (*Translation) WithHeader

func (t *Translation) WithHeader(key, value string) *Translation

WithHeader stamps key: value on the substituted call and returns t, so a mapping can declare the dialect the upstream method needs alongside the conversions themselves. It replaces any value the caller sent rather than adding to it: the caller did not ask for this upstream method and cannot know what its API expects, so its own header is not intent worth preserving. A caller invoking that API directly is forwarded untranslated and keeps its header.

Headers travel only on a call this translation substituted; a method the registry does not translate is untouched.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL