Skip to content

Commit 8c66162

Browse files
emilien-pugetdaveshanley
authored andcommitted
FindPath reports path and operation not found
1 parent bc63c2f commit 8c66162

File tree

3 files changed

+62
-109
lines changed

3 files changed

+62
-109
lines changed

errors/validation_error.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package errors
55

66
import (
77
"fmt"
8+
89
"github.com/santhosh-tekuri/jsonschema/v5"
910
)
1011

@@ -118,3 +119,8 @@ func (v *ValidationError) Error() string {
118119
func (v *ValidationError) IsPathMissingError() bool {
119120
return v.ValidationType == "path" && v.ValidationSubType == "missing"
120121
}
122+
123+
// IsOperationMissingError returns true if the error has a ValidationType of "request" and a ValidationSubType of "missingOperation"
124+
func (v *ValidationError) IsOperationMissingError() bool {
125+
return v.ValidationType == "path" && v.ValidationSubType == "missingOperation"
126+
}

paths/paths.go

Lines changed: 34 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ import (
1818

1919
// FindPath will find the path in the document that matches the request path. If a successful match was found, then
2020
// the first return value will be a pointer to the PathItem. The second return value will contain any validation errors
21-
// that were picked up when locating the path. Number/Integer validation is performed in any path parameters in the request.
21+
// that were picked up when locating the path.
2222
// The third return value will be the path that was found in the document, as it pertains to the contract, so all path
2323
// parameters will not have been replaced with their values from the request - allowing model lookups.
2424
func FindPath(request *http.Request, document *v3.Document) (*v3.PathItem, []*errors.ValidationError, string) {
25-
var validationErrors []*errors.ValidationError
26-
2725
basePaths := getBasePaths(document)
2826
stripped := StripRequestPath(request, document)
2927

@@ -34,7 +32,6 @@ func FindPath(request *http.Request, document *v3.Document) (*v3.PathItem, []*er
3432

3533
var pItem *v3.PathItem
3634
var foundPath string
37-
pathFound:
3835
for pair := orderedmap.First(document.Paths.PathItems); pair != nil; pair = pair.Next() {
3936
path := pair.Key()
4037
pathItem := pair.Value()
@@ -52,131 +49,63 @@ pathFound:
5249
segs = segs[1:]
5350
}
5451

55-
// collect path level params
56-
var errs []*errors.ValidationError
57-
var ok bool
52+
ok := comparePaths(segs, reqPathSegments, basePaths)
53+
if !ok {
54+
continue
55+
}
56+
pItem = pathItem
57+
foundPath = path
5858
switch request.Method {
5959
case http.MethodGet:
6060
if pathItem.Get != nil {
61-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
62-
pItem = pathItem
63-
foundPath = path
64-
break pathFound
65-
}
66-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
67-
pItem = pathItem
68-
foundPath = path
69-
validationErrors = errs
70-
break pathFound
71-
}
61+
return pathItem, nil, path
7262
}
7363
case http.MethodPost:
7464
if pathItem.Post != nil {
75-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
76-
pItem = pathItem
77-
foundPath = path
78-
break pathFound
79-
}
80-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
81-
pItem = pathItem
82-
foundPath = path
83-
validationErrors = errs
84-
break pathFound
85-
}
65+
return pathItem, nil, path
8666
}
8767
case http.MethodPut:
8868
if pathItem.Put != nil {
89-
// check for a literal match
90-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
91-
pItem = pathItem
92-
foundPath = path
93-
validationErrors = errs
94-
break pathFound
95-
}
96-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
97-
pItem = pathItem
98-
foundPath = path
99-
validationErrors = errs
100-
break pathFound
101-
}
69+
return pathItem, nil, path
10270
}
10371
case http.MethodDelete:
10472
if pathItem.Delete != nil {
105-
// check for a literal match
106-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
107-
pItem = pathItem
108-
foundPath = path
109-
break pathFound
110-
}
111-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
112-
pItem = pathItem
113-
foundPath = path
114-
validationErrors = errs
115-
break pathFound
116-
}
73+
return pathItem, nil, path
11774
}
11875
case http.MethodOptions:
11976
if pathItem.Options != nil {
120-
// check for a literal match
121-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
122-
pItem = pathItem
123-
foundPath = path
124-
break pathFound
125-
}
126-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
127-
pItem = pathItem
128-
foundPath = path
129-
validationErrors = errs
130-
break pathFound
131-
}
77+
return pathItem, nil, path
13278
}
13379
case http.MethodHead:
13480
if pathItem.Head != nil {
135-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
136-
pItem = pathItem
137-
foundPath = path
138-
break pathFound
139-
}
140-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
141-
pItem = pathItem
142-
foundPath = path
143-
validationErrors = errs
144-
break pathFound
145-
}
81+
return pathItem, nil, path
14682
}
14783
case http.MethodPatch:
14884
if pathItem.Patch != nil {
149-
// check for a literal match
150-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
151-
pItem = pathItem
152-
foundPath = path
153-
break pathFound
154-
}
155-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
156-
pItem = pathItem
157-
foundPath = path
158-
validationErrors = errs
159-
break pathFound
160-
}
85+
return pathItem, nil, path
16186
}
16287
case http.MethodTrace:
16388
if pathItem.Trace != nil {
164-
if checkPathAgainstBase(request.URL.Path, path, basePaths) {
165-
pItem = pathItem
166-
foundPath = path
167-
break pathFound
168-
}
169-
if ok = comparePaths(segs, reqPathSegments, basePaths); ok {
170-
pItem = pathItem
171-
foundPath = path
172-
validationErrors = errs
173-
break pathFound
174-
}
89+
return pathItem, nil, path
17590
}
17691
}
17792
}
178-
if pItem == nil && len(validationErrors) == 0 {
179-
validationErrors = append(validationErrors, &errors.ValidationError{
93+
if pItem != nil {
94+
validationErrors := []*errors.ValidationError{{
95+
ValidationType: helpers.ParameterValidationPath,
96+
ValidationSubType: "missingOperation",
97+
Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path),
98+
Reason: fmt.Sprintf("The %s method for that path does not exist in the specification",
99+
request.Method),
100+
SpecLine: -1,
101+
SpecCol: -1,
102+
HowToFix: errors.HowToFixPath,
103+
}}
104+
errors.PopulateValidationErrors(validationErrors, request, foundPath)
105+
return pItem, validationErrors, foundPath
106+
}
107+
validationErrors := []*errors.ValidationError{
108+
{
180109
ValidationType: helpers.ParameterValidationPath,
181110
ValidationSubType: "missing",
182111
Message: fmt.Sprintf("%s Path '%s' not found", request.Method, request.URL.Path),
@@ -186,14 +115,10 @@ pathFound:
186115
SpecLine: -1,
187116
SpecCol: -1,
188117
HowToFix: errors.HowToFixPath,
189-
})
190-
191-
errors.PopulateValidationErrors(validationErrors, request, foundPath)
192-
return pItem, validationErrors, foundPath
193-
} else {
194-
errors.PopulateValidationErrors(validationErrors, request, foundPath)
195-
return pItem, validationErrors, foundPath
118+
},
196119
}
120+
errors.PopulateValidationErrors(validationErrors, request, "")
121+
return nil, validationErrors, ""
197122
}
198123

