stash/vendor/github.com/99designs/gqlgen/plugin/federation/federation.gotpl
SmallCoccinelle 45f700d6ea
Support Go 1.18: Upgrade gqlgen to v0.17.2 (#2443)
* 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>
2022-04-02 18:08:14 +11:00

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}}