diff options
Diffstat (limited to 'vendor/github.com/go-openapi/validate/spec.go')
-rw-r--r-- | vendor/github.com/go-openapi/validate/spec.go | 210 |
1 files changed, 114 insertions, 96 deletions
diff --git a/vendor/github.com/go-openapi/validate/spec.go b/vendor/github.com/go-openapi/validate/spec.go index 08ccd22fef..f30dd79e38 100644 --- a/vendor/github.com/go-openapi/validate/spec.go +++ b/vendor/github.com/go-openapi/validate/spec.go @@ -71,25 +71,22 @@ func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidat } // Validate validates the swagger spec -func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) { +func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) { var sd *loads.Document - errs = new(Result) + errs, warnings := new(Result), new(Result) - switch v := data.(type) { - case *loads.Document: + if v, ok := data.(*loads.Document); ok { sd = v } if sd == nil { errs.AddErrors(invalidDocumentMsg()) - return + return errs, warnings // no point in continuing } s.spec = sd s.analyzer = analysis.New(sd.Spec()) - warnings = new(Result) - // Swagger schema validator - schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats) + schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats, SwaggerSchema(true)) var obj interface{} // Raw spec unmarshalling errors @@ -109,13 +106,13 @@ func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Resu errs.Merge(schv.Validate(obj)) // error - // There may be a point in continuing to try and determine more accurate errors if !s.Options.ContinueOnErrors && errs.HasErrors() { - return // no point in continuing + return errs, warnings // no point in continuing } errs.Merge(s.validateReferencesValid()) // error - // There may be a point in continuing to try and determine more accurate errors if !s.Options.ContinueOnErrors && errs.HasErrors() { - return // no point in continuing + return errs, warnings // no point in continuing } errs.Merge(s.validateDuplicateOperationIDs()) @@ -129,7 +126,7 @@ func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Resu // There may be a point in continuing to try and determine more accurate errors if !s.Options.ContinueOnErrors && errs.HasErrors() { - return // no point in continuing + return errs, warnings // no point in continuing } // Values provided as default MUST validate their schema @@ -147,7 +144,7 @@ func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Resu //errs.Merge(s.validateRefNoSibling()) // warning only errs.Merge(s.validateReferenced()) // warning only - return + return errs, warnings } func (s *SpecValidator) validateNonEmptyPathParamNames() *Result { @@ -172,9 +169,17 @@ func (s *SpecValidator) validateNonEmptyPathParamNames() *Result { func (s *SpecValidator) validateDuplicateOperationIDs() *Result { // OperationID, if specified, must be unique across the board + var analyzer *analysis.Spec + if s.expanded != nil { + // $ref are valid: we can analyze operations on an expanded spec + analyzer = analysis.New(s.expanded.Spec()) + } else { + // fallback on possible incomplete picture because of previous errors + analyzer = s.analyzer + } res := new(Result) known := make(map[string]int) - for _, v := range s.analyzer.OperationIDs() { + for _, v := range analyzer.OperationIDs() { if v != "" { known[v]++ } @@ -336,14 +341,14 @@ func (s *SpecValidator) validateItems() *Result { for path, op := range pi { for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { - if param.TypeName() == "array" && param.ItemsTypeName() == "" { + if param.TypeName() == arrayType && param.ItemsTypeName() == "" { res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID)) continue } - if param.In != "body" { + if param.In != swaggerBody { if param.Items != nil { items := param.Items - for items.TypeName() == "array" { + for items.TypeName() == arrayType { if items.ItemsTypeName() == "" { res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID)) break @@ -374,7 +379,7 @@ func (s *SpecValidator) validateItems() *Result { for _, resp := range responses { // Response headers with array for hn, hv := range resp.Headers { - if hv.TypeName() == "array" && hv.ItemsTypeName() == "" { + if hv.TypeName() == arrayType && hv.ItemsTypeName() == "" { res.AddErrors(arrayInHeaderRequiresItemsMsg(hn, op.ID)) } } @@ -390,7 +395,7 @@ func (s *SpecValidator) validateItems() *Result { // Verifies constraints on array type func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) *Result { res := new(Result) - if !schema.Type.Contains("array") { + if !schema.Type.Contains(arrayType) { return res } @@ -451,6 +456,7 @@ func (s *SpecValidator) validateReferenced() *Result { return &res } +// nolint: dupl func (s *SpecValidator) validateReferencedParameters() *Result { // Each referenceable definition should have references. params := s.spec.Spec().Parameters @@ -463,9 +469,7 @@ func (s *SpecValidator) validateReferencedParameters() *Result { expected["#/parameters/"+jsonpointer.Escape(k)] = struct{}{} } for _, k := range s.analyzer.AllParameterReferences() { - if _, ok := expected[k]; ok { - delete(expected, k) - } + delete(expected, k) } if len(expected) == 0 { @@ -478,6 +482,7 @@ func (s *SpecValidator) validateReferencedParameters() *Result { return result } +// nolint: dupl func (s *SpecValidator) validateReferencedResponses() *Result { // Each referenceable definition should have references. responses := s.spec.Spec().Responses @@ -490,9 +495,7 @@ func (s *SpecValidator) validateReferencedResponses() *Result { expected["#/responses/"+jsonpointer.Escape(k)] = struct{}{} } for _, k := range s.analyzer.AllResponseReferences() { - if _, ok := expected[k]; ok { - delete(expected, k) - } + delete(expected, k) } if len(expected) == 0 { @@ -505,6 +508,7 @@ func (s *SpecValidator) validateReferencedResponses() *Result { return result } +// nolint: dupl func (s *SpecValidator) validateReferencedDefinitions() *Result { // Each referenceable definition must have references. defs := s.spec.Spec().Definitions @@ -517,9 +521,7 @@ func (s *SpecValidator) validateReferencedDefinitions() *Result { expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{} } for _, k := range s.analyzer.AllDefinitionReferences() { - if _, ok := expected[k]; ok { - delete(expected, k) - } + delete(expected, k) } if len(expected) == 0 { @@ -624,98 +626,114 @@ func (s *SpecValidator) validateParameters() *Result { rexGarbledPathSegment := mustCompileRegexp(`.*[{}\s]+.*`) for method, pi := range s.analyzer.Operations() { methodPaths := make(map[string]map[string]string) - if pi != nil { // Safeguard - for path, op := range pi { - pathToAdd := pathHelp.stripParametersInPath(path) + for path, op := range pi { + pathToAdd := pathHelp.stripParametersInPath(path) - // Warn on garbled path afer param stripping - if rexGarbledPathSegment.MatchString(pathToAdd) { - res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd)) - } + // Warn on garbled path afer param stripping + if rexGarbledPathSegment.MatchString(pathToAdd) { + res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd)) + } - // Check uniqueness of stripped paths - if _, found := methodPaths[method][pathToAdd]; found { + // Check uniqueness of stripped paths + if _, found := methodPaths[method][pathToAdd]; found { - // Sort names for stable, testable output - if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 { - res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd])) - } else { - res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path)) - } + // Sort names for stable, testable output + if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 { + res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd])) } else { - if _, found := methodPaths[method]; !found { - methodPaths[method] = map[string]string{} - } - methodPaths[method][pathToAdd] = path //Original non stripped path - + res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path)) } + } else { + if _, found := methodPaths[method]; !found { + methodPaths[method] = map[string]string{} + } + methodPaths[method][pathToAdd] = path //Original non stripped path - var bodyParams []string - var paramNames []string - var hasForm, hasBody bool + } - // Check parameters names uniqueness for operation - // TODO: should be done after param expansion - res.Merge(s.checkUniqueParams(path, method, op)) + var bodyParams []string + var paramNames []string + var hasForm, hasBody bool - for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { - // Validate pattern regexp for parameters with a Pattern property - if _, err := compileRegexp(pr.Pattern); err != nil { - res.AddErrors(invalidPatternInParamMsg(op.ID, pr.Name, pr.Pattern)) - } + // Check parameters names uniqueness for operation + // TODO: should be done after param expansion + res.Merge(s.checkUniqueParams(path, method, op)) - // There must be at most one parameter in body: list them all - if pr.In == "body" { - bodyParams = append(bodyParams, fmt.Sprintf("%q", pr.Name)) - hasBody = true - } + for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { + // Validate pattern regexp for parameters with a Pattern property + if _, err := compileRegexp(pr.Pattern); err != nil { + res.AddErrors(invalidPatternInParamMsg(op.ID, pr.Name, pr.Pattern)) + } - if pr.In == "path" { - paramNames = append(paramNames, pr.Name) - // Path declared in path must have the required: true property - if !pr.Required { - res.AddErrors(pathParamRequiredMsg(op.ID, pr.Name)) - } - } + // There must be at most one parameter in body: list them all + if pr.In == swaggerBody { + bodyParams = append(bodyParams, fmt.Sprintf("%q", pr.Name)) + hasBody = true + } - if pr.In == "formData" { - hasForm = true + if pr.In == "path" { + paramNames = append(paramNames, pr.Name) + // Path declared in path must have the required: true property + if !pr.Required { + res.AddErrors(pathParamRequiredMsg(op.ID, pr.Name)) } } - // In:formData and In:body are mutually exclusive - if hasBody && hasForm { - res.AddErrors(bothFormDataAndBodyMsg(op.ID)) + if pr.In == "formData" { + hasForm = true } - // There must be at most one body param - // Accurately report situations when more than 1 body param is declared (possibly unnamed) - if len(bodyParams) > 1 { - sort.Strings(bodyParams) - res.AddErrors(multipleBodyParamMsg(op.ID, bodyParams)) + + if !(pr.Type == numberType || pr.Type == integerType) && + (pr.Maximum != nil || pr.Minimum != nil || pr.MultipleOf != nil) { + // A non-numeric parameter has validation keywords for numeric instances (number and integer) + res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type)) } - // Check uniqueness of parameters in path - paramsInPath := pathHelp.extractPathParams(path) - for i, p := range paramsInPath { - for j, q := range paramsInPath { - if p == q && i > j { - res.AddErrors(pathParamNotUniqueMsg(path, p, q)) - break - } - } + if !(pr.Type == stringType) && + // A non-string parameter has validation keywords for strings + (pr.MaxLength != nil || pr.MinLength != nil || pr.Pattern != "") { + res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type)) + } + + if !(pr.Type == arrayType) && + // A non-array parameter has validation keywords for arrays + (pr.MaxItems != nil || pr.MinItems != nil || pr.UniqueItems) { + res.AddWarnings(parameterValidationTypeMismatchMsg(pr.Name, path, pr.Type)) } + } + + // In:formData and In:body are mutually exclusive + if hasBody && hasForm { + res.AddErrors(bothFormDataAndBodyMsg(op.ID)) + } + // There must be at most one body param + // Accurately report situations when more than 1 body param is declared (possibly unnamed) + if len(bodyParams) > 1 { + sort.Strings(bodyParams) + res.AddErrors(multipleBodyParamMsg(op.ID, bodyParams)) + } - // Warns about possible malformed params in path - rexGarbledParam := mustCompileRegexp(`{.*[{}\s]+.*}`) - for _, p := range paramsInPath { - if rexGarbledParam.MatchString(p) { - res.AddWarnings(pathParamGarbledMsg(path, p)) + // Check uniqueness of parameters in path + paramsInPath := pathHelp.extractPathParams(path) + for i, p := range paramsInPath { + for j, q := range paramsInPath { + if p == q && i > j { + res.AddErrors(pathParamNotUniqueMsg(path, p, q)) + break } } + } - // Match params from path vs params from params section - res.Merge(s.validatePathParamPresence(path, paramsInPath, paramNames)) + // Warns about possible malformed params in path + rexGarbledParam := mustCompileRegexp(`{.*[{}\s]+.*}`) + for _, p := range paramsInPath { + if rexGarbledParam.MatchString(p) { + res.AddWarnings(pathParamGarbledMsg(path, p)) + } } + + // Match params from path vs params from params section + res.Merge(s.validatePathParamPresence(path, paramsInPath, paramNames)) } } return res |