You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

result.go 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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. func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
  115. if r.cachedFieldSchemta != nil {
  116. return r.cachedFieldSchemta
  117. }
  118. ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
  119. for _, fs := range r.fieldSchemata {
  120. key := NewFieldKey(fs.obj, fs.field)
  121. if fs.schemata.one != nil {
  122. ret[key] = append(ret[key], fs.schemata.one)
  123. } else if len(fs.schemata.multiple) > 0 {
  124. ret[key] = append(ret[key], fs.schemata.multiple...)
  125. }
  126. }
  127. r.cachedFieldSchemta = ret
  128. return ret
  129. }
  130. // ItemSchemata returns the schemata which apply to items in slices.
  131. func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
  132. if r.cachedItemSchemata != nil {
  133. return r.cachedItemSchemata
  134. }
  135. ret := make(map[ItemKey][]*spec.Schema, len(r.itemSchemata))
  136. for _, ss := range r.itemSchemata {
  137. key := NewItemKey(ss.slice, ss.index)
  138. if ss.schemata.one != nil {
  139. ret[key] = append(ret[key], ss.schemata.one)
  140. } else if len(ss.schemata.multiple) > 0 {
  141. ret[key] = append(ret[key], ss.schemata.multiple...)
  142. }
  143. }
  144. r.cachedItemSchemata = ret
  145. return ret
  146. }
  147. func (r *Result) resetCaches() {
  148. r.cachedFieldSchemta = nil
  149. r.cachedItemSchemata = nil
  150. }
  151. // mergeForField merges other into r, assigning other's root schemata to the given Object and field name.
  152. func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
  153. if other == nil {
  154. return r
  155. }
  156. r.mergeWithoutRootSchemata(other)
  157. if other.rootObjectSchemata.Len() > 0 {
  158. if r.fieldSchemata == nil {
  159. r.fieldSchemata = make([]fieldSchemata, len(obj))
  160. }
  161. r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{
  162. obj: obj,
  163. field: field,
  164. schemata: other.rootObjectSchemata,
  165. })
  166. }
  167. return r
  168. }
  169. // mergeForSlice merges other into r, assigning other's root schemata to the given slice and index.
  170. func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
  171. if other == nil {
  172. return r
  173. }
  174. r.mergeWithoutRootSchemata(other)
  175. if other.rootObjectSchemata.Len() > 0 {
  176. if r.itemSchemata == nil {
  177. r.itemSchemata = make([]itemSchemata, slice.Len())
  178. }
  179. r.itemSchemata = append(r.itemSchemata, itemSchemata{
  180. slice: slice,
  181. index: i,
  182. schemata: other.rootObjectSchemata,
  183. })
  184. }
  185. return r
  186. }
  187. // addRootObjectSchemata adds the given schemata for the root object of the result.
  188. // The slice schemata might be reused. I.e. do not modify it after being added to a result.
  189. func (r *Result) addRootObjectSchemata(s *spec.Schema) {
  190. r.rootObjectSchemata.Append(schemata{one: s})
  191. }
  192. // addPropertySchemata adds the given schemata for the object and field.
  193. // The slice schemata might be reused. I.e. do not modify it after being added to a result.
  194. func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) {
  195. if r.fieldSchemata == nil {
  196. r.fieldSchemata = make([]fieldSchemata, 0, len(obj))
  197. }
  198. r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}})
  199. }
  200. // addSliceSchemata adds the given schemata for the slice and index.
  201. // The slice schemata might be reused. I.e. do not modify it after being added to a result.
  202. func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schema) {
  203. if r.itemSchemata == nil {
  204. r.itemSchemata = make([]itemSchemata, 0, slice.Len())
  205. }
  206. r.itemSchemata = append(r.itemSchemata, itemSchemata{slice: slice, index: i, schemata: schemata{one: schema}})
  207. }
  208. // mergeWithoutRootSchemata merges other into r, ignoring the rootObject schemata.
  209. func (r *Result) mergeWithoutRootSchemata(other *Result) {
  210. r.resetCaches()
  211. r.AddErrors(other.Errors...)
  212. r.AddWarnings(other.Warnings...)
  213. r.MatchCount += other.MatchCount
  214. if other.fieldSchemata != nil {
  215. if r.fieldSchemata == nil {
  216. r.fieldSchemata = other.fieldSchemata
  217. } else {
  218. for _, x := range other.fieldSchemata {
  219. r.fieldSchemata = append(r.fieldSchemata, x)
  220. }
  221. }
  222. }
  223. if other.itemSchemata != nil {
  224. if r.itemSchemata == nil {
  225. r.itemSchemata = other.itemSchemata
  226. } else {
  227. for _, x := range other.itemSchemata {
  228. r.itemSchemata = append(r.itemSchemata, x)
  229. }
  230. }
  231. }
  232. }
  233. // MergeAsErrors merges this result with the other one(s), preserving match counts etc.
  234. //
  235. // Warnings from input are merged as Errors in the returned merged Result.
  236. func (r *Result) MergeAsErrors(others ...*Result) *Result {
  237. for _, other := range others {
  238. if other != nil {
  239. r.resetCaches()
  240. r.AddErrors(other.Errors...)
  241. r.AddErrors(other.Warnings...)
  242. r.MatchCount += other.MatchCount
  243. }
  244. }
  245. return r
  246. }
  247. // MergeAsWarnings merges this result with the other one(s), preserving match counts etc.
  248. //
  249. // Errors from input are merged as Warnings in the returned merged Result.
  250. func (r *Result) MergeAsWarnings(others ...*Result) *Result {
  251. for _, other := range others {
  252. if other != nil {
  253. r.resetCaches()
  254. r.AddWarnings(other.Errors...)
  255. r.AddWarnings(other.Warnings...)
  256. r.MatchCount += other.MatchCount
  257. }
  258. }
  259. return r
  260. }
  261. // AddErrors adds errors to this validation result (if not already reported).
  262. //
  263. // Since the same check may be passed several times while exploring the
  264. // spec structure (via $ref, ...) reported messages are kept
  265. // unique.
  266. func (r *Result) AddErrors(errors ...error) {
  267. for _, e := range errors {
  268. found := false
  269. if e != nil {
  270. for _, isReported := range r.Errors {
  271. if e.Error() == isReported.Error() {
  272. found = true
  273. break
  274. }
  275. }
  276. if !found {
  277. r.Errors = append(r.Errors, e)
  278. }
  279. }
  280. }
  281. }
  282. // AddWarnings adds warnings to this validation result (if not already reported).
  283. func (r *Result) AddWarnings(warnings ...error) {
  284. for _, e := range warnings {
  285. found := false
  286. if e != nil {
  287. for _, isReported := range r.Warnings {
  288. if e.Error() == isReported.Error() {
  289. found = true
  290. break
  291. }
  292. }
  293. if !found {
  294. r.Warnings = append(r.Warnings, e)
  295. }
  296. }
  297. }
  298. }
  299. func (r *Result) keepRelevantErrors() *Result {
  300. // TODO: this one is going to disapear...
  301. // keepRelevantErrors strips a result from standard errors and keeps
  302. // the ones which are supposedly more accurate.
  303. //
  304. // The original result remains unaffected (creates a new instance of Result).
  305. // This method is used to work around the "matchCount" filter which would otherwise
  306. // strip our result from some accurate error reporting from lower level validators.
  307. //
  308. // NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor
  309. // very efficient. On the other hand, relying on go-openapi/errors to manipulate
  310. // codes would require to change a lot here. So, for the moment, let's go with
  311. // placeholders.
  312. strippedErrors := []error{}
  313. for _, e := range r.Errors {
  314. if strings.HasPrefix(e.Error(), "IMPORTANT!") {
  315. strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
  316. }
  317. }
  318. strippedWarnings := []error{}
  319. for _, e := range r.Warnings {
  320. if strings.HasPrefix(e.Error(), "IMPORTANT!") {
  321. strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
  322. }
  323. }
  324. strippedResult := new(Result)
  325. strippedResult.Errors = strippedErrors
  326. strippedResult.Warnings = strippedWarnings
  327. return strippedResult
  328. }
  329. // IsValid returns true when this result is valid.
  330. //
  331. // Returns true on a nil *Result.
  332. func (r *Result) IsValid() bool {
  333. if r == nil {
  334. return true
  335. }
  336. return len(r.Errors) == 0
  337. }
  338. // HasErrors returns true when this result is invalid.
  339. //
  340. // Returns false on a nil *Result.
  341. func (r *Result) HasErrors() bool {
  342. if r == nil {
  343. return false
  344. }
  345. return !r.IsValid()
  346. }
  347. // HasWarnings returns true when this result contains warnings.
  348. //
  349. // Returns false on a nil *Result.
  350. func (r *Result) HasWarnings() bool {
  351. if r == nil {
  352. return false
  353. }
  354. return len(r.Warnings) > 0
  355. }
  356. // HasErrorsOrWarnings returns true when this result contains
  357. // either errors or warnings.
  358. //
  359. // Returns false on a nil *Result.
  360. func (r *Result) HasErrorsOrWarnings() bool {
  361. if r == nil {
  362. return false
  363. }
  364. return len(r.Errors) > 0 || len(r.Warnings) > 0
  365. }
  366. // Inc increments the match count
  367. func (r *Result) Inc() {
  368. r.MatchCount++
  369. }
  370. // AsError renders this result as an error interface
  371. //
  372. // TODO: reporting / pretty print with path ordered and indented
  373. func (r *Result) AsError() error {
  374. if r.IsValid() {
  375. return nil
  376. }
  377. return errors.CompositeValidationError(r.Errors...)
  378. }
  379. // schemata is an arbitrary number of schemata. It does a distinction between zero,
  380. // one and many schemata to avoid slice allocations.
  381. type schemata struct {
  382. // one is set if there is exactly one schema. In that case multiple must be nil.
  383. one *spec.Schema
  384. // multiple is an arbitrary number of schemas. If it is set, one must be nil.
  385. multiple []*spec.Schema
  386. }
  387. func (s *schemata) Len() int {
  388. if s.one != nil {
  389. return 1
  390. }
  391. return len(s.multiple)
  392. }
  393. func (s *schemata) Slice() []*spec.Schema {
  394. if s == nil {
  395. return nil
  396. }
  397. if s.one != nil {
  398. return []*spec.Schema{s.one}
  399. }
  400. return s.multiple
  401. }
  402. // appendSchemata appends the schemata in other to s. It mutated s in-place.
  403. func (s *schemata) Append(other schemata) {
  404. if other.one == nil && len(other.multiple) == 0 {
  405. return
  406. }
  407. if s.one == nil && len(s.multiple) == 0 {
  408. *s = other
  409. return
  410. }
  411. if s.one != nil {
  412. if other.one != nil {
  413. s.multiple = []*spec.Schema{s.one, other.one}
  414. } else {
  415. t := make([]*spec.Schema, 0, 1+len(other.multiple))
  416. s.multiple = append(append(t, s.one), other.multiple...)
  417. }
  418. s.one = nil
  419. } else {
  420. if other.one != nil {
  421. s.multiple = append(s.multiple, other.one)
  422. } else {
  423. if cap(s.multiple) >= len(s.multiple)+len(other.multiple) {
  424. s.multiple = append(s.multiple, other.multiple...)
  425. } else {
  426. t := make([]*spec.Schema, 0, len(s.multiple)+len(other.multiple))
  427. s.multiple = append(append(t, s.multiple...), other.multiple...)
  428. }
  429. }
  430. }
  431. }