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