podman

Форк
0
486 строк · 13.3 Кб
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

15
package validate
16

17
import (
18
	"fmt"
19
	"reflect"
20
	"strings"
21

22
	"github.com/go-openapi/errors"
23
	"github.com/go-openapi/spec"
24
)
25

26
// Result represents a validation result set, composed of
27
// errors and warnings.
28
//
29
// It is used to keep track of all detected errors and warnings during
30
// the validation of a specification.
31
//
32
// Matchcount is used to determine
33
// which errors are relevant in the case of AnyOf, OneOf
34
// schema validation. Results from the validation branch
35
// with most matches get eventually selected.
36
//
37
// TODO: keep path of key originating the error
38
type Result struct {
39
	Errors     []error
40
	Warnings   []error
41
	MatchCount int
42

43
	// the object data
44
	data interface{}
45

46
	// Schemata for the root object
47
	rootObjectSchemata schemata
48
	// Schemata for object fields
49
	fieldSchemata []fieldSchemata
50
	// Schemata for slice items
51
	itemSchemata []itemSchemata
52

53
	cachedFieldSchemta map[FieldKey][]*spec.Schema
54
	cachedItemSchemata map[ItemKey][]*spec.Schema
55
}
56

57
// FieldKey is a pair of an object and a field, usable as a key for a map.
58
type FieldKey struct {
59
	object reflect.Value // actually a map[string]interface{}, but the latter cannot be a key
60
	field  string
61
}
62

63
// ItemKey is a pair of a slice and an index, usable as a key for a map.
64
type ItemKey struct {
65
	slice reflect.Value // actually a []interface{}, but the latter cannot be a key
66
	index int
67
}
68

69
// NewFieldKey returns a pair of an object and field usable as a key of a map.
70
func NewFieldKey(obj map[string]interface{}, field string) FieldKey {
71
	return FieldKey{object: reflect.ValueOf(obj), field: field}
72
}
73

74
// Object returns the underlying object of this key.
75
func (fk *FieldKey) Object() map[string]interface{} {
76
	return fk.object.Interface().(map[string]interface{})
77
}
78

79
// Field returns the underlying field of this key.
80
func (fk *FieldKey) Field() string {
81
	return fk.field
82
}
83

84
// NewItemKey returns a pair of a slice and index usable as a key of a map.
85
func NewItemKey(slice interface{}, i int) ItemKey {
86
	return ItemKey{slice: reflect.ValueOf(slice), index: i}
87
}
88

89
// Slice returns the underlying slice of this key.
90
func (ik *ItemKey) Slice() []interface{} {
91
	return ik.slice.Interface().([]interface{})
92
}
93

94
// Index returns the underlying index of this key.
95
func (ik *ItemKey) Index() int {
96
	return ik.index
97
}
98

99
type fieldSchemata struct {
100
	obj      map[string]interface{}
101
	field    string
102
	schemata schemata
103
}
104

105
type itemSchemata struct {
106
	slice    reflect.Value
107
	index    int
108
	schemata schemata
109
}
110

111
// Merge merges this result with the other one(s), preserving match counts etc.
112
func (r *Result) Merge(others ...*Result) *Result {
113
	for _, other := range others {
114
		if other == nil {
115
			continue
116
		}
117
		r.mergeWithoutRootSchemata(other)
118
		r.rootObjectSchemata.Append(other.rootObjectSchemata)
119
	}
120
	return r
121
}
122

123
// Data returns the original data object used for validation. Mutating this renders
124
// the result invalid.
125
func (r *Result) Data() interface{} {
126
	return r.data
127
}
128

129
// RootObjectSchemata returns the schemata which apply to the root object.
130
func (r *Result) RootObjectSchemata() []*spec.Schema {
131
	return r.rootObjectSchemata.Slice()
132
}
133

