github.com/ndau/noms@v1.0.5/go/ngql/types.go (about) 1 // Copyright 2017 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package ngql 6 7 import ( 8 "context" 9 "errors" 10 "fmt" 11 12 "strings" 13 14 "github.com/attic-labs/graphql" 15 "github.com/ndau/noms/go/d" 16 "github.com/ndau/noms/go/types" 17 ) 18 19 // TypeConverter provides functions to convert between Noms types and GraphQL 20 // types. 21 type TypeConverter struct { 22 tm TypeMap 23 NameFunc NameFunc 24 } 25 26 // NewTypeConverter creates a new TypeConverter. 27 func NewTypeConverter() *TypeConverter { 28 return &TypeConverter{ 29 TypeMap{}, 30 DefaultNameFunc, 31 } 32 } 33 34 // NameFunc defines how to compute the GraphQL name for a Noms type. 35 type NameFunc func(nomsType *types.Type, isInputType bool) string 36 37 func (tc *TypeConverter) getTypeName(nomsType *types.Type) string { 38 return tc.NameFunc(nomsType, false) 39 } 40 41 func (tc *TypeConverter) getInputTypeName(nomsType *types.Type) string { 42 return tc.NameFunc(nomsType, true) 43 } 44 45 // NomsTypeToGraphQLType creates a GraphQL type from a Noms type that knows how 46 // to resolve the Noms values. 47 func (tc *TypeConverter) NomsTypeToGraphQLType(nomsType *types.Type) graphql.Type { 48 return tc.nomsTypeToGraphQLType(nomsType, false) 49 } 50 51 // NomsTypeToGraphQLInputType creates a GraphQL input type from a Noms type. 52 // Input types may not be unions or cyclic structs. If we encounter those 53 // this returns an error. 54 func (tc *TypeConverter) NomsTypeToGraphQLInputType(nomsType *types.Type) (graphql.Input, error) { 55 return tc.nomsTypeToGraphQLInputType(nomsType) 56 } 57 58 // TypeMap is used as a cache in NomsTypeToGraphQLType and 59 // NomsTypeToGraphQLInputType. 60 type TypeMap map[typeMapKey]graphql.Type 61 62 type typeMapKey struct { 63 name string 64 boxedIfScalar bool 65 } 66 67 // NewTypeMap creates a new map that is used as a cache in 68 // NomsTypeToGraphQLType and NomsTypeToGraphQLInputType. 69 func NewTypeMap() *TypeMap { 70 return &TypeMap{} 71 } 72 73 // GraphQL has two type systems. 74 // - One for output types which is used with resolvers to produce an output set. 75 // - And another one for input types. Input types are used to verify that the 76 // JSON like data passes as arguments are of the right type. 77 // There is some overlap here. Scalars are the same and List can be used in 78 // both. 79 // The significant difference is graphql.Object (output) vs graphql.InputObject 80 // Input types cannot be unions and input object types cannot contain cycles. 81 82 type graphQLTypeMode uint8 83 84 const ( 85 inputMode graphQLTypeMode = iota 86 outputMode 87 ) 88 89 // In terms of resolving a graph of data, there are three types of value: 90 // scalars, lists and maps. During resolution, we are converting some noms 91 // value to a graphql value. A getFieldFn will be invoked for a matching noms 92 // type. Its job is to retrieve the sub-value from the noms type which is 93 // mapped to a graphql map as a fieldname. 94 type getFieldFn func(v interface{}, fieldName string, ctx context.Context) types.Value 95 96 // When a field name is resolved, it may take key:value arguments. A 97 // getSubvaluesFn handles returning one or more *noms* values whose presence is 98 // indicated by the provided arguments. 99 type getSubvaluesFn func(vrw types.ValueReadWriter, v types.Value, args map[string]interface{}) interface{} 100 101 // GraphQL requires all memberTypes in a Union to be Structs, so when a noms 102 // union contains a scalar, we represent it in that context as a "boxed" value. 103 // E.g. 104 // Boolean! => 105 // type BooleanValue { 106 // scalarValue: Boolean! 107 // } 108 func (tc *TypeConverter) scalarToValue(nomsType *types.Type, scalarType graphql.Type) graphql.Type { 109 return graphql.NewObject(graphql.ObjectConfig{ 110 Name: fmt.Sprintf("%sValue", tc.getTypeName(nomsType)), 111 Fields: graphql.Fields{ 112 scalarValue: &graphql.Field{ 113 Type: graphql.NewNonNull(scalarType), 114 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 115 return p.Source, nil // p.Source is already a go-native scalar type 116 }, 117 }, 118 }}) 119 } 120 121 func isScalar(nomsType *types.Type) bool { 122 switch nomsType { 123 case types.BoolType, types.NumberType, types.StringType: 124 return true 125 default: 126 return false 127 } 128 } 129 130 // NomsTypeToGraphQLType creates a GraphQL type from a Noms type that knows how 131 // to resolve the Noms values. 132 func NomsTypeToGraphQLType(nomsType *types.Type, boxedIfScalar bool, tm *TypeMap) graphql.Type { 133 tc := TypeConverter{*tm, DefaultNameFunc} 134 return tc.nomsTypeToGraphQLType(nomsType, boxedIfScalar) 135 } 136 137 func (tc *TypeConverter) nomsTypeToGraphQLType(nomsType *types.Type, boxedIfScalar bool) graphql.Type { 138 name := tc.getTypeName(nomsType) 139 key := typeMapKey{name, boxedIfScalar && isScalar(nomsType)} 140 gqlType, ok := tc.tm[key] 141 if ok { 142 return gqlType 143 } 144 145 // The graphql package has built in support for recursive types using 146 // FieldsThunk which allows the inner type to refer to an outer type by 147 // lazily initializing the fields. 148 switch nomsType.TargetKind() { 149 case types.NumberKind: 150 gqlType = graphql.Float 151 if boxedIfScalar { 152 gqlType = tc.scalarToValue(nomsType, gqlType) 153 } 154 155 case types.StringKind: 156 gqlType = graphql.String 157 if boxedIfScalar { 158 gqlType = tc.scalarToValue(nomsType, gqlType) 159 } 160 161 case types.BoolKind: 162 gqlType = graphql.Boolean 163 if boxedIfScalar { 164 gqlType = tc.scalarToValue(nomsType, gqlType) 165 } 166 167 case types.StructKind: 168 gqlType = tc.structToGQLObject(nomsType) 169 170 case types.ListKind, types.SetKind: 171 gqlType = tc.listAndSetToGraphQLObject(nomsType) 172 173 case types.MapKind: 174 gqlType = tc.mapToGraphQLObject(nomsType) 175 176 case types.RefKind: 177 gqlType = tc.refToGraphQLObject(nomsType) 178 179 case types.UnionKind: 180 gqlType = tc.unionToGQLUnion(nomsType) 181 182 case types.BlobKind, types.ValueKind, types.TypeKind: 183 // TODO: https://github.com/ndau/noms/issues/3155 184 gqlType = graphql.String 185 186 case types.CycleKind: 187 panic("not reached") // we should never attempt to create a schema for any unresolved cycle 188 189 default: 190 panic("not reached") 191 } 192 193 tc.tm[key] = gqlType 194 return gqlType 195 } 196 197 // NomsTypeToGraphQLInputType creates a GraphQL input type from a Noms type. 198 // Input types may not be unions or cyclic structs. If we encounter those 199 // this returns an error. 200 func NomsTypeToGraphQLInputType(nomsType *types.Type, tm *TypeMap) (graphql.Input, error) { 201 tc := TypeConverter{*tm, DefaultNameFunc} 202 return tc.nomsTypeToGraphQLInputType(nomsType) 203 } 204 205 func (tc *TypeConverter) nomsTypeToGraphQLInputType(nomsType *types.Type) (graphql.Input, error) { 206 // GraphQL input types do not support cycles. 207 if types.HasStructCycles(nomsType) { 208 return nil, errors.New("GraphQL input type cannot contain cycles") 209 } 210 211 name := tc.getInputTypeName(nomsType) 212 key := typeMapKey{name, false} 213 gqlType, ok := tc.tm[key] 214 if ok { 215 return gqlType, nil 216 } 217 218 var err error 219 switch nomsType.TargetKind() { 220 case types.NumberKind: 221 gqlType = graphql.Float 222 223 case types.StringKind: 224 gqlType = graphql.String 225 226 case types.BoolKind: 227 gqlType = graphql.Boolean 228 229 case types.StructKind: 230 gqlType, err = tc.structToGQLInputObject(nomsType) 231 232 case types.ListKind, types.SetKind: 233 gqlType, err = tc.listAndSetToGraphQLInputObject(nomsType) 234 235 case types.MapKind: 236 gqlType, err = tc.mapToGraphQLInputObject(nomsType) 237 238 case types.RefKind: 239 gqlType = graphql.String 240 241 case types.UnionKind: 242 return nil, errors.New("GraphQL input type cannot contain unions") 243 244 case types.BlobKind, types.ValueKind, types.TypeKind: 245 // TODO: https://github.com/ndau/noms/issues/3155 246 gqlType = graphql.String 247 248 case types.CycleKind: 249 panic("not reachable") // This is handled at the top of nomsTypeToGraphQLInputType 250 251 default: 252 panic("not reached") 253 } 254 255 if err != nil { 256 return nil, err 257 } 258 259 tc.tm[key] = gqlType 260 return gqlType, nil 261 } 262 263 func isEmptyNomsUnion(nomsType *types.Type) bool { 264 return nomsType.TargetKind() == types.UnionKind && len(nomsType.Desc.(types.CompoundDesc).ElemTypes) == 0 265 } 266 267 // Creates a union of structs type. 268 func (tc *TypeConverter) unionToGQLUnion(nomsType *types.Type) *graphql.Union { 269 nomsMemberTypes := nomsType.Desc.(types.CompoundDesc).ElemTypes 270 memberTypes := make([]*graphql.Object, len(nomsMemberTypes)) 271 272 for i, nomsUnionType := range nomsMemberTypes { 273 // Member types cannot be non-null and must be struct (graphl.Object) 274 memberTypes[i] = tc.nomsTypeToGraphQLType(nomsUnionType, true).(*graphql.Object) 275 } 276 277 return graphql.NewUnion(graphql.UnionConfig{ 278 Name: tc.getTypeName(nomsType), 279 Types: memberTypes, 280 ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object { 281 if v, ok := p.Value.(types.Value); ok { 282 // We cannot just get the type of the value here. GraphQL requires 283 // us to return one of the types in memberTypes. 284 for i, t := range nomsMemberTypes { 285 if types.IsValueSubtypeOf(v, t) { 286 return memberTypes[i] 287 } 288 } 289 return nil 290 } 291 292 var nomsType *types.Type 293 switch p.Value.(type) { 294 case float64: 295 nomsType = types.NumberType 296 case string: 297 nomsType = types.StringType 298 case bool: 299 nomsType = types.BoolType 300 } 301 return tc.nomsTypeToGraphQLType(nomsType, true).(*graphql.Object) 302 }, 303 }) 304 } 305 306 func (tc *TypeConverter) structToGQLObject(nomsType *types.Type) *graphql.Object { 307 return graphql.NewObject(graphql.ObjectConfig{ 308 Name: tc.getTypeName(nomsType), 309 Fields: graphql.FieldsThunk(func() graphql.Fields { 310 structDesc := nomsType.Desc.(types.StructDesc) 311 fields := graphql.Fields{ 312 "hash": &graphql.Field{ 313 Type: graphql.NewNonNull(graphql.String), 314 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 315 return p.Source.(types.Struct).Hash().String(), nil 316 }, 317 }, 318 } 319 320 structDesc.IterFields(func(name string, nomsFieldType *types.Type, optional bool) { 321 fieldType := tc.nomsTypeToGraphQLType(nomsFieldType, false) 322 if !optional { 323 fieldType = graphql.NewNonNull(fieldType) 324 } 325 326 fields[name] = &graphql.Field{ 327 Type: fieldType, 328 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 329 if field, ok := p.Source.(types.Struct).MaybeGet(name); ok { 330 return MaybeGetScalar(field), nil 331 } 332 return nil, nil 333 }, 334 } 335 }) 336 337 return fields 338 }), 339 }) 340 } 341 342 func (tc *TypeConverter) listAndSetToGraphQLInputObject(nomsType *types.Type) (graphql.Input, error) { 343 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 344 elemType, err := tc.nomsTypeToGraphQLInputType(nomsValueType) 345 if err != nil { 346 return nil, err 347 } 348 return graphql.NewList(graphql.NewNonNull(elemType)), nil 349 } 350 351 func (tc *TypeConverter) mapToGraphQLInputObject(nomsType *types.Type) (graphql.Input, error) { 352 nomsKeyType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 353 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[1] 354 355 keyType, err := tc.nomsTypeToGraphQLInputType(nomsKeyType) 356 if err != nil { 357 return nil, err 358 } 359 valueType, err := tc.nomsTypeToGraphQLInputType(nomsValueType) 360 if err != nil { 361 return nil, err 362 } 363 364 entryType := tc.mapEntryToGraphQLInputObject(keyType, valueType, nomsKeyType, nomsValueType) 365 return graphql.NewList(entryType), nil 366 } 367 368 func (tc *TypeConverter) structToGQLInputObject(nomsType *types.Type) (graphql.Input, error) { 369 var err error 370 rv := graphql.NewInputObject(graphql.InputObjectConfig{ 371 Name: tc.getInputTypeName(nomsType), 372 Fields: graphql.InputObjectConfigFieldMapThunk(func() graphql.InputObjectConfigFieldMap { 373 structDesc := nomsType.Desc.(types.StructDesc) 374 fields := make(graphql.InputObjectConfigFieldMap, structDesc.Len()) 375 376 structDesc.IterFields(func(name string, nomsFieldType *types.Type, optional bool) { 377 if err != nil { 378 return 379 } 380 var fieldType graphql.Input 381 fieldType, err = tc.nomsTypeToGraphQLInputType(nomsFieldType) 382 if err != nil { 383 return 384 } 385 if !optional { 386 fieldType = graphql.NewNonNull(fieldType) 387 } 388 fields[name] = &graphql.InputObjectFieldConfig{ 389 Type: fieldType, 390 } 391 }) 392 393 return fields 394 }), 395 }) 396 if err != nil { 397 return nil, err 398 } 399 return rv, nil 400 } 401 402 var listArgs = graphql.FieldConfigArgument{ 403 atKey: &graphql.ArgumentConfig{Type: graphql.Int}, 404 countKey: &graphql.ArgumentConfig{Type: graphql.Int}, 405 } 406 407 func getListElements(vrw types.ValueReadWriter, v types.Value, args map[string]interface{}) interface{} { 408 l := v.(types.Collection) 409 idx := 0 410 count := int(l.Len()) 411 end := count 412 413 if at, ok := args[atKey].(int); ok { 414 idx = at 415 } 416 417 if c, ok := args[countKey].(int); ok { 418 count = c 419 } 420 421 // Clamp ranges 422 if count <= 0 || idx >= end { 423 return ([]interface{})(nil) 424 } 425 if idx < 0 { 426 idx = 0 427 } 428 if idx+count > end { 429 count = end - idx 430 } 431 432 values := make([]interface{}, count) 433 434 cols, offset := types.LoadLeafNodes([]types.Collection{l}, uint64(idx), uint64(idx+count)) 435 436 // Iterate the collections we got, skipping the first offset elements and bailing out 437 // once we've filled values with count elements. 438 elementsSeen := uint64(0) 439 maybeAddElement := func(v types.Value) { 440 if elementsSeen >= offset && elementsSeen-offset < uint64(count) { 441 values[elementsSeen-offset] = MaybeGetScalar(v) 442 } 443 elementsSeen++ 444 } 445 // TODO: Use a cursor so we do not have to instantiate all values. @arv has a 446 // change in the works that only creates Values as needed. 447 for _, c := range cols { 448 v := c.(types.Value) 449 v.WalkValues(maybeAddElement) 450 if elementsSeen-offset >= uint64(count) { 451 break 452 } 453 } 454 455 return values 456 } 457 458 func getSetElements(vrw types.ValueReadWriter, v types.Value, args map[string]interface{}) interface{} { 459 s := v.(types.Set) 460 461 iter, nomsKey, nomsThrough, count, singleExactMatch := getCollectionArgs(vrw, s, args, iteratorFactory{ 462 IteratorFrom: func(from types.Value) interface{} { 463 return s.IteratorFrom(from) 464 }, 465 IteratorAt: func(at uint64) interface{} { 466 return s.IteratorAt(at) 467 }, 468 First: func() interface{} { 469 return &setFirstIterator{s: s} 470 }, 471 }) 472 473 if count == 0 { 474 return ([]interface{})(nil) 475 } 476 477 setIter := iter.(types.SetIterator) 478 values := make([]interface{}, 0, count) 479 for i := uint64(0); i < count; i++ { 480 v := setIter.Next() 481 if v == nil { 482 break 483 } 484 if singleExactMatch { 485 if nomsKey.Equals(v) { 486 values = append(values, MaybeGetScalar(v)) 487 } 488 break 489 } 490 491 if nomsThrough != nil { 492 if !nomsThrough.Less(v) { 493 values = append(values, MaybeGetScalar(v)) 494 } else { 495 break 496 } 497 } else { 498 values = append(values, MaybeGetScalar(v)) 499 } 500 } 501 502 return values 503 } 504 505 func getCollectionArgs(vrw types.ValueReadWriter, col types.Collection, args map[string]interface{}, factory iteratorFactory) (iter interface{}, nomsKey, nomsThrough types.Value, count uint64, singleExactMatch bool) { 506 typ := types.TypeOf(col) 507 length := col.Len() 508 nomsKeyType := typ.Desc.(types.CompoundDesc).ElemTypes[0] 509 510 if keys, ok := args[keysKey]; ok { 511 slice := keys.([]interface{}) 512 nomsKeys := make(types.ValueSlice, len(slice)) 513 for i, v := range slice { 514 var nomsValue types.Value 515 nomsValue = InputToNomsValue(vrw, v, nomsKeyType) 516 nomsKeys[i] = nomsValue 517 } 518 count = uint64(len(slice)) 519 iter = &mapIteratorForKeys{ 520 m: col.(types.Map), 521 keys: nomsKeys, 522 } 523 return 524 } 525 526 nomsThrough = getThroughArg(vrw, nomsKeyType, args) 527 528 count, singleExactMatch = getCountArg(length, args) 529 530 if key, ok := args[keyKey]; ok { 531 nomsKey = InputToNomsValue(vrw, key, nomsKeyType) 532 iter = factory.IteratorFrom(nomsKey) 533 } else if at, ok := args[atKey]; ok { 534 idx := at.(int) 535 if idx < 0 { 536 idx = 0 537 } else if uint64(idx) > length { 538 count = 0 539 return 540 } 541 iter = factory.IteratorAt(uint64(idx)) 542 } else if count == 1 && !singleExactMatch { 543 // no key, no at, no through, but a count:1 544 iter = factory.First() 545 } else { 546 iter = factory.IteratorAt(0) 547 } 548 549 return 550 } 551 552 type mapAppender func(slice []interface{}, k, v types.Value) []interface{} 553 554 type mapiter interface { 555 Valid() bool 556 Entry() (k, v types.Value) 557 Next() bool 558 } 559 560 func getMapElements(vrw types.ValueReadWriter, v types.Value, args map[string]interface{}, app mapAppender) (interface{}, error) { 561 m := v.(types.Map) 562 563 iter, nomsKey, nomsThrough, count, singleExactMatch := getCollectionArgs(vrw, m, args, iteratorFactory{ 564 IteratorFrom: func(from types.Value) interface{} { 565 return m.IteratorFrom(from) 566 }, 567 IteratorAt: func(at uint64) interface{} { 568 return m.IteratorAt(at) 569 }, 570 First: func() interface{} { 571 return &mapFirstIterator{m: &m} 572 }, 573 }) 574 575 if count == 0 { 576 return ([]interface{})(nil), nil 577 } 578 579 mapIter := iter.(mapiter) 580 values := make([]interface{}, 0, count) 581 for i := uint64(0); i < count; i++ { 582 if !mapIter.Valid() { 583 break 584 } 585 586 k, v := mapIter.Entry() 587 588 if singleExactMatch { 589 if nomsKey.Equals(k) { 590 values = app(values, k, v) 591 } 592 break 593 } 594 595 if nomsThrough != nil { 596 if !nomsThrough.Less(k) { 597 values = app(values, k, v) 598 } else { 599 break 600 } 601 } else { 602 values = app(values, k, v) 603 } 604 605 mapIter.Next() 606 } 607 608 return values, nil 609 } 610 611 func getCountArg(count uint64, args map[string]interface{}) (c uint64, singleExactMatch bool) { 612 if c, ok := args[countKey]; ok { 613 c := c.(int) 614 if c <= 0 { 615 return 0, false 616 } 617 return uint64(c), false 618 } 619 // If we have key and no count/through we use count 1 620 _, hasKey := args[keyKey] 621 _, hasThrough := args[throughKey] 622 if hasKey && !hasThrough { 623 return uint64(1), true 624 } 625 626 return count, false 627 } 628 629 func getThroughArg(vrw types.ValueReadWriter, nomsKeyType *types.Type, args map[string]interface{}) types.Value { 630 if through, ok := args[throughKey]; ok { 631 return InputToNomsValue(vrw, through, nomsKeyType) 632 } 633 return nil 634 } 635 636 type iteratorFactory struct { 637 IteratorFrom func(from types.Value) interface{} 638 IteratorAt func(at uint64) interface{} 639 First func() interface{} 640 } 641 642 type mapEntry struct { 643 key, value types.Value 644 } 645 646 // Map data must be returned as a list of key-value pairs. Each unique keyType:valueType is 647 // represented as a graphql 648 // 649 // type <KeyTypeName><ValueTypeName>Entry { 650 // key: <KeyType>! 651 // value: <ValueType>! 652 // } 653 func (tc *TypeConverter) mapEntryToGraphQLObject(keyType, valueType graphql.Type, nomsKeyType, nomsValueType *types.Type) graphql.Type { 654 return graphql.NewNonNull(graphql.NewObject(graphql.ObjectConfig{ 655 Name: fmt.Sprintf("%s%sEntry", tc.getTypeName(nomsKeyType), tc.getTypeName(nomsValueType)), 656 Fields: graphql.FieldsThunk(func() graphql.Fields { 657 return graphql.Fields{ 658 keyKey: &graphql.Field{ 659 Type: keyType, 660 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 661 entry := p.Source.(mapEntry) 662 return MaybeGetScalar(entry.key), nil 663 }, 664 }, 665 valueKey: &graphql.Field{ 666 Type: valueType, 667 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 668 entry := p.Source.(mapEntry) 669 return MaybeGetScalar(entry.value), nil 670 }, 671 }, 672 } 673 }), 674 })) 675 } 676 677 func (tc *TypeConverter) mapEntryToGraphQLInputObject(keyType, valueType graphql.Input, nomsKeyType, nomsValueType *types.Type) graphql.Input { 678 return graphql.NewNonNull(graphql.NewInputObject(graphql.InputObjectConfig{ 679 Name: fmt.Sprintf("%s%sEntryInput", tc.getInputTypeName(nomsKeyType), tc.getInputTypeName(nomsValueType)), 680 Fields: graphql.InputObjectConfigFieldMapThunk(func() graphql.InputObjectConfigFieldMap { 681 return graphql.InputObjectConfigFieldMap{ 682 keyKey: &graphql.InputObjectFieldConfig{ 683 Type: graphql.NewNonNull(keyType), 684 }, 685 valueKey: &graphql.InputObjectFieldConfig{ 686 Type: graphql.NewNonNull(valueType), 687 }, 688 } 689 }), 690 })) 691 } 692 693 // DefaultNameFunc returns the GraphQL type name for a Noms type. 694 func DefaultNameFunc(nomsType *types.Type, isInputType bool) string { 695 if isInputType { 696 return GetInputTypeName(nomsType) 697 } 698 return GetTypeName(nomsType) 699 } 700 701 // GetTypeName provides a unique type name that is used by GraphQL. 702 func GetTypeName(nomsType *types.Type) string { 703 return getTypeName(nomsType, "") 704 } 705 706 // GetInputTypeName returns a type name that is unique and useful for GraphQL 707 // input types. 708 func GetInputTypeName(nomsType *types.Type) string { 709 return getTypeName(nomsType, "Input") 710 } 711 712 func getTypeName(nomsType *types.Type, suffix string) string { 713 switch nomsType.TargetKind() { 714 case types.BoolKind: 715 return "Boolean" 716 717 case types.NumberKind: 718 return "Number" 719 720 case types.StringKind: 721 return "String" 722 723 case types.BlobKind: 724 return "Blob" 725 726 case types.ValueKind: 727 return "Value" 728 729 case types.ListKind: 730 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 731 if isEmptyNomsUnion(nomsValueType) { 732 return "EmptyList" 733 } 734 return fmt.Sprintf("%sList%s", GetTypeName(nomsValueType), suffix) 735 736 case types.MapKind: 737 nomsKeyType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 738 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[1] 739 if isEmptyNomsUnion(nomsKeyType) { 740 d.Chk.True(isEmptyNomsUnion(nomsValueType)) 741 return "EmptyMap" 742 } 743 744 return fmt.Sprintf("%sTo%sMap%s", GetTypeName(nomsKeyType), GetTypeName(nomsValueType), suffix) 745 746 case types.RefKind: 747 return fmt.Sprintf("%sRef%s", GetTypeName(nomsType.Desc.(types.CompoundDesc).ElemTypes[0]), suffix) 748 749 case types.SetKind: 750 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 751 if isEmptyNomsUnion(nomsValueType) { 752 return "EmptySet" 753 } 754 755 return fmt.Sprintf("%sSet%s", GetTypeName(nomsValueType), suffix) 756 757 case types.StructKind: 758 // GraphQL Name cannot start with a number. 759 // GraphQL type names must be globally unique. 760 return fmt.Sprintf("%s%s_%s", nomsType.Desc.(types.StructDesc).Name, suffix, nomsType.Hash().String()[:6]) 761 762 case types.TypeKind: 763 // GraphQL Name cannot start with a number. 764 // TODO: https://github.com/ndau/noms/issues/3155 765 return fmt.Sprintf("Type%s_%s", suffix, nomsType.Hash().String()[:6]) 766 767 case types.UnionKind: 768 unionMemberTypes := nomsType.Desc.(types.CompoundDesc).ElemTypes 769 names := make([]string, len(unionMemberTypes)) 770 for i, unionMemberType := range unionMemberTypes { 771 names[i] = GetTypeName(unionMemberType) 772 } 773 return strings.Join(names, "Or") + suffix 774 775 case types.CycleKind: 776 return "Cycle" 777 778 default: 779 panic(fmt.Sprintf("(GetTypeName) not reached: %s", nomsType.Describe())) 780 } 781 } 782 783 func argsWithSize() graphql.Fields { 784 return graphql.Fields{ 785 sizeKey: &graphql.Field{ 786 Type: graphql.Float, 787 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 788 c := p.Source.(types.Collection) 789 return MaybeGetScalar(types.Number(c.Len())), nil 790 }, 791 }, 792 } 793 } 794 795 func (tc *TypeConverter) listAndSetToGraphQLObject(nomsType *types.Type) *graphql.Object { 796 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 797 var listType, valueType graphql.Type 798 var keyInputType graphql.Input 799 var keyInputError error 800 if !isEmptyNomsUnion(nomsValueType) { 801 valueType = tc.nomsTypeToGraphQLType(nomsValueType, false) 802 keyInputType, keyInputError = tc.nomsTypeToGraphQLInputType(nomsValueType) 803 listType = graphql.NewNonNull(valueType) 804 } 805 806 return graphql.NewObject(graphql.ObjectConfig{ 807 Name: tc.getTypeName(nomsType), 808 Fields: graphql.FieldsThunk(func() graphql.Fields { 809 fields := argsWithSize() 810 811 if listType != nil { 812 var args graphql.FieldConfigArgument 813 var getSubvalues getSubvaluesFn 814 815 switch nomsType.TargetKind() { 816 case types.ListKind: 817 args = listArgs 818 getSubvalues = getListElements 819 820 case types.SetKind: 821 args = graphql.FieldConfigArgument{ 822 atKey: &graphql.ArgumentConfig{Type: graphql.Int}, 823 countKey: &graphql.ArgumentConfig{Type: graphql.Int}, 824 } 825 if keyInputError == nil { 826 args[keyKey] = &graphql.ArgumentConfig{Type: keyInputType} 827 args[throughKey] = &graphql.ArgumentConfig{Type: keyInputType} 828 } 829 getSubvalues = getSetElements 830 } 831 valuesField := &graphql.Field{ 832 Type: graphql.NewList(listType), 833 Args: args, 834 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 835 c := p.Source.(types.Collection) 836 vrw := p.Context.Value(vrwKey).(types.ValueReadWriter) 837 return getSubvalues(vrw, c, p.Args), nil 838 }, 839 } 840 fields[valuesKey] = valuesField 841 fields[elementsKey] = valuesField 842 } 843 844 return fields 845 }), 846 }) 847 } 848 849 func (tc *TypeConverter) mapToGraphQLObject(nomsType *types.Type) *graphql.Object { 850 return graphql.NewObject(graphql.ObjectConfig{ 851 Name: tc.getTypeName(nomsType), 852 Fields: graphql.FieldsThunk(func() graphql.Fields { 853 nomsKeyType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 854 nomsValueType := nomsType.Desc.(types.CompoundDesc).ElemTypes[1] 855 isEmptyMap := isEmptyNomsUnion(nomsKeyType) || isEmptyNomsUnion(nomsValueType) 856 857 fields := argsWithSize() 858 859 if !isEmptyMap { 860 keyType := tc.nomsTypeToGraphQLType(nomsKeyType, false) 861 keyInputType, keyInputError := tc.nomsTypeToGraphQLInputType(nomsKeyType) 862 valueType := tc.nomsTypeToGraphQLType(nomsValueType, false) 863 entryType := tc.mapEntryToGraphQLObject(graphql.NewNonNull(keyType), valueType, nomsKeyType, nomsValueType) 864 865 args := graphql.FieldConfigArgument{ 866 atKey: &graphql.ArgumentConfig{Type: graphql.Int}, 867 countKey: &graphql.ArgumentConfig{Type: graphql.Int}, 868 } 869 if keyInputError == nil { 870 args[keyKey] = &graphql.ArgumentConfig{Type: keyInputType} 871 args[keysKey] = &graphql.ArgumentConfig{Type: graphql.NewList(graphql.NewNonNull(keyInputType))} 872 args[throughKey] = &graphql.ArgumentConfig{Type: keyInputType} 873 } 874 875 entriesField := &graphql.Field{ 876 Type: graphql.NewList(entryType), 877 Args: args, 878 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 879 c := p.Source.(types.Collection) 880 vrw := p.Context.Value(vrwKey).(types.ValueReadWriter) 881 return getMapElements(vrw, c, p.Args, mapAppendEntry) 882 }, 883 } 884 fields[entriesKey] = entriesField 885 fields[elementsKey] = entriesField 886 887 fields[keysKey] = &graphql.Field{ 888 Type: graphql.NewList(keyType), 889 Args: args, 890 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 891 c := p.Source.(types.Collection) 892 vrw := p.Context.Value(vrwKey).(types.ValueReadWriter) 893 return getMapElements(vrw, c, p.Args, mapAppendKey) 894 }, 895 } 896 fields[valuesKey] = &graphql.Field{ 897 Type: graphql.NewList(valueType), 898 Args: args, 899 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 900 c := p.Source.(types.Collection) 901 vrw := p.Context.Value(vrwKey).(types.ValueReadWriter) 902 return getMapElements(vrw, c, p.Args, mapAppendValue) 903 }, 904 } 905 } 906 907 return fields 908 }), 909 }) 910 } 911 912 func mapAppendKey(slice []interface{}, k, v types.Value) []interface{} { 913 return append(slice, MaybeGetScalar(k)) 914 } 915 916 func mapAppendValue(slice []interface{}, k, v types.Value) []interface{} { 917 return append(slice, MaybeGetScalar(v)) 918 } 919 920 func mapAppendEntry(slice []interface{}, k, v types.Value) []interface{} { 921 return append(slice, mapEntry{k, v}) 922 } 923 924 // Refs are represented as structs: 925 // 926 // type <ValueTypeName>Entry { 927 // targetHash: String! 928 // targetValue: <ValueType>! 929 // } 930 func (tc *TypeConverter) refToGraphQLObject(nomsType *types.Type) *graphql.Object { 931 return graphql.NewObject(graphql.ObjectConfig{ 932 Name: tc.getTypeName(nomsType), 933 Fields: graphql.FieldsThunk(func() graphql.Fields { 934 nomsTargetType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 935 targetType := tc.nomsTypeToGraphQLType(nomsTargetType, false) 936 937 return graphql.Fields{ 938 targetHashKey: &graphql.Field{ 939 Type: graphql.NewNonNull(graphql.String), 940 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 941 r := p.Source.(types.Ref) 942 return MaybeGetScalar(types.String(r.TargetHash().String())), nil 943 }, 944 }, 945 946 targetValueKey: &graphql.Field{ 947 Type: targetType, 948 Resolve: func(p graphql.ResolveParams) (interface{}, error) { 949 r := p.Source.(types.Ref) 950 return MaybeGetScalar(r.TargetValue(p.Context.Value(vrwKey).(types.ValueReader))), nil 951 }, 952 }, 953 } 954 }), 955 }) 956 } 957 958 func MaybeGetScalar(v types.Value) interface{} { 959 switch v.(type) { 960 case types.Bool: 961 return bool(v.(types.Bool)) 962 case types.Number: 963 return float64(v.(types.Number)) 964 case types.String: 965 return string(v.(types.String)) 966 case *types.Type, types.Blob: 967 // TODO: https://github.com/ndau/noms/issues/3155 968 return v.Hash() 969 } 970 971 return v 972 } 973 974 // InputToNomsValue converts a GraphQL input value (as used in arguments and 975 // variables) to a Noms value. 976 func InputToNomsValue(vrw types.ValueReadWriter, arg interface{}, nomsType *types.Type) types.Value { 977 switch nomsType.TargetKind() { 978 case types.BoolKind: 979 return types.Bool(arg.(bool)) 980 case types.NumberKind: 981 if i, ok := arg.(int); ok { 982 return types.Number(i) 983 } 984 return types.Number(arg.(float64)) 985 case types.StringKind: 986 return types.String(arg.(string)) 987 case types.ListKind, types.SetKind: 988 elemType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 989 sl := arg.([]interface{}) 990 vs := make(types.ValueSlice, len(sl)) 991 for i, v := range sl { 992 vs[i] = InputToNomsValue(vrw, v, elemType) 993 } 994 if nomsType.TargetKind() == types.ListKind { 995 return types.NewList(vrw, vs...) 996 } 997 return types.NewSet(vrw, vs...) 998 case types.MapKind: 999 // Maps are passed as [{key: K, value: V}, ...] 1000 keyType := nomsType.Desc.(types.CompoundDesc).ElemTypes[0] 1001 valType := nomsType.Desc.(types.CompoundDesc).ElemTypes[1] 1002 sl := arg.([]interface{}) 1003 kvs := make(types.ValueSlice, 2*len(sl)) 1004 for i, v := range sl { 1005 v := v.(map[string]interface{}) 1006 kvs[2*i] = InputToNomsValue(vrw, v["key"], keyType) 1007 kvs[2*i+1] = InputToNomsValue(vrw, v["value"], valType) 1008 } 1009 return types.NewMap(vrw, kvs...) 1010 case types.StructKind: 1011 desc := nomsType.Desc.(types.StructDesc) 1012 data := make(types.StructData, desc.Len()) 1013 m := arg.(map[string]interface{}) 1014 desc.IterFields(func(name string, t *types.Type, optional bool) { 1015 if m[name] != nil || !optional { 1016 data[name] = InputToNomsValue(vrw, m[name], t) 1017 } 1018 }) 1019 return types.NewStruct(desc.Name, data) 1020 } 1021 panic("not yet implemented") 1022 } 1023 1024 type mapIteratorForKeys struct { 1025 m types.Map 1026 keys types.ValueSlice 1027 idx int 1028 } 1029 1030 func (it *mapIteratorForKeys) Valid() bool { 1031 return it.idx < len(it.keys) 1032 } 1033 1034 func (it *mapIteratorForKeys) Entry() (k, v types.Value) { 1035 if it.idx >= len(it.keys) { 1036 return 1037 } 1038 k = it.keys[it.idx] 1039 v = it.m.Get(k) 1040 return 1041 } 1042 1043 func (it *mapIteratorForKeys) Next() bool { 1044 it.idx++ 1045 return it.Valid() 1046 } 1047 1048 type setFirstIterator struct { 1049 s types.Set 1050 } 1051 1052 func (it *setFirstIterator) Next() types.Value { 1053 return it.s.First() 1054 } 1055 1056 func (it *setFirstIterator) SkipTo(v types.Value) types.Value { 1057 panic("not implemented") 1058 } 1059 1060 type mapFirstIterator struct { 1061 m *types.Map 1062 } 1063 1064 func (it *mapFirstIterator) Valid() bool { 1065 return it.m != nil && !it.m.Empty() 1066 } 1067 1068 func (it *mapFirstIterator) Entry() (types.Value, types.Value) { 1069 if it.m == nil { 1070 return nil, nil 1071 } 1072 k, v := it.m.First() 1073 it.m = nil 1074 return k, v 1075 } 1076 1077 func (it *mapFirstIterator) Next() bool { 1078 return false 1079 }