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