func (s *SpecValidator) validateDuplicatePropertyNames() *Result { // definition can't declare a property that's already defined by one of its ancestors res := new(Result) for k, sch := range s.spec.Spec().Definitions { if len(sch.AllOf) == 0 { continue } knownanc := map[string]struct{}{ "#/definitions/" + k: struct{}{}, } ancs := s.validateCircularAncestry(k, sch, knownanc) if len(ancs) > 0 { res.AddErrors(errors.New(422, "definition %q has circular ancestry: %v", k, ancs)) return res } knowns := make(map[string]struct{}) dups := s.validateSchemaPropertyNames(k, sch, knowns) if len(dups) > 0 { var pns []string for _, v := range dups { pns = append(pns, v.Definition+"."+v.Name) } res.AddErrors(errors.New(422, "definition %q contains duplicate properties: %v", k, pns)) } } return res }
func (s *SpecValidator) validatePathParamPresence(path string, fromPath, fromOperation []string) *Result { // Each defined operation path parameters must correspond to a named element in the API's path pattern. // (For example, you cannot have a path parameter named id for the following path /pets/{petId} but you must have a path parameter named petId.) res := new(Result) for _, l := range fromPath { var matched bool for _, r := range fromOperation { if l == "{"+r+"}" { matched = true break } } if !matched { res.Errors = append(res.Errors, errors.New(422, "path param %q has no parameter definition", l)) } } for _, p := range fromOperation { var matched bool for _, r := range fromPath { if "{"+p+"}" == r { matched = true break } } if !matched { res.AddErrors(errors.New(422, "path param %q is not present in path %q", p, path)) } } return res }
func (s *SpecValidator) validateParameters() *Result { // each parameter should have a unique `name` and `type` combination // each operation should have only 1 parameter of type body // each api path should be non-verbatim (account for path param names) unique per method res := new(Result) for method, pi := range s.spec.Operations() { knownPaths := make(map[string]string) for path, op := range pi { segments, params := parsePath(path) knowns := make([]string, 0, len(segments)) for _, s := range segments { knowns = append(knowns, s) } var fromPath []string for _, i := range params { fromPath = append(fromPath, knowns[i]) knowns[i] = "!" } knownPath := strings.Join(knowns, "/") if orig, ok := knownPaths[knownPath]; ok { res.AddErrors(errors.New(422, "path %s overlaps with %s", path, orig)) } else { knownPaths[knownPath] = path } ptypes := make(map[string]map[string]struct{}) var firstBodyParam string var paramNames []string for _, pr := range op.Parameters { pnames, ok := ptypes[pr.In] if !ok { pnames = make(map[string]struct{}) ptypes[pr.In] = pnames } _, ok = pnames[pr.Name] if ok { res.AddErrors(errors.New(422, "duplicate parameter name %q for %q in operation %q", pr.Name, pr.In, op.ID)) } pnames[pr.Name] = struct{}{} } for _, pr := range s.spec.ParamsFor(method, path) { if pr.In == "body" { if firstBodyParam != "" { res.AddErrors(errors.New(422, "operation %q has more than 1 body param (accepted: %q, dropped: %q)", op.ID, firstBodyParam, pr.Name)) } firstBodyParam = pr.Name } if pr.In == "path" { paramNames = append(paramNames, pr.Name) } } res.Merge(s.validatePathParamPresence(fromPath, paramNames)) } } return res }
// Respond renders the response after doing some content negotiation func (c *Context) Respond(rw http.ResponseWriter, r *http.Request, produces []string, route *MatchedRoute, data interface{}) { offers := []string{c.api.DefaultProduces()} for _, mt := range produces { if mt != c.api.DefaultProduces() { offers = append(offers, mt) } } format := c.ResponseFormat(r, offers) rw.Header().Set(httpkit.HeaderContentType, format) if err, ok := data.(error); ok { if format == "" { rw.Header().Set(httpkit.HeaderContentType, httpkit.JSONMime) } if route == nil || route.Operation == nil { c.api.ServeErrorFor("")(rw, r, err) return } c.api.ServeErrorFor(route.Operation.ID)(rw, r, err) return } if route == nil || route.Operation == nil { rw.WriteHeader(200) if r.Method == "HEAD" { return } producers := c.api.ProducersFor(offers) prod, ok := producers[format] if !ok { panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format)) } if err := prod.Produce(rw, data); err != nil { panic(err) // let the recovery middleware deal with this } return } if _, code, ok := route.Operation.SuccessResponse(); ok { rw.WriteHeader(code) if code == 201 || code == 204 || r.Method == "HEAD" { return } producers := route.Producers prod, ok := producers[format] if !ok { panic(errors.New(http.StatusInternalServerError, "can't find a producer for "+format)) } if err := prod.Produce(rw, data); err != nil { panic(err) // let the recovery middleware deal with this } return } c.api.ServeErrorFor(route.Operation.ID)(rw, r, errors.New(http.StatusInternalServerError, "can't produce response")) }
func (s *SpecValidator) validateItems() *Result { // validate parameter, items, schema and response objects for presence of item if type is array res := new(Result) // TODO: implement support for lookups of refs for method, pi := range s.spec.Operations() { for path, op := range pi { for _, param := range s.spec.ParamsFor(method, path) { if param.TypeName() == "array" && param.ItemsTypeName() == "" { res.AddErrors(errors.New(422, "param %q for %q is a collection without an element type", param.Name, op.ID)) continue } if param.In != "body" { if param.Items != nil { items := param.Items for items.TypeName() == "array" { if items.ItemsTypeName() == "" { res.AddErrors(errors.New(422, "param %q for %q is a collection without an element type", param.Name, op.ID)) break } items = items.Items } } } else { if err := s.validateSchemaItems(*param.Schema, fmt.Sprintf("body param %q", param.Name), op.ID); err != nil { res.AddErrors(err) } } } var responses []spec.Response if op.Responses != nil { if op.Responses.Default != nil { responses = append(responses, *op.Responses.Default) } for _, v := range op.Responses.StatusCodeResponses { responses = append(responses, v) } } for _, resp := range responses { for hn, hv := range resp.Headers { if hv.TypeName() == "array" && hv.ItemsTypeName() == "" { res.AddErrors(errors.New(422, "header %q for %q is a collection without an element type", hn, op.ID)) } } if resp.Schema != nil { if err := s.validateSchemaItems(*resp.Schema, "response body", op.ID); err != nil { res.AddErrors(err) } } } } } return res }
// APIKeyAuth creates an authenticator that uses a token for authorization. // This token can be obtained from either a header or a query string func APIKeyAuth(name, in string, authenticate TokenAuthentication) httpkit.Authenticator { inl := strings.ToLower(in) if inl != "query" && inl != "header" { // panic because this is most likely a typo panic(errors.New(500, "api key auth: in value needs to be either \"query\" or \"header\".")) } var getToken func(*http.Request) string switch inl { case "header": getToken = func(r *http.Request) string { return r.Header.Get(name) } case "query": getToken = func(r *http.Request) string { return r.URL.Query().Get(name) } } return httpAuthenticator(func(r *http.Request) (bool, interface{}, error) { token := getToken(r) if token == "" { return false, nil, nil } p, err := authenticate(token) return true, p, err }) }
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls func (o *UploadTaskFileParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error if err := r.ParseMultipartForm(32 << 20); err != nil { if err != http.ErrNotMultipart { return err } else if err := r.ParseForm(); err != nil { return err } } fds := httpkit.Values(r.Form) fdDescription, fdhkDescription, _ := fds.GetOK("description") if err := o.bindDescription(fdDescription, fdhkDescription, route.Formats); err != nil { res = append(res, err) } file, fileHeader, err := r.FormFile("file") if err != nil { res = append(res, errors.New(400, "reading file %q failed: %v", "file", err)) } else { o.File = httpkit.File{Data: file, Header: fileHeader} } rID, rhkID, _ := route.Params.GetOK("id") if err := o.bindID(rID, rhkID, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil }
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls func (o *UploadFileParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error if err := r.ParseMultipartForm(32 << 20); err != nil { return err } fds := httpkit.Values(r.Form) fdAdditionalMetadata, fdhkAdditionalMetadata, _ := fds.GetOK("additionalMetadata") if err := o.bindAdditionalMetadata(fdAdditionalMetadata, fdhkAdditionalMetadata, route.Formats); err != nil { res = append(res, err) } file, fileHeader, err := r.FormFile("file") if err != nil { res = append(res, errors.New(400, "reading file %q failed: %v", "file", err)) } else { o.File = httpkit.File{Data: file, Header: fileHeader} } rPetID, rhkPetID, _ := route.Params.GetOK("petId") if err := o.bindPetID(rPetID, rhkPetID, route.Formats); err != nil { res = append(res, err) } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil }
func (s *SpecValidator) validateRequiredDefinitions() *Result { // Each definition property listed in the required array must be defined in the properties of the model res := new(Result) for d, v := range s.spec.Spec().Definitions { REQUIRED: for _, pn := range v.Required { if _, ok := v.Properties[pn]; ok { continue } for pp := range v.PatternProperties { re := regexp.MustCompile(pp) if re.MatchString(pn) { continue REQUIRED } } if v.AdditionalProperties != nil { if v.AdditionalProperties.Allows { continue } if v.AdditionalProperties.Schema != nil { continue } } res.AddErrors(errors.New(422, "%q is present in required but not defined as property in defintion %q", pn, d)) } } return res }
func (s *SpecValidator) validateReferencedDefinitions() *Result { // Each referenceable definition must have references. defs := s.spec.Spec().Definitions if len(defs) == 0 { return nil } expected := make(map[string]struct{}) for k := range defs { expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{} } for _, k := range s.spec.AllDefinitionReferences() { if _, ok := expected[k]; ok { delete(expected, k) } } if len(expected) == 0 { return nil } var result Result for k := range expected { result.AddErrors(errors.New(422, "definition %q is not used anywhere", k)) } return &result }
func (s *SpecValidator) validateNonEmptyPathParamNames() *Result { res := new(Result) for k := range s.spec.Spec().Paths.Paths { if strings.Contains(k, "{}") { res.AddErrors(errors.New(422, "%q contains an empty path parameter", k)) } } return res }
func (s *schemaSliceValidator) Validate(data interface{}) *Result { result := new(Result) if data == nil { return result } val := reflect.ValueOf(data) size := val.Len() if s.Items != nil && s.Items.Schema != nil { validator := NewSchemaValidator(s.Items.Schema, s.Root, s.Path, s.KnownFormats) for i := 0; i < size; i++ { validator.SetPath(fmt.Sprintf("%s.%d", s.Path, i)) value := val.Index(i) result.Merge(validator.Validate(value.Interface())) } } itemsSize := int64(0) if s.Items != nil && len(s.Items.Schemas) > 0 { itemsSize = int64(len(s.Items.Schemas)) for i := int64(0); i < itemsSize; i++ { validator := NewSchemaValidator(&s.Items.Schemas[i], s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats) result.Merge(validator.Validate(val.Index(int(i)).Interface())) } } if s.AdditionalItems != nil && itemsSize < int64(size) { if s.Items != nil && len(s.Items.Schemas) > 0 && !s.AdditionalItems.Allows { result.AddErrors(errors.New(422, "array doesn't allow for additional items")) } if s.AdditionalItems.Schema != nil { for i := itemsSize; i < (int64(size)-itemsSize)+1; i++ { validator := NewSchemaValidator(s.AdditionalItems.Schema, s.Root, fmt.Sprintf("%s.%d", s.Path, i), s.KnownFormats) result.Merge(validator.Validate(val.Index(int(i)).Interface())) } } } if s.MinItems != nil { if err := validate.MinItems(s.Path, s.In, int64(size), *s.MinItems); err != nil { result.AddErrors(err) } } if s.MaxItems != nil { if err := validate.MaxItems(s.Path, s.In, int64(size), *s.MaxItems); err != nil { result.AddErrors(err) } } if s.UniqueItems { if err := validate.UniqueItems(s.Path, s.In, val.Interface()); err != nil { result.AddErrors(err) } } result.Inc() return result }
// Bind perform the databinding and validation func (o *untypedRequestBinder) Bind(request *http.Request, routeParams RouteParams, consumer httpkit.Consumer, data interface{}) error { val := reflect.Indirect(reflect.ValueOf(data)) isMap := val.Kind() == reflect.Map var result []error for fieldName, param := range o.Parameters { binder := o.paramBinders[fieldName] var target reflect.Value if !isMap { binder.Name = fieldName target = val.FieldByName(fieldName) } if isMap { tpe := binder.Type() if tpe == nil { if param.Schema.Type.Contains("array") { tpe = reflect.TypeOf([]interface{}{}) } else { tpe = reflect.TypeOf(map[string]interface{}{}) } } target = reflect.Indirect(reflect.New(tpe)) } if !target.IsValid() { result = append(result, errors.New(500, "parameter name %q is an unknown field", binder.Name)) continue } if err := binder.Bind(request, routeParams, consumer, target); err != nil { result = append(result, err) continue } if binder.validator != nil { rr := binder.validator.Validate(target.Interface()) if rr != nil && rr.HasErrors() { result = append(result, rr.AsError()) } } if isMap { val.SetMapIndex(reflect.ValueOf(param.Name), target) } } if len(result) > 0 { return errors.CompositeValidationError(result...) } return nil }
func addItem(item *models.Item) error { if item == nil { return errors.New(500, "item must be present") } itemsLock.Lock() defer itemsLock.Unlock() item.ID = newItemID() items[item.ID] = item return nil }
func (s *SpecValidator) validateDuplicateOperationIDs() *Result { res := new(Result) known := make(map[string]int) for _, v := range s.spec.OperationIDs() { if v != "" { known[v]++ } } for k, v := range known { if v > 1 { res.AddErrors(errors.New(422, "%q is defined %d times", k, v)) } } return res }
// 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) var obj interface{} if err := json.Unmarshal(sd.Raw(), &obj); err != nil { errs.AddErrors(err) return } errs.Merge(schv.Validate(obj)) // 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.validateDuplicateOperationIDs()) errs.Merge(s.validateDuplicatePropertyNames()) // error - errs.Merge(s.validateParameters()) // error - errs.Merge(s.validateItems()) // error - errs.Merge(s.validateRequiredDefinitions()) // error - errs.Merge(s.validateDefaultValueValidAgainstSchema()) // error - errs.Merge(s.validateExamplesValidAgainstSchema()) // error - errs.Merge(s.validateNonEmptyPathParamNames()) warnings.Merge(s.validateUniqueSecurityScopes()) // warning warnings.Merge(s.validateUniqueScopesSecurityDefinitions()) // warning warnings.Merge(s.validateReferenced()) // warning return }
func (s *SpecValidator) validateReferencesValid() *Result { // each reference must point to a valid object res := new(Result) for _, r := range s.spec.AllRefs() { if !r.IsValidURI() { res.AddErrors(errors.New(404, "invalid ref %q", r.String())) } } if !res.HasErrors() { exp, err := s.spec.Expanded() if err != nil { res.AddErrors(err) } s.expanded = exp } return res }
// BindValidRequest binds a params object to a request but only when the request is valid // 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 requestContentType := "*/*" // check and validate content type, select consumer if httpkit.HasBody(request) { ct, _, err := httpkit.ContentType(request.Header) if err != nil { res = append(res, err) } else { if err := validateContentType(route.Consumes, ct); err != nil { res = append(res, err) } if len(res) == 0 { cons, ok := route.Consumers[ct] if !ok { res = append(res, errors.New(500, "no consumer registered for %s", ct)) } else { route.Consumer = cons requestContentType = ct } } } } // check and validate the response format if len(res) == 0 && httpkit.HasBody(request) { if str := NegotiateContentType(request, route.Produces, requestContentType); str == "" { res = append(res, errors.InvalidResponseFormat(request.Header.Get(httpkit.HeaderAccept), route.Produces)) } } // now bind the request with the provided binder // it's assumed the binder will also validate the request and return an error if the // request is invalid if binder != nil && len(res) == 0 { if err := binder.BindRequest(request, route); err != nil { return err } } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil }
func updateItem(id int64, item *models.Item) error { if item == nil { return errors.New(500, "item must be present") } itemsLock.Lock() defer itemsLock.Unlock() _, exists := items[id] if !exists { return errors.NotFound("not found: item %d", id) } item.ID = id items[id] = item return nil }
func unmarshalPricingComponent(data []byte, consumer httpkit.Consumer) (PricingComponent, error) { buf := bytes.NewBuffer(data) buf2 := bytes.NewBuffer(data) // the first time this is read is to fetch the value of the @type property. var getType struct { Type string `json:"@type"` } if err := consumer.Consume(buf, &getType); err != nil { return nil, err } if err := validate.RequiredString("@type", "body", getType.Type); err != nil { return nil, err } // The value of @type is used to determine which type to create and unmarshal the data into switch getType.Type { case "flatPricingComponent": var result FlatPricingComponent if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "tieredPricingComponent": var result TieredPricingComponent if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil case "tieredVolumePricingComponent": var result TieredVolumePricingComponent if err := consumer.Consume(buf2, &result); err != nil { return nil, err } return &result, nil } return nil, errors.New(422, "invalid @type value: %q", getType.Type) }
func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) error { if !schema.Type.Contains("array") { return nil } if schema.Items == nil || schema.Items.Len() == 0 { return errors.New(422, "%s for %q is a collection without an element type", prefix, opID) } schemas := schema.Items.Schemas if schema.Items.Schema != nil { schemas = []spec.Schema{*schema.Items.Schema} } for _, sch := range schemas { if err := s.validateSchemaItems(sch, prefix, opID); err != nil { return err } } return nil }
func (v *validation) contentType() { if len(v.result) == 0 && httpkit.HasBody(v.request) { ct, _, err := v.context.ContentType(v.request) if err != nil { v.result = append(v.result, err) } if len(v.result) == 0 { if err := validateContentType(v.route.Consumes, ct); err != nil { v.result = append(v.result, err) } } if ct != "" && v.route.Consumer == nil { cons, ok := v.route.Consumers[ct] if !ok { v.result = append(v.result, errors.New(500, "no consumer registered for %s", ct)) } else { v.route.Consumer = cons } } } }
// 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 }
func TestOperationExecutor(t *testing.T) { spec, api := petstore.NewAPI(t) api.RegisterOperation("get", "/pets", httpkit.OperationHandlerFunc(func(params interface{}) (interface{}, error) { return []interface{}{ map[string]interface{}{"id": 1, "name": "a dog"}, }, nil })) context := NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw := newOperationExecutor(context) recorder := httptest.NewRecorder() request, _ := http.NewRequest("GET", "/pets", nil) request.Header.Add("Accept", "application/json") request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 200, recorder.Code) assert.Equal(t, `[{"id":1,"name":"a dog"}]`+"\n", recorder.Body.String()) spec, api = petstore.NewAPI(t) api.RegisterOperation("get", "/pets", httpkit.OperationHandlerFunc(func(params interface{}) (interface{}, error) { return nil, errors.New(422, "expected") })) context = NewContext(spec, api, nil) context.router = DefaultRouter(spec, context.api) mw = newOperationExecutor(context) recorder = httptest.NewRecorder() request, _ = http.NewRequest("GET", "/pets", nil) request.Header.Add("Accept", "application/json") request.SetBasicAuth("admin", "admin") mw.ServeHTTP(recorder, request) assert.Equal(t, 422, recorder.Code) assert.Equal(t, `{"code":422,"message":"expected"}`, recorder.Body.String()) }
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface // for simple values it will use straight method calls func (o *AddScreenshotParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error { var res []error if err := r.ParseMultipartForm(32 << 20); err != nil { return err } //fds := httpkit.Values(r.Form) rID, rhkID, _ := route.Params.GetOK("id") if err := o.bindID(rID, rhkID, route.Formats); err != nil { res = append(res, err) } screenshot, screenshotHeader, err := r.FormFile("screenshot") if err != nil { res = append(res, errors.New(400, "reading file %q failed: %v", "screenshot", err)) } else { o.Screenshot = httpkit.File{Data: screenshot, Header: screenshotHeader} } if len(res) > 0 { return errors.CompositeValidationError(res...) } return nil }
func (p *untypedParamBinder) Bind(request *http.Request, routeParams RouteParams, consumer httpkit.Consumer, target reflect.Value) error { // fmt.Println("binding", p.name, "as", p.Type()) switch p.parameter.In { case "query": data, custom, err := p.readValue(request.URL.Query(), target) if err != nil { return err } if custom { return nil } return p.bindValue(data, target) case "header": data, custom, err := p.readValue(request.Header, target) if err != nil { return err } if custom { return nil } return p.bindValue(data, target) case "path": data, custom, err := p.readValue(routeParams, target) if err != nil { return err } if custom { return nil } return p.bindValue(data, target) case "formData": var err error var mt string mt, _, e := httpkit.ContentType(request.Header) if e != nil { // because of the interface conversion go thinks the error is not nil // so we first check for nil and then set the err var if it's not nil err = e } if err != nil { return errors.InvalidContentType("", []string{"multipart/form-data", "application/x-www-form-urlencoded"}) } if mt != "multipart/form-data" && mt != "application/x-www-form-urlencoded" { return errors.InvalidContentType(mt, []string{"multipart/form-data", "application/x-www-form-urlencoded"}) } if mt == "multipart/form-data" { if err := request.ParseMultipartForm(defaultMaxMemory); err != nil { return errors.NewParseError(p.Name, p.parameter.In, "", err) } } if err := request.ParseForm(); err != nil { return errors.NewParseError(p.Name, p.parameter.In, "", err) } if p.parameter.Type == "file" { file, header, err := request.FormFile(p.parameter.Name) if err != nil { return errors.NewParseError(p.Name, p.parameter.In, "", err) } target.Set(reflect.ValueOf(httpkit.File{Data: file, Header: header})) return nil } if request.MultipartForm != nil { data, custom, err := p.readValue(url.Values(request.MultipartForm.Value), target) if err != nil { return err } if custom { return nil } return p.bindValue(data, target) } data, custom, err := p.readValue(url.Values(request.PostForm), target) if err != nil { return err } if custom { return nil } return p.bindValue(data, target) case "body": newValue := reflect.New(target.Type()) if err := consumer.Consume(request.Body, newValue.Interface()); err != nil { if err == io.EOF && p.parameter.Default != nil { target.Set(reflect.ValueOf(p.parameter.Default)) return nil } tpe := p.parameter.Type if p.parameter.Format != "" { tpe = p.parameter.Format } return errors.InvalidType(p.Name, p.parameter.In, tpe, nil) } target.Set(reflect.Indirect(newValue)) return nil default: return errors.New(500, fmt.Sprintf("invalid parameter location %q", p.parameter.In)) } }
func (s *SpecValidator) validateDefaultValueValidAgainstSchema() *Result { // every default value that is specified must validate against the schema for that property // headers, items, parameters, schema res := new(Result) for method, pathItem := range s.spec.Operations() { for path, op := range pathItem { // parameters var hasForm, hasBody bool PARAMETERS: for _, pr := range s.spec.ParamsFor(method, path) { // expand ref is necessary param := pr for param.Ref.String() != "" { obj, _, err := param.Ref.GetPointer().Get(s.spec.Spec()) if err != nil { res.AddErrors(err) break PARAMETERS } param = obj.(spec.Parameter) } if param.In == "formData" { if hasBody && !hasForm { res.AddErrors(errors.New(422, "operation %q has both formData and body parameters", op.ID)) } hasForm = true } if param.In == "body" { if hasForm && !hasBody { res.AddErrors(errors.New(422, "operation %q has both body and formData parameters", op.ID)) } hasBody = true } // check simple paramters first if param.Default != nil && param.Schema == nil { //fmt.Println(param.Name, "in", param.In, "has a default without a schema") // check param valid res.Merge(NewParamValidator(¶m, s.KnownFormats).Validate(param.Default)) } if param.Items != nil { res.Merge(s.validateDefaultValueItemsAgainstSchema(param.Name, param.In, ¶m, param.Items)) } if param.Schema != nil { res.Merge(s.validateDefaultValueSchemaAgainstSchema(param.Name, param.In, param.Schema)) } } if op.Responses.Default != nil { dr := op.Responses.Default for nm, h := range dr.Headers { if h.Default != nil { res.Merge(NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default)) } if h.Items != nil { res.Merge(s.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items)) } } } for _, r := range op.Responses.StatusCodeResponses { for nm, h := range r.Headers { if h.Default != nil { res.Merge(NewHeaderValidator(nm, &h, s.KnownFormats).Validate(h.Default)) } if h.Items != nil { res.Merge(s.validateDefaultValueItemsAgainstSchema(nm, "header", &h, h.Items)) } } } } } for nm, sch := range s.spec.Spec().Definitions { res.Merge(s.validateDefaultValueSchemaAgainstSchema(fmt.Sprintf("definitions.%s", nm), "body", &sch)) } return res }
func addLearningResource(lr *models.LearningResource) error { if lr == nil { return errors.New(500, "lr must be present") } return db.CreateLearningResource(lr) }
func (s *schemaPropsValidator) Validate(data interface{}) *Result { mainResult := new(Result) if len(s.anyOfValidators) > 0 { var bestFailures *Result succeededOnce := false for _, anyOfSchema := range s.anyOfValidators { result := anyOfSchema.Validate(data) if result.IsValid() { bestFailures = nil succeededOnce = true break } if bestFailures == nil || result.MatchCount > bestFailures.MatchCount { bestFailures = result } } if !succeededOnce { mainResult.AddErrors(errors.New(422, "must validate at least one schema (anyOf)")) } if bestFailures != nil { mainResult.Merge(bestFailures) } } if len(s.oneOfValidators) > 0 { var bestFailures *Result validated := 0 for _, oneOfSchema := range s.oneOfValidators { result := oneOfSchema.Validate(data) if result.IsValid() { validated++ bestFailures = nil continue } if validated == 0 && (bestFailures == nil || result.MatchCount > bestFailures.MatchCount) { bestFailures = result } } if validated != 1 { mainResult.AddErrors(errors.New(422, "must validate one and only one schema (oneOf)")) if bestFailures != nil { mainResult.Merge(bestFailures) } } } if len(s.allOfValidators) > 0 { validated := 0 for _, allOfSchema := range s.allOfValidators { result := allOfSchema.Validate(data) if result.IsValid() { validated++ } mainResult.Merge(result) } if validated != len(s.allOfValidators) { mainResult.AddErrors(errors.New(422, "must validate all the schemas (allOf)")) } } if s.notValidator != nil { result := s.notValidator.Validate(data) if result.IsValid() { mainResult.AddErrors(errors.New(422, "must not validate the schema (not)")) } } if s.Dependencies != nil && len(s.Dependencies) > 0 && reflect.TypeOf(data).Kind() == reflect.Map { val := data.(map[string]interface{}) for key := range val { if dep, ok := s.Dependencies[key]; ok { if dep.Schema != nil { mainResult.Merge(NewSchemaValidator(dep.Schema, s.Root, s.Path+"."+key, s.KnownFormats).Validate(data)) continue } if len(dep.Property) > 0 { for _, depKey := range dep.Property { if _, ok := val[depKey]; !ok { mainResult.AddErrors(errors.New(422, "has a dependency on %s", depKey)) } } } } } } mainResult.Inc() return mainResult }