graphql/executor/extensions.go (156 lines of code) (raw):
package executor
import (
"context"
"errors"
"fmt"
"github.com/99designs/gqlgen/graphql"
)
// Use adds the given extension to this Executor.
func (e *Executor) Use(extension graphql.HandlerExtension) {
if err := extension.Validate(e.es); err != nil {
panic(err)
}
switch extension.(type) {
case graphql.OperationParameterMutator,
graphql.OperationContextMutator,
graphql.OperationInterceptor,
graphql.RootFieldInterceptor,
graphql.FieldInterceptor,
graphql.ResponseInterceptor:
e.extensions = append(e.extensions, extension)
e.ext = processExtensions(e.extensions)
default:
panic(fmt.Errorf("cannot Use %T as a gqlgen handler extension because it does not implement any extension hooks", extension))
}
}
// AroundFields is a convenience method for creating an extension that only implements field middleware
func (e *Executor) AroundFields(f graphql.FieldMiddleware) {
e.Use(aroundFieldFunc(f))
}
// AroundRootFields is a convenience method for creating an extension that only implements root field middleware
func (e *Executor) AroundRootFields(f graphql.RootFieldMiddleware) {
e.Use(aroundRootFieldFunc(f))
}
// AroundOperations is a convenience method for creating an extension that only implements operation middleware
func (e *Executor) AroundOperations(f graphql.OperationMiddleware) {
e.Use(aroundOpFunc(f))
}
// AroundResponses is a convenience method for creating an extension that only implements response middleware
func (e *Executor) AroundResponses(f graphql.ResponseMiddleware) {
e.Use(aroundRespFunc(f))
}
type extensions struct {
operationMiddleware graphql.OperationMiddleware
responseMiddleware graphql.ResponseMiddleware
rootFieldMiddleware graphql.RootFieldMiddleware
fieldMiddleware graphql.FieldMiddleware
operationParameterMutators []graphql.OperationParameterMutator
operationContextMutators []graphql.OperationContextMutator
}
func processExtensions(exts []graphql.HandlerExtension) extensions {
e := extensions{
operationMiddleware: func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
return next(ctx)
},
responseMiddleware: func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
return next(ctx)
},
rootFieldMiddleware: func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler {
return next(ctx)
},
fieldMiddleware: func(ctx context.Context, next graphql.Resolver) (res any, err error) {
return next(ctx)
},
}
// this loop goes backwards so the first extension is the outer most middleware and runs first.
for i := len(exts) - 1; i >= 0; i-- {
p := exts[i]
if p, ok := p.(graphql.OperationInterceptor); ok {
previous := e.operationMiddleware
e.operationMiddleware = func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
return p.InterceptOperation(ctx, func(ctx context.Context) graphql.ResponseHandler {
return previous(ctx, next)
})
}
}
if p, ok := p.(graphql.ResponseInterceptor); ok {
previous := e.responseMiddleware
e.responseMiddleware = func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
return p.InterceptResponse(ctx, func(ctx context.Context) *graphql.Response {
return previous(ctx, next)
})
}
}
if p, ok := p.(graphql.RootFieldInterceptor); ok {
previous := e.rootFieldMiddleware
e.rootFieldMiddleware = func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler {
return p.InterceptRootField(ctx, func(ctx context.Context) graphql.Marshaler {
return previous(ctx, next)
})
}
}
if p, ok := p.(graphql.FieldInterceptor); ok {
previous := e.fieldMiddleware
e.fieldMiddleware = func(ctx context.Context, next graphql.Resolver) (res any, err error) {
return p.InterceptField(ctx, func(ctx context.Context) (res any, err error) {
return previous(ctx, next)
})
}
}
}
for _, p := range exts {
if p, ok := p.(graphql.OperationParameterMutator); ok {
e.operationParameterMutators = append(e.operationParameterMutators, p)
}
if p, ok := p.(graphql.OperationContextMutator); ok {
e.operationContextMutators = append(e.operationContextMutators, p)
}
}
return e
}
type aroundOpFunc func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler
func (r aroundOpFunc) ExtensionName() string {
return "InlineOperationFunc"
}
func (r aroundOpFunc) Validate(schema graphql.ExecutableSchema) error {
if r == nil {
return errors.New("OperationFunc can not be nil")
}
return nil
}
func (r aroundOpFunc) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
return r(ctx, next)
}
type aroundRespFunc func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response
func (r aroundRespFunc) ExtensionName() string {
return "InlineResponseFunc"
}
func (r aroundRespFunc) Validate(schema graphql.ExecutableSchema) error {
if r == nil {
return errors.New("ResponseFunc can not be nil")
}
return nil
}
func (r aroundRespFunc) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
return r(ctx, next)
}
type aroundFieldFunc func(ctx context.Context, next graphql.Resolver) (res any, err error)
func (f aroundFieldFunc) ExtensionName() string {
return "InlineFieldFunc"
}
func (f aroundFieldFunc) Validate(schema graphql.ExecutableSchema) error {
if f == nil {
return errors.New("FieldFunc can not be nil")
}
return nil
}
func (f aroundFieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res any, err error) {
return f(ctx, next)
}
type aroundRootFieldFunc func(ctx context.Context, next graphql.RootResolver) graphql.Marshaler
func (f aroundRootFieldFunc) ExtensionName() string {
return "InlineRootFieldFunc"
}
func (f aroundRootFieldFunc) Validate(schema graphql.ExecutableSchema) error {
if f == nil {
return errors.New("RootFieldFunc can not be nil")
}
return nil
}
func (f aroundRootFieldFunc) InterceptRootField(ctx context.Context, next graphql.RootResolver) graphql.Marshaler {
return f(ctx, next)
}