1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package spec
16
17 import (
18 "encoding/json"
19 "fmt"
20 )
21
22
23
24
25
26
27
28
29
30 type ExpandOptions struct {
31 RelativeBase string
32 SkipSchemas bool
33 ContinueOnError bool
34 PathLoader func(string) (json.RawMessage, error) `json:"-"`
35 AbsoluteCircularRef bool
36 }
37
38 func optionsOrDefault(opts *ExpandOptions) *ExpandOptions {
39 if opts != nil {
40 clone := *opts
41 if clone.RelativeBase != "" {
42 clone.RelativeBase = normalizeBase(clone.RelativeBase)
43 }
44
45 return &clone
46 }
47 return &ExpandOptions{}
48 }
49
50
51 func ExpandSpec(spec *Swagger, options *ExpandOptions) error {
52 options = optionsOrDefault(options)
53 resolver := defaultSchemaLoader(spec, options, nil, nil)
54
55 specBasePath := options.RelativeBase
56
57 if !options.SkipSchemas {
58 for key, definition := range spec.Definitions {
59 parentRefs := make([]string, 0, 10)
60 parentRefs = append(parentRefs, "#/definitions/"+key)
61
62 def, err := expandSchema(definition, parentRefs, resolver, specBasePath)
63 if resolver.shouldStopOnError(err) {
64 return err
65 }
66 if def != nil {
67 spec.Definitions[key] = *def
68 }
69 }
70 }
71
72 for key := range spec.Parameters {
73 parameter := spec.Parameters[key]
74 if err := expandParameterOrResponse(¶meter, resolver, specBasePath); resolver.shouldStopOnError(err) {
75 return err
76 }
77 spec.Parameters[key] = parameter
78 }
79
80 for key := range spec.Responses {
81 response := spec.Responses[key]
82 if err := expandParameterOrResponse(&response, resolver, specBasePath); resolver.shouldStopOnError(err) {
83 return err
84 }
85 spec.Responses[key] = response
86 }
87
88 if spec.Paths != nil {
89 for key := range spec.Paths.Paths {
90 pth := spec.Paths.Paths[key]
91 if err := expandPathItem(&pth, resolver, specBasePath); resolver.shouldStopOnError(err) {
92 return err
93 }
94 spec.Paths.Paths[key] = pth
95 }
96 }
97
98 return nil
99 }
100
101 const rootBase = ".root"
102
103
104
105 func baseForRoot(root interface{}, cache ResolutionCache) string {
106
107 normalizedBase := normalizeBase(rootBase)
108
109 if root == nil {
110
111 cachedRoot, found := cache.Get(normalizedBase)
112 if found && cachedRoot != nil {
113
114 return normalizedBase
115 }
116
117 root = map[string]interface{}{}
118 }
119
120 cache.Set(normalizedBase, root)
121
122 return normalizedBase
123 }
124
125
126
127
128
129
130
131
132
133 func ExpandSchema(schema *Schema, root interface{}, cache ResolutionCache) error {
134 cache = cacheOrDefault(cache)
135 if root == nil {
136 root = schema
137 }
138
139 opts := &ExpandOptions{
140
141 RelativeBase: baseForRoot(root, cache),
142 SkipSchemas: false,
143 ContinueOnError: false,
144 }
145
146 return ExpandSchemaWithBasePath(schema, cache, opts)
147 }
148
149
150
151
152 func ExpandSchemaWithBasePath(schema *Schema, cache ResolutionCache, opts *ExpandOptions) error {
153 if schema == nil {
154 return nil
155 }
156
157 cache = cacheOrDefault(cache)
158
159 opts = optionsOrDefault(opts)
160
161 resolver := defaultSchemaLoader(nil, opts, cache, nil)
162
163 parentRefs := make([]string, 0, 10)
164 s, err := expandSchema(*schema, parentRefs, resolver, opts.RelativeBase)
165 if err != nil {
166 return err
167 }
168 if s != nil {
169
170 *schema = *s
171 }
172
173 return nil
174 }
175
176 func expandItems(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
177 if target.Items == nil {
178 return &target, nil
179 }
180
181
182 if target.Items.Schema != nil {
183 t, err := expandSchema(*target.Items.Schema, parentRefs, resolver, basePath)
184 if err != nil {
185 return nil, err
186 }
187 *target.Items.Schema = *t
188 }
189
190
191 for i := range target.Items.Schemas {
192 t, err := expandSchema(target.Items.Schemas[i], parentRefs, resolver, basePath)
193 if err != nil {
194 return nil, err
195 }
196 target.Items.Schemas[i] = *t
197 }
198
199 return &target, nil
200 }
201
202 func expandSchema(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
203 if target.Ref.String() == "" && target.Ref.IsRoot() {
204 newRef := normalizeRef(&target.Ref, basePath)
205 target.Ref = *newRef
206 return &target, nil
207 }
208
209
210
211 if target.ID != "" {
212 basePath, _ = resolver.setSchemaID(target, target.ID, basePath)
213 }
214
215 if target.Ref.String() != "" {
216 if !resolver.options.SkipSchemas {
217 return expandSchemaRef(target, parentRefs, resolver, basePath)
218 }
219
220
221
222 rebasedRef, err := NewRef(normalizeURI(target.Ref.String(), basePath))
223 if err != nil {
224 return nil, err
225 }
226 target.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
227
228 return &target, nil
229 }
230
231 for k := range target.Definitions {
232 tt, err := expandSchema(target.Definitions[k], parentRefs, resolver, basePath)
233 if resolver.shouldStopOnError(err) {
234 return &target, err
235 }
236 if tt != nil {
237 target.Definitions[k] = *tt
238 }
239 }
240
241 t, err := expandItems(target, parentRefs, resolver, basePath)
242 if resolver.shouldStopOnError(err) {
243 return &target, err
244 }
245 if t != nil {
246 target = *t
247 }
248
249 for i := range target.AllOf {
250 t, err := expandSchema(target.AllOf[i], parentRefs, resolver, basePath)
251 if resolver.shouldStopOnError(err) {
252 return &target, err
253 }
254 if t != nil {
255 target.AllOf[i] = *t
256 }
257 }
258
259 for i := range target.AnyOf {
260 t, err := expandSchema(target.AnyOf[i], parentRefs, resolver, basePath)
261 if resolver.shouldStopOnError(err) {
262 return &target, err
263 }
264 if t != nil {
265 target.AnyOf[i] = *t
266 }
267 }
268
269 for i := range target.OneOf {
270 t, err := expandSchema(target.OneOf[i], parentRefs, resolver, basePath)
271 if resolver.shouldStopOnError(err) {
272 return &target, err
273 }
274 if t != nil {
275 target.OneOf[i] = *t
276 }
277 }
278
279 if target.Not != nil {
280 t, err := expandSchema(*target.Not, parentRefs, resolver, basePath)
281 if resolver.shouldStopOnError(err) {
282 return &target, err
283 }
284 if t != nil {
285 *target.Not = *t
286 }
287 }
288
289 for k := range target.Properties {
290 t, err := expandSchema(target.Properties[k], parentRefs, resolver, basePath)
291 if resolver.shouldStopOnError(err) {
292 return &target, err
293 }
294 if t != nil {
295 target.Properties[k] = *t
296 }
297 }
298
299 if target.AdditionalProperties != nil && target.AdditionalProperties.Schema != nil {
300 t, err := expandSchema(*target.AdditionalProperties.Schema, parentRefs, resolver, basePath)
301 if resolver.shouldStopOnError(err) {
302 return &target, err
303 }
304 if t != nil {
305 *target.AdditionalProperties.Schema = *t
306 }
307 }
308
309 for k := range target.PatternProperties {
310 t, err := expandSchema(target.PatternProperties[k], parentRefs, resolver, basePath)
311 if resolver.shouldStopOnError(err) {
312 return &target, err
313 }
314 if t != nil {
315 target.PatternProperties[k] = *t
316 }
317 }
318
319 for k := range target.Dependencies {
320 if target.Dependencies[k].Schema != nil {
321 t, err := expandSchema(*target.Dependencies[k].Schema, parentRefs, resolver, basePath)
322 if resolver.shouldStopOnError(err) {
323 return &target, err
324 }
325 if t != nil {
326 *target.Dependencies[k].Schema = *t
327 }
328 }
329 }
330
331 if target.AdditionalItems != nil && target.AdditionalItems.Schema != nil {
332 t, err := expandSchema(*target.AdditionalItems.Schema, parentRefs, resolver, basePath)
333 if resolver.shouldStopOnError(err) {
334 return &target, err
335 }
336 if t != nil {
337 *target.AdditionalItems.Schema = *t
338 }
339 }
340 return &target, nil
341 }
342
343 func expandSchemaRef(target Schema, parentRefs []string, resolver *schemaLoader, basePath string) (*Schema, error) {
344
345
346
347
348 normalizedRef := normalizeRef(&target.Ref, basePath)
349 normalizedBasePath := normalizedRef.RemoteURI()
350
351 if resolver.isCircular(normalizedRef, basePath, parentRefs...) {
352
353
354
355 debugLog("short circuit circular ref: basePath: %s, normalizedPath: %s, normalized ref: %s",
356 basePath, normalizedBasePath, normalizedRef.String())
357 if !resolver.options.AbsoluteCircularRef {
358 target.Ref = denormalizeRef(normalizedRef, resolver.context.basePath, resolver.context.rootID)
359 } else {
360 target.Ref = *normalizedRef
361 }
362 return &target, nil
363 }
364
365 var t *Schema
366 err := resolver.Resolve(&target.Ref, &t, basePath)
367 if resolver.shouldStopOnError(err) {
368 return nil, err
369 }
370
371 if t == nil {
372
373 return &target, nil
374 }
375
376 parentRefs = append(parentRefs, normalizedRef.String())
377 transitiveResolver := resolver.transitiveResolver(basePath, target.Ref)
378
379 basePath = resolver.updateBasePath(transitiveResolver, normalizedBasePath)
380
381 return expandSchema(*t, parentRefs, transitiveResolver, basePath)
382 }
383
384 func expandPathItem(pathItem *PathItem, resolver *schemaLoader, basePath string) error {
385 if pathItem == nil {
386 return nil
387 }
388
389 parentRefs := make([]string, 0, 10)
390 if err := resolver.deref(pathItem, parentRefs, basePath); resolver.shouldStopOnError(err) {
391 return err
392 }
393
394 if pathItem.Ref.String() != "" {
395 transitiveResolver := resolver.transitiveResolver(basePath, pathItem.Ref)
396 basePath = transitiveResolver.updateBasePath(resolver, basePath)
397 resolver = transitiveResolver
398 }
399
400 pathItem.Ref = Ref{}
401 for i := range pathItem.Parameters {
402 if err := expandParameterOrResponse(&(pathItem.Parameters[i]), resolver, basePath); resolver.shouldStopOnError(err) {
403 return err
404 }
405 }
406
407 ops := []*Operation{
408 pathItem.Get,
409 pathItem.Head,
410 pathItem.Options,
411 pathItem.Put,
412 pathItem.Post,
413 pathItem.Patch,
414 pathItem.Delete,
415 }
416 for _, op := range ops {
417 if err := expandOperation(op, resolver, basePath); resolver.shouldStopOnError(err) {
418 return err
419 }
420 }
421
422 return nil
423 }
424
425 func expandOperation(op *Operation, resolver *schemaLoader, basePath string) error {
426 if op == nil {
427 return nil
428 }
429
430 for i := range op.Parameters {
431 param := op.Parameters[i]
432 if err := expandParameterOrResponse(¶m, resolver, basePath); resolver.shouldStopOnError(err) {
433 return err
434 }
435 op.Parameters[i] = param
436 }
437
438 if op.Responses == nil {
439 return nil
440 }
441
442 responses := op.Responses
443 if err := expandParameterOrResponse(responses.Default, resolver, basePath); resolver.shouldStopOnError(err) {
444 return err
445 }
446
447 for code := range responses.StatusCodeResponses {
448 response := responses.StatusCodeResponses[code]
449 if err := expandParameterOrResponse(&response, resolver, basePath); resolver.shouldStopOnError(err) {
450 return err
451 }
452 responses.StatusCodeResponses[code] = response
453 }
454
455 return nil
456 }
457
458
459
460
461
462
463
464 func ExpandResponseWithRoot(response *Response, root interface{}, cache ResolutionCache) error {
465 cache = cacheOrDefault(cache)
466 opts := &ExpandOptions{
467 RelativeBase: baseForRoot(root, cache),
468 }
469 resolver := defaultSchemaLoader(root, opts, cache, nil)
470
471 return expandParameterOrResponse(response, resolver, opts.RelativeBase)
472 }
473
474
475
476
477 func ExpandResponse(response *Response, basePath string) error {
478 opts := optionsOrDefault(&ExpandOptions{
479 RelativeBase: basePath,
480 })
481 resolver := defaultSchemaLoader(nil, opts, nil, nil)
482
483 return expandParameterOrResponse(response, resolver, opts.RelativeBase)
484 }
485
486
487
488
489
490 func ExpandParameterWithRoot(parameter *Parameter, root interface{}, cache ResolutionCache) error {
491 cache = cacheOrDefault(cache)
492
493 opts := &ExpandOptions{
494 RelativeBase: baseForRoot(root, cache),
495 }
496 resolver := defaultSchemaLoader(root, opts, cache, nil)
497
498 return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
499 }
500
501
502
503
504 func ExpandParameter(parameter *Parameter, basePath string) error {
505 opts := optionsOrDefault(&ExpandOptions{
506 RelativeBase: basePath,
507 })
508 resolver := defaultSchemaLoader(nil, opts, nil, nil)
509
510 return expandParameterOrResponse(parameter, resolver, opts.RelativeBase)
511 }
512
513 func getRefAndSchema(input interface{}) (*Ref, *Schema, error) {
514 var (
515 ref *Ref
516 sch *Schema
517 )
518
519 switch refable := input.(type) {
520 case *Parameter:
521 if refable == nil {
522 return nil, nil, nil
523 }
524 ref = &refable.Ref
525 sch = refable.Schema
526 case *Response:
527 if refable == nil {
528 return nil, nil, nil
529 }
530 ref = &refable.Ref
531 sch = refable.Schema
532 default:
533 return nil, nil, fmt.Errorf("unsupported type: %T: %w", input, ErrExpandUnsupportedType)
534 }
535
536 return ref, sch, nil
537 }
538
539 func expandParameterOrResponse(input interface{}, resolver *schemaLoader, basePath string) error {
540 ref, sch, err := getRefAndSchema(input)
541 if err != nil {
542 return err
543 }
544
545 if ref == nil && sch == nil {
546 return nil
547 }
548
549 parentRefs := make([]string, 0, 10)
550 if ref != nil {
551
552 if err = resolver.deref(input, parentRefs, basePath); resolver.shouldStopOnError(err) {
553 return err
554 }
555
556 ref, sch, _ = getRefAndSchema(input)
557 }
558
559 if ref.String() != "" {
560 transitiveResolver := resolver.transitiveResolver(basePath, *ref)
561 basePath = resolver.updateBasePath(transitiveResolver, basePath)
562 resolver = transitiveResolver
563 }
564
565 if sch == nil {
566
567 if ref != nil {
568 *ref = Ref{}
569 }
570
571 return nil
572 }
573
574 if sch.Ref.String() != "" {
575 rebasedRef, ern := NewRef(normalizeURI(sch.Ref.String(), basePath))
576 if ern != nil {
577 return ern
578 }
579
580 if resolver.isCircular(&rebasedRef, basePath, parentRefs...) {
581
582 if !resolver.options.AbsoluteCircularRef {
583 sch.Ref = denormalizeRef(&rebasedRef, resolver.context.basePath, resolver.context.rootID)
584 } else {
585 sch.Ref = rebasedRef
586 }
587 }
588 }
589
590
591 if ref != nil {
592 *ref = Ref{}
593 }
594
595
596
597 s, err := expandSchema(*sch, parentRefs, resolver, basePath)
598 if resolver.shouldStopOnError(err) {
599 return err
600 }
601
602 if s != nil {
603 *sch = *s
604 }
605
606 return nil
607 }
608
View as plain text