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  }