forked from go-openapi/validate
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhelpers.go
245 lines (218 loc) · 8.29 KB
/
helpers.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
// 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
// TODO: define this as package validate/internal
// This must be done while keeping CI intact with all tests and test coverage
import (
"reflect"
"strconv"
"strings"
"github.com/go-openapi/errors"
"github.com/go-openapi/spec"
)
// Helpers available at the package level
var (
pathHelp *pathHelper
valueHelp *valueHelper
errorHelp *errorHelper
paramHelp *paramHelper
responseHelp *responseHelper
)
type errorHelper struct {
// A collection of unexported helpers for error construction
}
func (h *errorHelper) sErr(err errors.Error) *Result {
// Builds a Result from standard errors.Error
return &Result{Errors: []error{err}}
}
func (h *errorHelper) addPointerError(res *Result, err error, ref string, fromPath string) *Result {
// Provides more context on error messages
// reported by the jsoinpointer package by altering the passed Result
if err != nil {
res.AddErrors(cannotResolveRefMsg(fromPath, ref, err))
}
return res
}
type pathHelper struct {
// A collection of unexported helpers for path validation
}
func (h *pathHelper) stripParametersInPath(path string) string {
// Returns a path stripped from all path parameters, with multiple or trailing slashes removed.
//
// Stripping is performed on a slash-separated basis, e.g '/a{/b}' remains a{/b} and not /a.
// - Trailing "/" make a difference, e.g. /a/ !~ /a (ex: canary/bitbucket.org/swagger.json)
// - presence or absence of a parameter makes a difference, e.g. /a/{log} !~ /a/ (ex: canary/kubernetes/swagger.json)
// Regexp to extract parameters from path, with surrounding {}.
// NOTE: important non-greedy modifier
rexParsePathParam := mustCompileRegexp(`{[^{}]+?}`)
strippedSegments := []string{}
for _, segment := range strings.Split(path, "/") {
strippedSegments = append(strippedSegments, rexParsePathParam.ReplaceAllString(segment, "X"))
}
return strings.Join(strippedSegments, "/")
}
func (h *pathHelper) extractPathParams(path string) (params []string) {
// Extracts all params from a path, with surrounding "{}"
rexParsePathParam := mustCompileRegexp(`{[^{}]+?}`)
for _, segment := range strings.Split(path, "/") {
for _, v := range rexParsePathParam.FindAllStringSubmatch(segment, -1) {
params = append(params, v...)
}
}
return
}
type valueHelper struct {
// A collection of unexported helpers for value validation
}
func (h *valueHelper) asInt64(val interface{}) int64 {
// Number conversion function for int64, without error checking
// (implements an implicit type upgrade).
v := reflect.ValueOf(val)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return int64(v.Uint())
case reflect.Float32, reflect.Float64:
return int64(v.Float())
default:
//panic("Non numeric value in asInt64()")
return 0
}
}
func (h *valueHelper) asUint64(val interface{}) uint64 {
// Number conversion function for uint64, without error checking
// (implements an implicit type upgrade).
v := reflect.ValueOf(val)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return uint64(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint()
case reflect.Float32, reflect.Float64:
return uint64(v.Float())
default:
//panic("Non numeric value in asUint64()")
return 0
}
}
// Same for unsigned floats
func (h *valueHelper) asFloat64(val interface{}) float64 {
// Number conversion function for float64, without error checking
// (implements an implicit type upgrade).
v := reflect.ValueOf(val)
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return float64(v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return float64(v.Uint())
case reflect.Float32, reflect.Float64:
return v.Float()
default:
//panic("Non numeric value in asFloat64()")
return 0
}
}
type paramHelper struct {
// A collection of unexported helpers for parameters resolution
}
func (h *paramHelper) safeExpandedParamsFor(path, method, operationID string, res *Result, s *SpecValidator) (params []spec.Parameter) {
operation, ok := s.analyzer.OperationFor(method, path)
if ok {
// expand parameters first if necessary
resolvedParams := []spec.Parameter{}
for _, ppr := range operation.Parameters {
resolvedParam, red := h.resolveParam(path, method, operationID, &ppr, s)
res.Merge(red)
if resolvedParam != nil {
resolvedParams = append(resolvedParams, *resolvedParam)
}
}
// remove params with invalid expansion from Slice
operation.Parameters = resolvedParams
for _, ppr := range s.analyzer.SafeParamsFor(method, path,
func(p spec.Parameter, err error) bool {
// since params have already been expanded, there are few causes for error
res.AddErrors(someParametersBrokenMsg(path, method, operationID))
// original error from analyzer
res.AddErrors(err)
return true
}) {
params = append(params, ppr)
}
}
return
}
func (h *paramHelper) resolveParam(path, method, operationID string, param *spec.Parameter, s *SpecValidator) (*spec.Parameter, *Result) {
// Expand parameter with $ref if needed
res := new(Result)
if param.Ref.String() != "" {
err := spec.ExpandParameter(param, s.spec.SpecFilePath())
if err != nil { // Safeguard
// NOTE: we may enter enter here when the whole parameter is an unresolved $ref
refPath := strings.Join([]string{"\"" + path + "\"", method}, ".")
errorHelp.addPointerError(res, err, param.Ref.String(), refPath)
return nil, res
}
res.Merge(h.checkExpandedParam(param, param.Name, param.In, operationID))
}
return param, res
}
func (h *paramHelper) checkExpandedParam(pr *spec.Parameter, path, in, operation string) *Result {
// Secure parameter structure after $ref resolution
res := new(Result)
simpleZero := spec.SimpleSchema{}
// Try to explain why... best guess
if pr.In == "body" && pr.SimpleSchema != simpleZero {
// Most likely, a $ref with a sibling is an unwanted situation: in itself this is a warning...
// but we detect it because of the following error:
// schema took over Parameter for an unexplained reason
res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation))
res.AddErrors(invalidParameterDefinitionMsg(path, in, operation))
} else if pr.In != "body" && pr.Schema != nil {
res.AddWarnings(refShouldNotHaveSiblingsMsg(path, operation))
res.AddErrors(invalidParameterDefinitionAsSchemaMsg(path, in, operation))
} else if (pr.In == "body" && pr.Schema == nil) || (pr.In != "body" && pr.SimpleSchema == simpleZero) { // Safeguard
// Other unexpected mishaps
res.AddErrors(invalidParameterDefinitionMsg(path, in, operation))
}
return res
}
type responseHelper struct {
// A collection of unexported helpers for response resolution
}
func (r *responseHelper) expandResponseRef(response *spec.Response, path string, s *SpecValidator) (*spec.Response, *Result) {
// Expand response with $ref if needed
res := new(Result)
if response.Ref.String() != "" {
err := spec.ExpandResponse(response, s.spec.SpecFilePath())
if err != nil { // Safeguard
// NOTE: we may enter here when the whole response is an unresolved $ref.
errorHelp.addPointerError(res, err, response.Ref.String(), path)
return nil, res
}
}
return response, res
}
func (r *responseHelper) responseMsgVariants(responseType string, responseCode int) (responseName, responseCodeAsStr string) {
// Path variants for messages
if responseType == "default" {
responseCodeAsStr = "default"
responseName = "default response"
} else {
responseCodeAsStr = strconv.Itoa(responseCode)
responseName = "response " + responseCodeAsStr
}
return
}