134
// FieldSchemata returns the schemata which apply to fields in objects.
135
// nolint: dupl
136
func (r *Result) FieldSchemata() map[FieldKey][]*spec.Schema {
137
	if r.cachedFieldSchemta != nil {
138
		return r.cachedFieldSchemta
139
	}
140

141
	ret := make(map[FieldKey][]*spec.Schema, len(r.fieldSchemata))
142
	for _, fs := range r.fieldSchemata {
143
		key := NewFieldKey(fs.obj, fs.field)
144
		if fs.schemata.one != nil {
145
			ret[key] = append(ret[key], fs.schemata.one)
146
		} else if len(fs.schemata.multiple) > 0 {
147
			ret[key] = append(ret[key], fs.schemata.multiple...)
148
		}
149
	}
150
	r.cachedFieldSchemta = ret
151
	return ret
152
}
153

154
// ItemSchemata returns the schemata which apply to items in slices.
155
// nolint: dupl
156
func (r *Result) ItemSchemata() map[ItemKey][]*spec.Schema {
157
	if r.cachedItemSchemata != nil {
158
		return r.cachedItemSchemata
159
	}
160

161
	ret := make(map[ItemKey][]*spec.Schema, len(r.itemSchemata))
162
	for _, ss := range r.itemSchemata {
163
		key := NewItemKey(ss.slice, ss.index)
164
		if ss.schemata.one != nil {
165
			ret[key] = append(ret[key], ss.schemata.one)
166
		} else if len(ss.schemata.multiple) > 0 {
167
			ret[key] = append(ret[key], ss.schemata.multiple...)
168
		}
169
	}
170
	r.cachedItemSchemata = ret
171
	return ret
172
}
173

174
func (r *Result) resetCaches() {
175
	r.cachedFieldSchemta = nil
176
	r.cachedItemSchemata = nil
177
}
178

179
// mergeForField merges other into r, assigning other's root schemata to the given Object and field name.
180
// nolint: unparam
181
func (r *Result) mergeForField(obj map[string]interface{}, field string, other *Result) *Result {
182
	if other == nil {
183
		return r
184
	}
185
	r.mergeWithoutRootSchemata(other)
186

187
	if other.rootObjectSchemata.Len() > 0 {
188
		if r.fieldSchemata == nil {
189
			r.fieldSchemata = make([]fieldSchemata, len(obj))
190
		}
191
		r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{
192
			obj:      obj,
193
			field:    field,
194
			schemata: other.rootObjectSchemata,
195
		})
196
	}
197

198
	return r
199
}
200

201
// mergeForSlice merges other into r, assigning other's root schemata to the given slice and index.
202
// nolint: unparam
203
func (r *Result) mergeForSlice(slice reflect.Value, i int, other *Result) *Result {
204
	if other == nil {
205
		return r
206
	}
207
	r.mergeWithoutRootSchemata(other)
208

209
	if other.rootObjectSchemata.Len() > 0 {
210
		if r.itemSchemata == nil {
211
			r.itemSchemata = make([]itemSchemata, slice.Len())
212
		}
213
		r.itemSchemata = append(r.itemSchemata, itemSchemata{
214
			slice:    slice,
215
			index:    i,
216
			schemata: other.rootObjectSchemata,
217
		})
218
	}
219

220
	return r
221
}
222

223
// addRootObjectSchemata adds the given schemata for the root object of the result.
224
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
225
func (r *Result) addRootObjectSchemata(s *spec.Schema) {
226
	r.rootObjectSchemata.Append(schemata{one: s})
227
}
228

229
// addPropertySchemata adds the given schemata for the object and field.
230
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
231
func (r *Result) addPropertySchemata(obj map[string]interface{}, fld string, schema *spec.Schema) {
232
	if r.fieldSchemata == nil {
233
		r.fieldSchemata = make([]fieldSchemata, 0, len(obj))
234
	}
235
	r.fieldSchemata = append(r.fieldSchemata, fieldSchemata{obj: obj, field: fld, schemata: schemata{one: schema}})
236
}
237

238
/*
239
// addSliceSchemata adds the given schemata for the slice and index.
240
// The slice schemata might be reused. I.e. do not modify it after being added to a result.
241
func (r *Result) addSliceSchemata(slice reflect.Value, i int, schema *spec.Schema) {
242
	if r.itemSchemata == nil {
243
		r.itemSchemata = make([]itemSchemata, 0, slice.Len())
244
	}
245
	r.itemSchemata = append(r.itemSchemata, itemSchemata{slice: slice, index: i, schemata: schemata{one: schema}})
246
}
247
*/
248

