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 }