func (s *specAnalyzer) initialize() { for _, c := range s.spec.Consumes { s.consumes[c] = struct{}{} } for _, c := range s.spec.Produces { s.produces[c] = struct{}{} } for _, ss := range s.spec.Security { for k := range ss { s.authSchemes[k] = struct{}{} } } for path, pathItem := range s.AllPaths() { s.analyzeOperations(path, &pathItem) } for name, parameter := range s.spec.Parameters { if parameter.In == "body" && parameter.Schema != nil { s.analyzeSchema("schema", *parameter.Schema, slashpath.Join("/parameters", jsonpointer.Escape(name))) } } for name, response := range s.spec.Responses { if response.Schema != nil { s.analyzeSchema("schema", *response.Schema, slashpath.Join("/responses", jsonpointer.Escape(name))) } } for name, schema := range s.spec.Definitions { s.analyzeSchema(name, schema, "/definitions") } // TODO: after analyzing all things and flattening shemas etc // resolve all the collected references to their final representations // best put in a separate method because this could get expensive }
func (s *specAnalyzer) initialize() { for _, c := range s.spec.Consumes { s.consumes[c] = struct{}{} } for _, c := range s.spec.Produces { s.produces[c] = struct{}{} } for _, ss := range s.spec.Security { for k := range ss { s.authSchemes[k] = struct{}{} } } for path, pathItem := range s.AllPaths() { s.analyzeOperations(path, &pathItem) } for name, parameter := range s.spec.Parameters { if parameter.In == "body" && parameter.Schema != nil { s.analyzeSchema("schema", *parameter.Schema, slashpath.Join("/parameters", jsonpointer.Escape(name))) } } for name, response := range s.spec.Responses { if response.Schema != nil { s.analyzeSchema("schema", *response.Schema, slashpath.Join("/responses", jsonpointer.Escape(name))) } } for name, schema := range s.spec.Definitions { s.analyzeSchema(name, schema, "/definitions") } }
func (s *SpecValidator) validateReferencedDefinitions() *Result { // Each referenceable definition must have references. defs := s.spec.Spec().Definitions if len(defs) == 0 { return nil } expected := make(map[string]struct{}) for k := range defs { expected["#/definitions/"+jsonpointer.Escape(k)] = struct{}{} } for _, k := range s.spec.AllDefinitionReferences() { if _, ok := expected[k]; ok { delete(expected, k) } } if len(expected) == 0 { return nil } var result Result for k := range expected { result.AddErrors(errors.New(422, "definition %q is not used anywhere", k)) } return &result }
func (s *specAnalyzer) analyzeOperation(method, path string, op *Operation) { if op == nil { return } for _, c := range op.Consumes { s.consumes[c] = struct{}{} } for _, c := range op.Produces { s.produces[c] = struct{}{} } for _, ss := range op.Security { for k := range ss { s.authSchemes[k] = struct{}{} } } if _, ok := s.operations[method]; !ok { s.operations[method] = make(map[string]*Operation) } s.operations[method][path] = op prefix := slashpath.Join("/paths", jsonpointer.Escape(path), strings.ToLower(method)) for i, param := range op.Parameters { refPref := slashpath.Join(prefix, "parameters", strconv.Itoa(i)) if param.Ref.String() != "" { s.references.addParamRef(refPref, ¶m) } if param.In == "body" && param.Schema != nil { s.analyzeSchema("schema", *param.Schema, refPref) } } if op.Responses != nil { if op.Responses.Default != nil { refPref := slashpath.Join(prefix, "responses", "default") if op.Responses.Default.Ref.String() != "" { s.references.addResponseRef(refPref, op.Responses.Default) } if op.Responses.Default.Schema != nil { s.analyzeSchema("schema", *op.Responses.Default.Schema, refPref) } } for k, res := range op.Responses.StatusCodeResponses { refPref := slashpath.Join(prefix, "responses", strconv.Itoa(k)) if res.Ref.String() != "" { s.references.addResponseRef(refPref, &res) } if res.Schema != nil { s.analyzeSchema("schema", *res.Schema, refPref) } } } }
func TestResolveLocalRef_PathItem(t *testing.T) { rootDoc := new(Swagger) b, err := ioutil.ReadFile("../fixtures/specs/refed.json") if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) { var tgt PathItem ref, err := NewRef("#/paths/" + jsonpointer.Escape("/pets/{id}")) if assert.NoError(t, err) { resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) if assert.NoError(t, resolver.Resolve(&ref, &tgt)) { assert.Equal(t, rootDoc.Paths.Paths["/pets/{id}"].Get, tgt.Get) } } } }
func (s *specAnalyzer) analyzeOperations(path string, op *PathItem) { s.analyzeOperation("GET", path, op.Get) s.analyzeOperation("PUT", path, op.Put) s.analyzeOperation("POST", path, op.Post) s.analyzeOperation("PATCH", path, op.Patch) s.analyzeOperation("DELETE", path, op.Delete) s.analyzeOperation("HEAD", path, op.Head) s.analyzeOperation("OPTIONS", path, op.Options) for i, param := range op.Parameters { if param.Schema != nil { s.analyzeSchema("schema", *param.Schema, slashpath.Join("/paths", jsonpointer.Escape(path), "parameters", strconv.Itoa(i))) } } }
func (s *specAnalyzer) analyzeSchema(name string, schema Schema, prefix string) { refURI := slashpath.Join(prefix, jsonpointer.Escape(name)) schRef := SchemaRef{ Name: name, Schema: &schema, Ref: MustCreateRef("#" + refURI), } s.allSchemas["#"+refURI] = schRef if schema.Ref.String() != "" { s.references.addSchemaRef(refURI, schRef) } for k, v := range schema.Definitions { s.analyzeSchema(k, v, slashpath.Join(refURI, "definitions")) } for k, v := range schema.Properties { s.analyzeSchema(k, v, slashpath.Join(refURI, "properties")) } for k, v := range schema.PatternProperties { s.analyzeSchema(k, v, slashpath.Join(refURI, "patternProperties")) } for i, v := range schema.AllOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "allOf")) } if len(schema.AllOf) > 0 { s.allOfs["#"+refURI] = SchemaRef{Name: name, Schema: &schema, Ref: MustCreateRef("#" + refURI)} } for i, v := range schema.AnyOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "anyOf")) } for i, v := range schema.OneOf { s.analyzeSchema(strconv.Itoa(i), v, slashpath.Join(refURI, "oneOf")) } if schema.Not != nil { s.analyzeSchema("not", *schema.Not, refURI) } if schema.AdditionalProperties != nil && schema.AdditionalProperties.Schema != nil { s.analyzeSchema("additionalProperties", *schema.AdditionalProperties.Schema, refURI) } if schema.AdditionalItems != nil && schema.AdditionalItems.Schema != nil { s.analyzeSchema("additionalItems", *schema.AdditionalItems.Schema, refURI) } if schema.Items != nil { if schema.Items.Schema != nil { s.analyzeSchema("items", *schema.Items.Schema, refURI) } for i, sch := range schema.Items.Schemas { s.analyzeSchema(strconv.Itoa(i), sch, slashpath.Join(refURI, "items")) } } }
func TestResolveRemoteRef_ToPathItem(t *testing.T) { specs := "../fixtures/specs" fileserver := http.FileServer(http.Dir(specs)) server := httptest.NewServer(fileserver) defer server.Close() rootDoc := new(Swagger) b, err := ioutil.ReadFile("../fixtures/specs/refed.json") if assert.NoError(t, err) && assert.NoError(t, json.Unmarshal(b, rootDoc)) { var tgt PathItem ref, err := NewRef(server.URL + "/refed.json#/paths/" + jsonpointer.Escape("/pets/{id}")) if assert.NoError(t, err) { resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) if assert.NoError(t, resolver.Resolve(&ref, &tgt)) { assert.Equal(t, rootDoc.Paths.Paths["/pets/{id}"].Get, tgt.Get) } } } }
func (s *specAnalyzer) analyzeOperations(path string, pi *PathItem) { // TODO: resolve refs here? op := pi s.analyzeOperation("GET", path, op.Get) s.analyzeOperation("PUT", path, op.Put) s.analyzeOperation("POST", path, op.Post) s.analyzeOperation("PATCH", path, op.Patch) s.analyzeOperation("DELETE", path, op.Delete) s.analyzeOperation("HEAD", path, op.Head) s.analyzeOperation("OPTIONS", path, op.Options) for i, param := range op.Parameters { refPref := slashpath.Join("/paths", jsonpointer.Escape(path), "parameters", strconv.Itoa(i)) if param.Ref.String() != "" { s.references.addParamRef(refPref, ¶m) } if param.Schema != nil { s.analyzeSchema("schema", *param.Schema, refPref) } } }
func TestResolveLocalRef(t *testing.T) { rootDoc := new(Swagger) json.Unmarshal(testingutil.PetStoreJSONMessage, rootDoc) Convey("resolving local a ref", t, func() { Convey("in a swagger spec", func() { Convey("to a schema", func() { Convey("resolves root to same ptr instance", func() { result := new(Swagger) ref, _ := NewRef("#") resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err := resolver.Resolve(&ref, result) So(err, ShouldBeNil) So(result, ShouldResemble, rootDoc) }) Convey("from a fragment", func() { var tgt Schema ref, err := NewRef("#/definitions/Category") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.ID, ShouldEqual, "Category") }) Convey("from an invalid fragment", func() { var tgt Schema ref, err := NewRef("#/definitions/NotThere") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldNotBeNil) }) }) Convey("to a parameter", func() { rootDoc = new(Swagger) b, err := ioutil.ReadFile("../fixtures/specs/refed.json") So(err, ShouldBeNil) json.Unmarshal(b, rootDoc) var tgt Parameter ref, err := NewRef("#/parameters/idParam") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Name, ShouldEqual, "id") So(tgt.In, ShouldEqual, "path") So(tgt.Description, ShouldEqual, "ID of pet to fetch") So(tgt.Required, ShouldBeTrue) So(tgt.Type, ShouldEqual, "integer") So(tgt.Format, ShouldEqual, "int64") }) Convey("to a path item object", func() { rootDoc = new(Swagger) b, err := ioutil.ReadFile("../fixtures/specs/refed.json") So(err, ShouldBeNil) json.Unmarshal(b, rootDoc) var tgt PathItem ref, err := NewRef("#/paths/" + jsonpointer.Escape("/pets/{id}")) So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Get, ShouldEqual, rootDoc.Paths.Paths["/pets/{id}"].Get) }) Convey("to a response object", func() { rootDoc = new(Swagger) b, err := ioutil.ReadFile("../fixtures/specs/refed.json") So(err, ShouldBeNil) json.Unmarshal(b, rootDoc) var tgt Response ref, err := NewRef("#/responses/petResponse") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt, ShouldResemble, rootDoc.Responses["petResponse"]) }) }) }) }
func TestResolveRemoteRef(t *testing.T) { specs := "../fixtures/specs" fileserver := http.FileServer(http.Dir(specs)) Convey("resolving a remote ref", t, func() { server := httptest.NewServer(fileserver) Reset(func() { server.Close() }) Convey("in a swagger spec", func() { rootDoc := new(Swagger) b, err := ioutil.ReadFile("../fixtures/specs/refed.json") So(err, ShouldBeNil) json.Unmarshal(b, rootDoc) Convey("resolves root to same schema", func() { var result Swagger ref, _ := NewRef(server.URL + "/refed.json#") resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &result) So(err, ShouldBeNil) compareSpecs(result, *rootDoc) }) Convey("to a schema", func() { Convey("from a fragment", func() { var tgt Schema ref, err := NewRef(server.URL + "/refed.json#/definitions/pet") So(err, ShouldBeNil) resolver := &schemaLoader{root: rootDoc, cache: defaultResolutionCache(), loadDoc: swag.JSONDoc} err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Required, ShouldResemble, []string{"id", "name"}) }) Convey("from an invalid fragment", func() { var tgt Schema ref, err := NewRef(server.URL + "/refed.json#/definitions/NotThere") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldNotBeNil) }) Convey("with a resolution context", func() { server.Close() server = resolutionContextServer() var tgt Schema ref, err := NewRef(server.URL + "/resolution.json#/definitions/bool") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Type, ShouldResemble, StringOrArray([]string{"boolean"})) }) Convey("with a nested resolution context", func() { server.Close() server = resolutionContextServer() var tgt Schema ref, err := NewRef(server.URL + "/resolution.json#/items/items") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Type, ShouldResemble, StringOrArray([]string{"string"})) }) Convey("with a nested resolution context with a fragment", func() { server.Close() server = resolutionContextServer() var tgt Schema ref, err := NewRef(server.URL + "/resolution2.json#/items/items") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Type, ShouldResemble, StringOrArray([]string{"file"})) }) }) Convey("to a parameter", func() { var tgt Parameter ref, err := NewRef(server.URL + "/refed.json#/parameters/idParam") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Name, ShouldEqual, "id") So(tgt.In, ShouldEqual, "path") So(tgt.Description, ShouldEqual, "ID of pet to fetch") So(tgt.Required, ShouldBeTrue) So(tgt.Type, ShouldEqual, "integer") So(tgt.Format, ShouldEqual, "int64") }) Convey("to a path item object", func() { var tgt PathItem ref, err := NewRef(server.URL + "/refed.json#/paths/" + jsonpointer.Escape("/pets/{id}")) So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt.Get, ShouldResemble, rootDoc.Paths.Paths["/pets/{id}"].Get) }) Convey("to a response object", func() { var tgt Response ref, err := NewRef(server.URL + "/refed.json#/responses/petResponse") So(err, ShouldBeNil) resolver, _ := defaultSchemaLoader(rootDoc, nil, nil) err = resolver.Resolve(&ref, &tgt) So(err, ShouldBeNil) So(tgt, ShouldResemble, rootDoc.Responses["petResponse"]) }) }) }) }