249
// mergeWithoutRootSchemata merges other into r, ignoring the rootObject schemata.
250
func (r *Result) mergeWithoutRootSchemata(other *Result) {
251
	r.resetCaches()
252
	r.AddErrors(other.Errors...)
253
	r.AddWarnings(other.Warnings...)
254
	r.MatchCount += other.MatchCount
255

256
	if other.fieldSchemata != nil {
257
		if r.fieldSchemata == nil {
258
			r.fieldSchemata = other.fieldSchemata
259
		} else {
260
			r.fieldSchemata = append(r.fieldSchemata, other.fieldSchemata...)
261
		}
262
	}
263

264
	if other.itemSchemata != nil {
265
		if r.itemSchemata == nil {
266
			r.itemSchemata = other.itemSchemata
267
		} else {
268
			r.itemSchemata = append(r.itemSchemata, other.itemSchemata...)
269
		}
270
	}
271
}
272

273
// MergeAsErrors merges this result with the other one(s), preserving match counts etc.
274
//
275
// Warnings from input are merged as Errors in the returned merged Result.
276
func (r *Result) MergeAsErrors(others ...*Result) *Result {
277
	for _, other := range others {
278
		if other != nil {
279
			r.resetCaches()
280
			r.AddErrors(other.Errors...)
281
			r.AddErrors(other.Warnings...)
282
			r.MatchCount += other.MatchCount
283
		}
284
	}
285
	return r
286
}
287

288
// MergeAsWarnings merges this result with the other one(s), preserving match counts etc.
289
//
290
// Errors from input are merged as Warnings in the returned merged Result.
291
func (r *Result) MergeAsWarnings(others ...*Result) *Result {
292
	for _, other := range others {
293
		if other != nil {
294
			r.resetCaches()
295
			r.AddWarnings(other.Errors...)
296
			r.AddWarnings(other.Warnings...)
297
			r.MatchCount += other.MatchCount
298
		}
299
	}
300
	return r
301
}
302

303
// AddErrors adds errors to this validation result (if not already reported).
304
//
305
// Since the same check may be passed several times while exploring the
306
// spec structure (via $ref, ...) reported messages are kept
307
// unique.
308
func (r *Result) AddErrors(errors ...error) {
309
	for _, e := range errors {
310
		found := false
311
		if e != nil {
312
			for _, isReported := range r.Errors {
313
				if e.Error() == isReported.Error() {
314
					found = true
315
					break
316
				}
317
			}
318
			if !found {
319
				r.Errors = append(r.Errors, e)
320
			}
321
		}
322
	}
323
}
324

325
// AddWarnings adds warnings to this validation result (if not already reported).
326
func (r *Result) AddWarnings(warnings ...error) {
327
	for _, e := range warnings {
328
		found := false
329
		if e != nil {
330
			for _, isReported := range r.Warnings {
331
				if e.Error() == isReported.Error() {
332
					found = true
333
					break
334
				}
335
			}
336
			if !found {
337
				r.Warnings = append(r.Warnings, e)
338
			}
339
		}
340
	}
341
}
342

