podman

Форк
0
594 строки · 16.7 Кб
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 spec
16

17
import (
18
	"encoding/json"
19
	"fmt"
20
)
21

22
// ExpandOptions provides options for the spec expander.
23
//
24
// RelativeBase is the path to the root document. This can be a remote URL or a path to a local file.
25
//
26
// If left empty, the root document is assumed to be located in the current working directory:
27
// all relative $ref's will be resolved from there.
28
//
29
// PathLoader injects a document loading method. By default, this resolves to the function provided by the SpecLoader package variable.
30
//
31
type ExpandOptions struct {
32
	RelativeBase        string                                // the path to the root document to expand. This is a file, not a directory
33
	SkipSchemas         bool                                  // do not expand schemas, just paths, parameters and responses
34
	ContinueOnError     bool                                  // continue expanding even after and error is found
35
	PathLoader          func(string) (json.RawMessage, error) `json:"-"` // the document loading method that takes a path as input and yields a json document
36
	AbsoluteCircularRef bool                                  // circular $ref remaining after expansion remain absolute URLs
37
}
38

39
func optionsOrDefault(opts *ExpandOptions) *ExpandOptions {
40
	if opts != nil {
41
		clone := *opts // shallow clone to avoid internal changes to be propagated to the caller
42
		if clone.RelativeBase != "" {
43
			clone.RelativeBase = normalizeBase(clone.RelativeBase)
44
		}
45
		// if the relative base is empty, let the schema loader choose a pseudo root document
46
		return &clone
47
	}
48
	return &ExpandOptions{}
49
}
50

51
// ExpandSpec expands the references in a swagger spec
52
func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
53
	options = optionsOrDefault(options)
54
	resolver := defaultSchemaLoader(spec, options, nil, nil)
55

56
	specBasePath := options.RelativeBase
57

58
	if !options.SkipSchemas {
59
		for key, definition := range spec.Definitions {
60
			parentRefs := make([]string, 0, 10)
61
			parentRefs = append(parentRefs, fmt.Sprintf("#/definitions/%s", key))
62

63
			def, err := expandSchema(definition, parentRefs, resolver, specBasePath)
64
			if resolver.shouldStopOnError(err) {
65
				return err
66
			}
67
			if def != nil {
68
				spec.Definitions[key] = *def
69
			}
70
		}
71
	}
72

73
	for key := range spec.Parameters {
74
		parameter := spec.Parameters[key]
75
		if err := expandParameterOrResponse(&parameter, resolver, specBasePath); resolver.shouldStopOnError(err) {
76
			return err
77
		}
78
		spec.Parameters[key] = parameter
79
	}
80

81
	for key := range spec.Responses {
82
		response := spec.Responses[key]
83
		if err := expandParameterOrResponse(&response, resolver, specBasePath); resolver.shouldStopOnError(err) {
84
			return err
85
		}
86
		spec.Responses[key] = response
87
	}
88

89
	if spec.Paths != nil {
90
		for key := range spec.Paths.Paths {
91
			pth := spec.Paths.Paths[key]
92
			if err := expandPathItem(&pth, resolver, specBasePath); resolver.shouldStopOnError(err) {
93
				return err
94
			}
95
			spec.Paths.Paths[key] = pth
96
		}
97
	}
98

99
	return nil
100
}
101

102
const rootBase = ".root"
103

104
// baseForRoot loads in the cache the root document and produces a fake ".root" base path entry
105
// for further $ref resolution
106
//
107
// Setting the cache is optional and this parameter may safely be left to nil.
108
func baseForRoot(root interface{}, cache ResolutionCache) string {
109
	if root == nil {
110
		return ""
111
	}
112

113
	// cache the root document to resolve $ref's
114
	normalizedBase := normalizeBase(rootBase)
115
	cache.Set(normalizedBase, root)
116

117
	return normalizedBase
118
}
119

120
// ExpandSchema expands the refs in the schema object with reference to the root object.
121
//
122
// go-openapi/validate uses this function.
123
//
124
// Notice that it is impossible to reference a json schema in a different document other than root
125
// (use ExpandSchemaWithBasePath to resolve external references).
126
//
127
// Setting the cache is optional and this parameter may safely be left to nil.
128
func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
129
	cache = cacheOrDefault(cache)
130
	if root == nil {
131
		root = schema
132
	}
133

134
	opts := &ExpandOptions{
135
		// when a root is specified, cache the root as an in-memory document for $ref retrieval
136
		RelativeBase:    baseForRoot(root, cache),
137
		SkipSchemas:     false,
138
		ContinueOnError: false,
139
	}
