github.com/apipluspower/gqlgen@v0.15.2/graphql/handler/server.go (about) 1 package handler 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "net/http" 8 "time" 9 10 "github.com/apipluspower/gqlgen/graphql" 11 "github.com/apipluspower/gqlgen/graphql/executor" 12 "github.com/apipluspower/gqlgen/graphql/handler/extension" 13 "github.com/apipluspower/gqlgen/graphql/handler/lru" 14 "github.com/apipluspower/gqlgen/graphql/handler/transport" 15 "github.com/vektah/gqlparser/v2/gqlerror" 16 ) 17 18 type ( 19 Server struct { 20 transports []graphql.Transport 21 exec *executor.Executor 22 } 23 ) 24 25 func New(es graphql.ExecutableSchema) *Server { 26 return &Server{ 27 exec: executor.New(es), 28 } 29 } 30 31 func NewDefaultServer(es graphql.ExecutableSchema) *Server { 32 srv := New(es) 33 34 srv.AddTransport(transport.Websocket{ 35 KeepAlivePingInterval: 10 * time.Second, 36 }) 37 srv.AddTransport(transport.Options{}) 38 srv.AddTransport(transport.GET{}) 39 srv.AddTransport(transport.POST{}) 40 srv.AddTransport(transport.MultipartForm{}) 41 42 srv.SetQueryCache(lru.New(1000)) 43 44 srv.Use(extension.Introspection{}) 45 srv.Use(extension.AutomaticPersistedQuery{ 46 Cache: lru.New(100), 47 }) 48 49 return srv 50 } 51 52 func (s *Server) AddTransport(transport graphql.Transport) { 53 s.transports = append(s.transports, transport) 54 } 55 56 func (s *Server) SetErrorPresenter(f graphql.ErrorPresenterFunc) { 57 s.exec.SetErrorPresenter(f) 58 } 59 60 func (s *Server) SetRecoverFunc(f graphql.RecoverFunc) { 61 s.exec.SetRecoverFunc(f) 62 } 63 64 func (s *Server) SetQueryCache(cache graphql.Cache) { 65 s.exec.SetQueryCache(cache) 66 } 67 68 func (s *Server) Use(extension graphql.HandlerExtension) { 69 s.exec.Use(extension) 70 } 71 72 // AroundFields is a convenience method for creating an extension that only implements field middleware 73 func (s *Server) AroundFields(f graphql.FieldMiddleware) { 74 s.exec.AroundFields(f) 75 } 76 77 // AroundRootFields is a convenience method for creating an extension that only implements field middleware 78 func (s *Server) AroundRootFields(f graphql.RootFieldMiddleware) { 79 s.exec.AroundRootFields(f) 80 } 81 82 // AroundOperations is a convenience method for creating an extension that only implements operation middleware 83 func (s *Server) AroundOperations(f graphql.OperationMiddleware) { 84 s.exec.AroundOperations(f) 85 } 86 87 // AroundResponses is a convenience method for creating an extension that only implements response middleware 88 func (s *Server) AroundResponses(f graphql.ResponseMiddleware) { 89 s.exec.AroundResponses(f) 90 } 91 92 func (s *Server) getTransport(r *http.Request) graphql.Transport { 93 for _, t := range s.transports { 94 if t.Supports(r) { 95 return t 96 } 97 } 98 return nil 99 } 100 101 func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { 102 defer func() { 103 if err := recover(); err != nil { 104 err := s.exec.PresentRecoveredError(r.Context(), err) 105 resp := &graphql.Response{Errors: []*gqlerror.Error{err}} 106 b, _ := json.Marshal(resp) 107 w.WriteHeader(http.StatusUnprocessableEntity) 108 w.Write(b) 109 } 110 }() 111 112 r = r.WithContext(graphql.StartOperationTrace(r.Context())) 113 114 transport := s.getTransport(r) 115 if transport == nil { 116 sendErrorf(w, http.StatusBadRequest, "transport not supported") 117 return 118 } 119 120 transport.Do(w, r, s.exec) 121 } 122 123 func sendError(w http.ResponseWriter, code int, errors ...*gqlerror.Error) { 124 w.WriteHeader(code) 125 b, err := json.Marshal(&graphql.Response{Errors: errors}) 126 if err != nil { 127 panic(err) 128 } 129 w.Write(b) 130 } 131 132 func sendErrorf(w http.ResponseWriter, code int, format string, args ...interface{}) { 133 sendError(w, code, &gqlerror.Error{Message: fmt.Sprintf(format, args...)}) 134 } 135 136 type OperationFunc func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler 137 138 func (r OperationFunc) ExtensionName() string { 139 return "InlineOperationFunc" 140 } 141 142 func (r OperationFunc) Validate(schema graphql.ExecutableSchema) error { 143 if r == nil { 144 return fmt.Errorf("OperationFunc can not be nil") 145 } 146 return nil 147 } 148 149 func (r OperationFunc) InterceptOperation(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler { 150 return r(ctx, next) 151 } 152 153 type ResponseFunc func(ctx context.Context, next graphql.ResponseHandler) *graphql.Response 154 155 func (r ResponseFunc) ExtensionName() string { 156 return "InlineResponseFunc" 157 } 158 159 func (r ResponseFunc) Validate(schema graphql.ExecutableSchema) error { 160 if r == nil { 161 return fmt.Errorf("ResponseFunc can not be nil") 162 } 163 return nil 164 } 165 166 func (r ResponseFunc) InterceptResponse(ctx context.Context, next graphql.ResponseHandler) *graphql.Response { 167 return r(ctx, next) 168 } 169 170 type FieldFunc func(ctx context.Context, next graphql.Resolver) (res interface{}, err error) 171 172 func (f FieldFunc) ExtensionName() string { 173 return "InlineFieldFunc" 174 } 175 176 func (f FieldFunc) Validate(schema graphql.ExecutableSchema) error { 177 if f == nil { 178 return fmt.Errorf("FieldFunc can not be nil") 179 } 180 return nil 181 } 182 183 func (f FieldFunc) InterceptField(ctx context.Context, next graphql.Resolver) (res interface{}, err error) { 184 return f(ctx, next) 185 }