Skip to content

Commit 99afba0

Browse files
committed
Option to disable strict path param uniquess
Add a validation option to remove strict path uniqueness validation behavior where path parameters are ignored. Signed-off-by: Alex Dadgar <[email protected]>
1 parent 348543c commit 99afba0

File tree

4 files changed

+45
-20
lines changed

4 files changed

+45
-20
lines changed

doc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Reported as errors:
3333
3434
[x] definition can't declare a property that's already defined by one of its ancestors
3535
[x] definition's ancestor can't be a descendant of the same model
36-
[x] path uniqueness: each api path should be non-verbatim (account for path param names) unique per method
36+
[x] path uniqueness: each api path should be non-verbatim (account for path param names) unique per method. Validation can be laxed by disabling StrictPathParamUniqueness.
3737
[x] each security reference should contain only unique scopes
3838
[x] each security scope in a security definition should be unique
3939
[x] parameters in path must be unique

options.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,28 @@ import "sync"
2121
// NOTE: other options might be needed, for example a go-swagger specific mode.
2222
type Opts struct {
2323
ContinueOnErrors bool // true: continue reporting errors, even if spec is invalid
24+
25+
// StrictPathParamUniqueness enables a strict validation of paths that include
26+
// path parameters. When true, it will enforce that for each method, the path
27+
// is unique, regardless of path parameters such that GET:/petstore/{id} and
28+
// GET:/petstore/{pet} anre considered duplicate paths.
29+
//
30+
// Consider disabling if path parameters can include slashes such as
31+
// GET:/v1/{shelve} and GET:/v1/{book}, where the IDs are "shelve/*" and
32+
// /"shelve/*/book/*" respectively.
33+
StrictPathParamUniqueness bool
2434
}
2535

2636
var (
27-
defaultOpts = Opts{ContinueOnErrors: false} // default is to stop validation on errors
37+
defaultOpts = Opts{
38+
// default is to stop validation on errors
39+
ContinueOnErrors: false,
40+
41+
// StrictPathParamUniqueness is defaulted to true. This maintains existing
42+
// behavior.
43+
StrictPathParamUniqueness: true,
44+
}
45+
2846
defaultOptsMutex = &sync.Mutex{}
2947
)
3048

spec.go

+20-18
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ func (s *SpecValidator) validateRequiredProperties(path, in string, v *spec.Sche
614614
func (s *SpecValidator) validateParameters() *Result {
615615
// - for each method, path is unique, regardless of path parameters
616616
// e.g. GET:/petstore/{id}, GET:/petstore/{pet}, GET:/petstore are
617-
// considered duplicate paths
617+
// considered duplicate paths, if StrictPathParamUniqueness is enabled.
618618
// - each parameter should have a unique `name` and `type` combination
619619
// - each operation should have only 1 parameter of type body
620620
// - there must be at most 1 parameter in body
@@ -626,28 +626,30 @@ func (s *SpecValidator) validateParameters() *Result {
626626
for method, pi := range s.expandedAnalyzer().Operations() {
627627
methodPaths := make(map[string]map[string]string)
628628
for path, op := range pi {
629-
pathToAdd := pathHelp.stripParametersInPath(path)
629+
if s.Options.StrictPathParamUniqueness {
630+
pathToAdd := pathHelp.stripParametersInPath(path)
630631

631-
// Warn on garbled path afer param stripping
632-
if rexGarbledPathSegment.MatchString(pathToAdd) {
633-
res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd))
634-
}
632+
// Warn on garbled path afer param stripping
633+
if rexGarbledPathSegment.MatchString(pathToAdd) {
634+
res.AddWarnings(pathStrippedParamGarbledMsg(pathToAdd))
635+
}
635636

636-
// Check uniqueness of stripped paths
637-
if _, found := methodPaths[method][pathToAdd]; found {
637+
// Check uniqueness of stripped paths
638+
if _, found := methodPaths[method][pathToAdd]; found {
638639

639-
// Sort names for stable, testable output
640-
if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 {
641-
res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd]))
640+
// Sort names for stable, testable output
641+
if strings.Compare(path, methodPaths[method][pathToAdd]) < 0 {
642+
res.AddErrors(pathOverlapMsg(path, methodPaths[method][pathToAdd]))
643+
} else {
644+
res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path))
645+
}
642646
} else {
643-
res.AddErrors(pathOverlapMsg(methodPaths[method][pathToAdd], path))
644-
}
645-
} else {
646-
if _, found := methodPaths[method]; !found {
647-
methodPaths[method] = map[string]string{}
648-
}
649-
methodPaths[method][pathToAdd] = path // Original non stripped path
647+
if _, found := methodPaths[method]; !found {
648+
methodPaths[method] = map[string]string{}
649+
}
650+
methodPaths[method][pathToAdd] = path // Original non stripped path
650651

652+
}
651653
}
652654

653655
var bodyParams []string

spec_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,11 @@ func TestSpec_ValidateParameters(t *testing.T) {
451451
assert.Len(t, res.Errors, 1)
452452
assert.Contains(t, res.Errors[0].Error(), "overlaps with")
453453

454+
// Disable strict path param uniqueness and ensure there is no error
455+
validator.Options.StrictPathParamUniqueness = false
456+
res = validator.validateParameters()
457+
assert.Empty(t, res.Errors)
458+
454459
doc, _ = loads.Analyzed(PetStoreJSONMessage, "")
455460
validator = NewSpecValidator(spec.MustLoadSwagger20Schema(), strfmt.Default)
456461
validator.spec = doc

0 commit comments

Comments
 (0)