140

141
	return ExpandSchemaWithBasePath(schema, cache, opts)
142
}
143

144
// ExpandSchemaWithBasePath expands the refs in the schema object, base path configured through expand options.
145
//
146
// Setting the cache is optional and this parameter may safely be left to nil.
147
func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error {
148
	if schema == nil {
149
		return nil
150
	}
151

152
	cache = cacheOrDefault(cache)
153

154
	opts = optionsOrDefault(opts)
155

156
	resolver := defaultSchemaLoader(nil, opts, cache, nil)
157

158
	parentRefs := make([]string, 0, 10)
159
	s, err := expandSchema(*schema, parentRefs, resolver, opts.RelativeBase)
160
	if err != nil {
161
		return err
162
	}
163
	if s != nil {
164
		// guard for when continuing on error
165
		*schema = *s
166
	}
167

168
	return nil
169
}
170

171
func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
172
	if target.Items == nil {
173
		return &target, nil
174
	}
175

176
	// array
177
	if target.Items.Schema != nil {
178
		t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath)
179
		if err != nil {
180
			return nil, err
181
		}
182
		*target.Items.Schema = *t
183
	}
184

185
	// tuple
186
	for i := range target.Items.Schemas {
187
		t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath)
188
		if err != nil {
189
			return nil, err
190
		}
191
		target.Items.Schemas[i] = *t
192
	}
193

194
	return &target, nil
195
}
196

197
func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
198
	if target.Ref.String() == "" && target.Ref.IsRoot() {
199
		newRef := normalizeRef(&target.Ref, basePath)
200
		target.Ref = *newRef
201
		return &target, nil
202
	}
203

204
	// change the base path of resolution when an ID is encountered
205
	// otherwise the basePath should inherit the parent's
206
	if target.ID != "" {
207
		basePath, _ = resolver.setSchemaID(target, target.ID, basePath)
208
	}
209

210
	if target.Ref.String() != "" {
211
		return expandSchemaRef(target, parentRefs, resolver, basePath)
212
	}
213

214
	for k := range target.Definitions {
215
		tt, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath)
216
		if resolver.shouldStopOnError(err) {
217
			return &target, err
218
		}
219
		if tt != nil {
220
			target.Definitions[k] = *tt
221
		}
222
	}
223

224
	t, err := expandItems(target, parentRefs, resolver, basePath)
225
	if resolver.shouldStopOnError(err) {
226
		return &target, err
227
	}
228
	if t != nil {
229
		target = *t
230
	}
231

232
	for i := range target.AllOf {
233
		t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath)
234
		if resolver.shouldStopOnError(err) {
235
			return &target, err
236
		}
237
		if t != nil {
238
			target.AllOf[i] = *t
239
		}
240
	}
241

242
	for i := range target.AnyOf {
243
		t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath)
244
		if resolver.shouldStopOnError(err) {
245
			return &target, err
246
		}
247
		if t != nil {
248
			target.AnyOf[i] = *t
249
		}
250
	}
251

252
	for i := range target.OneOf {
253
		t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath)
254
		if resolver.shouldStopOnError(err) {
255
			return &target, err
256
		}
257
		if t != nil {
258
			target.OneOf[i] = *t
259
		}
260
	}
261

262
	if target.Not != nil {
263
		t, err := expandSchema(*target.Not, parentRefs, resolver, basePath)
264
		if resolver.shouldStopOnError(err) {
265
			return &target, err
266
		}
267
		if t != nil {
268
			*target.Not = *t
269
		}
270
	}
271

272
	for k := range target.Properties {
273
		t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath)
274
		if resolver.shouldStopOnError(err) {
275
			return &target, err
276
		}
277
		if t != nil {
278
			target.Properties[k] = *t
279
		}
280
	}
281

282
	if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil {
283
		t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath)
284
		if resolver.shouldStopOnError(err) {
285
			return &target, err
286
		}
287
		if t != nil {
288
			*target.AdditionalProperties.Schema = *t
289
		}
290
	}
291

292
	for k := range target.PatternProperties {
293
		t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath)
294
		if resolver.shouldStopOnError(err) {
295
			return &target, err
296
		}
297
		if t != nil {
298
			target.PatternProperties[k] = *t
299
		}
300
	}
301

