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