mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 21:04:37 +03:00
Updated dependencies
This commit is contained in:
30
vendor/github.com/99designs/gqlgen/graphql/bool.go
generated
vendored
Normal file
30
vendor/github.com/99designs/gqlgen/graphql/bool.go
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func MarshalBoolean(b bool) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
if b {
|
||||
w.Write(trueLit)
|
||||
} else {
|
||||
w.Write(falseLit)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UnmarshalBoolean(v interface{}) (bool, error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return strings.ToLower(v) == "true", nil
|
||||
case int:
|
||||
return v != 0, nil
|
||||
case bool:
|
||||
return v, nil
|
||||
default:
|
||||
return false, fmt.Errorf("%T is not a bool", v)
|
||||
}
|
||||
}
|
||||
253
vendor/github.com/99designs/gqlgen/graphql/context.go
generated
vendored
Normal file
253
vendor/github.com/99designs/gqlgen/graphql/context.go
generated
vendored
Normal file
@@ -0,0 +1,253 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/vektah/gqlparser/ast"
|
||||
"github.com/vektah/gqlparser/gqlerror"
|
||||
)
|
||||
|
||||
type Resolver func(ctx context.Context) (res interface{}, err error)
|
||||
type FieldMiddleware func(ctx context.Context, next Resolver) (res interface{}, err error)
|
||||
type RequestMiddleware func(ctx context.Context, next func(ctx context.Context) []byte) []byte
|
||||
|
||||
type RequestContext struct {
|
||||
RawQuery string
|
||||
Variables map[string]interface{}
|
||||
Doc *ast.QueryDocument
|
||||
|
||||
ComplexityLimit int
|
||||
OperationComplexity int
|
||||
DisableIntrospection bool
|
||||
|
||||
// ErrorPresenter will be used to generate the error
|
||||
// message from errors given to Error().
|
||||
ErrorPresenter ErrorPresenterFunc
|
||||
Recover RecoverFunc
|
||||
ResolverMiddleware FieldMiddleware
|
||||
DirectiveMiddleware FieldMiddleware
|
||||
RequestMiddleware RequestMiddleware
|
||||
Tracer Tracer
|
||||
|
||||
errorsMu sync.Mutex
|
||||
Errors gqlerror.List
|
||||
extensionsMu sync.Mutex
|
||||
Extensions map[string]interface{}
|
||||
}
|
||||
|
||||
func DefaultResolverMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
func DefaultDirectiveMiddleware(ctx context.Context, next Resolver) (res interface{}, err error) {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
func DefaultRequestMiddleware(ctx context.Context, next func(ctx context.Context) []byte) []byte {
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
func NewRequestContext(doc *ast.QueryDocument, query string, variables map[string]interface{}) *RequestContext {
|
||||
return &RequestContext{
|
||||
Doc: doc,
|
||||
RawQuery: query,
|
||||
Variables: variables,
|
||||
ResolverMiddleware: DefaultResolverMiddleware,
|
||||
DirectiveMiddleware: DefaultDirectiveMiddleware,
|
||||
RequestMiddleware: DefaultRequestMiddleware,
|
||||
Recover: DefaultRecover,
|
||||
ErrorPresenter: DefaultErrorPresenter,
|
||||
Tracer: &NopTracer{},
|
||||
}
|
||||
}
|
||||
|
||||
type key string
|
||||
|
||||
const (
|
||||
request key = "request_context"
|
||||
resolver key = "resolver_context"
|
||||
)
|
||||
|
||||
func GetRequestContext(ctx context.Context) *RequestContext {
|
||||
val := ctx.Value(request)
|
||||
if val == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.(*RequestContext)
|
||||
}
|
||||
|
||||
func WithRequestContext(ctx context.Context, rc *RequestContext) context.Context {
|
||||
return context.WithValue(ctx, request, rc)
|
||||
}
|
||||
|
||||
type ResolverContext struct {
|
||||
Parent *ResolverContext
|
||||
// The name of the type this field belongs to
|
||||
Object string
|
||||
// These are the args after processing, they can be mutated in middleware to change what the resolver will get.
|
||||
Args map[string]interface{}
|
||||
// The raw field
|
||||
Field CollectedField
|
||||
// The index of array in path.
|
||||
Index *int
|
||||
// The result object of resolver
|
||||
Result interface{}
|
||||
}
|
||||
|
||||
func (r *ResolverContext) Path() []interface{} {
|
||||
var path []interface{}
|
||||
for it := r; it != nil; it = it.Parent {
|
||||
if it.Index != nil {
|
||||
path = append(path, *it.Index)
|
||||
} else if it.Field.Field != nil {
|
||||
path = append(path, it.Field.Alias)
|
||||
}
|
||||
}
|
||||
|
||||
// because we are walking up the chain, all the elements are backwards, do an inplace flip.
|
||||
for i := len(path)/2 - 1; i >= 0; i-- {
|
||||
opp := len(path) - 1 - i
|
||||
path[i], path[opp] = path[opp], path[i]
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func GetResolverContext(ctx context.Context) *ResolverContext {
|
||||
val, _ := ctx.Value(resolver).(*ResolverContext)
|
||||
return val
|
||||
}
|
||||
|
||||
func WithResolverContext(ctx context.Context, rc *ResolverContext) context.Context {
|
||||
rc.Parent = GetResolverContext(ctx)
|
||||
return context.WithValue(ctx, resolver, rc)
|
||||
}
|
||||
|
||||
// This is just a convenient wrapper method for CollectFields
|
||||
func CollectFieldsCtx(ctx context.Context, satisfies []string) []CollectedField {
|
||||
resctx := GetResolverContext(ctx)
|
||||
return CollectFields(ctx, resctx.Field.Selections, satisfies)
|
||||
}
|
||||
|
||||
// Errorf sends an error string to the client, passing it through the formatter.
|
||||
func (c *RequestContext) Errorf(ctx context.Context, format string, args ...interface{}) {
|
||||
c.errorsMu.Lock()
|
||||
defer c.errorsMu.Unlock()
|
||||
|
||||
c.Errors = append(c.Errors, c.ErrorPresenter(ctx, fmt.Errorf(format, args...)))
|
||||
}
|
||||
|
||||
// Error sends an error to the client, passing it through the formatter.
|
||||
func (c *RequestContext) Error(ctx context.Context, err error) {
|
||||
c.errorsMu.Lock()
|
||||
defer c.errorsMu.Unlock()
|
||||
|
||||
c.Errors = append(c.Errors, c.ErrorPresenter(ctx, err))
|
||||
}
|
||||
|
||||
// HasError returns true if the current field has already errored
|
||||
func (c *RequestContext) HasError(rctx *ResolverContext) bool {
|
||||
c.errorsMu.Lock()
|
||||
defer c.errorsMu.Unlock()
|
||||
path := rctx.Path()
|
||||
|
||||
for _, err := range c.Errors {
|
||||
if equalPath(err.Path, path) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetErrors returns a list of errors that occurred in the current field
|
||||
func (c *RequestContext) GetErrors(rctx *ResolverContext) gqlerror.List {
|
||||
c.errorsMu.Lock()
|
||||
defer c.errorsMu.Unlock()
|
||||
path := rctx.Path()
|
||||
|
||||
var errs gqlerror.List
|
||||
for _, err := range c.Errors {
|
||||
if equalPath(err.Path, path) {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
func equalPath(a []interface{}, b []interface{}) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(a); i++ {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// AddError is a convenience method for adding an error to the current response
|
||||
func AddError(ctx context.Context, err error) {
|
||||
GetRequestContext(ctx).Error(ctx, err)
|
||||
}
|
||||
|
||||
// AddErrorf is a convenience method for adding an error to the current response
|
||||
func AddErrorf(ctx context.Context, format string, args ...interface{}) {
|
||||
GetRequestContext(ctx).Errorf(ctx, format, args...)
|
||||
}
|
||||
|
||||
// RegisterExtension registers an extension, returns error if extension has already been registered
|
||||
func (c *RequestContext) RegisterExtension(key string, value interface{}) error {
|
||||
c.extensionsMu.Lock()
|
||||
defer c.extensionsMu.Unlock()
|
||||
|
||||
if c.Extensions == nil {
|
||||
c.Extensions = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if _, ok := c.Extensions[key]; ok {
|
||||
return fmt.Errorf("extension already registered for key %s", key)
|
||||
}
|
||||
|
||||
c.Extensions[key] = value
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChainFieldMiddleware add chain by FieldMiddleware
|
||||
func ChainFieldMiddleware(handleFunc ...FieldMiddleware) FieldMiddleware {
|
||||
n := len(handleFunc)
|
||||
|
||||
if n > 1 {
|
||||
lastI := n - 1
|
||||
return func(ctx context.Context, next Resolver) (interface{}, error) {
|
||||
var (
|
||||
chainHandler Resolver
|
||||
curI int
|
||||
)
|
||||
chainHandler = func(currentCtx context.Context) (interface{}, error) {
|
||||
if curI == lastI {
|
||||
return next(currentCtx)
|
||||
}
|
||||
curI++
|
||||
res, err := handleFunc[curI](currentCtx, chainHandler)
|
||||
curI--
|
||||
return res, err
|
||||
|
||||
}
|
||||
return handleFunc[0](ctx, chainHandler)
|
||||
}
|
||||
}
|
||||
|
||||
if n == 1 {
|
||||
return handleFunc[0]
|
||||
}
|
||||
|
||||
return func(ctx context.Context, next Resolver) (interface{}, error) {
|
||||
return next(ctx)
|
||||
}
|
||||
}
|
||||
31
vendor/github.com/99designs/gqlgen/graphql/error.go
generated
vendored
Normal file
31
vendor/github.com/99designs/gqlgen/graphql/error.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/vektah/gqlparser/gqlerror"
|
||||
)
|
||||
|
||||
type ErrorPresenterFunc func(context.Context, error) *gqlerror.Error
|
||||
|
||||
type ExtendedError interface {
|
||||
Extensions() map[string]interface{}
|
||||
}
|
||||
|
||||
func DefaultErrorPresenter(ctx context.Context, err error) *gqlerror.Error {
|
||||
if gqlerr, ok := err.(*gqlerror.Error); ok {
|
||||
gqlerr.Path = GetResolverContext(ctx).Path()
|
||||
return gqlerr
|
||||
}
|
||||
|
||||
var extensions map[string]interface{}
|
||||
if ee, ok := err.(ExtendedError); ok {
|
||||
extensions = ee.Extensions()
|
||||
}
|
||||
|
||||
return &gqlerror.Error{
|
||||
Message: err.Error(),
|
||||
Path: GetResolverContext(ctx).Path(),
|
||||
Extensions: extensions,
|
||||
}
|
||||
}
|
||||
135
vendor/github.com/99designs/gqlgen/graphql/exec.go
generated
vendored
Normal file
135
vendor/github.com/99designs/gqlgen/graphql/exec.go
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/vektah/gqlparser/ast"
|
||||
)
|
||||
|
||||
type ExecutableSchema interface {
|
||||
Schema() *ast.Schema
|
||||
|
||||
Complexity(typeName, fieldName string, childComplexity int, args map[string]interface{}) (int, bool)
|
||||
Query(ctx context.Context, op *ast.OperationDefinition) *Response
|
||||
Mutation(ctx context.Context, op *ast.OperationDefinition) *Response
|
||||
Subscription(ctx context.Context, op *ast.OperationDefinition) func() *Response
|
||||
}
|
||||
|
||||
func CollectFields(ctx context.Context, selSet ast.SelectionSet, satisfies []string) []CollectedField {
|
||||
return collectFields(GetRequestContext(ctx), selSet, satisfies, map[string]bool{})
|
||||
}
|
||||
|
||||
func collectFields(reqCtx *RequestContext, selSet ast.SelectionSet, satisfies []string, visited map[string]bool) []CollectedField {
|
||||
var groupedFields []CollectedField
|
||||
|
||||
for _, sel := range selSet {
|
||||
switch sel := sel.(type) {
|
||||
case *ast.Field:
|
||||
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
|
||||
continue
|
||||
}
|
||||
f := getOrCreateField(&groupedFields, sel.Alias, func() CollectedField {
|
||||
return CollectedField{Field: sel}
|
||||
})
|
||||
|
||||
f.Selections = append(f.Selections, sel.SelectionSet...)
|
||||
case *ast.InlineFragment:
|
||||
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) || !instanceOf(sel.TypeCondition, satisfies) {
|
||||
continue
|
||||
}
|
||||
for _, childField := range collectFields(reqCtx, sel.SelectionSet, satisfies, visited) {
|
||||
f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField })
|
||||
f.Selections = append(f.Selections, childField.Selections...)
|
||||
}
|
||||
|
||||
case *ast.FragmentSpread:
|
||||
if !shouldIncludeNode(sel.Directives, reqCtx.Variables) {
|
||||
continue
|
||||
}
|
||||
fragmentName := sel.Name
|
||||
if _, seen := visited[fragmentName]; seen {
|
||||
continue
|
||||
}
|
||||
visited[fragmentName] = true
|
||||
|
||||
fragment := reqCtx.Doc.Fragments.ForName(fragmentName)
|
||||
if fragment == nil {
|
||||
// should never happen, validator has already run
|
||||
panic(fmt.Errorf("missing fragment %s", fragmentName))
|
||||
}
|
||||
|
||||
if !instanceOf(fragment.TypeCondition, satisfies) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, childField := range collectFields(reqCtx, fragment.SelectionSet, satisfies, visited) {
|
||||
f := getOrCreateField(&groupedFields, childField.Name, func() CollectedField { return childField })
|
||||
f.Selections = append(f.Selections, childField.Selections...)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(fmt.Errorf("unsupported %T", sel))
|
||||
}
|
||||
}
|
||||
|
||||
return groupedFields
|
||||
}
|
||||
|
||||
type CollectedField struct {
|
||||
*ast.Field
|
||||
|
||||
Selections ast.SelectionSet
|
||||
}
|
||||
|
||||
func instanceOf(val string, satisfies []string) bool {
|
||||
for _, s := range satisfies {
|
||||
if val == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func getOrCreateField(c *[]CollectedField, name string, creator func() CollectedField) *CollectedField {
|
||||
for i, cf := range *c {
|
||||
if cf.Alias == name {
|
||||
return &(*c)[i]
|
||||
}
|
||||
}
|
||||
|
||||
f := creator()
|
||||
|
||||
*c = append(*c, f)
|
||||
return &(*c)[len(*c)-1]
|
||||
}
|
||||
|
||||
func shouldIncludeNode(directives ast.DirectiveList, variables map[string]interface{}) bool {
|
||||
skip, include := false, true
|
||||
|
||||
if d := directives.ForName("skip"); d != nil {
|
||||
skip = resolveIfArgument(d, variables)
|
||||
}
|
||||
|
||||
if d := directives.ForName("include"); d != nil {
|
||||
include = resolveIfArgument(d, variables)
|
||||
}
|
||||
|
||||
return !skip && include
|
||||
}
|
||||
|
||||
func resolveIfArgument(d *ast.Directive, variables map[string]interface{}) bool {
|
||||
arg := d.Arguments.ForName("if")
|
||||
if arg == nil {
|
||||
panic(fmt.Sprintf("%s: argument 'if' not defined", d.Name))
|
||||
}
|
||||
value, err := arg.Value.Value(variables)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ret, ok := value.(bool)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("%s: argument 'if' is not a boolean", d.Name))
|
||||
}
|
||||
return ret
|
||||
}
|
||||
63
vendor/github.com/99designs/gqlgen/graphql/fieldset.go
generated
vendored
Normal file
63
vendor/github.com/99designs/gqlgen/graphql/fieldset.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type FieldSet struct {
|
||||
fields []CollectedField
|
||||
Values []Marshaler
|
||||
delayed []delayedResult
|
||||
}
|
||||
|
||||
type delayedResult struct {
|
||||
i int
|
||||
f func() Marshaler
|
||||
}
|
||||
|
||||
func NewFieldSet(fields []CollectedField) *FieldSet {
|
||||
return &FieldSet{
|
||||
fields: fields,
|
||||
Values: make([]Marshaler, len(fields)),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *FieldSet) Concurrently(i int, f func() Marshaler) {
|
||||
m.delayed = append(m.delayed, delayedResult{i: i, f: f})
|
||||
}
|
||||
|
||||
func (m *FieldSet) Dispatch() {
|
||||
if len(m.delayed) == 1 {
|
||||
// only one concurrent task, no need to spawn a goroutine or deal create waitgroups
|
||||
d := m.delayed[0]
|
||||
m.Values[d.i] = d.f()
|
||||
} else if len(m.delayed) > 1 {
|
||||
// more than one concurrent task, use the main goroutine to do one, only spawn goroutines for the others
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, d := range m.delayed[1:] {
|
||||
wg.Add(1)
|
||||
go func(d delayedResult) {
|
||||
m.Values[d.i] = d.f()
|
||||
wg.Done()
|
||||
}(d)
|
||||
}
|
||||
|
||||
m.Values[m.delayed[0].i] = m.delayed[0].f()
|
||||
wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (m *FieldSet) MarshalGQL(writer io.Writer) {
|
||||
writer.Write(openBrace)
|
||||
for i, field := range m.fields {
|
||||
if i != 0 {
|
||||
writer.Write(comma)
|
||||
}
|
||||
writeQuotedString(writer, field.Alias)
|
||||
writer.Write(colon)
|
||||
m.Values[i].MarshalGQL(writer)
|
||||
}
|
||||
writer.Write(closeBrace)
|
||||
}
|
||||
31
vendor/github.com/99designs/gqlgen/graphql/float.go
generated
vendored
Normal file
31
vendor/github.com/99designs/gqlgen/graphql/float.go
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func MarshalFloat(f float64) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
io.WriteString(w, fmt.Sprintf("%g", f))
|
||||
})
|
||||
}
|
||||
|
||||
func UnmarshalFloat(v interface{}) (float64, error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return strconv.ParseFloat(v, 64)
|
||||
case int:
|
||||
return float64(v), nil
|
||||
case int64:
|
||||
return float64(v), nil
|
||||
case float64:
|
||||
return v, nil
|
||||
case json.Number:
|
||||
return strconv.ParseFloat(string(v), 64)
|
||||
default:
|
||||
return 0, fmt.Errorf("%T is not an float", v)
|
||||
}
|
||||
}
|
||||
36
vendor/github.com/99designs/gqlgen/graphql/id.go
generated
vendored
Normal file
36
vendor/github.com/99designs/gqlgen/graphql/id.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func MarshalID(s string) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
io.WriteString(w, strconv.Quote(s))
|
||||
})
|
||||
}
|
||||
func UnmarshalID(v interface{}) (string, error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return v, nil
|
||||
case json.Number:
|
||||
return string(v), nil
|
||||
case int:
|
||||
return strconv.Itoa(v), nil
|
||||
case float64:
|
||||
return fmt.Sprintf("%f", v), nil
|
||||
case bool:
|
||||
if v {
|
||||
return "true", nil
|
||||
} else {
|
||||
return "false", nil
|
||||
}
|
||||
case nil:
|
||||
return "null", nil
|
||||
default:
|
||||
return "", fmt.Errorf("%T is not a string", v)
|
||||
}
|
||||
}
|
||||
29
vendor/github.com/99designs/gqlgen/graphql/int.go
generated
vendored
Normal file
29
vendor/github.com/99designs/gqlgen/graphql/int.go
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func MarshalInt(i int) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
io.WriteString(w, strconv.Itoa(i))
|
||||
})
|
||||
}
|
||||
|
||||
func UnmarshalInt(v interface{}) (int, error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return strconv.Atoi(v)
|
||||
case int:
|
||||
return v, nil
|
||||
case int64:
|
||||
return int(v), nil
|
||||
case json.Number:
|
||||
return strconv.Atoi(string(v))
|
||||
default:
|
||||
return 0, fmt.Errorf("%T is not an int", v)
|
||||
}
|
||||
}
|
||||
72
vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go
generated
vendored
Normal file
72
vendor/github.com/99designs/gqlgen/graphql/introspection/introspection.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// introspection implements the spec defined in https://github.com/facebook/graphql/blob/master/spec/Section%204%20--%20Introspection.md#schema-introspection
|
||||
package introspection
|
||||
|
||||
import "github.com/vektah/gqlparser/ast"
|
||||
|
||||
type (
|
||||
Directive struct {
|
||||
Name string
|
||||
Description string
|
||||
Locations []string
|
||||
Args []InputValue
|
||||
}
|
||||
|
||||
EnumValue struct {
|
||||
Name string
|
||||
Description string
|
||||
deprecation *ast.Directive
|
||||
}
|
||||
|
||||
Field struct {
|
||||
Name string
|
||||
Description string
|
||||
Type *Type
|
||||
Args []InputValue
|
||||
deprecation *ast.Directive
|
||||
}
|
||||
|
||||
InputValue struct {
|
||||
Name string
|
||||
Description string
|
||||
DefaultValue *string
|
||||
Type *Type
|
||||
}
|
||||
)
|
||||
|
||||
func WrapSchema(schema *ast.Schema) *Schema {
|
||||
return &Schema{schema: schema}
|
||||
}
|
||||
|
||||
func (f *EnumValue) IsDeprecated() bool {
|
||||
return f.deprecation != nil
|
||||
}
|
||||
|
||||
func (f *EnumValue) DeprecationReason() *string {
|
||||
if f.deprecation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reason := f.deprecation.Arguments.ForName("reason")
|
||||
if reason == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &reason.Value.Raw
|
||||
}
|
||||
|
||||
func (f *Field) IsDeprecated() bool {
|
||||
return f.deprecation != nil
|
||||
}
|
||||
|
||||
func (f *Field) DeprecationReason() *string {
|
||||
if f.deprecation == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
reason := f.deprecation.Arguments.ForName("reason")
|
||||
if reason == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &reason.Value.Raw
|
||||
}
|
||||
104
vendor/github.com/99designs/gqlgen/graphql/introspection/query.go
generated
vendored
Normal file
104
vendor/github.com/99designs/gqlgen/graphql/introspection/query.go
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
package introspection
|
||||
|
||||
// Query is the query generated by graphiql to determine type information
|
||||
const Query = `
|
||||
query IntrospectionQuery {
|
||||
__schema {
|
||||
queryType {
|
||||
name
|
||||
}
|
||||
mutationType {
|
||||
name
|
||||
}
|
||||
subscriptionType {
|
||||
name
|
||||
}
|
||||
types {
|
||||
...FullType
|
||||
}
|
||||
directives {
|
||||
name
|
||||
description
|
||||
locations
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment FullType on __Type {
|
||||
kind
|
||||
name
|
||||
description
|
||||
fields(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
args {
|
||||
...InputValue
|
||||
}
|
||||
type {
|
||||
...TypeRef
|
||||
}
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
inputFields {
|
||||
...InputValue
|
||||
}
|
||||
interfaces {
|
||||
...TypeRef
|
||||
}
|
||||
enumValues(includeDeprecated: true) {
|
||||
name
|
||||
description
|
||||
isDeprecated
|
||||
deprecationReason
|
||||
}
|
||||
possibleTypes {
|
||||
...TypeRef
|
||||
}
|
||||
}
|
||||
|
||||
fragment InputValue on __InputValue {
|
||||
name
|
||||
description
|
||||
type {
|
||||
...TypeRef
|
||||
}
|
||||
defaultValue
|
||||
}
|
||||
|
||||
fragment TypeRef on __Type {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
ofType {
|
||||
kind
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
68
vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go
generated
vendored
Normal file
68
vendor/github.com/99designs/gqlgen/graphql/introspection/schema.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package introspection
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/ast"
|
||||
)
|
||||
|
||||
type Schema struct {
|
||||
schema *ast.Schema
|
||||
}
|
||||
|
||||
func (s *Schema) Types() []Type {
|
||||
var types []Type
|
||||
for _, typ := range s.schema.Types {
|
||||
if strings.HasPrefix(typ.Name, "__") {
|
||||
continue
|
||||
}
|
||||
types = append(types, *WrapTypeFromDef(s.schema, typ))
|
||||
}
|
||||
return types
|
||||
}
|
||||
|
||||
func (s *Schema) QueryType() *Type {
|
||||
return WrapTypeFromDef(s.schema, s.schema.Query)
|
||||
}
|
||||
|
||||
func (s *Schema) MutationType() *Type {
|
||||
return WrapTypeFromDef(s.schema, s.schema.Mutation)
|
||||
}
|
||||
|
||||
func (s *Schema) SubscriptionType() *Type {
|
||||
return WrapTypeFromDef(s.schema, s.schema.Subscription)
|
||||
}
|
||||
|
||||
func (s *Schema) Directives() []Directive {
|
||||
var res []Directive
|
||||
|
||||
for _, d := range s.schema.Directives {
|
||||
res = append(res, s.directiveFromDef(d))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (s *Schema) directiveFromDef(d *ast.DirectiveDefinition) Directive {
|
||||
var locs []string
|
||||
for _, loc := range d.Locations {
|
||||
locs = append(locs, string(loc))
|
||||
}
|
||||
|
||||
var args []InputValue
|
||||
for _, arg := range d.Arguments {
|
||||
args = append(args, InputValue{
|
||||
Name: arg.Name,
|
||||
Description: arg.Description,
|
||||
DefaultValue: defaultValue(arg.DefaultValue),
|
||||
Type: WrapTypeFromType(s.schema, arg.Type),
|
||||
})
|
||||
}
|
||||
|
||||
return Directive{
|
||||
Name: d.Name,
|
||||
Description: d.Description,
|
||||
Locations: locs,
|
||||
Args: args,
|
||||
}
|
||||
}
|
||||
172
vendor/github.com/99designs/gqlgen/graphql/introspection/type.go
generated
vendored
Normal file
172
vendor/github.com/99designs/gqlgen/graphql/introspection/type.go
generated
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
package introspection
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/vektah/gqlparser/ast"
|
||||
)
|
||||
|
||||
type Type struct {
|
||||
schema *ast.Schema
|
||||
def *ast.Definition
|
||||
typ *ast.Type
|
||||
}
|
||||
|
||||
func WrapTypeFromDef(s *ast.Schema, def *ast.Definition) *Type {
|
||||
if def == nil {
|
||||
return nil
|
||||
}
|
||||
return &Type{schema: s, def: def}
|
||||
}
|
||||
|
||||
func WrapTypeFromType(s *ast.Schema, typ *ast.Type) *Type {
|
||||
if typ == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !typ.NonNull && typ.NamedType != "" {
|
||||
return &Type{schema: s, def: s.Types[typ.NamedType]}
|
||||
}
|
||||
return &Type{schema: s, typ: typ}
|
||||
}
|
||||
|
||||
func (t *Type) Kind() string {
|
||||
if t.typ != nil {
|
||||
if t.typ.NonNull {
|
||||
return "NON_NULL"
|
||||
}
|
||||
|
||||
if t.typ.Elem != nil {
|
||||
return "LIST"
|
||||
}
|
||||
} else {
|
||||
return string(t.def.Kind)
|
||||
}
|
||||
|
||||
panic("UNKNOWN")
|
||||
}
|
||||
|
||||
func (t *Type) Name() *string {
|
||||
if t.def == nil {
|
||||
return nil
|
||||
}
|
||||
return &t.def.Name
|
||||
}
|
||||
|
||||
func (t *Type) Description() string {
|
||||
if t.def == nil {
|
||||
return ""
|
||||
}
|
||||
return t.def.Description
|
||||
}
|
||||
|
||||
func (t *Type) Fields(includeDeprecated bool) []Field {
|
||||
if t.def == nil || (t.def.Kind != ast.Object && t.def.Kind != ast.Interface) {
|
||||
return nil
|
||||
}
|
||||
var fields []Field
|
||||
for _, f := range t.def.Fields {
|
||||
if strings.HasPrefix(f.Name, "__") {
|
||||
continue
|
||||
}
|
||||
|
||||
var args []InputValue
|
||||
for _, arg := range f.Arguments {
|
||||
args = append(args, InputValue{
|
||||
Type: WrapTypeFromType(t.schema, arg.Type),
|
||||
Name: arg.Name,
|
||||
Description: arg.Description,
|
||||
DefaultValue: defaultValue(arg.DefaultValue),
|
||||
})
|
||||
}
|
||||
|
||||
fields = append(fields, Field{
|
||||
Name: f.Name,
|
||||
Description: f.Description,
|
||||
Args: args,
|
||||
Type: WrapTypeFromType(t.schema, f.Type),
|
||||
deprecation: f.Directives.ForName("deprecated"),
|
||||
})
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func (t *Type) InputFields() []InputValue {
|
||||
if t.def == nil || t.def.Kind != ast.InputObject {
|
||||
return nil
|
||||
}
|
||||
|
||||
var res []InputValue
|
||||
for _, f := range t.def.Fields {
|
||||
res = append(res, InputValue{
|
||||
Name: f.Name,
|
||||
Description: f.Description,
|
||||
Type: WrapTypeFromType(t.schema, f.Type),
|
||||
DefaultValue: defaultValue(f.DefaultValue),
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func defaultValue(value *ast.Value) *string {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
val := value.String()
|
||||
return &val
|
||||
}
|
||||
|
||||
func (t *Type) Interfaces() []Type {
|
||||
if t.def == nil || t.def.Kind != ast.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
var res []Type
|
||||
for _, intf := range t.def.Interfaces {
|
||||
res = append(res, *WrapTypeFromDef(t.schema, t.schema.Types[intf]))
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *Type) PossibleTypes() []Type {
|
||||
if t.def == nil || (t.def.Kind != ast.Interface && t.def.Kind != ast.Union) {
|
||||
return nil
|
||||
}
|
||||
|
||||
var res []Type
|
||||
for _, pt := range t.schema.GetPossibleTypes(t.def) {
|
||||
res = append(res, *WrapTypeFromDef(t.schema, pt))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *Type) EnumValues(includeDeprecated bool) []EnumValue {
|
||||
if t.def == nil || t.def.Kind != ast.Enum {
|
||||
return nil
|
||||
}
|
||||
|
||||
var res []EnumValue
|
||||
for _, val := range t.def.EnumValues {
|
||||
res = append(res, EnumValue{
|
||||
Name: val.Name,
|
||||
Description: val.Description,
|
||||
deprecation: val.Directives.ForName("deprecated"),
|
||||
})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (t *Type) OfType() *Type {
|
||||
if t.typ == nil {
|
||||
return nil
|
||||
}
|
||||
if t.typ.NonNull {
|
||||
// fake non null nodes
|
||||
cpy := *t.typ
|
||||
cpy.NonNull = false
|
||||
|
||||
return WrapTypeFromType(t.schema, &cpy)
|
||||
}
|
||||
return WrapTypeFromType(t.schema, t.typ.Elem)
|
||||
}
|
||||
52
vendor/github.com/99designs/gqlgen/graphql/jsonw.go
generated
vendored
Normal file
52
vendor/github.com/99designs/gqlgen/graphql/jsonw.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
var nullLit = []byte(`null`)
|
||||
var trueLit = []byte(`true`)
|
||||
var falseLit = []byte(`false`)
|
||||
var openBrace = []byte(`{`)
|
||||
var closeBrace = []byte(`}`)
|
||||
var openBracket = []byte(`[`)
|
||||
var closeBracket = []byte(`]`)
|
||||
var colon = []byte(`:`)
|
||||
var comma = []byte(`,`)
|
||||
|
||||
var Null = &lit{nullLit}
|
||||
var True = &lit{trueLit}
|
||||
var False = &lit{falseLit}
|
||||
|
||||
type Marshaler interface {
|
||||
MarshalGQL(w io.Writer)
|
||||
}
|
||||
|
||||
type Unmarshaler interface {
|
||||
UnmarshalGQL(v interface{}) error
|
||||
}
|
||||
|
||||
type WriterFunc func(writer io.Writer)
|
||||
|
||||
func (f WriterFunc) MarshalGQL(w io.Writer) {
|
||||
f(w)
|
||||
}
|
||||
|
||||
type Array []Marshaler
|
||||
|
||||
func (a Array) MarshalGQL(writer io.Writer) {
|
||||
writer.Write(openBracket)
|
||||
for i, val := range a {
|
||||
if i != 0 {
|
||||
writer.Write(comma)
|
||||
}
|
||||
val.MarshalGQL(writer)
|
||||
}
|
||||
writer.Write(closeBracket)
|
||||
}
|
||||
|
||||
type lit struct{ b []byte }
|
||||
|
||||
func (l lit) MarshalGQL(w io.Writer) {
|
||||
w.Write(l.b)
|
||||
}
|
||||
24
vendor/github.com/99designs/gqlgen/graphql/map.go
generated
vendored
Normal file
24
vendor/github.com/99designs/gqlgen/graphql/map.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func MarshalMap(val map[string]interface{}) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
err := json.NewEncoder(w).Encode(val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func UnmarshalMap(v interface{}) (map[string]interface{}, error) {
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%T is not a map", v)
|
||||
}
|
||||
14
vendor/github.com/99designs/gqlgen/graphql/oneshot.go
generated
vendored
Normal file
14
vendor/github.com/99designs/gqlgen/graphql/oneshot.go
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
package graphql
|
||||
|
||||
func OneShot(resp *Response) func() *Response {
|
||||
var oneshot bool
|
||||
|
||||
return func() *Response {
|
||||
if oneshot {
|
||||
return nil
|
||||
}
|
||||
oneshot = true
|
||||
|
||||
return resp
|
||||
}
|
||||
}
|
||||
19
vendor/github.com/99designs/gqlgen/graphql/recovery.go
generated
vendored
Normal file
19
vendor/github.com/99designs/gqlgen/graphql/recovery.go
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
type RecoverFunc func(ctx context.Context, err interface{}) (userMessage error)
|
||||
|
||||
func DefaultRecover(ctx context.Context, err interface{}) error {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
fmt.Fprintln(os.Stderr)
|
||||
debug.PrintStack()
|
||||
|
||||
return errors.New("internal system error")
|
||||
}
|
||||
24
vendor/github.com/99designs/gqlgen/graphql/response.go
generated
vendored
Normal file
24
vendor/github.com/99designs/gqlgen/graphql/response.go
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/vektah/gqlparser/gqlerror"
|
||||
)
|
||||
|
||||
// Errors are intentionally serialized first based on the advice in
|
||||
// https://github.com/facebook/graphql/commit/7b40390d48680b15cb93e02d46ac5eb249689876#diff-757cea6edf0288677a9eea4cfc801d87R107
|
||||
// and https://github.com/facebook/graphql/pull/384
|
||||
type Response struct {
|
||||
Errors gqlerror.List `json:"errors,omitempty"`
|
||||
Data json.RawMessage `json:"data"`
|
||||
Extensions map[string]interface{} `json:"extensions,omitempty"`
|
||||
}
|
||||
|
||||
func ErrorResponse(ctx context.Context, messagef string, args ...interface{}) *Response {
|
||||
return &Response{
|
||||
Errors: gqlerror.List{{Message: fmt.Sprintf(messagef, args...)}},
|
||||
}
|
||||
}
|
||||
68
vendor/github.com/99designs/gqlgen/graphql/string.go
generated
vendored
Normal file
68
vendor/github.com/99designs/gqlgen/graphql/string.go
generated
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const encodeHex = "0123456789ABCDEF"
|
||||
|
||||
func MarshalString(s string) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
writeQuotedString(w, s)
|
||||
})
|
||||
}
|
||||
|
||||
func writeQuotedString(w io.Writer, s string) {
|
||||
start := 0
|
||||
io.WriteString(w, `"`)
|
||||
|
||||
for i, c := range s {
|
||||
if c < 0x20 || c == '\\' || c == '"' {
|
||||
io.WriteString(w, s[start:i])
|
||||
|
||||
switch c {
|
||||
case '\t':
|
||||
io.WriteString(w, `\t`)
|
||||
case '\r':
|
||||
io.WriteString(w, `\r`)
|
||||
case '\n':
|
||||
io.WriteString(w, `\n`)
|
||||
case '\\':
|
||||
io.WriteString(w, `\\`)
|
||||
case '"':
|
||||
io.WriteString(w, `\"`)
|
||||
default:
|
||||
io.WriteString(w, `\u00`)
|
||||
w.Write([]byte{encodeHex[c>>4], encodeHex[c&0xf]})
|
||||
}
|
||||
|
||||
start = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
io.WriteString(w, s[start:])
|
||||
io.WriteString(w, `"`)
|
||||
}
|
||||
|
||||
func UnmarshalString(v interface{}) (string, error) {
|
||||
switch v := v.(type) {
|
||||
case string:
|
||||
return v, nil
|
||||
case int:
|
||||
return strconv.Itoa(v), nil
|
||||
case float64:
|
||||
return fmt.Sprintf("%f", v), nil
|
||||
case bool:
|
||||
if v {
|
||||
return "true", nil
|
||||
} else {
|
||||
return "false", nil
|
||||
}
|
||||
case nil:
|
||||
return "null", nil
|
||||
default:
|
||||
return "", fmt.Errorf("%T is not a string", v)
|
||||
}
|
||||
}
|
||||
21
vendor/github.com/99designs/gqlgen/graphql/time.go
generated
vendored
Normal file
21
vendor/github.com/99designs/gqlgen/graphql/time.go
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func MarshalTime(t time.Time) Marshaler {
|
||||
return WriterFunc(func(w io.Writer) {
|
||||
io.WriteString(w, strconv.Quote(t.Format(time.RFC3339)))
|
||||
})
|
||||
}
|
||||
|
||||
func UnmarshalTime(v interface{}) (time.Time, error) {
|
||||
if tmpStr, ok := v.(string); ok {
|
||||
return time.Parse(time.RFC3339, tmpStr)
|
||||
}
|
||||
return time.Time{}, errors.New("time should be RFC3339 formatted string")
|
||||
}
|
||||
58
vendor/github.com/99designs/gqlgen/graphql/tracer.go
generated
vendored
Normal file
58
vendor/github.com/99designs/gqlgen/graphql/tracer.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
var _ Tracer = (*NopTracer)(nil)
|
||||
|
||||
type Tracer interface {
|
||||
StartOperationParsing(ctx context.Context) context.Context
|
||||
EndOperationParsing(ctx context.Context)
|
||||
StartOperationValidation(ctx context.Context) context.Context
|
||||
EndOperationValidation(ctx context.Context)
|
||||
StartOperationExecution(ctx context.Context) context.Context
|
||||
StartFieldExecution(ctx context.Context, field CollectedField) context.Context
|
||||
StartFieldResolverExecution(ctx context.Context, rc *ResolverContext) context.Context
|
||||
StartFieldChildExecution(ctx context.Context) context.Context
|
||||
EndFieldExecution(ctx context.Context)
|
||||
EndOperationExecution(ctx context.Context)
|
||||
}
|
||||
|
||||
type NopTracer struct{}
|
||||
|
||||
func (NopTracer) StartOperationParsing(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (NopTracer) EndOperationParsing(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (NopTracer) StartOperationValidation(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (NopTracer) EndOperationValidation(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (NopTracer) StartOperationExecution(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (NopTracer) StartFieldExecution(ctx context.Context, field CollectedField) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (NopTracer) StartFieldResolverExecution(ctx context.Context, rc *ResolverContext) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (NopTracer) StartFieldChildExecution(ctx context.Context) context.Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (NopTracer) EndFieldExecution(ctx context.Context) {
|
||||
}
|
||||
|
||||
func (NopTracer) EndOperationExecution(ctx context.Context) {
|
||||
}
|
||||
3
vendor/github.com/99designs/gqlgen/graphql/version.go
generated
vendored
Normal file
3
vendor/github.com/99designs/gqlgen/graphql/version.go
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
package graphql
|
||||
|
||||
const Version = "dev"
|
||||
Reference in New Issue
Block a user