302
	for k := range target.Dependencies {
303
		if target.Dependencies[k].Schema != nil {
304
			t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath)
305
			if resolver.shouldStopOnError(err) {
306
				return &target, err
307
			}
308
			if t != nil {
309
				*target.Dependencies[k].Schema = *t
310
			}
311
		}
312
	}
313

314
	if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil {
315
		t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath)
316
		if resolver.shouldStopOnError(err) {
317
			return &target, err
318
		}
319
		if t != nil {
320
			*target.AdditionalItems.Schema = *t
321
		}
322
	}
323
	return &target, nil
324
}
325

326
func expandSchemaRef(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
327
	// if a Ref is found, all sibling fields are skipped
328
	// Ref also changes the resolution scope of children expandSchema
329

330
	// here the resolution scope is changed because a $ref was encountered
331
	normalizedRef := normalizeRef(&target.Ref, basePath)
332
	normalizedBasePath := normalizedRef.RemoteURI()
333

334
	if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
335
		// this means there is a cycle in the recursion tree: return the Ref
336
		// - circular refs cannot be expanded. We leave them as ref.
337
		// - denormalization means that a new local file ref is set relative to the original basePath
338
		debugLog("short circuit circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s",
339
			basePath, normalizedBasePath, normalizedRef.String())
340
		if !resolver.options.AbsoluteCircularRef {
341
			target.Ref = denormalizeRef(normalizedRef, resolver.context.basePath, resolver.context.rootID)
342
		} else {
343
			target.Ref = *normalizedRef
344
		}
345
		return &target, nil
346
	}
347

348
	var t *Schema
349
	err := resolver.Resolve(&target.Ref, &t, basePath)
350
	if resolver.shouldStopOnError(err) {
351
		return nil, err
352
	}
353

354
	if t == nil {
355
		// guard for when continuing on error
356
		return &target, nil
357
	}
358

359
	parentRefs = append(parentRefs, normalizedRef.String())
360
	transitiveResolver := resolver.transitiveResolver(basePath, target.Ref)
361

362
	basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath)
363

364
	return expandSchema(*t, parentRefs, transitiveResolver, basePath)
365
}
366

367
func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error {
368
	if pathItem == nil {
369
		return nil
370
	}
371

372
	parentRefs := make([]string, 0, 10)
373
	if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) {
374
		return err
375
	}
376

377
	if pathItem.Ref.String() != "" {
378
		transitiveResolver := resolver.transitiveResolver(basePath, pathItem.Ref)
379
		basePath = transitiveResolver.updateBasePath(resolver, basePath)
380
		resolver = transitiveResolver
381
	}
382

383
	pathItem.Ref = Ref{}
384
	for i := range pathItem.Parameters {
385
		if err := expandParameterOrResponse(&(pathItem.Parameters[i]), resolver, basePath); resolver.shouldStopOnError(err) {
386
			return err
387
		}
388
	}
389

390
	ops := []*Operation{
391
		pathItem.Get,
392
		pathItem.Head,
393
		pathItem.Options,
394
		pathItem.Put,
395
		pathItem.Post,
396
		pathItem.Patch,
397
		pathItem.Delete,
398
	}
399
	for _, op := range ops {
400
		if err := expandOperation(op, resolver, basePath); resolver.shouldStopOnError(err) {
401
			return err
402
		}
403
	}
404

405
	return nil
406
}
407

408
func expandOperation(op *Operation, resolver *schemaLoader, basePath string) error {
409
	if op == nil {
410
		return nil
411
	}
412

413
	for i := range op.Parameters {
414
		param := op.Parameters[i]
415
		if err := expandParameterOrResponse(&param, resolver, basePath); resolver.shouldStopOnError(err) {
416
			return err
417
		}
418
		op.Parameters[i] = param
419
	}
420

421
	if op.Responses == nil {
422
		return nil
423
	}
424

425
	responses := op.Responses
426
	if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) {
427
		return err
428
	}
429

430
	for code := range responses.StatusCodeResponses {
431
		response := responses.StatusCodeResponses[code]
432
		if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) {
433
			return err
434
		}
435
		responses.StatusCodeResponses[code] = response
436
	}
437

438
	return nil
439
}
440

441
// ExpandResponseWithRoot expands a response based on a root document, not a fetchable document
442
//
443
// Notice that it is impossible to reference a json schema in a different document other than root
444
// (use ExpandResponse to resolve external references).
445
//
446
// Setting the cache is optional and this parameter may safely be left to nil.
447
func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error {
448
	cache = cacheOrDefault(cache)
449
	opts := &ExpandOptions{
450
		RelativeBase: baseForRoot(root, cache),
451
	}
452
	resolver := defaultSchemaLoader(root, opts, cache, nil)
453

454
	return expandParameterOrResponse(response, resolver, opts.RelativeBase)
455
}
456

