github.com/brycereitano/goa@v0.0.0-20170315073847-8ffa6c85e265/design/apidsl/response.go (about) 1 package apidsl 2 3 import ( 4 "github.com/goadesign/goa/design" 5 "github.com/goadesign/goa/dslengine" 6 ) 7 8 // Response implements the response definition DSL. Response takes the name of the response as 9 // first parameter. goa defines all the standard HTTP status name as global variables so they can be 10 // readily used as response names. The response body data type can be specified as second argument. 11 // If a type is specified it overrides any type defined by in the response media type. Response also 12 // accepts optional arguments that correspond to the arguments defined by the corresponding response 13 // template (the response template with the same name) if there is one, see ResponseTemplate. 14 // 15 // A response may also optionally use an anonymous function as last argument to specify the response 16 // status code, media type and headers overriding what the default response or response template 17 // specifies: 18 // 19 // Response(OK, "text/plain") // OK response template accepts one argument: 20 // // the media type identifier 21 // 22 // Response(OK, BottleMedia) // or a media type defined in the design 23 // 24 // Response(OK, "application/vnd.bottle") // optionally referred to by identifier 25 // 26 // Response(OK, func() { 27 // Media("application/vnd.bottle") // Alternatively media type is set with Media 28 // }) 29 // 30 // Response(OK, BottleMedia, func() { 31 // Headers(func() { // Headers list the response HTTP headers 32 // Header("X-Request-Id") // Header syntax is identical to Attribute's 33 // }) 34 // }) 35 // 36 // Response(OK, BottleMedia, func() { 37 // Status(201) // Set response status (overrides template's) 38 // }) 39 // 40 // Response("MyResponse", func() { // Define custom response (using no template) 41 // Description("This is my response") 42 // Media(BottleMedia) 43 // Headers(func() { 44 // Header("X-Request-Id", func() { 45 // Pattern("[a-f0-9]+") 46 // }) 47 // }) 48 // Status(200) 49 // }) 50 // 51 // goa defines a default response template for all the HTTP status code. The default template simply sets 52 // the status code. So if an action can return NotFound for example all it has to do is specify 53 // Response(NotFound) - there is no need to specify the status code as the default response already 54 // does it, in other words: 55 // 56 // Response(NotFound) 57 // 58 // is equivalent to: 59 // 60 // Response(NotFound, func() { 61 // Status(404) 62 // }) 63 // 64 // goa also defines a default response template for the OK response which takes a single argument: 65 // the identifier of the media type used to render the response. The API DSL can define additional 66 // response templates or override the default OK response template using ResponseTemplate. 67 // 68 // The media type identifier specified in a response definition via the Media function can be 69 // "generic" such as "text/plain" or "application/json" or can correspond to the identifier of a 70 // media type defined in the API DSL. In this latter case goa uses the media type definition to 71 // generate helper response methods. These methods know how to render the views defined on the media 72 // type and run the validations defined in the media type during rendering. 73 func Response(name string, paramsAndDSL ...interface{}) { 74 switch def := dslengine.CurrentDefinition().(type) { 75 case *design.ActionDefinition: 76 if def.Responses == nil { 77 def.Responses = make(map[string]*design.ResponseDefinition) 78 } 79 if _, ok := def.Responses[name]; ok { 80 dslengine.ReportError("response %s is defined twice", name) 81 return 82 } 83 if resp := executeResponseDSL(name, paramsAndDSL...); resp != nil { 84 if resp.Status == 200 && resp.MediaType == "" { 85 resp.MediaType = def.Parent.MediaType 86 resp.ViewName = def.Parent.DefaultViewName 87 } 88 resp.Parent = def 89 def.Responses[name] = resp 90 } 91 92 case *design.ResourceDefinition: 93 if def.Responses == nil { 94 def.Responses = make(map[string]*design.ResponseDefinition) 95 } 96 if _, ok := def.Responses[name]; ok { 97 dslengine.ReportError("response %s is defined twice", name) 98 return 99 } 100 if resp := executeResponseDSL(name, paramsAndDSL...); resp != nil { 101 if resp.Status == 200 && resp.MediaType == "" { 102 resp.MediaType = def.MediaType 103 resp.ViewName = def.DefaultViewName 104 } 105 resp.Parent = def 106 def.Responses[name] = resp 107 } 108 109 default: 110 dslengine.IncompatibleDSL() 111 } 112 } 113 114 // Status sets the Response status. 115 func Status(status int) { 116 if r, ok := responseDefinition(); ok { 117 r.Status = status 118 } 119 } 120 121 func executeResponseDSL(name string, paramsAndDSL ...interface{}) *design.ResponseDefinition { 122 var params []string 123 var dsl func() 124 var ok bool 125 var dt design.DataType 126 if len(paramsAndDSL) > 0 { 127 d := paramsAndDSL[len(paramsAndDSL)-1] 128 if dsl, ok = d.(func()); ok { 129 paramsAndDSL = paramsAndDSL[:len(paramsAndDSL)-1] 130 } 131 if len(paramsAndDSL) > 0 { 132 t := paramsAndDSL[0] 133 if dt, ok = t.(design.DataType); ok { 134 paramsAndDSL = paramsAndDSL[1:] 135 } 136 } 137 params = make([]string, len(paramsAndDSL)) 138 for i, p := range paramsAndDSL { 139 params[i], ok = p.(string) 140 if !ok { 141 dslengine.ReportError("invalid response template parameter %#v, must be a string", p) 142 return nil 143 } 144 } 145 } 146 var resp *design.ResponseDefinition 147 if len(params) > 0 { 148 if tmpl, ok := design.Design.ResponseTemplates[name]; ok { 149 resp = tmpl.Template(params...) 150 } else if tmpl, ok := design.Design.DefaultResponseTemplates[name]; ok { 151 resp = tmpl.Template(params...) 152 } else { 153 dslengine.ReportError("no response template named %#v", name) 154 return nil 155 } 156 } else { 157 if ar, ok := design.Design.Responses[name]; ok { 158 resp = ar.Dup() 159 } else if ar, ok := design.Design.DefaultResponses[name]; ok { 160 resp = ar.Dup() 161 resp.Standard = true 162 } else { 163 resp = &design.ResponseDefinition{Name: name} 164 } 165 } 166 if dsl != nil { 167 if !dslengine.Execute(dsl, resp) { 168 return nil 169 } 170 resp.Standard = false 171 } 172 if dt != nil { 173 if mt, ok := dt.(*design.MediaTypeDefinition); ok { 174 resp.MediaType = mt.Identifier 175 } 176 resp.Type = dt 177 resp.Standard = false 178 } 179 return resp 180 }