mirror of
https://github.com/stashapp/stash.git
synced 2026-01-16 05:02:44 +01: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>
259 lines
7.2 KiB
Text
259 lines
7.2 KiB
Text
{{ reserveImport "context" }}
|
|
{{ reserveImport "errors" }}
|
|
{{ reserveImport "fmt" }}
|
|
{{ reserveImport "strings" }}
|
|
{{ reserveImport "sync" }}
|
|
|
|
{{ reserveImport "github.com/99designs/gqlgen/plugin/federation/fedruntime" }}
|
|
|
|
var (
|
|
ErrUnknownType = errors.New("unknown type")
|
|
ErrTypeNotFound = errors.New("type not found")
|
|
)
|
|
|
|
func (ec *executionContext) __resolve__service(ctx context.Context) (fedruntime.Service, error) {
|
|
if ec.DisableIntrospection {
|
|
return fedruntime.Service{}, errors.New("federated introspection disabled")
|
|
}
|
|
|
|
var sdl []string
|
|
|
|
for _, src := range sources {
|
|
if src.BuiltIn {
|
|
continue
|
|
}
|
|
sdl = append(sdl, src.Input)
|
|
}
|
|
|
|
return fedruntime.Service{
|
|
SDL: strings.Join(sdl, "\n"),
|
|
}, nil
|
|
}
|
|
|
|
{{if .Entities}}
|
|
func (ec *executionContext) __resolve_entities(ctx context.Context, representations []map[string]interface{}) []fedruntime.Entity {
|
|
list := make([]fedruntime.Entity, len(representations))
|
|
|
|
repsMap := map[string]struct {
|
|
i []int
|
|
r []map[string]interface{}
|
|
}{}
|
|
|
|
// We group entities by typename so that we can parallelize their resolution.
|
|
// This is particularly helpful when there are entity groups in multi mode.
|
|
buildRepresentationGroups := func(reps []map[string]interface{}) {
|
|
for i, rep := range reps {
|
|
typeName, ok := rep["__typename"].(string)
|
|
if !ok {
|
|
// If there is no __typename, we just skip the representation;
|
|
// we just won't be resolving these unknown types.
|
|
ec.Error(ctx, errors.New("__typename must be an existing string"))
|
|
continue
|
|
}
|
|
|
|
_r := repsMap[typeName]
|
|
_r.i = append(_r.i, i)
|
|
_r.r = append(_r.r, rep)
|
|
repsMap[typeName] = _r
|
|
}
|
|
}
|
|
|
|
isMulti := func(typeName string) bool {
|
|
switch typeName {
|
|
{{- range .Entities -}}
|
|
{{- if .Resolvers -}}
|
|
{{- if .Multi -}}
|
|
case "{{.Def.Name}}":
|
|
return true
|
|
{{ end }}
|
|
{{- end -}}
|
|
{{- end -}}
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
resolveEntity := func(ctx context.Context, typeName string, rep map[string]interface{}, idx []int, i int) (err error) {
|
|
// we need to do our own panic handling, because we may be called in a
|
|
// goroutine, where the usual panic handling can't catch us
|
|
defer func () {
|
|
if r := recover(); r != nil {
|
|
err = ec.Recover(ctx, r)
|
|
}
|
|
}()
|
|
|
|
switch typeName {
|
|
{{ range $_, $entity := .Entities }}
|
|
{{- if and .Resolvers (not .Multi) -}}
|
|
case "{{.Def.Name}}":
|
|
resolverName, err := entityResolverNameFor{{.Def.Name}}(ctx, rep)
|
|
if err != nil {
|
|
return fmt.Errorf(`finding resolver for Entity "{{.Def.Name}}": %w`, err)
|
|
}
|
|
switch resolverName {
|
|
{{ range $i, $resolver := .Resolvers }}
|
|
case "{{.ResolverName}}":
|
|
{{- range $j, $keyField := .KeyFields }}
|
|
id{{$j}}, err := ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
|
|
if err != nil {
|
|
return fmt.Errorf(`unmarshalling param {{$j}} for {{$resolver.ResolverName}}(): %w`, err)
|
|
}
|
|
{{- end}}
|
|
entity, err := ec.resolvers.Entity().{{.ResolverName | go}}(ctx, {{- range $j, $_ := .KeyFields -}} id{{$j}}, {{end}})
|
|
if err != nil {
|
|
return fmt.Errorf(`resolving Entity "{{$entity.Def.Name}}": %w`, err)
|
|
}
|
|
{{ range $entity.Requires }}
|
|
entity.{{.Field.JoinGo `.`}}, err = ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
{{- end }}
|
|
list[idx[i]] = entity
|
|
return nil
|
|
{{- end }}
|
|
}
|
|
{{ end }}
|
|
{{- end }}
|
|
}
|
|
return fmt.Errorf("%w: %s", ErrUnknownType, typeName)
|
|
}
|
|
|
|
resolveManyEntities := func(ctx context.Context, typeName string, reps []map[string]interface{}, idx []int) (err error) {
|
|
// we need to do our own panic handling, because we may be called in a
|
|
// goroutine, where the usual panic handling can't catch us
|
|
defer func () {
|
|
if r := recover(); r != nil {
|
|
err = ec.Recover(ctx, r)
|
|
}
|
|
}()
|
|
|
|
switch typeName {
|
|
{{ range $_, $entity := .Entities }}
|
|
{{ if and .Resolvers .Multi -}}
|
|
case "{{.Def.Name}}":
|
|
{{range $i, $_ := .Resolvers -}}
|
|
_reps := make([]*{{.InputType}}, len(reps))
|
|
|
|
for i, rep := range reps {
|
|
{{ range $i, $keyField := .KeyFields -}}
|
|
id{{$i}}, err := ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
|
|
if err != nil {
|
|
return errors.New(fmt.Sprintf("Field %s undefined in schema.", "{{.Definition.Name}}"))
|
|
}
|
|
{{end}}
|
|
|
|
_reps[i] = &{{.InputType}} {
|
|
{{ range $i, $keyField := .KeyFields -}}
|
|
{{$keyField.Field.ToGo}}: id{{$i}},
|
|
{{end}}
|
|
}
|
|
}
|
|
|
|
entities, err := ec.resolvers.Entity().{{.ResolverName | go}}(ctx, _reps)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for i, entity := range entities {
|
|
{{- range $entity.Requires }}
|
|
entity.{{.Field.JoinGo `.`}}, err = ec.{{.Type.UnmarshalFunc}}(ctx, reps[i]["{{.Field.Join `"].(map[string]interface{})["`}}"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
{{- end}}
|
|
list[idx[i]] = entity
|
|
}
|
|
return nil
|
|
{{ end }}
|
|
{{ end }}
|
|
{{- end }}
|
|
default:
|
|
return errors.New("unknown type: "+typeName)
|
|
}
|
|
}
|
|
|
|
resolveEntityGroup := func(typeName string, reps []map[string]interface{}, idx []int) {
|
|
if isMulti(typeName) {
|
|
err := resolveManyEntities(ctx, typeName, reps, idx)
|
|
if err != nil {
|
|
ec.Error(ctx, err)
|
|
}
|
|
} else {
|
|
// if there are multiple entities to resolve, parallelize (similar to
|
|
// graphql.FieldSet.Dispatch)
|
|
var e sync.WaitGroup
|
|
e.Add(len(reps))
|
|
for i, rep := range reps {
|
|
i, rep := i, rep
|
|
go func(i int, rep map[string]interface{}) {
|
|
err := resolveEntity(ctx, typeName, rep, idx, i)
|
|
if err != nil {
|
|
ec.Error(ctx, err)
|
|
}
|
|
e.Done()
|
|
}(i, rep)
|
|
}
|
|
e.Wait()
|
|
}
|
|
}
|
|
buildRepresentationGroups(representations)
|
|
|
|
switch len(repsMap) {
|
|
case 0:
|
|
return list
|
|
case 1:
|
|
for typeName, reps := range repsMap {
|
|
resolveEntityGroup(typeName, reps.r, reps.i)
|
|
}
|
|
return list
|
|
default:
|
|
var g sync.WaitGroup
|
|
g.Add(len(repsMap))
|
|
for typeName, reps := range repsMap {
|
|
go func(typeName string, reps []map[string]interface{}, idx []int) {
|
|
resolveEntityGroup(typeName, reps, idx)
|
|
g.Done()
|
|
}(typeName, reps.r, reps.i)
|
|
}
|
|
g.Wait()
|
|
return list
|
|
}
|
|
}
|
|
|
|
{{- /* Make sure the required fields are in the given entity representation and return the name of the proper resolver. */ -}}
|
|
|
|
{{ range $_, $entity := .Entities }}
|
|
{{- if .Resolvers }}
|
|
|
|
func entityResolverNameFor{{$entity.Name}}(ctx context.Context, rep map[string]interface{}) (string, error) {
|
|
{{- range .Resolvers }}
|
|
for {
|
|
var (
|
|
m map[string]interface{}
|
|
val interface{}
|
|
ok bool
|
|
)
|
|
_ = val
|
|
{{- range $_, $keyField := .KeyFields }}
|
|
m = rep
|
|
{{- range $i, $field := .Field }}
|
|
if {{ if (ne $i $keyField.Field.LastIndex ) -}}val{{- else -}}_{{- end -}}, ok = m["{{.}}"]; !ok {
|
|
break
|
|
}
|
|
{{- if (ne $i $keyField.Field.LastIndex ) }}
|
|
if m, ok = val.(map[string]interface{}); !ok {
|
|
break
|
|
}
|
|
{{- end}}
|
|
{{- end}}
|
|
{{- end }}
|
|
return "{{.ResolverName}}", nil
|
|
}
|
|
{{- end }}
|
|
return "", fmt.Errorf("%w for {{$entity.Name}}", ErrTypeNotFound)
|
|
}
|
|
{{- end }}
|
|
{{- end }}
|
|
|
|
{{end}}
|