| // Copyright 2015 go-swagger maintainers |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package untyped |
| |
| import ( |
| "fmt" |
| "net/http" |
| "sort" |
| "strings" |
| |
| "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" |
| ) |
| |
| // NewAPI creates the default untyped API |
| func NewAPI(spec *loads.Document) *API { |
| var an *analysis.Spec |
| if spec != nil && spec.Spec() != nil { |
| an = analysis.New(spec.Spec()) |
| } |
| api := &API{ |
| spec: spec, |
| analyzer: an, |
| consumers: make(map[string]runtime.Consumer, 10), |
| producers: make(map[string]runtime.Producer, 10), |
| authenticators: make(map[string]runtime.Authenticator), |
| operations: make(map[string]map[string]runtime.OperationHandler), |
| ServeError: errors.ServeError, |
| Models: make(map[string]func() interface{}), |
| formats: strfmt.NewFormats(), |
| } |
| return api.WithJSONDefaults() |
| } |
| |
| // API represents an untyped mux for a swagger spec |
| type API struct { |
| spec *loads.Document |
| analyzer *analysis.Spec |
| DefaultProduces string |
| DefaultConsumes string |
| consumers map[string]runtime.Consumer |
| producers map[string]runtime.Producer |
| authenticators map[string]runtime.Authenticator |
| authorizer runtime.Authorizer |
| operations map[string]map[string]runtime.OperationHandler |
| ServeError func(http.ResponseWriter, *http.Request, error) |
| Models map[string]func() interface{} |
| formats strfmt.Registry |
| } |
| |
| // WithJSONDefaults loads the json defaults for this api |
| func (d *API) WithJSONDefaults() *API { |
| d.DefaultConsumes = runtime.JSONMime |
| d.DefaultProduces = runtime.JSONMime |
| d.consumers[runtime.JSONMime] = runtime.JSONConsumer() |
| d.producers[runtime.JSONMime] = runtime.JSONProducer() |
| return d |
| } |
| |
| // WithoutJSONDefaults clears the json defaults for this api |
| func (d *API) WithoutJSONDefaults() *API { |
| d.DefaultConsumes = "" |
| d.DefaultProduces = "" |
| delete(d.consumers, runtime.JSONMime) |
| delete(d.producers, runtime.JSONMime) |
| return d |
| } |
| |
| // Formats returns the registered string formats |
| func (d *API) Formats() strfmt.Registry { |
| if d.formats == nil { |
| d.formats = strfmt.NewFormats() |
| } |
| return d.formats |
| } |
| |
| // RegisterFormat registers a custom format validator |
| func (d *API) RegisterFormat(name string, format strfmt.Format, validator strfmt.Validator) { |
| if d.formats == nil { |
| d.formats = strfmt.NewFormats() |
| } |
| d.formats.Add(name, format, validator) |
| } |
| |
| // RegisterAuth registers an auth handler in this api |
| func (d *API) RegisterAuth(scheme string, handler runtime.Authenticator) { |
| if d.authenticators == nil { |
| d.authenticators = make(map[string]runtime.Authenticator) |
| } |
| d.authenticators[scheme] = handler |
| } |
| |
| // RegisterAuthorizer registers an authorizer handler in this api |
| func (d *API) RegisterAuthorizer(handler runtime.Authorizer) { |
| d.authorizer = handler |
| } |
| |
| // RegisterConsumer registers a consumer for a media type. |
| func (d *API) RegisterConsumer(mediaType string, handler runtime.Consumer) { |
| if d.consumers == nil { |
| d.consumers = make(map[string]runtime.Consumer, 10) |
| } |
| d.consumers[strings.ToLower(mediaType)] = handler |
| } |
| |
| // RegisterProducer registers a producer for a media type |
| func (d *API) RegisterProducer(mediaType string, handler runtime.Producer) { |
| if d.producers == nil { |
| d.producers = make(map[string]runtime.Producer, 10) |
| } |
| d.producers[strings.ToLower(mediaType)] = handler |
| } |
| |
| // RegisterOperation registers an operation handler for an operation name |
| func (d *API) RegisterOperation(method, path string, handler runtime.OperationHandler) { |
| if d.operations == nil { |
| d.operations = make(map[string]map[string]runtime.OperationHandler, 30) |
| } |
| um := strings.ToUpper(method) |
| if b, ok := d.operations[um]; !ok || b == nil { |
| d.operations[um] = make(map[string]runtime.OperationHandler) |
| } |
| d.operations[um][path] = handler |
| } |
| |
| // OperationHandlerFor returns the operation handler for the specified id if it can be found |
| func (d *API) OperationHandlerFor(method, path string) (runtime.OperationHandler, bool) { |
| if d.operations == nil { |
| return nil, false |
| } |
| if pi, ok := d.operations[strings.ToUpper(method)]; ok { |
| h, ok := pi[path] |
| return h, ok |
| } |
| return nil, false |
| } |
| |
| // ConsumersFor gets the consumers for the specified media types |
| func (d *API) ConsumersFor(mediaTypes []string) map[string]runtime.Consumer { |
| result := make(map[string]runtime.Consumer) |
| for _, mt := range mediaTypes { |
| if consumer, ok := d.consumers[mt]; ok { |
| result[mt] = consumer |
| } |
| } |
| return result |
| } |
| |
| // ProducersFor gets the producers for the specified media types |
| func (d *API) ProducersFor(mediaTypes []string) map[string]runtime.Producer { |
| result := make(map[string]runtime.Producer) |
| for _, mt := range mediaTypes { |
| if producer, ok := d.producers[mt]; ok { |
| result[mt] = producer |
| } |
| } |
| return result |
| } |
| |
| // AuthenticatorsFor gets the authenticators for the specified security schemes |
| func (d *API) AuthenticatorsFor(schemes map[string]spec.SecurityScheme) map[string]runtime.Authenticator { |
| result := make(map[string]runtime.Authenticator) |
| for k := range schemes { |
| if a, ok := d.authenticators[k]; ok { |
| result[k] = a |
| } |
| } |
| return result |
| } |
| |
| // AuthorizersFor returns the registered authorizer |
| func (d *API) Authorizer() runtime.Authorizer { |
| return d.authorizer |
| } |
| |
| // Validate validates this API for any missing items |
| func (d *API) Validate() error { |
| return d.validate() |
| } |
| |
| // validateWith validates the registrations in this API against the provided spec analyzer |
| func (d *API) validate() error { |
| var consumes []string |
| for k := range d.consumers { |
| consumes = append(consumes, k) |
| } |
| |
| var produces []string |
| for k := range d.producers { |
| produces = append(produces, k) |
| } |
| |
| var authenticators []string |
| for k := range d.authenticators { |
| authenticators = append(authenticators, k) |
| } |
| |
| var operations []string |
| for m, v := range d.operations { |
| for p := range v { |
| operations = append(operations, fmt.Sprintf("%s %s", strings.ToUpper(m), p)) |
| } |
| } |
| |
| var definedAuths []string |
| for k := range d.spec.Spec().SecurityDefinitions { |
| definedAuths = append(definedAuths, k) |
| } |
| |
| if err := d.verify("consumes", consumes, d.analyzer.RequiredConsumes()); err != nil { |
| return err |
| } |
| if err := d.verify("produces", produces, d.analyzer.RequiredProduces()); err != nil { |
| return err |
| } |
| if err := d.verify("operation", operations, d.analyzer.OperationMethodPaths()); err != nil { |
| return err |
| } |
| |
| requiredAuths := d.analyzer.RequiredSecuritySchemes() |
| if err := d.verify("auth scheme", authenticators, requiredAuths); err != nil { |
| return err |
| } |
| if err := d.verify("security definitions", definedAuths, requiredAuths); err != nil { |
| return err |
| } |
| return nil |
| } |
| |
| func (d *API) verify(name string, registrations []string, expectations []string) error { |
| |
| sort.Sort(sort.StringSlice(registrations)) |
| sort.Sort(sort.StringSlice(expectations)) |
| |
| expected := map[string]struct{}{} |
| seen := map[string]struct{}{} |
| |
| for _, v := range expectations { |
| expected[v] = struct{}{} |
| } |
| |
| var unspecified []string |
| for _, v := range registrations { |
| seen[v] = struct{}{} |
| if _, ok := expected[v]; !ok { |
| unspecified = append(unspecified, v) |
| } |
| } |
| |
| for k := range seen { |
| delete(expected, k) |
| } |
| |
| var unregistered []string |
| for k := range expected { |
| unregistered = append(unregistered, k) |
| } |
| sort.Sort(sort.StringSlice(unspecified)) |
| sort.Sort(sort.StringSlice(unregistered)) |
| |
| if len(unregistered) > 0 || len(unspecified) > 0 { |
| return &errors.APIVerificationFailed{ |
| Section: name, |
| MissingSpecification: unspecified, |
| MissingRegistration: unregistered, |
| } |
| } |
| |
| return nil |
| } |