457
// ExpandResponse expands a response based on a basepath
458
//
459
// All refs inside response will be resolved relative to basePath
460
func ExpandResponse(response *Response, basePath string) error {
461
	opts := optionsOrDefault(&ExpandOptions{
462
		RelativeBase: basePath,
463
	})
464
	resolver := defaultSchemaLoader(nil, opts, nil, nil)
465

466
	return expandParameterOrResponse(response, resolver, opts.RelativeBase)
467
}
468

469
// ExpandParameterWithRoot expands a parameter based on a root document, not a fetchable document.
470
//
471
// Notice that it is impossible to reference a json schema in a different document other than root
472
// (use ExpandParameter to resolve external references).
473
func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error {
474
	cache = cacheOrDefault(cache)
475

476
	opts := &ExpandOptions{
477
		RelativeBase: baseForRoot(root, cache),
478
	}
479
	resolver := defaultSchemaLoader(root, opts, cache, nil)
480

481
	return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
482
}
483

484
// ExpandParameter expands a parameter based on a basepath.
485
// This is the exported version of expandParameter
486
// all refs inside parameter will be resolved relative to basePath
487
func ExpandParameter(parameter *Parameter, basePath string) error {
488
	opts := optionsOrDefault(&ExpandOptions{
489
		RelativeBase: basePath,
490
	})
491
	resolver := defaultSchemaLoader(nil, opts, nil, nil)
492

493
	return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
494
}
495

496
func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
497
	var (
498
		ref *Ref
499
		sch *Schema
500
	)
501

502
	switch refable := input.(type) {
503
	case *Parameter:
504
		if refable == nil {
505
			return nil, nil, nil
506
		}
507
		ref = &refable.Ref
508
		sch = refable.Schema
509
	case *Response:
510
		if refable == nil {
511
			return nil, nil, nil
512
		}
513
		ref = &refable.Ref
514
		sch = refable.Schema
515
	default:
516
		return nil, nil, fmt.Errorf("unsupported type: %T: %w", input, ErrExpandUnsupportedType)
517
	}
518

519
	return ref, sch, nil
520
}
521

522
func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error {
523
	ref, _, err := getRefAndSchema(input)
524
	if err != nil {
525
		return err
526
	}
527

528
	if ref == nil {
529
		return nil
530
	}
531

532
	parentRefs := make([]string, 0, 10)
533
	if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
534
		return err
535
	}
536

537
	ref, sch, _ := getRefAndSchema(input)
538
	if ref.String() != "" {
539
		transitiveResolver := resolver.transitiveResolver(basePath, *ref)
540
		basePath = resolver.updateBasePath(transitiveResolver, basePath)
541
		resolver = transitiveResolver
542
	}
543

544
	if sch == nil {
545
		// nothing to be expanded
546
		if ref != nil {
547
			*ref = Ref{}
548
		}
549
		return nil
550
	}
551

552
	if sch.Ref.String() != "" {
553
		rebasedRef, ern := NewRef(normalizeURI(sch.Ref.String(), basePath))
554
		if ern != nil {
555
			return ern
556
		}
557

558
		switch {
559
		case resolver.isCircular(&rebasedRef, basePath, parentRefs...):
560
			// this is a circular $ref: stop expansion
561
			if !resolver.options.AbsoluteCircularRef {
562
				sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
563
			} else {
564
				sch.Ref = rebasedRef
565
			}
566
		case !resolver.options.SkipSchemas:
567
			// schema expanded to a $ref in another root
568
			sch.Ref = rebasedRef
569
			debugLog("rebased to: %s", sch.Ref.String())
570
		default:
571
			// skip schema expansion but rebase $ref to schema
572
			sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
573
		}
574
	}
575

576
	if ref != nil {
577
		*ref = Ref{}
578
	}
579

580
	// expand schema
581
	if !resolver.options.SkipSchemas {
582
		s, err := expandSchema(*sch, parentRefs, resolver, basePath)
583
		if resolver.shouldStopOnError(err) {
584
			return err
585
		}
586
		if s == nil {
587
			// guard for when continuing on error
588
			return nil
589
		}
590
		*sch = *s
591
	}
592

593
	return nil
594
}
595

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

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

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

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