199124
func getBasePaths(document *v3.Document) []string {

paths/paths_test.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,31 @@ paths:
314314
assert.Nil(t, pathItem)
315315
assert.NotNil(t, errs)
316316
assert.Equal(t, "HEAD Path '/not/here' not found", errs[0].Message)
317+
assert.True(t, errs[0].IsPathMissingError())
317318

318319
}
319320

321+
func TestNewValidator_FindOperationMissing(t *testing.T) {
322+
323+
spec := `openapi: 3.1.0
324+
paths:
325+
/burgers/{burgerId}:
326+
trace:
327+
operationId: locateBurger
328+
`
329+
330+
doc, _ := libopenapi.NewDocument([]byte(spec))
331+
m, _ := doc.BuildV3Model()
332+
333+
request, _ := http.NewRequest(http.MethodPut, "https://things.com/burgers/12345", nil)
334+
335+
pathItem, errs, _ := FindPath(request, &m.Model)
336+
assert.NotNil(t, pathItem)
337+
assert.NotNil(t, errs)
338+
assert.Equal(t, "PUT Path '/burgers/12345' not found", errs[0].Message)
339+
assert.True(t, errs[0].IsOperationMissingError())
340+
}
341+
320342
func TestNewValidator_GetLiteralMatch(t *testing.T) {
321343

322344
request, _ := http.NewRequest(http.MethodGet, "https://things.com/store/inventory", nil)

0 commit comments

Comments
 (0)