github.com/geneva/gqlgen@v0.17.7-0.20230801155730-7b9317164836/plugin/federation/entity.go (about)

     1  package federation
     2  
     3  import (
     4  	"go/types"
     5  
     6  	"github.com/geneva/gqlgen/codegen/config"
     7  	"github.com/geneva/gqlgen/codegen/templates"
     8  	"github.com/geneva/gqlgen/plugin/federation/fieldset"
     9  	"github.com/vektah/gqlparser/v2/ast"
    10  )
    11  
    12  // Entity represents a federated type
    13  // that was declared in the GQL schema.
    14  type Entity struct {
    15  	Name      string // The same name as the type declaration
    16  	Def       *ast.Definition
    17  	Resolvers []*EntityResolver
    18  	Requires  []*Requires
    19  	Multi     bool
    20  }
    21  
    22  type EntityResolver struct {
    23  	ResolverName  string      // The resolver name, such as FindUserByID
    24  	KeyFields     []*KeyField // The fields declared in @key.
    25  	InputType     types.Type  // The Go generated input type for multi entity resolvers
    26  	InputTypeName string
    27  }
    28  
    29  func (e *EntityResolver) LookupInputType() string {
    30  	return templates.CurrentImports.LookupType(e.InputType)
    31  }
    32  
    33  type KeyField struct {
    34  	Definition *ast.FieldDefinition
    35  	Field      fieldset.Field        // len > 1 for nested fields
    36  	Type       *config.TypeReference // The Go representation of that field type
    37  }
    38  
    39  // Requires represents an @requires clause
    40  type Requires struct {
    41  	Name  string                // the name of the field
    42  	Field fieldset.Field        // source Field, len > 1 for nested fields
    43  	Type  *config.TypeReference // The Go representation of that field type
    44  }
    45  
    46  func (e *Entity) allFieldsAreExternal(federationVersion int) bool {
    47  	for _, field := range e.Def.Fields {
    48  		if !e.isFieldImplicitlyExternal(field, federationVersion) && field.Directives.ForName("external") == nil {
    49  			return false
    50  		}
    51  	}
    52  	return true
    53  }
    54  
    55  // In federation v2, key fields are implicitly external.
    56  func (e *Entity) isFieldImplicitlyExternal(field *ast.FieldDefinition, federationVersion int) bool {
    57  	// Key fields are only implicitly external in Federation 2
    58  	if federationVersion != 2 {
    59  		return false
    60  	}
    61  	// TODO: From the spec, it seems like if an entity is not resolvable then it should not only not have a resolver, but should not appear in the _Entitiy union.
    62  	// The current implementation is a less drastic departure from the previous behavior, but should probably be reviewed.
    63  	// See https://www.apollographql.com/docs/federation/subgraph-spec/
    64  	if e.isResolvable() {
    65  		return false
    66  	}
    67  	// If the field is a key field, it is implicitly external
    68  	if e.isKeyField(field) {
    69  		return true
    70  	}
    71  
    72  	return false
    73  }
    74  
    75  // Determine if the entity is resolvable.
    76  func (e *Entity) isResolvable() bool {
    77  	key := e.Def.Directives.ForName("key")
    78  	if key == nil {
    79  		// If there is no key directive, the entity is resolvable.
    80  		return true
    81  	}
    82  	resolvable := key.Arguments.ForName("resolvable")
    83  	if resolvable == nil {
    84  		// If there is no resolvable argument, the entity is resolvable.
    85  		return true
    86  	}
    87  	// only if resolvable: false has been set on the @key directive do we consider the entity non-resolvable.
    88  	return resolvable.Value.Raw != "false"
    89  }
    90  
    91  // Determine if a field is part of the entities key.
    92  func (e *Entity) isKeyField(field *ast.FieldDefinition) bool {
    93  	for _, keyField := range e.keyFields() {
    94  		if keyField == field.Name {
    95  			return true
    96  		}
    97  	}
    98  	return false
    99  }
   100  
   101  // Get the key fields for this entity.
   102  func (e *Entity) keyFields() []string {
   103  	key := e.Def.Directives.ForName("key")
   104  	if key == nil {
   105  		return []string{}
   106  	}
   107  	fields := key.Arguments.ForName("fields")
   108  	if fields == nil {
   109  		return []string{}
   110  	}
   111  	fieldSet := fieldset.New(fields.Value.Raw, nil)
   112  	keyFields := make([]string, len(fieldSet))
   113  	for i, field := range fieldSet {
   114  		keyFields[i] = field[0]
   115  	}
   116  	return keyFields
   117  }