343
func (r *Result) keepRelevantErrors() *Result {
344
	// TODO: this one is going to disapear...
345
	// keepRelevantErrors strips a result from standard errors and keeps
346
	// the ones which are supposedly more accurate.
347
	//
348
	// The original result remains unaffected (creates a new instance of Result).
349
	// This method is used to work around the "matchCount" filter which would otherwise
350
	// strip our result from some accurate error reporting from lower level validators.
351
	//
352
	// NOTE: this implementation with a placeholder (IMPORTANT!) is neither clean nor
353
	// very efficient. On the other hand, relying on go-openapi/errors to manipulate
354
	// codes would require to change a lot here. So, for the moment, let's go with
355
	// placeholders.
356
	strippedErrors := []error{}
357
	for _, e := range r.Errors {
358
		if strings.HasPrefix(e.Error(), "IMPORTANT!") {
359
			strippedErrors = append(strippedErrors, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
360
		}
361
	}
362
	strippedWarnings := []error{}
363
	for _, e := range r.Warnings {
364
		if strings.HasPrefix(e.Error(), "IMPORTANT!") {
365
			strippedWarnings = append(strippedWarnings, fmt.Errorf(strings.TrimPrefix(e.Error(), "IMPORTANT!")))
366
		}
367
	}
368
	strippedResult := new(Result)
369
	strippedResult.Errors = strippedErrors
370
	strippedResult.Warnings = strippedWarnings
371
	return strippedResult
372
}
373

374
// IsValid returns true when this result is valid.
375
//
376
// Returns true on a nil *Result.
377
func (r *Result) IsValid() bool {
378
	if r == nil {
379
		return true
380
	}
381
	return len(r.Errors) == 0
382
}
383

384
// HasErrors returns true when this result is invalid.
385
//
386
// Returns false on a nil *Result.
387
func (r *Result) HasErrors() bool {
388
	if r == nil {
389
		return false
390
	}
391
	return !r.IsValid()
392
}
393

394
// HasWarnings returns true when this result contains warnings.
395
//
396
// Returns false on a nil *Result.
397
func (r *Result) HasWarnings() bool {
398
	if r == nil {
399
		return false
400
	}
401
	return len(r.Warnings) > 0
402
}
403

404
// HasErrorsOrWarnings returns true when this result contains
405
// either errors or warnings.
406
//
407
// Returns false on a nil *Result.
408
func (r *Result) HasErrorsOrWarnings() bool {
409
	if r == nil {
410
		return false
411
	}
412
	return len(r.Errors) > 0 || len(r.Warnings) > 0
413
}
414

415
// Inc increments the match count
416
func (r *Result) Inc() {
417
	r.MatchCount++
418
}
419

420
// AsError renders this result as an error interface
421
//
422
// TODO: reporting / pretty print with path ordered and indented
423
func (r *Result) AsError() error {
424
	if r.IsValid() {
425
		return nil
426
	}
427
	return errors.CompositeValidationError(r.Errors...)
428
}
429

430
// schemata is an arbitrary number of schemata. It does a distinction between zero,
431
// one and many schemata to avoid slice allocations.
432
type schemata struct {
433
	// one is set if there is exactly one schema. In that case multiple must be nil.
434
	one *spec.Schema
435
	// multiple is an arbitrary number of schemas. If it is set, one must be nil.
436
	multiple []*spec.Schema
437
}
438

439
func (s *schemata) Len() int {
440
	if s.one != nil {
441
		return 1
442
	}
443
	return len(s.multiple)
444
}
445

446
func (s *schemata) Slice() []*spec.Schema {
447
	if s == nil {
448
		return nil
449
	}
450
	if s.one != nil {
451
		return []*spec.Schema{s.one}
452
	}
453
	return s.multiple
454
}
455

456
// appendSchemata appends the schemata in other to s. It mutated s in-place.
457
func (s *schemata) Append(other schemata) {
458
	if other.one == nil && len(other.multiple) == 0 {
459
		return
460
	}
461
	if s.one == nil && len(s.multiple) == 0 {
462
		*s = other
463
		return
464
	}
465

466
	if s.one != nil {
467
		if other.one != nil {
468
			s.multiple = []*spec.Schema{s.one, other.one}
469
		} else {
470
			t := make([]*spec.Schema, 0, 1+len(other.multiple))
471
			s.multiple = append(append(t, s.one), other.multiple...)
472
		}
473
		s.one = nil
474
	} else {
475
		if other.one != nil {
476
			s.multiple = append(s.multiple, other.one)
477
		} else {
478
			if cap(s.multiple) >= len(s.multiple)+len(other.multiple) {
479
				s.multiple = append(s.multiple, other.multiple...)
480
			} else {
481
				t := make([]*spec.Schema, 0, len(s.multiple)+len(other.multiple))
482
				s.multiple = append(append(t, s.multiple...), other.multiple...)
483
			}
484
		}
485
	}
486
}
487

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.