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