github.com/dgraph-io/dgraph@v1.2.8/graphql/schema/wrappers.go (about) 1 /* 2 * Copyright 2019 Dgraph Labs, Inc. and Contributors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package schema 18 19 import ( 20 "fmt" 21 "strconv" 22 "strings" 23 24 "github.com/dgraph-io/dgraph/x" 25 "github.com/pkg/errors" 26 "github.com/vektah/gqlparser/ast" 27 ) 28 29 // Wrap the github.com/vektah/gqlparser/ast defintions so that the bulk of the GraphQL 30 // algorithm and interface is dependent on behaviours we expect from a GraphQL schema 31 // and validation, but not dependent the exact structure in the gqlparser. 32 // 33 // This also auto hooks up some bookkeeping that's otherwise no fun. E.g. getting values for 34 // field arguments requires the variable map from the operation - so we'd need to carry vars 35 // through all the resolver functions. Much nicer if they are resolved by magic here. 36 37 // QueryType is currently supported queries 38 type QueryType string 39 40 // MutationType is currently supported mutations 41 type MutationType string 42 43 // Query/Mutation types and arg names 44 const ( 45 GetQuery QueryType = "get" 46 FilterQuery QueryType = "query" 47 SchemaQuery QueryType = "schema" 48 NotSupportedQuery QueryType = "notsupported" 49 AddMutation MutationType = "add" 50 UpdateMutation MutationType = "update" 51 DeleteMutation MutationType = "delete" 52 NotSupportedMutation MutationType = "notsupported" 53 IDType = "ID" 54 IDArgName = "id" 55 InputArgName = "input" 56 FilterArgName = "filter" 57 ) 58 59 // Schema represents a valid GraphQL schema 60 type Schema interface { 61 Operation(r *Request) (Operation, error) 62 Queries(t QueryType) []string 63 Mutations(t MutationType) []string 64 } 65 66 // An Operation is a single valid GraphQL operation. It contains either 67 // Queries or Mutations, but not both. Subscriptions are not yet supported. 68 type Operation interface { 69 Queries() []Query 70 Mutations() []Mutation 71 Schema() Schema 72 IsQuery() bool 73 IsMutation() bool 74 IsSubscription() bool 75 } 76 77 // A Field is one field from an Operation. 78 type Field interface { 79 Name() string 80 Alias() string 81 ResponseName() string 82 ArgValue(name string) interface{} 83 IsArgListType(name string) bool 84 IDArgValue() (*string, uint64, error) 85 XIDArg() string 86 SetArgTo(arg string, val interface{}) 87 Skip() bool 88 Include() bool 89 Type() Type 90 SelectionSet() []Field 91 Location() x.Location 92 DgraphPredicate() string 93 Operation() Operation 94 // InterfaceType tells us whether this field represents a GraphQL Interface. 95 InterfaceType() bool 96 IncludeInterfaceField(types []interface{}) bool 97 TypeName(dgraphTypes []interface{}) string 98 GetObjectName() string 99 } 100 101 // A Mutation is a field (from the schema's Mutation type) from an Operation 102 type Mutation interface { 103 Field 104 MutationType() MutationType 105 MutatedType() Type 106 QueryField() Field 107 } 108 109 // A Query is a field (from the schema's Query type) from an Operation 110 type Query interface { 111 Field 112 QueryType() QueryType 113 Rename(newName string) 114 } 115 116 // A Type is a GraphQL type like: Float, T, T! and [T!]!. If it's not a list, then 117 // ListType is nil. If it's an object type then Field gets field definitions by 118 // name from the definition of the type; IDField gets the ID field of the type. 119 type Type interface { 120 Field(name string) FieldDefinition 121 IDField() FieldDefinition 122 XIDField() FieldDefinition 123 Name() string 124 DgraphName() string 125 DgraphPredicate(fld string) string 126 Nullable() bool 127 ListType() Type 128 Interfaces() []string 129 EnsureNonNulls(map[string]interface{}, string) error 130 fmt.Stringer 131 } 132 133 // A FieldDefinition is a field as defined in some Type in the schema. As opposed 134 // to a Field, which is an instance of a query or mutation asking for a field 135 // (which in turn must have a FieldDefinition of the right type in the schema.) 136 type FieldDefinition interface { 137 Name() string 138 Type() Type 139 IsID() bool 140 Inverse() (Type, FieldDefinition) 141 } 142 143 type astType struct { 144 typ *ast.Type 145 inSchema *ast.Schema 146 dgraphPredicate map[string]map[string]string 147 } 148 149 type schema struct { 150 schema *ast.Schema 151 // dgraphPredicate gives us the dgraph predicate corresponding to a typeName + fieldName. 152 // It is pre-computed so that runtime queries and mutations can look it 153 // up quickly. 154 // The key for the first map are the type names. The second map has a mapping of the 155 // fieldName => dgraphPredicate. 156 dgraphPredicate map[string]map[string]string 157 // Map of mutation field name to mutated type. 158 mutatedType map[string]*astType 159 // Map from typename to ast.Definition 160 typeNameAst map[string][]*ast.Definition 161 } 162 163 type operation struct { 164 op *ast.OperationDefinition 165 vars map[string]interface{} 166 167 // The fields below are used by schema introspection queries. 168 query string 169 doc *ast.QueryDocument 170 inSchema *schema 171 } 172 173 type field struct { 174 field *ast.Field 175 op *operation 176 sel ast.Selection 177 // arguments contains the computed values for arguments taking into account the values 178 // for the GraphQL variables supplied in the query. 179 arguments map[string]interface{} 180 } 181 182 type fieldDefinition struct { 183 fieldDef *ast.FieldDefinition 184 inSchema *ast.Schema 185 dgraphPredicate map[string]map[string]string 186 } 187 188 type mutation field 189 type query field 190 191 func (s *schema) Queries(t QueryType) []string { 192 var result []string 193 for _, q := range s.schema.Query.Fields { 194 if queryType(q.Name) == t { 195 result = append(result, q.Name) 196 } 197 } 198 return result 199 } 200 201 func (s *schema) Mutations(t MutationType) []string { 202 var result []string 203 for _, m := range s.schema.Mutation.Fields { 204 if mutationType(m.Name) == t { 205 result = append(result, m.Name) 206 } 207 } 208 return result 209 } 210 211 func (o *operation) IsQuery() bool { 212 return o.op.Operation == ast.Query 213 } 214 215 func (o *operation) IsMutation() bool { 216 return o.op.Operation == ast.Mutation 217 } 218 219 func (o *operation) IsSubscription() bool { 220 return o.op.Operation == ast.Subscription 221 } 222 223 func (o *operation) Schema() Schema { 224 return o.inSchema 225 } 226 227 func (o *operation) Queries() (qs []Query) { 228 if !o.IsQuery() { 229 return 230 } 231 232 for _, s := range o.op.SelectionSet { 233 if f, ok := s.(*ast.Field); ok { 234 qs = append(qs, &query{field: f, op: o, sel: s}) 235 } 236 } 237 238 return 239 } 240 241 func (o *operation) Mutations() (ms []Mutation) { 242 if !o.IsMutation() { 243 return 244 } 245 246 for _, s := range o.op.SelectionSet { 247 if f, ok := s.(*ast.Field); ok { 248 ms = append(ms, &mutation{field: f, op: o}) 249 } 250 } 251 252 return 253 } 254 255 // parentInterface returns the name of an interface that a field belonging to a type definition 256 // typDef inherited from. If there is no such interface, then it returns an empty string. 257 // 258 // Given the following schema 259 // interface A { 260 // name: String 261 // } 262 // 263 // type B implements A { 264 // name: String 265 // age: Int 266 // } 267 // 268 // calling parentInterface on the fieldName name with type definition for B, would return A. 269 func parentInterface(sch *ast.Schema, typDef *ast.Definition, fieldName string) *ast.Definition { 270 if len(typDef.Interfaces) == 0 { 271 return nil 272 } 273 274 for _, iface := range typDef.Interfaces { 275 interfaceDef := sch.Types[iface] 276 for _, interfaceField := range interfaceDef.Fields { 277 if fieldName == interfaceField.Name { 278 return interfaceDef 279 } 280 } 281 } 282 return nil 283 } 284 285 func dgraphMapping(sch *ast.Schema) map[string]map[string]string { 286 const ( 287 add = "Add" 288 update = "Update" 289 del = "Delete" 290 payload = "Payload" 291 ) 292 293 dgraphPredicate := make(map[string]map[string]string) 294 for _, inputTyp := range sch.Types { 295 // We only want to consider input types (object and interface) defined by the user as part 296 // of the schema hence we ignore BuiltIn, query and mutation types. 297 if inputTyp.BuiltIn || inputTyp.Name == "Query" || inputTyp.Name == "Mutation" || 298 (inputTyp.Kind != ast.Object && inputTyp.Kind != ast.Interface) { 299 continue 300 } 301 302 originalTyp := inputTyp 303 inputTypeName := inputTyp.Name 304 if strings.HasPrefix(inputTypeName, add) && strings.HasSuffix(inputTypeName, payload) { 305 continue 306 } 307 308 dgraphPredicate[originalTyp.Name] = make(map[string]string) 309 310 if (strings.HasPrefix(inputTypeName, update) || strings.HasPrefix(inputTypeName, del)) && 311 strings.HasSuffix(inputTypeName, payload) { 312 // For UpdateTypePayload and DeleteTypePayload, inputTyp should be Type. 313 if strings.HasPrefix(inputTypeName, update) { 314 inputTypeName = strings.TrimSuffix(strings.TrimPrefix(inputTypeName, update), 315 payload) 316 } else if strings.HasPrefix(inputTypeName, del) { 317 inputTypeName = strings.TrimSuffix(strings.TrimPrefix(inputTypeName, del), payload) 318 } 319 inputTyp = sch.Types[inputTypeName] 320 } 321 322 for _, fld := range inputTyp.Fields { 323 if isID(fld) { 324 // We don't need a mapping for the field, as we the dgraph predicate for them is 325 // fixed i.e. uid. 326 continue 327 } 328 typName := typeName(inputTyp) 329 parentInt := parentInterface(sch, inputTyp, fld.Name) 330 if parentInt != nil { 331 typName = typeName(parentInt) 332 } 333 // 1. For fields that have @dgraph(pred: xxxName) directive, field name would be 334 // xxxName. 335 // 2. For fields where the type (or underlying interface) has a @dgraph(type: xxxName) 336 // directive, field name would be xxxName.fldName. 337 // 338 // The cases below cover the cases where neither the type or field have @dgraph 339 // directive. 340 // 3. For types which don't inherit from an interface the keys, value would be. 341 // typName,fldName => typName.fldName 342 // 4. For types which inherit fields from an interface 343 // typName,fldName => interfaceName.fldName 344 // 5. For DeleteTypePayload type 345 // DeleteTypePayload,fldName => typName.fldName 346 347 fname := fieldName(fld, typName) 348 dgraphPredicate[originalTyp.Name][fld.Name] = fname 349 } 350 } 351 return dgraphPredicate 352 } 353 354 func mutatedTypeMapping(s *ast.Schema, 355 dgraphPredicate map[string]map[string]string) map[string]*astType { 356 if s.Mutation == nil { 357 return nil 358 } 359 360 m := make(map[string]*astType, len(s.Mutation.Fields)) 361 for _, field := range s.Mutation.Fields { 362 mutatedTypeName := "" 363 switch { 364 case strings.HasPrefix(field.Name, "add"): 365 mutatedTypeName = strings.TrimPrefix(field.Name, "add") 366 case strings.HasPrefix(field.Name, "update"): 367 mutatedTypeName = strings.TrimPrefix(field.Name, "update") 368 case strings.HasPrefix(field.Name, "delete"): 369 mutatedTypeName = strings.TrimPrefix(field.Name, "delete") 370 default: 371 } 372 // This is a convoluted way of getting the type for mutatedTypeName. We get the definition 373 // for UpdateTPayload and get the type from the first field. There is no direct way to get 374 // the type from the definition of an object. We use Update and not Add here because 375 // Interfaces only have Update. 376 var def *ast.Definition 377 if def = s.Types["Update"+mutatedTypeName+"Payload"]; def == nil { 378 def = s.Types["Add"+mutatedTypeName+"Payload"] 379 } 380 381 if def == nil { 382 continue 383 } 384 385 // Accessing 0th element should be safe to do as according to the spec an object must define 386 // one or more fields. 387 typ := def.Fields[0].Type 388 // This would contain mapping of mutation field name to the Type() 389 // for e.g. addPost => astType for Post 390 m[field.Name] = &astType{typ, s, dgraphPredicate} 391 } 392 return m 393 } 394 395 func typeMappings(s *ast.Schema) map[string][]*ast.Definition { 396 typeNameAst := make(map[string][]*ast.Definition) 397 398 for _, typ := range s.Types { 399 name := typeName(typ) 400 typeNameAst[name] = append(typeNameAst[name], typ) 401 } 402 403 return typeNameAst 404 } 405 406 // AsSchema wraps a github.com/vektah/gqlparser/ast.Schema. 407 func AsSchema(s *ast.Schema) Schema { 408 dgraphPredicate := dgraphMapping(s) 409 return &schema{ 410 schema: s, 411 dgraphPredicate: dgraphPredicate, 412 mutatedType: mutatedTypeMapping(s, dgraphPredicate), 413 typeNameAst: typeMappings(s), 414 } 415 } 416 417 func responseName(f *ast.Field) string { 418 if f.Alias == "" { 419 return f.Name 420 } 421 return f.Alias 422 } 423 424 func (f *field) Name() string { 425 return f.field.Name 426 } 427 428 func (f *field) Alias() string { 429 return f.field.Alias 430 } 431 432 func (f *field) ResponseName() string { 433 return responseName(f.field) 434 } 435 436 func (f *field) SetArgTo(arg string, val interface{}) { 437 if f.arguments == nil { 438 f.arguments = make(map[string]interface{}) 439 } 440 f.arguments[arg] = val 441 } 442 443 func (f *field) ArgValue(name string) interface{} { 444 if f.arguments == nil { 445 // Compute and cache the map first time this function is called for a field. 446 f.arguments = f.field.ArgumentMap(f.op.vars) 447 } 448 return f.arguments[name] 449 } 450 451 func (f *field) IsArgListType(name string) bool { 452 arg := f.field.Arguments.ForName(name) 453 if arg == nil { 454 return false 455 } 456 457 return arg.Value.ExpectedType.Elem != nil 458 } 459 460 func (f *field) Skip() bool { 461 dir := f.field.Directives.ForName("skip") 462 if dir == nil { 463 return false 464 } 465 return dir.ArgumentMap(f.op.vars)["if"].(bool) 466 } 467 468 func (f *field) Include() bool { 469 dir := f.field.Directives.ForName("include") 470 if dir == nil { 471 return true 472 } 473 return dir.ArgumentMap(f.op.vars)["if"].(bool) 474 } 475 476 func (f *field) XIDArg() string { 477 xidArgName := "" 478 for _, arg := range f.field.Arguments { 479 if arg.Name != IDArgName { 480 xidArgName = arg.Name 481 } 482 } 483 return f.Type().DgraphPredicate(xidArgName) 484 } 485 486 func (f *field) IDArgValue() (xid *string, uid uint64, err error) { 487 idField := f.Type().IDField() 488 xidArgName := "" 489 // This method is only called for Get queries. These queries can accept one of the 490 // combinations as input. 491 // 1. ID only 492 // 2. XID only 493 // 3. ID and XID fields 494 // Therefore, the non ID field is an XID field. 495 for _, arg := range f.field.Arguments { 496 if arg.Name != idField.Name() { 497 xidArgName = arg.Name 498 } 499 } 500 if xidArgName != "" { 501 xidArgVal, ok := f.ArgValue(xidArgName).(string) 502 pos := f.field.GetPosition() 503 if !ok { 504 err = x.GqlErrorf("Argument (%s) of %s was not able to be parsed as a string", 505 xidArgName, f.Name()).WithLocations(x.Location{Line: pos.Line, Column: pos.Column}) 506 return 507 } 508 xid = &xidArgVal 509 } 510 511 if idField == nil && xid == nil { 512 // This means that both were optional and were not supplied, lets return here. 513 return 514 } 515 516 idArg := f.ArgValue(idField.Name()) 517 if idArg != nil { 518 id, ok := idArg.(string) 519 var ierr error 520 uid, ierr = strconv.ParseUint(id, 0, 64) 521 522 if !ok || ierr != nil { 523 pos := f.field.GetPosition() 524 err = x.GqlErrorf("ID argument (%s) of %s was not able to be parsed", id, f.Name()). 525 WithLocations(x.Location{Line: pos.Line, Column: pos.Column}) 526 return 527 } 528 } 529 530 return 531 } 532 533 func (f *field) Type() Type { 534 return &astType{ 535 typ: f.field.Definition.Type, 536 inSchema: f.op.inSchema.schema, 537 dgraphPredicate: f.op.inSchema.dgraphPredicate, 538 } 539 } 540 541 func (f *field) InterfaceType() bool { 542 return f.op.inSchema.schema.Types[f.field.Definition.Type.Name()].Kind == ast.Interface 543 } 544 545 func (f *field) GetObjectName() string { 546 return f.field.ObjectDefinition.Name 547 } 548 549 func (f *field) SelectionSet() (flds []Field) { 550 for _, s := range f.field.SelectionSet { 551 if fld, ok := s.(*ast.Field); ok { 552 flds = append(flds, &field{ 553 field: fld, 554 op: f.op, 555 }) 556 } 557 if fragment, ok := s.(*ast.InlineFragment); ok { 558 // This is the case where an inline fragment is defined within a query 559 // block. Usually this is for requesting some fields for a concrete type 560 // within a query for an interface. 561 for _, s := range fragment.SelectionSet { 562 if fld, ok := s.(*ast.Field); ok { 563 flds = append(flds, &field{ 564 field: fld, 565 op: f.op, 566 }) 567 } 568 } 569 } 570 } 571 572 return 573 } 574 575 func (f *field) Location() x.Location { 576 return x.Location{ 577 Line: f.field.Position.Line, 578 Column: f.field.Position.Column} 579 } 580 581 func (f *field) Operation() Operation { 582 return f.op 583 } 584 585 func (f *field) DgraphPredicate() string { 586 return f.op.inSchema.dgraphPredicate[f.field.ObjectDefinition.Name][f.Name()] 587 } 588 589 func (f *field) TypeName(dgraphTypes []interface{}) string { 590 for _, typ := range dgraphTypes { 591 styp, ok := typ.(string) 592 if !ok { 593 continue 594 } 595 596 for _, origTyp := range f.op.inSchema.typeNameAst[styp] { 597 if origTyp.Kind != ast.Object { 598 continue 599 } 600 return origTyp.Name 601 } 602 603 } 604 return "" 605 } 606 607 func (f *field) IncludeInterfaceField(dgraphTypes []interface{}) bool { 608 // Given a list of dgraph types, we query the schema and find the one which is an ast.Object 609 // and not an Interface object. 610 for _, typ := range dgraphTypes { 611 styp, ok := typ.(string) 612 if !ok { 613 continue 614 } 615 for _, origTyp := range f.op.inSchema.typeNameAst[styp] { 616 if origTyp.Kind == ast.Object { 617 // If the field doesn't exist in the map corresponding to the object type, then we 618 // don't need to include it. 619 _, ok := f.op.inSchema.dgraphPredicate[origTyp.Name][f.Name()] 620 return ok || f.Name() == Typename 621 } 622 } 623 624 } 625 return false 626 } 627 628 func (q *query) Rename(newName string) { 629 q.field.Name = newName 630 } 631 632 func (q *query) Name() string { 633 return (*field)(q).Name() 634 } 635 636 func (q *query) Alias() string { 637 return (*field)(q).Alias() 638 } 639 640 func (q *query) SetArgTo(arg string, val interface{}) { 641 (*field)(q).SetArgTo(arg, val) 642 } 643 644 func (q *query) ArgValue(name string) interface{} { 645 return (*field)(q).ArgValue(name) 646 } 647 648 func (q *query) IsArgListType(name string) bool { 649 return (*field)(q).IsArgListType(name) 650 } 651 652 func (q *query) Skip() bool { 653 return false 654 } 655 656 func (q *query) Include() bool { 657 return true 658 } 659 660 func (q *query) IDArgValue() (*string, uint64, error) { 661 return (*field)(q).IDArgValue() 662 } 663 664 func (q *query) XIDArg() string { 665 return (*field)(q).XIDArg() 666 } 667 668 func (q *query) Type() Type { 669 return (*field)(q).Type() 670 } 671 672 func (q *query) SelectionSet() []Field { 673 return (*field)(q).SelectionSet() 674 } 675 676 func (q *query) Location() x.Location { 677 return (*field)(q).Location() 678 } 679 680 func (q *query) ResponseName() string { 681 return (*field)(q).ResponseName() 682 } 683 684 func (q *query) GetObjectName() string { 685 return q.field.ObjectDefinition.Name 686 } 687 688 func (q *query) QueryType() QueryType { 689 return queryType(q.Name()) 690 } 691 692 func queryType(name string) QueryType { 693 switch { 694 case strings.HasPrefix(name, "get"): 695 return GetQuery 696 case name == "__schema" || name == "__type": 697 return SchemaQuery 698 case strings.HasPrefix(name, "query"): 699 return FilterQuery 700 default: 701 return NotSupportedQuery 702 } 703 } 704 705 func (q *query) Operation() Operation { 706 return (*field)(q).Operation() 707 } 708 709 func (q *query) DgraphPredicate() string { 710 return (*field)(q).DgraphPredicate() 711 } 712 713 func (q *query) InterfaceType() bool { 714 return (*field)(q).InterfaceType() 715 } 716 717 func (q *query) TypeName(dgraphTypes []interface{}) string { 718 return (*field)(q).TypeName(dgraphTypes) 719 } 720 721 func (q *query) IncludeInterfaceField(dgraphTypes []interface{}) bool { 722 return (*field)(q).IncludeInterfaceField(dgraphTypes) 723 } 724 725 func (m *mutation) Name() string { 726 return (*field)(m).Name() 727 } 728 729 func (m *mutation) Alias() string { 730 return (*field)(m).Alias() 731 } 732 733 func (m *mutation) SetArgTo(arg string, val interface{}) { 734 (*field)(m).SetArgTo(arg, val) 735 } 736 737 func (m *mutation) IsArgListType(name string) bool { 738 return (*field)(m).IsArgListType(name) 739 } 740 741 func (m *mutation) ArgValue(name string) interface{} { 742 return (*field)(m).ArgValue(name) 743 } 744 745 func (m *mutation) Skip() bool { 746 return false 747 } 748 749 func (m *mutation) Include() bool { 750 return true 751 } 752 753 func (m *mutation) Type() Type { 754 return (*field)(m).Type() 755 } 756 757 func (m *mutation) InterfaceType() bool { 758 return (*field)(m).InterfaceType() 759 } 760 761 func (m *mutation) XIDArg() string { 762 return (*field)(m).XIDArg() 763 } 764 765 func (m *mutation) IDArgValue() (*string, uint64, error) { 766 return (*field)(m).IDArgValue() 767 } 768 769 func (m *mutation) SelectionSet() []Field { 770 return (*field)(m).SelectionSet() 771 } 772 773 func (m *mutation) QueryField() Field { 774 // TODO: All our mutations currently have exactly 1 field, but that will change 775 // - need a better way to get the right field by convention. 776 return m.SelectionSet()[0] 777 } 778 779 func (m *mutation) Location() x.Location { 780 return (*field)(m).Location() 781 } 782 783 func (m *mutation) ResponseName() string { 784 return (*field)(m).ResponseName() 785 } 786 787 // MutatedType returns the underlying type that gets mutated by m. 788 // 789 // It's not the same as the response type of m because mutations don't directly 790 // return what they mutate. Mutations return a payload package that includes 791 // the actual node mutated as a field. 792 func (m *mutation) MutatedType() Type { 793 // ATM there's a single field in the mutation payload. 794 return m.op.inSchema.mutatedType[m.Name()] 795 } 796 797 func (m *mutation) GetObjectName() string { 798 return m.field.ObjectDefinition.Name 799 } 800 801 func (m *mutation) MutationType() MutationType { 802 return mutationType(m.Name()) 803 } 804 805 func mutationType(name string) MutationType { 806 switch { 807 case strings.HasPrefix(name, "add"): 808 return AddMutation 809 case strings.HasPrefix(name, "update"): 810 return UpdateMutation 811 case strings.HasPrefix(name, "delete"): 812 return DeleteMutation 813 default: 814 return NotSupportedMutation 815 } 816 } 817 818 func (m *mutation) Operation() Operation { 819 return (*field)(m).Operation() 820 } 821 822 func (m *mutation) DgraphPredicate() string { 823 return (*field)(m).DgraphPredicate() 824 } 825 826 func (m *mutation) TypeName(dgraphTypes []interface{}) string { 827 return (*field)(m).TypeName(dgraphTypes) 828 } 829 830 func (m *mutation) IncludeInterfaceField(dgraphTypes []interface{}) bool { 831 return (*field)(m).IncludeInterfaceField(dgraphTypes) 832 } 833 834 func (t *astType) Field(name string) FieldDefinition { 835 typName := t.Name() 836 parentInt := parentInterface(t.inSchema, t.inSchema.Types[typName], name) 837 if parentInt != nil { 838 typName = parentInt.Name 839 } 840 841 return &fieldDefinition{ 842 // this ForName lookup is a loop in the underlying schema :-( 843 fieldDef: t.inSchema.Types[typName].Fields.ForName(name), 844 inSchema: t.inSchema, 845 dgraphPredicate: t.dgraphPredicate, 846 } 847 } 848 849 func (fd *fieldDefinition) Name() string { 850 return fd.fieldDef.Name 851 } 852 853 func (fd *fieldDefinition) IsID() bool { 854 return isID(fd.fieldDef) 855 } 856 857 func hasIDDirective(fd *ast.FieldDefinition) bool { 858 id := fd.Directives.ForName("id") 859 return id != nil 860 } 861 862 func isID(fd *ast.FieldDefinition) bool { 863 return fd.Type.Name() == "ID" 864 } 865 866 func (fd *fieldDefinition) Type() Type { 867 return &astType{ 868 typ: fd.fieldDef.Type, 869 inSchema: fd.inSchema, 870 dgraphPredicate: fd.dgraphPredicate, 871 } 872 } 873 874 func (fd *fieldDefinition) Inverse() (Type, FieldDefinition) { 875 876 invDirective := fd.fieldDef.Directives.ForName(inverseDirective) 877 if invDirective == nil { 878 return nil, nil 879 } 880 881 invFieldArg := invDirective.Arguments.ForName(inverseArg) 882 if invFieldArg == nil { 883 return nil, nil // really not possible 884 } 885 886 // typ must exist if the schema passed GQL validation 887 typ := fd.inSchema.Types[fd.Type().Name()] 888 889 // fld must exist if the schema passed our validation 890 fld := typ.Fields.ForName(invFieldArg.Value.Raw) 891 892 return fd.Type(), &fieldDefinition{fieldDef: fld, inSchema: fd.inSchema} 893 } 894 895 func (t *astType) Name() string { 896 if t.typ.NamedType == "" { 897 return t.typ.Elem.NamedType 898 } 899 return t.typ.NamedType 900 } 901 902 func (t *astType) DgraphName() string { 903 typeDef := t.inSchema.Types[t.typ.Name()] 904 name := typeName(typeDef) 905 if name != "" { 906 return name 907 } 908 return t.Name() 909 } 910 911 func (t *astType) Nullable() bool { 912 return !t.typ.NonNull 913 } 914 915 func (t *astType) ListType() Type { 916 if t.typ.Elem == nil { 917 return nil 918 } 919 return &astType{typ: t.typ.Elem} 920 } 921 922 // DgraphPredicate returns the name of the predicate in Dgraph that represents this 923 // type's field fld. Mostly this will be type_name.field_name,. 924 func (t *astType) DgraphPredicate(fld string) string { 925 return t.dgraphPredicate[t.Name()][fld] 926 } 927 928 func (t *astType) String() string { 929 if t == nil { 930 return "" 931 } 932 933 var sb strings.Builder 934 // give it enough space in case it happens to be `[t.Name()!]!` 935 sb.Grow(len(t.Name()) + 4) 936 937 if t.ListType() == nil { 938 x.Check2(sb.WriteString(t.Name())) 939 } else { 940 // There's no lists of lists, so this needn't be recursive 941 x.Check2(sb.WriteRune('[')) 942 x.Check2(sb.WriteString(t.Name())) 943 if !t.ListType().Nullable() { 944 x.Check2(sb.WriteRune('!')) 945 } 946 x.Check2(sb.WriteRune(']')) 947 } 948 949 if !t.Nullable() { 950 x.Check2(sb.WriteRune('!')) 951 } 952 953 return sb.String() 954 } 955 956 func (t *astType) IDField() FieldDefinition { 957 def := t.inSchema.Types[t.Name()] 958 if def.Kind != ast.Object && def.Kind != ast.Interface { 959 return nil 960 } 961 962 for _, fd := range def.Fields { 963 if isID(fd) { 964 return &fieldDefinition{ 965 fieldDef: fd, 966 inSchema: t.inSchema, 967 } 968 } 969 } 970 971 return nil 972 } 973 974 func (t *astType) XIDField() FieldDefinition { 975 def := t.inSchema.Types[t.Name()] 976 if def.Kind != ast.Object && def.Kind != ast.Interface { 977 return nil 978 } 979 980 for _, fd := range def.Fields { 981 if hasIDDirective(fd) { 982 return &fieldDefinition{ 983 fieldDef: fd, 984 inSchema: t.inSchema, 985 } 986 } 987 } 988 989 return nil 990 } 991 992 func (t *astType) Interfaces() []string { 993 interfaces := t.inSchema.Types[t.typ.Name()].Interfaces 994 if len(interfaces) == 0 { 995 return nil 996 } 997 998 // Look up the interface types in the schema and find their typeName which could have been 999 // overwritten using @dgraph(type: ...) 1000 names := make([]string, 0, len(interfaces)) 1001 for _, intr := range interfaces { 1002 i := t.inSchema.Types[intr] 1003 name := intr 1004 if n := typeName(i); n != "" { 1005 name = n 1006 } 1007 names = append(names, name) 1008 } 1009 return names 1010 } 1011 1012 // CheckNonNulls checks that any non nullables in t are present in obj. 1013 // Fields of type ID are not checked, nor is any exclusion. 1014 // 1015 // For our reference types for adding/linking objects, we'd like to have something like 1016 // 1017 // input PostRef { 1018 // id: ID! 1019 // } 1020 // 1021 // input PostNew { 1022 // title: String! 1023 // text: String 1024 // author: AuthorRef! 1025 // } 1026 // 1027 // and then have something like this 1028 // 1029 // input PostNewOrReference = PostRef | PostNew 1030 // 1031 // input AuthorNew { 1032 // ... 1033 // posts: [PostNewOrReference] 1034 // } 1035 // 1036 // but GraphQL doesn't allow union types in input, so best we can do is 1037 // 1038 // input PostRef { 1039 // id: ID 1040 // title: String 1041 // text: String 1042 // author: AuthorRef 1043 // } 1044 // 1045 // and then check ourselves that either there's an ID, or there's all the bits to 1046 // satisfy a valid post. 1047 func (t *astType) EnsureNonNulls(obj map[string]interface{}, exclusion string) error { 1048 for _, fld := range t.inSchema.Types[t.Name()].Fields { 1049 if fld.Type.NonNull && !isID(fld) && !(fld.Name == exclusion) { 1050 if val, ok := obj[fld.Name]; !ok || val == nil { 1051 return errors.Errorf( 1052 "type %s requires a value for field %s, but no value present", 1053 t.Name(), fld.Name) 1054 } 1055 } 1056 } 1057 return nil 1058 }