1{{ reserveImport "context" }}
2{{ reserveImport "errors" }}
3{{ reserveImport "fmt" }}
4{{ reserveImport "strings" }}
5{{ reserveImport "sync" }}
6
7{{ reserveImport "github.com/99designs/gqlgen/plugin/federation/fedruntime" }}
8{{ $options := .PackageOptions }}
9{{ $usePointers := .UsePointers }}
10
11var (
12 ErrUnknownType = errors.New("unknown type")
13 ErrTypeNotFound = errors.New("type not found")
14)
15
16func (ec *executionContext) __resolve__service(ctx context.Context) (fedruntime.Service, error) {
17 if ec.DisableIntrospection {
18 return fedruntime.Service{}, errors.New("federated introspection disabled")
19 }
20
21 var sdl []string
22
23 for _, src := range sources {
24 if src.BuiltIn {
25 continue
26 }
27 sdl = append(sdl, src.Input)
28 }
29
30 return fedruntime.Service{
31 SDL: strings.Join(sdl, "\n"),
32 }, nil
33}
34
35{{if .Entities}}
36func (ec *executionContext) __resolve_entities(ctx context.Context, representations []map[string]interface{}) []fedruntime.Entity {
37 list := make([]fedruntime.Entity, len(representations))
38
39 repsMap := map[string]struct {
40 i []int
41 r []map[string]interface{}
42 }{}
43
44 // We group entities by typename so that we can parallelize their resolution.
45 // This is particularly helpful when there are entity groups in multi mode.
46 buildRepresentationGroups := func(reps []map[string]interface{}) {
47 for i, rep := range reps {
48 typeName, ok := rep["__typename"].(string)
49 if !ok {
50 // If there is no __typename, we just skip the representation;
51 // we just won't be resolving these unknown types.
52 ec.Error(ctx, errors.New("__typename must be an existing string"))
53 continue
54 }
55
56 _r := repsMap[typeName]
57 _r.i = append(_r.i, i)
58 _r.r = append(_r.r, rep)
59 repsMap[typeName] = _r
60 }
61 }
62
63 isMulti := func(typeName string) bool {
64 switch typeName {
65 {{- range .Entities -}}
66 {{- if .Resolvers -}}
67 {{- if .Multi -}}
68 case "{{.Def.Name}}":
69 return true
70 {{ end }}
71 {{- end -}}
72 {{- end -}}
73 default:
74 return false
75 }
76 }
77
78 resolveEntity := func(ctx context.Context, typeName string, rep map[string]interface{}, idx []int, i int) (err error) {
79 // we need to do our own panic handling, because we may be called in a
80 // goroutine, where the usual panic handling can't catch us
81 defer func () {
82 if r := recover(); r != nil {
83 err = ec.Recover(ctx, r)
84 }
85 }()
86
87 switch typeName {
88 {{ range $_, $entity := .Entities }}
89 {{- if and .Resolvers (not .Multi) -}}
90 case "{{.Def.Name}}":
91 resolverName, err := entityResolverNameFor{{.Def.Name}}(ctx, rep)
92 if err != nil {
93 return fmt.Errorf(`finding resolver for Entity "{{.Def.Name}}": %w`, err)
94 }
95 switch resolverName {
96 {{ range $i, $resolver := .Resolvers }}
97 case "{{.ResolverName}}":
98 {{- range $j, $keyField := .KeyFields }}
99 id{{$j}}, err := ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
100 if err != nil {
101 return fmt.Errorf(`unmarshalling param {{$j}} for {{$resolver.ResolverName}}(): %w`, err)
102 }
103 {{- end}}
104 entity, err := ec.resolvers.Entity().{{.ResolverName | go}}(ctx, {{- range $j, $_ := .KeyFields -}} id{{$j}}, {{end}})
105 if err != nil {
106 return fmt.Errorf(`resolving Entity "{{$entity.Def.Name}}": %w`, err)
107 }
108 {{ if and (index $options "explicit_requires") $entity.Requires }}
109 err = ec.Populate{{$entity.Def.Name}}Requires(ctx, {{- if (not $usePointers) -}}&{{- end -}}entity, rep)
110 if err != nil {
111 return fmt.Errorf(`populating requires for Entity "{{$entity.Def.Name}}": %w`, err)
112 }
113 {{- else }}
114 {{ range $entity.Requires }}
115 entity.{{.Field.JoinGo `.`}}, err = ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
116 if err != nil {
117 return err
118 }
119 {{- end }}
120 {{- end }}
121 list[idx[i]] = entity
122 return nil
123 {{- end }}
124 }
125 {{ end }}
126 {{- end }}
127 }
128 return fmt.Errorf("%w: %s", ErrUnknownType, typeName)
129 }
130
131 resolveManyEntities := func(ctx context.Context, typeName string, reps []map[string]interface{}, idx []int) (err error) {
132 // we need to do our own panic handling, because we may be called in a
133 // goroutine, where the usual panic handling can't catch us
134 defer func () {
135 if r := recover(); r != nil {
136 err = ec.Recover(ctx, r)
137 }
138 }()
139
140 switch typeName {
141 {{ range $_, $entity := .Entities }}
142 {{ if and .Resolvers .Multi -}}
143 case "{{.Def.Name}}":
144 resolverName, err := entityResolverNameFor{{.Def.Name}}(ctx, reps[0])
145 if err != nil {
146 return fmt.Errorf(`finding resolver for Entity "{{.Def.Name}}": %w`, err)
147 }
148 switch resolverName {
149 {{ range $i, $resolver := .Resolvers }}
150 case "{{.ResolverName}}":
151 _reps := make([]*{{.LookupInputType}}, len(reps))
152
153 for i, rep := range reps {
154 {{ range $i, $keyField := .KeyFields -}}
155 id{{$i}}, err := ec.{{.Type.UnmarshalFunc}}(ctx, rep["{{.Field.Join `"].(map[string]interface{})["`}}"])
156 if err != nil {
157 return errors.New(fmt.Sprintf("Field %s undefined in schema.", "{{.Definition.Name}}"))
158 }
159 {{end}}
160
161 _reps[i] = &{{.LookupInputType}} {
162 {{ range $i, $keyField := .KeyFields -}}
163 {{$keyField.Field.ToGo}}: id{{$i}},
164 {{end}}
165 }
166 }
167
168 entities, err := ec.resolvers.Entity().{{.ResolverName | go}}(ctx, _reps)
169 if err != nil {
170 return err
171 }
172
173 for i, entity := range entities {
174 {{- range $entity.Requires }}
175 entity.{{.Field.JoinGo `.`}}, err = ec.{{.Type.UnmarshalFunc}}(ctx, reps[i]["{{.Field.Join `"].(map[string]interface{})["`}}"])
176 if err != nil {
177 return err
178 }
179 {{- end}}
180 list[idx[i]] = entity
181 }
182 return nil
183 {{ end }}
184 default:
185 return fmt.Errorf("unknown resolver: %s", resolverName)
186 }
187 {{ end }}
188 {{- end }}
189 default:
190 return errors.New("unknown type: "+typeName)
191 }
192 }
193
194 resolveEntityGroup := func(typeName string, reps []map[string]interface{}, idx []int) {
195 if isMulti(typeName) {
196 err := resolveManyEntities(ctx, typeName, reps, idx)
197 if err != nil {
198 ec.Error(ctx, err)
199 }
200 } else {
201 // if there are multiple entities to resolve, parallelize (similar to
202 // graphql.FieldSet.Dispatch)
203 var e sync.WaitGroup
204 e.Add(len(reps))
205 for i, rep := range reps {
206 i, rep := i, rep
207 go func(i int, rep map[string]interface{}) {
208 err := resolveEntity(ctx, typeName, rep, idx, i)
209 if err != nil {
210 ec.Error(ctx, err)
211 }
212 e.Done()
213 }(i, rep)
214 }
215 e.Wait()
216 }
217 }
218 buildRepresentationGroups(representations)
219
220 switch len(repsMap) {
221 case 0:
222 return list
223 case 1:
224 for typeName, reps := range repsMap {
225 resolveEntityGroup(typeName, reps.r, reps.i)
226 }
227 return list
228 default:
229 var g sync.WaitGroup
230 g.Add(len(repsMap))
231 for typeName, reps := range repsMap {
232 go func(typeName string, reps []map[string]interface{}, idx []int) {
233 resolveEntityGroup(typeName, reps, idx)
234 g.Done()
235 }(typeName, reps.r, reps.i)
236 }
237 g.Wait()
238 return list
239 }
240}
241
242{{- /* Make sure the required fields are in the given entity representation and return the name of the proper resolver. */ -}}
243
244{{ range $_, $entity := .Entities }}
245 {{- if .Resolvers }}
246
247 func entityResolverNameFor{{$entity.Name}}(ctx context.Context, rep map[string]interface{}) (string, error) {
248 {{- range .Resolvers }}
249 for {
250 var (
251 m map[string]interface{}
252 val interface{}
253 ok bool
254 )
255 _ = val
256 {{- range $_, $keyField := .KeyFields }}
257 m = rep
258 {{- range $i, $field := .Field }}
259 if {{ if (ne $i $keyField.Field.LastIndex ) -}}val{{- else -}}_{{- end -}}, ok = m["{{.}}"]; !ok {
260 break
261 }
262 {{- if (ne $i $keyField.Field.LastIndex ) }}
263 if m, ok = val.(map[string]interface{}); !ok {
264 break
265 }
266 {{- end}}
267 {{- end}}
268 {{- end }}
269 return "{{.ResolverName}}", nil
270 }
271 {{- end }}
272 return "", fmt.Errorf("%w for {{$entity.Name}}", ErrTypeNotFound)
273 }
274 {{- end }}
275{{- end }}
276
277{{end}}
View as plain text