github.com/ccrossley/goa@v1.3.1/design/apidsl/type.go (about) 1 package apidsl 2 3 import ( 4 "github.com/goadesign/goa/design" 5 "github.com/goadesign/goa/dslengine" 6 ) 7 8 // Type implements the type definition dsl. A type definition describes a data structure consisting 9 // of attributes. Each attribute has a type which can also refer to a type definition (or use a 10 // primitive type or nested attibutes). The dsl syntax for define a type definition is the 11 // Attribute dsl, see Attribute. 12 // 13 // On top of specifying any attribute type, type definitions can also be used to describe the data 14 // structure of a request payload. They can also be used by media type definitions as reference, see 15 // Reference. Here is an example: 16 // 17 // Type("createPayload", func() { 18 // Description("Type of create and upload action payloads") 19 // Attribute("name", String, "name of bottle") 20 // Attribute("origin", Origin, "Details on wine origin") // See Origin definition below 21 // Required("name") 22 // }) 23 // 24 // var Origin = Type("origin", func() { 25 // Description("Origin of bottle") 26 // Attribute("Country") 27 // }) 28 // 29 // This function returns the newly defined type so the value can be used throughout the dsl. 30 func Type(name string, dsl func()) *design.UserTypeDefinition { 31 if design.Design.Types == nil { 32 design.Design.Types = make(map[string]*design.UserTypeDefinition) 33 } else if _, ok := design.Design.Types[name]; ok { 34 dslengine.ReportError("type %#v defined twice", name) 35 return nil 36 } 37 38 if !dslengine.IsTopLevelDefinition() { 39 dslengine.IncompatibleDSL() 40 return nil 41 } 42 43 t := &design.UserTypeDefinition{ 44 TypeName: name, 45 AttributeDefinition: &design.AttributeDefinition{DSLFunc: dsl}, 46 } 47 if dsl == nil { 48 t.Type = design.String 49 } else { 50 t.Type = make(design.Object) 51 } 52 design.Design.Types[name] = t 53 return t 54 } 55 56 // ArrayOf creates an array type from its element type. The result can be used 57 // anywhere a type can. Examples: 58 // 59 // var Bottle = Type("bottle", func() { 60 // Attribute("name") 61 // }) 62 // 63 // Action("update", func() { 64 // Params(func() { 65 // Param("ids", ArrayOf(Integer)) 66 // }) 67 // Payload(ArrayOf(Bottle)) 68 // }) 69 // 70 // ArrayOf accepts an optional DSL as second argument which allows providing 71 // validations for the elements of the array: 72 // 73 // Action("update", func() { 74 // Params(func() { 75 // Param("ids", ArrayOf(Integer, func() { 76 // Minimum(1) 77 // })) 78 // }) 79 // Payload(ArrayOf(Bottle)) 80 // }) 81 // 82 // If you are looking to return a collection of elements in a Response clause, 83 // refer to CollectionOf. ArrayOf creates a type, where CollectionOf creates a 84 // media type. 85 func ArrayOf(v interface{}, dsl ...func()) *design.Array { 86 t := resolveType(v) 87 // never return nil to avoid panics, errors are reported after DSL execution 88 res := &design.Array{ElemType: &design.AttributeDefinition{Type: design.String}} 89 if t == nil { 90 dslengine.ReportError("invalid ArrayOf argument: not a type and not a known user type name") 91 return res 92 } 93 if len(dsl) > 1 { 94 dslengine.ReportError("ArrayOf: too many arguments") 95 return res 96 } 97 at := design.AttributeDefinition{Type: t} 98 if len(dsl) == 1 { 99 dslengine.Execute(dsl[0], &at) 100 } 101 return &design.Array{ElemType: &at} 102 } 103 104 // HashOf creates a hash map from its key and element types. The result can be 105 // used anywhere a type can. Examples: 106 // 107 // var Bottle = Type("bottle", func() { 108 // Attribute("name") 109 // }) 110 // 111 // var RatedBottles = HashOf(String, Bottle) 112 // 113 // Action("updateRatings", func() { 114 // Payload(func() { 115 // Member("ratings", HashOf(String, Integer)) 116 // Member("bottles", RatedBottles) 117 // // Member("bottles", "RatedBottles") // can use name of user type 118 // }) 119 // 120 // HashOf accepts optional DSLs as third and fourth argument which allows 121 // providing validations for the keys and values of the hash respectively: 122 // 123 // var RatedBottles = HashOf(String, Bottle, func() { 124 // Pattern("[a-zA-Z]+") // Validate bottle names 125 // }) 126 // 127 // func ValidateKey() { 128 // Pattern("^foo") 129 // } 130 // 131 // func TypeValue() { 132 // Metadata("struct:field:type", "json.RawMessage", "encoding/json") 133 // } 134 // 135 // var Mappings = HashOf(String, String, ValidateKey, TypeValue) 136 // 137 func HashOf(k, v interface{}, dsls ...func()) *design.Hash { 138 tk := resolveType(k) 139 tv := resolveType(v) 140 if tk == nil || tv == nil { 141 // never return nil to avoid panics, errors are reported after DSL execution 142 dslengine.ReportError("HashOf: invalid type name") 143 return &design.Hash{ 144 KeyType: &design.AttributeDefinition{Type: design.String}, 145 ElemType: &design.AttributeDefinition{Type: design.String}, 146 } 147 } 148 kat := design.AttributeDefinition{Type: tk} 149 vat := design.AttributeDefinition{Type: tv} 150 if len(dsls) > 2 { 151 // never return nil to avoid panics, errors are reported after DSL execution 152 dslengine.ReportError("HashOf: too many arguments") 153 return &design.Hash{KeyType: &kat, ElemType: &vat} 154 } 155 if len(dsls) >= 1 { 156 dslengine.Execute(dsls[0], &kat) 157 if len(dsls) == 2 { 158 dslengine.Execute(dsls[1], &vat) 159 } 160 } 161 return &design.Hash{KeyType: &kat, ElemType: &vat} 162 } 163 164 func resolveType(v interface{}) design.DataType { 165 if t, ok := v.(design.DataType); ok { 166 return t 167 } 168 if name, ok := v.(string); ok { 169 if ut, ok := design.Design.Types[name]; ok { 170 return ut 171 } 172 if mt, ok := design.Design.MediaTypes[name]; ok { 173 return mt 174 } 175 } 176 return nil 177 }