diff options
author | 2025-03-09 17:47:56 +0100 | |
---|---|---|
committer | 2025-03-10 01:59:49 +0100 | |
commit | 3ac1ee16f377d31a0fb80c8dae28b6239ac4229e (patch) | |
tree | f61faa581feaaeaba2542b9f2b8234a590684413 /vendor/github.com/go-openapi/validate/spec.go | |
parent | [chore] update URLs to forked source (diff) | |
download | gotosocial-3ac1ee16f377d31a0fb80c8dae28b6239ac4229e.tar.xz |
[chore] remove vendor
Diffstat (limited to 'vendor/github.com/go-openapi/validate/spec.go')
-rw-r--r-- | vendor/github.com/go-openapi/validate/spec.go | 852 |
1 files changed, 0 insertions, 852 deletions
diff --git a/vendor/github.com/go-openapi/validate/spec.go b/vendor/github.com/go-openapi/validate/spec.go deleted file mode 100644 index 965452566..000000000 --- a/vendor/github.com/go-openapi/validate/spec.go +++ /dev/null @@ -1,852 +0,0 @@ -// 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 validate - -import ( - "bytes" - "encoding/gob" - "encoding/json" - "fmt" - "sort" - "strings" - - "github.com/go-openapi/analysis" - "github.com/go-openapi/errors" - "github.com/go-openapi/jsonpointer" - "github.com/go-openapi/loads" - "github.com/go-openapi/spec" - "github.com/go-openapi/strfmt" - "github.com/go-openapi/swag" -) - -// Spec validates an OpenAPI 2.0 specification document. -// -// Returns an error flattening in a single standard error, all validation messages. -// -// - TODO: $ref should not have siblings -// - TODO: make sure documentation reflects all checks and warnings -// - TODO: check on discriminators -// - TODO: explicit message on unsupported keywords (better than "forbidden property"...) -// - TODO: full list of unresolved refs -// - TODO: validate numeric constraints (issue#581): this should be handled like defaults and examples -// - TODO: option to determine if we validate for go-swagger or in a more general context -// - TODO: check on required properties to support anyOf, allOf, oneOf -// -// NOTE: SecurityScopes are maps: no need to check uniqueness -func Spec(doc *loads.Document, formats strfmt.Registry) error { - errs, _ /*warns*/ := NewSpecValidator(doc.Schema(), formats).Validate(doc) - if errs.HasErrors() { - return errors.CompositeValidationError(errs.Errors...) - } - return nil -} - -// SpecValidator validates a swagger 2.0 spec -type SpecValidator struct { - schema *spec.Schema // swagger 2.0 schema - spec *loads.Document - analyzer *analysis.Spec - expanded *loads.Document - KnownFormats strfmt.Registry - Options Opts // validation options - schemaOptions *SchemaValidatorOptions -} - -// NewSpecValidator creates a new swagger spec validator instance -func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidator { - // schema options that apply to all called validators - schemaOptions := new(SchemaValidatorOptions) - for _, o := range []Option{ - SwaggerSchema(true), - WithRecycleValidators(true), - // withRecycleResults(true), - } { - o(schemaOptions) - } - - return &SpecValidator{ - schema: schema, - KnownFormats: formats, - Options: defaultOpts, - schemaOptions: schemaOptions, - } -} - -// Validate validates the swagger spec -func (s *SpecValidator) Validate(data interface{}) (*Result, *Result) { - s.schemaOptions.skipSchemataResult = s.Options.SkipSchemataResult - var sd *loads.Document - errs, warnings := new(Result), new(Result) - - if v, ok := data.(*loads.Document); ok { - sd = v - } - if sd == nil { - errs.AddErrors(invalidDocumentMsg()) - return errs, warnings // no point in continuing - } - s.spec = sd - s.analyzer = analysis.New(sd.Spec()) - - // Raw spec unmarshalling errors - var obj interface{} - if err := json.Unmarshal(sd.Raw(), &obj); err != nil { - // NOTE: under normal conditions, the *load.Document has been already unmarshalled - // So this one is just a paranoid check on the behavior of the spec package - panic(InvalidDocumentError) - } - - defer func() { - // errs holds all errors and warnings, - // warnings only warnings - errs.MergeAsWarnings(warnings) - warnings.AddErrors(errs.Warnings...) - }() - - // Swagger schema validator - schv := newSchemaValidator(s.schema, nil, "", s.KnownFormats, s.schemaOptions) - 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 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 errs, warnings // no point in continuing - } - - errs.Merge(s.validateDuplicateOperationIDs()) - errs.Merge(s.validateDuplicatePropertyNames()) // error - - errs.Merge(s.validateParameters()) // error - - errs.Merge(s.validateItems()) // error - - - // Properties in required definition MUST validate their schema - // Properties SHOULD NOT be declared as both required and readOnly (warning) - errs.Merge(s.validateRequiredDefinitions()) // error and warning - - // There may be a point in continuing to try and determine more accurate errors - if !s.Options.ContinueOnErrors && errs.HasErrors() { - return errs, warnings // no point in continuing - } - - // Values provided as default MUST validate their schema - df := &defaultValidator{SpecValidator: s, schemaOptions: s.schemaOptions} - errs.Merge(df.Validate()) - - // Values provided as examples MUST validate their schema - // Value provided as examples in a response without schema generate a warning - // Known limitations: examples in responses for mime type not application/json are ignored (warning) - ex := &exampleValidator{SpecValidator: s, schemaOptions: s.schemaOptions} - errs.Merge(ex.Validate()) - - errs.Merge(s.validateNonEmptyPathParamNames()) - - // errs.Merge(s.validateRefNoSibling()) // warning only - errs.Merge(s.validateReferenced()) // warning only - - return errs, warnings -} - -func (s *SpecValidator) validateNonEmptyPathParamNames() *Result { - res := pools.poolOfResults.BorrowResult() - if s.spec.Spec().Paths == nil { - // There is no Paths object: error - res.AddErrors(noValidPathMsg()) - - return res - } - - if s.spec.Spec().Paths.Paths == nil { - // Paths may be empty: warning - res.AddWarnings(noValidPathMsg()) - - return res - } - - for k := range s.spec.Spec().Paths.Paths { - if strings.Contains(k, "{}") { - res.AddErrors(emptyPathParameterMsg(k)) - } - } - - return res -} - -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 := pools.poolOfResults.BorrowResult() - known := make(map[string]int) - for _, v := range analyzer.OperationIDs() { - if v != "" { - known[v]++ - } - } - for k, v := range known { - if v > 1 { - res.AddErrors(nonUniqueOperationIDMsg(k, v)) - } - } - return res -} - -type dupProp struct { - Name string - Definition string -} - -func (s *SpecValidator) validateDuplicatePropertyNames() *Result { - // definition can't declare a property that's already defined by one of its ancestors - res := pools.poolOfResults.BorrowResult() - for k, sch := range s.spec.Spec().Definitions { - if len(sch.AllOf) == 0 { - continue - } - - knownanc := map[string]struct{}{ - "#/definitions/" + k: {}, - } - - ancs, rec := s.validateCircularAncestry(k, sch, knownanc) - if rec != nil && (rec.HasErrors() || !rec.HasWarnings()) { - res.Merge(rec) - } - if len(ancs) > 0 { - res.AddErrors(circularAncestryDefinitionMsg(k, ancs)) - return res - } - - knowns := make(map[string]struct{}) - dups, rep := s.validateSchemaPropertyNames(k, sch, knowns) - if rep != nil && (rep.HasErrors() || rep.HasWarnings()) { - res.Merge(rep) - } - if len(dups) > 0 { - var pns []string - for _, v := range dups { - pns = append(pns, v.Definition+"."+v.Name) - } - res.AddErrors(duplicatePropertiesMsg(k, pns)) - } - - } - return res -} - -func (s *SpecValidator) resolveRef(ref *spec.Ref) (*spec.Schema, error) { - if s.spec.SpecFilePath() != "" { - return spec.ResolveRefWithBase(s.spec.Spec(), ref, &spec.ExpandOptions{RelativeBase: s.spec.SpecFilePath()}) - } - // NOTE: it looks like with the new spec resolver, this code is now unrecheable - return spec.ResolveRef(s.spec.Spec(), ref) -} - -func (s *SpecValidator) validateSchemaPropertyNames(nm string, sch spec.Schema, knowns map[string]struct{}) ([]dupProp, *Result) { - var dups []dupProp - - schn := nm - schc := &sch - res := pools.poolOfResults.BorrowResult() - - for schc.Ref.String() != "" { - // gather property names - reso, err := s.resolveRef(&schc.Ref) - if err != nil { - errorHelp.addPointerError(res, err, schc.Ref.String(), nm) - return dups, res - } - schc = reso - schn = sch.Ref.String() - } - - if len(schc.AllOf) > 0 { - for _, chld := range schc.AllOf { - dup, rep := s.validateSchemaPropertyNames(schn, chld, knowns) - if rep != nil && (rep.HasErrors() || rep.HasWarnings()) { - res.Merge(rep) - } - dups = append(dups, dup...) - } - return dups, res - } - - for k := range schc.Properties { - _, ok := knowns[k] - if ok { - dups = append(dups, dupProp{Name: k, Definition: schn}) - } else { - knowns[k] = struct{}{} - } - } - - return dups, res -} - -func (s *SpecValidator) validateCircularAncestry(nm string, sch spec.Schema, knowns map[string]struct{}) ([]string, *Result) { - res := pools.poolOfResults.BorrowResult() - - if sch.Ref.String() == "" && len(sch.AllOf) == 0 { // Safeguard. We should not be able to actually get there - return nil, res - } - var ancs []string - - schn := nm - schc := &sch - - for schc.Ref.String() != "" { - reso, err := s.resolveRef(&schc.Ref) - if err != nil { - errorHelp.addPointerError(res, err, schc.Ref.String(), nm) - return ancs, res - } - schc = reso - schn = sch.Ref.String() - } - - if schn != nm && schn != "" { - if _, ok := knowns[schn]; ok { - ancs = append(ancs, schn) - } - knowns[schn] = struct{}{} - - if len(ancs) > 0 { - return ancs, res - } - } - - if len(schc.AllOf) > 0 { - for _, chld := range schc.AllOf { - if chld.Ref.String() != "" || len(chld.AllOf) > 0 { - anc, rec := s.validateCircularAncestry(schn, chld, knowns) - if rec != nil && (rec.HasErrors() || !rec.HasWarnings()) { - res.Merge(rec) - } - ancs = append(ancs, anc...) - if len(ancs) > 0 { - return ancs, res - } - } - } - } - return ancs, res -} - -func (s *SpecValidator) validateItems() *Result { - // validate parameter, items, schema and response objects for presence of item if type is array - res := pools.poolOfResults.BorrowResult() - - for method, pi := range s.analyzer.Operations() { - for path, op := range pi { - for _, param := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { - - if param.TypeName() == arrayType && param.ItemsTypeName() == "" { - res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID)) - continue - } - if param.In != swaggerBody { - if param.Items != nil { - items := param.Items - for items.TypeName() == arrayType { - if items.ItemsTypeName() == "" { - res.AddErrors(arrayInParamRequiresItemsMsg(param.Name, op.ID)) - break - } - items = items.Items - } - } - } else { - // In: body - if param.Schema != nil { - res.Merge(s.validateSchemaItems(*param.Schema, fmt.Sprintf("body param %q", param.Name), op.ID)) - } - } - } - - var responses []spec.Response - if op.Responses != nil { - if op.Responses.Default != nil { - responses = append(responses, *op.Responses.Default) - } - if op.Responses.StatusCodeResponses != nil { - for _, v := range op.Responses.StatusCodeResponses { - responses = append(responses, v) - } - } - } - - for _, resp := range responses { - // Response headers with array - for hn, hv := range resp.Headers { - if hv.TypeName() == arrayType && hv.ItemsTypeName() == "" { - res.AddErrors(arrayInHeaderRequiresItemsMsg(hn, op.ID)) - } - } - if resp.Schema != nil { - res.Merge(s.validateSchemaItems(*resp.Schema, "response body", op.ID)) - } - } - } - } - return res -} - -// Verifies constraints on array type -func (s *SpecValidator) validateSchemaItems(schema spec.Schema, prefix, opID string) *Result { - res := pools.poolOfResults.BorrowResult() - if !schema.Type.Contains(arrayType) { - return res - } - - if schema.Items == nil || schema.Items.Len() == 0 { - res.AddErrors(arrayRequiresItemsMsg(prefix, opID)) - return res - } - - if schema.Items.Schema != nil { - schema = *schema.Items.Schema - if _, err := compileRegexp(schema.Pattern); err != nil { - res.AddErrors(invalidItemsPatternMsg(prefix, opID, schema.Pattern)) - } - - res.Merge(s.validateSchemaItems(schema, prefix, opID)) - } - 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 := pools.poolOfResults.BorrowResult() - for _, l := range fromPath { - var matched bool - for _, r := range fromOperation { - if l == "{"+r+"}" { - matched = true - break - } - } - if !matched { - res.AddErrors(noParameterInPathMsg(l)) - } - } - - for _, p := range fromOperation { - var matched bool - for _, r := range fromPath { - if "{"+p+"}" == r { - matched = true - break - } - } - if !matched { - res.AddErrors(pathParamNotInPathMsg(path, p)) - } - } - - return res -} - -func (s *SpecValidator) validateReferenced() *Result { - var res Result - res.MergeAsWarnings(s.validateReferencedParameters()) - res.MergeAsWarnings(s.validateReferencedResponses()) - res.MergeAsWarnings(s.validateReferencedDefinitions()) - return &res -} - -func (s *SpecValidator) validateReferencedParameters() *Result { - // Each referenceable definition should have references. - params := s.spec.Spec().Parameters - if len(params) == 0 { - return nil - } - - expected := make(map[string]struct{}) - for k := range params { - expected["#/parameters/"+jsonpointer.Escape(k)] = struct{}{} - } - for _, k := range s.analyzer.AllParameterReferences() { - delete(expected, k) - } - - if len(expected) == 0 { - return nil - } - result := pools.poolOfResults.BorrowResult() - for k := range expected { - result.AddWarnings(unusedParamMsg(k)) - } - return result -} - -func (s *SpecValidator) validateReferencedResponses() *Result { - // Each referenceable definition should have references. - responses := s.spec.Spec().Responses - if len(responses) == 0 { - return nil - } - - expected := make(map[string]struct{}) - for k := range responses { - expected["#/responses/"+jsonpointer.Escape(k)] = struct{}{} - } - for _, k := range s.analyzer.AllResponseReferences() { - delete(expected, k) - } - - if len(expected) == 0 { - return nil - } - result := pools.poolOfResults.BorrowResult() - for k := range expected { - result.AddWarnings(unusedResponseMsg(k)) - } - return result -} - -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.analyzer.AllDefinitionReferences() { - delete(expected, k) - } - - if len(expected) == 0 { - return nil - } - - result := new(Result) - for k := range expected { - result.AddWarnings(unusedDefinitionMsg(k)) - } - return result -} - -func (s *SpecValidator) validateRequiredDefinitions() *Result { - // Each property listed in the required array must be defined in the properties of the model - res := pools.poolOfResults.BorrowResult() - -DEFINITIONS: - for d, schema := range s.spec.Spec().Definitions { - if schema.Required != nil { // Safeguard - for _, pn := range schema.Required { - red := s.validateRequiredProperties(pn, d, &schema) //#nosec - res.Merge(red) - if !red.IsValid() && !s.Options.ContinueOnErrors { - break DEFINITIONS // there is an error, let's stop that bleeding - } - } - } - } - return res -} - -func (s *SpecValidator) validateRequiredProperties(path, in string, v *spec.Schema) *Result { - // Takes care of recursive property definitions, which may be nested in additionalProperties schemas - res := pools.poolOfResults.BorrowResult() - propertyMatch := false - patternMatch := false - additionalPropertiesMatch := false - isReadOnly := false - - // Regular properties - if _, ok := v.Properties[path]; ok { - propertyMatch = true - isReadOnly = v.Properties[path].ReadOnly - } - - // NOTE: patternProperties are not supported in swagger. Even though, we continue validation here - // We check all defined patterns: if one regexp is invalid, croaks an error - for pp, pv := range v.PatternProperties { - re, err := compileRegexp(pp) - if err != nil { - res.AddErrors(invalidPatternMsg(pp, in)) - } else if re.MatchString(path) { - patternMatch = true - if !propertyMatch { - isReadOnly = pv.ReadOnly - } - } - } - - if !(propertyMatch || patternMatch) { - if v.AdditionalProperties != nil { - if v.AdditionalProperties.Allows && v.AdditionalProperties.Schema == nil { - additionalPropertiesMatch = true - } else if v.AdditionalProperties.Schema != nil { - // additionalProperties as schema are upported in swagger - // recursively validates additionalProperties schema - // TODO : anyOf, allOf, oneOf like in schemaPropsValidator - red := s.validateRequiredProperties(path, in, v.AdditionalProperties.Schema) - if red.IsValid() { - additionalPropertiesMatch = true - if !propertyMatch && !patternMatch { - isReadOnly = v.AdditionalProperties.Schema.ReadOnly - } - } - res.Merge(red) - } - } - } - - if !(propertyMatch || patternMatch || additionalPropertiesMatch) { - res.AddErrors(requiredButNotDefinedMsg(path, in)) - } - - if isReadOnly { - res.AddWarnings(readOnlyAndRequiredMsg(in, path)) - } - return res -} - -func (s *SpecValidator) validateParameters() *Result { - // - for each method, path is unique, regardless of path parameters - // e.g. GET:/petstore/{id}, GET:/petstore/{pet}, GET:/petstore are - // considered duplicate paths, if StrictPathParamUniqueness is enabled. - // - each parameter should have a unique `name` and `type` combination - // - each operation should have only 1 parameter of type body - // - there must be at most 1 parameter in body - // - parameters with pattern property must specify valid patterns - // - $ref in parameters must resolve - // - path param must be required - res := pools.poolOfResults.BorrowResult() - rexGarbledPathSegment := mustCompileRegexp(`.*[{}\s]+.*`) - for method, pi := range s.expandedAnalyzer().Operations() { - methodPaths := make(map[string]map[string]string) - for path, op := range pi { - if s.Options.StrictPathParamUniqueness { - pathToAdd := pathHelp.stripParametersInPath(path) - - // 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 { - - // 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)) - } - } 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)) - - // pick the root schema from the swagger specification which describes a parameter - origSchema, ok := s.schema.Definitions["parameter"] - if !ok { - panic("unexpected swagger schema: missing #/definitions/parameter") - } - // clone it once to avoid expanding a global schema (e.g. swagger spec) - paramSchema, err := deepCloneSchema(origSchema) - if err != nil { - panic(fmt.Errorf("can't clone schema: %v", err)) - } - - for _, pr := range paramHelp.safeExpandedParamsFor(path, method, op.ID, res, s) { - // An expanded parameter must validate the Parameter schema (an unexpanded $ref always passes high-level schema validation) - schv := newSchemaValidator(¶mSchema, s.schema, fmt.Sprintf("%s.%s.parameters.%s", path, method, pr.Name), s.KnownFormats, s.schemaOptions) - obj := swag.ToDynamicJSON(pr) - res.Merge(schv.Validate(obj)) - - // 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)) - } - - // 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 == "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)) - } - } - - if pr.In == "formData" { - hasForm = true - } - - 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)) - } - - 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)) - } - - // 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 - } - } - } - - // 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 -} - -func (s *SpecValidator) validateReferencesValid() *Result { - // each reference must point to a valid object - res := pools.poolOfResults.BorrowResult() - for _, r := range s.analyzer.AllRefs() { - if !r.IsValidURI(s.spec.SpecFilePath()) { // Safeguard - spec should always yield a valid URI - res.AddErrors(invalidRefMsg(r.String())) - } - } - if !res.HasErrors() { - // NOTE: with default settings, loads.Document.Expanded() - // stops on first error. Anyhow, the expand option to continue - // on errors fails to report errors at all. - exp, err := s.spec.Expanded() - if err != nil { - res.AddErrors(unresolvedReferencesMsg(err)) - } - s.expanded = exp - } - return res -} - -func (s *SpecValidator) checkUniqueParams(path, method string, op *spec.Operation) *Result { - // Check for duplicate parameters declaration in param section. - // Each parameter should have a unique `name` and `type` combination - // NOTE: this could be factorized in analysis (when constructing the params map) - // However, there are some issues with such a factorization: - // - analysis does not seem to fully expand params - // - param keys may be altered by x-go-name - res := pools.poolOfResults.BorrowResult() - pnames := make(map[string]struct{}) - - if op.Parameters != nil { // Safeguard - for _, ppr := range op.Parameters { - var ok bool - pr, red := paramHelp.resolveParam(path, method, op.ID, &ppr, s) //#nosec - res.Merge(red) - - if pr != nil && pr.Name != "" { // params with empty name does no participate the check - key := fmt.Sprintf("%s#%s", pr.In, pr.Name) - - if _, ok = pnames[key]; ok { - res.AddErrors(duplicateParamNameMsg(pr.In, pr.Name, op.ID)) - } - pnames[key] = struct{}{} - } - } - } - return res -} - -// SetContinueOnErrors sets the ContinueOnErrors option for this validator. -func (s *SpecValidator) SetContinueOnErrors(c bool) { - s.Options.ContinueOnErrors = c -} - -// expandedAnalyzer returns expanded.Analyzer when it is available. -// otherwise just analyzer. -func (s *SpecValidator) expandedAnalyzer() *analysis.Spec { - if s.expanded != nil && s.expanded.Analyzer != nil { - return s.expanded.Analyzer - } - return s.analyzer -} - -func deepCloneSchema(src spec.Schema) (spec.Schema, error) { - var b bytes.Buffer - if err := gob.NewEncoder(&b).Encode(src); err != nil { - return spec.Schema{}, err - } - - var dst spec.Schema - if err := gob.NewDecoder(&b).Decode(&dst); err != nil { - return spec.Schema{}, err - } - - return dst, nil -} |