// Spec validates a spec document // It validates the spec json against the json schema for swagger // and then validates a number of extra rules that can't be expressed in json schema: // // - definition can't declare a property that's already defined by one of its ancestors // - definition's ancestor can't be a descendant of the same model // - each api path should be non-verbatim (account for path param names) unique per method // - each security reference should contain only unique scopes // - each security scope in a security definition should be unique // - each path parameter should correspond to a parameter placeholder and vice versa // - each referencable defintion must have references // - each definition property listed in the required array must be defined in the properties of the model // - each parameter should have a unique `name` and `type` combination // - each operation should have only 1 parameter of type body // - each reference must point to a valid object // - every default value that is specified must validate against the schema for that property // - items property is required for all schemas/definitions of type `array` func Spec(doc *spec.Document, formats strfmt.Registry) error { errs, _ /*warns*/ := validate.NewSpecValidator(doc.Schema(), formats).Validate(doc) if errs.HasErrors() { return errors.CompositeValidationError(errs.Errors...) } return nil }
func petAPIRouterBuilder(spec *spec.Document, api *untyped.API) *defaultRouteBuilder { builder := newDefaultRouteBuilder(spec, newRoutableUntypedAPI(spec, api, new(Context))) builder.AddRoute("GET", "/pets", spec.AllPaths()["/pets"].Get) builder.AddRoute("POST", "/pets", spec.AllPaths()["/pets"].Post) builder.AddRoute("DELETE", "/pets/{id}", spec.AllPaths()["/pets/{id}"].Delete) builder.AddRoute("GET", "/pets/{id}", spec.AllPaths()["/pets/{id}"].Get) return builder }
func makeCodegenModel(name, pkg string, schema spec.Schema, specDoc *spec.Document) *genModel { receiver := "m" props := make(map[string]genModelProperty) for pn, p := range schema.Properties { var required bool for _, v := range schema.Required { if v == pn { required = true break } } props[swag.ToJSONName(pn)] = makeGenModelProperty( "\""+pn+"\"", swag.ToJSONName(pn), swag.ToGoName(pn), receiver, "i", receiver+"."+swag.ToGoName(pn), p, required) } for _, p := range schema.AllOf { if p.Ref.GetURL() != nil { tn := filepath.Base(p.Ref.GetURL().Fragment) p = specDoc.Spec().Definitions[tn] } mod := makeCodegenModel(name, pkg, p, specDoc) if mod != nil { for _, prop := range mod.Properties { props[prop.ParamName] = prop } } } var properties []genModelProperty var hasValidations bool for _, v := range props { if v.HasValidations { hasValidations = v.HasValidations } properties = append(properties, v) } return &genModel{ Package: filepath.Base(pkg), ClassName: swag.ToGoName(name), Name: swag.ToJSONName(name), ReceiverName: receiver, Properties: properties, Description: schema.Description, DocString: modelDocString(swag.ToGoName(name), schema.Description), HumanClassName: swag.ToHumanNameLower(swag.ToGoName(name)), HasValidations: hasValidations, } }
func newRoutableUntypedAPI(spec *spec.Document, api *untyped.API, context *Context) *routableUntypedAPI { var handlers map[string]http.Handler if spec == nil || api == nil { return nil } for _, hls := range spec.Operations() { for _, op := range hls { schemes := spec.SecurityDefinitionsFor(op) if oh, ok := api.OperationHandlerFor(op.ID); ok { if handlers == nil { handlers = make(map[string]http.Handler) } handlers[op.ID] = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // lookup route info in the context route, _ := context.RouteInfo(r) // bind and validate the request using reflection bound, validation := context.BindAndValidate(r, route) if validation != nil { context.Respond(w, r, route.Produces, route, validation) return } // actually handle the request result, err := oh.Handle(bound) if err != nil { // respond with failure context.Respond(w, r, route.Produces, route, err) return } // respond with success context.Respond(w, r, route.Produces, route, result) }) if len(schemes) > 0 { handlers[op.ID] = newSecureAPI(context, handlers[op.ID]) } } } } return &routableUntypedAPI{ api: api, handlers: handlers, defaultProduces: api.DefaultProduces, defaultConsumes: api.DefaultConsumes, } }
// DefaultRouter creates a default implemenation of the router func DefaultRouter(spec *spec.Document, api RoutableAPI) Router { builder := newDefaultRouteBuilder(spec, api) if spec != nil { for method, paths := range spec.Operations() { for path, operation := range paths { builder.AddRoute(method, path, operation) } } } return builder.Build() }
// Validate validates the swagger spec func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) { var sd *spec.Document switch v := data.(type) { case *spec.Document: sd = v } if sd == nil { errs = sErr(errors.New(500, "spec validator can only validate spec.Document objects")) return } s.spec = sd errs = new(Result) warnings = new(Result) schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats) errs.Merge(schv.Validate(sd.Spec())) // error - if errs.HasErrors() { return // no point in continuing } errs.Merge(s.validateReferencesValid()) // error if errs.HasErrors() { return // no point in continuing } errs.Merge(s.validateParameters()) // error - errs.Merge(s.validateItems()) // error - errs.Merge(s.validateRequiredDefinitions()) // error - errs.Merge(s.validateDefaultValueValidAgainstSchema()) // error warnings.Merge(s.validateUniqueSecurityScopes()) // warning warnings.Merge(s.validateUniqueScopesSecurityDefinitions()) // warning warnings.Merge(s.validateReferenced()) // warning return }