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 }