Skip to content

Add middleware/interceptors #41

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

Merged
merged 1 commit into from
Feb 21, 2025
Merged

Add middleware/interceptors #41

merged 1 commit into from
Feb 21, 2025

Conversation

Quinn-With-Two-Ns
Copy link
Contributor

@Quinn-With-Two-Ns Quinn-With-Two-Ns commented Feb 5, 2025

Taken over by @bergundy (thanks @Quinn-With-Two-Ns for kicking this off).

💥 BREAKING CHANGE 💥 The experimental WithHandlerContext method signature was changed to accept the associated HandlerInfo.

Do not merge until the Temporal SDK work that depends on this is ready for review.


type OperationInvoker[I, O any] interface {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we should call this OperationHandler since it similar to the Handler interface but for a single Operation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh yeah we have the name OperationHandler available in Go, that is a better name

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

// CancelOperation implements Handler.
func (r *registryHandler) CancelOperation(ctx context.Context, service, operation string, token string, options CancelOperationOptions) error {
s, ok := r.services[service]
func (r *registryHandler) getOperation(options OperationOptions) (OperationInvoker[any, any], error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Go doesn't like the get prefix for getters.

Suggested change
func (r *registryHandler) getOperation(options OperationOptions) (OperationInvoker[any, any], error) {
func (r *registryHandler) operationHandler(options OperationOptions) (OperationHandler[any, any], error) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

// OperationOptions contains the general options for an operation, across different handlers.
//
// NOTE: Experimental
type OperationOptions struct {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not loving the name Options but I can see that it's consistent across all methods and every method has its own Options struct.

Maybe we should call this MiddlewareOptions or MiddlewareInfo?

Copy link
Contributor Author

@Quinn-With-Two-Ns Quinn-With-Two-Ns Feb 5, 2025

Choose a reason for hiding this comment

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

Alternatively we could call this something like CommonOperationOptions and embedded it in every other operation option struct

Copy link
Contributor

Choose a reason for hiding this comment

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

No need to embed it, the options are used both in the client and the server and this information would be redundant.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another option is HandlerInfo, does that seem better to you?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm it is OK

Comment on lines 22 to 25
// ServiceName is the name of the service that contains the operation.
ServiceName string
// OperationName is the name of the operation.
OperationName string
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can drop the Name suffix here but don't have a strong opinion.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Contributor

@bergundy bergundy left a comment

Choose a reason for hiding this comment

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

LGTM, left a couple of minor comments.

return h.Cancel(ctx, operationID, options)
}

// operationHandlerInfo implements Handler.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: fix the docstring?

return h.GetInfo(ctx, operationID, options)
}

// operationHandlerResult implements Handler.
Copy link
Contributor

Choose a reason for hiding this comment

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

and here.

// If the middleware wants to stop the chain before any handler is called, it can return an error.
//
// NOTE: Experimental
type MiddlewareFunc func(HandlerInfo, OperationHandler[any, any]) (OperationHandler[any, any], error)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm wondering if we should just put HandlerInfo on the Go context, it could be useful outside of middleware context.

You also will want to expose the Go context here to allow middleware to call functions that require a context.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm, so the HandlerInfo contains the headers that the respective handler inputs already have. Would we be OK moving them out of the handler input into the HandlerInfo ?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm on the fence here. I kept going back and forth between headers in the context and explicit argument and that's where I landed. We should discuss this offline before we stabilize the API.

return nil, HandlerErrorf(HandlerErrorTypeNotFound, "operation %q not found", operation)
}

func (r *rootOperationHandler) GetInfo(ctx context.Context, token string, options GetOperationInfoOptions) (*OperationInfo, error) {
// NOTE: We could avoid reflection here if we put the Cancel method on RegisterableOperation but it doesn't seem
Copy link
Contributor

Choose a reason for hiding this comment

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

Noticed my own copy pasta.

Suggested change
// NOTE: We could avoid reflection here if we put the Cancel method on RegisterableOperation but it doesn't seem
// NOTE: We could avoid reflection here if we put the GetInfo method on RegisterableOperation but it doesn't seem

@bergundy bergundy force-pushed the interceptors branch 2 times, most recently from ef1bfcb to 62c5ffc Compare February 19, 2025 19:57
@bergundy bergundy marked this pull request as ready for review February 19, 2025 20:07
Copy link
Contributor Author

@Quinn-With-Two-Ns Quinn-With-Two-Ns left a comment

Choose a reason for hiding this comment

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

I can' approve because this is my own PR but LGTM

@bergundy
Copy link
Contributor

Verified with the Temporal SDK, good to merge now.

@bergundy bergundy merged commit cd7e067 into main Feb 21, 2025
3 checks passed
@bergundy bergundy deleted the interceptors branch February 21, 2025 21:21
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