-
Notifications
You must be signed in to change notification settings - Fork 498
feat: [Go] added middleware framework for actions and models #429
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
Open
pavelgj
wants to merge
13
commits into
main
Choose a base branch
from
pj/goMiddleware
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
0beba4f
feat: [Go] added middleware fremework for actions and models
pavelgj 3375acb
fix tests
pavelgj 374d2ba
typos
pavelgj 0f10f32
cleanup middlware interface
pavelgj a335b0a
test that makes sense
pavelgj be28596
more cleanup
pavelgj 8f0ba3b
format
pavelgj 3fb3ab2
more test cleanup
pavelgj bbd3967
Middlware
pavelgj 3e65eae
docs fix
pavelgj 3c7afb2
feedback
pavelgj d26b3f1
fix
pavelgj 22cd0a6
simplify
pavelgj File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,36 @@ import ( | |
"github.com/invopop/jsonschema" | ||
) | ||
|
||
// Middleware is a function that takes in an action handler function and | ||
// returns a new handler function that might be changing input/output in | ||
// some way. | ||
// | ||
// Middleware functions can: | ||
// - execute arbitrary code; | ||
// - change the request and response; | ||
// - terminate response by returning a response (or error); | ||
// - call the next middleware function. | ||
type Middleware[I, O, S any] func(Func[I, O, S]) Func[I, O, S] | ||
|
||
// Middlewares returns an array of middlewares that are passes in as an argument. | ||
// core.Middlewares(apple, banana) is identical to []core.Middleware[InputType, OutputType]{apple, banana} | ||
func Middlewares[I, O, S any](ms ...Middleware[I, O, S]) []Middleware[I, O, S] { | ||
return ms | ||
} | ||
|
||
// ChainMiddleware creates a new Middleware that applies a sequence of | ||
// Middlewares, so that they execute in the given order when handling action | ||
// request. | ||
// In other words, ChainMiddleware(m1, m2)(handler) = m1(m2(handler)) | ||
func ChainMiddleware[I, O, S any](middlewares ...Middleware[I, O, S]) Middleware[I, O, S] { | ||
return func(h Func[I, O, S]) Func[I, O, S] { | ||
for i := range middlewares { | ||
h = middlewares[len(middlewares)-1-i](h) | ||
} | ||
return h | ||
} | ||
} | ||
|
||
// Func is the type of function that Actions and Flows execute. | ||
// It takes an input of type Int and returns an output of type Out, optionally | ||
// streaming values of type Stream incrementally by invoking a callback. | ||
|
@@ -63,6 +93,7 @@ type Action[In, Out, Stream any] struct { | |
// optional | ||
description string | ||
metadata map[string]any | ||
middleware []Middleware[In, Out, Stream] | ||
} | ||
|
||
// See js/core/src/action.ts | ||
|
@@ -78,18 +109,18 @@ func defineAction[In, Out any](r *registry, provider, name string, atype atype.A | |
return a | ||
} | ||
|
||
func DefineStreamingAction[In, Out, Stream any](provider, name string, atype atype.ActionType, metadata map[string]any, fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
return defineStreamingAction(globalRegistry, provider, name, atype, metadata, fn) | ||
func DefineStreamingAction[In, Out, Stream any](provider, name string, atype atype.ActionType, metadata map[string]any, middleware []Middleware[In, Out, Stream], fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
return defineStreamingAction(globalRegistry, provider, name, atype, metadata, middleware, fn) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm still unclear about why the middlewares are getting passed all the way down. I can just apply them to the action function at the top level. I don't think anything in action.go needs to change. |
||
} | ||
|
||
func defineStreamingAction[In, Out, Stream any](r *registry, provider, name string, atype atype.ActionType, metadata map[string]any, fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
a := newStreamingAction(name, atype, metadata, fn) | ||
func defineStreamingAction[In, Out, Stream any](r *registry, provider, name string, atype atype.ActionType, metadata map[string]any, middleware []Middleware[In, Out, Stream], fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
a := newStreamingAction(name, atype, metadata, middleware, fn) | ||
r.registerAction(provider, a) | ||
return a | ||
} | ||
|
||
func DefineCustomAction[In, Out, Stream any](provider, name string, metadata map[string]any, fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
return DefineStreamingAction(provider, name, atype.Custom, metadata, fn) | ||
return DefineStreamingAction(provider, name, atype.Custom, metadata, nil, fn) | ||
} | ||
|
||
// DefineActionWithInputSchema creates a new Action and registers it. | ||
|
@@ -108,13 +139,13 @@ func defineActionWithInputSchema[Out any](r *registry, provider, name string, at | |
|
||
// newAction creates a new Action with the given name and non-streaming function. | ||
func newAction[In, Out any](name string, atype atype.ActionType, metadata map[string]any, fn func(context.Context, In) (Out, error)) *Action[In, Out, struct{}] { | ||
return newStreamingAction(name, atype, metadata, func(ctx context.Context, in In, cb NoStream) (Out, error) { | ||
return newStreamingAction(name, atype, metadata, nil, func(ctx context.Context, in In, cb NoStream) (Out, error) { | ||
return fn(ctx, in) | ||
}) | ||
} | ||
|
||
// newStreamingAction creates a new Action with the given name and streaming function. | ||
func newStreamingAction[In, Out, Stream any](name string, atype atype.ActionType, metadata map[string]any, fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
func newStreamingAction[In, Out, Stream any](name string, atype atype.ActionType, metadata map[string]any, middleware []Middleware[In, Out, Stream], fn Func[In, Out, Stream]) *Action[In, Out, Stream] { | ||
var i In | ||
var o Out | ||
return &Action[In, Out, Stream]{ | ||
|
@@ -127,6 +158,7 @@ func newStreamingAction[In, Out, Stream any](name string, atype atype.ActionType | |
inputSchema: inferJSONSchema(i), | ||
outputSchema: inferJSONSchema(o), | ||
metadata: metadata, | ||
middleware: middleware, | ||
} | ||
} | ||
|
||
|
@@ -169,6 +201,7 @@ func (a *Action[In, Out, Stream]) Run(ctx context.Context, input In, cb func(con | |
// This action has probably not been registered. | ||
tstate = globalRegistry.tstate | ||
} | ||
|
||
return tracing.RunInNewSpan(ctx, tstate, a.name, "action", false, input, | ||
func(ctx context.Context, input In) (Out, error) { | ||
start := time.Now() | ||
|
@@ -178,7 +211,8 @@ func (a *Action[In, Out, Stream]) Run(ctx context.Context, input In, cb func(con | |
} | ||
var output Out | ||
if err == nil { | ||
output, err = a.fn(ctx, input, cb) | ||
dispatch := ChainMiddleware(a.middleware...) | ||
output, err = dispatch(a.fn)(ctx, input, cb) | ||
if err == nil { | ||
if err = validateValue(output, a.outputSchema); err != nil { | ||
err = fmt.Errorf("invalid output: %w", err) | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.