diff options
Diffstat (limited to 'vendor/github.com/go-openapi/runtime/middleware')
10 files changed, 254 insertions, 33 deletions
diff --git a/vendor/github.com/go-openapi/runtime/middleware/context.go b/vendor/github.com/go-openapi/runtime/middleware/context.go index 54a8c21f15..fba5748db5 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/context.go +++ b/vendor/github.com/go-openapi/runtime/middleware/context.go @@ -21,16 +21,16 @@ import ( "strings" "sync" - "github.com/go-openapi/runtime/security" - "github.com/go-openapi/analysis" "github.com/go-openapi/errors" "github.com/go-openapi/loads" + "github.com/go-openapi/spec" + "github.com/go-openapi/strfmt" + "github.com/go-openapi/runtime" "github.com/go-openapi/runtime/logger" "github.com/go-openapi/runtime/middleware/untyped" - "github.com/go-openapi/spec" - "github.com/go-openapi/strfmt" + "github.com/go-openapi/runtime/security" ) // Debug when true turns on verbose logging @@ -280,8 +280,8 @@ func (c *Context) RequiredProduces() []string { // if the request is not valid an error will be returned func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, binder RequestBinder) error { var res []error + var requestContentType string - requestContentType := "*/*" // check and validate content type, select consumer if runtime.HasBody(request) { ct, _, err := runtime.ContentType(request.Header) @@ -304,7 +304,13 @@ func (c *Context) BindValidRequest(request *http.Request, route *MatchedRoute, b } // check and validate the response format - if len(res) == 0 && runtime.HasBody(request) { + if len(res) == 0 { + // if the route does not provide Produces and a default contentType could not be identified + // based on a body, typical for GET and DELETE requests, then default contentType to. + if len(route.Produces) == 0 && requestContentType == "" { + requestContentType = "*/*" + } + if str := NegotiateContentType(request, route.Produces, requestContentType); str == "" { res = append(res, errors.InvalidResponseFormat(request.Header.Get(runtime.HeaderAccept), route.Produces)) } @@ -433,6 +439,8 @@ func (c *Context) Authorize(request *http.Request, route *MatchedRoute) (interfa } } + rCtx = request.Context() + rCtx = stdContext.WithValue(rCtx, ctxSecurityPrincipal, usr) rCtx = stdContext.WithValue(rCtx, ctxSecurityScopes, route.Authenticator.AllScopes()) return usr, request.WithContext(rCtx), nil @@ -559,6 +567,26 @@ func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []st c.api.ServeErrorFor(route.Operation.ID)(rw, r, errors.New(http.StatusInternalServerError, "can't produce response")) } +func (c *Context) APIHandlerSwaggerUI(builder Builder) http.Handler { + b := builder + if b == nil { + b = PassthroughBuilder + } + + var title string + sp := c.spec.Spec() + if sp != nil && sp.Info != nil && sp.Info.Title != "" { + title = sp.Info.Title + } + + swaggerUIOpts := SwaggerUIOpts{ + BasePath: c.BasePath(), + Title: title, + } + + return Spec("", c.spec.Raw(), SwaggerUI(swaggerUIOpts, c.RoutesHandler(b))) +} + // APIHandler returns a handler to serve the API, this includes a swagger spec, router and the contract defined in the swagger spec func (c *Context) APIHandler(builder Builder) http.Handler { b := builder diff --git a/vendor/github.com/go-openapi/runtime/middleware/denco/router.go b/vendor/github.com/go-openapi/runtime/middleware/denco/router.go index 73703fddec..ecacc31ff6 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/denco/router.go +++ b/vendor/github.com/go-openapi/runtime/middleware/denco/router.go @@ -17,6 +17,9 @@ const ( // TerminationCharacter is a special character for end of path. TerminationCharacter = '#' + // SeparatorCharacter separates path segments. + SeparatorCharacter = '/' + // MaxSize is max size of records and internal slice. MaxSize = (1 << 22) - 1 ) @@ -420,10 +423,11 @@ type record struct { // makeRecords returns the records that use to build Double-Arrays. func makeRecords(srcs []Record) (statics, params []*record) { - spChars := string([]byte{ParamCharacter, WildcardCharacter}) termChar := string(TerminationCharacter) + paramPrefix := string(SeparatorCharacter) + string(ParamCharacter) + wildcardPrefix := string(SeparatorCharacter) + string(WildcardCharacter) for _, r := range srcs { - if strings.ContainsAny(r.Key, spChars) { + if strings.Contains(r.Key, paramPrefix) || strings.Contains(r.Key, wildcardPrefix) { r.Key += termChar params = append(params, &record{Record: r}) } else { diff --git a/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go b/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go index 466f553db4..bc6942a0f1 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go +++ b/vendor/github.com/go-openapi/runtime/middleware/not_implemented.go @@ -38,11 +38,30 @@ func (e *errorResp) WriteResponse(rw http.ResponseWriter, producer runtime.Produ rw.WriteHeader(http.StatusInternalServerError) } if err := producer.Produce(rw, e.response); err != nil { - panic(err) + Logger.Printf("failed to write error response: %v", err) } } // NotImplemented the error response when the response is not implemented func NotImplemented(message string) Responder { - return &errorResp{http.StatusNotImplemented, message, make(http.Header)} + return Error(http.StatusNotImplemented, message) +} + +// Error creates a generic responder for returning errors, the data will be serialized +// with the matching producer for the request +func Error(code int, data interface{}, headers ...http.Header) Responder { + var hdr http.Header + for _, h := range headers { + for k, v := range h { + if hdr == nil { + hdr = make(http.Header) + } + hdr[k] = v + } + } + return &errorResp{ + code: code, + response: data, + headers: hdr, + } } diff --git a/vendor/github.com/go-openapi/runtime/middleware/parameter.go b/vendor/github.com/go-openapi/runtime/middleware/parameter.go index 8975b6e1c8..8fa0cf4e46 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/parameter.go +++ b/vendor/github.com/go-openapi/runtime/middleware/parameter.go @@ -24,11 +24,12 @@ import ( "strconv" "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" "github.com/go-openapi/swag" "github.com/go-openapi/validate" + + "github.com/go-openapi/runtime" ) const defaultMaxMemory = 32 << 20 @@ -275,7 +276,7 @@ func (p *untypedParamBinder) setFieldValue(target reflect.Value, defaultValue in } if (!hasKey || (!p.parameter.AllowEmptyValue && data == "")) && p.parameter.Required && p.parameter.Default == nil { - return errors.Required(p.Name, p.parameter.In) + return errors.Required(p.Name, p.parameter.In, data) } ok, err := p.tryUnmarshaler(target, defaultValue, data) @@ -450,7 +451,7 @@ func (p *untypedParamBinder) readFormattedSliceFieldValue(data string, target re func (p *untypedParamBinder) setSliceFieldValue(target reflect.Value, defaultValue interface{}, data []string, hasKey bool) error { sz := len(data) if (!hasKey || (!p.parameter.AllowEmptyValue && (sz == 0 || (sz == 1 && data[0] == "")))) && p.parameter.Required && defaultValue == nil { - return errors.Required(p.Name, p.parameter.In) + return errors.Required(p.Name, p.parameter.In, data) } defVal := reflect.Zero(target.Type()) diff --git a/vendor/github.com/go-openapi/runtime/middleware/redoc.go b/vendor/github.com/go-openapi/runtime/middleware/redoc.go index 21277948c0..019c854295 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/redoc.go +++ b/vendor/github.com/go-openapi/runtime/middleware/redoc.go @@ -16,7 +16,7 @@ type RedocOpts struct { Path string // SpecURL the url to find the spec for SpecURL string - // RedocURL for the js that generates the redoc site, defaults to: https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js + // RedocURL for the js that generates the redoc site, defaults to: https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js RedocURL string // Title for the documentation site, default to: API documentation Title string @@ -74,13 +74,15 @@ func Redoc(opts RedocOpts, next http.Handler) http.Handler { } const ( - redocLatest = "https://rebilly.github.io/ReDoc/releases/latest/redoc.min.js" + redocLatest = "https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js" redocTemplate = `<!DOCTYPE html> <html> <head> <title>{{ .Title }}</title> - <!-- needed for adaptive design --> - <meta name="viewport" content="width=device-width, initial-scale=1"> + <!-- needed for adaptive design --> + <meta charset="utf-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet"> <!-- ReDoc doesn't change outer page styles diff --git a/vendor/github.com/go-openapi/runtime/middleware/request.go b/vendor/github.com/go-openapi/runtime/middleware/request.go index ee725f587a..760c37861d 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/request.go +++ b/vendor/github.com/go-openapi/runtime/middleware/request.go @@ -19,26 +19,27 @@ import ( "reflect" "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" + + "github.com/go-openapi/runtime" ) -// RequestBinder binds and validates the data from a http request -type untypedRequestBinder struct { +// UntypedRequestBinder binds and validates the data from a http request +type UntypedRequestBinder struct { Spec *spec.Swagger Parameters map[string]spec.Parameter Formats strfmt.Registry paramBinders map[string]*untypedParamBinder } -// NewRequestBinder creates a new binder for reading a request. -func newUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Swagger, formats strfmt.Registry) *untypedRequestBinder { +// NewUntypedRequestBinder creates a new binder for reading a request. +func NewUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Swagger, formats strfmt.Registry) *UntypedRequestBinder { binders := make(map[string]*untypedParamBinder) for fieldName, param := range parameters { binders[fieldName] = newUntypedParamBinder(param, spec, formats) } - return &untypedRequestBinder{ + return &UntypedRequestBinder{ Parameters: parameters, paramBinders: binders, Spec: spec, @@ -47,7 +48,7 @@ func newUntypedRequestBinder(parameters map[string]spec.Parameter, spec *spec.Sw } // Bind perform the databinding and validation -func (o *untypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, data interface{}) error { +func (o *UntypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer runtime.Consumer, data interface{}) error { val := reflect.Indirect(reflect.ValueOf(data)) isMap := val.Kind() == reflect.Map var result []error @@ -71,7 +72,6 @@ func (o *untypedRequestBinder) Bind(request *http.Request, routeParams RoutePara } } target = reflect.Indirect(reflect.New(tpe)) - } if !target.IsValid() { diff --git a/vendor/github.com/go-openapi/runtime/middleware/router.go b/vendor/github.com/go-openapi/runtime/middleware/router.go index 539d8471a8..02768bade2 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/router.go +++ b/vendor/github.com/go-openapi/runtime/middleware/router.go @@ -26,10 +26,11 @@ import ( "github.com/go-openapi/analysis" "github.com/go-openapi/errors" "github.com/go-openapi/loads" - "github.com/go-openapi/runtime" - "github.com/go-openapi/runtime/middleware/denco" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" + + "github.com/go-openapi/runtime" + "github.com/go-openapi/runtime/middleware/denco" ) // RouteParam is a object to capture route params in a framework agnostic way. @@ -290,7 +291,7 @@ type routeEntry struct { Parameters map[string]spec.Parameter Handler http.Handler Formats strfmt.Registry - Binder *untypedRequestBinder + Binder *UntypedRequestBinder Authenticators RouteAuthenticators Authorizer runtime.Authorizer } @@ -428,7 +429,7 @@ func (d *defaultRouteBuilder) AddRoute(method, path string, operation *spec.Oper Producers: d.api.ProducersFor(normalizeOffers(produces)), Parameters: parameters, Formats: d.api.Formats(), - Binder: newUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()), + Binder: NewUntypedRequestBinder(parameters, d.spec.Spec(), d.api.Formats()), Authenticators: d.buildAuthenticators(operation), Authorizer: d.api.Authorizer(), }) diff --git a/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go b/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go new file mode 100644 index 0000000000..2c92f5c91f --- /dev/null +++ b/vendor/github.com/go-openapi/runtime/middleware/swaggerui.go @@ -0,0 +1,162 @@ +package middleware + +import ( + "bytes" + "fmt" + "html/template" + "net/http" + "path" +) + +// SwaggerUIOpts configures the Swaggerui middlewares +type SwaggerUIOpts struct { + // BasePath for the UI path, defaults to: / + BasePath string + // Path combines with BasePath for the full UI path, defaults to: docs + Path string + // SpecURL the url to find the spec for + SpecURL string + + // The three components needed to embed swagger-ui + SwaggerURL string + SwaggerPresetURL string + SwaggerStylesURL string + + Favicon32 string + Favicon16 string + + // Title for the documentation site, default to: API documentation + Title string +} + +// EnsureDefaults in case some options are missing +func (r *SwaggerUIOpts) EnsureDefaults() { + if r.BasePath == "" { + r.BasePath = "/" + } + if r.Path == "" { + r.Path = "docs" + } + if r.SpecURL == "" { + r.SpecURL = "/swagger.json" + } + if r.SwaggerURL == "" { + r.SwaggerURL = swaggerLatest + } + if r.SwaggerPresetURL == "" { + r.SwaggerPresetURL = swaggerPresetLatest + } + if r.SwaggerStylesURL == "" { + r.SwaggerStylesURL = swaggerStylesLatest + } + if r.Favicon16 == "" { + r.Favicon16 = swaggerFavicon16Latest + } + if r.Favicon32 == "" { + r.Favicon32 = swaggerFavicon32Latest + } + if r.Title == "" { + r.Title = "API documentation" + } +} + +// SwaggerUI creates a middleware to serve a documentation site for a swagger spec. +// This allows for altering the spec before starting the http listener. +func SwaggerUI(opts SwaggerUIOpts, next http.Handler) http.Handler { + opts.EnsureDefaults() + + pth := path.Join(opts.BasePath, opts.Path) + tmpl := template.Must(template.New("swaggerui").Parse(swaggeruiTemplate)) + + buf := bytes.NewBuffer(nil) + _ = tmpl.Execute(buf, &opts) + b := buf.Bytes() + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if r.URL.Path == pth { + rw.Header().Set("Content-Type", "text/html; charset=utf-8") + rw.WriteHeader(http.StatusOK) + + _, _ = rw.Write(b) + return + } + + if next == nil { + rw.Header().Set("Content-Type", "text/plain") + rw.WriteHeader(http.StatusNotFound) + _, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth))) + return + } + next.ServeHTTP(rw, r) + }) +} + +const ( + swaggerLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js" + swaggerPresetLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui-standalone-preset.js" + swaggerStylesLatest = "https://unpkg.com/swagger-ui-dist/swagger-ui.css" + swaggerFavicon32Latest = "https://unpkg.com/swagger-ui-dist/favicon-32x32.png" + swaggerFavicon16Latest = "https://unpkg.com/swagger-ui-dist/favicon-16x16.png" + swaggeruiTemplate = ` +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <title>{{ .Title }}</title> + + <link rel="stylesheet" type="text/css" href="{{ .SwaggerStylesURL }}" > + <link rel="icon" type="image/png" href="{{ .Favicon32 }}" sizes="32x32" /> + <link rel="icon" type="image/png" href="{{ .Favicon16 }}" sizes="16x16" /> + <style> + html + { + box-sizing: border-box; + overflow: -moz-scrollbars-vertical; + overflow-y: scroll; + } + + *, + *:before, + *:after + { + box-sizing: inherit; + } + + body + { + margin:0; + background: #fafafa; + } + </style> + </head> + + <body> + <div id="swagger-ui"></div> + + <script src="{{ .SwaggerURL }}"> </script> + <script src="{{ .SwaggerPresetURL }}"> </script> + <script> + window.onload = function() { + // Begin Swagger UI call region + const ui = SwaggerUIBundle({ + url: '{{ .SpecURL }}', + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout" + }) + // End Swagger UI call region + + window.ui = ui + } + </script> + </body> +</html> +` +) diff --git a/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go b/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go index 8226b1ed1c..39a85f7d9e 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go +++ b/vendor/github.com/go-openapi/runtime/middleware/untyped/api.go @@ -23,9 +23,10 @@ import ( "github.com/go-openapi/analysis" "github.com/go-openapi/errors" "github.com/go-openapi/loads" - "github.com/go-openapi/runtime" "github.com/go-openapi/spec" "github.com/go-openapi/strfmt" + + "github.com/go-openapi/runtime" ) // NewAPI creates the default untyped API @@ -244,7 +245,6 @@ func (d *API) validate() error { } func (d *API) verify(name string, registrations []string, expectations []string) error { - sort.Strings(registrations) sort.Strings(expectations) diff --git a/vendor/github.com/go-openapi/runtime/middleware/validation.go b/vendor/github.com/go-openapi/runtime/middleware/validation.go index bb8df3cb3d..1f0135b578 100644 --- a/vendor/github.com/go-openapi/runtime/middleware/validation.go +++ b/vendor/github.com/go-openapi/runtime/middleware/validation.go @@ -20,8 +20,9 @@ import ( "strings" "github.com/go-openapi/errors" - "github.com/go-openapi/runtime" "github.com/go-openapi/swag" + + "github.com/go-openapi/runtime" ) type validation struct { @@ -115,7 +116,10 @@ func (v *validation) contentType() { } func (v *validation) responseFormat() { - if str, rCtx := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && runtime.HasBody(v.request) { + // if the route provides values for Produces and no format could be identify then return an error. + // if the route does not specify values for Produces then treat request as valid since the API designer + // choose not to specify the format for responses. + if str, rCtx := v.context.ResponseFormat(v.request, v.route.Produces); str == "" && len(v.route.Produces) > 0 { v.request = rCtx v.result = append(v.result, errors.InvalidResponseFormat(v.request.Header.Get(runtime.HeaderAccept), v.route.Produces)) } |