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