Skip to content

Commit

Permalink
Bring operation middleware inline with other handler interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
vektah committed Oct 30, 2019
1 parent ab5665a commit 4a69bcd
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 33 deletions.
1 change: 1 addition & 0 deletions graphql/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

type Resolver func(ctx context.Context) (res interface{}, err error)
type ResultMiddleware func(ctx context.Context, next ResultHandler) *Response
type OperationMiddleware func(ctx context.Context, next OperationHandler, writer Writer)
type FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error)
type ComplexityLimitFunc func(ctx context.Context) int

Expand Down
27 changes: 12 additions & 15 deletions graphql/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,17 @@ type (
}

// HandlerPlugin interface is entirely optional, see the list of possible hook points below
// Its important to understand the lifecycle of a graphql request and the terminology we use in gqlgen before working with these
// +---REQUEST POST /graphql------------------------------------------------+
// | |
// | ++OPERATION query OpName { viewer { name } }+------------------------+ |
// | | | |
// | | RESULT { "data": { "viewer": { "name": "bob" } } } | |
// | | | |
// | ++OPERATION subscription OpName2 { chat { message } }+---------------+ |
// | | | |
// | | RESULT { "data": { "chat": { "message": "hello" } } } | |
// | | | |
// | | RESULT { "data": { "chat": { "message": "byee" } } } | |
// | | | |
// Its important to understand the lifecycle of a graphql request and the terminology we use in gqlgen
// before working with these
//
// +--- REQUEST POST /graphql --------------------------------------------+
// | +- OPERATION query OpName { viewer { name } } -----------------------+ |
// | | RESULT { "data": { "viewer": { "name": "bob" } } } | |
// | +- OPERATION subscription OpName2 { chat { message } } --------------+ |
// | | RESULT { "data": { "chat": { "message": "hello" } } } | |
// | | RESULT { "data": { "chat": { "message": "byee" } } } | |
// | +--------------------------------------------------------------------+ |
// +------------------------------------------------------------------------+

HandlerPlugin interface{}

// RequestParameterMutator is called before creating a request context. allows manipulating the raw query
Expand All @@ -57,8 +52,10 @@ type (
MutateRequestContext(ctx context.Context, rc *RequestContext) *gqlerror.Error
}

// OperationInterceptor is called for each incoming query, for basic requests the writer will be invoked once,
// for subscriptions it will be invoked multiple times.
OperationInterceptor interface {
InterceptOperation(next OperationHandler) OperationHandler
InterceptOperation(ctx context.Context, next OperationHandler, writer Writer)
}

// ResultInterceptor is called around each graphql operation result. This can be called many times for a single
Expand Down
12 changes: 7 additions & 5 deletions graphql/handler/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
)

type executor struct {
operationHandler graphql.OperationHandler
operationMiddleware graphql.OperationHandler
resultHandler graphql.ResultMiddleware
responseMiddleware graphql.FieldMiddleware
es graphql.ExecutableSchema
Expand All @@ -25,7 +25,7 @@ func newExecutor(es graphql.ExecutableSchema, plugins []graphql.HandlerPlugin) e
e := executor{
es: es,
}
e.operationHandler = e.executableSchemaHandler
e.operationMiddleware = e.executableSchemaHandler
e.resultHandler = func(ctx context.Context, next graphql.ResultHandler) *graphql.Response {
return next(ctx)
}
Expand All @@ -37,8 +37,10 @@ func newExecutor(es graphql.ExecutableSchema, plugins []graphql.HandlerPlugin) e
for i := len(plugins) - 1; i >= 0; i-- {
p := plugins[i]
if p, ok := p.(graphql.OperationInterceptor); ok {
previous := e.operationHandler
e.operationHandler = p.InterceptOperation(previous)
previous := e.operationMiddleware
e.operationMiddleware = func(ctx context.Context, writer graphql.Writer) {
p.InterceptOperation(ctx, previous, writer)
}
}

if p, ok := p.(graphql.ResultInterceptor); ok {
Expand Down Expand Up @@ -75,7 +77,7 @@ func newExecutor(es graphql.ExecutableSchema, plugins []graphql.HandlerPlugin) e
}

func (e executor) DispatchRequest(ctx context.Context, writer graphql.Writer) {
e.operationHandler(ctx, writer)
e.operationMiddleware(ctx, writer)
}

func (e executor) CreateRequestContext(ctx context.Context, params *graphql.RawParams) (*graphql.RequestContext, gqlerror.List) {
Expand Down
22 changes: 9 additions & 13 deletions graphql/handler/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,13 @@ func TestServer(t *testing.T) {

t.Run("invokes operation middleware in order", func(t *testing.T) {
var calls []string
srv.Use(opFunc(func(next graphql.OperationHandler) graphql.OperationHandler {
return func(ctx context.Context, writer graphql.Writer) {
calls = append(calls, "first")
next(ctx, writer)
}
srv.Use(opFunc(func(ctx context.Context, next graphql.OperationHandler, writer graphql.Writer) {
calls = append(calls, "first")
next(ctx, writer)
}))
srv.Use(opFunc(func(next graphql.OperationHandler) graphql.OperationHandler {
return func(ctx context.Context, writer graphql.Writer) {
calls = append(calls, "second")
next(ctx, writer)
}
srv.Use(opFunc(func(ctx context.Context, next graphql.OperationHandler, writer graphql.Writer) {
calls = append(calls, "second")
next(ctx, writer)
}))

resp := get(srv, "/foo?query={a}")
Expand All @@ -108,10 +104,10 @@ func TestServer(t *testing.T) {
})
}

type opFunc func(next graphql.OperationHandler) graphql.OperationHandler
type opFunc func(ctx context.Context, next graphql.OperationHandler, writer graphql.Writer)

func (r opFunc) InterceptOperation(next graphql.OperationHandler) graphql.OperationHandler {
return r(next)
func (r opFunc) InterceptOperation(ctx context.Context, next graphql.OperationHandler, writer graphql.Writer) {
r(ctx, next, writer)
}

type fieldFunc func(ctx context.Context, next graphql.Resolver) (res interface{}, err error)
Expand Down

0 comments on commit 4a69bcd

Please sign in to comment.