1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package spec
16
17 import (
18 "encoding/json"
19 "io"
20 "log"
21 "net/http"
22 "net/http/httptest"
23 "os"
24 "path/filepath"
25 "testing"
26
27 "github.com/stretchr/testify/assert"
28 "github.com/stretchr/testify/require"
29 )
30
31 const (
32 crossFileRefFixture = "fixtures/expansion/crossFileRef.json"
33 withoutSchemaID = "removed"
34 withSchemaID = "schema"
35 pathItemsFixture = "fixtures/expansion/pathItems.json"
36 extraRefFixture = "fixtures/expansion/extraRef.json"
37 )
38
39 var (
40
41 PetStoreJSONMessage = json.RawMessage([]byte(PetStore20))
42 specs = filepath.Join("fixtures", "specs")
43 )
44
45 func TestExpand_Issue148(t *testing.T) {
46 fp := filepath.Join("fixtures", "bugs", "schema-148.json")
47 b, err := jsonDoc(fp)
48 require.NoError(t, err)
49
50 assertAdditionalProps := func(sp Swagger) func(*testing.T) {
51 return func(t *testing.T) {
52 require.Len(t, sp.Definitions, 2)
53
54 require.Contains(t, sp.Definitions, "empty")
55 empty := sp.Definitions["empty"]
56 require.NotNil(t, empty.AdditionalProperties)
57 require.NotNil(t, empty.AdditionalProperties.Schema)
58 require.True(t, empty.AdditionalProperties.Allows)
59
60 require.Contains(t, sp.Definitions, "false")
61 additionalIsFalse := sp.Definitions["false"]
62 require.NotNil(t, additionalIsFalse.AdditionalProperties)
63 require.Nil(t, additionalIsFalse.AdditionalProperties.Schema)
64 require.False(t, additionalIsFalse.AdditionalProperties.Allows)
65 }
66 }
67
68 var sp Swagger
69 require.NoError(t, json.Unmarshal(b, &sp))
70 t.Run("check additional properties", assertAdditionalProps(sp))
71
72 require.NoError(t, ExpandSpec(&sp, nil))
73 t.Run("check additional properties after expansion", assertAdditionalProps(sp))
74 }
75
76 func TestExpand_KnownRef(t *testing.T) {
77
78 schema := RefProperty("http://json-schema.org/draft-04/schema#")
79 require.NoError(t, ExpandSchema(schema, nil, nil))
80
81 assert.Equal(t, "Core schema meta-schema", schema.Description)
82
83
84 jazon := asJSON(t, schema)
85
86 assertRefResolve(t, jazon, "", schema)
87 }
88
89 func TestExpand_ResponseSchema(t *testing.T) {
90 fp := filepath.Join("fixtures", "local_expansion", "spec.json")
91 b, err := jsonDoc(fp)
92 require.NoError(t, err)
93
94 var spec Swagger
95 require.NoError(t, json.Unmarshal(b, &spec))
96
97 require.NoError(t, ExpandSpec(&spec, &ExpandOptions{RelativeBase: fp}))
98
99
100 jazon := asJSON(t, spec)
101 assertNoRef(t, jazon)
102
103 sch := spec.Paths.Paths["/item"].Get.Responses.StatusCodeResponses[200].Schema
104 require.NotNil(t, sch)
105
106 assert.Empty(t, sch.Ref.String())
107 assert.Contains(t, sch.Type, "object")
108 assert.Len(t, sch.Properties, 2)
109 }
110
111 func TestExpand_EmptySpec(t *testing.T) {
112 spec := new(Swagger)
113
114
115 require.NoError(t, ExpandSpec(spec, nil))
116
117
118 var schema *Schema
119 require.NoError(t, ExpandSchemaWithBasePath(schema, nil, nil))
120
121
122 var paths *PathItem
123 resolver := defaultSchemaLoader(spec, nil, nil, nil)
124 require.NoError(t, expandPathItem(paths, resolver, ""))
125
126
127 var param *Parameter
128 require.NoError(t, expandParameterOrResponse(param, resolver, ""))
129 }
130
131 func TestExpand_Spec(t *testing.T) {
132
133
134 specPath := filepath.Join("fixtures", "expansion", "all-the-things.json")
135 specDoc, err := jsonDoc(specPath)
136 require.NoError(t, err)
137
138 opts := &ExpandOptions{
139 RelativeBase: specPath,
140 }
141
142 spec := new(Swagger)
143 require.NoError(t, json.Unmarshal(specDoc, spec))
144
145
146 pet := spec.Definitions["pet"]
147 errorModel := spec.Definitions["errorModel"]
148 petResponse := spec.Responses["petResponse"]
149 petResponse.Schema = &pet
150 stringResponse := spec.Responses["stringResponse"]
151 tagParam := spec.Parameters["tag"]
152 idParam := spec.Parameters["idParam"]
153
154 require.NoError(t, ExpandSpec(spec, opts))
155
156
157 assertNoRef(t, asJSON(t, spec))
158
159 assert.Equal(t, tagParam, spec.Parameters["query"])
160 assert.Equal(t, petResponse, spec.Responses["petResponse"])
161 assert.Equal(t, petResponse, spec.Responses["anotherPet"])
162 assert.Equal(t, pet, *spec.Responses["petResponse"].Schema)
163 assert.Equal(t, stringResponse, *spec.Paths.Paths["/"].Get.Responses.Default)
164 assert.Equal(t, petResponse, spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200])
165 assert.Equal(t, pet, *spec.Paths.Paths["/pets"].Get.Responses.StatusCodeResponses[200].Schema.Items.Schema)
166 assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Get.Responses.Default.Schema)
167 assert.Equal(t, pet, spec.Definitions["petInput"].AllOf[0])
168 assert.Equal(t, spec.Definitions["petInput"], *spec.Paths.Paths["/pets"].Post.Parameters[0].Schema)
169 assert.Equal(t, petResponse, spec.Paths.Paths["/pets"].Post.Responses.StatusCodeResponses[200])
170 assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Post.Responses.Default.Schema)
171
172 pi := spec.Paths.Paths["/pets/{id}"]
173 assert.Equal(t, idParam, pi.Get.Parameters[0])
174 assert.Equal(t, petResponse, pi.Get.Responses.StatusCodeResponses[200])
175 assert.Equal(t, errorModel, *pi.Get.Responses.Default.Schema)
176 assert.Equal(t, idParam, pi.Delete.Parameters[0])
177 assert.Equal(t, errorModel, *pi.Delete.Responses.Default.Schema)
178 }
179
180 func TestExpand_InternalResponse(t *testing.T) {
181 basePath := normalizeBase(filepath.Join("fixtures", "expansion", "all-the-things.json"))
182 specDoc, err := jsonDoc(basePath)
183 require.NoError(t, err)
184
185 spec := new(Swagger)
186 require.NoError(t, json.Unmarshal(specDoc, spec))
187
188 resolver := defaultSchemaLoader(spec, nil, nil, nil)
189
190 expectedPet := spec.Responses["petResponse"]
191 require.NoError(t, expandParameterOrResponse(&expectedPet, resolver, basePath))
192
193 jazon := asJSON(t, expectedPet)
194
195 assert.JSONEq(t, `{
196 "description": "pet response",
197 "schema": {
198 "required": [
199 "id",
200 "name"
201 ],
202 "properties": {
203 "id": {
204 "type": "integer",
205 "format": "int64"
206 },
207 "name": {
208 "type": "string"
209 },
210 "tag": {
211 "type": "string"
212 }
213 }
214 }
215 }`, jazon)
216
217
218 another := spec.Responses["anotherPet"]
219 require.NoError(t, expandParameterOrResponse(&another, resolver, basePath))
220 assert.Equal(t, expectedPet, another)
221
222 defaultResponse := spec.Paths.Paths["/"].Get.Responses.Default
223
224 require.NoError(t, expandParameterOrResponse(defaultResponse, resolver, basePath))
225
226 expectedString := spec.Responses["stringResponse"]
227 assert.Equal(t, expectedString, *defaultResponse)
228
229
230 successResponse := spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
231
232 jazon = asJSON(t, successResponse)
233
234 assert.JSONEq(t, `{
235 "$ref": "#/responses/anotherPet"
236 }`, jazon)
237
238 require.NoError(t, expandParameterOrResponse(&successResponse, resolver, basePath))
239 assert.Equal(t, expectedPet, successResponse)
240 }
241
242 func TestExpand_Response(t *testing.T) {
243 basePath := filepath.Join("fixtures", "expansion", "all-the-things.json")
244
245 specDoc, err := jsonDoc(basePath)
246 require.NoError(t, err)
247
248 spec := new(Swagger)
249 require.NoError(t, json.Unmarshal(specDoc, spec))
250
251 resp := spec.Responses["anotherPet"]
252 expected := spec.Responses["petResponse"]
253
254 require.NoError(t, ExpandResponse(&expected, basePath))
255
256 require.NoError(t, ExpandResponse(&resp, basePath))
257 assert.Equal(t, expected, resp)
258
259 resp2 := spec.Paths.Paths["/"].Get.Responses.Default
260 expected = spec.Responses["stringResponse"]
261
262 require.NoError(t, ExpandResponse(resp2, basePath))
263 assert.Equal(t, expected, *resp2)
264
265 resp = spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
266 expected = spec.Responses["petResponse"]
267
268 require.NoError(t, ExpandResponse(&resp, basePath))
269 assert.Equal(t, expected, resp)
270 }
271
272 func TestExpand_ResponseAndParamWithRoot(t *testing.T) {
273 specDoc, err := jsonDoc("fixtures/bugs/1614/gitea.json")
274 require.NoError(t, err)
275
276 var spec Swagger
277 err = json.Unmarshal(specDoc, &spec)
278 require.NoError(t, err)
279
280
281 resp := spec.Paths.Paths["/admin/users"].Post.Responses.StatusCodeResponses[201]
282 require.NoError(t, ExpandResponseWithRoot(&resp, spec, nil))
283
284 jazon := asJSON(t, resp)
285 assertNoRef(t, jazon)
286
287 resp = spec.Paths.Paths["/admin/users"].Post.Responses.StatusCodeResponses[403]
288 require.NoError(t, ExpandResponseWithRoot(&resp, spec, nil))
289
290 jazon = asJSON(t, resp)
291 assertNoRef(t, jazon)
292
293
294 param := spec.Paths.Paths["/admin/users"].Post.Parameters[0]
295 require.NoError(t, ExpandParameterWithRoot(¶m, spec, nil))
296
297 jazon = asJSON(t, param)
298 assertNoRef(t, jazon)
299 }
300
301 func TestExpand_InternalParameter(t *testing.T) {
302 basePath := normalizeBase(filepath.Join("fixtures", "expansion", "params.json"))
303
304 paramDoc, err := jsonDoc(basePath)
305 require.NoError(t, err)
306
307 spec := new(Swagger)
308 require.NoError(t, json.Unmarshal(paramDoc, spec))
309
310 resolver := defaultSchemaLoader(spec, nil, nil, nil)
311
312 param := spec.Parameters["query"]
313 expected := spec.Parameters["tag"]
314
315 require.NoError(t, expandParameterOrResponse(¶m, resolver, basePath))
316
317 assert.Equal(t, expected, param)
318
319 param = spec.Paths.Paths["/cars/{id}"].Parameters[0]
320 expected = spec.Parameters["id"]
321
322 require.NoError(t, expandParameterOrResponse(¶m, resolver, basePath))
323
324 assert.Equal(t, expected, param)
325 }
326
327 func TestExpand_Parameter(t *testing.T) {
328 basePath := filepath.Join("fixtures", "expansion", "params.json")
329
330 paramDoc, err := jsonDoc(basePath)
331 require.NoError(t, err)
332
333 spec := new(Swagger)
334 require.NoError(t, json.Unmarshal(paramDoc, spec))
335
336 param := spec.Parameters["query"]
337 expected := spec.Parameters["tag"]
338
339 require.NoError(t, ExpandParameter(¶m, basePath))
340 assert.Equal(t, expected, param)
341
342 param = spec.Paths.Paths["/cars/{id}"].Parameters[0]
343 expected = spec.Parameters["id"]
344
345 require.NoError(t, ExpandParameter(¶m, basePath))
346 assert.Equal(t, expected, param)
347 }
348
349 func TestExpand_JSONSchemaDraft4(t *testing.T) {
350 fixturePath := filepath.Join("schemas", "jsonschema-draft-04.json")
351 jazon, sch := expandThisSchemaOrDieTrying(t, fixturePath)
352
353
354
355
356
357
358
359 assertRefInJSONRegexp(t, jazon, `^(http://json-schema.org/draft-04/)|(#/definitions/schemaArray)`)
360
361
362 assertRefExpand(t, jazon, "", sch)
363 }
364
365 func TestExpand_SwaggerSchema(t *testing.T) {
366 fixturePath := filepath.Join("schemas", "v2", "schema.json")
367 jazon, sch := expandThisSchemaOrDieTrying(t, fixturePath)
368
369
370
371
372
373 assertRefInJSON(t, jazon, "#/definitions/")
374
375
376 assertRefExpand(t, jazon, "", sch)
377 }
378
379 func TestExpand_ContinueOnError(t *testing.T) {
380 specPath := filepath.Join("fixtures", "expansion", "missingRef.json")
381
382 defer log.SetOutput(os.Stdout)
383 log.SetOutput(io.Discard)
384
385
386 missingRefDoc, err := jsonDoc(specPath)
387 require.NoError(t, err)
388
389 testCase := struct {
390 Input *Swagger `json:"input"`
391 Expected *Swagger `json:"expected"`
392 }{}
393 require.NoError(t, json.Unmarshal(missingRefDoc, &testCase))
394
395 opts := &ExpandOptions{
396 ContinueOnError: true,
397 RelativeBase: specPath,
398 }
399 require.NoError(t, ExpandSpec(testCase.Input, opts))
400
401 assert.Equal(t, testCase.Expected, testCase.Input, "Should continue expanding spec when a definition can't be found.")
402
403
404 doc, err := jsonDoc("fixtures/expansion/missingItemRef.json")
405 require.NoError(t, err)
406
407 spec := new(Swagger)
408 require.NoError(t, json.Unmarshal(doc, spec))
409
410 assert.NotPanics(t, func() {
411 require.NoError(t, ExpandSpec(spec, opts))
412 }, "Array of missing refs should not cause a panic, and continue to expand spec.")
413 }
414
415 func TestExpand_InternalSchemas2(t *testing.T) {
416 basePath := normalizeBase(filepath.Join("fixtures", "expansion", "schemas2.json"))
417
418 carsDoc, err := jsonDoc(basePath)
419 require.NoError(t, err)
420
421 spec := new(Swagger)
422 require.NoError(t, json.Unmarshal(carsDoc, spec))
423
424 resolver := defaultSchemaLoader(spec, nil, nil, nil)
425
426
427 schema := spec.Definitions["car"]
428 oldBrand := schema.Properties["brand"]
429 require.NotEmpty(t, oldBrand.Items.Schema.Ref.String())
430 require.NotEqual(t, spec.Definitions["brand"], oldBrand)
431
432 _, err = expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
433 require.NoError(t, err)
434
435
436 newBrand := schema.Properties["brand"]
437 assert.Empty(t, newBrand.Items.Schema.Ref.String())
438 assert.Equal(t, spec.Definitions["brand"], *newBrand.Items.Schema)
439
440
441 schema = spec.Definitions["truck"]
442 require.NotEmpty(t, schema.Items.Schema.Ref.String())
443 s, err := expandSchema(schema, []string{"#/definitions/truck"}, resolver, basePath)
444 require.NoError(t, err)
445 require.NotNil(t, s)
446
447 schema = *s
448 assert.Empty(t, schema.Items.Schema.Ref.String())
449 assert.False(t, schema.Items.Schema.Ref.IsRoot())
450 assert.Equal(t, spec.Definitions["car"], *schema.Items.Schema)
451
452 sch := new(Schema)
453 _, err = expandSchema(*sch, []string{""}, resolver, basePath)
454 require.NoError(t, err)
455
456
457 schema = spec.Definitions["batch"]
458 s, err = expandSchema(schema, []string{"#/definitions/batch"}, resolver, basePath)
459 require.NoError(t, err)
460 require.NotNil(t, s)
461
462 schema = *s
463 assert.Empty(t, schema.Items.Schema.Items.Schema.Ref.String())
464 assert.Equal(t, *schema.Items.Schema.Items.Schema, spec.Definitions["brand"])
465
466
467 schema = spec.Definitions["batch2"]
468 s, err = expandSchema(schema, []string{"#/definitions/batch2"}, resolver, basePath)
469 require.NoError(t, err)
470 require.NotNil(t, s)
471
472 schema = *s
473 assert.Empty(t, schema.Items.Schemas[0].Items.Schema.Ref.String())
474 assert.Empty(t, schema.Items.Schemas[1].Items.Schema.Ref.String())
475 assert.Equal(t, *schema.Items.Schemas[0].Items.Schema, spec.Definitions["brand"])
476 assert.Equal(t, *schema.Items.Schemas[1].Items.Schema, spec.Definitions["tag"])
477
478
479 schema = spec.Definitions["allofBoth"]
480 s, err = expandSchema(schema, []string{"#/definitions/allofBoth"}, resolver, basePath)
481 require.NoError(t, err)
482 require.NotNil(t, s)
483
484 schema = *s
485 assert.Empty(t, schema.AllOf[0].Items.Schema.Ref.String())
486 assert.Empty(t, schema.AllOf[1].Items.Schema.Ref.String())
487 assert.Equal(t, *schema.AllOf[0].Items.Schema, spec.Definitions["brand"])
488 assert.Equal(t, *schema.AllOf[1].Items.Schema, spec.Definitions["tag"])
489
490
491 schema = spec.Definitions["anyofBoth"]
492 s, err = expandSchema(schema, []string{"#/definitions/anyofBoth"}, resolver, basePath)
493 require.NoError(t, err)
494 require.NotNil(t, s)
495
496 schema = *s
497 assert.Empty(t, schema.AnyOf[0].Items.Schema.Ref.String())
498 assert.Empty(t, schema.AnyOf[1].Items.Schema.Ref.String())
499 assert.Equal(t, *schema.AnyOf[0].Items.Schema, spec.Definitions["brand"])
500 assert.Equal(t, *schema.AnyOf[1].Items.Schema, spec.Definitions["tag"])
501
502
503 schema = spec.Definitions["oneofBoth"]
504 s, err = expandSchema(schema, []string{"#/definitions/oneofBoth"}, resolver, basePath)
505 require.NoError(t, err)
506 require.NotNil(t, s)
507
508 schema = *s
509 assert.Empty(t, schema.OneOf[0].Items.Schema.Ref.String())
510 assert.Empty(t, schema.OneOf[1].Items.Schema.Ref.String())
511 assert.Equal(t, *schema.OneOf[0].Items.Schema, spec.Definitions["brand"])
512 assert.Equal(t, *schema.OneOf[1].Items.Schema, spec.Definitions["tag"])
513
514
515 schema = spec.Definitions["notSomething"]
516 s, err = expandSchema(schema, []string{"#/definitions/notSomething"}, resolver, basePath)
517 require.NoError(t, err)
518 require.NotNil(t, s)
519
520 schema = *s
521 assert.Empty(t, schema.Not.Items.Schema.Ref.String())
522 assert.Equal(t, *schema.Not.Items.Schema, spec.Definitions["tag"])
523
524
525 schema = spec.Definitions["withAdditional"]
526 s, err = expandSchema(schema, []string{"#/definitions/withAdditional"}, resolver, basePath)
527 require.NoError(t, err)
528 require.NotNil(t, s)
529
530 schema = *s
531 assert.Empty(t, schema.AdditionalProperties.Schema.Items.Schema.Ref.String())
532 assert.Equal(t, *schema.AdditionalProperties.Schema.Items.Schema, spec.Definitions["tag"])
533
534
535 schema = spec.Definitions["withAdditionalItems"]
536 s, err = expandSchema(schema, []string{"#/definitions/withAdditionalItems"}, resolver, basePath)
537 require.NoError(t, err)
538 require.NotNil(t, s)
539
540 schema = *s
541 assert.Empty(t, schema.AdditionalItems.Schema.Items.Schema.Ref.String())
542 assert.Equal(t, *schema.AdditionalItems.Schema.Items.Schema, spec.Definitions["tag"])
543
544
545 schema = spec.Definitions["withPattern"]
546 s, err = expandSchema(schema, []string{"#/definitions/withPattern"}, resolver, basePath)
547 require.NoError(t, err)
548 require.NotNil(t, s)
549
550 schema = *s
551 prop := schema.PatternProperties["^x-ab"]
552 assert.Empty(t, prop.Items.Schema.Ref.String())
553 assert.Equal(t, *prop.Items.Schema, spec.Definitions["tag"])
554
555
556 schema = spec.Definitions["deps"]
557 s, err = expandSchema(schema, []string{"#/definitions/deps"}, resolver, basePath)
558 require.NoError(t, err)
559 require.NotNil(t, s)
560
561 schema = *s
562 prop2 := schema.Dependencies["something"]
563 assert.Empty(t, prop2.Schema.Items.Schema.Ref.String())
564 assert.Equal(t, *prop2.Schema.Items.Schema, spec.Definitions["tag"])
565
566
567 schema = spec.Definitions["defined"]
568 s, err = expandSchema(schema, []string{"#/definitions/defined"}, resolver, basePath)
569 require.NoError(t, err)
570 require.NotNil(t, s)
571
572 schema = *s
573 prop = schema.Definitions["something"]
574 assert.Empty(t, prop.Items.Schema.Ref.String())
575 assert.Equal(t, *prop.Items.Schema, spec.Definitions["tag"])
576 }
577
578 func TestExpand_InternalSchemas1(t *testing.T) {
579 basePath := normalizeBase(filepath.Join("fixtures", "expansion", "schemas1.json"))
580
581 carsDoc, err := jsonDoc(basePath)
582 require.NoError(t, err)
583
584 spec := new(Swagger)
585 require.NoError(t, json.Unmarshal(carsDoc, spec))
586
587 resolver := defaultSchemaLoader(spec, nil, nil, nil)
588
589 schema := spec.Definitions["car"]
590 oldBrand := schema.Properties["brand"]
591 assert.NotEmpty(t, oldBrand.Ref.String())
592 assert.NotEqual(t, spec.Definitions["brand"], oldBrand)
593
594 s, err := expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
595 require.NoError(t, err)
596 require.NotNil(t, s)
597
598 schema = *s
599
600 newBrand := schema.Properties["brand"]
601 assert.Empty(t, newBrand.Ref.String())
602 assert.Equal(t, spec.Definitions["brand"], newBrand)
603
604 schema = spec.Definitions["truck"]
605 assert.NotEmpty(t, schema.Ref.String())
606
607 s, err = expandSchema(schema, []string{"#/definitions/truck"}, resolver, basePath)
608 require.NoError(t, err)
609 require.NotNil(t, s)
610
611 schema = *s
612 assert.Empty(t, schema.Ref.String())
613 assert.Equal(t, spec.Definitions["car"], schema)
614
615 sch := new(Schema)
616 _, err = expandSchema(*sch, []string{""}, resolver, basePath)
617 require.NoError(t, err)
618
619 schema = spec.Definitions["batch"]
620 s, err = expandSchema(schema, []string{"#/definitions/batch"}, resolver, basePath)
621 require.NoError(t, err)
622 require.NotNil(t, s)
623
624 schema = *s
625 assert.Empty(t, schema.Items.Schema.Ref.String())
626 assert.Equal(t, *schema.Items.Schema, spec.Definitions["brand"])
627
628 schema = spec.Definitions["batch2"]
629 s, err = expandSchema(schema, []string{"#/definitions/batch2"}, resolver, basePath)
630 require.NoError(t, err)
631 require.NotNil(t, s)
632
633 schema = *s
634 assert.Empty(t, schema.Items.Schemas[0].Ref.String())
635 assert.Empty(t, schema.Items.Schemas[1].Ref.String())
636 assert.Equal(t, schema.Items.Schemas[0], spec.Definitions["brand"])
637 assert.Equal(t, schema.Items.Schemas[1], spec.Definitions["tag"])
638
639 schema = spec.Definitions["allofBoth"]
640 s, err = expandSchema(schema, []string{"#/definitions/allofBoth"}, resolver, basePath)
641 require.NoError(t, err)
642 require.NotNil(t, s)
643
644 schema = *s
645 assert.Empty(t, schema.AllOf[0].Ref.String())
646 assert.Empty(t, schema.AllOf[1].Ref.String())
647 assert.Equal(t, schema.AllOf[0], spec.Definitions["brand"])
648 assert.Equal(t, schema.AllOf[1], spec.Definitions["tag"])
649
650 schema = spec.Definitions["anyofBoth"]
651 s, err = expandSchema(schema, []string{"#/definitions/anyofBoth"}, resolver, basePath)
652 require.NoError(t, err)
653 require.NotNil(t, s)
654
655 schema = *s
656 assert.Empty(t, schema.AnyOf[0].Ref.String())
657 assert.Empty(t, schema.AnyOf[1].Ref.String())
658 assert.Equal(t, schema.AnyOf[0], spec.Definitions["brand"])
659 assert.Equal(t, schema.AnyOf[1], spec.Definitions["tag"])
660
661 schema = spec.Definitions["oneofBoth"]
662 s, err = expandSchema(schema, []string{"#/definitions/oneofBoth"}, resolver, basePath)
663 require.NoError(t, err)
664 require.NotNil(t, s)
665
666 schema = *s
667 assert.Empty(t, schema.OneOf[0].Ref.String())
668 assert.Empty(t, schema.OneOf[1].Ref.String())
669 assert.Equal(t, schema.OneOf[0], spec.Definitions["brand"])
670 assert.Equal(t, schema.OneOf[1], spec.Definitions["tag"])
671
672 schema = spec.Definitions["notSomething"]
673 s, err = expandSchema(schema, []string{"#/definitions/notSomething"}, resolver, basePath)
674 require.NoError(t, err)
675 require.NotNil(t, s)
676
677 schema = *s
678 assert.Empty(t, schema.Not.Ref.String())
679 assert.Equal(t, *schema.Not, spec.Definitions["tag"])
680
681 schema = spec.Definitions["withAdditional"]
682 s, err = expandSchema(schema, []string{"#/definitions/withAdditional"}, resolver, basePath)
683 require.NoError(t, err)
684 require.NotNil(t, s)
685
686 schema = *s
687 assert.Empty(t, schema.AdditionalProperties.Schema.Ref.String())
688 assert.Equal(t, *schema.AdditionalProperties.Schema, spec.Definitions["tag"])
689
690 schema = spec.Definitions["withAdditionalItems"]
691 s, err = expandSchema(schema, []string{"#/definitions/withAdditionalItems"}, resolver, basePath)
692 require.NoError(t, err)
693 require.NotNil(t, s)
694
695 schema = *s
696 assert.Empty(t, schema.AdditionalItems.Schema.Ref.String())
697 assert.Equal(t, *schema.AdditionalItems.Schema, spec.Definitions["tag"])
698
699 schema = spec.Definitions["withPattern"]
700 s, err = expandSchema(schema, []string{"#/definitions/withPattern"}, resolver, basePath)
701 require.NoError(t, err)
702 require.NotNil(t, s)
703
704 schema = *s
705 prop := schema.PatternProperties["^x-ab"]
706 assert.Empty(t, prop.Ref.String())
707 assert.Equal(t, prop, spec.Definitions["tag"])
708
709 schema = spec.Definitions["deps"]
710 s, err = expandSchema(schema, []string{"#/definitions/deps"}, resolver, basePath)
711 require.NoError(t, err)
712 require.NotNil(t, s)
713
714 schema = *s
715 prop2 := schema.Dependencies["something"]
716 assert.Empty(t, prop2.Schema.Ref.String())
717 assert.Equal(t, *prop2.Schema, spec.Definitions["tag"])
718
719 schema = spec.Definitions["defined"]
720 s, err = expandSchema(schema, []string{"#/definitions/defined"}, resolver, basePath)
721 require.NoError(t, err)
722 require.NotNil(t, s)
723
724 schema = *s
725 prop = schema.Definitions["something"]
726 assert.Empty(t, prop.Ref.String())
727 assert.Equal(t, prop, spec.Definitions["tag"])
728 }
729
730 func TestExpand_RelativeBaseURI(t *testing.T) {
731 server := httptest.NewServer(http.FileServer(http.Dir("fixtures/remote")))
732 defer server.Close()
733
734 spec := new(Swagger)
735
736 require.NoError(t, ExpandSpec(spec, nil))
737
738
739 specDoc, err := jsonDoc("fixtures/remote/all-the-things.json")
740 require.NoError(t, err)
741
742 spec = new(Swagger)
743 require.NoError(t, json.Unmarshal(specDoc, spec))
744
745 pet := spec.Definitions["pet"]
746 errorModel := spec.Definitions["errorModel"]
747 petResponse := spec.Responses["petResponse"]
748 petResponse.Schema = &pet
749 stringResponse := spec.Responses["stringResponse"]
750 tagParam := spec.Parameters["tag"]
751 idParam := spec.Parameters["idParam"]
752 anotherPet := spec.Responses["anotherPet"]
753
754
755 anotherPet.Ref = MustCreateRef(server.URL + "/" + anotherPet.Ref.String())
756
757 opts := &ExpandOptions{
758 RelativeBase: server.URL + "/all-the-things.json",
759 }
760
761 require.NoError(t, ExpandResponse(&anotherPet, opts.RelativeBase))
762
763 spec.Responses["anotherPet"] = anotherPet
764
765 circularA := spec.Responses["circularA"]
766 circularA.Ref = MustCreateRef(server.URL + "/" + circularA.Ref.String())
767
768 require.NoError(t, ExpandResponse(&circularA, opts.RelativeBase))
769
770
771
772 assert.Empty(t, circularA.Description)
773 assert.Empty(t, circularA.Ref)
774
775 spec.Responses["circularA"] = circularA
776
777
778 require.NoError(t, ExpandSpec(spec, opts))
779
780
781 backRef := spec.Responses["backRef"]
782 require.NoError(t, ExpandResponse(&backRef, opts.RelativeBase))
783 assert.Equal(t, "pet response", backRef.Description)
784 assert.NotEmpty(t, backRef.Schema)
785 assert.Empty(t, backRef.Ref)
786
787 assert.Equal(t, tagParam, spec.Parameters["query"])
788
789 assert.Equal(t, petResponse, spec.Responses["petResponse"])
790 assert.Equal(t, petResponse, spec.Responses["anotherPet"])
791 assert.Equal(t, petResponse, spec.Paths.Paths["/pets"].Post.Responses.StatusCodeResponses[200])
792 assert.Equal(t, petResponse, spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200])
793
794 assert.Equal(t, pet, *spec.Paths.Paths["/pets"].Get.Responses.StatusCodeResponses[200].Schema.Items.Schema)
795 assert.Equal(t, pet, *spec.Responses["petResponse"].Schema)
796 assert.Equal(t, pet, spec.Definitions["petInput"].AllOf[0])
797
798 assert.Equal(t, spec.Definitions["petInput"], *spec.Paths.Paths["/pets"].Post.Parameters[0].Schema)
799
800 assert.Equal(t, stringResponse, *spec.Paths.Paths["/"].Get.Responses.Default)
801
802 assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Get.Responses.Default.Schema)
803 assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Post.Responses.Default.Schema)
804
805 pi := spec.Paths.Paths["/pets/{id}"]
806 assert.Equal(t, idParam, pi.Get.Parameters[0])
807 assert.Equal(t, petResponse, pi.Get.Responses.StatusCodeResponses[200])
808 assert.Equal(t, idParam, pi.Delete.Parameters[0])
809
810 assert.Equal(t, errorModel, *pi.Get.Responses.Default.Schema)
811 assert.Equal(t, errorModel, *pi.Delete.Responses.Default.Schema)
812 }
813
814 func resolutionContextServer() *httptest.Server {
815 var servedAt string
816 server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
817 if req.URL.Path == "/resolution.json" {
818
819 b, _ := os.ReadFile(filepath.Join(specs, "resolution.json"))
820 var ctnt map[string]interface{}
821 _ = json.Unmarshal(b, &ctnt)
822 ctnt["id"] = servedAt
823
824 rw.Header().Set("Content-Type", "application/json")
825 rw.WriteHeader(http.StatusOK)
826 bb, _ := json.Marshal(ctnt)
827 _, _ = rw.Write(bb)
828 return
829 }
830 if req.URL.Path == "/resolution2.json" {
831 b, _ := os.ReadFile(filepath.Join(specs, "resolution2.json"))
832 var ctnt map[string]interface{}
833 _ = json.Unmarshal(b, &ctnt)
834 ctnt["id"] = servedAt
835
836 rw.Header().Set("Content-Type", "application/json")
837 bb, _ := json.Marshal(ctnt)
838 _, _ = rw.Write(bb)
839 return
840 }
841
842 if req.URL.Path == "/boolProp.json" {
843 rw.Header().Set("Content-Type", "application/json")
844 b, _ := json.Marshal(map[string]interface{}{
845 "type": "boolean",
846 })
847 _, _ = rw.Write(b)
848 return
849 }
850
851 if req.URL.Path == "/deeper/stringProp.json" {
852 rw.Header().Set("Content-Type", "application/json")
853 b, _ := json.Marshal(map[string]interface{}{
854 "type": "string",
855 })
856 _, _ = rw.Write(b)
857 return
858 }
859
860 if req.URL.Path == "/deeper/arrayProp.json" {
861 rw.Header().Set("Content-Type", "application/json")
862 b, _ := json.Marshal(map[string]interface{}{
863 "type": "array",
864 "items": map[string]interface{}{
865 "type": "file",
866 },
867 })
868 _, _ = rw.Write(b)
869 return
870 }
871
872 rw.WriteHeader(http.StatusNotFound)
873 }))
874 servedAt = server.URL
875 return server
876 }
877
878 func TestExpand_RemoteRefWithResolutionContext(t *testing.T) {
879 server := resolutionContextServer()
880 defer server.Close()
881
882 tgt := RefSchema(server.URL + "/resolution.json#/definitions/bool")
883 require.NoError(t, ExpandSchema(tgt, nil, nil))
884
885 assert.Equal(t, StringOrArray([]string{"boolean"}), tgt.Type)
886 assert.Empty(t, tgt.Ref)
887 }
888
889 func TestExpandRemoteRef_WithNestedResolutionContext(t *testing.T) {
890 server := resolutionContextServer()
891 defer server.Close()
892
893 tgt := RefSchema(server.URL + "/resolution.json#/items")
894 require.NoError(t, ExpandSchema(tgt, nil, nil))
895
896 require.Empty(t, tgt.Ref)
897 require.NotNil(t, tgt.Items)
898 require.NotNil(t, tgt.Schema)
899 assert.Equal(t, "deeper/", tgt.ID)
900
901 assert.Equal(t, StringOrArray([]string{"string"}), tgt.Items.Schema.Type)
902 assert.Empty(t, tgt.Items.Schema.Ref)
903 }
904
905
919
920 func TestExpand_RemoteRefWithNestedResolutionContextWithFragment(t *testing.T) {
921 server := resolutionContextServer()
922 defer server.Close()
923
924 tgt := RefSchema(server.URL + "/resolution2.json#/items")
925 require.NoError(t, ExpandSchema(tgt, nil, nil))
926
927 require.Empty(t, tgt.Ref)
928 require.NotNil(t, tgt.Items)
929 require.NotNil(t, tgt.Schema)
930 assert.Equal(t, "deeper/", tgt.ID)
931
932 assert.Equal(t, StringOrArray([]string{"file"}), tgt.Items.Schema.Type)
933 assert.Empty(t, tgt.Items.Schema.Ref)
934 }
935
936 func TestExpand_TransitiveRefs(t *testing.T) {
937 basePath := filepath.Join(specs, "todos.json")
938
939 rawSpec, err := os.ReadFile(basePath)
940 require.NoError(t, err)
941
942 var spec *Swagger
943 require.NoError(t, json.Unmarshal(rawSpec, &spec))
944
945 opts := &ExpandOptions{
946 RelativeBase: basePath,
947 }
948
949 require.NoError(t, ExpandSpec(spec, opts))
950
951 assert.Equal(t, "todos.stoplight.io", spec.Host)
952 jazon := asJSON(t, spec)
953
954
955 assertNoRef(t, jazon)
956 }
957
958 func TestExpand_SchemaWithRoot(t *testing.T) {
959 root := new(Swagger)
960 require.NoError(t, json.Unmarshal(PetStoreJSONMessage, root))
961
962
963 origPet := root.Definitions["Pet"]
964 newPet := origPet
965 newPet.ID = ""
966 root.Definitions["Pet"] = newPet
967 expandRootWithID(t, root, withoutSchemaID)
968
969
970
971 root.Definitions["Pet"] = origPet
972 expandRootWithID(t, root, withSchemaID)
973 }
974
975 func expandRootWithID(t testing.TB, root *Swagger, testcase string) {
976 t.Logf("case: expanding $ref to schema without ID, with nested $ref with %s ID", testcase)
977 sch := RefSchema("#/definitions/newPet")
978 err := ExpandSchema(sch, root, nil)
979
980 if testcase == withSchemaID {
981 require.Errorf(t, err, "expected %s NOT to expand properly because of the ID in the parent schema", sch.Ref.String())
982 } else {
983 require.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
984
985
986 jazon := asJSON(t, sch)
987 assertNoRef(t, jazon)
988 }
989
990 t.Log("case: expanding $ref to schema without nested $ref")
991 sch = RefSchema("#/definitions/Category")
992 require.NoErrorf(t, ExpandSchema(sch, root, nil), "expected %s to expand properly", sch.Ref.String())
993
994 t.Logf("case: expanding $ref to schema with %s ID and nested $ref", testcase)
995 sch = RefSchema("#/definitions/Pet")
996 err = ExpandSchema(sch, root, nil)
997
998 if testcase == withSchemaID {
999 require.Errorf(t, err, "expected %s NOT to expand properly because of the ID in the parent schema", sch.Ref.String())
1000 } else {
1001 require.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
1002
1003
1004 jazon := asJSON(t, sch)
1005 assertNoRef(t, jazon)
1006 }
1007 }
1008
1009 func TestExpand_PathItem(t *testing.T) {
1010 jazon, _ := expandThisOrDieTrying(t, pathItemsFixture)
1011 assert.JSONEq(t, `{
1012 "swagger": "2.0",
1013 "info": {
1014 "title": "PathItems refs",
1015 "version": "1.0"
1016 },
1017 "paths": {
1018 "/todos": {
1019 "get": {
1020 "responses": {
1021 "200": {
1022 "description": "List Todos",
1023 "schema": {
1024 "type": "array",
1025 "items": {
1026 "type": "string"
1027 }
1028 }
1029 },
1030 "404": {
1031 "description": "error"
1032 }
1033 }
1034 }
1035 }
1036 }
1037 }`, jazon)
1038 }
1039
1040 func TestExpand_ExtraItems(t *testing.T) {
1041 jazon, _ := expandThisOrDieTrying(t, extraRefFixture)
1042 assert.JSONEq(t, `{
1043 "schemes": [
1044 "http"
1045 ],
1046 "swagger": "2.0",
1047 "info": {
1048 "title": "Supported, but non Swagger 20 compliant $ref constructs",
1049 "version": "2.1.0"
1050 },
1051 "host": "item.com",
1052 "basePath": "/extraRefs",
1053 "paths": {
1054 "/employees": {
1055 "get": {
1056 "summary": "List Employee Types",
1057 "operationId": "LIST-Employees",
1058 "parameters": [
1059 {
1060 "description": "unsupported $ref in simple param",
1061 "type": "array",
1062 "items": {
1063 "$ref": "#/definitions/arrayType"
1064 },
1065 "name": "myQueryParam",
1066 "in": "query"
1067 }
1068 ],
1069 "responses": {
1070 "200": {
1071 "description": "unsupported $ref in header",
1072 "schema": {
1073 "type": "string"
1074 },
1075 "headers": {
1076 "X-header": {
1077 "type": "array",
1078 "items": {
1079 "$ref": "#/definitions/headerType"
1080 }
1081 }
1082 }
1083 }
1084 }
1085 }
1086 }
1087 },
1088 "definitions": {
1089 "arrayType": {
1090 "type": "integer",
1091 "format": "int32"
1092 },
1093 "headerType": {
1094 "type": "string",
1095 "format": "uuid"
1096 }
1097 }
1098 }`, jazon)
1099 }
1100
1101 func TestExpand_Issue145(t *testing.T) {
1102 cwd, err := os.Getwd()
1103 require.NoError(t, err)
1104 pseudoRoot := normalizeBase(filepath.Join(cwd, rootBase))
1105
1106
1107 t.Run("with nil root, empty cache", func(t *testing.T) {
1108 cache := defaultResolutionCache()
1109 require.Equal(t, pseudoRoot, baseForRoot(nil, cache))
1110
1111 t.Run("empty root is cached", func(t *testing.T) {
1112 value, ok := cache.Get(pseudoRoot)
1113 require.True(t, ok)
1114 asMap, ok := value.(map[string]interface{})
1115 require.True(t, ok)
1116 require.Empty(t, asMap)
1117 })
1118 })
1119
1120 t.Run("with non-nil root, empty cache", func(t *testing.T) {
1121 cache := defaultResolutionCache()
1122 require.Equal(t, pseudoRoot, baseForRoot(map[string]interface{}{"key": "arbitrary"}, cache))
1123
1124 t.Run("non-empty root is cached", func(t *testing.T) {
1125 value, ok := cache.Get(pseudoRoot)
1126 require.True(t, ok)
1127 asMap, ok := value.(map[string]interface{})
1128 require.True(t, ok)
1129 require.Contains(t, asMap, "key")
1130 require.Equal(t, "arbitrary", asMap["key"])
1131 })
1132
1133 t.Run("with nil root, non-empty cache", func(t *testing.T) {
1134 require.Equal(t, pseudoRoot, baseForRoot(nil, cache))
1135
1136 t.Run("non-empty root is kept", func(t *testing.T) {
1137 value, ok := cache.Get(pseudoRoot)
1138 require.True(t, ok)
1139 asMap, ok := value.(map[string]interface{})
1140 require.True(t, ok)
1141 require.Contains(t, asMap, "key")
1142 require.Equal(t, "arbitrary", asMap["key"])
1143 })
1144 })
1145 })
1146 }
1147
1148
1149 const PetStore20 = `{
1150 "swagger": "2.0",
1151 "info": {
1152 "version": "1.0.0",
1153 "title": "Swagger Petstore",
1154 "contact": {
1155 "name": "Wordnik API Team",
1156 "url": "http://developer.wordnik.com"
1157 },
1158 "license": {
1159 "name": "Creative Commons 4.0 International",
1160 "url": "http://creativecommons.org/licenses/by/4.0/"
1161 }
1162 },
1163 "host": "petstore.swagger.wordnik.com",
1164 "basePath": "/api",
1165 "schemes": [
1166 "http"
1167 ],
1168 "paths": {
1169 "/pets": {
1170 "get": {
1171 "security": [
1172 {
1173 "basic": []
1174 }
1175 ],
1176 "tags": [ "Pet Operations" ],
1177 "operationId": "getAllPets",
1178 "parameters": [
1179 {
1180 "name": "status",
1181 "in": "query",
1182 "description": "The status to filter by",
1183 "type": "string"
1184 },
1185 {
1186 "name": "limit",
1187 "in": "query",
1188 "description": "The maximum number of results to return",
1189 "type": "integer",
1190 "format": "int64"
1191 }
1192 ],
1193 "summary": "Finds all pets in the system",
1194 "responses": {
1195 "200": {
1196 "description": "Pet response",
1197 "schema": {
1198 "type": "array",
1199 "items": {
1200 "$ref": "#/definitions/Pet"
1201 }
1202 }
1203 },
1204 "default": {
1205 "description": "Unexpected error",
1206 "schema": {
1207 "$ref": "#/definitions/Error"
1208 }
1209 }
1210 }
1211 },
1212 "post": {
1213 "security": [
1214 {
1215 "basic": []
1216 }
1217 ],
1218 "tags": [ "Pet Operations" ],
1219 "operationId": "createPet",
1220 "summary": "Creates a new pet",
1221 "consumes": ["application/x-yaml"],
1222 "produces": ["application/x-yaml"],
1223 "parameters": [
1224 {
1225 "name": "pet",
1226 "in": "body",
1227 "description": "The Pet to create",
1228 "required": true,
1229 "schema": {
1230 "$ref": "#/definitions/newPet"
1231 }
1232 }
1233 ],
1234 "responses": {
1235 "200": {
1236 "description": "Created Pet response",
1237 "schema": {
1238 "$ref": "#/definitions/Pet"
1239 }
1240 },
1241 "default": {
1242 "description": "Unexpected error",
1243 "schema": {
1244 "$ref": "#/definitions/Error"
1245 }
1246 }
1247 }
1248 }
1249 },
1250 "/pets/{id}": {
1251 "delete": {
1252 "security": [
1253 {
1254 "apiKey": []
1255 }
1256 ],
1257 "description": "Deletes the Pet by id",
1258 "operationId": "deletePet",
1259 "parameters": [
1260 {
1261 "name": "id",
1262 "in": "path",
1263 "description": "ID of pet to delete",
1264 "required": true,
1265 "type": "integer",
1266 "format": "int64"
1267 }
1268 ],
1269 "responses": {
1270 "204": {
1271 "description": "pet deleted"
1272 },
1273 "default": {
1274 "description": "unexpected error",
1275 "schema": {
1276 "$ref": "#/definitions/Error"
1277 }
1278 }
1279 }
1280 },
1281 "get": {
1282 "tags": [ "Pet Operations" ],
1283 "operationId": "getPetById",
1284 "summary": "Finds the pet by id",
1285 "responses": {
1286 "200": {
1287 "description": "Pet response",
1288 "schema": {
1289 "$ref": "#/definitions/Pet"
1290 }
1291 },
1292 "default": {
1293 "description": "Unexpected error",
1294 "schema": {
1295 "$ref": "#/definitions/Error"
1296 }
1297 }
1298 }
1299 },
1300 "parameters": [
1301 {
1302 "name": "id",
1303 "in": "path",
1304 "description": "ID of pet",
1305 "required": true,
1306 "type": "integer",
1307 "format": "int64"
1308 }
1309 ]
1310 }
1311 },
1312 "definitions": {
1313 "Category": {
1314 "id": "Category",
1315 "properties": {
1316 "id": {
1317 "format": "int64",
1318 "type": "integer"
1319 },
1320 "name": {
1321 "type": "string"
1322 }
1323 }
1324 },
1325 "Pet": {
1326 "id": "Pet",
1327 "properties": {
1328 "category": {
1329 "$ref": "#/definitions/Category"
1330 },
1331 "id": {
1332 "description": "unique identifier for the pet",
1333 "format": "int64",
1334 "maximum": 100.0,
1335 "minimum": 0.0,
1336 "type": "integer"
1337 },
1338 "name": {
1339 "type": "string"
1340 },
1341 "photoUrls": {
1342 "items": {
1343 "type": "string"
1344 },
1345 "type": "array"
1346 },
1347 "status": {
1348 "description": "pet status in the store",
1349 "enum": [
1350 "available",
1351 "pending",
1352 "sold"
1353 ],
1354 "type": "string"
1355 },
1356 "tags": {
1357 "items": {
1358 "$ref": "#/definitions/Tag"
1359 },
1360 "type": "array"
1361 }
1362 },
1363 "required": [
1364 "id",
1365 "name"
1366 ]
1367 },
1368 "newPet": {
1369 "anyOf": [
1370 {
1371 "$ref": "#/definitions/Pet"
1372 },
1373 {
1374 "required": [
1375 "name"
1376 ]
1377 }
1378 ]
1379 },
1380 "Tag": {
1381 "id": "Tag",
1382 "properties": {
1383 "id": {
1384 "format": "int64",
1385 "type": "integer"
1386 },
1387 "name": {
1388 "type": "string"
1389 }
1390 }
1391 },
1392 "Error": {
1393 "required": [
1394 "code",
1395 "message"
1396 ],
1397 "properties": {
1398 "code": {
1399 "type": "integer",
1400 "format": "int32"
1401 },
1402 "message": {
1403 "type": "string"
1404 }
1405 }
1406 }
1407 },
1408 "consumes": [
1409 "application/json",
1410 "application/xml"
1411 ],
1412 "produces": [
1413 "application/json",
1414 "application/xml",
1415 "text/plain",
1416 "text/html"
1417 ],
1418 "securityDefinitions": {
1419 "basic": {
1420 "type": "basic"
1421 },
1422 "apiKey": {
1423 "type": "apiKey",
1424 "in": "header",
1425 "name": "X-API-KEY"
1426 }
1427 }
1428 }
1429 `
1430
View as plain text