result.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. // Copyright 2015 go-swagger maintainers
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package validate
  15. import (
  16. "fmt"
  17. "reflect"
  18. "strings"
  19. "github.com/go-openapi/errors"
  20. "github.com/go-openapi/spec"
  21. )
  22. // Result represents a validation result set, composed of
  23. // errors and warnings.
  24. //
  25. // It is used to keep track of all detected errors and warnings during
  26. // the validation of a specification.
  27. //
  28. // Matchcount is used to determine
  29. // which errors are relevant in the case of AnyOf, OneOf
  30. // schema validation. Results from the validation branch
  31. // with most matches get eventually selected.
  32. //
  33. // TODO: keep path of key originating the error
  34. type Result struct {
  35. Errors []error
  36. Warnings []error
  37. MatchCount int
  38. // the object data
  39. data interface{}
  40. // Schemata for the root object
  41. rootObjectSchemata schemata
  42. // Schemata for object fields
  43. fieldSchemata []fieldSchemata
  44. // Schemata for slice items
  45. itemSchemata []itemSchemata
  46. cachedFieldSchemta map[FieldKey][]*spec.Schema
  47. cachedItemSchemata map[ItemKey][]*spec.Schema
  48. }
  49. // FieldKey is a pair of an object and a field, usable as a key for a map.
  50. type FieldKey struct {
  51. object reflect.Value // actually a map[string]interface{}, but the latter cannot be a key
  52. field string
  53. }
  54. // ItemKey is a pair of a slice and an index, usable as a key for a map.
  55. type ItemKey struct {
  56. slice reflect.Value // actually a []interface{}, but the latter cannot be a key
  57. index int
  58. }
  59. // NewFieldKey returns a pair of an object and field usable as a key of a map.
  60. func NewFieldKey(obj map[string]interface{}, field string) FieldKey {
  61. return FieldKey{object: reflect.ValueOf(obj), field: field}
  62. }
  63. // Object returns the underlying object of this key.
  64. func (fk *FieldKey) Object() map[string]interface{} {
  65. return fk.object.Interface().(map[string]interface{})
  66. }
  67. // Field returns the underlying field of this key.
  68. func (fk *FieldKey) Field() string {
  69. return fk.field
  70. }
  71. // NewItemKey returns a pair of a slice and index usable as a key of a map.
  72. func NewItemKey(slice interface{}, i int) ItemKey {
  73. return ItemKey{slice: reflect.ValueOf(slice), index: i}
  74. }
  75. // Slice returns the underlying slice of this key.
  76. func (ik *ItemKey) Slice() []interface{} {
  77. return ik.slice.Interface().([]interface{})
  78. }
  79. // Index returns the underlying index of this key.
  80. func (ik *ItemKey) Index() int {
  81. return ik.index
  82. }
  83. type fieldSchemata struct {
  84. obj map[string]interface{}
  85. field string
  86. schemata schemata
  87. }
  88. type itemSchemata struct {
  89. slice reflect.Value
  90. index int
  91. schemata schemata
  92. }
  93. // Merge merges this result with the other one(s), preserving match counts etc.
  94. func (r *Result) Merge(others ...*Result) *Result {
  95. for _, other := range others {
  96. if other == nil {
  97. continue
  98. }
  99. r.mergeWithoutRootSchemata(other)
  100. r.rootObjectSchemata.Append(other.rootObjectSchemata)
  101. }
  102. return r
  103. }
  104. // Data returns the original data object used for validation. Mutating this renders
  105. // the result invalid.
  106. func (r *Result) Data() interface{} {
  107. return r.data
  108. }
  109. // RootObjectSchemata returns the schemata which apply to the root object.
  110. func (r *Result) RootObjectSchemata() []*spec.Schema {
  111. return r.rootObjectSchemata.Slice()
  112. }
  113. // FieldSchemata returns the schemata which apply to fields in objects.
  114. // nolint: dupl
  115. func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
  116. if r.cachedFieldSchemta != nil {
  117. return r.cachedFieldSchemta
  118. }
  119. ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
  120. for _, fs := range r.fieldSchemata {
  121. key := NewFieldKey(fs.obj, fs.field)
  122. if fs.schemata.one != nil {
  123. ret[key] = append(ret[key], fs.schemata.one)
  124. } else if len(fs.schemata.multiple) > 0 {
  125. ret[key] = append(ret[key], fs.schemata.multiple...)
  126. }
  127. }
  128. r.cachedFieldSchemta = ret
  129. return ret
  130. }
  131. // ItemSchemata returns the schemata which apply to items in slices.
  132. // nolint: dupl
  133. func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
  134. if r.cachedItemSchemata != nil {
  135. return r.cachedItemSchemata
  136. }
  137. ret := make(map[ItemKey][]*spec.Schema, len(r.itemSchemata))
  138. for _, ss := range r.itemSchemata {
  139. key := NewItemKey(ss.slice, ss.index)
  140. if ss.schemata.one != nil {
  141. ret[key] = append(ret[key], ss.schemata.one)
  142. } else if len(ss.schemata.multiple) > 0 {
  143. ret[key] = append(ret[key], ss.schemata.multiple...)
  144. }
  145. }
  146. r.cachedItemSchemata = ret
  147. return ret
  148. }
  149. func (r *Result) resetCaches() {
  150. r.cachedFieldSchemta = nil
  151. r.cachedItemSchemata = nil
  152. }
  153. // mergeForField merges other into r, assigning other's root schemata to the given Object and field name.
  154. // nolint: unparam
  155. func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
  156. if other == nil {
  157. return r
  158. }
  159. r.mergeWithoutRootSchemata(other)
  160. if other.rootObjectSchemata.Len() > 0 {
  161. if r.fieldSchemata == nil {
  162. r.fieldSchemata = make([]fieldSchemata, len(obj))
  163. }
  164. r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{
  165. obj: obj,
  166. field: field,
  167. schemata: other.rootObjectSchemata,
  168. })
  169. }
  170. return r
  171. }
  172. // mergeForSlice merges other into r, assigning other's root schemata to the given slice and index.
  173. // nolint: unparam
  174. func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
  175. if other == nil {
  176. return r
  177. }
  178. r.mergeWithoutRootSchemata(other)
  179. if other.rootObjectSchemata.Len() > 0 {
  180. if r.itemSchemata == nil {
  181. r.itemSchemata = make([]itemSchemata, slice.Len())
  182. }
  183. r.itemSchemata = append(r.itemSchemata, itemSchemata{
  184. slice: slice,
  185. index: i,
  186. schemata: other.rootObjectSchemata,
  187. })
  188. }
  189. return r
  190. }
  191. // addRootObjectSchemata adds the given schemata for the root object of the result.
  192. // The slice schemata might be reused. I.e. do not modify it after being added to a result.
  193. func (r *Result) addRootObjectSchemata(s *spec.Schema) {
  194. r.rootObjectSchemata.Append(schemata{one: s})
  195. }
  196. // addPropertySchemata adds the given schemata for the object and field.
  197. // The slice schemata might be reused. I.e. do not modify it after being added to a result.
  198. func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) {
  199. if r.fieldSchemata == nil {
  200. r.fieldSchemata = make([]fieldSchemata, 0, len(obj))
  201. }
  202. r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}})
  203. }
  204. /*
  205. // addSliceSchemata adds the given schemata for the slice and index.
  206. // The slice schemata might be reused. I.e. do not modify it after being added to a result.
  207. func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schema) {
  208. if r.itemSchemata == nil {
  209. r.itemSchemata = make([]itemSchemata, 0, slice.Len())
  210. }
  211. r.itemSchemata = append(r.itemSchemata, itemSchemata{slice: slice, index: i, schemata: schemata{one: schema}})
  212. }
  213. */
  214. // mergeWithoutRootSchemata merges other into r, ignoring the rootObject schemata.
  215. func (r *Result) mergeWithoutRootSchemata(other *Result) {
  216. r.resetCaches()
  217. r.AddErrors(other.Errors...)
  218. r.AddWarnings(other.Warnings...)
  219. r.MatchCount += other.MatchCount
  220. if other.fieldSchemata != nil {
  221. if r.fieldSchemata == nil {
  222. r.fieldSchemata = other.fieldSchemata
  223. } else {
  224. r.fieldSchemata = append(r.fieldSchemata, other.fieldSchemata...)
  225. }
  226. }
  227. if other.itemSchemata != nil {
  228. if r.itemSchemata == nil {
  229. r.itemSchemata = other.itemSchemata
  230. } else {
  231. r.itemSchemata = append(r.itemSchemata, other.itemSchemata...)
  232. }
  233. }
  234. }
  235. // MergeAsErrors merges this result with the other one(s), preserving match counts etc.
  236. //
  237. // Warnings from input are merged as Errors in the returned merged Result.
  238. func (r *Result) MergeAsErrors(others ...*Result) *Result {
  239. for _, other := range others {
  240. if other != nil {
  241. r.resetCaches()
  242. r.AddErrors(other.Errors...)
  243. r.AddErrors(other.Warnings...)
  244. r.MatchCount += other.MatchCount
  245. }
  246. }
  247. return r
  248. }
  249. // MergeAsWarnings merges this result with the other one(s), preserving match counts etc.
  250. //
  251. // Errors from input are merged as Warnings in the returned merged Result.
  252. func (r *Result) MergeAsWarnings(others ...*Result) *Result {
  253. for _, other := range others {
  254. if other != nil {
  255. r.resetCaches()
  256. r.AddWarnings(other.Errors...)
  257. r.AddWarnings(other.Warnings...)
  258. r.MatchCount += other.MatchCount
  259. }
  260. }
  261. return r
  262. }
  263. // AddErrors adds errors to this validation result (if not already reported).
  264. //
  265. // Since the same check may be passed several times while exploring the
  266. // spec structure (via $ref, ...) reported messages are kept
  267. // unique.
  268. func (r *Result) AddErrors(errors ...error) {
  269. for _, e := range errors {
  270. found := false
  271. if e != nil {
  272. for _, isReported := range r.Errors {
  273. if e.Error() == isReported.Error() {
  274. found = true
  275. break
  276. }
  277. }
  278. if !found {
  279. r.Errors = append(r.Errors, e)
  280. }
  281. }
  282. }
  283. }
  284. // AddWarnings adds warnings to this validation result (if not already reported).
  285. func (r *Result) AddWarnings(warnings ...error) {
  286. for _, e := range warnings {
  287. found := false
  288. if e != nil {
  289. for _, isReported := range r.Warnings {
  290. if e.Error() == isReported.Error() {
  291. found = true
  292. break
  293. }
  294. }
  295. if !found {
  296. r.Warnings = append(r.Warnings, e)
  297. }
  298. }
  299. }
  300. }
  301. func (r *Result) keepRelevantErrors() *Result {
  302. // TODO: this one is going to disapear...
  303. // keepRelevantErrors strips a result from standard errors and keeps
  304. // the ones which are supposedly more accurate.
  305. //
  306. // The original result remains unaffected (creates a new instance of Result).
  307. // This method is used to work around the "matchCount" filter which would otherwise
  308. // strip our result from some accurate error reporting from lower level validators.
  309. //
  310. // NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor
  311. // very efficient. On the other hand, relying on go-openapi/errors to manipulate
  312. // codes would require to change a lot here. So, for the moment, let's go with
  313. // placeholders.
  314. strippedErrors := []error{}
  315. for _, e := range r.Errors {
  316. if strings.HasPrefix(e.Error(), "IMPORTANT!") {
  317. strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
  318. }
  319. }
  320. strippedWarnings := []error{}
  321. for _, e := range r.Warnings {
  322. if strings.HasPrefix(e.Error(), "IMPORTANT!") {
  323. strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
  324. }
  325. }
  326. strippedResult := new(Result)
  327. strippedResult.Errors = strippedErrors
  328. strippedResult.Warnings = strippedWarnings
  329. return strippedResult
  330. }
  331. // IsValid returns true when this result is valid.
  332. //
  333. // Returns true on a nil *Result.
  334. func (r *Result) IsValid() bool {
  335. if r == nil {
  336. return true
  337. }
  338. return len(r.Errors) == 0
  339. }
  340. // HasErrors returns true when this result is invalid.
  341. //
  342. // Returns false on a nil *Result.
  343. func (r *Result) HasErrors() bool {
  344. if r == nil {
  345. return false
  346. }
  347. return !r.IsValid()
  348. }
  349. // HasWarnings returns true when this result contains warnings.
  350. //
  351. // Returns false on a nil *Result.
  352. func (r *Result) HasWarnings() bool {
  353. if r == nil {
  354. return false
  355. }
  356. return len(r.Warnings) > 0
  357. }
  358. // HasErrorsOrWarnings returns true when this result contains
  359. // either errors or warnings.
  360. //
  361. // Returns false on a nil *Result.
  362. func (r *Result) HasErrorsOrWarnings() bool {
  363. if r == nil {
  364. return false
  365. }
  366. return len(r.Errors) > 0 || len(r.Warnings) > 0
  367. }
  368. // Inc increments the match count
  369. func (r *Result) Inc() {
  370. r.MatchCount++
  371. }
  372. // AsError renders this result as an error interface
  373. //
  374. // TODO: reporting / pretty print with path ordered and indented
  375. func (r *Result) AsError() error {
  376. if r.IsValid() {
  377. return nil
  378. }
  379. return errors.CompositeValidationError(r.Errors...)
  380. }
  381. // schemata is an arbitrary number of schemata. It does a distinction between zero,
  382. // one and many schemata to avoid slice allocations.
  383. type schemata struct {
  384. // one is set if there is exactly one schema. In that case multiple must be nil.
  385. one *spec.Schema
  386. // multiple is an arbitrary number of schemas. If it is set, one must be nil.
  387. multiple []*spec.Schema
  388. }
  389. func (s *schemata) Len() int {
  390. if s.one != nil {
  391. return 1
  392. }
  393. return len(s.multiple)
  394. }
  395. func (s *schemata) Slice() []*spec.Schema {
  396. if s == nil {
  397. return nil
  398. }
  399. if s.one != nil {
  400. return []*spec.Schema{s.one}
  401. }
  402. return s.multiple
  403. }
  404. // appendSchemata appends the schemata in other to s. It mutated s in-place.
  405. func (s *schemata) Append(other schemata) {
  406. if other.one == nil && len(other.multiple) == 0 {
  407. return
  408. }
  409. if s.one == nil && len(s.multiple) == 0 {
  410. *s = other
  411. return
  412. }
  413. if s.one != nil {
  414. if other.one != nil {
  415. s.multiple = []*spec.Schema{s.one, other.one}
  416. } else {
  417. t := make([]*spec.Schema, 0, 1+len(other.multiple))
  418. s.multiple = append(append(t, s.one), other.multiple...)
  419. }
  420. s.one = nil
  421. } else {
  422. if other.one != nil {
  423. s.multiple = append(s.multiple, other.one)
  424. } else {
  425. if cap(s.multiple) >= len(s.multiple)+len(other.multiple) {
  426. s.multiple = append(s.multiple, other.multiple...)
  427. } else {
  428. t := make([]*spec.Schema, 0, len(s.multiple)+len(other.multiple))
  429. s.multiple = append(append(t, s.multiple...), other.multiple...)
  430. }
  431. }
  432. }
  433. }