mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
* Upgrade gqlgen to v0.17.2 This enables builds on Go 1.18. github.com/vektah/gqlparser is upgraded to the newest version too. Getting this to work is a bit of a hazzle. I had to first remove vendoring from the repository, perform the upgrade and then re-introduce the vendor directory. I think gqlgens analysis went wrong for some reason on the upgrade. It would seem a clean-room installation fixed it. * Bump project to 1.18 * Update all packages, address gqlgenc breaking changes * Let `go mod tidy` handle the go.mod file * Upgrade linter to 1.45.2 * Introduce v1.45.2 of the linter The linter now correctly warns on `strings.Title` because it isn't unicode-aware. Fix this by using the suggested fix from x/text/cases to produce unicode-aware strings. The mapping isn't entirely 1-1 as this new approach has a larger iface: it spans all of unicode rather than just ASCII. It coincides for ASCII however, so things should be largely the same. * Ready ourselves for errchkjson and contextcheck. * Revert dockerfile golang version changes for now Co-authored-by: Kermie <kermie@isinthe.house> Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
131 lines
4.8 KiB
Go
131 lines
4.8 KiB
Go
package graphql
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/vektah/gqlparser/v2/gqlerror"
|
|
)
|
|
|
|
type (
|
|
OperationMiddleware func(ctx context.Context, next OperationHandler) ResponseHandler
|
|
OperationHandler func(ctx context.Context) ResponseHandler
|
|
|
|
ResponseHandler func(ctx context.Context) *Response
|
|
ResponseMiddleware func(ctx context.Context, next ResponseHandler) *Response
|
|
|
|
Resolver func(ctx context.Context) (res interface{}, err error)
|
|
FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error)
|
|
|
|
RootResolver func(ctx context.Context) Marshaler
|
|
RootFieldMiddleware func(ctx context.Context, next RootResolver) Marshaler
|
|
|
|
RawParams struct {
|
|
Query string `json:"query"`
|
|
OperationName string `json:"operationName"`
|
|
Variables map[string]interface{} `json:"variables"`
|
|
Extensions map[string]interface{} `json:"extensions"`
|
|
|
|
ReadTime TraceTiming `json:"-"`
|
|
}
|
|
|
|
GraphExecutor interface {
|
|
CreateOperationContext(ctx context.Context, params *RawParams) (*OperationContext, gqlerror.List)
|
|
DispatchOperation(ctx context.Context, rc *OperationContext) (ResponseHandler, context.Context)
|
|
DispatchError(ctx context.Context, list gqlerror.List) *Response
|
|
}
|
|
|
|
// HandlerExtension adds functionality to the http handler. See the list of possible hook points below
|
|
// Its important to understand the lifecycle of a graphql request and the terminology we use in gqlgen
|
|
// before working with these
|
|
//
|
|
// +--- REQUEST POST /graphql --------------------------------------------+
|
|
// | +- OPERATION query OpName { viewer { name } } -----------------------+ |
|
|
// | | RESPONSE { "data": { "viewer": { "name": "bob" } } } | |
|
|
// | +- OPERATION subscription OpName2 { chat { message } } --------------+ |
|
|
// | | RESPONSE { "data": { "chat": { "message": "hello" } } } | |
|
|
// | | RESPONSE { "data": { "chat": { "message": "byee" } } } | |
|
|
// | +--------------------------------------------------------------------+ |
|
|
// +------------------------------------------------------------------------+
|
|
HandlerExtension interface {
|
|
// ExtensionName should be a CamelCase string version of the extension which may be shown in stats and logging.
|
|
ExtensionName() string
|
|
// Validate is called when adding an extension to the server, it allows validation against the servers schema.
|
|
Validate(schema ExecutableSchema) error
|
|
}
|
|
|
|
// OperationParameterMutator is called before creating a request context. allows manipulating the raw query
|
|
// on the way in.
|
|
OperationParameterMutator interface {
|
|
MutateOperationParameters(ctx context.Context, request *RawParams) *gqlerror.Error
|
|
}
|
|
|
|
// OperationContextMutator is called after creating the request context, but before executing the root resolver.
|
|
OperationContextMutator interface {
|
|
MutateOperationContext(ctx context.Context, rc *OperationContext) *gqlerror.Error
|
|
}
|
|
|
|
// OperationInterceptor is called for each incoming query, for basic requests the writer will be invoked once,
|
|
// for subscriptions it will be invoked multiple times.
|
|
OperationInterceptor interface {
|
|
InterceptOperation(ctx context.Context, next OperationHandler) ResponseHandler
|
|
}
|
|
|
|
// ResponseInterceptor is called around each graphql operation response. This can be called many times for a single
|
|
// operation the case of subscriptions.
|
|
ResponseInterceptor interface {
|
|
InterceptResponse(ctx context.Context, next ResponseHandler) *Response
|
|
}
|
|
|
|
RootFieldInterceptor interface {
|
|
InterceptRootField(ctx context.Context, next RootResolver) Marshaler
|
|
}
|
|
|
|
// FieldInterceptor called around each field
|
|
FieldInterceptor interface {
|
|
InterceptField(ctx context.Context, next Resolver) (res interface{}, err error)
|
|
}
|
|
|
|
// Transport provides support for different wire level encodings of graphql requests, eg Form, Get, Post, Websocket
|
|
Transport interface {
|
|
Supports(r *http.Request) bool
|
|
Do(w http.ResponseWriter, r *http.Request, exec GraphExecutor)
|
|
}
|
|
)
|
|
|
|
type Status int
|
|
|
|
func (p *RawParams) AddUpload(upload Upload, key, path string) *gqlerror.Error {
|
|
if !strings.HasPrefix(path, "variables.") {
|
|
return gqlerror.Errorf("invalid operations paths for key %s", key)
|
|
}
|
|
|
|
var ptr interface{} = p.Variables
|
|
parts := strings.Split(path, ".")
|
|
|
|
// skip the first part (variables) because we started there
|
|
for i, p := range parts[1:] {
|
|
last := i == len(parts)-2
|
|
if ptr == nil {
|
|
return gqlerror.Errorf("path is missing \"variables.\" prefix, key: %s, path: %s", key, path)
|
|
}
|
|
if index, parseNbrErr := strconv.Atoi(p); parseNbrErr == nil {
|
|
if last {
|
|
ptr.([]interface{})[index] = upload
|
|
} else {
|
|
ptr = ptr.([]interface{})[index]
|
|
}
|
|
} else {
|
|
if last {
|
|
ptr.(map[string]interface{})[p] = upload
|
|
} else {
|
|
ptr = ptr.(map[string]interface{})[p]
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|