vendorify
diff --git a/go/vendor/github.com/go-openapi/validate/spec.go b/go/vendor/github.com/go-openapi/validate/spec.go
new file mode 100644
index 0000000..08ccd22
--- /dev/null
+++ b/go/vendor/github.com/go-openapi/validate/spec.go
@@ -0,0 +1,777 @@
+// 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 (
+	"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"
+)
+
+// 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
+}
+
+// NewSpecValidator creates a new swagger spec validator instance
+func NewSpecValidator(schema *spec.Schema, formats strfmt.Registry) *SpecValidator {
+	return &SpecValidator{
+		schema:       schema,
+		KnownFormats: formats,
+		Options:      defaultOpts,
+	}
+}
+
+// Validate validates the swagger spec
+func (s *SpecValidator) Validate(data interface{}) (errs *Result, warnings *Result) {
+	var sd *loads.Document
+	errs = new(Result)
+
+	switch v := data.(type) {
+	case *loads.Document:
+		sd = v
+	}
+	if sd == nil {
+		errs.AddErrors(invalidDocumentMsg())
+		return
+	}
+	s.spec = sd
+	s.analyzer = analysis.New(sd.Spec())
+
+	warnings = new(Result)
+
+	// Swagger schema validator
+	schv := NewSchemaValidator(s.schema, nil, "", s.KnownFormats)
+	var obj interface{}
+
+	// Raw spec unmarshalling errors
+	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...)
+	}()
+
+	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
+	}
+
+	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
+	}
+
+	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 // no point in continuing
+	}
+
+	// Values provided as default MUST validate their schema
+	df := &defaultValidator{SpecValidator: s}
+	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}
+	errs.Merge(ex.Validate())
+
+	errs.Merge(s.validateNonEmptyPathParamNames())
+
+	//errs.Merge(s.validateRefNoSibling()) // warning only
+	errs.Merge(s.validateReferenced()) // warning only
+
+	return
+}
+
+func (s *SpecValidator) validateNonEmptyPathParamNames() *Result {
+	res := new(Result)
+	if s.spec.Spec().Paths == nil {
+		// There is no Paths object: error
+		res.AddErrors(noValidPathMsg())
+	} else {
+		if s.spec.Spec().Paths.Paths == nil {
+			// Paths may be empty: warning
+			res.AddWarnings(noValidPathMsg())
+		} else {
+			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
+	res := new(Result)
+	known := make(map[string]int)
+	for _, v := range s.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 := new(Result)
+	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 := new(Result)
+
+	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 := new(Result)
+
+	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 := new(Result)
+
+	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() == "array" && param.ItemsTypeName() == "" {
+					res.AddErrors(arrayInParamRequiresItemsMsg(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(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() == "array" && 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 := new(Result)
+	if !schema.Type.Contains("array") {
+		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 := new(Result)
+	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() {
+		if _, ok := expected[k]; ok {
+			delete(expected, k)
+		}
+	}
+
+	if len(expected) == 0 {
+		return nil
+	}
+	result := new(Result)
+	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() {
+		if _, ok := expected[k]; ok {
+			delete(expected, k)
+		}
+	}
+
+	if len(expected) == 0 {
+		return nil
+	}
+	result := new(Result)
+	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() {
+		if _, ok := expected[k]; ok {
+			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 := new(Result)
+
+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)
+				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 := new(Result)
+	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
+	// - 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 := new(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)
+
+				// 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))
+
+				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))
+					}
+
+					// 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
+					}
+
+					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
+					}
+				}
+
+				// 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 := new(Result)
+	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 := new(Result)
+	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)
+			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
+}