github.com/fortexxx/gqlgen@v0.10.3-0.20191216030626-ca5ea8b21ead/graphql/handler/executor.go (about)

     1  package handler
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/99designs/gqlgen/graphql"
     7  	"github.com/vektah/gqlparser/ast"
     8  	"github.com/vektah/gqlparser/gqlerror"
     9  	"github.com/vektah/gqlparser/parser"
    10  	"github.com/vektah/gqlparser/validator"
    11  )
    12  
    13  type executor struct {
    14  	operationMiddleware        graphql.OperationMiddleware
    15  	responseMiddleware         graphql.ResponseMiddleware
    16  	fieldMiddleware            graphql.FieldMiddleware
    17  	operationParameterMutators []graphql.OperationParameterMutator
    18  	operationContextMutators   []graphql.OperationContextMutator
    19  	server                     *Server
    20  }
    21  
    22  var _ graphql.GraphExecutor = executor{}
    23  
    24  func newExecutor(s *Server) executor {
    25  	e := executor{
    26  		server: s,
    27  	}
    28  	e.operationMiddleware = func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
    29  		return next(ctx)
    30  	}
    31  	e.responseMiddleware = func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
    32  		return next(ctx)
    33  	}
    34  	e.fieldMiddleware = func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
    35  		return next(ctx)
    36  	}
    37  
    38  	// this loop goes backwards so the first extension is the outer most middleware and runs first.
    39  	for i := len(s.extensions) - 1; i >= 0; i-- {
    40  		p := s.extensions[i]
    41  		if p, ok := p.(graphql.OperationInterceptor); ok {
    42  			previous := e.operationMiddleware
    43  			e.operationMiddleware = func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
    44  				return p.InterceptOperation(ctx, func(ctx context.Context) graphql.ResponseHandler {
    45  					return previous(ctx, next)
    46  				})
    47  			}
    48  		}
    49  
    50  		if p, ok := p.(graphql.ResponseInterceptor); ok {
    51  			previous := e.responseMiddleware
    52  			e.responseMiddleware = func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response {
    53  				return p.InterceptResponse(ctx, func(ctx context.Context) *graphql.Response {
    54  					return previous(ctx, next)
    55  				})
    56  			}
    57  		}
    58  
    59  		if p, ok := p.(graphql.FieldInterceptor); ok {
    60  			previous := e.fieldMiddleware
    61  			e.fieldMiddleware = func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) {
    62  				return p.InterceptField(ctx, func(ctx context.Context) (res interface{}, err error) {
    63  					return previous(ctx, next)
    64  				})
    65  			}
    66  		}
    67  	}
    68  
    69  	for _, p := range s.extensions {
    70  		if p, ok := p.(graphql.OperationParameterMutator); ok {
    71  			e.operationParameterMutators = append(e.operationParameterMutators, p)
    72  		}
    73  
    74  		if p, ok := p.(graphql.OperationContextMutator); ok {
    75  			e.operationContextMutators = append(e.operationContextMutators, p)
    76  		}
    77  
    78  	}
    79  
    80  	return e
    81  }
    82  
    83  func (e executor) DispatchOperation(ctx context.Context, rc *graphql.OperationContext) (h graphql.ResponseHandler, resctx context.Context) {
    84  	ctx = graphql.WithOperationContext(ctx, rc)
    85  
    86  	var innerCtx context.Context
    87  	res := e.operationMiddleware(ctx, func(ctx context.Context) graphql.ResponseHandler {
    88  		innerCtx = ctx
    89  
    90  		tmpResponseContext := graphql.WithResponseContext(ctx, e.server.errorPresenter, e.server.recoverFunc)
    91  		responses := e.server.es.Exec(tmpResponseContext)
    92  		if errs := graphql.GetErrors(tmpResponseContext); errs != nil {
    93  			return graphql.OneShot(&graphql.Response{Errors: errs})
    94  		}
    95  
    96  		return func(ctx context.Context) *graphql.Response {
    97  			ctx = graphql.WithResponseContext(ctx, e.server.errorPresenter, e.server.recoverFunc)
    98  			resp := e.responseMiddleware(ctx, func(ctx context.Context) *graphql.Response {
    99  				resp := responses(ctx)
   100  				if resp == nil {
   101  					return nil
   102  				}
   103  				resp.Extensions = graphql.GetExtensions(ctx)
   104  				return resp
   105  			})
   106  			if resp == nil {
   107  				return nil
   108  			}
   109  
   110  			resp.Errors = append(resp.Errors, graphql.GetErrors(ctx)...)
   111  			return resp
   112  		}
   113  	})
   114  
   115  	return res, innerCtx
   116  }
   117  
   118  func (e executor) CreateOperationContext(ctx context.Context, params *graphql.RawParams) (*graphql.OperationContext, gqlerror.List) {
   119  	rc := &graphql.OperationContext{
   120  		DisableIntrospection: true,
   121  		Recover:              graphql.DefaultRecover,
   122  		ResolverMiddleware:   e.fieldMiddleware,
   123  		Stats:                graphql.Stats{OperationStart: graphql.GetStartTime(ctx)},
   124  	}
   125  	ctx = graphql.WithOperationContext(ctx, rc)
   126  
   127  	for _, p := range e.operationParameterMutators {
   128  		if err := p.MutateOperationParameters(ctx, params); err != nil {
   129  			return rc, gqlerror.List{err}
   130  		}
   131  	}
   132  
   133  	rc.RawQuery = params.Query
   134  	rc.OperationName = params.OperationName
   135  
   136  	var listErr gqlerror.List
   137  	rc.Doc, listErr = e.parseQuery(ctx, &rc.Stats, params.Query)
   138  	if len(listErr) != 0 {
   139  		return rc, listErr
   140  	}
   141  
   142  	rc.Operation = rc.Doc.Operations.ForName(params.OperationName)
   143  	if rc.Operation == nil {
   144  		return rc, gqlerror.List{gqlerror.Errorf("operation %s not found", params.OperationName)}
   145  	}
   146  
   147  	var err *gqlerror.Error
   148  	rc.Variables, err = validator.VariableValues(e.server.es.Schema(), rc.Operation, params.Variables)
   149  	if err != nil {
   150  		return rc, gqlerror.List{err}
   151  	}
   152  	rc.Stats.Validation.End = graphql.Now()
   153  
   154  	for _, p := range e.operationContextMutators {
   155  		if err := p.MutateOperationContext(ctx, rc); err != nil {
   156  			return rc, gqlerror.List{err}
   157  		}
   158  	}
   159  
   160  	return rc, nil
   161  }
   162  
   163  func (e executor) DispatchError(ctx context.Context, list gqlerror.List) *graphql.Response {
   164  	ctx = graphql.WithResponseContext(ctx, e.server.errorPresenter, e.server.recoverFunc)
   165  	resp := e.responseMiddleware(ctx, func(ctx context.Context) *graphql.Response {
   166  		resp := &graphql.Response{
   167  			Errors: list,
   168  		}
   169  		resp.Extensions = graphql.GetExtensions(ctx)
   170  		return resp
   171  	})
   172  
   173  	return resp
   174  }
   175  
   176  // parseQuery decodes the incoming query and validates it, pulling from cache if present.
   177  //
   178  // NOTE: This should NOT look at variables, they will change per request. It should only parse and validate
   179  // the raw query string.
   180  func (e executor) parseQuery(ctx context.Context, stats *graphql.Stats, query string) (*ast.QueryDocument, gqlerror.List) {
   181  	stats.Parsing.Start = graphql.Now()
   182  
   183  	if doc, ok := e.server.queryCache.Get(query); ok {
   184  		now := graphql.Now()
   185  
   186  		stats.Parsing.End = now
   187  		stats.Validation.Start = now
   188  		return doc.(*ast.QueryDocument), nil
   189  	}
   190  
   191  	doc, err := parser.ParseQuery(&ast.Source{Input: query})
   192  	if err != nil {
   193  		return nil, gqlerror.List{err}
   194  	}
   195  	stats.Parsing.End = graphql.Now()
   196  
   197  	stats.Validation.Start = graphql.Now()
   198  	listErr := validator.Validate(e.server.es.Schema(), doc)
   199  	if len(listErr) != 0 {
   200  		return nil, listErr
   201  	}
   202  
   203  	e.server.queryCache.Add(query, doc)
   204  
   205  	return doc, nil
   206  }