github.com/mstephano/gqlgen-schemagen@v0.0.0-20230113041936-dd2cd4ea46aa/plugin/federation/entity.go (about)

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