github.com/99designs/gqlgen@v0.17.45/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/99designs/gqlgen/plugin/federation/fedruntime" }}
     8  {{ $options := .PackageOptions }}
     9  {{ $usePointers := .UsePointers }}
    10  
    11  var (
    12  	ErrUnknownType = errors.New("unknown type")
    13  	ErrTypeNotFound = errors.New("type not found")
    14  )
    15  
    16  func (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}}
    36  func (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}}