github.com/shish-dev/gqlgen@v0.99.0/codegen/data.go (about) 1 package codegen 2 3 import ( 4 "fmt" 5 "sort" 6 7 "github.com/vektah/gqlparser/v2/ast" 8 9 "github.com/shish-dev/gqlgen/codegen/config" 10 ) 11 12 // Data is a unified model of the code to be generated. Plugins may modify this structure to do things like implement 13 // resolvers or directives automatically (eg grpc, validation) 14 type Data struct { 15 Config *config.Config 16 Schema *ast.Schema 17 // If a schema is broken up into multiple Data instance, each representing part of the schema, 18 // AllDirectives should contain the directives for the entire schema. Directives() can 19 // then be used to get the directives that were defined in this Data instance's sources. 20 // If a single Data instance is used for the entire schema, AllDirectives and Directives() 21 // will be identical. 22 // AllDirectives should rarely be used directly. 23 AllDirectives DirectiveList 24 Objects Objects 25 Inputs Objects 26 Interfaces map[string]*Interface 27 ReferencedTypes map[string]*config.TypeReference 28 ComplexityRoots map[string]*Object 29 30 QueryRoot *Object 31 MutationRoot *Object 32 SubscriptionRoot *Object 33 } 34 35 type builder struct { 36 Config *config.Config 37 Schema *ast.Schema 38 Binder *config.Binder 39 Directives map[string]*Directive 40 } 41 42 // Get only the directives which are defined in the config's sources. 43 func (d *Data) Directives() DirectiveList { 44 res := DirectiveList{} 45 for k, directive := range d.AllDirectives { 46 for _, s := range d.Config.Sources { 47 if directive.Position.Src.Name == s.Name { 48 res[k] = directive 49 break 50 } 51 } 52 } 53 return res 54 } 55 56 func BuildData(cfg *config.Config) (*Data, error) { 57 // We reload all packages to allow packages to be compared correctly. 58 cfg.ReloadAllPackages() 59 60 b := builder{ 61 Config: cfg, 62 Schema: cfg.Schema, 63 } 64 65 b.Binder = b.Config.NewBinder() 66 67 var err error 68 b.Directives, err = b.buildDirectives() 69 if err != nil { 70 return nil, err 71 } 72 73 dataDirectives := make(map[string]*Directive) 74 for name, d := range b.Directives { 75 if !d.Builtin { 76 dataDirectives[name] = d 77 } 78 } 79 80 s := Data{ 81 Config: cfg, 82 AllDirectives: dataDirectives, 83 Schema: b.Schema, 84 Interfaces: map[string]*Interface{}, 85 } 86 87 for _, schemaType := range b.Schema.Types { 88 switch schemaType.Kind { 89 case ast.Object: 90 obj, err := b.buildObject(schemaType) 91 if err != nil { 92 return nil, fmt.Errorf("unable to build object definition: %w", err) 93 } 94 95 s.Objects = append(s.Objects, obj) 96 case ast.InputObject: 97 input, err := b.buildObject(schemaType) 98 if err != nil { 99 return nil, fmt.Errorf("unable to build input definition: %w", err) 100 } 101 102 s.Inputs = append(s.Inputs, input) 103 104 case ast.Union, ast.Interface: 105 s.Interfaces[schemaType.Name], err = b.buildInterface(schemaType) 106 if err != nil { 107 return nil, fmt.Errorf("unable to bind to interface: %w", err) 108 } 109 } 110 } 111 112 if s.Schema.Query != nil { 113 s.QueryRoot = s.Objects.ByName(s.Schema.Query.Name) 114 } else { 115 return nil, fmt.Errorf("query entry point missing") 116 } 117 118 if s.Schema.Mutation != nil { 119 s.MutationRoot = s.Objects.ByName(s.Schema.Mutation.Name) 120 } 121 122 if s.Schema.Subscription != nil { 123 s.SubscriptionRoot = s.Objects.ByName(s.Schema.Subscription.Name) 124 } 125 126 if err := b.injectIntrospectionRoots(&s); err != nil { 127 return nil, err 128 } 129 130 s.ReferencedTypes = b.buildTypes() 131 132 sort.Slice(s.Objects, func(i, j int) bool { 133 return s.Objects[i].Definition.Name < s.Objects[j].Definition.Name 134 }) 135 136 sort.Slice(s.Inputs, func(i, j int) bool { 137 return s.Inputs[i].Definition.Name < s.Inputs[j].Definition.Name 138 }) 139 140 if b.Binder.SawInvalid { 141 // if we have a syntax error, show it 142 err := cfg.Packages.Errors() 143 if len(err) > 0 { 144 return nil, err 145 } 146 147 // otherwise show a generic error message 148 return nil, fmt.Errorf("invalid types were encountered while traversing the go source code, this probably means the invalid code generated isnt correct. add try adding -v to debug") 149 } 150 151 return &s, nil 152 } 153 154 func (b *builder) injectIntrospectionRoots(s *Data) error { 155 obj := s.Objects.ByName(b.Schema.Query.Name) 156 if obj == nil { 157 return fmt.Errorf("root query type must be defined") 158 } 159 160 __type, err := b.buildField(obj, &ast.FieldDefinition{ 161 Name: "__type", 162 Type: ast.NamedType("__Type", nil), 163 Arguments: []*ast.ArgumentDefinition{ 164 { 165 Name: "name", 166 Type: ast.NonNullNamedType("String", nil), 167 }, 168 }, 169 }) 170 if err != nil { 171 return err 172 } 173 174 __schema, err := b.buildField(obj, &ast.FieldDefinition{ 175 Name: "__schema", 176 Type: ast.NamedType("__Schema", nil), 177 }) 178 if err != nil { 179 return err 180 } 181 182 obj.Fields = append(obj.Fields, __type, __schema) 183 184 return nil 185 }