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
15package spec
16
17import (
18	"encoding/json"
19	"io/ioutil"
20	"log"
21	"net/http"
22	"net/http/httptest"
23	"os"
24	"path/filepath"
25	"regexp"
26	"runtime"
27	"strings"
28	"testing"
29
30	"github.com/go-openapi/jsonpointer"
31	"github.com/go-openapi/swag"
32	"github.com/stretchr/testify/assert"
33	"github.com/stretchr/testify/require"
34)
35
36var specs = filepath.Join("fixtures", "specs")
37
38var (
39	rex = regexp.MustCompile(`"\$ref":\s*"(.+)"`)
40)
41
42func jsonDoc(path string) (json.RawMessage, error) {
43	data, err := swag.LoadFromFileOrHTTP(path)
44	if err != nil {
45		return nil, err
46	}
47	return json.RawMessage(data), nil
48}
49
50// tests that paths are normalized correctly
51func TestNormalizePaths(t *testing.T) {
52	type testNormalizePathsTestCases []struct {
53		refPath   string
54		base      string
55		expOutput string
56	}
57	testCases := func() testNormalizePathsTestCases {
58		testCases := testNormalizePathsTestCases{
59			{
60				// http basePath, absolute refPath
61				refPath:   "http://www.anotherexample.com/another/base/path/swagger.json#/definitions/Pet",
62				base:      "http://www.example.com/base/path/swagger.json",
63				expOutput: "http://www.anotherexample.com/another/base/path/swagger.json#/definitions/Pet",
64			},
65			{
66				// http basePath, relative refPath
67				refPath:   "another/base/path/swagger.json#/definitions/Pet",
68				base:      "http://www.example.com/base/path/swagger.json",
69				expOutput: "http://www.example.com/base/path/another/base/path/swagger.json#/definitions/Pet",
70			},
71		}
72		if runtime.GOOS == "windows" {
73			testCases = append(testCases, testNormalizePathsTestCases{
74				{
75					// file basePath, absolute refPath, no fragment
76					refPath:   `C:\another\base\path.json`,
77					base:      `C:\base\path.json`,
78					expOutput: `C:\another\base\path.json`,
79				},
80				{
81					// file basePath, absolute refPath
82					refPath:   `C:\another\base\path.json#/definitions/Pet`,
83					base:      `C:\base\path.json`,
84					expOutput: `C:\another\base\path.json#/definitions/Pet`,
85				},
86				{
87					// file basePath, relative refPath
88					refPath:   `another\base\path.json#/definitions/Pet`,
89					base:      `C:\base\path.json`,
90					expOutput: `C:\base\another\base\path.json#/definitions/Pet`,
91				},
92			}...)
93			return testCases
94		}
95		// linux case
96		testCases = append(testCases, testNormalizePathsTestCases{
97			{
98				// file basePath, absolute refPath, no fragment
99				refPath:   "/another/base/path.json",
100				base:      "/base/path.json",
101				expOutput: "/another/base/path.json",
102			},
103			{
104				// file basePath, absolute refPath
105				refPath:   "/another/base/path.json#/definitions/Pet",
106				base:      "/base/path.json",
107				expOutput: "/another/base/path.json#/definitions/Pet",
108			},
109			{
110				// file basePath, relative refPath
111				refPath:   "another/base/path.json#/definitions/Pet",
112				base:      "/base/path.json",
113				expOutput: "/base/another/base/path.json#/definitions/Pet",
114			},
115		}...)
116		return testCases
117	}()
118
119	for _, tcase := range testCases {
120		out := normalizePaths(tcase.refPath, tcase.base)
121		assert.Equal(t, tcase.expOutput, out)
122	}
123}
124
125func TestExpandsKnownRef(t *testing.T) {
126	schema := RefProperty("http://json-schema.org/draft-04/schema#")
127	if assert.NoError(t, ExpandSchema(schema, nil, nil)) {
128		assert.Equal(t, "Core schema meta-schema", schema.Description)
129	}
130}
131
132func TestExpandResponseSchema(t *testing.T) {
133	fp := "./fixtures/local_expansion/spec.json"
134	b, err := jsonDoc(fp)
135	if assert.NoError(t, err) {
136		var spec Swagger
137		if err := json.Unmarshal(b, &spec); assert.NoError(t, err) {
138			err := ExpandSpec(&spec, &ExpandOptions{RelativeBase: fp})
139			if assert.NoError(t, err) {
140				sch := spec.Paths.Paths["/item"].Get.Responses.StatusCodeResponses[200].Schema
141				if assert.NotNil(t, sch) {
142					assert.Empty(t, sch.Ref.String())
143					assert.Contains(t, sch.Type, "object")
144					assert.Len(t, sch.Properties, 2)
145				}
146			}
147		}
148	}
149}
150
151func TestSpecExpansion(t *testing.T) {
152	spec := new(Swagger)
153
154	err := ExpandSpec(spec, nil)
155	assert.NoError(t, err)
156
157	specDoc, err := jsonDoc("fixtures/expansion/all-the-things.json")
158	assert.NoError(t, err)
159
160	specPath, _ := absPath("fixtures/expansion/all-the-things.json")
161	opts := &ExpandOptions{
162		RelativeBase: specPath,
163	}
164
165	spec = new(Swagger)
166	err = json.Unmarshal(specDoc, spec)
167	assert.NoError(t, err)
168
169	pet := spec.Definitions["pet"]
170	errorModel := spec.Definitions["errorModel"]
171	petResponse := spec.Responses["petResponse"]
172	petResponse.Schema = &pet
173	stringResponse := spec.Responses["stringResponse"]
174	tagParam := spec.Parameters["tag"]
175	idParam := spec.Parameters["idParam"]
176
177	err = ExpandSpec(spec, opts)
178	assert.NoError(t, err)
179
180	assert.Equal(t, tagParam, spec.Parameters["query"])
181	assert.Equal(t, petResponse, spec.Responses["petResponse"])
182	assert.Equal(t, petResponse, spec.Responses["anotherPet"])
183	assert.Equal(t, pet, *spec.Responses["petResponse"].Schema)
184	assert.Equal(t, stringResponse, *spec.Paths.Paths["/"].Get.Responses.Default)
185	assert.Equal(t, petResponse, spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200])
186	assert.Equal(t, pet, *spec.Paths.Paths["/pets"].Get.Responses.StatusCodeResponses[200].Schema.Items.Schema)
187	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Get.Responses.Default.Schema)
188	assert.Equal(t, pet, spec.Definitions["petInput"].AllOf[0])
189	assert.Equal(t, spec.Definitions["petInput"], *spec.Paths.Paths["/pets"].Post.Parameters[0].Schema)
190	assert.Equal(t, petResponse, spec.Paths.Paths["/pets"].Post.Responses.StatusCodeResponses[200])
191	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Post.Responses.Default.Schema)
192	pi := spec.Paths.Paths["/pets/{id}"]
193	assert.Equal(t, idParam, pi.Get.Parameters[0])
194	assert.Equal(t, petResponse, pi.Get.Responses.StatusCodeResponses[200])
195	assert.Equal(t, errorModel, *pi.Get.Responses.Default.Schema)
196	assert.Equal(t, idParam, pi.Delete.Parameters[0])
197	assert.Equal(t, errorModel, *pi.Delete.Responses.Default.Schema)
198}
199
200func TestResolveRef(t *testing.T) {
201	var root interface{}
202	err := json.Unmarshal([]byte(PetStore20), &root)
203	assert.NoError(t, err)
204	ref, err := NewRef("#/definitions/Category")
205	assert.NoError(t, err)
206	sch, err := ResolveRef(root, &ref)
207	assert.NoError(t, err)
208	b, _ := sch.MarshalJSON()
209	assert.JSONEq(t, `{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}`, string(b))
210
211	// WithBase variant
212	sch, err = ResolveRefWithBase(root, &ref, &ExpandOptions{
213		RelativeBase: "/",
214	})
215	assert.NoError(t, err)
216	b, _ = sch.MarshalJSON()
217	assert.JSONEq(t, `{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}`, string(b))
218}
219
220func TestResponseExpansion(t *testing.T) {
221	specDoc, err := jsonDoc("fixtures/expansion/all-the-things.json")
222	assert.NoError(t, err)
223
224	basePath, err := absPath("fixtures/expansion/all-the-things.json")
225	assert.NoError(t, err)
226
227	spec := new(Swagger)
228	err = json.Unmarshal(specDoc, spec)
229	assert.NoError(t, err)
230
231	resolver, err := defaultSchemaLoader(spec, nil, nil, nil)
232	assert.NoError(t, err)
233
234	resp := spec.Responses["anotherPet"]
235	expected := spec.Responses["petResponse"]
236	err = expandParameterOrResponse(&expected, resolver, basePath)
237	assert.NoError(t, err)
238
239	jazon, _ := json.MarshalIndent(expected, "", " ")
240	assert.JSONEq(t, `{
241         "description": "pet response",
242         "schema": {
243          "required": [
244           "id",
245           "name"
246          ],
247          "properties": {
248           "id": {
249            "type": "integer",
250            "format": "int64"
251           },
252           "name": {
253            "type": "string"
254           },
255           "tag": {
256            "type": "string"
257           }
258          }
259         }
260			 }`, string(jazon))
261
262	err = expandParameterOrResponse(&resp, resolver, basePath)
263	assert.NoError(t, err)
264	assert.Equal(t, expected, resp)
265
266	resp2 := spec.Paths.Paths["/"].Get.Responses.Default
267	expected = spec.Responses["stringResponse"]
268
269	err = expandParameterOrResponse(resp2, resolver, basePath)
270	assert.NoError(t, err)
271	assert.Equal(t, expected, *resp2)
272
273	// cascading ref
274	resp = spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
275	expected = spec.Responses["petResponse"]
276	jazon, _ = json.MarshalIndent(resp, "", " ")
277	assert.JSONEq(t, `{
278         "$ref": "#/responses/anotherPet"
279        }`, string(jazon))
280
281	err = expandParameterOrResponse(&resp, resolver, basePath)
282	assert.NoError(t, err)
283	// NOTE(fredbi): fixed this testcase in schema_loader.go#227
284	assert.Equal(t, expected, resp)
285}
286
287func TestResponseResolve(t *testing.T) {
288	specDoc, err := jsonDoc("fixtures/expansion/all-the-things.json")
289	assert.NoError(t, err)
290
291	spec := new(Swagger)
292	err = json.Unmarshal(specDoc, spec)
293	assert.NoError(t, err)
294
295	// Resolve with root version
296	resp := spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
297	resp2, err := ResolveResponse(spec, resp.Ref)
298	assert.NoError(t, err)
299	// resolve resolves the ref, but dos not expand
300	jazon, _ := json.MarshalIndent(resp2, "", " ")
301	assert.JSONEq(t, `{
302         "$ref": "#/responses/petResponse"
303        }`, string(jazon))
304}
305
306// test the exported version of ExpandResponse
307func TestExportedResponseExpansion(t *testing.T) {
308	specDoc, err := jsonDoc("fixtures/expansion/all-the-things.json")
309	if !assert.NoError(t, err) {
310		t.FailNow()
311	}
312
313	basePath, err := absPath("fixtures/expansion/all-the-things.json")
314	assert.NoError(t, err)
315
316	spec := new(Swagger)
317	err = json.Unmarshal(specDoc, spec)
318	assert.NoError(t, err)
319
320	resp := spec.Responses["anotherPet"]
321	expected := spec.Responses["petResponse"]
322	err = ExpandResponse(&expected, basePath)
323	assert.NoError(t, err)
324
325	err = ExpandResponse(&resp, basePath)
326	assert.NoError(t, err)
327	assert.Equal(t, expected, resp)
328
329	resp2 := spec.Paths.Paths["/"].Get.Responses.Default
330	expected = spec.Responses["stringResponse"]
331
332	err = ExpandResponse(resp2, basePath)
333	assert.NoError(t, err)
334	assert.Equal(t, expected, *resp2)
335
336	resp = spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
337	expected = spec.Responses["petResponse"]
338
339	err = ExpandResponse(&resp, basePath)
340	assert.NoError(t, err)
341	// NOTE(fredbi): fixed this testcase in schema_loader.go#227
342	assert.Equal(t, expected, resp)
343}
344
345func TestExpandResponseAndParamWithRoot(t *testing.T) {
346	specDoc, err := jsonDoc("fixtures/bugs/1614/gitea.json")
347	if !assert.NoError(t, err) {
348		t.FailNow()
349		return
350	}
351	var spec Swagger
352	_ = json.Unmarshal(specDoc, &spec)
353
354	// check responses with $ref
355	resp := spec.Paths.Paths["/admin/users"].Post.Responses.StatusCodeResponses[201]
356	err = ExpandResponseWithRoot(&resp, spec, nil)
357	assert.NoError(t, err)
358	jazon, _ := json.MarshalIndent(resp, "", " ")
359	m := rex.FindAllStringSubmatch(string(jazon), -1)
360	assert.Nil(t, m)
361
362	resp = spec.Paths.Paths["/admin/users"].Post.Responses.StatusCodeResponses[403]
363	err = ExpandResponseWithRoot(&resp, spec, nil)
364	assert.NoError(t, err)
365	jazon, _ = json.MarshalIndent(resp, "", " ")
366	m = rex.FindAllStringSubmatch(string(jazon), -1)
367	assert.Nil(t, m)
368
369	// check param with $ref
370	param := spec.Paths.Paths["/admin/users"].Post.Parameters[0]
371	err = ExpandParameterWithRoot(&param, spec, nil)
372	assert.NoError(t, err)
373	jazon, _ = json.MarshalIndent(param, "", " ")
374	m = rex.FindAllStringSubmatch(string(jazon), -1)
375	assert.Nil(t, m)
376}
377
378func TestResolveParam(t *testing.T) {
379	specDoc, err := jsonDoc("fixtures/expansion/all-the-things.json")
380	if !assert.NoError(t, err) {
381		t.FailNow()
382	}
383	var spec Swagger
384	_ = json.Unmarshal(specDoc, &spec)
385
386	param := spec.Paths.Paths["/pets/{id}"].Get.Parameters[0]
387	par, err := ResolveParameter(spec, param.Ref)
388	assert.NoError(t, err)
389	jazon, _ := json.MarshalIndent(par, "", " ")
390	assert.JSONEq(t, `{
391      "name": "id",
392      "in": "path",
393      "description": "ID of pet to fetch",
394      "required": true,
395      "type": "integer",
396      "format": "int64"
397      }`, string(jazon))
398}
399
400func TestIssue3(t *testing.T) {
401	spec := new(Swagger)
402	specDoc, err := jsonDoc("fixtures/expansion/overflow.json")
403	assert.NoError(t, err)
404
405	specPath, _ := absPath("fixtures/expansion/overflow.json")
406	opts := &ExpandOptions{
407		RelativeBase: specPath,
408	}
409
410	err = json.Unmarshal(specDoc, spec)
411	assert.NoError(t, err)
412
413	assert.NotPanics(t, func() {
414		err = ExpandSpec(spec, opts)
415		assert.NoError(t, err)
416	}, "Calling expand spec with circular refs, should not panic!")
417}
418
419func TestParameterExpansion(t *testing.T) {
420	paramDoc, err := jsonDoc("fixtures/expansion/params.json")
421	assert.NoError(t, err)
422
423	spec := new(Swagger)
424	err = json.Unmarshal(paramDoc, spec)
425	assert.NoError(t, err)
426
427	basePath, err := absPath("fixtures/expansion/params.json")
428	assert.NoError(t, err)
429
430	resolver, err := defaultSchemaLoader(spec, nil, nil, nil)
431	assert.NoError(t, err)
432
433	param := spec.Parameters["query"]
434	expected := spec.Parameters["tag"]
435
436	err = expandParameterOrResponse(&param, resolver, basePath)
437	assert.NoError(t, err)
438	assert.Equal(t, expected, param)
439
440	param = spec.Paths.Paths["/cars/{id}"].Parameters[0]
441	expected = spec.Parameters["id"]
442
443	err = expandParameterOrResponse(&param, resolver, basePath)
444	assert.NoError(t, err)
445	assert.Equal(t, expected, param)
446}
447
448func TestExportedParameterExpansion(t *testing.T) {
449	paramDoc, err := jsonDoc("fixtures/expansion/params.json")
450	assert.NoError(t, err)
451
452	spec := new(Swagger)
453	err = json.Unmarshal(paramDoc, spec)
454	assert.NoError(t, err)
455
456	basePath, err := absPath("fixtures/expansion/params.json")
457	assert.NoError(t, err)
458
459	param := spec.Parameters["query"]
460	expected := spec.Parameters["tag"]
461
462	err = ExpandParameter(&param, basePath)
463	assert.NoError(t, err)
464	assert.Equal(t, expected, param)
465
466	param = spec.Paths.Paths["/cars/{id}"].Parameters[0]
467	expected = spec.Parameters["id"]
468
469	err = ExpandParameter(&param, basePath)
470	assert.NoError(t, err)
471	assert.Equal(t, expected, param)
472}
473
474func TestCircularRefsExpansion(t *testing.T) {
475	carsDoc, err := jsonDoc("fixtures/expansion/circularRefs.json")
476	assert.NoError(t, err)
477
478	basePath, _ := absPath("fixtures/expansion/circularRefs.json")
479
480	spec := new(Swagger)
481	err = json.Unmarshal(carsDoc, spec)
482	assert.NoError(t, err)
483
484	resolver, err := defaultSchemaLoader(spec, &ExpandOptions{RelativeBase: basePath}, nil, nil)
485	assert.NoError(t, err)
486	schema := spec.Definitions["car"]
487
488	assert.NotPanics(t, func() {
489		_, err := expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
490		assert.NoError(t, err)
491	}, "Calling expand schema with circular refs, should not panic!")
492}
493
494func TestCircularSpec2Expansion(t *testing.T) {
495	// TODO: assert repeatable results (see commented section below)
496
497	fixturePath := filepath.Join("fixtures", "expansion", "circular-minimal.json")
498	jazon := expandThisOrDieTrying(t, fixturePath)
499	assert.NotEmpty(t, jazon)
500	// assert stripped $ref in result
501	assert.NotContainsf(t, jazon, "circular-minimal.json#/",
502		"expected %s to be expanded with stripped circular $ref", fixturePath)
503
504	fixturePath = "fixtures/expansion/circularSpec2.json"
505	jazon = expandThisOrDieTrying(t, fixturePath)
506	assert.NotEmpty(t, jazon)
507	assert.NotContainsf(t, jazon, "circularSpec.json#/",
508		"expected %s to be expanded with stripped circular $ref", fixturePath)
509
510	/*
511
512		At the moment, the result of expanding circular references is not stable,
513		when several cycles have intersections:
514		the spec structure is randomly walked through and mutating as expansion is carried out.
515		detected cycles in $ref are not necessarily the shortest matches.
516
517		This may result in different, functionally correct expanded spec (e.g. with same validations)
518
519			for i := 0; i < 1; i++ {
520				bbb := expandThisOrDieTrying(t, fixturePath)
521				t.Log(bbb)
522				if !assert.JSONEqf(t, jazon, bbb, "on iteration %d, we should have stable expanded spec", i) {
523					t.FailNow()
524					return
525				}
526			}
527	*/
528}
529
530func Test_MoreCircular(t *testing.T) {
531	// Additional testcase for circular $ref (from go-openapi/validate):
532	// - $ref with file = current file
533	// - circular is located in remote file
534	//
535	// There are 4 variants to run:
536	// - with/without $ref with local file (so its not really remote)
537	// - with circular in a schema in  #/responses
538	// - with circular in a schema in  #/parameters
539
540	fixturePath := "fixtures/more_circulars/spec.json"
541	jazon := expandThisOrDieTrying(t, fixturePath)
542	m := rex.FindAllStringSubmatch(jazon, -1)
543	if assert.NotNil(t, m) {
544		for _, matched := range m {
545			subMatch := matched[1]
546			assert.True(t, strings.HasPrefix(subMatch, "item.json#/item"),
547				"expected $ref to be relative, got: %s", matched[0])
548		}
549	}
550
551	fixturePath = "fixtures/more_circulars/spec2.json"
552	jazon = expandThisOrDieTrying(t, fixturePath)
553	m = rex.FindAllStringSubmatch(jazon, -1)
554	if assert.NotNil(t, m) {
555		for _, matched := range m {
556			subMatch := matched[1]
557			assert.True(t, strings.HasPrefix(subMatch, "item2.json#/item"),
558				"expected $ref to be relative, got: %s", matched[0])
559		}
560	}
561
562	fixturePath = "fixtures/more_circulars/spec3.json"
563	jazon = expandThisOrDieTrying(t, fixturePath)
564	m = rex.FindAllStringSubmatch(jazon, -1)
565	if assert.NotNil(t, m) {
566		for _, matched := range m {
567			subMatch := matched[1]
568			assert.True(t, strings.HasPrefix(subMatch, "item.json#/item"),
569				"expected $ref to be relative, got: %s", matched[0])
570		}
571	}
572
573	fixturePath = "fixtures/more_circulars/spec4.json"
574	jazon = expandThisOrDieTrying(t, fixturePath)
575	m = rex.FindAllStringSubmatch(jazon, -1)
576	if assert.NotNil(t, m) {
577		for _, matched := range m {
578			subMatch := matched[1]
579			assert.True(t, strings.HasPrefix(subMatch, "item4.json#/item"),
580				"expected $ref to be relative, got: %s", matched[0])
581		}
582	}
583}
584
585func Test_Issue957(t *testing.T) {
586	fixturePath := "fixtures/bugs/957/fixture-957.json"
587	jazon := expandThisOrDieTrying(t, fixturePath)
588	if assert.NotEmpty(t, jazon) {
589		assert.NotContainsf(t, jazon, "fixture-957.json#/",
590			"expected %s to be expanded with stripped circular $ref", fixturePath)
591		m := rex.FindAllStringSubmatch(jazon, -1)
592		if assert.NotNil(t, m) {
593			for _, matched := range m {
594				subMatch := matched[1]
595				assert.True(t, strings.HasPrefix(subMatch, "#/definitions/"),
596					"expected $ref to be inlined, got: %s", matched[0])
597			}
598		}
599		//t.Log(jazon)
600	}
601}
602
603func Test_Bitbucket(t *testing.T) {
604	// Additional testcase for circular $ref (from bitbucket api)
605
606	fixturePath := "fixtures/more_circulars/bitbucket.json"
607	jazon := expandThisOrDieTrying(t, fixturePath)
608	m := rex.FindAllStringSubmatch(jazon, -1)
609	if assert.NotNil(t, m) {
610		for _, matched := range m {
611			subMatch := matched[1]
612			assert.True(t, strings.HasPrefix(subMatch, "#/definitions/"),
613				"expected $ref to be inlined, got: %s", matched[0])
614		}
615	}
616}
617
618func Test_ExpandJSONSchemaDraft4(t *testing.T) {
619	fixturePath := filepath.Join("schemas", "jsonschema-draft-04.json")
620	jazon := expandThisSchemaOrDieTrying(t, fixturePath)
621	// assert all $ref maches  "$ref": "http://json-schema.org/draft-04/something"
622	m := rex.FindAllStringSubmatch(jazon, -1)
623	if assert.NotNil(t, m) {
624		for _, matched := range m {
625			subMatch := matched[1]
626			assert.True(t, strings.HasPrefix(subMatch, "http://json-schema.org/draft-04/"),
627				"expected $ref to be remote, got: %s", matched[0])
628		}
629	}
630}
631
632func Test_ExpandSwaggerSchema(t *testing.T) {
633	fixturePath := filepath.Join("schemas", "v2", "schema.json")
634	jazon := expandThisSchemaOrDieTrying(t, fixturePath)
635	// assert all $ref maches  "$ref": "#/definitions/something"
636	m := rex.FindAllStringSubmatch(jazon, -1)
637	if assert.NotNil(t, m) {
638		for _, matched := range m {
639			// matched := m[0]
640			subMatch := matched[1]
641			assert.True(t, strings.HasPrefix(subMatch, "#/definitions/"),
642				"expected $ref to be inlined, got: %s", matched[0])
643		}
644	}
645}
646
647func expandThisSchemaOrDieTrying(t *testing.T, fixturePath string) string {
648	doc, err := jsonDoc(fixturePath)
649	require.NoError(t, err)
650
651	specPath, _ := absPath(fixturePath)
652
653	opts := &ExpandOptions{
654		RelativeBase: specPath,
655	}
656
657	sch := new(Schema)
658	err = json.Unmarshal(doc, sch)
659	require.NoError(t, err)
660
661	require.NotPanics(t, func() {
662		err = ExpandSchemaWithBasePath(sch, nil, opts)
663		assert.NoError(t, err)
664	}, "Calling expand schema circular refs, should not panic!")
665
666	bbb, _ := json.MarshalIndent(sch, "", " ")
667	return string(bbb)
668}
669
670func expandThisOrDieTrying(t *testing.T, fixturePath string) string {
671	doc, err := jsonDoc(fixturePath)
672	if !assert.NoError(t, err) {
673		t.FailNow()
674		return ""
675	}
676
677	specPath, _ := absPath(fixturePath)
678
679	opts := &ExpandOptions{
680		RelativeBase: specPath,
681	}
682
683	spec := new(Swagger)
684	err = json.Unmarshal(doc, spec)
685	if !assert.NoError(t, err) {
686		t.FailNow()
687		return ""
688	}
689
690	assert.NotPanics(t, func() {
691		err = ExpandSpec(spec, opts)
692		assert.NoError(t, err)
693	}, "Calling expand spec with circular refs, should not panic!")
694
695	bbb, _ := json.MarshalIndent(spec, "", " ")
696	return string(bbb)
697}
698
699func TestContinueOnErrorExpansion(t *testing.T) {
700	defer log.SetOutput(os.Stdout)
701	log.SetOutput(ioutil.Discard)
702
703	missingRefDoc, err := jsonDoc("fixtures/expansion/missingRef.json")
704	assert.NoError(t, err)
705
706	specPath, _ := absPath("fixtures/expansion/missingRef.json")
707
708	testCase := struct {
709		Input    *Swagger `json:"input"`
710		Expected *Swagger `json:"expected"`
711	}{}
712	err = json.Unmarshal(missingRefDoc, &testCase)
713	assert.NoError(t, err)
714
715	opts := &ExpandOptions{
716		ContinueOnError: true,
717		RelativeBase:    specPath,
718	}
719	err = ExpandSpec(testCase.Input, opts)
720	assert.NoError(t, err)
721	// b, _ := testCase.Input.MarshalJSON()
722	// log.Printf(string(b))
723	assert.Equal(t, testCase.Input, testCase.Expected, "Should continue expanding spec when a definition can't be found.")
724
725	doc, err := jsonDoc("fixtures/expansion/missingItemRef.json")
726	spec := new(Swagger)
727	err = json.Unmarshal(doc, spec)
728	assert.NoError(t, err)
729
730	assert.NotPanics(t, func() {
731		err = ExpandSpec(spec, opts)
732		assert.NoError(t, err)
733	}, "Array of missing refs should not cause a panic, and continue to expand spec.")
734}
735
736func TestIssue415(t *testing.T) {
737	doc, err := jsonDoc("fixtures/expansion/clickmeter.json")
738	assert.NoError(t, err)
739
740	specPath, _ := absPath("fixtures/expansion/clickmeter.json")
741
742	opts := &ExpandOptions{
743		RelativeBase: specPath,
744	}
745
746	spec := new(Swagger)
747	err = json.Unmarshal(doc, spec)
748	assert.NoError(t, err)
749
750	assert.NotPanics(t, func() {
751		err = ExpandSpec(spec, opts)
752		assert.NoError(t, err)
753	}, "Calling expand spec with response schemas that have circular refs, should not panic!")
754}
755
756func TestCircularSpecExpansion(t *testing.T) {
757	doc, err := jsonDoc("fixtures/expansion/circularSpec.json")
758	assert.NoError(t, err)
759
760	specPath, _ := absPath("fixtures/expansion/circularSpec.json")
761
762	opts := &ExpandOptions{
763		RelativeBase: specPath,
764	}
765
766	spec := new(Swagger)
767	err = json.Unmarshal(doc, spec)
768	assert.NoError(t, err)
769
770	assert.NotPanics(t, func() {
771		err = ExpandSpec(spec, opts)
772		assert.NoError(t, err)
773	}, "Calling expand spec with circular refs, should not panic!")
774}
775
776func TestItemsExpansion(t *testing.T) {
777	carsDoc, err := jsonDoc("fixtures/expansion/schemas2.json")
778	assert.NoError(t, err)
779
780	basePath, _ := absPath("fixtures/expansion/schemas2.json")
781
782	spec := new(Swagger)
783	err = json.Unmarshal(carsDoc, spec)
784	assert.NoError(t, err)
785
786	resolver, err := defaultSchemaLoader(spec, nil, nil, nil)
787	assert.NoError(t, err)
788
789	schema := spec.Definitions["car"]
790	oldBrand := schema.Properties["brand"]
791	assert.NotEmpty(t, oldBrand.Items.Schema.Ref.String())
792	assert.NotEqual(t, spec.Definitions["brand"], oldBrand)
793
794	_, err = expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
795	assert.NoError(t, err)
796
797	newBrand := schema.Properties["brand"]
798	assert.Empty(t, newBrand.Items.Schema.Ref.String())
799	assert.Equal(t, spec.Definitions["brand"], *newBrand.Items.Schema)
800
801	schema = spec.Definitions["truck"]
802	assert.NotEmpty(t, schema.Items.Schema.Ref.String())
803
804	s, err := expandSchema(schema, []string{"#/definitions/truck"}, resolver, basePath)
805	schema = *s
806	assert.NoError(t, err)
807	assert.Empty(t, schema.Items.Schema.Ref.String())
808	assert.Equal(t, spec.Definitions["car"], *schema.Items.Schema)
809
810	sch := new(Schema)
811	_, err = expandSchema(*sch, []string{""}, resolver, basePath)
812	assert.NoError(t, err)
813
814	schema = spec.Definitions["batch"]
815	s, err = expandSchema(schema, []string{"#/definitions/batch"}, resolver, basePath)
816	schema = *s
817	assert.NoError(t, err)
818	assert.Empty(t, schema.Items.Schema.Items.Schema.Ref.String())
819	assert.Equal(t, *schema.Items.Schema.Items.Schema, spec.Definitions["brand"])
820
821	schema = spec.Definitions["batch2"]
822	s, err = expandSchema(schema, []string{"#/definitions/batch2"}, resolver, basePath)
823	schema = *s
824	assert.NoError(t, err)
825	assert.Empty(t, schema.Items.Schemas[0].Items.Schema.Ref.String())
826	assert.Empty(t, schema.Items.Schemas[1].Items.Schema.Ref.String())
827	assert.Equal(t, *schema.Items.Schemas[0].Items.Schema, spec.Definitions["brand"])
828	assert.Equal(t, *schema.Items.Schemas[1].Items.Schema, spec.Definitions["tag"])
829
830	schema = spec.Definitions["allofBoth"]
831	s, err = expandSchema(schema, []string{"#/definitions/allofBoth"}, resolver, basePath)
832	schema = *s
833	assert.NoError(t, err)
834	assert.Empty(t, schema.AllOf[0].Items.Schema.Ref.String())
835	assert.Empty(t, schema.AllOf[1].Items.Schema.Ref.String())
836	assert.Equal(t, *schema.AllOf[0].Items.Schema, spec.Definitions["brand"])
837	assert.Equal(t, *schema.AllOf[1].Items.Schema, spec.Definitions["tag"])
838
839	schema = spec.Definitions["anyofBoth"]
840	s, err = expandSchema(schema, []string{"#/definitions/anyofBoth"}, resolver, basePath)
841	schema = *s
842	assert.NoError(t, err)
843	assert.Empty(t, schema.AnyOf[0].Items.Schema.Ref.String())
844	assert.Empty(t, schema.AnyOf[1].Items.Schema.Ref.String())
845	assert.Equal(t, *schema.AnyOf[0].Items.Schema, spec.Definitions["brand"])
846	assert.Equal(t, *schema.AnyOf[1].Items.Schema, spec.Definitions["tag"])
847
848	schema = spec.Definitions["oneofBoth"]
849	s, err = expandSchema(schema, []string{"#/definitions/oneofBoth"}, resolver, basePath)
850	schema = *s
851	assert.NoError(t, err)
852	assert.Empty(t, schema.OneOf[0].Items.Schema.Ref.String())
853	assert.Empty(t, schema.OneOf[1].Items.Schema.Ref.String())
854	assert.Equal(t, *schema.OneOf[0].Items.Schema, spec.Definitions["brand"])
855	assert.Equal(t, *schema.OneOf[1].Items.Schema, spec.Definitions["tag"])
856
857	schema = spec.Definitions["notSomething"]
858	s, err = expandSchema(schema, []string{"#/definitions/notSomething"}, resolver, basePath)
859	schema = *s
860	assert.NoError(t, err)
861	assert.Empty(t, schema.Not.Items.Schema.Ref.String())
862	assert.Equal(t, *schema.Not.Items.Schema, spec.Definitions["tag"])
863
864	schema = spec.Definitions["withAdditional"]
865	s, err = expandSchema(schema, []string{"#/definitions/withAdditional"}, resolver, basePath)
866	schema = *s
867	assert.NoError(t, err)
868	assert.Empty(t, schema.AdditionalProperties.Schema.Items.Schema.Ref.String())
869	assert.Equal(t, *schema.AdditionalProperties.Schema.Items.Schema, spec.Definitions["tag"])
870
871	schema = spec.Definitions["withAdditionalItems"]
872	s, err = expandSchema(schema, []string{"#/definitions/withAdditionalItems"}, resolver, basePath)
873	schema = *s
874	assert.NoError(t, err)
875	assert.Empty(t, schema.AdditionalItems.Schema.Items.Schema.Ref.String())
876	assert.Equal(t, *schema.AdditionalItems.Schema.Items.Schema, spec.Definitions["tag"])
877
878	schema = spec.Definitions["withPattern"]
879	s, err = expandSchema(schema, []string{"#/definitions/withPattern"}, resolver, basePath)
880	schema = *s
881	assert.NoError(t, err)
882	prop := schema.PatternProperties["^x-ab"]
883	assert.Empty(t, prop.Items.Schema.Ref.String())
884	assert.Equal(t, *prop.Items.Schema, spec.Definitions["tag"])
885
886	schema = spec.Definitions["deps"]
887	s, err = expandSchema(schema, []string{"#/definitions/deps"}, resolver, basePath)
888	schema = *s
889	assert.NoError(t, err)
890	prop2 := schema.Dependencies["something"]
891	assert.Empty(t, prop2.Schema.Items.Schema.Ref.String())
892	assert.Equal(t, *prop2.Schema.Items.Schema, spec.Definitions["tag"])
893
894	schema = spec.Definitions["defined"]
895	s, err = expandSchema(schema, []string{"#/definitions/defined"}, resolver, basePath)
896	schema = *s
897	assert.NoError(t, err)
898	prop = schema.Definitions["something"]
899	assert.Empty(t, prop.Items.Schema.Ref.String())
900	assert.Equal(t, *prop.Items.Schema, spec.Definitions["tag"])
901}
902
903func TestSchemaExpansion(t *testing.T) {
904	carsDoc, err := jsonDoc("fixtures/expansion/schemas1.json")
905	assert.NoError(t, err)
906
907	basePath, _ := absPath("fixtures/expansion/schemas1.json")
908
909	spec := new(Swagger)
910	err = json.Unmarshal(carsDoc, spec)
911	assert.NoError(t, err)
912
913	resolver, err := defaultSchemaLoader(spec, nil, nil, nil)
914	assert.NoError(t, err)
915
916	schema := spec.Definitions["car"]
917	oldBrand := schema.Properties["brand"]
918	assert.NotEmpty(t, oldBrand.Ref.String())
919	assert.NotEqual(t, spec.Definitions["brand"], oldBrand)
920
921	s, err := expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
922	schema = *s
923	assert.NoError(t, err)
924
925	newBrand := schema.Properties["brand"]
926	assert.Empty(t, newBrand.Ref.String())
927	assert.Equal(t, spec.Definitions["brand"], newBrand)
928
929	schema = spec.Definitions["truck"]
930	assert.NotEmpty(t, schema.Ref.String())
931
932	s, err = expandSchema(schema, []string{"#/definitions/truck"}, resolver, basePath)
933	schema = *s
934	assert.NoError(t, err)
935	assert.Empty(t, schema.Ref.String())
936	assert.Equal(t, spec.Definitions["car"], schema)
937
938	sch := new(Schema)
939	_, err = expandSchema(*sch, []string{""}, resolver, basePath)
940	assert.NoError(t, err)
941
942	schema = spec.Definitions["batch"]
943	s, err = expandSchema(schema, []string{"#/definitions/batch"}, resolver, basePath)
944	schema = *s
945	assert.NoError(t, err)
946	assert.Empty(t, schema.Items.Schema.Ref.String())
947	assert.Equal(t, *schema.Items.Schema, spec.Definitions["brand"])
948
949	schema = spec.Definitions["batch2"]
950	s, err = expandSchema(schema, []string{"#/definitions/batch2"}, resolver, basePath)
951	schema = *s
952	assert.NoError(t, err)
953	assert.Empty(t, schema.Items.Schemas[0].Ref.String())
954	assert.Empty(t, schema.Items.Schemas[1].Ref.String())
955	assert.Equal(t, schema.Items.Schemas[0], spec.Definitions["brand"])
956	assert.Equal(t, schema.Items.Schemas[1], spec.Definitions["tag"])
957
958	schema = spec.Definitions["allofBoth"]
959	s, err = expandSchema(schema, []string{"#/definitions/allofBoth"}, resolver, basePath)
960	schema = *s
961	assert.NoError(t, err)
962	assert.Empty(t, schema.AllOf[0].Ref.String())
963	assert.Empty(t, schema.AllOf[1].Ref.String())
964	assert.Equal(t, schema.AllOf[0], spec.Definitions["brand"])
965	assert.Equal(t, schema.AllOf[1], spec.Definitions["tag"])
966
967	schema = spec.Definitions["anyofBoth"]
968	s, err = expandSchema(schema, []string{"#/definitions/anyofBoth"}, resolver, basePath)
969	schema = *s
970	assert.NoError(t, err)
971	assert.Empty(t, schema.AnyOf[0].Ref.String())
972	assert.Empty(t, schema.AnyOf[1].Ref.String())
973	assert.Equal(t, schema.AnyOf[0], spec.Definitions["brand"])
974	assert.Equal(t, schema.AnyOf[1], spec.Definitions["tag"])
975
976	schema = spec.Definitions["oneofBoth"]
977	s, err = expandSchema(schema, []string{"#/definitions/oneofBoth"}, resolver, basePath)
978	schema = *s
979	assert.NoError(t, err)
980	assert.Empty(t, schema.OneOf[0].Ref.String())
981	assert.Empty(t, schema.OneOf[1].Ref.String())
982	assert.Equal(t, schema.OneOf[0], spec.Definitions["brand"])
983	assert.Equal(t, schema.OneOf[1], spec.Definitions["tag"])
984
985	schema = spec.Definitions["notSomething"]
986	s, err = expandSchema(schema, []string{"#/definitions/notSomething"}, resolver, basePath)
987	schema = *s
988	assert.NoError(t, err)
989	assert.Empty(t, schema.Not.Ref.String())
990	assert.Equal(t, *schema.Not, spec.Definitions["tag"])
991
992	schema = spec.Definitions["withAdditional"]
993	s, err = expandSchema(schema, []string{"#/definitions/withAdditional"}, resolver, basePath)
994	schema = *s
995	assert.NoError(t, err)
996	assert.Empty(t, schema.AdditionalProperties.Schema.Ref.String())
997	assert.Equal(t, *schema.AdditionalProperties.Schema, spec.Definitions["tag"])
998
999	schema = spec.Definitions["withAdditionalItems"]
1000	s, err = expandSchema(schema, []string{"#/definitions/withAdditionalItems"}, resolver, basePath)
1001	schema = *s
1002	assert.NoError(t, err)
1003	assert.Empty(t, schema.AdditionalItems.Schema.Ref.String())
1004	assert.Equal(t, *schema.AdditionalItems.Schema, spec.Definitions["tag"])
1005
1006	schema = spec.Definitions["withPattern"]
1007	s, err = expandSchema(schema, []string{"#/definitions/withPattern"}, resolver, basePath)
1008	schema = *s
1009	assert.NoError(t, err)
1010	prop := schema.PatternProperties["^x-ab"]
1011	assert.Empty(t, prop.Ref.String())
1012	assert.Equal(t, prop, spec.Definitions["tag"])
1013
1014	schema = spec.Definitions["deps"]
1015	s, err = expandSchema(schema, []string{"#/definitions/deps"}, resolver, basePath)
1016	schema = *s
1017	assert.NoError(t, err)
1018	prop2 := schema.Dependencies["something"]
1019	assert.Empty(t, prop2.Schema.Ref.String())
1020	assert.Equal(t, *prop2.Schema, spec.Definitions["tag"])
1021
1022	schema = spec.Definitions["defined"]
1023	s, err = expandSchema(schema, []string{"#/definitions/defined"}, resolver, basePath)
1024	schema = *s
1025	assert.NoError(t, err)
1026	prop = schema.Definitions["something"]
1027	assert.Empty(t, prop.Ref.String())
1028	assert.Equal(t, prop, spec.Definitions["tag"])
1029
1030}
1031
1032func TestDefaultResolutionCache(t *testing.T) {
1033
1034	cache := initResolutionCache()
1035
1036	sch, ok := cache.Get("not there")
1037	assert.False(t, ok)
1038	assert.Nil(t, sch)
1039
1040	sch, ok = cache.Get("http://swagger.io/v2/schema.json")
1041	assert.True(t, ok)
1042	assert.Equal(t, swaggerSchema, sch)
1043
1044	sch, ok = cache.Get("http://json-schema.org/draft-04/schema")
1045	assert.True(t, ok)
1046	assert.Equal(t, jsonSchema, sch)
1047
1048	cache.Set("something", "here")
1049	sch, ok = cache.Get("something")
1050	assert.True(t, ok)
1051	assert.Equal(t, "here", sch)
1052}
1053
1054func TestRelativeBaseURI(t *testing.T) {
1055	server := httptest.NewServer(http.FileServer(http.Dir("fixtures/remote")))
1056	defer server.Close()
1057
1058	spec := new(Swagger)
1059	// resolver, err := defaultSchemaLoader(spec, nil, nil,nil)
1060	// assert.NoError(t, err)
1061
1062	err := ExpandSpec(spec, nil)
1063	assert.NoError(t, err)
1064
1065	specDoc, err := jsonDoc("fixtures/remote/all-the-things.json")
1066	assert.NoError(t, err)
1067
1068	opts := &ExpandOptions{
1069		RelativeBase: server.URL + "/all-the-things.json",
1070	}
1071
1072	spec = new(Swagger)
1073	err = json.Unmarshal(specDoc, spec)
1074	assert.NoError(t, err)
1075
1076	pet := spec.Definitions["pet"]
1077	errorModel := spec.Definitions["errorModel"]
1078	petResponse := spec.Responses["petResponse"]
1079	petResponse.Schema = &pet
1080	stringResponse := spec.Responses["stringResponse"]
1081	tagParam := spec.Parameters["tag"]
1082	idParam := spec.Parameters["idParam"]
1083
1084	anotherPet := spec.Responses["anotherPet"]
1085	anotherPet.Ref = MustCreateRef(server.URL + "/" + anotherPet.Ref.String())
1086	err = ExpandResponse(&anotherPet, opts.RelativeBase)
1087	assert.NoError(t, err)
1088	spec.Responses["anotherPet"] = anotherPet
1089
1090	circularA := spec.Responses["circularA"]
1091	circularA.Ref = MustCreateRef(server.URL + "/" + circularA.Ref.String())
1092	err = ExpandResponse(&circularA, opts.RelativeBase)
1093	assert.NoError(t, err)
1094	spec.Responses["circularA"] = circularA
1095
1096	err = ExpandSpec(spec, opts)
1097	assert.NoError(t, err)
1098
1099	assert.Equal(t, tagParam, spec.Parameters["query"])
1100	assert.Equal(t, petResponse, spec.Responses["petResponse"])
1101	// ICI
1102	assert.Equal(t, petResponse, spec.Responses["anotherPet"])
1103	// ICI
1104	assert.Equal(t, pet, *spec.Responses["petResponse"].Schema)
1105	assert.Equal(t, stringResponse, *spec.Paths.Paths["/"].Get.Responses.Default)
1106	assert.Equal(t, petResponse, spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200])
1107	assert.Equal(t, pet, *spec.Paths.Paths["/pets"].Get.Responses.StatusCodeResponses[200].Schema.Items.Schema)
1108	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Get.Responses.Default.Schema)
1109	assert.Equal(t, pet, spec.Definitions["petInput"].AllOf[0])
1110	assert.Equal(t, spec.Definitions["petInput"], *spec.Paths.Paths["/pets"].Post.Parameters[0].Schema)
1111	assert.Equal(t, petResponse, spec.Paths.Paths["/pets"].Post.Responses.StatusCodeResponses[200])
1112	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Post.Responses.Default.Schema)
1113	pi := spec.Paths.Paths["/pets/{id}"]
1114	assert.Equal(t, idParam, pi.Get.Parameters[0])
1115	assert.Equal(t, petResponse, pi.Get.Responses.StatusCodeResponses[200])
1116	assert.Equal(t, errorModel, *pi.Get.Responses.Default.Schema)
1117	assert.Equal(t, idParam, pi.Delete.Parameters[0])
1118	assert.Equal(t, errorModel, *pi.Delete.Responses.Default.Schema)
1119}
1120
1121func resolutionContextServer() *httptest.Server {
1122	var servedAt string
1123	server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
1124		// fmt.Println("got a request for", req.URL.String())
1125		if req.URL.Path == "/resolution.json" {
1126
1127			b, _ := ioutil.ReadFile(filepath.Join(specs, "resolution.json"))
1128			var ctnt map[string]interface{}
1129			_ = json.Unmarshal(b, &ctnt)
1130			ctnt["id"] = servedAt
1131
1132			rw.Header().Set("Content-Type", "application/json")
1133			rw.WriteHeader(200)
1134			bb, _ := json.Marshal(ctnt)
1135			_, _ = rw.Write(bb)
1136			return
1137		}
1138		if req.URL.Path == "/resolution2.json" {
1139			b, _ := ioutil.ReadFile(filepath.Join(specs, "resolution2.json"))
1140			var ctnt map[string]interface{}
1141			_ = json.Unmarshal(b, &ctnt)
1142			ctnt["id"] = servedAt
1143
1144			rw.Header().Set("Content-Type", "application/json")
1145			rw.WriteHeader(200)
1146			bb, _ := json.Marshal(ctnt)
1147			_, _ = rw.Write(bb)
1148			return
1149		}
1150
1151		if req.URL.Path == "/boolProp.json" {
1152			rw.Header().Set("Content-Type", "application/json")
1153			rw.WriteHeader(200)
1154			b, _ := json.Marshal(map[string]interface{}{
1155				"type": "boolean",
1156			})
1157			_, _ = rw.Write(b)
1158			return
1159		}
1160
1161		if req.URL.Path == "/deeper/stringProp.json" {
1162			rw.Header().Set("Content-Type", "application/json")
1163			rw.WriteHeader(200)
1164			b, _ := json.Marshal(map[string]interface{}{
1165				"type": "string",
1166			})
1167			_, _ = rw.Write(b)
1168			return
1169		}
1170
1171		if req.URL.Path == "/deeper/arrayProp.json" {
1172			rw.Header().Set("Content-Type", "application/json")
1173			rw.WriteHeader(200)
1174			b, _ := json.Marshal(map[string]interface{}{
1175				"type": "array",
1176				"items": map[string]interface{}{
1177					"type": "file",
1178				},
1179			})
1180			_, _ = rw.Write(b)
1181			return
1182		}
1183
1184		rw.WriteHeader(http.StatusNotFound)
1185	}))
1186	servedAt = server.URL
1187	return server
1188}
1189
1190func TestResolveRemoteRef_RootSame(t *testing.T) {
1191	fileserver := http.FileServer(http.Dir(specs))
1192	server := httptest.NewServer(fileserver)
1193	defer server.Close()
1194
1195	rootDoc := new(Swagger)
1196	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1197	// the filename doesn't matter because ref will eventually point to refed.json
1198	specBase, _ := absPath(filepath.Join(specs, "anyotherfile.json"))
1199	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1200		var result0 Swagger
1201		ref0, _ := NewRef(server.URL + "/refed.json#")
1202		resolver0, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1203		if assert.NoError(t, resolver0.Resolve(&ref0, &result0, "")) {
1204			assertSpecs(t, result0, *rootDoc)
1205		}
1206
1207		var result1 Swagger
1208		ref1, _ := NewRef("./refed.json")
1209		resolver1, _ := defaultSchemaLoader(rootDoc, &ExpandOptions{
1210			RelativeBase: specBase,
1211		}, nil, nil)
1212		if assert.NoError(t, resolver1.Resolve(&ref1, &result1, specBase)) {
1213			assertSpecs(t, result1, *rootDoc)
1214		}
1215	}
1216}
1217
1218func TestResolveRemoteRef_FromFragment(t *testing.T) {
1219	fileserver := http.FileServer(http.Dir(specs))
1220	server := httptest.NewServer(fileserver)
1221	defer server.Close()
1222
1223	rootDoc := new(Swagger)
1224	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1225
1226	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1227		var tgt Schema
1228		ref, err := NewRef(server.URL + "/refed.json#/definitions/pet")
1229		if assert.NoError(t, err) {
1230			resolver := &schemaLoader{root: rootDoc, cache: initResolutionCache(), loadDoc: jsonDoc}
1231			if assert.NoError(t, resolver.Resolve(&ref, &tgt, "")) {
1232				assert.Equal(t, []string{"id", "name"}, tgt.Required)
1233			}
1234		}
1235	}
1236}
1237
1238func TestResolveRemoteRef_FromInvalidFragment(t *testing.T) {
1239	fileserver := http.FileServer(http.Dir(specs))
1240	server := httptest.NewServer(fileserver)
1241	defer server.Close()
1242
1243	rootDoc := new(Swagger)
1244	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1245	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1246		var tgt Schema
1247		ref, err := NewRef(server.URL + "/refed.json#/definitions/NotThere")
1248		if assert.NoError(t, err) {
1249			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1250			assert.Error(t, resolver.Resolve(&ref, &tgt, ""))
1251		}
1252	}
1253}
1254
1255func TestResolveRemoteRef_WithResolutionContext(t *testing.T) {
1256	server := resolutionContextServer()
1257	defer server.Close()
1258
1259	var tgt Schema
1260	ref, err := NewRef(server.URL + "/resolution.json#/definitions/bool")
1261	if assert.NoError(t, err) {
1262		tgt.Ref = ref
1263		ere := ExpandSchema(&tgt, nil, nil)
1264		assert.NoError(t, ere)
1265		assert.Equal(t, StringOrArray([]string{"boolean"}), tgt.Type)
1266	}
1267
1268}
1269
1270func TestResolveRemoteRef_WithNestedResolutionContext(t *testing.T) {
1271	server := resolutionContextServer()
1272	defer server.Close()
1273
1274	var tgt Schema
1275	ref, err := NewRef(server.URL + "/resolution.json#/items")
1276	if assert.NoError(t, err) {
1277		tgt.Ref = ref
1278		ere := ExpandSchema(&tgt, nil, nil)
1279		assert.NoError(t, ere)
1280		assert.Equal(t, StringOrArray([]string{"string"}), tgt.Items.Schema.Type)
1281	}
1282}
1283
1284/* This next test will have to wait until we do full $ID analysis for every subschema on every file that is referenced */
1285/* For now, TestResolveRemoteRef_WithNestedResolutionContext replaces this next test */
1286// func TestResolveRemoteRef_WithNestedResolutionContext_WithParentID(t *testing.T) {
1287// 	server := resolutionContextServer()
1288// 	defer server.Close()
1289
1290// 	var tgt Schema
1291// 	ref, err := NewRef(server.URL + "/resolution.json#/items/items")
1292// 	if assert.NoError(t, err) {
1293// 		tgt.Ref = ref
1294// 		ExpandSchema(&tgt, nil, nil)
1295// 		assert.Equal(t, StringOrArray([]string{"string"}), tgt.Type)
1296// 	}
1297// }
1298
1299func TestResolveRemoteRef_WithNestedResolutionContextWithFragment(t *testing.T) {
1300	server := resolutionContextServer()
1301	defer server.Close()
1302
1303	var tgt Schema
1304	ref, err := NewRef(server.URL + "/resolution2.json#/items")
1305	if assert.NoError(t, err) {
1306		tgt.Ref = ref
1307		ere := ExpandSchema(&tgt, nil, nil)
1308		assert.NoError(t, ere)
1309		assert.Equal(t, StringOrArray([]string{"file"}), tgt.Items.Schema.Type)
1310	}
1311
1312}
1313
1314/* This next test will have to wait until we do full $ID analysis for every subschema on every file that is referenced */
1315/* For now, TestResolveRemoteRef_WithNestedResolutionContext replaces this next test */
1316// func TestResolveRemoteRef_WithNestedResolutionContextWithFragment_WithParentID(t *testing.T) {
1317// 	server := resolutionContextServer()
1318// 	defer server.Close()
1319
1320// 	rootDoc := new(Swagger)
1321// 	b, err := ioutil.ReadFile("fixtures/specs/refed.json")
1322// 	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1323// 		var tgt Schema
1324// 		ref, err := NewRef(server.URL + "/resolution2.json#/items/items")
1325// 		if assert.NoError(t, err) {
1326// 			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil,nil)
1327// 			if assert.NoError(t, resolver.Resolve(&ref, &tgt, "")) {
1328// 				assert.Equal(t, StringOrArray([]string{"file"}), tgt.Type)
1329// 			}
1330// 		}
1331// 	}
1332// }
1333
1334func TestResolveRemoteRef_ToParameter(t *testing.T) {
1335	fileserver := http.FileServer(http.Dir(specs))
1336	server := httptest.NewServer(fileserver)
1337	defer server.Close()
1338
1339	rootDoc := new(Swagger)
1340	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1341	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1342		var tgt Parameter
1343		ref, err := NewRef(server.URL + "/refed.json#/parameters/idParam")
1344		if assert.NoError(t, err) {
1345
1346			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1347			if assert.NoError(t, resolver.Resolve(&ref, &tgt, "")) {
1348				assert.Equal(t, "id", tgt.Name)
1349				assert.Equal(t, "path", tgt.In)
1350				assert.Equal(t, "ID of pet to fetch", tgt.Description)
1351				assert.True(t, tgt.Required)
1352				assert.Equal(t, "integer", tgt.Type)
1353				assert.Equal(t, "int64", tgt.Format)
1354			}
1355		}
1356	}
1357}
1358
1359func TestResolveRemoteRef_ToPathItem(t *testing.T) {
1360	fileserver := http.FileServer(http.Dir(specs))
1361	server := httptest.NewServer(fileserver)
1362	defer server.Close()
1363
1364	rootDoc := new(Swagger)
1365	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1366	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1367		var tgt PathItem
1368		ref, err := NewRef(server.URL + "/refed.json#/paths/" + jsonpointer.Escape("/pets/{id}"))
1369		if assert.NoError(t, err) {
1370
1371			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1372			if assert.NoError(t, resolver.Resolve(&ref, &tgt, "")) {
1373				assert.Equal(t, rootDoc.Paths.Paths["/pets/{id}"].Get, tgt.Get)
1374			}
1375		}
1376	}
1377}
1378
1379func TestResolveRemoteRef_ToResponse(t *testing.T) {
1380	fileserver := http.FileServer(http.Dir(specs))
1381	server := httptest.NewServer(fileserver)
1382	defer server.Close()
1383
1384	rootDoc := new(Swagger)
1385	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1386	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1387		var tgt Response
1388		ref, err := NewRef(server.URL + "/refed.json#/responses/petResponse")
1389		if assert.NoError(t, err) {
1390
1391			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1392			if assert.NoError(t, resolver.Resolve(&ref, &tgt, "")) {
1393				assert.Equal(t, rootDoc.Responses["petResponse"], tgt)
1394			}
1395		}
1396	}
1397}
1398
1399func TestResolveLocalRef_SameRoot(t *testing.T) {
1400	rootDoc := new(Swagger)
1401	_ = json.Unmarshal(PetStoreJSONMessage, rootDoc)
1402
1403	result := new(Swagger)
1404	ref, _ := NewRef("#")
1405	resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1406	err := resolver.Resolve(&ref, result, "")
1407	if assert.NoError(t, err) {
1408		assert.Equal(t, rootDoc, result)
1409	}
1410}
1411
1412func TestResolveLocalRef_FromFragment(t *testing.T) {
1413	rootDoc := new(Swagger)
1414	_ = json.Unmarshal(PetStoreJSONMessage, rootDoc)
1415
1416	var tgt Schema
1417	ref, err := NewRef("#/definitions/Category")
1418	if assert.NoError(t, err) {
1419		resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1420		err := resolver.Resolve(&ref, &tgt, "")
1421		if assert.NoError(t, err) {
1422			assert.Equal(t, "Category", tgt.ID)
1423		}
1424	}
1425}
1426
1427func TestResolveLocalRef_FromInvalidFragment(t *testing.T) {
1428	rootDoc := new(Swagger)
1429	_ = json.Unmarshal(PetStoreJSONMessage, rootDoc)
1430
1431	var tgt Schema
1432	ref, err := NewRef("#/definitions/NotThere")
1433	if assert.NoError(t, err) {
1434		resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1435		err := resolver.Resolve(&ref, &tgt, "")
1436		assert.Error(t, err)
1437	}
1438}
1439
1440func TestResolveLocalRef_Parameter(t *testing.T) {
1441	rootDoc := new(Swagger)
1442	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1443	basePath, _ := absPath(filepath.Join(specs, "refed.json"))
1444	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1445		var tgt Parameter
1446		ref, err := NewRef("#/parameters/idParam")
1447		if assert.NoError(t, err) {
1448			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1449			if assert.NoError(t, resolver.Resolve(&ref, &tgt, basePath)) {
1450				assert.Equal(t, "id", tgt.Name)
1451				assert.Equal(t, "path", tgt.In)
1452				assert.Equal(t, "ID of pet to fetch", tgt.Description)
1453				assert.True(t, tgt.Required)
1454				assert.Equal(t, "integer", tgt.Type)
1455				assert.Equal(t, "int64", tgt.Format)
1456			}
1457		}
1458	}
1459}
1460
1461func TestResolveLocalRef_PathItem(t *testing.T) {
1462	rootDoc := new(Swagger)
1463	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1464	basePath, _ := absPath(filepath.Join(specs, "refed.json"))
1465	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1466		var tgt PathItem
1467		ref, err := NewRef("#/paths/" + jsonpointer.Escape("/pets/{id}"))
1468		if assert.NoError(t, err) {
1469			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1470			if assert.NoError(t, resolver.Resolve(&ref, &tgt, basePath)) {
1471				assert.Equal(t, rootDoc.Paths.Paths["/pets/{id}"].Get, tgt.Get)
1472			}
1473		}
1474	}
1475}
1476
1477func TestResolveLocalRef_Response(t *testing.T) {
1478	rootDoc := new(Swagger)
1479	b, err := ioutil.ReadFile(filepath.Join(specs, "refed.json"))
1480	basePath, _ := absPath(filepath.Join(specs, "refed.json"))
1481	if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) {
1482		var tgt Response
1483		ref, err := NewRef("#/responses/petResponse")
1484		if assert.NoError(t, err) {
1485			resolver, _ := defaultSchemaLoader(rootDoc, nil, nil, nil)
1486			if assert.NoError(t, resolver.Resolve(&ref, &tgt, basePath)) {
1487				assert.Equal(t, rootDoc.Responses["petResponse"], tgt)
1488			}
1489		}
1490	}
1491}
1492
1493func TestResolveForTransitiveRefs(t *testing.T) {
1494	var spec *Swagger
1495	rawSpec, err := ioutil.ReadFile(filepath.Join(specs, "todos.json"))
1496	assert.NoError(t, err)
1497
1498	basePath, err := absPath(filepath.Join(specs, "todos.json"))
1499	assert.NoError(t, err)
1500
1501	opts := &ExpandOptions{
1502		RelativeBase: basePath,
1503	}
1504
1505	err = json.Unmarshal(rawSpec, &spec)
1506	assert.NoError(t, err)
1507
1508	err = ExpandSpec(spec, opts)
1509	assert.NoError(t, err)
1510}
1511
1512const (
1513	withoutSchemaID = "removed"
1514	withSchemaID    = "schema"
1515)
1516
1517func TestExpandSchemaWithRoot(t *testing.T) {
1518	root := new(Swagger)
1519	_ = json.Unmarshal(PetStoreJSONMessage, root)
1520
1521	// 1. remove ID from root definition
1522	origPet := root.Definitions["Pet"]
1523	newPet := origPet
1524	newPet.ID = ""
1525	root.Definitions["Pet"] = newPet
1526	expandRootWithID(t, root, withoutSchemaID)
1527
1528	// 2. put back ID in Pet definition
1529	// nested $ref should fail
1530	//Debug = true
1531	root.Definitions["Pet"] = origPet
1532	expandRootWithID(t, root, withSchemaID)
1533}
1534
1535func expandRootWithID(t *testing.T, root *Swagger, testcase string) {
1536	t.Logf("case: expanding $ref to schema without ID, with nested $ref with %s ID", testcase)
1537	sch := &Schema{
1538		SchemaProps: SchemaProps{
1539			Ref: MustCreateRef("#/definitions/newPet"),
1540		},
1541	}
1542	err := ExpandSchema(sch, root, nil)
1543	if testcase == withSchemaID {
1544		assert.Errorf(t, err, "expected %s NOT to expand properly because of the ID in the parent schema", sch.Ref.String())
1545	} else {
1546		assert.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
1547	}
1548	if Debug {
1549		bbb, _ := json.MarshalIndent(sch, "", " ")
1550		t.Log(string(bbb))
1551	}
1552
1553	t.Log("case: expanding $ref to schema without nested $ref")
1554	sch = &Schema{
1555		SchemaProps: SchemaProps{
1556			Ref: MustCreateRef("#/definitions/Category"),
1557		},
1558	}
1559	err = ExpandSchema(sch, root, nil)
1560	assert.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
1561	if Debug {
1562		bbb, _ := json.MarshalIndent(sch, "", " ")
1563		t.Log(string(bbb))
1564	}
1565	t.Logf("case: expanding $ref to schema with %s ID and nested $ref", testcase)
1566	sch = &Schema{
1567		SchemaProps: SchemaProps{
1568			Ref: MustCreateRef("#/definitions/Pet"),
1569		},
1570	}
1571	err = ExpandSchema(sch, root, nil)
1572	if testcase == withSchemaID {
1573		assert.Errorf(t, err, "expected %s NOT to expand properly because of the ID in the parent schema", sch.Ref.String())
1574	} else {
1575		assert.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
1576	}
1577	if Debug {
1578		bbb, _ := json.MarshalIndent(sch, "", " ")
1579		t.Log(string(bbb))
1580	}
1581}
1582
1583const pathItemsFixture = "fixtures/expansion/pathItems.json"
1584
1585func TestExpandPathItem(t *testing.T) {
1586	spec := new(Swagger)
1587	specDoc, err := jsonDoc(pathItemsFixture)
1588	assert.NoError(t, err)
1589	_ = json.Unmarshal(specDoc, spec)
1590	specPath, _ := absPath(pathItemsFixture)
1591
1592	// ExpandSpec use case
1593	err = ExpandSpec(spec, &ExpandOptions{RelativeBase: specPath})
1594	if !assert.NoError(t, err) {
1595		t.FailNow()
1596	}
1597	jazon, _ := json.MarshalIndent(spec, "", " ")
1598	assert.JSONEq(t, `{
1599         "swagger": "2.0",
1600         "info": {
1601          "title": "PathItems refs",
1602          "version": "1.0"
1603         },
1604         "paths": {
1605          "/todos": {
1606           "get": {
1607            "responses": {
1608             "200": {
1609              "description": "List Todos",
1610              "schema": {
1611               "type": "array",
1612               "items": {
1613                "type": "string"
1614               }
1615              }
1616             },
1617             "404": {
1618              "description": "error"
1619             }
1620            }
1621           }
1622          }
1623         }
1624			 }`, string(jazon))
1625}
1626
1627func TestResolvePathItem(t *testing.T) {
1628	spec := new(Swagger)
1629	specDoc, err := jsonDoc(pathItemsFixture)
1630	assert.NoError(t, err)
1631	_ = json.Unmarshal(specDoc, spec)
1632	specPath, _ := absPath(pathItemsFixture)
1633
1634	// Resolve use case
1635	pth := spec.Paths.Paths["/todos"]
1636	pathItem, err := ResolvePathItem(spec, pth.Ref, &ExpandOptions{RelativeBase: specPath})
1637	assert.NoError(t, err)
1638	jazon, _ := json.MarshalIndent(pathItem, "", " ")
1639	assert.JSONEq(t, `{
1640         "get": {
1641          "responses": {
1642           "200": {
1643            "description": "List Todos",
1644            "schema": {
1645             "type": "array",
1646             "items": {
1647              "type": "string"
1648             }
1649            }
1650           },
1651           "404": {
1652            "description": "error"
1653           }
1654          }
1655         }
1656			 }`, string(jazon))
1657}
1658
1659const extraRefFixture = "fixtures/expansion/extraRef.json"
1660
1661func TestExpandExtraItems(t *testing.T) {
1662	spec := new(Swagger)
1663	specDoc, err := jsonDoc(extraRefFixture)
1664	assert.NoError(t, err)
1665	_ = json.Unmarshal(specDoc, spec)
1666	specPath, _ := absPath(extraRefFixture)
1667
1668	// ExpandSpec use case: unsupported $refs are not expanded
1669	err = ExpandSpec(spec, &ExpandOptions{RelativeBase: specPath})
1670	if !assert.NoError(t, err) {
1671		t.FailNow()
1672	}
1673	jazon, _ := json.MarshalIndent(spec, "", " ")
1674	assert.JSONEq(t, `{
1675         "schemes": [
1676          "http"
1677         ],
1678         "swagger": "2.0",
1679         "info": {
1680          "title": "Supported, but non Swagger 20 compliant $ref constructs",
1681          "version": "2.1.0"
1682         },
1683         "host": "item.com",
1684         "basePath": "/extraRefs",
1685         "paths": {
1686          "/employees": {
1687           "get": {
1688            "summary": "List Employee Types",
1689            "operationId": "LIST-Employees",
1690            "parameters": [
1691             {
1692							"description": "unsupported $ref in simple param",
1693              "type": "array",
1694              "items": {
1695               "$ref": "#/definitions/arrayType"
1696              },
1697              "name": "myQueryParam",
1698              "in": "query"
1699             }
1700            ],
1701            "responses": {
1702             "200": {
1703							"description": "unsupported $ref in header",
1704              "schema": {
1705               "type": "string"
1706              },
1707              "headers": {
1708               "X-header": {
1709                  "type": "array",
1710                  "items": {
1711                    "$ref": "#/definitions/headerType"
1712                  }
1713							  }
1714              }
1715             }
1716            }
1717           }
1718          }
1719         },
1720         "definitions": {
1721          "arrayType": {
1722           "type": "integer",
1723           "format": "int32"
1724          },
1725          "headerType": {
1726           "type": "string",
1727           "format": "uuid"
1728          }
1729         }
1730			 }`, string(jazon))
1731}
1732
1733func TestResolveExtraItem(t *testing.T) {
1734	// go-openapi extra goodie: $ref in simple schema Items and Headers
1735	spec := new(Swagger)
1736	specDoc, err := jsonDoc(extraRefFixture)
1737	assert.NoError(t, err)
1738	_ = json.Unmarshal(specDoc, spec)
1739	specPath, _ := absPath(extraRefFixture)
1740
1741	// Resolve param Items use case: here we explicitly resolve the unsuppord case
1742	parm := spec.Paths.Paths["/employees"].Get.Parameters[0]
1743	parmItem, err := ResolveItems(spec, parm.Items.Ref, &ExpandOptions{RelativeBase: specPath})
1744	assert.NoError(t, err)
1745	jazon, _ := json.MarshalIndent(parmItem, "", " ")
1746	assert.JSONEq(t, `{
1747         "type": "integer",
1748         "format": "int32"
1749			 }`, string(jazon))
1750
1751	// Resolve header Items use case: here we explicitly resolve the unsuppord case
1752	hdr := spec.Paths.Paths["/employees"].Get.Responses.StatusCodeResponses[200].Headers["X-header"]
1753	hdrItem, err := ResolveItems(spec, hdr.Items.Ref, &ExpandOptions{RelativeBase: specPath})
1754	assert.NoError(t, err)
1755	jazon, _ = json.MarshalIndent(hdrItem, "", " ")
1756	assert.JSONEq(t, `{
1757         "type": "string",
1758         "format": "uuid"
1759			 }`, string(jazon))
1760}
1761
1762// PetStoreJSONMessage json raw message for Petstore20
1763var PetStoreJSONMessage = json.RawMessage([]byte(PetStore20))
1764
1765// PetStore20 json doc for swagger 2.0 pet store
1766const PetStore20 = `{
1767  "swagger": "2.0",
1768  "info": {
1769    "version": "1.0.0",
1770    "title": "Swagger Petstore",
1771    "contact": {
1772      "name": "Wordnik API Team",
1773      "url": "http://developer.wordnik.com"
1774    },
1775    "license": {
1776      "name": "Creative Commons 4.0 International",
1777      "url": "http://creativecommons.org/licenses/by/4.0/"
1778    }
1779  },
1780  "host": "petstore.swagger.wordnik.com",
1781  "basePath": "/api",
1782  "schemes": [
1783    "http"
1784  ],
1785  "paths": {
1786    "/pets": {
1787      "get": {
1788        "security": [
1789          {
1790            "basic": []
1791          }
1792        ],
1793        "tags": [ "Pet Operations" ],
1794        "operationId": "getAllPets",
1795        "parameters": [
1796          {
1797            "name": "status",
1798            "in": "query",
1799            "description": "The status to filter by",
1800            "type": "string"
1801          },
1802          {
1803            "name": "limit",
1804            "in": "query",
1805            "description": "The maximum number of results to return",
1806            "type": "integer",
1807						"format": "int64"
1808          }
1809        ],
1810        "summary": "Finds all pets in the system",
1811        "responses": {
1812          "200": {
1813            "description": "Pet response",
1814            "schema": {
1815              "type": "array",
1816              "items": {
1817                "$ref": "#/definitions/Pet"
1818              }
1819            }
1820          },
1821          "default": {
1822            "description": "Unexpected error",
1823            "schema": {
1824              "$ref": "#/definitions/Error"
1825            }
1826          }
1827        }
1828      },
1829      "post": {
1830        "security": [
1831          {
1832            "basic": []
1833          }
1834        ],
1835        "tags": [ "Pet Operations" ],
1836        "operationId": "createPet",
1837        "summary": "Creates a new pet",
1838        "consumes": ["application/x-yaml"],
1839        "produces": ["application/x-yaml"],
1840        "parameters": [
1841          {
1842            "name": "pet",
1843            "in": "body",
1844            "description": "The Pet to create",
1845            "required": true,
1846            "schema": {
1847              "$ref": "#/definitions/newPet"
1848            }
1849          }
1850        ],
1851        "responses": {
1852          "200": {
1853            "description": "Created Pet response",
1854            "schema": {
1855              "$ref": "#/definitions/Pet"
1856            }
1857          },
1858          "default": {
1859            "description": "Unexpected error",
1860            "schema": {
1861              "$ref": "#/definitions/Error"
1862            }
1863          }
1864        }
1865      }
1866    },
1867    "/pets/{id}": {
1868      "delete": {
1869        "security": [
1870          {
1871            "apiKey": []
1872          }
1873        ],
1874        "description": "Deletes the Pet by id",
1875        "operationId": "deletePet",
1876        "parameters": [
1877          {
1878            "name": "id",
1879            "in": "path",
1880            "description": "ID of pet to delete",
1881            "required": true,
1882            "type": "integer",
1883            "format": "int64"
1884          }
1885        ],
1886        "responses": {
1887          "204": {
1888            "description": "pet deleted"
1889          },
1890          "default": {
1891            "description": "unexpected error",
1892            "schema": {
1893              "$ref": "#/definitions/Error"
1894            }
1895          }
1896        }
1897      },
1898      "get": {
1899        "tags": [ "Pet Operations" ],
1900        "operationId": "getPetById",
1901        "summary": "Finds the pet by id",
1902        "responses": {
1903          "200": {
1904            "description": "Pet response",
1905            "schema": {
1906              "$ref": "#/definitions/Pet"
1907            }
1908          },
1909          "default": {
1910            "description": "Unexpected error",
1911            "schema": {
1912              "$ref": "#/definitions/Error"
1913            }
1914          }
1915        }
1916      },
1917      "parameters": [
1918        {
1919          "name": "id",
1920          "in": "path",
1921          "description": "ID of pet",
1922          "required": true,
1923          "type": "integer",
1924          "format": "int64"
1925        }
1926      ]
1927    }
1928  },
1929  "definitions": {
1930    "Category": {
1931      "id": "Category",
1932      "properties": {
1933        "id": {
1934          "format": "int64",
1935          "type": "integer"
1936        },
1937        "name": {
1938          "type": "string"
1939        }
1940      }
1941    },
1942    "Pet": {
1943      "id": "Pet",
1944      "properties": {
1945        "category": {
1946          "$ref": "#/definitions/Category"
1947        },
1948        "id": {
1949          "description": "unique identifier for the pet",
1950          "format": "int64",
1951          "maximum": 100.0,
1952          "minimum": 0.0,
1953          "type": "integer"
1954        },
1955        "name": {
1956          "type": "string"
1957        },
1958        "photoUrls": {
1959          "items": {
1960            "type": "string"
1961          },
1962          "type": "array"
1963        },
1964        "status": {
1965          "description": "pet status in the store",
1966          "enum": [
1967            "available",
1968            "pending",
1969            "sold"
1970          ],
1971          "type": "string"
1972        },
1973        "tags": {
1974          "items": {
1975            "$ref": "#/definitions/Tag"
1976          },
1977          "type": "array"
1978        }
1979      },
1980      "required": [
1981        "id",
1982        "name"
1983      ]
1984    },
1985    "newPet": {
1986      "anyOf": [
1987        {
1988          "$ref": "#/definitions/Pet"
1989        },
1990        {
1991          "required": [
1992            "name"
1993          ]
1994        }
1995      ]
1996    },
1997    "Tag": {
1998      "id": "Tag",
1999      "properties": {
2000        "id": {
2001          "format": "int64",
2002          "type": "integer"
2003        },
2004        "name": {
2005          "type": "string"
2006        }
2007      }
2008    },
2009    "Error": {
2010      "required": [
2011        "code",
2012        "message"
2013      ],
2014      "properties": {
2015        "code": {
2016          "type": "integer",
2017          "format": "int32"
2018        },
2019        "message": {
2020          "type": "string"
2021        }
2022      }
2023    }
2024  },
2025  "consumes": [
2026    "application/json",
2027    "application/xml"
2028  ],
2029  "produces": [
2030    "application/json",
2031    "application/xml",
2032    "text/plain",
2033    "text/html"
2034  ],
2035  "securityDefinitions": {
2036    "basic": {
2037      "type": "basic"
2038    },
2039    "apiKey": {
2040      "type": "apiKey",
2041      "in": "header",
2042      "name": "X-API-KEY"
2043    }
2044  }
2045}
2046`
2047