github.com/ccrossley/goa@v1.3.1/design/types.go (about) 1 // Package design defines types which describe the data types used by action controllers. 2 // These are the data structures of the request payloads and parameters as well as the response 3 // payloads. 4 // There are primitive types corresponding to the JSON primitive types (bool, string, integer and 5 // number), array types which represent a collection of another type and object types corresponding 6 // to JSON objects (i.e. a map indexed by strings where each value may be any of the data types). 7 // On top of these the package also defines "user types" and "media types". Both these types are 8 // named objects with additional properties (a description and for media types the media type 9 // identifier, links and views). 10 package design 11 12 import ( 13 "fmt" 14 "mime" 15 "reflect" 16 "sort" 17 "strings" 18 "time" 19 20 "github.com/goadesign/goa/dslengine" 21 "github.com/satori/go.uuid" 22 ) 23 24 // DefaultView is the name of the default view. 25 const DefaultView = "default" 26 27 // It returns the default view - or if not available the link view - or if not available the first 28 // view by alphabetical order. 29 type ( 30 // A Kind defines the JSON type that a DataType represents. 31 Kind uint 32 33 // DataType is the common interface to all types. 34 DataType interface { 35 // Kind of data type, one of the Kind enum. 36 Kind() Kind 37 // Name returns the type name. 38 Name() string 39 // IsPrimitive returns true if the underlying type is one of the primitive types. 40 IsPrimitive() bool 41 // HasAttributes returns true if the underlying type has any attributes. 42 HasAttributes() bool 43 // IsObject returns true if the underlying type is an object, a user type which 44 // is an object or a media type whose type is an object. 45 IsObject() bool 46 // IsArray returns true if the underlying type is an array, a user type which 47 // is an array or a media type whose type is an array. 48 IsArray() bool 49 // IsHash returns true if the underlying type is a hash map, a user type which 50 // is a hash map or a media type whose type is a hash map. 51 IsHash() bool 52 // ToObject returns the underlying object if any (i.e. if IsObject returns true), 53 // nil otherwise. 54 ToObject() Object 55 // ToArray returns the underlying array if any (i.e. if IsArray returns true), 56 // nil otherwise. 57 ToArray() *Array 58 // ToHash returns the underlying hash map if any (i.e. if IsHash returns true), 59 // nil otherwise. 60 ToHash() *Hash 61 // CanHaveDefault returns whether the data type can have a default value. 62 CanHaveDefault() bool 63 // IsCompatible checks whether val has a Go type that is 64 // compatible with the data type. 65 IsCompatible(val interface{}) bool 66 // GenerateExample returns a random value for the given data type. 67 // If the data type has validations then the example value validates them. 68 // seen keeps track of the user and media types that have been traversed via 69 // recursion to prevent infinite loops. 70 GenerateExample(r *RandomGenerator, seen []string) interface{} 71 } 72 73 // DataStructure is the interface implemented by all data structure types. 74 // That is attribute definitions, user types and media types. 75 DataStructure interface { 76 // Definition returns the data structure definition. 77 Definition() *AttributeDefinition 78 // Walk traverses the data structure recursively and calls the given function once 79 // on each attribute starting with the attribute returned by Definition. 80 // User type and media type attributes are traversed once even for recursive 81 // definitions to avoid infinite recursion. 82 // Walk stops and returns the error if the function returns a non-nil error. 83 Walk(func(*AttributeDefinition) error) error 84 } 85 86 // Primitive is the type for null, boolean, integer, number, string, and time. 87 Primitive Kind 88 89 // Array is the type for a JSON array. 90 Array struct { 91 ElemType *AttributeDefinition 92 } 93 94 // ArrayVal is the value of an array used to specify the default value. 95 ArrayVal []interface{} 96 97 // Object is the type for a JSON object. 98 Object map[string]*AttributeDefinition 99 100 // Hash is the type for a hash map. 101 Hash struct { 102 KeyType *AttributeDefinition 103 ElemType *AttributeDefinition 104 } 105 106 // HashVal is the value of a hash used to specify the default value. 107 HashVal map[interface{}]interface{} 108 109 // UserTypeDefinition is the type for user defined types that are not media types 110 // (e.g. payload types). 111 UserTypeDefinition struct { 112 // A user type is an attribute definition. 113 *AttributeDefinition 114 // Name of type 115 TypeName string 116 } 117 118 // MediaTypeDefinition describes the rendering of a resource using property and link 119 // definitions. A property corresponds to a single member of the media type, 120 // it has a name and a type as well as optional validation rules. A link has a 121 // name and a URL that points to a related resource. 122 // Media types also define views which describe which members and links to render when 123 // building the response body for the corresponding view. 124 MediaTypeDefinition struct { 125 // A media type is a type 126 *UserTypeDefinition 127 // Identifier is the RFC 6838 media type identifier. 128 Identifier string 129 // ContentType identifies the value written to the response "Content-Type" header. 130 // Defaults to Identifier. 131 ContentType string 132 // Links list the rendered links indexed by name. 133 Links map[string]*LinkDefinition 134 // Views list the supported views indexed by name. 135 Views map[string]*ViewDefinition 136 // Resource this media type is the canonical representation for if any 137 Resource *ResourceDefinition 138 } 139 ) 140 141 const ( 142 // BooleanKind represents a JSON bool. 143 BooleanKind Kind = iota + 1 144 // IntegerKind represents a JSON integer. 145 IntegerKind 146 // NumberKind represents a JSON number including integers. 147 NumberKind 148 // StringKind represents a JSON string. 149 StringKind 150 // DateTimeKind represents a JSON string that is parsed as a Go time.Time 151 DateTimeKind 152 // UUIDKind represents a JSON string that is parsed as a Go uuid.UUID 153 UUIDKind 154 // AnyKind represents a generic interface{}. 155 AnyKind 156 // ArrayKind represents a JSON array. 157 ArrayKind 158 // ObjectKind represents a JSON object. 159 ObjectKind 160 // HashKind represents a JSON object where the keys are not known in advance. 161 HashKind 162 // UserTypeKind represents a user type. 163 UserTypeKind 164 // MediaTypeKind represents a media type. 165 MediaTypeKind 166 ) 167 168 const ( 169 // Boolean is the type for a JSON boolean. 170 Boolean = Primitive(BooleanKind) 171 172 // Integer is the type for a JSON number without a fraction or exponent part. 173 Integer = Primitive(IntegerKind) 174 175 // Number is the type for any JSON number, including integers. 176 Number = Primitive(NumberKind) 177 178 // String is the type for a JSON string. 179 String = Primitive(StringKind) 180 181 // DateTime is the type for a JSON string parsed as a Go time.Time 182 // DateTime expects an RFC3339 formatted value. 183 DateTime = Primitive(DateTimeKind) 184 185 // UUID is the type for a JSON string parsed as a Go uuid.UUID 186 // UUID expects an RFC4122 formatted value. 187 UUID = Primitive(UUIDKind) 188 189 // Any is the type for an arbitrary JSON value (interface{} in Go). 190 Any = Primitive(AnyKind) 191 ) 192 193 // DataType implementation 194 195 // Kind implements DataKind. 196 func (p Primitive) Kind() Kind { return Kind(p) } 197 198 // Name returns the JSON type name. 199 func (p Primitive) Name() string { 200 switch p { 201 case Boolean: 202 return "boolean" 203 case Integer: 204 return "integer" 205 case Number: 206 return "number" 207 case String, DateTime, UUID: 208 return "string" 209 case Any: 210 return "any" 211 default: 212 panic("unknown primitive type") // bug 213 } 214 } 215 216 // IsPrimitive returns true. 217 func (p Primitive) IsPrimitive() bool { return true } 218 219 // HasAttributes returns false. 220 func (p Primitive) HasAttributes() bool { return false } 221 222 // IsObject returns false. 223 func (p Primitive) IsObject() bool { return false } 224 225 // IsArray returns false. 226 func (p Primitive) IsArray() bool { return false } 227 228 // IsHash returns false. 229 func (p Primitive) IsHash() bool { return false } 230 231 // ToObject returns nil. 232 func (p Primitive) ToObject() Object { return nil } 233 234 // ToArray returns nil. 235 func (p Primitive) ToArray() *Array { return nil } 236 237 // ToHash returns nil. 238 func (p Primitive) ToHash() *Hash { return nil } 239 240 // CanHaveDefault returns whether the primitive can have a default value. 241 func (p Primitive) CanHaveDefault() (ok bool) { 242 switch p { 243 case Boolean, Integer, Number, String, DateTime: 244 ok = true 245 } 246 return 247 } 248 249 // IsCompatible returns true if val is compatible with p. 250 func (p Primitive) IsCompatible(val interface{}) bool { 251 if p != Boolean && p != Integer && p != Number && p != String && p != DateTime && p != UUID && p != Any { 252 panic("unknown primitive type") // bug 253 } 254 if p == Any { 255 return true 256 } 257 switch val.(type) { 258 case bool: 259 return p == Boolean 260 case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 261 return p == Integer || p == Number 262 case float32, float64: 263 return p == Number 264 case string: 265 if p == String { 266 return true 267 } 268 if p == DateTime { 269 _, err := time.Parse(time.RFC3339, val.(string)) 270 return err == nil 271 } 272 if p == UUID { 273 _, err := uuid.FromString(val.(string)) 274 return err == nil 275 } 276 } 277 return false 278 } 279 280 var anyPrimitive = []Primitive{Boolean, Integer, Number, DateTime, UUID} 281 282 // GenerateExample returns an instance of the given data type. 283 func (p Primitive) GenerateExample(r *RandomGenerator, seen []string) interface{} { 284 switch p { 285 case Boolean: 286 return r.Bool() 287 case Integer: 288 return r.Int() 289 case Number: 290 return r.Float64() 291 case String: 292 return r.String() 293 case DateTime: 294 return r.DateTime() 295 case UUID: 296 return r.UUID().String() // Generate string to can be JSON marshaled 297 case Any: 298 // to not make it too complicated, pick one of the primitive types 299 return anyPrimitive[r.Int()%len(anyPrimitive)].GenerateExample(r, seen) 300 default: 301 panic("unknown primitive type") // bug 302 } 303 } 304 305 // Kind implements DataKind. 306 func (a *Array) Kind() Kind { return ArrayKind } 307 308 // Name returns the type name. 309 func (a *Array) Name() string { 310 return "array" 311 } 312 313 // IsPrimitive returns false. 314 func (a *Array) IsPrimitive() bool { return false } 315 316 // HasAttributes returns true if the array's element type is user defined. 317 func (a *Array) HasAttributes() bool { 318 return a.ElemType.Type.HasAttributes() 319 } 320 321 // IsObject returns false. 322 func (a *Array) IsObject() bool { return false } 323 324 // IsArray returns true. 325 func (a *Array) IsArray() bool { return true } 326 327 // IsHash returns false. 328 func (a *Array) IsHash() bool { return false } 329 330 // ToObject returns nil. 331 func (a *Array) ToObject() Object { return nil } 332 333 // ToArray returns a. 334 func (a *Array) ToArray() *Array { return a } 335 336 // ToHash returns nil. 337 func (a *Array) ToHash() *Hash { return nil } 338 339 // CanHaveDefault returns true if the array type can have a default value. 340 // The array type can have a default value only if the element type can 341 // have a default value. 342 func (a *Array) CanHaveDefault() bool { 343 return a.ElemType.Type.CanHaveDefault() 344 } 345 346 // IsCompatible returns true if val is compatible with p. 347 func (a *Array) IsCompatible(val interface{}) bool { 348 k := reflect.TypeOf(val).Kind() 349 if k != reflect.Array && k != reflect.Slice { 350 return false 351 } 352 v := reflect.ValueOf(val) 353 for i := 0; i < v.Len(); i++ { 354 compat := (a.ElemType.Type != nil) && a.ElemType.Type.IsCompatible(v.Index(i).Interface()) 355 if !compat { 356 return false 357 } 358 } 359 return true 360 } 361 362 // GenerateExample produces a random array value. 363 func (a *Array) GenerateExample(r *RandomGenerator, seen []string) interface{} { 364 count := r.Int()%3 + 1 365 res := make([]interface{}, count) 366 for i := 0; i < count; i++ { 367 res[i] = a.ElemType.Type.GenerateExample(r, seen) 368 } 369 return a.MakeSlice(res) 370 } 371 372 // MakeSlice examines the key type from the Array and create a slice with builtin type if possible. 373 // The idea is to avoid generating []interface{} and produce more known types. 374 func (a *Array) MakeSlice(s []interface{}) interface{} { 375 slice := reflect.MakeSlice(toReflectType(a), 0, len(s)) 376 for _, item := range s { 377 slice = reflect.Append(slice, reflect.ValueOf(item)) 378 } 379 return slice.Interface() 380 } 381 382 // Kind implements DataKind. 383 func (o Object) Kind() Kind { return ObjectKind } 384 385 // Name returns the type name. 386 func (o Object) Name() string { return "object" } 387 388 // IsPrimitive returns false. 389 func (o Object) IsPrimitive() bool { return false } 390 391 // HasAttributes returns true. 392 func (o Object) HasAttributes() bool { return true } 393 394 // IsObject returns true. 395 func (o Object) IsObject() bool { return true } 396 397 // IsArray returns false. 398 func (o Object) IsArray() bool { return false } 399 400 // IsHash returns false. 401 func (o Object) IsHash() bool { return false } 402 403 // ToObject returns the underlying object. 404 func (o Object) ToObject() Object { return o } 405 406 // ToArray returns nil. 407 func (o Object) ToArray() *Array { return nil } 408 409 // ToHash returns nil. 410 func (o Object) ToHash() *Hash { return nil } 411 412 // CanHaveDefault returns false. 413 func (o Object) CanHaveDefault() bool { return false } 414 415 // Merge copies other's attributes into o overridding any pre-existing attribute with the same name. 416 func (o Object) Merge(other Object) { 417 for n, att := range other { 418 o[n] = DupAtt(att) 419 } 420 } 421 422 // IsCompatible returns true if val is compatible with p. 423 func (o Object) IsCompatible(val interface{}) bool { 424 k := reflect.TypeOf(val).Kind() 425 return k == reflect.Map || k == reflect.Struct 426 } 427 428 // GenerateExample returns a random value of the object. 429 func (o Object) GenerateExample(r *RandomGenerator, seen []string) interface{} { 430 // ensure fixed ordering 431 keys := make([]string, 0, len(o)) 432 for n := range o { 433 keys = append(keys, n) 434 } 435 sort.Strings(keys) 436 437 res := make(map[string]interface{}) 438 for _, n := range keys { 439 att := o[n] 440 res[n] = att.Type.GenerateExample(r, seen) 441 } 442 return res 443 } 444 445 // Kind implements DataKind. 446 func (h *Hash) Kind() Kind { return HashKind } 447 448 // Name returns the type name. 449 func (h *Hash) Name() string { return "hash" } 450 451 // IsPrimitive returns false. 452 func (h *Hash) IsPrimitive() bool { return false } 453 454 // HasAttributes returns true if the either hash's key type is user defined 455 // or the element type is user defined. 456 func (h *Hash) HasAttributes() bool { 457 return h.KeyType.Type.HasAttributes() || h.ElemType.Type.HasAttributes() 458 } 459 460 // IsObject returns false. 461 func (h *Hash) IsObject() bool { return false } 462 463 // IsArray returns false. 464 func (h *Hash) IsArray() bool { return false } 465 466 // IsHash returns true. 467 func (h *Hash) IsHash() bool { return true } 468 469 // ToObject returns nil. 470 func (h *Hash) ToObject() Object { return nil } 471 472 // ToArray returns nil. 473 func (h *Hash) ToArray() *Array { return nil } 474 475 // ToHash returns the underlying hash map. 476 func (h *Hash) ToHash() *Hash { return h } 477 478 // CanHaveDefault returns true if the hash type can have a default value. 479 // The hash type can have a default value only if both the key type and 480 // the element type can have a default value. 481 func (h *Hash) CanHaveDefault() bool { 482 return h.KeyType.Type.CanHaveDefault() && h.ElemType.Type.CanHaveDefault() 483 } 484 485 // IsCompatible returns true if val is compatible with p. 486 func (h *Hash) IsCompatible(val interface{}) bool { 487 k := reflect.TypeOf(val).Kind() 488 if k != reflect.Map { 489 return false 490 } 491 v := reflect.ValueOf(val) 492 for _, key := range v.MapKeys() { 493 keyCompat := h.KeyType.Type == nil || h.KeyType.Type.IsCompatible(key.Interface()) 494 elemCompat := h.ElemType.Type == nil || h.ElemType.Type.IsCompatible(v.MapIndex(key).Interface()) 495 if !keyCompat || !elemCompat { 496 return false 497 } 498 } 499 return true 500 } 501 502 // GenerateExample returns a random hash value. 503 func (h *Hash) GenerateExample(r *RandomGenerator, seen []string) interface{} { 504 count := r.Int()%3 + 1 505 pair := map[interface{}]interface{}{} 506 for i := 0; i < count; i++ { 507 pair[h.KeyType.Type.GenerateExample(r, seen)] = h.ElemType.Type.GenerateExample(r, seen) 508 } 509 return h.MakeMap(pair) 510 } 511 512 // MakeMap examines the key type from a Hash and create a map with builtin type if possible. 513 // The idea is to avoid generating map[interface{}]interface{}, which cannot be handled by json.Marshal. 514 func (h *Hash) MakeMap(m map[interface{}]interface{}) interface{} { 515 hash := reflect.MakeMap(toReflectType(h)) 516 for key, value := range m { 517 hash.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(value)) 518 } 519 return hash.Interface() 520 } 521 522 // AttributeIterator is the type of the function given to IterateAttributes. 523 type AttributeIterator func(string, *AttributeDefinition) error 524 525 // IterateAttributes calls the given iterator passing in each attribute sorted in alphabetical order. 526 // Iteration stops if an iterator returns an error and in this case IterateObject returns that 527 // error. 528 func (o Object) IterateAttributes(it AttributeIterator) error { 529 names := make([]string, len(o)) 530 i := 0 531 for n := range o { 532 names[i] = n 533 i++ 534 } 535 sort.Strings(names) 536 for _, n := range names { 537 if err := it(n, o[n]); err != nil { 538 return err 539 } 540 } 541 return nil 542 } 543 544 // UserTypes traverses the data type recursively and collects all the user types used to 545 // define it. The returned map is indexed by type name. 546 func UserTypes(dt DataType) map[string]*UserTypeDefinition { 547 collect := func(types map[string]*UserTypeDefinition) func(*AttributeDefinition) error { 548 return func(at *AttributeDefinition) error { 549 if u, ok := at.Type.(*UserTypeDefinition); ok { 550 types[u.TypeName] = u 551 } else if m, ok := at.Type.(*MediaTypeDefinition); ok { 552 types[m.TypeName] = m.UserTypeDefinition 553 } 554 return nil 555 } 556 } 557 switch actual := dt.(type) { 558 case Primitive: 559 return nil 560 case *Array: 561 return UserTypes(actual.ElemType.Type) 562 case *Hash: 563 ktypes := UserTypes(actual.KeyType.Type) 564 vtypes := UserTypes(actual.ElemType.Type) 565 if vtypes == nil { 566 return ktypes 567 } 568 for n, ut := range ktypes { 569 vtypes[n] = ut 570 } 571 return vtypes 572 case Object: 573 types := make(map[string]*UserTypeDefinition) 574 for _, att := range actual { 575 att.Walk(collect(types)) 576 } 577 if len(types) == 0 { 578 return nil 579 } 580 return types 581 case *UserTypeDefinition: 582 types := map[string]*UserTypeDefinition{actual.TypeName: actual} 583 actual.Walk(collect(types)) 584 return types 585 case *MediaTypeDefinition: 586 types := map[string]*UserTypeDefinition{actual.TypeName: actual.UserTypeDefinition} 587 actual.Walk(collect(types)) 588 return types 589 default: 590 panic("unknown type") // bug 591 } 592 } 593 594 // ToSlice converts an ArrayVal to a slice. 595 func (a ArrayVal) ToSlice() []interface{} { 596 arr := make([]interface{}, len(a)) 597 for i, elem := range a { 598 switch actual := elem.(type) { 599 case ArrayVal: 600 arr[i] = actual.ToSlice() 601 case HashVal: 602 arr[i] = actual.ToMap() 603 default: 604 arr[i] = actual 605 } 606 } 607 return arr 608 } 609 610 // ToMap converts a HashVal to a map. 611 func (h HashVal) ToMap() map[interface{}]interface{} { 612 mp := make(map[interface{}]interface{}, len(h)) 613 for k, v := range h { 614 switch actual := v.(type) { 615 case ArrayVal: 616 mp[k] = actual.ToSlice() 617 case HashVal: 618 mp[k] = actual.ToMap() 619 default: 620 mp[k] = actual 621 } 622 } 623 return mp 624 } 625 626 // NewUserTypeDefinition creates a user type definition but does not 627 // execute the DSL. 628 func NewUserTypeDefinition(name string, dsl func()) *UserTypeDefinition { 629 return &UserTypeDefinition{ 630 TypeName: name, 631 AttributeDefinition: &AttributeDefinition{DSLFunc: dsl}, 632 } 633 } 634 635 // Kind implements DataKind. 636 func (u *UserTypeDefinition) Kind() Kind { return UserTypeKind } 637 638 // Name returns the JSON type name. 639 func (u *UserTypeDefinition) Name() string { return u.Type.Name() } 640 641 // IsPrimitive calls IsPrimitive on the user type underlying data type. 642 func (u *UserTypeDefinition) IsPrimitive() bool { return u.Type != nil && u.Type.IsPrimitive() } 643 644 // HasAttributes calls the HasAttributes on the user type underlying data type. 645 func (u *UserTypeDefinition) HasAttributes() bool { return u.Type.HasAttributes() } 646 647 // IsObject calls IsObject on the user type underlying data type. 648 func (u *UserTypeDefinition) IsObject() bool { return u.Type != nil && u.Type.IsObject() } 649 650 // IsArray calls IsArray on the user type underlying data type. 651 func (u *UserTypeDefinition) IsArray() bool { return u.Type != nil && u.Type.IsArray() } 652 653 // IsHash calls IsHash on the user type underlying data type. 654 func (u *UserTypeDefinition) IsHash() bool { return u.Type != nil && u.Type.IsHash() } 655 656 // ToObject calls ToObject on the user type underlying data type. 657 func (u *UserTypeDefinition) ToObject() Object { return u.Type.ToObject() } 658 659 // ToArray calls ToArray on the user type underlying data type. 660 func (u *UserTypeDefinition) ToArray() *Array { return u.Type.ToArray() } 661 662 // ToHash calls ToHash on the user type underlying data type. 663 func (u *UserTypeDefinition) ToHash() *Hash { return u.Type.ToHash() } 664 665 // CanHaveDefault calls CanHaveDefault on the user type underlying data type. 666 func (u *UserTypeDefinition) CanHaveDefault() bool { return u.Type.CanHaveDefault() } 667 668 // IsCompatible returns true if val is compatible with u. 669 func (u *UserTypeDefinition) IsCompatible(val interface{}) bool { 670 return u.Type == nil || u.Type.IsCompatible(val) 671 } 672 673 // Finalize merges base type attributes. 674 func (u *UserTypeDefinition) Finalize() { 675 if u.Reference != nil { 676 if bat := u.AttributeDefinition; bat != nil { 677 u.AttributeDefinition.Inherit(bat) 678 } 679 } 680 681 u.GenerateExample(Design.RandomGenerator(), nil) 682 } 683 684 // NewMediaTypeDefinition creates a media type definition but does not 685 // execute the DSL. 686 func NewMediaTypeDefinition(name, identifier string, dsl func()) *MediaTypeDefinition { 687 return &MediaTypeDefinition{ 688 UserTypeDefinition: &UserTypeDefinition{ 689 AttributeDefinition: &AttributeDefinition{Type: Object{}, DSLFunc: dsl}, 690 TypeName: name, 691 }, 692 Identifier: identifier, 693 } 694 } 695 696 // Kind implements DataKind. 697 func (m *MediaTypeDefinition) Kind() Kind { return MediaTypeKind } 698 699 // IsError returns true if the media type is implemented via a goa struct. 700 func (m *MediaTypeDefinition) IsError() bool { 701 base, params, err := mime.ParseMediaType(m.Identifier) 702 if err != nil { 703 panic("invalid media type identifier " + m.Identifier) // bug 704 } 705 delete(params, "view") 706 return mime.FormatMediaType(base, params) == ErrorMedia.Identifier 707 } 708 709 // ComputeViews returns the media type views recursing as necessary if the media type is a 710 // collection. 711 func (m *MediaTypeDefinition) ComputeViews() map[string]*ViewDefinition { 712 if m.Views != nil { 713 return m.Views 714 } 715 if m.IsArray() { 716 if mt, ok := m.ToArray().ElemType.Type.(*MediaTypeDefinition); ok { 717 return mt.ComputeViews() 718 } 719 } 720 return nil 721 } 722 723 // Finalize sets the value of ContentType to the identifier if not set. 724 func (m *MediaTypeDefinition) Finalize() { 725 if m.ContentType == "" { 726 m.ContentType = m.Identifier 727 } 728 m.UserTypeDefinition.Finalize() 729 } 730 731 // ViewIterator is the type of the function given to IterateViews. 732 type ViewIterator func(*ViewDefinition) error 733 734 // IterateViews calls the given iterator passing in each attribute sorted in alphabetical order. 735 // Iteration stops if an iterator returns an error and in this case IterateViews returns that 736 // error. 737 func (m *MediaTypeDefinition) IterateViews(it ViewIterator) error { 738 o := m.Views 739 // gather names and sort them 740 names := make([]string, len(o)) 741 i := 0 742 for n := range o { 743 names[i] = n 744 i++ 745 } 746 sort.Strings(names) 747 // iterate 748 for _, n := range names { 749 if err := it(o[n]); err != nil { 750 return err 751 } 752 } 753 return nil 754 } 755 756 // Project creates a MediaTypeDefinition containing the fields defined in the given view. The 757 // resuling media type only defines the default view and its identifier is modified to indicate that 758 // it was projected by adding the view as id parameter. links is a user type of type Object where 759 // each key corresponds to a linked media type as defined by the media type "links" attribute. 760 func (m *MediaTypeDefinition) Project(view string) (*MediaTypeDefinition, *UserTypeDefinition, error) { 761 canonical := m.projectCanonical(view) 762 if p, ok := ProjectedMediaTypes[canonical]; ok { 763 var links *UserTypeDefinition 764 mLinks := ProjectedMediaTypes[canonical+"; links"] 765 if mLinks != nil { 766 links = mLinks.UserTypeDefinition 767 } 768 return p, links, nil 769 } 770 if m.IsArray() { 771 return m.projectCollection(view) 772 } 773 return m.projectSingle(view, canonical) 774 } 775 776 func (m *MediaTypeDefinition) projectSingle(view, canonical string) (p *MediaTypeDefinition, links *UserTypeDefinition, err error) { 777 v, ok := m.Views[view] 778 if !ok { 779 return nil, nil, fmt.Errorf("unknown view %#v", view) 780 } 781 viewObj := v.Type.ToObject() 782 783 // Compute validations - view may not have all attributes 784 var val *dslengine.ValidationDefinition 785 if m.Validation != nil { 786 names := m.Validation.Required 787 var required []string 788 for _, n := range names { 789 if _, ok := viewObj[n]; ok { 790 required = append(required, n) 791 } 792 } 793 val = m.Validation.Dup() 794 val.Required = required 795 } 796 797 // Compute description 798 desc := m.Description 799 if desc == "" { 800 desc = m.TypeName + " media type" 801 } 802 desc += " (" + view + " view)" 803 804 p = &MediaTypeDefinition{ 805 Identifier: m.projectIdentifier(view), 806 UserTypeDefinition: &UserTypeDefinition{ 807 TypeName: m.projectTypeName(view), 808 AttributeDefinition: &AttributeDefinition{ 809 Description: desc, 810 Type: Dup(v.Type), 811 Validation: val, 812 }, 813 }, 814 } 815 p.Views = map[string]*ViewDefinition{"default": { 816 Name: "default", 817 AttributeDefinition: DupAtt(v.AttributeDefinition), 818 Parent: p, 819 }} 820 821 ProjectedMediaTypes[canonical] = p 822 projectedObj := p.Type.ToObject() 823 mtObj := m.Type.ToObject() 824 _, hasAttNamedLinks := mtObj["links"] 825 for n := range viewObj { 826 if n == "links" && !hasAttNamedLinks { 827 linkObj := make(Object) 828 for n, link := range m.Links { 829 linkView := link.View 830 if linkView == "" { 831 linkView = "link" 832 } 833 mtAtt, ok := mtObj[n] 834 if !ok { 835 return nil, nil, fmt.Errorf("unknown attribute %#v used in links", n) 836 } 837 mtt := mtAtt.Type.(*MediaTypeDefinition) 838 vl, _, err := mtt.Project(linkView) 839 if err != nil { 840 return nil, nil, err 841 } 842 linkObj[n] = &AttributeDefinition{Type: vl, Validation: mtt.Validation, Metadata: mtAtt.Metadata} 843 } 844 lTypeName := fmt.Sprintf("%sLinks", m.TypeName) 845 links = &UserTypeDefinition{ 846 AttributeDefinition: &AttributeDefinition{ 847 Description: fmt.Sprintf("%s contains links to related resources of %s.", lTypeName, m.TypeName), 848 Type: linkObj, 849 }, 850 TypeName: lTypeName, 851 } 852 projectedObj[n] = &AttributeDefinition{Type: links, Description: "Links to related resources"} 853 ProjectedMediaTypes[canonical+"; links"] = &MediaTypeDefinition{UserTypeDefinition: links} 854 } else { 855 if at := mtObj[n]; at != nil { 856 at = DupAtt(at) 857 if mt, ok := at.Type.(*MediaTypeDefinition); ok { 858 vatt := viewObj[n] 859 view := vatt.View 860 if view == "" { 861 view = at.View 862 } 863 if view == "" { 864 view = DefaultView 865 } 866 pr, _, err := mt.Project(view) 867 if err != nil { 868 return nil, nil, fmt.Errorf("view %#v on field %#v cannot be computed: %s", view, n, err) 869 } 870 at.Type = pr 871 // Force example to be generated again 872 // since set of attributes has changed 873 at.Example = nil 874 } 875 projectedObj[n] = at 876 } 877 } 878 } 879 return 880 } 881 882 func (m *MediaTypeDefinition) projectCollection(view string) (*MediaTypeDefinition, *UserTypeDefinition, error) { 883 // Project the collection element media type 884 e := m.ToArray().ElemType.Type.(*MediaTypeDefinition) // validation checked this cast would work 885 pe, le, err2 := e.Project(view) 886 if err2 != nil { 887 return nil, nil, fmt.Errorf("collection element: %s", err2) 888 } 889 890 // Build the projected collection with the results 891 desc := m.TypeName + " is the media type for an array of " + e.TypeName + " (" + view + " view)" 892 p := &MediaTypeDefinition{ 893 Identifier: m.projectIdentifier(view), 894 UserTypeDefinition: &UserTypeDefinition{ 895 AttributeDefinition: &AttributeDefinition{ 896 Description: desc, 897 Type: &Array{ElemType: &AttributeDefinition{Type: pe}}, 898 Example: nil, 899 }, 900 TypeName: pe.TypeName + "Collection", 901 }, 902 } 903 p.Views = map[string]*ViewDefinition{"default": &ViewDefinition{ 904 AttributeDefinition: DupAtt(pe.Views["default"].AttributeDefinition), 905 Name: "default", 906 Parent: p, 907 }} 908 909 // Run the DSL that was created by the CollectionOf function 910 if !dslengine.Execute(p.DSL(), p) { 911 return nil, nil, dslengine.Errors 912 } 913 914 // Build the links user type 915 var links *UserTypeDefinition 916 if le != nil { 917 lTypeName := le.TypeName + "Array" 918 links = &UserTypeDefinition{ 919 AttributeDefinition: &AttributeDefinition{ 920 Type: &Array{ElemType: &AttributeDefinition{Type: le}}, 921 Description: fmt.Sprintf("%s contains links to related resources of %s.", lTypeName, m.TypeName), 922 }, 923 TypeName: lTypeName, 924 } 925 } 926 927 return p, links, nil 928 } 929 930 // projectIdentifier computes the projected media type identifier by adding the "view" param. We 931 // need the projected media type identifier to be different so that looking up projected media types 932 // from ProjectedMediaTypes works correctly. It's also good for clients. 933 func (m *MediaTypeDefinition) projectIdentifier(view string) string { 934 base, params, err := mime.ParseMediaType(m.Identifier) 935 if err != nil { 936 base = m.Identifier 937 } 938 params["view"] = view 939 return mime.FormatMediaType(base, params) 940 } 941 942 // projectIdentifier computes the projected canonical media type identifier by adding the "view" 943 // param if the view is not the default view. 944 func (m *MediaTypeDefinition) projectCanonical(view string) string { 945 cano := CanonicalIdentifier(m.Identifier) 946 base, params, _ := mime.ParseMediaType(cano) 947 if params["view"] != "" { 948 return cano // Already projected 949 } 950 params["view"] = view 951 return mime.FormatMediaType(base, params) 952 } 953 954 // projectTypeName appends the view name to the media type name if the view name is not "default". 955 func (m *MediaTypeDefinition) projectTypeName(view string) string { 956 typeName := m.TypeName 957 if view != "default" { 958 typeName += strings.Title(view) 959 } 960 return typeName 961 } 962 963 // DataStructure implementation 964 965 // Definition returns the underlying attribute definition. 966 // Note that this function is "inherited" by both UserTypeDefinition and 967 // MediaTypeDefinition. 968 func (a *AttributeDefinition) Definition() *AttributeDefinition { 969 return a 970 } 971 972 // Walk traverses the data structure recursively and calls the given function once 973 // on each attribute starting with the attribute returned by Definition. 974 func (a *AttributeDefinition) Walk(walker func(*AttributeDefinition) error) error { 975 return walk(a, walker, make(map[string]bool)) 976 } 977 978 // Walk traverses the data structure recursively and calls the given function once 979 // on each attribute starting with the attribute returned by Definition. 980 func (u *UserTypeDefinition) Walk(walker func(*AttributeDefinition) error) error { 981 return walk(u.AttributeDefinition, walker, map[string]bool{u.TypeName: true}) 982 } 983 984 // Recursive implementation of the Walk methods. Takes care of avoiding infinite recursions by 985 // keeping track of types that have already been walked. 986 func walk(at *AttributeDefinition, walker func(*AttributeDefinition) error, seen map[string]bool) error { 987 if err := walker(at); err != nil { 988 return err 989 } 990 walkUt := func(ut *UserTypeDefinition) error { 991 if _, ok := seen[ut.TypeName]; ok { 992 return nil 993 } 994 seen[ut.TypeName] = true 995 return walk(ut.AttributeDefinition, walker, seen) 996 } 997 switch actual := at.Type.(type) { 998 case Primitive: 999 return nil 1000 case *Array: 1001 return walk(actual.ElemType, walker, seen) 1002 case *Hash: 1003 if err := walk(actual.KeyType, walker, seen); err != nil { 1004 return err 1005 } 1006 return walk(actual.ElemType, walker, seen) 1007 case Object: 1008 for _, cat := range actual { 1009 if err := walk(cat, walker, seen); err != nil { 1010 return err 1011 } 1012 } 1013 case *UserTypeDefinition: 1014 return walkUt(actual) 1015 case *MediaTypeDefinition: 1016 return walkUt(actual.UserTypeDefinition) 1017 default: 1018 panic("unknown attribute type") // bug 1019 } 1020 return nil 1021 } 1022 1023 // toReflectType converts the DataType to reflect.Type. 1024 func toReflectType(dtype DataType) reflect.Type { 1025 switch dtype.Kind() { 1026 case BooleanKind: 1027 return reflect.TypeOf(true) 1028 case IntegerKind: 1029 return reflect.TypeOf(int(0)) 1030 case NumberKind: 1031 return reflect.TypeOf(float64(0)) 1032 case UUIDKind, StringKind: 1033 return reflect.TypeOf("") 1034 case DateTimeKind: 1035 return reflect.TypeOf(time.Time{}) 1036 case ObjectKind, UserTypeKind, MediaTypeKind: 1037 return reflect.TypeOf(map[string]interface{}{}) 1038 case ArrayKind: 1039 return reflect.SliceOf(toReflectType(dtype.ToArray().ElemType.Type)) 1040 case HashKind: 1041 hash := dtype.ToHash() 1042 // avoid complication: not allow object as the hash key 1043 var ktype reflect.Type 1044 if !hash.KeyType.Type.IsObject() { 1045 ktype = toReflectType(hash.KeyType.Type) 1046 } else { 1047 ktype = reflect.TypeOf([]interface{}{}).Elem() 1048 } 1049 return reflect.MapOf(ktype, toReflectType(hash.ElemType.Type)) 1050 default: 1051 return reflect.TypeOf([]interface{}{}).Elem() 1052 } 1053 }