diff --git a/README.md b/README.md index 6354742c..8d13b702 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,29 @@ [![license](http://img.shields.io/badge/license-Apache%20v2-orange.svg)](https://raw.githubusercontent.com/go-openapi/spec/master/LICENSE) [![GoDoc](https://godoc.org/github.com/go-openapi/spec?status.svg)](http://godoc.org/github.com/go-openapi/spec) -[![GolangCI](https://golangci.com/badges/github.com/go-openapi/spec.svg)](https://golangci.com) [![Go Report Card](https://goreportcard.com/badge/github.com/go-openapi/spec)](https://goreportcard.com/report/github.com/go-openapi/spec) The object model for OpenAPI specification documents. -Currently supports Swagger 2.0. +### FAQ + +* What does this do? + +> 1. This package knows how to marshal and unmarshal Swagger API specifications into a golang object model +> 2. It knows how to resolve $ref and expand them to make a single root documment + +* How does it play with the rest of the go-openapi packages ? + +> 1. This package is at the core of the go-openapi suite of packages and [code generator](https://github.com/go-swagger/go-swagger) +> 2. There is a [spec loading package](https://github.com/go-openapi/loads) to fetch specs as JSON or YAML from local or remote locations +> 3. There is a [spec validation package](https://github.com/go-openapi/validate) built on top of it +> 4. There is a [spec analysis package](https://github.com/go-openapi/analysis) built on top of it, to analyze, flatten, fix and merge spec documents + +* Does this library support OpenAPI 3? + +> No. +> This package currently only supports OpenAPI 2.0 (aka Swagger 2.0). +> There is no plan to make it evolve toward supporting OpenAPI 3.x. +> This [discussion thread](https://github.com/go-openapi/spec/issues/21) relates the full story. +> +> An early attempt to support Swagger 3 may be found at: https://github.com/go-openapi/spec3 diff --git a/bindata.go b/bindata.go index 66b1f326..afc83850 100644 --- a/bindata.go +++ b/bindata.go @@ -247,9 +247,9 @@ type bintree struct { } var _bintree = &bintree{nil, map[string]*bintree{ - "jsonschema-draft-04.json": &bintree{jsonschemaDraft04Json, map[string]*bintree{}}, - "v2": &bintree{nil, map[string]*bintree{ - "schema.json": &bintree{v2SchemaJson, map[string]*bintree{}}, + "jsonschema-draft-04.json": {jsonschemaDraft04Json, map[string]*bintree{}}, + "v2": {nil, map[string]*bintree{ + "schema.json": {v2SchemaJson, map[string]*bintree{}}, }}, }} diff --git a/contact_info.go b/contact_info.go index f9bf42e8..2f7bb219 100644 --- a/contact_info.go +++ b/contact_info.go @@ -28,12 +28,14 @@ type ContactInfo struct { VendorExtensible } +// ContactInfoProps hold the properties of a ContactInfo object type ContactInfoProps struct { Name string `json:"name,omitempty"` URL string `json:"url,omitempty"` Email string `json:"email,omitempty"` } +// UnmarshalJSON hydrates ContactInfo from json func (c *ContactInfo) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &c.ContactInfoProps); err != nil { return err @@ -41,6 +43,7 @@ func (c *ContactInfo) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &c.VendorExtensible) } +// MarshalJSON produces ContactInfo as json func (c ContactInfo) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(c.ContactInfoProps) if err != nil { diff --git a/license.go b/license.go index e1529b40..b42f8036 100644 --- a/license.go +++ b/license.go @@ -28,11 +28,13 @@ type License struct { VendorExtensible } +// LicenseProps holds the properties of a License object type LicenseProps struct { Name string `json:"name,omitempty"` URL string `json:"url,omitempty"` } +// UnmarshalJSON hydrates License from json func (l *License) UnmarshalJSON(data []byte) error { if err := json.Unmarshal(data, &l.LicenseProps); err != nil { return err @@ -40,6 +42,7 @@ func (l *License) UnmarshalJSON(data []byte) error { return json.Unmarshal(data, &l.VendorExtensible) } +// MarshalJSON produces License as json func (l License) MarshalJSON() ([]byte, error) { b1, err := json.Marshal(l.LicenseProps) if err != nil { diff --git a/properties.go b/properties.go index bda09153..2af13787 100644 --- a/properties.go +++ b/properties.go @@ -7,13 +7,18 @@ import ( "sort" ) +// OrderSchemaItem holds a named schema (e.g. from a property of an object) type OrderSchemaItem struct { Name string Schema } +// OrderSchemaItems is a sortable slice of named schemas. +// The ordering is defined by the x-order schema extension. type OrderSchemaItems []OrderSchemaItem +// MarshalJSON produces a json object with keys defined by the name schemas +// of the OrderSchemaItems slice, keeping the original order of the slice. func (items OrderSchemaItems) MarshalJSON() ([]byte, error) { buf := bytes.NewBuffer(nil) buf.WriteString("{") @@ -60,8 +65,11 @@ func (items OrderSchemaItems) Less(i, j int) (ret bool) { return items[i].Name < items[j].Name } +// SchemaProperties is a map representing the properties of a Schema object. +// It knows how to transform its keys into an ordered slice. type SchemaProperties map[string]Schema +// ToOrderedSchemaItems transforms the map of properties into a sortable slice func (properties SchemaProperties) ToOrderedSchemaItems() OrderSchemaItems { items := make(OrderSchemaItems, 0, len(properties)) for k, v := range properties { @@ -74,6 +82,7 @@ func (properties SchemaProperties) ToOrderedSchemaItems() OrderSchemaItems { return items } +// MarshalJSON produces properties as json, keeping their order. func (properties SchemaProperties) MarshalJSON() ([]byte, error) { if properties == nil { return []byte("null"), nil diff --git a/spec_test.go b/spec_test.go index e1129fa6..eb9248bc 100644 --- a/spec_test.go +++ b/spec_test.go @@ -72,25 +72,8 @@ func Test_Issue1429(t *testing.T) { // assert well expanded require.Truef(t, (sp.Paths != nil && sp.Paths.Paths != nil), "expected paths to be available in fixture") - for _, pi := range sp.Paths.Paths { - for _, param := range pi.Get.Parameters { - if assert.NotNilf(t, param.Schema, "expected param schema not to be nil") { - // all param fixtures are body param with schema - // all $ref expanded - assert.Equal(t, "", param.Schema.Ref.String()) - } - } - for code, response := range pi.Get.Responses.StatusCodeResponses { - // all response fixtures are with StatusCodeResponses, but 200 - if code == 200 { - assert.Nilf(t, response.Schema, "expected response schema to be nil") - continue - } - if assert.NotNilf(t, response.Schema, "expected response schema not to be nil") { - assert.Equal(t, "", response.Schema.Ref.String()) - } - } - } + assertPaths1429(t, sp) + for _, def := range sp.Definitions { assert.Equal(t, "", def.Ref.String()) } @@ -103,48 +86,74 @@ func Test_Issue1429(t *testing.T) { // assert well resolved require.Truef(t, (sp.Paths != nil && sp.Paths.Paths != nil), "expected paths to be available in fixture") + assertPaths1429SkipSchema(t, sp) + + for _, def := range sp.Definitions { + assert.Contains(t, def.Ref.String(), "responses.yaml#/") + } +} + +func assertPaths1429(t testing.TB, sp *spec.Swagger) { for _, pi := range sp.Paths.Paths { for _, param := range pi.Get.Parameters { - if assert.NotNilf(t, param.Schema, "expected param schema not to be nil") { - // all param fixtures are body param with schema - if param.Name == "plainRequest" { - // this one is expanded - assert.Equal(t, "", param.Schema.Ref.String()) - continue - } - if param.Name == "nestedBody" { - // this one is local - assert.Truef(t, strings.HasPrefix(param.Schema.Ref.String(), "#/definitions/"), - "expected rooted definitions $ref, got: %s", param.Schema.Ref.String()) - continue - } - if param.Name == "remoteRequest" { - assert.Contains(t, param.Schema.Ref.String(), "remote/remote.yaml#/") - continue - } - assert.Contains(t, param.Schema.Ref.String(), "responses.yaml#/") - } + require.NotNilf(t, param.Schema, "expected param schema not to be nil") + // all param fixtures are body param with schema + // all $ref expanded + assert.Equal(t, "", param.Schema.Ref.String()) } + for code, response := range pi.Get.Responses.StatusCodeResponses { // all response fixtures are with StatusCodeResponses, but 200 if code == 200 { assert.Nilf(t, response.Schema, "expected response schema to be nil") continue } - if code == 204 { - assert.Contains(t, response.Schema.Ref.String(), "remote/remote.yaml#/") + require.NotNilf(t, response.Schema, "expected response schema not to be nil") + assert.Equal(t, "", response.Schema.Ref.String()) + } + } +} + +func assertPaths1429SkipSchema(t testing.TB, sp *spec.Swagger) { + for _, pi := range sp.Paths.Paths { + for _, param := range pi.Get.Parameters { + require.NotNilf(t, param.Schema, "expected param schema not to be nil") + + // all param fixtures are body param with schema + switch param.Name { + case "plainRequest": + // this one is expanded + assert.Equal(t, "", param.Schema.Ref.String()) + continue + case "nestedBody": + // this one is local + assert.Truef(t, strings.HasPrefix(param.Schema.Ref.String(), "#/definitions/"), + "expected rooted definitions $ref, got: %s", param.Schema.Ref.String()) + continue + case "remoteRequest": + assert.Contains(t, param.Schema.Ref.String(), "remote/remote.yaml#/") continue } - if code == 404 { + assert.Contains(t, param.Schema.Ref.String(), "responses.yaml#/") + + } + + for code, response := range pi.Get.Responses.StatusCodeResponses { + // all response fixtures are with StatusCodeResponses, but 200 + switch code { + case 200: + assert.Nilf(t, response.Schema, "expected response schema to be nil") + continue + case 204: + assert.Contains(t, response.Schema.Ref.String(), "remote/remote.yaml#/") + continue + case 404: assert.Equal(t, "", response.Schema.Ref.String()) continue } assert.Containsf(t, response.Schema.Ref.String(), "responses.yaml#/", "expected remote ref at resp. %d", code) } } - for _, def := range sp.Definitions { - assert.Contains(t, def.Ref.String(), "responses.yaml#/") - } } func Test_MoreLocalExpansion(t *testing.T) {