├── .gitignore ├── LICENSE ├── README.md └── generator ├── api_test.go ├── generator.go ├── generator_test.go ├── schema.go ├── schema_test.go └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # graphql-go-gen 2 | 3 | This project is discontinued but you are welcome to fork it. 4 | -------------------------------------------------------------------------------- /generator/api_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "github.com/graphql-go/graphql" 6 | "testing" 7 | "encoding/json" 8 | ) 9 | 10 | func TestBasicServer(t *testing.T) { 11 | schema_string := ` 12 | type Query { 13 | hello: String 14 | } 15 | ` 16 | 17 | ctx, err := Generate(schema_string) 18 | if err != nil { 19 | fmt.Print(err) 20 | t.FailNow() 21 | } 22 | 23 | ctx.Extend("Query", UpdateObjectFn(func(config graphql.ObjectConfig) graphql.ObjectConfig { 24 | fields := config.Fields.(graphql.Fields) 25 | fields["hello"].Resolve = func(p graphql.ResolveParams) (interface{}, error) { 26 | return "world", nil 27 | } 28 | return config 29 | })) 30 | 31 | schema, serr := CreateSchemaFromContext(ctx) 32 | if serr != nil { 33 | fmt.Print(serr) 34 | t.FailNow() 35 | } 36 | 37 | 38 | // Setup Query 39 | query := ` 40 | { 41 | hello 42 | } 43 | ` 44 | 45 | params := graphql.Params { Schema: schema, RequestString: query } 46 | r := graphql.Do(params) 47 | if len(r.Errors) > 0 { 48 | fmt.Print("failed to execute graphql operation, errors: %+v", r.Errors) 49 | t.FailNow() 50 | } 51 | rJSON, _ := json.Marshal(r) 52 | fmt.Printf("%s \n", rJSON) 53 | } 54 | -------------------------------------------------------------------------------- /generator/generator.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "github.com/graphql-go/graphql" 6 | "github.com/graphql-go/graphql/language/ast" 7 | "github.com/graphql-go/graphql/language/parser" 8 | "strconv" 9 | ) 10 | 11 | type UpdateObjectFn func(graphql.ObjectConfig) graphql.ObjectConfig 12 | type UpdateInterfaceFn func(graphql.InterfaceConfig) graphql.InterfaceConfig 13 | type UpdateEnumFn func(graphql.EnumConfig) graphql.EnumConfig 14 | type UpdateUnionFn func(graphql.UnionConfig) graphql.UnionConfig 15 | type UpdateScalarFn func(graphql.ScalarConfig) graphql.ScalarConfig 16 | type UpdateInputObjectFn func(graphql.InputObjectConfig) graphql.InputObjectConfig 17 | 18 | type Context struct { 19 | objects map[string]*graphql.Object 20 | interfaces map[string]*graphql.Interface 21 | enums map[string]*graphql.Enum 22 | unions map[string]*graphql.Union 23 | scalars map[string]*graphql.Scalar 24 | inputs map[string]*graphql.InputObject 25 | 26 | objectConfigs map[string]graphql.ObjectConfig 27 | interfaceConfigs map[string]graphql.InterfaceConfig 28 | enumConfigs map[string]graphql.EnumConfig 29 | unionConfigs map[string]graphql.UnionConfig 30 | scalarConfigs map[string]graphql.ScalarConfig 31 | inputConfigs map[string]graphql.InputObjectConfig 32 | 33 | processed []int 34 | } 35 | 36 | func (g *Context) Object(which string) *graphql.Object { 37 | return g.objects[which] 38 | } 39 | 40 | func (g *Context) Interface(which string) *graphql.Interface { 41 | return g.interfaces[which] 42 | } 43 | 44 | func (g *Context) Enums(which string) *graphql.Enum { 45 | return g.enums[which] 46 | } 47 | 48 | func (g *Context) Union(which string) *graphql.Union { 49 | return g.unions[which] 50 | } 51 | 52 | func (g *Context) Scalar(which string) *graphql.Scalar { 53 | return g.scalars[which] 54 | } 55 | 56 | func (g *Context) InputObject(which string) *graphql.InputObject { 57 | return g.inputs[which] 58 | } 59 | 60 | func (g *Context) GetObject(which string) (graphql.Output, bool) { 61 | if i, ok := g.scalars[which]; ok { 62 | return i, true 63 | } 64 | if i, ok := g.objects[which]; ok { 65 | return i, true 66 | } 67 | if i, ok := g.interfaces[which]; ok { 68 | return i, true 69 | } 70 | if i, ok := g.unions[which]; ok { 71 | return i, true 72 | } 73 | if i, ok := g.inputs[which]; ok { 74 | return i, true 75 | } 76 | if i, ok := g.enums[which]; ok { 77 | return i, true 78 | } 79 | return nil, false 80 | } 81 | 82 | func (g *Context) GetObjectConfig(which string) (interface{}, bool) { 83 | if i, ok := g.scalarConfigs[which]; ok { 84 | return i, true 85 | } 86 | if i, ok := g.objectConfigs[which]; ok { 87 | return i, true 88 | } 89 | if i, ok := g.interfaceConfigs[which]; ok { 90 | return i, true 91 | } 92 | if i, ok := g.unionConfigs[which]; ok { 93 | return i, true 94 | } 95 | if i, ok := g.inputConfigs[which]; ok { 96 | return i, true 97 | } 98 | if i, ok := g.enumConfigs[which]; ok { 99 | return i, true 100 | } 101 | return nil, false 102 | } 103 | 104 | func (g *Context) UpdateObject(which string, config interface{}) error { 105 | config, ok := g.GetObjectConfig(which) 106 | if !ok { 107 | return fmt.Errorf("Could not find Object with name %s.\n", which) 108 | } 109 | if _, ok := g.scalars[which]; ok { 110 | g.scalars[which] = graphql.NewScalar(config.(graphql.ScalarConfig)) 111 | } 112 | if _, ok := g.objects[which]; ok { 113 | g.objects[which] = graphql.NewObject(config.(graphql.ObjectConfig)) 114 | } 115 | if _, ok := g.interfaces[which]; ok { 116 | g.interfaces[which] = graphql.NewInterface(config.(graphql.InterfaceConfig)) 117 | } 118 | if _, ok := g.unions[which]; ok { 119 | g.unions[which] = graphql.NewUnion(config.(graphql.UnionConfig)) 120 | } 121 | if _, ok := g.inputs[which]; ok { 122 | g.inputs[which] = graphql.NewInputObject(config.(graphql.InputObjectConfig)) 123 | } 124 | if _, ok := g.enums[which]; ok { 125 | g.enums[which] = graphql.NewEnum(config.(graphql.EnumConfig)) 126 | } 127 | return nil 128 | } 129 | 130 | func (g *Context) Extend(which string, updateFn interface{}) *Context { 131 | objectConfig, ok := g.GetObjectConfig(which) 132 | if !ok { 133 | panic("No object with name " + which + " found.\n") 134 | } 135 | 136 | switch objectConfig.(type) { 137 | case graphql.ObjectConfig: 138 | fn, ok := updateFn.(UpdateObjectFn) 139 | if !ok { 140 | panic("Given updatefunction (updateFn) has to be of type UpdateObjectFn!") 141 | } 142 | 143 | obConfig, ok := objectConfig.(graphql.ObjectConfig) 144 | if !ok { 145 | panic("Object found is not of type graphql.Object!\n") 146 | } 147 | 148 | fn(obConfig) 149 | break 150 | case graphql.InterfaceConfig: 151 | fn, ok := updateFn.(UpdateInterfaceFn) 152 | if !ok { 153 | panic("Given updatefunction (updateFn) has to be of type UpdateObjectFn!") 154 | } 155 | 156 | obConfig, ok := objectConfig.(graphql.InterfaceConfig) 157 | if !ok { 158 | panic("Object found is not of type graphql.Object!\n") 159 | } 160 | 161 | fn(obConfig) 162 | break 163 | case graphql.UnionConfig: 164 | fn, ok := updateFn.(UpdateUnionFn) 165 | if !ok { 166 | panic("Given updatefunction (updateFn) has to be of type UpdateUnionFn!") 167 | } 168 | 169 | obConfig, ok := objectConfig.(graphql.UnionConfig) 170 | if !ok { 171 | panic("Object found is not of type graphql.Union!") 172 | } 173 | 174 | fn(obConfig) 175 | break 176 | case graphql.ScalarConfig: 177 | fn, ok := updateFn.(UpdateScalarFn) 178 | if !ok { 179 | panic("Given updatefunction (updateFn) has to be of type UpdateScalarFn!") 180 | } 181 | 182 | obConfig, ok := objectConfig.(graphql.ScalarConfig) 183 | if !ok { 184 | panic("Object found is not of type graphql.Scalar!") 185 | } 186 | 187 | fn(obConfig) 188 | break 189 | case graphql.EnumConfig: 190 | fn, ok := updateFn.(UpdateEnumFn) 191 | if !ok { 192 | panic("Given updatefunction (updateFn) has to be of type UpdateEnumFn!") 193 | } 194 | 195 | obConfig, ok := objectConfig.(graphql.EnumConfig) 196 | if !ok { 197 | panic("Object found is not of type graphql.Enum!") 198 | } 199 | 200 | fn(obConfig) 201 | break 202 | case graphql.InputObjectConfig: 203 | fn, ok := updateFn.(UpdateInputObjectFn) 204 | if !ok { 205 | panic("Given updatefunction (updateFn) has to be of type UpdateInputObjectFn!") 206 | } 207 | 208 | obConfig, ok := objectConfig.(graphql.InputObjectConfig) 209 | if !ok { 210 | panic("Object found is not of type graphql.InputObject!") 211 | } 212 | 213 | fn(obConfig) 214 | } 215 | return g 216 | } 217 | 218 | func mapType(ctx *Context, typ ast.Type) (graphql.Output, error) { 219 | switch typ.(type) { 220 | case *ast.NonNull: 221 | nonullType, err := mapType(ctx, typ.(*ast.NonNull).Type) 222 | if err != nil { 223 | return nil, err 224 | } 225 | return graphql.NewNonNull(nonullType.(graphql.Type)), nil 226 | case *ast.List: 227 | listType, err := mapType(ctx, typ.(*ast.List).Type) 228 | if err != nil { 229 | return nil, err 230 | } 231 | return graphql.NewList(listType.(graphql.Type)), nil 232 | case *ast.Named: 233 | switch typ.(*ast.Named).Name.Value { 234 | case "String": 235 | return graphql.String, nil 236 | case "Boolean": 237 | return graphql.Boolean, nil 238 | case "Int": 239 | return graphql.Int, nil 240 | case "Float": 241 | return graphql.Float, nil 242 | case "ID": 243 | return graphql.ID, nil 244 | default: 245 | if ob, ok := ctx.GetObject(typ.(*ast.Named).Name.Value); ok { 246 | return ob, nil 247 | } 248 | } 249 | } 250 | return nil, fmt.Errorf("Could not map type %s: Type not found!", typ) 251 | } 252 | 253 | func createValues(valType interface{}) interface{} { 254 | value := valType.(ast.Value) 255 | return value.GetValue() 256 | } 257 | 258 | func mapValue(ctx *Context, value interface{}) (interface{}, error) { 259 | switch value.(type) { 260 | case *ast.ListValue: 261 | listValue := value.(*ast.ListValue) 262 | return mapSlice(listValue.Values, createValues), nil 263 | case *ast.FloatValue: 264 | val := value.(*ast.FloatValue) 265 | return strconv.ParseFloat(val.Value, 64) 266 | case *ast.IntValue: 267 | val := value.(*ast.IntValue) 268 | return strconv.Atoi(val.Value) 269 | case *ast.StringValue: 270 | val := value.(*ast.StringValue) 271 | return val.Value, nil 272 | case *ast.BooleanValue: 273 | val := value.(*ast.BooleanValue) 274 | return val.Value, nil 275 | } 276 | return nil, nil 277 | } 278 | 279 | func generateFieldArguments(ctx *Context, def *ast.FieldDefinition) (graphql.FieldConfigArgument, error) { 280 | args := make(graphql.FieldConfigArgument, len(def.Arguments)) 281 | 282 | for _, arg := range def.Arguments { 283 | typ, err := mapType(ctx, arg.Type) 284 | if err != nil { 285 | return nil, err 286 | } 287 | 288 | argConfig := &graphql.ArgumentConfig{ 289 | Type: typ, 290 | } 291 | 292 | if arg.DefaultValue != nil { 293 | defaultValue, mapErr := mapValue(ctx, arg.DefaultValue) 294 | if err != nil { 295 | return nil, mapErr 296 | } 297 | argConfig.DefaultValue = defaultValue 298 | } 299 | 300 | args[arg.Name.Value] = argConfig 301 | } 302 | 303 | if len(args) > 0 { 304 | return args, nil 305 | } 306 | return nil, nil 307 | } 308 | 309 | func generateInputFields(ctx *Context, def *ast.InputObjectDefinition) (graphql.InputObjectConfigFieldMap, error) { 310 | fields := make(graphql.InputObjectConfigFieldMap, len(def.Fields)) 311 | for _, fieldDef := range def.Fields { 312 | typ, err := mapType(ctx, fieldDef.Type) 313 | if err != nil { 314 | return nil, err 315 | } 316 | 317 | field := &graphql.InputObjectFieldConfig{ 318 | Type: typ, 319 | } 320 | 321 | if fieldDef.DefaultValue != nil { 322 | field.DefaultValue = fieldDef.DefaultValue.GetValue() 323 | } 324 | 325 | fields[fieldDef.Name.Value] = field 326 | } 327 | 328 | if len(fields) > 0 { 329 | return fields, nil 330 | } 331 | return nil, nil 332 | } 333 | 334 | func generateFields(ctx *Context, def interface{}) (graphql.Fields, error) { 335 | var fieldDefs []*ast.FieldDefinition 336 | 337 | switch def.(type) { 338 | case *ast.ObjectDefinition: 339 | fieldDefs = def.(*ast.ObjectDefinition).Fields 340 | case *ast.InterfaceDefinition: 341 | fieldDefs = def.(*ast.InterfaceDefinition).Fields 342 | default: 343 | return nil, fmt.Errorf("GenerateFields: Given definition was no Object or Interface definition.") 344 | } 345 | 346 | fields := make(map[string]*graphql.Field, len(fieldDefs)) 347 | for _, fieldDef := range fieldDefs { 348 | typ, err := mapType(ctx, fieldDef.Type) 349 | if err != nil { 350 | return nil, err 351 | } 352 | 353 | field := &graphql.Field{ 354 | Type: typ, 355 | } 356 | 357 | args, err := generateFieldArguments(ctx, fieldDef) 358 | if err != nil { 359 | return nil, err 360 | } 361 | if args != nil { 362 | field.Args = args 363 | } 364 | 365 | fields[fieldDef.Name.Value] = field 366 | } 367 | 368 | if len(fields) > 0 { 369 | return graphql.Fields(fields), nil 370 | } 371 | return nil, nil 372 | } 373 | 374 | func generateEnumValues(def *ast.EnumDefinition) graphql.EnumValueConfigMap { 375 | enumMap := make(graphql.EnumValueConfigMap, len(def.Values)) 376 | 377 | for i, valueConfig := range def.Values { 378 | enumMap[valueConfig.Name.Value] = &graphql.EnumValueConfig{ 379 | Value: i, 380 | } 381 | } 382 | if len(enumMap) > 0 { 383 | return enumMap 384 | } 385 | return nil 386 | } 387 | 388 | func generateInterfaces(ctx *Context, obdef *ast.ObjectDefinition) ([]*graphql.Interface, error) { 389 | ifaces := make([]*graphql.Interface, len(obdef.Interfaces)) 390 | for i, iface := range obdef.Interfaces { 391 | if lookupIface, ok := ctx.interfaces[iface.Name.Value]; ok { 392 | ifaces[i] = lookupIface 393 | } else { 394 | return nil, fmt.Errorf("An interface with name %s was not declared and can therefore not be "+ 395 | "implemented to object %s\n", iface.Name.Value, obdef.Name.Value) 396 | } 397 | } 398 | if len(ifaces) > 0 { 399 | return ifaces, nil 400 | } 401 | return nil, nil 402 | } 403 | 404 | func generateUnionTypes(ctx *Context, def *ast.UnionDefinition) ([]*graphql.Object, error) { 405 | uTypes := make([]*graphql.Object, len(def.Types)) 406 | for i, utyp := range def.Types { 407 | if ob, ok := ctx.objects[utyp.Name.Value]; ok { 408 | uTypes[i] = ob 409 | } else { 410 | return nil, fmt.Errorf("An object with name %s was not declared and can therefore not be "+ 411 | "implemented in union %s\n", utyp.Name.Value, def.Name.Value) 412 | } 413 | } 414 | if len(uTypes) > 0 { 415 | return uTypes, nil 416 | } 417 | return nil, nil 418 | } 419 | 420 | // Collaborate interfaces on first pass 421 | func walk(context *Context, astDoc *ast.Document) bool { 422 | var found bool 423 | for astIndex, def := range astDoc.Definitions { 424 | var foundInCycle bool 425 | for _, storedAstIndex := range context.processed { 426 | if storedAstIndex == astIndex { 427 | goto scanNext 428 | } 429 | } 430 | 431 | switch def.(type) { 432 | case *ast.InterfaceDefinition: 433 | idef := def.(*ast.InterfaceDefinition) 434 | 435 | iConfig := graphql.InterfaceConfig{ 436 | Name: idef.Name.Value, 437 | } 438 | fields, err := generateFields(context, idef) 439 | if err != nil { 440 | continue // Get in next cycle 441 | } else if fields != nil { 442 | iConfig.Fields = fields 443 | } 444 | 445 | correspondingInterface := graphql.NewInterface(iConfig) 446 | context.interfaces[idef.Name.Value] = correspondingInterface 447 | context.interfaceConfigs[idef.Name.Value] = iConfig 448 | foundInCycle = true 449 | case *ast.EnumDefinition: 450 | edef := def.(*ast.EnumDefinition) 451 | eConfig := graphql.EnumConfig{ 452 | Name: edef.Name.Value, 453 | } 454 | 455 | values := generateEnumValues(edef) 456 | if values != nil { 457 | eConfig.Values = values 458 | } 459 | 460 | correspondingEnum := graphql.NewEnum(eConfig) 461 | context.enums[edef.Name.Value] = correspondingEnum 462 | context.enumConfigs[edef.Name.Value] = eConfig 463 | foundInCycle = true 464 | case *ast.ScalarDefinition: 465 | sdef := def.(*ast.ScalarDefinition) 466 | sConfig := graphql.ScalarConfig{ 467 | Name: sdef.Name.Value, 468 | } 469 | correspondingScalar := graphql.NewScalar(sConfig) 470 | context.scalars[sdef.Name.Value] = correspondingScalar 471 | context.scalarConfigs[sdef.Name.Value] = sConfig 472 | foundInCycle = true 473 | case *ast.UnionDefinition: 474 | udef := def.(*ast.UnionDefinition) 475 | uConfig := graphql.UnionConfig{ 476 | Name: udef.Name.Value, 477 | } 478 | 479 | uTypes, err := generateUnionTypes(context, udef) 480 | if err != nil { 481 | continue // Get in next cycle 482 | } 483 | if uTypes != nil { 484 | uConfig.Types = uTypes 485 | } 486 | 487 | correspondingUnion := graphql.NewUnion(uConfig) 488 | context.unions[udef.Name.Value] = correspondingUnion 489 | context.unionConfigs[udef.Name.Value] = uConfig 490 | foundInCycle = true 491 | case *ast.TypeExtensionDefinition: 492 | obdef := def.(*ast.TypeExtensionDefinition).Definition 493 | ob := context.Object(obdef.Name.Value) 494 | if ob == nil { 495 | continue // No object with this type. Get in next cycle. 496 | } 497 | fields, err := generateFields(context, obdef) 498 | if err != nil { 499 | continue 500 | } else if fields != nil { 501 | for fieldName, field := range fields { 502 | // _, ok := ob.Fields()[fieldName] 503 | // if ok { 504 | // continue // Ignore field since its already implemented 505 | // } 506 | 507 | // ** OVERRIDE ** --> Maybe change that behaviour later 508 | ob.AddFieldConfig(fieldName, field) 509 | } 510 | } 511 | foundInCycle = true 512 | case *ast.ObjectDefinition: 513 | obdef := def.(*ast.ObjectDefinition) 514 | obConfig := graphql.ObjectConfig{ 515 | Name: obdef.Name.Value, 516 | } 517 | 518 | // Include interfaces 519 | ifaces, err := generateInterfaces(context, obdef) 520 | if err != nil { 521 | continue // Get i next cycle 522 | } 523 | if ifaces != nil { 524 | obConfig.Interfaces = ifaces 525 | } 526 | 527 | // Include Fields 528 | fields, err := generateFields(context, obdef) 529 | if err != nil { 530 | continue // Get in next cycle 531 | } else if fields != nil { 532 | obConfig.Fields = fields 533 | } 534 | 535 | correspondingObject := graphql.NewObject(obConfig) 536 | context.objects[obdef.Name.Value] = correspondingObject 537 | context.objectConfigs[obdef.Name.Value] = obConfig 538 | foundInCycle = true 539 | case *ast.InputObjectDefinition: 540 | idef := def.(*ast.InputObjectDefinition) 541 | iConfig := graphql.InputObjectConfig{ 542 | Name: idef.Name.Value, 543 | } 544 | 545 | inputFields, err := generateInputFields(context, idef) 546 | if err != nil { 547 | continue // Get in next cycle 548 | } else if inputFields != nil { 549 | iConfig.Fields = inputFields 550 | } 551 | 552 | correspondingInput := graphql.NewInputObject(iConfig) 553 | context.inputs[idef.Name.Value] = correspondingInput 554 | context.inputConfigs[idef.Name.Value] = iConfig 555 | foundInCycle = true 556 | } 557 | 558 | if foundInCycle { 559 | context.processed = append(context.processed, astIndex) 560 | found = true 561 | } 562 | scanNext: 563 | } 564 | return found 565 | } 566 | 567 | func Generate(source string) (*Context, error) { 568 | astDoc, err := parser.Parse(parser.ParseParams{ 569 | Source: source, 570 | Options: parser.ParseOptions{ 571 | NoLocation: true, 572 | NoSource: false, 573 | }, 574 | }) 575 | 576 | if err != nil { 577 | return nil, err 578 | } 579 | 580 | context := &Context{} 581 | context.interfaces = make(map[string]*graphql.Interface) 582 | context.enums = make(map[string]*graphql.Enum) 583 | context.scalars = make(map[string]*graphql.Scalar) 584 | context.inputs = make(map[string]*graphql.InputObject) 585 | context.unions = make(map[string]*graphql.Union) 586 | context.objects = make(map[string]*graphql.Object) 587 | 588 | context.interfaceConfigs = make(map[string]graphql.InterfaceConfig) 589 | context.enumConfigs = make(map[string]graphql.EnumConfig) 590 | context.scalarConfigs = make(map[string]graphql.ScalarConfig) 591 | context.inputConfigs = make(map[string]graphql.InputObjectConfig) 592 | context.unionConfigs = make(map[string]graphql.UnionConfig) 593 | context.objectConfigs = make(map[string]graphql.ObjectConfig) 594 | 595 | for walk(context, astDoc) { 596 | } 597 | 598 | return context, nil 599 | } 600 | -------------------------------------------------------------------------------- /generator/generator_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davecgh/go-spew/spew" 6 | "github.com/graphql-go/graphql" 7 | "reflect" 8 | "regexp" 9 | "strings" 10 | "testing" 11 | ) 12 | 13 | func matchStrings(exp, got string) bool { 14 | trexp := strings.TrimSpace(exp) 15 | trgot := strings.TrimSpace(got) 16 | reg := regexp.MustCompile(`0x[0-9a-z]+`) 17 | trexp = reg.ReplaceAllString(trexp, "") 18 | trgot = reg.ReplaceAllString(trgot, "") 19 | return trexp == trgot 20 | } 21 | 22 | func nextLineMatch(exp, got []string, index1, index2 *int) bool { 23 | for i := *index1 + 1; i < len(exp); i++ { 24 | for p := *index2 + 1; p < len(got); p++ { 25 | if matchStrings(exp[i], got[p]) { 26 | *index1 = i 27 | *index2 = p 28 | return true 29 | } 30 | } 31 | } 32 | *index1, *index2 = -1, -1 33 | return false 34 | } 35 | 36 | func compareLines(str1, str2 string) { 37 | l1 := strings.Split(str1, "\n") 38 | l2 := strings.Split(str2, "\n") 39 | 40 | index1 := -1 41 | index2 := -1 42 | lastExp := -1 43 | lastGot := -1 44 | for nextLineMatch(l1, l2, &index1, &index2) { 45 | if index1-lastExp > 1 || index2-lastGot > 1 { 46 | // Unmatching lines in between! 47 | unmatched := l1[lastExp+1 : index1] 48 | unmatchedGot := l2[lastGot+1 : index2] 49 | fmt.Println("-----------------------------------------------") 50 | fmt.Println("Mismatch. Expected:") 51 | fmt.Println(strings.Join(unmatched, "\n")) 52 | fmt.Println("Got:") 53 | fmt.Println(strings.Join(unmatchedGot, "\n")) 54 | fmt.Println("-----------------------------------------------") 55 | } 56 | lastExp = index1 57 | lastGot = index2 58 | } 59 | 60 | if len(l1)-1 > lastExp { 61 | unmatched := l1[lastExp+1 : len(l1)-1] 62 | unmatchedGot := l2[lastGot+1 : len(l2)-1] 63 | fmt.Println("Mismatch. Expected:\n") 64 | fmt.Println(strings.Join(unmatched, "\n")) 65 | fmt.Println("\nGot:\n") 66 | fmt.Println(strings.Join(unmatchedGot, "\n")) 67 | } 68 | } 69 | 70 | func printFail(var1, var2 interface{}, t *testing.T) { 71 | t.Fail() 72 | spew.Config.DisableMethods = true 73 | fmt.Println("Unexpected schema found.\n") 74 | compareLines(spew.Sdump(var1), spew.Sdump(var2)) 75 | //compareVars("", var1, var2) 76 | } 77 | 78 | func TestSimpleType(t *testing.T) { 79 | gql := ` 80 | type Oncle { 81 | pipe: ID 82 | } 83 | ` 84 | 85 | expected := graphql.NewObject(graphql.ObjectConfig{ 86 | Name: "Oncle", 87 | Fields: graphql.Fields{ 88 | "pipe": &graphql.Field{ 89 | Type: graphql.ID, 90 | }, 91 | }, 92 | }) 93 | 94 | ctx, _ := Generate(gql) 95 | oncle := ctx.Object("Oncle") 96 | if !reflect.DeepEqual(oncle, expected) { 97 | printFail(expected, oncle, t) 98 | } 99 | } 100 | 101 | func TestReferenceTypes(t *testing.T) { 102 | gql := ` 103 | type Oncle { 104 | pipe: p 105 | } 106 | type p { 107 | feld: e 108 | } 109 | interface e {} 110 | ` 111 | 112 | e := graphql.NewInterface(graphql.InterfaceConfig{ 113 | Name: "e", 114 | }) 115 | p := graphql.NewObject(graphql.ObjectConfig{ 116 | Name: "p", 117 | Fields: graphql.Fields{ 118 | "feld": &graphql.Field{ 119 | Type: e, 120 | }, 121 | }, 122 | }) 123 | expected := graphql.NewObject(graphql.ObjectConfig{ 124 | Name: "Oncle", 125 | Fields: graphql.Fields{ 126 | "pipe": &graphql.Field{ 127 | Type: p, 128 | }, 129 | }, 130 | }) 131 | 132 | ctx, _ := Generate(gql) 133 | oncle := ctx.Object("Oncle") 134 | if !reflect.DeepEqual(oncle, expected) { 135 | printFail(expected, oncle, t) 136 | } 137 | } 138 | 139 | func TestRequiredType(t *testing.T) { 140 | gql := ` 141 | type Oncle { 142 | pipe: String! 143 | } 144 | ` 145 | 146 | expected := graphql.NewObject(graphql.ObjectConfig{ 147 | Name: "Oncle", 148 | Fields: graphql.Fields{ 149 | "pipe": &graphql.Field{ 150 | Type: graphql.NewNonNull(graphql.String), 151 | }, 152 | }, 153 | }) 154 | 155 | ctx, _ := Generate(gql) 156 | oncle := ctx.Object("Oncle") 157 | if !reflect.DeepEqual(oncle, expected) { 158 | printFail(expected, oncle, t) 159 | } 160 | } 161 | 162 | func TestSimpleInterface(t *testing.T) { 163 | gql := ` 164 | interface World {} 165 | ` 166 | expected := graphql.NewInterface(graphql.InterfaceConfig{ 167 | Name: "World", 168 | }) 169 | 170 | ctx, _ := Generate(gql) 171 | world := ctx.Interface("World") 172 | if !reflect.DeepEqual(world, expected) { 173 | printFail(expected, world, t) 174 | } 175 | } 176 | 177 | func TestSimpleTypeImplementsInterface(t *testing.T) { 178 | gql := ` 179 | interface World {} 180 | type Oncle implements World {} 181 | ` 182 | world := graphql.NewInterface(graphql.InterfaceConfig{ 183 | Name: "World", 184 | }) 185 | expected := graphql.NewObject(graphql.ObjectConfig{ 186 | Name: "Oncle", 187 | Interfaces: []*graphql.Interface{world}, 188 | }) 189 | 190 | ctx, _ := Generate(gql) 191 | oncle := ctx.Object("Oncle") 192 | if !reflect.DeepEqual(oncle, expected) { 193 | printFail(expected, oncle, t) 194 | } 195 | } 196 | 197 | func TestSimpleTypeImplementsMultipleInterfaces(t *testing.T) { 198 | gql := ` 199 | interface World {} 200 | interface Balloon {} 201 | type Oncle implements World, Balloon {} 202 | ` 203 | world := graphql.NewInterface(graphql.InterfaceConfig{ 204 | Name: "World", 205 | }) 206 | balloon := graphql.NewInterface(graphql.InterfaceConfig{ 207 | Name: "Balloon", 208 | }) 209 | expected := graphql.NewObject(graphql.ObjectConfig{ 210 | Name: "Oncle", 211 | Interfaces: []*graphql.Interface{world, balloon}, 212 | }) 213 | 214 | ctx, _ := Generate(gql) 215 | oncle := ctx.Object("Oncle") 216 | if !reflect.DeepEqual(oncle, expected) { 217 | printFail(expected, oncle, t) 218 | } 219 | } 220 | 221 | func TestSingleValueEnum(t *testing.T) { 222 | gql := `enum Hello { WORLD }` 223 | 224 | expected := graphql.NewEnum(graphql.EnumConfig{ 225 | Name: "Hello", 226 | Values: graphql.EnumValueConfigMap{ 227 | "WORLD": &graphql.EnumValueConfig{ 228 | Value: 0, 229 | }, 230 | }, 231 | }) 232 | 233 | ctx, _ := Generate(gql) 234 | enum := ctx.Enums("Hello") 235 | if !reflect.DeepEqual(enum, expected) { 236 | printFail(expected, enum, t) 237 | } 238 | } 239 | 240 | func TestMultiValueEnum(t *testing.T) { 241 | gql := `enum Hello { WORLD, HERE }` 242 | 243 | expected := graphql.NewEnum(graphql.EnumConfig{ 244 | Name: "Hello", 245 | Values: graphql.EnumValueConfigMap{ 246 | "WORLD": &graphql.EnumValueConfig{ 247 | Value: 0, 248 | }, 249 | "HERE": &graphql.EnumValueConfig{ 250 | Value: 1, 251 | }, 252 | }, 253 | }) 254 | 255 | ctx, _ := Generate(gql) 256 | enum := ctx.Enums("Hello") 257 | if !reflect.DeepEqual(enum, expected) { 258 | printFail(expected, enum, t) 259 | } 260 | } 261 | 262 | func TestSimpleFieldWithArg(t *testing.T) { 263 | gql := ` 264 | type Hello { 265 | world(flag: Boolean): String 266 | } 267 | ` 268 | 269 | expected := graphql.NewObject(graphql.ObjectConfig{ 270 | Name: "Hello", 271 | Fields: graphql.Fields{ 272 | "world": &graphql.Field{ 273 | Type: graphql.String, 274 | Args: graphql.FieldConfigArgument{ 275 | "flag": &graphql.ArgumentConfig{ 276 | Type: graphql.Boolean, 277 | }, 278 | }, 279 | }, 280 | }, 281 | }) 282 | 283 | ctx, _ := Generate(gql) 284 | hello := ctx.Object("Hello") 285 | if !reflect.DeepEqual(hello, expected) { 286 | printFail(expected, hello, t) 287 | } 288 | } 289 | 290 | func TestSimpleFieldWithArgDefaultValue(t *testing.T) { 291 | gql := ` 292 | type Hello { 293 | world(flag: Boolean! = true): String 294 | } 295 | ` 296 | 297 | expected := graphql.NewObject(graphql.ObjectConfig{ 298 | Name: "Hello", 299 | Fields: graphql.Fields{ 300 | "world": &graphql.Field{ 301 | Type: graphql.String, 302 | Args: graphql.FieldConfigArgument{ 303 | "flag": &graphql.ArgumentConfig{ 304 | Type: graphql.NewNonNull(graphql.Boolean), 305 | DefaultValue: true, 306 | }, 307 | }, 308 | }, 309 | }, 310 | }) 311 | 312 | ctx, _ := Generate(gql) 313 | hello := ctx.Object("Hello") 314 | if !reflect.DeepEqual(hello, expected) { 315 | printFail(expected, hello, t) 316 | } 317 | } 318 | 319 | func TestSimpleFieldWithListArg(t *testing.T) { 320 | gql := ` 321 | type Hello { 322 | world(things: [String]): String 323 | } 324 | ` 325 | 326 | expected := graphql.NewObject(graphql.ObjectConfig{ 327 | Name: "Hello", 328 | Fields: graphql.Fields{ 329 | "world": &graphql.Field{ 330 | Type: graphql.String, 331 | Args: graphql.FieldConfigArgument{ 332 | "things": &graphql.ArgumentConfig{ 333 | Type: graphql.NewList(graphql.String), 334 | }, 335 | }, 336 | }, 337 | }, 338 | }) 339 | 340 | ctx, _ := Generate(gql) 341 | hello := ctx.Object("Hello") 342 | if !reflect.DeepEqual(hello, expected) { 343 | printFail(expected, hello, t) 344 | } 345 | } 346 | 347 | func TestSimpleFieldWithTwoArg(t *testing.T) { 348 | gql := ` 349 | type Hello { 350 | world(argOne: Boolean, argTwo: Int): String 351 | } 352 | ` 353 | 354 | expected := graphql.NewObject(graphql.ObjectConfig{ 355 | Name: "Hello", 356 | Fields: graphql.Fields{ 357 | "world": &graphql.Field{ 358 | Type: graphql.String, 359 | Args: graphql.FieldConfigArgument{ 360 | "argOne": &graphql.ArgumentConfig{ 361 | Type: graphql.Boolean, 362 | }, 363 | "argTwo": &graphql.ArgumentConfig{ 364 | Type: graphql.Int, 365 | }, 366 | }, 367 | }, 368 | }, 369 | }) 370 | 371 | ctx, _ := Generate(gql) 372 | hello := ctx.Object("Hello") 373 | if !reflect.DeepEqual(hello, expected) { 374 | printFail(expected, hello, t) 375 | } 376 | } 377 | 378 | func TestWithArgumentAndComplexDefaultValueType(t *testing.T) { 379 | gql := ` 380 | type Oncle { 381 | pipe: ID 382 | five(argument: [String] = ["String", "String"] ): String 383 | } 384 | ` 385 | 386 | expected := graphql.NewObject(graphql.ObjectConfig{ 387 | Name: "Oncle", 388 | Fields: graphql.Fields{ 389 | "pipe": &graphql.Field{ 390 | Type: graphql.ID, 391 | }, 392 | "five": &graphql.Field{ 393 | Type: graphql.String, 394 | Args: graphql.FieldConfigArgument{ 395 | "argument": &graphql.ArgumentConfig{ 396 | Type: graphql.NewList(graphql.String), 397 | DefaultValue: []interface{}{"String", "String"}, 398 | }, 399 | }, 400 | }, 401 | }, 402 | }) 403 | 404 | ctx, _ := Generate(gql) 405 | oncle := ctx.Object("Oncle") 406 | if !reflect.DeepEqual(oncle, expected) { 407 | printFail(expected, oncle, t) 408 | } 409 | } 410 | 411 | func TestSimpleUnion(t *testing.T) { 412 | gql := ` 413 | union Hello = World 414 | type World {}` 415 | 416 | world := graphql.NewObject(graphql.ObjectConfig{ 417 | Name: "World", 418 | }) 419 | expected := graphql.NewUnion(graphql.UnionConfig{ 420 | Name: "Hello", 421 | Types: []*graphql.Object{world}, 422 | }) 423 | 424 | ctx, _ := Generate(gql) 425 | hello := ctx.Union("Hello") 426 | if !reflect.DeepEqual(hello, expected) { 427 | printFail(expected, hello, t) 428 | } 429 | } 430 | 431 | func TestMultiUnion(t *testing.T) { 432 | gql := ` 433 | type Wor {} 434 | type ld {} 435 | union Hello = Wor | ld` 436 | 437 | wor := graphql.NewObject(graphql.ObjectConfig{ 438 | Name: "Wor", 439 | }) 440 | ld := graphql.NewObject(graphql.ObjectConfig{ 441 | Name: "ld", 442 | }) 443 | expected := graphql.NewUnion(graphql.UnionConfig{ 444 | Name: "Hello", 445 | Types: []*graphql.Object{wor, ld}, 446 | }) 447 | 448 | ctx, _ := Generate(gql) 449 | hello := ctx.Union("Hello") 450 | if !reflect.DeepEqual(hello, expected) { 451 | printFail(expected, hello, t) 452 | } 453 | } 454 | 455 | func TestScalar(t *testing.T) { 456 | gql := `scalar Hello` 457 | 458 | expected := graphql.NewScalar(graphql.ScalarConfig{ 459 | Name: "Hello", 460 | }) 461 | 462 | ctx, _ := Generate(gql) 463 | hello := ctx.Scalar("Hello") 464 | if !reflect.DeepEqual(hello, expected) { 465 | printFail(expected, hello, t) 466 | } 467 | } 468 | 469 | func TestSimpleInputObject(t *testing.T) { 470 | gql := ` 471 | input Hello { 472 | world: String 473 | }` 474 | 475 | expected := graphql.NewInputObject(graphql.InputObjectConfig{ 476 | Name: "Hello", 477 | Fields: graphql.InputObjectConfigFieldMap{ 478 | "world": &graphql.InputObjectFieldConfig{ 479 | Type: graphql.String, 480 | }, 481 | }, 482 | }) 483 | 484 | ctx, _ := Generate(gql) 485 | hello := ctx.InputObject("Hello") 486 | if !reflect.DeepEqual(hello, expected) { 487 | printFail(expected, hello, t) 488 | } 489 | } 490 | 491 | func TestSimpleExtendType(t *testing.T) { 492 | gql := ` 493 | type Hello { 494 | test: Boolean 495 | } 496 | extend type Hello { 497 | world: String 498 | }` 499 | expected := graphql.NewObject(graphql.ObjectConfig{ 500 | Name: "Hello", 501 | Fields: graphql.Fields{ 502 | "test": &graphql.Field{ 503 | Type: graphql.Boolean, 504 | }, 505 | "world": &graphql.Field{ 506 | Type: graphql.String, 507 | }, 508 | }, 509 | }) 510 | 511 | ctx, _ := Generate(gql) 512 | hello := ctx.Object("Hello") 513 | if !reflect.DeepEqual(hello, expected) { 514 | printFail(expected, hello, t) 515 | } 516 | } 517 | -------------------------------------------------------------------------------- /generator/schema.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "errors" 5 | "github.com/graphql-go/graphql" 6 | ) 7 | 8 | func CreateSchemaFromContext(ctx *Context) (graphql.Schema, error) { 9 | if query, ok := ctx.objects["Query"]; ok { 10 | return graphql.NewSchema(graphql.SchemaConfig { 11 | Query: query, 12 | }) 13 | } else { 14 | return graphql.Schema{}, errors.New("Your context does not define a Query root type!") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /generator/schema_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | //"github.com/graphql-go/graphql" 7 | ) 8 | 9 | func TestSchemaCreation(t *testing.T) { 10 | gql := ` 11 | type Hello { 12 | f: String 13 | } 14 | type Query { 15 | a: Hello 16 | }` 17 | 18 | ctx, errp := Generate(gql) 19 | if errp != nil { 20 | fmt.Print(errp) 21 | t.FailNow() 22 | } 23 | _, err := CreateSchemaFromContext(ctx) 24 | if err != nil { 25 | fmt.Print(err) 26 | t.FailNow() 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /generator/utils.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "github.com/graphql-go/graphql/language/ast" 5 | ) 6 | 7 | type mapFunc func(x interface{}) interface{} 8 | 9 | func mapSlice(b []ast.Value, fn mapFunc) (newSlice []interface{}) { 10 | for _, item := range b { 11 | newSlice = append(newSlice, fn(item)) 12 | } 13 | return 14 | } 15 | --------------------------------------------------------------------------------