github.com/99designs/gqlgen@v0.17.45/graphql/handler.go (about) 1 package graphql 2 3 import ( 4 "context" 5 "net/http" 6 "strconv" 7 "strings" 8 9 "github.com/vektah/gqlparser/v2/gqlerror" 10 ) 11 12 type ( 13 OperationMiddleware func(ctx context.Context, next OperationHandler) ResponseHandler 14 OperationHandler func(ctx context.Context) ResponseHandler 15 16 ResponseHandler func(ctx context.Context) *Response 17 ResponseMiddleware func(ctx context.Context, next ResponseHandler) *Response 18 19 Resolver func(ctx context.Context) (res interface{}, err error) 20 FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error) 21 22 RootResolver func(ctx context.Context) Marshaler 23 RootFieldMiddleware func(ctx context.Context, next RootResolver) Marshaler 24 25 RawParams struct { 26 Query string `json:"query"` 27 OperationName string `json:"operationName"` 28 Variables map[string]interface{} `json:"variables"` 29 Extensions map[string]interface{} `json:"extensions"` 30 Headers http.Header `json:"headers"` 31 32 ReadTime TraceTiming `json:"-"` 33 } 34 35 GraphExecutor interface { 36 CreateOperationContext(ctx context.Context, params *RawParams) (*OperationContext, gqlerror.List) 37 DispatchOperation(ctx context.Context, rc *OperationContext) (ResponseHandler, context.Context) 38 DispatchError(ctx context.Context, list gqlerror.List) *Response 39 } 40 41 // HandlerExtension adds functionality to the http handler. See the list of possible hook points below 42 // Its important to understand the lifecycle of a graphql request and the terminology we use in gqlgen 43 // before working with these 44 // 45 // +--- REQUEST POST /graphql --------------------------------------------+ 46 // | +- OPERATION query OpName { viewer { name } } -----------------------+ | 47 // | | RESPONSE { "data": { "viewer": { "name": "bob" } } } | | 48 // | +- OPERATION subscription OpName2 { chat { message } } --------------+ | 49 // | | RESPONSE { "data": { "chat": { "message": "hello" } } } | | 50 // | | RESPONSE { "data": { "chat": { "message": "byee" } } } | | 51 // | +--------------------------------------------------------------------+ | 52 // +------------------------------------------------------------------------+ 53 HandlerExtension interface { 54 // ExtensionName should be a CamelCase string version of the extension which may be shown in stats and logging. 55 ExtensionName() string 56 // Validate is called when adding an extension to the server, it allows validation against the servers schema. 57 Validate(schema ExecutableSchema) error 58 } 59 60 // OperationParameterMutator is called before creating a request context. allows manipulating the raw query 61 // on the way in. 62 OperationParameterMutator interface { 63 MutateOperationParameters(ctx context.Context, request *RawParams) *gqlerror.Error 64 } 65 66 // OperationContextMutator is called after creating the request context, but before executing the root resolver. 67 OperationContextMutator interface { 68 MutateOperationContext(ctx context.Context, rc *OperationContext) *gqlerror.Error 69 } 70 71 // OperationInterceptor is called for each incoming query, for basic requests the writer will be invoked once, 72 // for subscriptions it will be invoked multiple times. 73 OperationInterceptor interface { 74 InterceptOperation(ctx context.Context, next OperationHandler) ResponseHandler 75 } 76 77 // ResponseInterceptor is called around each graphql operation response. This can be called many times for a single 78 // operation the case of subscriptions. 79 ResponseInterceptor interface { 80 InterceptResponse(ctx context.Context, next ResponseHandler) *Response 81 } 82 83 RootFieldInterceptor interface { 84 InterceptRootField(ctx context.Context, next RootResolver) Marshaler 85 } 86 87 // FieldInterceptor called around each field 88 FieldInterceptor interface { 89 InterceptField(ctx context.Context, next Resolver) (res interface{}, err error) 90 } 91 92 // Transport provides support for different wire level encodings of graphql requests, eg Form, Get, Post, Websocket 93 Transport interface { 94 Supports(r *http.Request) bool 95 Do(w http.ResponseWriter, r *http.Request, exec GraphExecutor) 96 } 97 ) 98 99 type Status int 100 101 func (p *RawParams) AddUpload(upload Upload, key, path string) *gqlerror.Error { 102 if !strings.HasPrefix(path, "variables.") { 103 return gqlerror.Errorf("invalid operations paths for key %s", key) 104 } 105 106 var ptr interface{} = p.Variables 107 parts := strings.Split(path, ".") 108 109 // skip the first part (variables) because we started there 110 for i, p := range parts[1:] { 111 last := i == len(parts)-2 112 if ptr == nil { 113 return gqlerror.Errorf("path is missing \"variables.\" prefix, key: %s, path: %s", key, path) 114 } 115 if index, parseNbrErr := strconv.Atoi(p); parseNbrErr == nil { 116 if last { 117 ptr.([]interface{})[index] = upload 118 } else { 119 ptr = ptr.([]interface{})[index] 120 } 121 } else { 122 if last { 123 ptr.(map[string]interface{})[p] = upload 124 } else { 125 ptr = ptr.(map[string]interface{})[p] 126 } 127 } 128 } 129 130 return nil 131 }