Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / github.com / emicklei / go-restful / route_builder.go
1 package restful
2
3 // Copyright 2013 Ernest Micklei. All rights reserved.
4 // Use of this source code is governed by a license
5 // that can be found in the LICENSE file.
6
7 import (
8         "fmt"
9         "os"
10         "reflect"
11         "runtime"
12         "strings"
13         "sync/atomic"
14
15         "github.com/emicklei/go-restful/log"
16 )
17
18 // RouteBuilder is a helper to construct Routes.
19 type RouteBuilder struct {
20         rootPath    string
21         currentPath string
22         produces    []string
23         consumes    []string
24         httpMethod  string        // required
25         function    RouteFunction // required
26         filters     []FilterFunction
27         conditions  []RouteSelectionConditionFunction
28
29         typeNameHandleFunc TypeNameHandleFunction // required
30
31         // documentation
32         doc                     string
33         notes                   string
34         operation               string
35         readSample, writeSample interface{}
36         parameters              []*Parameter
37         errorMap                map[int]ResponseError
38         defaultResponse         *ResponseError
39         metadata                map[string]interface{}
40         deprecated              bool
41 }
42
43 // Do evaluates each argument with the RouteBuilder itself.
44 // This allows you to follow DRY principles without breaking the fluent programming style.
45 // Example:
46 //              ws.Route(ws.DELETE("/{name}").To(t.deletePerson).Do(Returns200, Returns500))
47 //
48 //              func Returns500(b *RouteBuilder) {
49 //                      b.Returns(500, "Internal Server Error", restful.ServiceError{})
50 //              }
51 func (b *RouteBuilder) Do(oneArgBlocks ...func(*RouteBuilder)) *RouteBuilder {
52         for _, each := range oneArgBlocks {
53                 each(b)
54         }
55         return b
56 }
57
58 // To bind the route to a function.
59 // If this route is matched with the incoming Http Request then call this function with the *Request,*Response pair. Required.
60 func (b *RouteBuilder) To(function RouteFunction) *RouteBuilder {
61         b.function = function
62         return b
63 }
64
65 // Method specifies what HTTP method to match. Required.
66 func (b *RouteBuilder) Method(method string) *RouteBuilder {
67         b.httpMethod = method
68         return b
69 }
70
71 // Produces specifies what MIME types can be produced ; the matched one will appear in the Content-Type Http header.
72 func (b *RouteBuilder) Produces(mimeTypes ...string) *RouteBuilder {
73         b.produces = mimeTypes
74         return b
75 }
76
77 // Consumes specifies what MIME types can be consumes ; the Accept Http header must matched any of these
78 func (b *RouteBuilder) Consumes(mimeTypes ...string) *RouteBuilder {
79         b.consumes = mimeTypes
80         return b
81 }
82
83 // Path specifies the relative (w.r.t WebService root path) URL path to match. Default is "/".
84 func (b *RouteBuilder) Path(subPath string) *RouteBuilder {
85         b.currentPath = subPath
86         return b
87 }
88
89 // Doc tells what this route is all about. Optional.
90 func (b *RouteBuilder) Doc(documentation string) *RouteBuilder {
91         b.doc = documentation
92         return b
93 }
94
95 // Notes is a verbose explanation of the operation behavior. Optional.
96 func (b *RouteBuilder) Notes(notes string) *RouteBuilder {
97         b.notes = notes
98         return b
99 }
100
101 // Reads tells what resource type will be read from the request payload. Optional.
102 // A parameter of type "body" is added ,required is set to true and the dataType is set to the qualified name of the sample's type.
103 func (b *RouteBuilder) Reads(sample interface{}, optionalDescription ...string) *RouteBuilder {
104         fn := b.typeNameHandleFunc
105         if fn == nil {
106                 fn = reflectTypeName
107         }
108         typeAsName := fn(sample)
109         description := ""
110         if len(optionalDescription) > 0 {
111                 description = optionalDescription[0]
112         }
113         b.readSample = sample
114         bodyParameter := &Parameter{&ParameterData{Name: "body", Description: description}}
115         bodyParameter.beBody()
116         bodyParameter.Required(true)
117         bodyParameter.DataType(typeAsName)
118         b.Param(bodyParameter)
119         return b
120 }
121
122 // ParameterNamed returns a Parameter already known to the RouteBuilder. Returns nil if not.
123 // Use this to modify or extend information for the Parameter (through its Data()).
124 func (b RouteBuilder) ParameterNamed(name string) (p *Parameter) {
125         for _, each := range b.parameters {
126                 if each.Data().Name == name {
127                         return each
128                 }
129         }
130         return p
131 }
132
133 // Writes tells what resource type will be written as the response payload. Optional.
134 func (b *RouteBuilder) Writes(sample interface{}) *RouteBuilder {
135         b.writeSample = sample
136         return b
137 }
138
139 // Param allows you to document the parameters of the Route. It adds a new Parameter (does not check for duplicates).
140 func (b *RouteBuilder) Param(parameter *Parameter) *RouteBuilder {
141         if b.parameters == nil {
142                 b.parameters = []*Parameter{}
143         }
144         b.parameters = append(b.parameters, parameter)
145         return b
146 }
147
148 // Operation allows you to document what the actual method/function call is of the Route.
149 // Unless called, the operation name is derived from the RouteFunction set using To(..).
150 func (b *RouteBuilder) Operation(name string) *RouteBuilder {
151         b.operation = name
152         return b
153 }
154
155 // ReturnsError is deprecated, use Returns instead.
156 func (b *RouteBuilder) ReturnsError(code int, message string, model interface{}) *RouteBuilder {
157         log.Print("ReturnsError is deprecated, use Returns instead.")
158         return b.Returns(code, message, model)
159 }
160
161 // Returns allows you to document what responses (errors or regular) can be expected.
162 // The model parameter is optional ; either pass a struct instance or use nil if not applicable.
163 func (b *RouteBuilder) Returns(code int, message string, model interface{}) *RouteBuilder {
164         err := ResponseError{
165                 Code:      code,
166                 Message:   message,
167                 Model:     model,
168                 IsDefault: false, // this field is deprecated, use default response instead.
169         }
170         // lazy init because there is no NewRouteBuilder (yet)
171         if b.errorMap == nil {
172                 b.errorMap = map[int]ResponseError{}
173         }
174         b.errorMap[code] = err
175         return b
176 }
177
178 // DefaultReturns is a special Returns call that sets the default of the response.
179 func (b *RouteBuilder) DefaultReturns(message string, model interface{}) *RouteBuilder {
180         b.defaultResponse = &ResponseError{
181                 Message: message,
182                 Model:   model,
183         }
184         return b
185 }
186
187 // Metadata adds or updates a key=value pair to the metadata map.
188 func (b *RouteBuilder) Metadata(key string, value interface{}) *RouteBuilder {
189         if b.metadata == nil {
190                 b.metadata = map[string]interface{}{}
191         }
192         b.metadata[key] = value
193         return b
194 }
195
196 // Deprecate sets the value of deprecated to true.  Deprecated routes have a special UI treatment to warn against use
197 func (b *RouteBuilder) Deprecate() *RouteBuilder {
198         b.deprecated = true
199         return b
200 }
201
202 // ResponseError represents a response; not necessarily an error.
203 type ResponseError struct {
204         Code      int
205         Message   string
206         Model     interface{}
207         IsDefault bool
208 }
209
210 func (b *RouteBuilder) servicePath(path string) *RouteBuilder {
211         b.rootPath = path
212         return b
213 }
214
215 // Filter appends a FilterFunction to the end of filters for this Route to build.
216 func (b *RouteBuilder) Filter(filter FilterFunction) *RouteBuilder {
217         b.filters = append(b.filters, filter)
218         return b
219 }
220
221 // If sets a condition function that controls matching the Route based on custom logic.
222 // The condition function is provided the HTTP request and should return true if the route
223 // should be considered.
224 //
225 // Efficiency note: the condition function is called before checking the method, produces, and
226 // consumes criteria, so that the correct HTTP status code can be returned.
227 //
228 // Lifecycle note: no filter functions have been called prior to calling the condition function,
229 // so the condition function should not depend on any context that might be set up by container
230 // or route filters.
231 func (b *RouteBuilder) If(condition RouteSelectionConditionFunction) *RouteBuilder {
232         b.conditions = append(b.conditions, condition)
233         return b
234 }
235
236 // If no specific Route path then set to rootPath
237 // If no specific Produces then set to rootProduces
238 // If no specific Consumes then set to rootConsumes
239 func (b *RouteBuilder) copyDefaults(rootProduces, rootConsumes []string) {
240         if len(b.produces) == 0 {
241                 b.produces = rootProduces
242         }
243         if len(b.consumes) == 0 {
244                 b.consumes = rootConsumes
245         }
246 }
247
248 // typeNameHandler sets the function that will convert types to strings in the parameter
249 // and model definitions.
250 func (b *RouteBuilder) typeNameHandler(handler TypeNameHandleFunction) *RouteBuilder {
251         b.typeNameHandleFunc = handler
252         return b
253 }
254
255 // Build creates a new Route using the specification details collected by the RouteBuilder
256 func (b *RouteBuilder) Build() Route {
257         pathExpr, err := newPathExpression(b.currentPath)
258         if err != nil {
259                 log.Printf("Invalid path:%s because:%v", b.currentPath, err)
260                 os.Exit(1)
261         }
262         if b.function == nil {
263                 log.Printf("No function specified for route:" + b.currentPath)
264                 os.Exit(1)
265         }
266         operationName := b.operation
267         if len(operationName) == 0 && b.function != nil {
268                 // extract from definition
269                 operationName = nameOfFunction(b.function)
270         }
271         route := Route{
272                 Method:          b.httpMethod,
273                 Path:            concatPath(b.rootPath, b.currentPath),
274                 Produces:        b.produces,
275                 Consumes:        b.consumes,
276                 Function:        b.function,
277                 Filters:         b.filters,
278                 If:              b.conditions,
279                 relativePath:    b.currentPath,
280                 pathExpr:        pathExpr,
281                 Doc:             b.doc,
282                 Notes:           b.notes,
283                 Operation:       operationName,
284                 ParameterDocs:   b.parameters,
285                 ResponseErrors:  b.errorMap,
286                 DefaultResponse: b.defaultResponse,
287                 ReadSample:      b.readSample,
288                 WriteSample:     b.writeSample,
289                 Metadata:        b.metadata,
290                 Deprecated:      b.deprecated}
291         route.postBuild()
292         return route
293 }
294
295 func concatPath(path1, path2 string) string {
296         return strings.TrimRight(path1, "/") + "/" + strings.TrimLeft(path2, "/")
297 }
298
299 var anonymousFuncCount int32
300
301 // nameOfFunction returns the short name of the function f for documentation.
302 // It uses a runtime feature for debugging ; its value may change for later Go versions.
303 func nameOfFunction(f interface{}) string {
304         fun := runtime.FuncForPC(reflect.ValueOf(f).Pointer())
305         tokenized := strings.Split(fun.Name(), ".")
306         last := tokenized[len(tokenized)-1]
307         last = strings.TrimSuffix(last, ")·fm") // < Go 1.5
308         last = strings.TrimSuffix(last, ")-fm") // Go 1.5
309         last = strings.TrimSuffix(last, "·fm")  // < Go 1.5
310         last = strings.TrimSuffix(last, "-fm")  // Go 1.5
311         if last == "func1" {                    // this could mean conflicts in API docs
312                 val := atomic.AddInt32(&anonymousFuncCount, 1)
313                 last = "func" + fmt.Sprintf("%d", val)
314                 atomic.StoreInt32(&anonymousFuncCount, val)
315         }
316         return last
317 }