diff --git a/datamodel/high/v3/document_test.go b/datamodel/high/v3/document_test.go index 80e2c93..fbf8f16 100644 --- a/datamodel/high/v3/document_test.go +++ b/datamodel/high/v3/document_test.go @@ -4,393 +4,389 @@ package v3 import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel" - lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) var lowDoc *lowv3.Document func initTest() { - data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - lowDoc, err = lowv3.CreateDocument(info) - if err != nil { - panic("broken something") - } + data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + lowDoc, err = lowv3.CreateDocument(info) + if err != nil { + panic("broken something") + } } func BenchmarkNewDocument(b *testing.B) { - initTest() - for i := 0; i < b.N; i++ { - _ = NewDocument(lowDoc) - } + initTest() + for i := 0; i < b.N; i++ { + _ = NewDocument(lowDoc) + } } func TestNewDocument_Extensions(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Equal(t, "darkside", h.Extensions["x-something-something"]) + initTest() + h := NewDocument(lowDoc) + assert.Equal(t, "darkside", h.Extensions["x-something-something"]) } func TestNewDocument_ExternalDocs(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Equal(t, "https://pb33f.io", h.ExternalDocs.URL) + initTest() + h := NewDocument(lowDoc) + assert.Equal(t, "https://pb33f.io", h.ExternalDocs.URL) } func TestNewDocument_Info(t *testing.T) { - initTest() - highDoc := NewDocument(lowDoc) - assert.Equal(t, "3.1.0", highDoc.Version) - assert.Equal(t, "Burger Shop", highDoc.Info.Title) - assert.Equal(t, "https://pb33f.io", highDoc.Info.TermsOfService) - assert.Equal(t, "pb33f", highDoc.Info.Contact.Name) - assert.Equal(t, "buckaroo@pb33f.io", highDoc.Info.Contact.Email) - assert.Equal(t, "https://pb33f.io", highDoc.Info.Contact.URL) - assert.Equal(t, "pb33f", highDoc.Info.License.Name) - assert.Equal(t, "https://pb33f.io/made-up", highDoc.Info.License.URL) - assert.Equal(t, "1.2", highDoc.Info.Version) - assert.Equal(t, "https://pb33f.io/schema", highDoc.JsonSchemaDialect) + initTest() + highDoc := NewDocument(lowDoc) + assert.Equal(t, "3.1.0", highDoc.Version) + assert.Equal(t, "Burger Shop", highDoc.Info.Title) + assert.Equal(t, "https://pb33f.io", highDoc.Info.TermsOfService) + assert.Equal(t, "pb33f", highDoc.Info.Contact.Name) + assert.Equal(t, "buckaroo@pb33f.io", highDoc.Info.Contact.Email) + assert.Equal(t, "https://pb33f.io", highDoc.Info.Contact.URL) + assert.Equal(t, "pb33f", highDoc.Info.License.Name) + assert.Equal(t, "https://pb33f.io/made-up", highDoc.Info.License.URL) + assert.Equal(t, "1.2", highDoc.Info.Version) + assert.Equal(t, "https://pb33f.io/schema", highDoc.JsonSchemaDialect) - wentLow := highDoc.GoLow() - assert.Equal(t, 1, wentLow.Version.ValueNode.Line) - assert.Equal(t, 3, wentLow.Info.Value.Title.KeyNode.Line) + wentLow := highDoc.GoLow() + assert.Equal(t, 1, wentLow.Version.ValueNode.Line) + assert.Equal(t, 3, wentLow.Info.Value.Title.KeyNode.Line) - wentLower := highDoc.Info.Contact.GoLow() - assert.Equal(t, 8, wentLower.Name.ValueNode.Line) - assert.Equal(t, 11, wentLower.Name.ValueNode.Column) + wentLower := highDoc.Info.Contact.GoLow() + assert.Equal(t, 8, wentLower.Name.ValueNode.Line) + assert.Equal(t, 11, wentLower.Name.ValueNode.Column) - wentLowAgain := highDoc.Info.GoLow() - assert.Equal(t, 3, wentLowAgain.Title.ValueNode.Line) - assert.Equal(t, 10, wentLowAgain.Title.ValueNode.Column) + wentLowAgain := highDoc.Info.GoLow() + assert.Equal(t, 3, wentLowAgain.Title.ValueNode.Line) + assert.Equal(t, 10, wentLowAgain.Title.ValueNode.Column) - wentOnceMore := highDoc.Info.License.GoLow() - assert.Equal(t, 12, wentOnceMore.Name.ValueNode.Line) - assert.Equal(t, 11, wentOnceMore.Name.ValueNode.Column) + wentOnceMore := highDoc.Info.License.GoLow() + assert.Equal(t, 12, wentOnceMore.Name.ValueNode.Line) + assert.Equal(t, 11, wentOnceMore.Name.ValueNode.Column) } func TestNewDocument_Servers(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Servers, 2) - assert.Equal(t, "{scheme}://api.pb33f.io", h.Servers[0].URL) - assert.Equal(t, "this is our main API server, for all fun API things.", h.Servers[0].Description) - assert.Len(t, h.Servers[0].Variables, 1) - assert.Equal(t, "https", h.Servers[0].Variables["scheme"].Default) - assert.Len(t, h.Servers[0].Variables["scheme"].Enum, 2) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Servers, 2) + assert.Equal(t, "{scheme}://api.pb33f.io", h.Servers[0].URL) + assert.Equal(t, "this is our main API server, for all fun API things.", h.Servers[0].Description) + assert.Len(t, h.Servers[0].Variables, 1) + assert.Equal(t, "https", h.Servers[0].Variables["scheme"].Default) + assert.Len(t, h.Servers[0].Variables["scheme"].Enum, 2) - assert.Equal(t, "https://{domain}.{host}.com", h.Servers[1].URL) - assert.Equal(t, "this is our second API server, for all fun API things.", h.Servers[1].Description) - assert.Len(t, h.Servers[1].Variables, 2) - assert.Equal(t, "api", h.Servers[1].Variables["domain"].Default) - assert.Equal(t, "pb33f.io", h.Servers[1].Variables["host"].Default) + assert.Equal(t, "https://{domain}.{host}.com", h.Servers[1].URL) + assert.Equal(t, "this is our second API server, for all fun API things.", h.Servers[1].Description) + assert.Len(t, h.Servers[1].Variables, 2) + assert.Equal(t, "api", h.Servers[1].Variables["domain"].Default) + assert.Equal(t, "pb33f.io", h.Servers[1].Variables["host"].Default) - wentLow := h.GoLow() - assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.KeyNode.Line) - assert.Equal(t, 5, wentLow.Servers.Value[0].Value.Description.KeyNode.Column) - assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.ValueNode.Line) - assert.Equal(t, 18, wentLow.Servers.Value[0].Value.Description.ValueNode.Column) + wentLow := h.GoLow() + assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.KeyNode.Line) + assert.Equal(t, 5, wentLow.Servers.Value[0].Value.Description.KeyNode.Column) + assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.ValueNode.Line) + assert.Equal(t, 18, wentLow.Servers.Value[0].Value.Description.ValueNode.Column) - wentLower := h.Servers[0].GoLow() - assert.Equal(t, 45, wentLower.Description.ValueNode.Line) - assert.Equal(t, 18, wentLower.Description.ValueNode.Column) + wentLower := h.Servers[0].GoLow() + assert.Equal(t, 45, wentLower.Description.ValueNode.Line) + assert.Equal(t, 18, wentLower.Description.ValueNode.Column) - wentLowest := h.Servers[0].Variables["scheme"].GoLow() - assert.Equal(t, 50, wentLowest.Description.ValueNode.Line) - assert.Equal(t, 22, wentLowest.Description.ValueNode.Column) + wentLowest := h.Servers[0].Variables["scheme"].GoLow() + assert.Equal(t, 50, wentLowest.Description.ValueNode.Line) + assert.Equal(t, 22, wentLowest.Description.ValueNode.Column) } func TestNewDocument_Tags(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Tags, 2) - assert.Equal(t, "Burgers", h.Tags[0].Name) - assert.Equal(t, "All kinds of yummy burgers.", h.Tags[0].Description) - assert.Equal(t, "Find out more", h.Tags[0].ExternalDocs.Description) - assert.Equal(t, "https://pb33f.io", h.Tags[0].ExternalDocs.URL) - assert.Equal(t, "somethingSpecial", h.Tags[0].Extensions["x-internal-ting"]) - assert.Equal(t, int64(1), h.Tags[0].Extensions["x-internal-tong"]) - assert.Equal(t, 1.2, h.Tags[0].Extensions["x-internal-tang"]) - assert.True(t, h.Tags[0].Extensions["x-internal-tung"].(bool)) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Tags, 2) + assert.Equal(t, "Burgers", h.Tags[0].Name) + assert.Equal(t, "All kinds of yummy burgers.", h.Tags[0].Description) + assert.Equal(t, "Find out more", h.Tags[0].ExternalDocs.Description) + assert.Equal(t, "https://pb33f.io", h.Tags[0].ExternalDocs.URL) + assert.Equal(t, "somethingSpecial", h.Tags[0].Extensions["x-internal-ting"]) + assert.Equal(t, int64(1), h.Tags[0].Extensions["x-internal-tong"]) + assert.Equal(t, 1.2, h.Tags[0].Extensions["x-internal-tang"]) + assert.True(t, h.Tags[0].Extensions["x-internal-tung"].(bool)) - wentLow := h.Tags[1].GoLow() - assert.Equal(t, 39, wentLow.Description.KeyNode.Line) - assert.Equal(t, 5, wentLow.Description.KeyNode.Column) + wentLow := h.Tags[1].GoLow() + assert.Equal(t, 39, wentLow.Description.KeyNode.Line) + assert.Equal(t, 5, wentLow.Description.KeyNode.Column) - wentLower := h.Tags[0].ExternalDocs.GoLow() - assert.Equal(t, 23, wentLower.Description.ValueNode.Line) - assert.Equal(t, 20, wentLower.Description.ValueNode.Column) + wentLower := h.Tags[0].ExternalDocs.GoLow() + assert.Equal(t, 23, wentLower.Description.ValueNode.Line) + assert.Equal(t, 20, wentLower.Description.ValueNode.Column) } func TestNewDocument_Webhooks(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Webhooks, 1) - assert.Equal(t, "Information about a new burger", h.Webhooks["someHook"].Post.RequestBody.Description) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Webhooks, 1) + assert.Equal(t, "Information about a new burger", h.Webhooks["someHook"].Post.RequestBody.Description) } func TestNewDocument_Components_Links(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Links, 2) - assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId) - assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"]) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Links, 2) + assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId) + assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"]) - wentLow := h.Components.Links["LocateBurger"].GoLow() - assert.Equal(t, 305, wentLow.OperationId.ValueNode.Line) - assert.Equal(t, 20, wentLow.OperationId.ValueNode.Column) + wentLow := h.Components.Links["LocateBurger"].GoLow() + assert.Equal(t, 305, wentLow.OperationId.ValueNode.Line) + assert.Equal(t, 20, wentLow.OperationId.ValueNode.Column) } func TestNewDocument_Components_Callbacks(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Callbacks, 1) - assert.Equal(t, "Callback payload", - h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Callbacks, 1) + assert.Equal(t, "Callback payload", + h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description) - assert.Equal(t, 293, - h.Components.Callbacks["BurgerCallback"].GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Line) - assert.Equal(t, 9, - h.Components.Callbacks["BurgerCallback"].GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Column) + assert.Equal(t, 293, + h.Components.Callbacks["BurgerCallback"].GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Line) + assert.Equal(t, 9, + h.Components.Callbacks["BurgerCallback"].GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Column) - assert.Equal(t, "please", h.Components.Callbacks["BurgerCallback"].Extensions["x-break-everything"]) + assert.Equal(t, "please", h.Components.Callbacks["BurgerCallback"].Extensions["x-break-everything"]) - for k := range h.Components.GoLow().Callbacks.Value { - if k.Value == "BurgerCallback" { - assert.Equal(t, 290, k.KeyNode.Line) - assert.Equal(t, 5, k.KeyNode.Column) - } - } + for k := range h.Components.GoLow().Callbacks.Value { + if k.Value == "BurgerCallback" { + assert.Equal(t, 290, k.KeyNode.Line) + assert.Equal(t, 5, k.KeyNode.Column) + } + } } func TestNewDocument_Components_Schemas(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Schemas, 6) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Schemas, 6) - goLow := h.Components.GoLow() + goLow := h.Components.GoLow() - a := h.Components.Schemas["Error"] - abcd := a.Schema().Properties["message"].Schema().Example - assert.Equal(t, "No such burger as 'Big-Whopper'", abcd) - assert.Equal(t, 428, goLow.Schemas.KeyNode.Line) - assert.Equal(t, 3, goLow.Schemas.KeyNode.Column) - assert.Equal(t, 431, a.Schema().GoLow().Description.KeyNode.Line) + a := h.Components.Schemas["Error"] + abcd := a.Schema().Properties["message"].Schema().Example + assert.Equal(t, "No such burger as 'Big-Whopper'", abcd) + assert.Equal(t, 428, goLow.Schemas.KeyNode.Line) + assert.Equal(t, 3, goLow.Schemas.KeyNode.Column) + assert.Equal(t, 431, a.Schema().GoLow().Description.KeyNode.Line) - b := h.Components.Schemas["Burger"] - assert.Len(t, b.Schema().Required, 2) - assert.Equal(t, "golden slices of happy fun joy", b.Schema().Properties["fries"].Schema().Description) - assert.Equal(t, int64(2), b.Schema().Properties["numPatties"].Schema().Example) - assert.Equal(t, 443, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Line) - assert.Equal(t, 7, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Column) - assert.Equal(t, 445, b.Schema().GoLow().FindProperty("name").ValueNode.Line) + b := h.Components.Schemas["Burger"] + assert.Len(t, b.Schema().Required, 2) + assert.Equal(t, "golden slices of happy fun joy", b.Schema().Properties["fries"].Schema().Description) + assert.Equal(t, int64(2), b.Schema().Properties["numPatties"].Schema().Example) + assert.Equal(t, 443, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Line) + assert.Equal(t, 7, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Column) + assert.Equal(t, 445, b.Schema().GoLow().FindProperty("name").ValueNode.Line) - f := h.Components.Schemas["Fries"] - assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items[0].Schema().Example) - assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 2) + f := h.Components.Schemas["Fries"] + assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items[0].Schema().Example) + assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 2) - d := h.Components.Schemas["Drink"] - assert.Len(t, d.Schema().Required, 2) - assert.True(t, d.Schema().AdditionalProperties.(bool)) - assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName) - assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"]) - assert.Equal(t, 511, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line) - assert.Equal(t, 23, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Column) + d := h.Components.Schemas["Drink"] + assert.Len(t, d.Schema().Required, 2) + assert.True(t, d.Schema().AdditionalProperties.(bool)) + assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName) + assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"]) + assert.Equal(t, 511, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line) + assert.Equal(t, 23, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Column) - pl := h.Components.Schemas["SomePayload"] - assert.Equal(t, "is html programming? yes.", pl.Schema().XML.Name) - assert.Equal(t, 518, pl.Schema().XML.GoLow().Name.ValueNode.Line) + pl := h.Components.Schemas["SomePayload"] + assert.Equal(t, "is html programming? yes.", pl.Schema().XML.Name) + assert.Equal(t, 518, pl.Schema().XML.GoLow().Name.ValueNode.Line) - ext := h.Components.Extensions - assert.Equal(t, "loud", ext["x-screaming-baby"]) + ext := h.Components.Extensions + assert.Equal(t, "loud", ext["x-screaming-baby"]) } func TestNewDocument_Components_Headers(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Headers, 1) - assert.Equal(t, "this is a header example for UseOil", h.Components.Headers["UseOil"].Description) - assert.Equal(t, 318, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Line) - assert.Equal(t, 20, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Column) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Headers, 1) + assert.Equal(t, "this is a header example for UseOil", h.Components.Headers["UseOil"].Description) + assert.Equal(t, 318, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Line) + assert.Equal(t, 20, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Column) } func TestNewDocument_Components_RequestBodies(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.RequestBodies, 1) - assert.Equal(t, "Give us the new burger!", h.Components.RequestBodies["BurgerRequest"].Description) - assert.Equal(t, 323, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Line) - assert.Equal(t, 20, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Column) - assert.Len(t, h.Components.RequestBodies["BurgerRequest"].Content["application/json"].Examples, 2) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.RequestBodies, 1) + assert.Equal(t, "Give us the new burger!", h.Components.RequestBodies["BurgerRequest"].Description) + assert.Equal(t, 323, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Line) + assert.Equal(t, 20, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Column) + assert.Len(t, h.Components.RequestBodies["BurgerRequest"].Content["application/json"].Examples, 2) } func TestNewDocument_Components_Examples(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Examples, 1) - assert.Equal(t, "A juicy two hander sammich", h.Components.Examples["QuarterPounder"].Summary) - assert.Equal(t, 341, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Line) - assert.Equal(t, 16, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Column) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Examples, 1) + assert.Equal(t, "A juicy two hander sammich", h.Components.Examples["QuarterPounder"].Summary) + assert.Equal(t, 341, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Line) + assert.Equal(t, 16, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Column) } func TestNewDocument_Components_Responses(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Responses, 1) - assert.Equal(t, "all the dressings for a burger.", h.Components.Responses["DressingResponse"].Description) - assert.Equal(t, "array", h.Components.Responses["DressingResponse"].Content["application/json"].Schema.Schema().Type[0]) - assert.Equal(t, 347, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Line) - assert.Equal(t, 7, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Column) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Responses, 1) + assert.Equal(t, "all the dressings for a burger.", h.Components.Responses["DressingResponse"].Description) + assert.Equal(t, "array", h.Components.Responses["DressingResponse"].Content["application/json"].Schema.Schema().Type[0]) + assert.Equal(t, 347, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Line) + assert.Equal(t, 7, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Column) } func TestNewDocument_Components_SecuritySchemes(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.SecuritySchemes, 3) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.SecuritySchemes, 3) - api := h.Components.SecuritySchemes["APIKeyScheme"] - assert.Equal(t, "an apiKey security scheme", api.Description) - assert.Equal(t, 359, api.GoLow().Description.ValueNode.Line) - assert.Equal(t, 20, api.GoLow().Description.ValueNode.Column) + api := h.Components.SecuritySchemes["APIKeyScheme"] + assert.Equal(t, "an apiKey security scheme", api.Description) + assert.Equal(t, 359, api.GoLow().Description.ValueNode.Line) + assert.Equal(t, 20, api.GoLow().Description.ValueNode.Column) - jwt := h.Components.SecuritySchemes["JWTScheme"] - assert.Equal(t, "an JWT security scheme", jwt.Description) - assert.Equal(t, 364, jwt.GoLow().Description.ValueNode.Line) - assert.Equal(t, 20, jwt.GoLow().Description.ValueNode.Column) + jwt := h.Components.SecuritySchemes["JWTScheme"] + assert.Equal(t, "an JWT security scheme", jwt.Description) + assert.Equal(t, 364, jwt.GoLow().Description.ValueNode.Line) + assert.Equal(t, 20, jwt.GoLow().Description.ValueNode.Column) - oAuth := h.Components.SecuritySchemes["OAuthScheme"] - assert.Equal(t, "an oAuth security scheme", oAuth.Description) - assert.Equal(t, 370, oAuth.GoLow().Description.ValueNode.Line) - assert.Equal(t, 20, oAuth.GoLow().Description.ValueNode.Column) - assert.Len(t, oAuth.Flows.Implicit.Scopes, 2) - assert.Equal(t, "read all burgers", oAuth.Flows.Implicit.Scopes["read:burgers"]) - assert.Equal(t, "https://pb33f.io/oauth", oAuth.Flows.AuthorizationCode.AuthorizationUrl) + oAuth := h.Components.SecuritySchemes["OAuthScheme"] + assert.Equal(t, "an oAuth security scheme", oAuth.Description) + assert.Equal(t, 370, oAuth.GoLow().Description.ValueNode.Line) + assert.Equal(t, 20, oAuth.GoLow().Description.ValueNode.Column) + assert.Len(t, oAuth.Flows.Implicit.Scopes, 2) + assert.Equal(t, "read all burgers", oAuth.Flows.Implicit.Scopes["read:burgers"]) + assert.Equal(t, "https://pb33f.io/oauth", oAuth.Flows.AuthorizationCode.AuthorizationUrl) - // check the lowness is low. - assert.Equal(t, 375, oAuth.Flows.GoLow().Implicit.Value.Scopes.KeyNode.Line) - assert.Equal(t, 11, oAuth.Flows.GoLow().Implicit.Value.Scopes.KeyNode.Column) - assert.Equal(t, 375, oAuth.Flows.Implicit.GoLow().Scopes.KeyNode.Line) - assert.Equal(t, 11, oAuth.Flows.Implicit.GoLow().Scopes.KeyNode.Column) + // check the lowness is low. + assert.Equal(t, 375, oAuth.Flows.GoLow().Implicit.Value.Scopes.KeyNode.Line) + assert.Equal(t, 11, oAuth.Flows.GoLow().Implicit.Value.Scopes.KeyNode.Column) + assert.Equal(t, 375, oAuth.Flows.Implicit.GoLow().Scopes.KeyNode.Line) + assert.Equal(t, 11, oAuth.Flows.Implicit.GoLow().Scopes.KeyNode.Column) } func TestNewDocument_Components_Parameters(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Components.Parameters, 2) - bh := h.Components.Parameters["BurgerHeader"] - assert.Equal(t, "burgerHeader", bh.Name) - assert.Equal(t, 387, bh.GoLow().Name.KeyNode.Line) - assert.Len(t, bh.Schema.Schema().Properties, 2) - assert.Equal(t, "big-mac", bh.Example) - assert.True(t, bh.Required) - assert.Equal(t, "this is a header", - bh.Content["application/json"].Encoding["burgerTheme"].Headers["someHeader"].Description) - assert.Len(t, bh.Content["application/json"].Schema.Schema().Properties, 2) - assert.Equal(t, 404, bh.Content["application/json"].Encoding["burgerTheme"].GoLow().ContentType.ValueNode.Line) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Components.Parameters, 2) + bh := h.Components.Parameters["BurgerHeader"] + assert.Equal(t, "burgerHeader", bh.Name) + assert.Equal(t, 387, bh.GoLow().Name.KeyNode.Line) + assert.Len(t, bh.Schema.Schema().Properties, 2) + assert.Equal(t, "big-mac", bh.Example) + assert.True(t, bh.Required) + assert.Equal(t, "this is a header", + bh.Content["application/json"].Encoding["burgerTheme"].Headers["someHeader"].Description) + assert.Len(t, bh.Content["application/json"].Schema.Schema().Properties, 2) + assert.Equal(t, 404, bh.Content["application/json"].Encoding["burgerTheme"].GoLow().ContentType.ValueNode.Line) } func TestNewDocument_Paths(t *testing.T) { - initTest() - h := NewDocument(lowDoc) - assert.Len(t, h.Paths.PathItems, 5) + initTest() + h := NewDocument(lowDoc) + assert.Len(t, h.Paths.PathItems, 5) - burgersOp := h.Paths.PathItems["/burgers"] - assert.Equal(t, "meaty", burgersOp.Extensions["x-burger-meta"]) - assert.Nil(t, burgersOp.Get) - assert.Nil(t, burgersOp.Put) - assert.Nil(t, burgersOp.Patch) - assert.Nil(t, burgersOp.Head) - assert.Nil(t, burgersOp.Options) - assert.Nil(t, burgersOp.Trace) - assert.Equal(t, 64, burgersOp.GoLow().Post.KeyNode.Line) - assert.Equal(t, "createBurger", burgersOp.Post.OperationId) - assert.Len(t, burgersOp.Post.Tags, 1) - assert.Equal(t, "A new burger for our menu, yummy yum yum.", burgersOp.Post.Description) - assert.Equal(t, "Give us the new burger!", burgersOp.Post.RequestBody.Description) - assert.Len(t, burgersOp.Post.Responses.Codes, 3) - assert.Equal(t, 63, h.Paths.GoLow().FindPath("/burgers").ValueNode.Line) + burgersOp := h.Paths.PathItems["/burgers"] + assert.Equal(t, "meaty", burgersOp.Extensions["x-burger-meta"]) + assert.Nil(t, burgersOp.Get) + assert.Nil(t, burgersOp.Put) + assert.Nil(t, burgersOp.Patch) + assert.Nil(t, burgersOp.Head) + assert.Nil(t, burgersOp.Options) + assert.Nil(t, burgersOp.Trace) + assert.Equal(t, 64, burgersOp.GoLow().Post.KeyNode.Line) + assert.Equal(t, "createBurger", burgersOp.Post.OperationId) + assert.Len(t, burgersOp.Post.Tags, 1) + assert.Equal(t, "A new burger for our menu, yummy yum yum.", burgersOp.Post.Description) + assert.Equal(t, "Give us the new burger!", burgersOp.Post.RequestBody.Description) + assert.Len(t, burgersOp.Post.Responses.Codes, 3) + assert.Equal(t, 63, h.Paths.GoLow().FindPath("/burgers").ValueNode.Line) - okResp := burgersOp.Post.Responses.FindResponseByCode(200) - assert.Len(t, okResp.Headers, 1) - assert.Equal(t, "A tasty burger for you to eat.", okResp.Description) - assert.Equal(t, 69, burgersOp.Post.GoLow().Description.ValueNode.Line) - assert.Len(t, okResp.Content["application/json"].Examples, 2) - assert.Equal(t, "a cripsy fish sammich filled with ocean goodness.", - okResp.Content["application/json"].Examples["filetOFish"].Summary) - assert.Equal(t, 74, burgersOp.Post.Responses.GoLow().FindResponseByCode("200").ValueNode.Line) + okResp := burgersOp.Post.Responses.FindResponseByCode(200) + assert.Len(t, okResp.Headers, 1) + assert.Equal(t, "A tasty burger for you to eat.", okResp.Description) + assert.Equal(t, 69, burgersOp.Post.GoLow().Description.ValueNode.Line) + assert.Len(t, okResp.Content["application/json"].Examples, 2) + assert.Equal(t, "a cripsy fish sammich filled with ocean goodness.", + okResp.Content["application/json"].Examples["filetOFish"].Summary) + assert.Equal(t, 74, burgersOp.Post.Responses.GoLow().FindResponseByCode("200").ValueNode.Line) - assert.Equal(t, 80, okResp.Content["application/json"].GoLow().Schema.KeyNode.Line) - assert.Equal(t, 15, okResp.Content["application/json"].GoLow().Schema.KeyNode.Column) + assert.Equal(t, 80, okResp.Content["application/json"].GoLow().Schema.KeyNode.Line) + assert.Equal(t, 15, okResp.Content["application/json"].GoLow().Schema.KeyNode.Column) - assert.Equal(t, 77, okResp.GoLow().Description.KeyNode.Line) - assert.Len(t, okResp.Links, 2) - assert.Equal(t, "locateBurger", okResp.Links["LocateBurger"].OperationId) - assert.Equal(t, 305, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line) - assert.Len(t, burgersOp.Post.Security[0].Requirements, 1) - assert.Len(t, burgersOp.Post.Security[0].Requirements["OAuthScheme"], 2) - assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements["OAuthScheme"][0]) - assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line) - assert.Len(t, burgersOp.Post.Servers, 1) - assert.Equal(t, "https://pb33f.io", burgersOp.Post.Servers[0].URL) + assert.Equal(t, 77, okResp.GoLow().Description.KeyNode.Line) + assert.Len(t, okResp.Links, 2) + assert.Equal(t, "locateBurger", okResp.Links["LocateBurger"].OperationId) + assert.Equal(t, 305, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line) + assert.Len(t, burgersOp.Post.Security[0].Requirements, 1) + assert.Len(t, burgersOp.Post.Security[0].Requirements["OAuthScheme"], 2) + assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements["OAuthScheme"][0]) + assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line) + assert.Len(t, burgersOp.Post.Servers, 1) + assert.Equal(t, "https://pb33f.io", burgersOp.Post.Servers[0].URL) } func TestStripeAsDoc(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - lowDoc, err = lowv3.CreateDocument(info) - if err != nil { - panic("broken something") - } - d := NewDocument(lowDoc) - fmt.Println(d) + data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + lowDoc, err = lowv3.CreateDocument(info) + assert.Len(t, err, 21) + d := NewDocument(lowDoc) + fmt.Println(d) } func TestAsanaAsDoc(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/asana.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - lowDoc, err = lowv3.CreateDocument(info) - if err != nil { - panic("broken something") - } - d := NewDocument(lowDoc) - fmt.Println(d) + data, _ := ioutil.ReadFile("../../../test_specs/asana.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + lowDoc, err = lowv3.CreateDocument(info) + if err != nil { + panic("broken something") + } + d := NewDocument(lowDoc) + fmt.Println(d) } func TestPetstoreAsDoc(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - lowDoc, err = lowv3.CreateDocument(info) - if err != nil { - panic("broken something") - } - d := NewDocument(lowDoc) - fmt.Println(d) + data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + lowDoc, err = lowv3.CreateDocument(info) + if err != nil { + panic("broken something") + } + d := NewDocument(lowDoc) + fmt.Println(d) } func TestCircularReferencesDoc(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/circular-tests.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - lowDoc, err = lowv3.CreateDocument(info) - if err != nil { - panic("broken something") - } - d := NewDocument(lowDoc) - assert.Len(t, d.Components.Schemas, 9) - assert.Len(t, d.Index.GetCircularReferences(), 3) + data, _ := ioutil.ReadFile("../../../test_specs/circular-tests.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + lowDoc, err = lowv3.CreateDocument(info) + assert.Len(t, err, 3) + d := NewDocument(lowDoc) + assert.Len(t, d.Components.Schemas, 9) + assert.Len(t, d.Index.GetCircularReferences(), 3) } diff --git a/datamodel/low/v2/swagger.go b/datamodel/low/v2/swagger.go index 7a479b0..6f5102d 100644 --- a/datamodel/low/v2/swagger.go +++ b/datamodel/low/v2/swagger.go @@ -12,12 +12,13 @@ package v2 import ( - "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/resolver" - "gopkg.in/yaml.v3" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/resolver" + "gopkg.in/yaml.v3" ) // processes a property of a Swagger document asynchronously using bool and error channels for signals. @@ -26,242 +27,250 @@ type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, // Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification. type Swagger struct { - // Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition. - Swagger low.ValueReference[string] + // Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition. + Swagger low.ValueReference[string] - // Info represents a specification Info definition. - // Provides metadata about the API. The metadata can be used by the clients if needed. - // - https://swagger.io/specification/v2/#infoObject - Info low.NodeReference[*base.Info] + // Info represents a specification Info definition. + // Provides metadata about the API. The metadata can be used by the clients if needed. + // - https://swagger.io/specification/v2/#infoObject + Info low.NodeReference[*base.Info] - // Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor - // sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used - // (including the port). The host does not support path templating. - Host low.NodeReference[string] + // Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor + // sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used + // (including the port). The host does not support path templating. + Host low.NodeReference[string] - // BasePath is The base path on which the API is served, which is relative to the host. If it is not included, - // the API is served directly under the host. The value MUST start with a leading slash (/). - // The basePath does not support path templating. - BasePath low.NodeReference[string] + // BasePath is The base path on which the API is served, which is relative to the host. If it is not included, + // the API is served directly under the host. The value MUST start with a leading slash (/). + // The basePath does not support path templating. + BasePath low.NodeReference[string] - // Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss". - // If the schemes is not included, the default scheme to be used is the one used to access - // the Swagger definition itself. - Schemes low.NodeReference[[]low.ValueReference[string]] + // Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss". + // If the schemes is not included, the default scheme to be used is the one used to access + // the Swagger definition itself. + Schemes low.NodeReference[[]low.ValueReference[string]] - // Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on - // specific API calls. Value MUST be as described under Mime Types. - Consumes low.NodeReference[[]low.ValueReference[string]] + // Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Consumes low.NodeReference[[]low.ValueReference[string]] - // Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on - // specific API calls. Value MUST be as described under Mime Types. - Produces low.NodeReference[[]low.ValueReference[string]] + // Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on + // specific API calls. Value MUST be as described under Mime Types. + Produces low.NodeReference[[]low.ValueReference[string]] - // Paths are the paths and operations for the API. Perhaps the most important part of the specification. - // - https://swagger.io/specification/v2/#pathsObject - Paths low.NodeReference[*Paths] + // Paths are the paths and operations for the API. Perhaps the most important part of the specification. + // - https://swagger.io/specification/v2/#pathsObject + Paths low.NodeReference[*Paths] - // Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances - // - https://swagger.io/specification/v2/#definitionsObject - Definitions low.NodeReference[*Definitions] + // Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances + // - https://swagger.io/specification/v2/#definitionsObject + Definitions low.NodeReference[*Definitions] - // SecurityDefinitions represents security scheme definitions that can be used across the specification. - // - https://swagger.io/specification/v2/#securityDefinitionsObject - SecurityDefinitions low.NodeReference[*SecurityDefinitions] + // SecurityDefinitions represents security scheme definitions that can be used across the specification. + // - https://swagger.io/specification/v2/#securityDefinitionsObject + SecurityDefinitions low.NodeReference[*SecurityDefinitions] - // Parameters is an object to hold parameters that can be used across operations. - // This property does not define global parameters for all operations. - // - https://swagger.io/specification/v2/#parametersDefinitionsObject - Parameters low.NodeReference[*ParameterDefinitions] + // Parameters is an object to hold parameters that can be used across operations. + // This property does not define global parameters for all operations. + // - https://swagger.io/specification/v2/#parametersDefinitionsObject + Parameters low.NodeReference[*ParameterDefinitions] - // Responses is an object to hold responses that can be used across operations. - // This property does not define global responses for all operations. - // - https://swagger.io/specification/v2/#responsesDefinitionsObject - Responses low.NodeReference[*ResponsesDefinitions] + // Responses is an object to hold responses that can be used across operations. + // This property does not define global responses for all operations. + // - https://swagger.io/specification/v2/#responsesDefinitionsObject + Responses low.NodeReference[*ResponsesDefinitions] - // Security is a declaration of which security schemes are applied for the API as a whole. The list of values - // describes alternative security schemes that can be used (that is, there is a logical OR between the security - // requirements). Individual operations can override this definition. - // - https://swagger.io/specification/v2/#securityRequirementObject - Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] + // Security is a declaration of which security schemes are applied for the API as a whole. The list of values + // describes alternative security schemes that can be used (that is, there is a logical OR between the security + // requirements). Individual operations can override this definition. + // - https://swagger.io/specification/v2/#securityRequirementObject + Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] - // Tags are A list of tags used by the specification with additional metadata. - // The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used - // by the Operation Object must be declared. The tags that are not declared may be organized randomly or based - // on the tools' logic. Each tag name in the list MUST be unique. - // - https://swagger.io/specification/v2/#tagObject - Tags low.NodeReference[[]low.ValueReference[*base.Tag]] + // Tags are A list of tags used by the specification with additional metadata. + // The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used + // by the Operation Object must be declared. The tags that are not declared may be organized randomly or based + // on the tools' logic. Each tag name in the list MUST be unique. + // - https://swagger.io/specification/v2/#tagObject + Tags low.NodeReference[[]low.ValueReference[*base.Tag]] - // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate? - ExternalDocs low.NodeReference[*base.ExternalDoc] + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate? + ExternalDocs low.NodeReference[*base.ExternalDoc] - // Extensions contains all custom extensions defined for the top-level document. - Extensions map[low.KeyReference[string]]low.ValueReference[any] + // Extensions contains all custom extensions defined for the top-level document. + Extensions map[low.KeyReference[string]]low.ValueReference[any] - // Index is a reference to the index.SpecIndex that was created for the document and used - // as a guide when building out the Document. Ideal if further processing is required on the model and - // the original details are required to continue the work. - // - // This property is not a part of the OpenAPI schema, this is custom to libopenapi. - Index *index.SpecIndex + // Index is a reference to the index.SpecIndex that was created for the document and used + // as a guide when building out the Document. Ideal if further processing is required on the model and + // the original details are required to continue the work. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + Index *index.SpecIndex - // SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read. - // - // This property is not a part of the OpenAPI schema, this is custom to libopenapi. - SpecInfo *datamodel.SpecInfo + // SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read. + // + // This property is not a part of the OpenAPI schema, this is custom to libopenapi. + SpecInfo *datamodel.SpecInfo } // FindExtension locates an extension from the root of the Swagger document. func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] { - return low.FindItemInMap[any](ext, s.Extensions) + return low.FindItemInMap[any](ext, s.Extensions) } func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) { - doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}} - doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) + doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}} + doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) - // build an index - idx := index.NewSpecIndex(info.RootNode) - doc.Index = idx - doc.SpecInfo = info + // build an index + idx := index.NewSpecIndex(info.RootNode) + doc.Index = idx + doc.SpecInfo = info - var errors []error + var errors []error - // build out swagger scalar variables. - _ = low.BuildModel(info.RootNode.Content[0], &doc) + // build out swagger scalar variables. + _ = low.BuildModel(info.RootNode.Content[0], &doc) - // extract externalDocs - extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx) - if err != nil { - errors = append(errors, err) - } + // extract externalDocs + extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx) + if err != nil { + errors = append(errors, err) + } - doc.ExternalDocs = extDocs + doc.ExternalDocs = extDocs - // create resolver and check for circular references. - resolve := resolver.NewResolver(idx) - _ = resolve.CheckForCircularReferences() + // create resolver and check for circular references. + resolve := resolver.NewResolver(idx) + resolvingErrors := resolve.CheckForCircularReferences() - extractionFuncs := []documentFunction{ - extractInfo, - extractPaths, - extractDefinitions, - extractParamDefinitions, - extractResponsesDefinitions, - extractSecurityDefinitions, - extractTags, - extractSecurity, - } - doneChan := make(chan bool) - errChan := make(chan error) - for i := range extractionFuncs { - go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan) - } - completedExtractions := 0 - for completedExtractions < len(extractionFuncs) { - select { - case <-doneChan: - completedExtractions++ - case e := <-errChan: - completedExtractions++ - errors = append(errors, e) - } - } + if len(resolvingErrors) > 0 { + for r := range resolvingErrors { + errors = append(errors, + fmt.Errorf("%s (%s) [%d:%d]", resolvingErrors[r].Error.Error(), + resolvingErrors[r].Path, resolvingErrors[r].Node.Line, resolvingErrors[r].Node.Column)) + } + } - return &doc, errors + extractionFuncs := []documentFunction{ + extractInfo, + extractPaths, + extractDefinitions, + extractParamDefinitions, + extractResponsesDefinitions, + extractSecurityDefinitions, + extractTags, + extractSecurity, + } + doneChan := make(chan bool) + errChan := make(chan error) + for i := range extractionFuncs { + go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan) + } + completedExtractions := 0 + for completedExtractions < len(extractionFuncs) { + select { + case <-doneChan: + completedExtractions++ + case e := <-errChan: + completedExtractions++ + errors = append(errors, e) + } + } + + return &doc, errors } func (s *Swagger) GetExternalDocs() *low.NodeReference[any] { - return &low.NodeReference[any]{ - KeyNode: s.ExternalDocs.KeyNode, - ValueNode: s.ExternalDocs.ValueNode, - Value: s.ExternalDocs.Value, - } + return &low.NodeReference[any]{ + KeyNode: s.ExternalDocs.KeyNode, + ValueNode: s.ExternalDocs.ValueNode, + Value: s.ExternalDocs.Value, + } } func extractInfo(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Info = info - c <- true + info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Info = info + c <- true } func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Paths = paths - c <- true + paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Paths = paths + c <- true } func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Definitions = def - c <- true + def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Definitions = def + c <- true } func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Parameters = param - c <- true + param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Parameters = param + c <- true } func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Responses = resp - c <- true + resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Responses = resp + c <- true } func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.SecurityDefinitions = sec - c <- true + sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.SecurityDefinitions = sec + c <- true } func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ - Value: tags, - KeyNode: ln, - ValueNode: vn, - } - c <- true + tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ + Value: tags, + KeyNode: ln, + ValueNode: vn, + } + c <- true } func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) { - sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx) - if err != nil { - e <- err - return - } - doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ - Value: sec, - KeyNode: ln, - ValueNode: vn, - } - c <- true + sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx) + if err != nil { + e <- err + return + } + doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: ln, + ValueNode: vn, + } + c <- true } diff --git a/datamodel/low/v2/swagger_test.go b/datamodel/low/v2/swagger_test.go index 169a4bb..19970f9 100644 --- a/datamodel/low/v2/swagger_test.go +++ b/datamodel/low/v2/swagger_test.go @@ -4,333 +4,343 @@ package v2 import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) var doc *Swagger func initTest() { - if doc != nil { - return - } - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - if err != nil { - fmt.Print(err) - panic(err) - } + if doc != nil { + return + } + data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + if err != nil { + fmt.Print(err) + panic(err) + } } func BenchmarkCreateDocument(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - doc, _ = CreateDocument(info) - } + data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + for i := 0; i < b.N; i++ { + doc, _ = CreateDocument(info) + } } func TestCreateDocument(t *testing.T) { - initTest() - doc := doc - assert.Equal(t, "2.0", doc.SpecInfo.Version) - assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value) - assert.Equal(t, "petstore.swagger.io", doc.Host.Value) - assert.Equal(t, "/v2", doc.BasePath.Value) - assert.Len(t, doc.Parameters.Value.Definitions, 1) - assert.Len(t, doc.Tags.Value, 3) - assert.Len(t, doc.Schemes.Value, 2) - assert.Len(t, doc.Definitions.Value.Schemas, 6) - assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3) - assert.Len(t, doc.Paths.Value.PathItems, 15) - assert.Len(t, doc.Responses.Value.Definitions, 2) - assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value) - assert.Equal(t, true, doc.FindExtension("x-pet").Value) - assert.Equal(t, true, doc.FindExtension("X-Pet").Value) - assert.NotNil(t, doc.GetExternalDocs()) + initTest() + doc := doc + assert.Equal(t, "2.0", doc.SpecInfo.Version) + assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value) + assert.Equal(t, "petstore.swagger.io", doc.Host.Value) + assert.Equal(t, "/v2", doc.BasePath.Value) + assert.Len(t, doc.Parameters.Value.Definitions, 1) + assert.Len(t, doc.Tags.Value, 3) + assert.Len(t, doc.Schemes.Value, 2) + assert.Len(t, doc.Definitions.Value.Schemas, 6) + assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3) + assert.Len(t, doc.Paths.Value.PathItems, 15) + assert.Len(t, doc.Responses.Value.Definitions, 2) + assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value) + assert.Equal(t, true, doc.FindExtension("x-pet").Value) + assert.Equal(t, true, doc.FindExtension("X-Pet").Value) + assert.NotNil(t, doc.GetExternalDocs()) } func TestCreateDocument_Info(t *testing.T) { - initTest() - assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value) - assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value) - assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value) + initTest() + assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value) + assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value) + assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value) } func TestCreateDocument_Parameters(t *testing.T) { - initTest() - simpleParam := doc.Parameters.Value.FindParameter("simpleParam") - assert.NotNil(t, simpleParam) - assert.Equal(t, "simple", simpleParam.Value.Name.Value) - assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value) + initTest() + simpleParam := doc.Parameters.Value.FindParameter("simpleParam") + assert.NotNil(t, simpleParam) + assert.Equal(t, "simple", simpleParam.Value.Name.Value) + assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value) } func TestCreateDocument_Tags(t *testing.T) { - initTest() - assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value) - assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) - assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value) - assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value) - assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value) + initTest() + assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value) + assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) + assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value) + assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value) + assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value) } func TestCreateDocument_SecurityDefinitions(t *testing.T) { - initTest() - apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key") - assert.Equal(t, "apiKey", apiKey.Value.Type.Value) - petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth") - assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value) - assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value) - assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2) - assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value) + initTest() + apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key") + assert.Equal(t, "apiKey", apiKey.Value.Type.Value) + petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth") + assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value) + assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value) + assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2) + assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value) } func TestCreateDocument_Definitions(t *testing.T) { - initTest() - apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema() - assert.NotNil(t, apiResp) - assert.Len(t, apiResp.Properties.Value, 3) - assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A) + initTest() + apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema() + assert.NotNil(t, apiResp) + assert.Len(t, apiResp.Properties.Value, 3) + assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A) - pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema() - assert.NotNil(t, pet) - assert.Len(t, pet.Required.Value, 2) + pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema() + assert.NotNil(t, pet) + assert.Len(t, pet.Required.Value, 2) - // perform a deep inline lookup on a schema to ensure chains work - assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value) + // perform a deep inline lookup on a schema to ensure chains work + assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value) - // check enums - assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3) + // check enums + assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3) } func TestCreateDocument_ResponseDefinitions(t *testing.T) { - initTest() - apiResp := doc.Responses.Value.FindResponse("200") - assert.NotNil(t, apiResp) - assert.Equal(t, "OK", apiResp.Value.Description.Value) - assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value) + initTest() + apiResp := doc.Responses.Value.FindResponse("200") + assert.NotNil(t, apiResp) + assert.Equal(t, "OK", apiResp.Value.Description.Value) + assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value) - header := apiResp.Value.FindHeader("noHeader") - assert.NotNil(t, header) - assert.True(t, header.Value.FindExtension("x-empty").Value.(bool)) + header := apiResp.Value.FindHeader("noHeader") + assert.NotNil(t, header) + assert.True(t, header.Value.FindExtension("x-empty").Value.(bool)) - header = apiResp.Value.FindHeader("myHeader") - if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok { - assert.Equal(t, "here", k["something"]) - } else { - panic("should not fail.") - } - if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok { - assert.Len(t, k, 2) - assert.Equal(t, "two", k[1]) - } else { - panic("should not fail.") - } + header = apiResp.Value.FindHeader("myHeader") + if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok { + assert.Equal(t, "here", k["something"]) + } else { + panic("should not fail.") + } + if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok { + assert.Len(t, k, 2) + assert.Equal(t, "two", k[1]) + } else { + panic("should not fail.") + } - header = apiResp.Value.FindHeader("yourHeader") - assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value) + header = apiResp.Value.FindHeader("yourHeader") + assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value) - assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value) + assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value) } func TestCreateDocument_Paths(t *testing.T) { - initTest() - uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value - assert.NotNil(t, uploadImage) - assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow")) - assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value) - assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value) - assert.Equal(t, "successful operation", - uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value) + initTest() + uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value + assert.NotNil(t, uploadImage) + assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow")) + assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value) + assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value) + assert.Equal(t, "successful operation", + uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value) } func TestCreateDocument_Bad(t *testing.T) { - yml := `swagger: + yml := `swagger: $ref: bork` - info, err := datamodel.ExtractSpecInfo([]byte(yml)) - assert.Nil(t, info) - assert.Error(t, err) + info, err := datamodel.ExtractSpecInfo([]byte(yml)) + assert.Nil(t, info) + assert.Error(t, err) } func TestCreateDocument_ExternalDocsBad(t *testing.T) { - yml := `externalDocs: + yml := `externalDocs: $ref: bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_TagsBad(t *testing.T) { - yml := `tags: + yml := `tags: $ref: bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_PathsBad(t *testing.T) { - yml := `paths: + yml := `paths: "/hey": post: responses: "200": $ref: bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_SecurityBad(t *testing.T) { - yml := `security: + yml := `security: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { - yml := `securityDefinitions: + yml := `securityDefinitions: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_ResponsesBad(t *testing.T) { - yml := `responses: + yml := `responses: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_ParametersBad(t *testing.T) { - yml := `parameters: + yml := `parameters: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_DefinitionsBad(t *testing.T) { - yml := `definitions: + yml := `definitions: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) } func TestCreateDocument_InfoBad(t *testing.T) { - yml := `info: + yml := `info: $ref: ` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - wait := true - for wait { - select { - case <-info.JsonParsingChannel: - wait = false - } - } - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + wait := true + for wait { + select { + case <-info.JsonParsingChannel: + wait = false + } + } + assert.Len(t, err, 1) +} + +func TestCircularReferenceError(t *testing.T) { + + data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + circDoc, err := CreateDocument(info) + assert.NotNil(t, circDoc) + assert.Len(t, err, 3) + } diff --git a/datamodel/low/v3/create_document.go b/datamodel/low/v3/create_document.go index 990c520..7e3805c 100644 --- a/datamodel/low/v3/create_document.go +++ b/datamodel/low/v3/create_document.go @@ -1,203 +1,213 @@ package v3 import ( - "errors" - "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/resolver" - "github.com/pb33f/libopenapi/utils" - "sync" + "errors" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/resolver" + "github.com/pb33f/libopenapi/utils" + "sync" ) func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) { - _, labelNode, versionNode := utils.FindKeyNodeFull(OpenAPILabel, info.RootNode.Content) - var version low.NodeReference[string] - if versionNode == nil { - return nil, []error{errors.New("no openapi version/tag found, cannot create document")} - } - version = low.NodeReference[string]{Value: versionNode.Value, KeyNode: labelNode, ValueNode: versionNode} - doc := Document{Version: version} + _, labelNode, versionNode := utils.FindKeyNodeFull(OpenAPILabel, info.RootNode.Content) + var version low.NodeReference[string] + if versionNode == nil { + return nil, []error{errors.New("no openapi version/tag found, cannot create document")} + } + version = low.NodeReference[string]{Value: versionNode.Value, KeyNode: labelNode, ValueNode: versionNode} + doc := Document{Version: version} - // build an index - idx := index.NewSpecIndex(info.RootNode) - doc.Index = idx + // build an index + idx := index.NewSpecIndex(info.RootNode) + doc.Index = idx - // create resolver and check for circular references. - resolve := resolver.NewResolver(idx) - _ = resolve.CheckForCircularReferences() + var errors []error - var wg sync.WaitGroup - var errors []error + // create resolver and check for circular references. + resolve := resolver.NewResolver(idx) + resolvingErrors := resolve.CheckForCircularReferences() - doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) + if len(resolvingErrors) > 0 { + for r := range resolvingErrors { + errors = append(errors, + fmt.Errorf("%s: %s [%d:%d]", resolvingErrors[r].Error.Error(), + resolvingErrors[r].Path, resolvingErrors[r].Node.Line, resolvingErrors[r].Node.Column)) + } + } - // if set, extract jsonSchemaDialect (3.1) - _, dialectLabel, dialectNode := utils.FindKeyNodeFull(JSONSchemaDialectLabel, info.RootNode.Content) - if dialectNode != nil { - doc.JsonSchemaDialect = low.NodeReference[string]{ - Value: dialectNode.Value, KeyNode: dialectLabel, ValueNode: dialectNode} - } + var wg sync.WaitGroup - var runExtraction = func(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex, - runFunc func(i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error, - ers *[]error, - wg *sync.WaitGroup) { + doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) - if er := runFunc(info, doc, idx); er != nil { - *ers = append(*ers, er) - } - wg.Done() - } - extractionFuncs := []func(i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error{ - extractInfo, - extractServers, - extractTags, - extractComponents, - extractSecurity, - extractExternalDocs, - extractPaths, - extractWebhooks, - } + // if set, extract jsonSchemaDialect (3.1) + _, dialectLabel, dialectNode := utils.FindKeyNodeFull(JSONSchemaDialectLabel, info.RootNode.Content) + if dialectNode != nil { + doc.JsonSchemaDialect = low.NodeReference[string]{ + Value: dialectNode.Value, KeyNode: dialectLabel, ValueNode: dialectNode} + } - wg.Add(len(extractionFuncs)) - for _, f := range extractionFuncs { - go runExtraction(info, &doc, idx, f, &errors, &wg) - } - wg.Wait() - return &doc, errors + var runExtraction = func(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex, + runFunc func(i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error, + ers *[]error, + wg *sync.WaitGroup) { + + if er := runFunc(info, doc, idx); er != nil { + *ers = append(*ers, er) + } + wg.Done() + } + extractionFuncs := []func(i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error{ + extractInfo, + extractServers, + extractTags, + extractComponents, + extractSecurity, + extractExternalDocs, + extractPaths, + extractWebhooks, + } + + wg.Add(len(extractionFuncs)) + for _, f := range extractionFuncs { + go runExtraction(info, &doc, idx, f, &errors, &wg) + } + wg.Wait() + return &doc, errors } func extractInfo(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - _, ln, vn := utils.FindKeyNodeFullTop(base.InfoLabel, info.RootNode.Content[0].Content) - if vn != nil { - ir := base.Info{} - _ = low.BuildModel(vn, &ir) - _ = ir.Build(vn, idx) - nr := low.NodeReference[*base.Info]{Value: &ir, ValueNode: vn, KeyNode: ln} - doc.Info = nr - } - return nil + _, ln, vn := utils.FindKeyNodeFullTop(base.InfoLabel, info.RootNode.Content[0].Content) + if vn != nil { + ir := base.Info{} + _ = low.BuildModel(vn, &ir) + _ = ir.Build(vn, idx) + nr := low.NodeReference[*base.Info]{Value: &ir, ValueNode: vn, KeyNode: ln} + doc.Info = nr + } + return nil } func extractSecurity(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, info.RootNode.Content[0], idx) - if err != nil { - return err - } - doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ - Value: sec, - KeyNode: ln, - ValueNode: vn, - } - return nil + sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, info.RootNode.Content[0], idx) + if err != nil { + return err + } + doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{ + Value: sec, + KeyNode: ln, + ValueNode: vn, + } + return nil } func extractExternalDocs(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - extDocs, dErr := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode.Content[0], idx) - if dErr != nil { - return dErr - } - doc.ExternalDocs = extDocs - return nil + extDocs, dErr := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode.Content[0], idx) + if dErr != nil { + return dErr + } + doc.ExternalDocs = extDocs + return nil } func extractComponents(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - _, ln, vn := utils.FindKeyNodeFullTop(ComponentsLabel, info.RootNode.Content[0].Content) - if vn != nil { - ir := Components{} - _ = low.BuildModel(vn, &ir) - err := ir.Build(vn, idx) - if err != nil { - return err - } - nr := low.NodeReference[*Components]{Value: &ir, ValueNode: vn, KeyNode: ln} - doc.Components = nr - } - return nil + _, ln, vn := utils.FindKeyNodeFullTop(ComponentsLabel, info.RootNode.Content[0].Content) + if vn != nil { + ir := Components{} + _ = low.BuildModel(vn, &ir) + err := ir.Build(vn, idx) + if err != nil { + return err + } + nr := low.NodeReference[*Components]{Value: &ir, ValueNode: vn, KeyNode: ln} + doc.Components = nr + } + return nil } func extractServers(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - _, ln, vn := utils.FindKeyNodeFull(ServersLabel, info.RootNode.Content[0].Content) - if vn != nil { - if utils.IsNodeArray(vn) { - var servers []low.ValueReference[*Server] - for _, srvN := range vn.Content { - if utils.IsNodeMap(srvN) { - srvr := Server{} - _ = low.BuildModel(srvN, &srvr) - _ = srvr.Build(srvN, idx) - servers = append(servers, low.ValueReference[*Server]{ - Value: &srvr, - ValueNode: srvN, - }) - } - } - doc.Servers = low.NodeReference[[]low.ValueReference[*Server]]{ - Value: servers, - KeyNode: ln, - ValueNode: vn, - } - } - } - return nil + _, ln, vn := utils.FindKeyNodeFull(ServersLabel, info.RootNode.Content[0].Content) + if vn != nil { + if utils.IsNodeArray(vn) { + var servers []low.ValueReference[*Server] + for _, srvN := range vn.Content { + if utils.IsNodeMap(srvN) { + srvr := Server{} + _ = low.BuildModel(srvN, &srvr) + _ = srvr.Build(srvN, idx) + servers = append(servers, low.ValueReference[*Server]{ + Value: &srvr, + ValueNode: srvN, + }) + } + } + doc.Servers = low.NodeReference[[]low.ValueReference[*Server]]{ + Value: servers, + KeyNode: ln, + ValueNode: vn, + } + } + } + return nil } func extractTags(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - _, ln, vn := utils.FindKeyNodeFull(base.TagsLabel, info.RootNode.Content[0].Content) - if vn != nil { - if utils.IsNodeArray(vn) { - var tags []low.ValueReference[*base.Tag] - for _, tagN := range vn.Content { - if utils.IsNodeMap(tagN) { - tag := base.Tag{} - _ = low.BuildModel(tagN, &tag) - if err := tag.Build(tagN, idx); err != nil { - return err - } - tags = append(tags, low.ValueReference[*base.Tag]{ - Value: &tag, - ValueNode: tagN, - }) - } - } - doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ - Value: tags, - KeyNode: ln, - ValueNode: vn, - } - } - } - return nil + _, ln, vn := utils.FindKeyNodeFull(base.TagsLabel, info.RootNode.Content[0].Content) + if vn != nil { + if utils.IsNodeArray(vn) { + var tags []low.ValueReference[*base.Tag] + for _, tagN := range vn.Content { + if utils.IsNodeMap(tagN) { + tag := base.Tag{} + _ = low.BuildModel(tagN, &tag) + if err := tag.Build(tagN, idx); err != nil { + return err + } + tags = append(tags, low.ValueReference[*base.Tag]{ + Value: &tag, + ValueNode: tagN, + }) + } + } + doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{ + Value: tags, + KeyNode: ln, + ValueNode: vn, + } + } + } + return nil } func extractPaths(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - _, ln, vn := utils.FindKeyNodeFull(PathsLabel, info.RootNode.Content[0].Content) - if vn != nil { - ir := Paths{} - err := ir.Build(vn, idx) - if err != nil { - return err - } - nr := low.NodeReference[*Paths]{Value: &ir, ValueNode: vn, KeyNode: ln} - doc.Paths = nr - } - return nil + _, ln, vn := utils.FindKeyNodeFull(PathsLabel, info.RootNode.Content[0].Content) + if vn != nil { + ir := Paths{} + err := ir.Build(vn, idx) + if err != nil { + return err + } + nr := low.NodeReference[*Paths]{Value: &ir, ValueNode: vn, KeyNode: ln} + doc.Paths = nr + } + return nil } func extractWebhooks(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { - hooks, hooksL, hooksN, eErr := low.ExtractMap[*PathItem](WebhooksLabel, info.RootNode, idx) - if eErr != nil { - return eErr - } - if hooks != nil { - doc.Webhooks = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]{ - Value: hooks, - KeyNode: hooksL, - ValueNode: hooksN, - } - } - return nil + hooks, hooksL, hooksN, eErr := low.ExtractMap[*PathItem](WebhooksLabel, info.RootNode, idx) + if eErr != nil { + return eErr + } + if hooks != nil { + doc.Webhooks = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]{ + Value: hooks, + KeyNode: hooksL, + ValueNode: hooksN, + } + } + return nil } diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 15e4ec5..575ecfa 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -1,510 +1,517 @@ package v3 import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) var doc *Document func initTest() { - if doc != nil { - return - } - data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - var err []error - doc, err = CreateDocument(info) - if err != nil { - panic("broken something") - } + if doc != nil { + return + } + data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + var err []error + doc, err = CreateDocument(info) + if err != nil { + panic("broken something") + } } func BenchmarkCreateDocument(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - doc, _ = CreateDocument(info) - } + data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + for i := 0; i < b.N; i++ { + doc, _ = CreateDocument(info) + } } func BenchmarkCreateDocument_Circular(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/circular-tests.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - _, err := CreateDocument(info) - if err != nil { - panic("this should not error") - } - } + data, _ := ioutil.ReadFile("../../../test_specs/circular-tests.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + for i := 0; i < b.N; i++ { + _, err := CreateDocument(info) + if err != nil { + panic("this should not error") + } + } } func BenchmarkCreateDocument_k8s(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/k8s.json") - info, _ := datamodel.ExtractSpecInfo(data) + data, _ := ioutil.ReadFile("../../../test_specs/k8s.json") + info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { + for i := 0; i < b.N; i++ { - _, err := CreateDocument(info) - if err != nil { - panic("this should not error") - } - } + _, err := CreateDocument(info) + if err != nil { + panic("this should not error") + } + } +} + +func TestCircularReferenceError(t *testing.T) { + + data, _ := ioutil.ReadFile("../../../test_specs/circular-tests.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + circDoc, err := CreateDocument(info) + assert.NotNil(t, circDoc) + assert.Len(t, err, 3) } func BenchmarkCreateDocument_Stripe(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") - info, _ := datamodel.ExtractSpecInfo(data) + data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") + info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - _, err := CreateDocument(info) - if err != nil { - panic("this should not error") - } - } + for i := 0; i < b.N; i++ { + _, err := CreateDocument(info) + if err != nil { + panic("this should not error") + } + } } func BenchmarkCreateDocument_Petstore(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") - info, _ := datamodel.ExtractSpecInfo(data) - for i := 0; i < b.N; i++ { - _, err := CreateDocument(info) - if err != nil { - panic("this should not error") - } - } + data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + info, _ := datamodel.ExtractSpecInfo(data) + for i := 0; i < b.N; i++ { + _, err := CreateDocument(info) + if err != nil { + panic("this should not error") + } + } } func TestCreateDocumentStripe(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") - info, _ := datamodel.ExtractSpecInfo(data) - d, err := CreateDocument(info) - if err != nil { - panic("broken something") - } + data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml") + info, _ := datamodel.ExtractSpecInfo(data) + d, err := CreateDocument(info) + assert.Len(t, err, 21) - assert.Equal(t, "3.0.0", d.Version.Value) - assert.Equal(t, "Stripe API", d.Info.Value.Title.Value) - assert.NotEmpty(t, d.Info.Value.Title.Value) + assert.Equal(t, "3.0.0", d.Version.Value) + assert.Equal(t, "Stripe API", d.Info.Value.Title.Value) + assert.NotEmpty(t, d.Info.Value.Title.Value) } func TestCreateDocument(t *testing.T) { - initTest() - assert.Equal(t, "3.1.0", doc.Version.Value) - assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value) - assert.NotEmpty(t, doc.Info.Value.Title.Value) - assert.Equal(t, "https://pb33f.io/schema", doc.JsonSchemaDialect.Value) + initTest() + assert.Equal(t, "3.1.0", doc.Version.Value) + assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value) + assert.NotEmpty(t, doc.Info.Value.Title.Value) + assert.Equal(t, "https://pb33f.io/schema", doc.JsonSchemaDialect.Value) } func TestCreateDocument_Info(t *testing.T) { - initTest() - assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value) - assert.Equal(t, "pb33f", doc.Info.Value.Contact.Value.Name.Value) - assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.Value) - assert.Equal(t, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value) - assert.Equal(t, "pb33f", doc.Info.Value.License.Value.Name.Value) - assert.Equal(t, "https://pb33f.io/made-up", doc.Info.Value.License.Value.URL.Value) + initTest() + assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value) + assert.Equal(t, "pb33f", doc.Info.Value.Contact.Value.Name.Value) + assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.Value) + assert.Equal(t, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value) + assert.Equal(t, "pb33f", doc.Info.Value.License.Value.Name.Value) + assert.Equal(t, "https://pb33f.io/made-up", doc.Info.Value.License.Value.URL.Value) } func TestCreateDocument_WebHooks(t *testing.T) { - initTest() - assert.Len(t, doc.Webhooks.Value, 1) - for i := range doc.Webhooks.Value { - // a nice deep model should be available for us. - assert.Equal(t, "Information about a new burger", - doc.Webhooks.Value[i].Value.Post.Value.RequestBody.Value.Description.Value) - } + initTest() + assert.Len(t, doc.Webhooks.Value, 1) + for i := range doc.Webhooks.Value { + // a nice deep model should be available for us. + assert.Equal(t, "Information about a new burger", + doc.Webhooks.Value[i].Value.Post.Value.RequestBody.Value.Description.Value) + } } func TestCreateDocument_WebHooks_Error(t *testing.T) { - yml := `webhooks: + yml := `webhooks: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - _, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + _, err = CreateDocument(info) + assert.Len(t, err, 1) } func TestCreateDocument_Servers(t *testing.T) { - initTest() - assert.Len(t, doc.Servers.Value, 2) - server1 := doc.Servers.Value[0].Value - server2 := doc.Servers.Value[1].Value + initTest() + assert.Len(t, doc.Servers.Value, 2) + server1 := doc.Servers.Value[0].Value + server2 := doc.Servers.Value[1].Value - // server 1 - assert.Equal(t, "{scheme}://api.pb33f.io", server1.URL.Value) - assert.NotEmpty(t, server1.Description.Value) - assert.Len(t, server1.Variables.Value, 1) - assert.Len(t, server1.FindVariable("scheme").Value.Enum, 2) - assert.Equal(t, server1.FindVariable("scheme").Value.Default.Value, "https") - assert.NotEmpty(t, server1.FindVariable("scheme").Value.Description.Value) + // server 1 + assert.Equal(t, "{scheme}://api.pb33f.io", server1.URL.Value) + assert.NotEmpty(t, server1.Description.Value) + assert.Len(t, server1.Variables.Value, 1) + assert.Len(t, server1.FindVariable("scheme").Value.Enum, 2) + assert.Equal(t, server1.FindVariable("scheme").Value.Default.Value, "https") + assert.NotEmpty(t, server1.FindVariable("scheme").Value.Description.Value) - // server 2 - assert.Equal(t, "https://{domain}.{host}.com", server2.URL.Value) - assert.NotEmpty(t, server2.Description.Value) - assert.Len(t, server2.Variables.Value, 2) - assert.Equal(t, "api", server2.FindVariable("domain").Value.Default.Value) - assert.NotEmpty(t, server2.FindVariable("domain").Value.Description.Value) - assert.NotEmpty(t, server2.FindVariable("host").Value.Description.Value) - assert.Equal(t, server2.FindVariable("host").Value.Default.Value, "pb33f.io") - assert.Equal(t, "1.2", doc.Info.Value.Version.Value) + // server 2 + assert.Equal(t, "https://{domain}.{host}.com", server2.URL.Value) + assert.NotEmpty(t, server2.Description.Value) + assert.Len(t, server2.Variables.Value, 2) + assert.Equal(t, "api", server2.FindVariable("domain").Value.Default.Value) + assert.NotEmpty(t, server2.FindVariable("domain").Value.Description.Value) + assert.NotEmpty(t, server2.FindVariable("host").Value.Description.Value) + assert.Equal(t, server2.FindVariable("host").Value.Default.Value, "pb33f.io") + assert.Equal(t, "1.2", doc.Info.Value.Version.Value) } func TestCreateDocument_Tags(t *testing.T) { - initTest() - assert.Len(t, doc.Tags.Value, 2) + initTest() + assert.Len(t, doc.Tags.Value, 2) - // tag1 - assert.Equal(t, "Burgers", doc.Tags.Value[0].Value.Name.Value) - assert.NotEmpty(t, doc.Tags.Value[0].Value.Description.Value) - assert.NotNil(t, doc.Tags.Value[0].Value.ExternalDocs.Value) - assert.Equal(t, "https://pb33f.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) - assert.NotEmpty(t, doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) - assert.Len(t, doc.Tags.Value[0].Value.Extensions, 7) + // tag1 + assert.Equal(t, "Burgers", doc.Tags.Value[0].Value.Name.Value) + assert.NotEmpty(t, doc.Tags.Value[0].Value.Description.Value) + assert.NotNil(t, doc.Tags.Value[0].Value.ExternalDocs.Value) + assert.Equal(t, "https://pb33f.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) + assert.NotEmpty(t, doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value) + assert.Len(t, doc.Tags.Value[0].Value.Extensions, 7) - for key, extension := range doc.Tags.Value[0].Value.Extensions { - switch key.Value { - case "x-internal-ting": - assert.Equal(t, "somethingSpecial", extension.Value) - case "x-internal-tong": - assert.Equal(t, int64(1), extension.Value) - case "x-internal-tang": - assert.Equal(t, 1.2, extension.Value) - case "x-internal-tung": - assert.Equal(t, true, extension.Value) - case "x-internal-arr": - assert.Len(t, extension.Value, 2) - assert.Equal(t, "one", extension.Value.([]interface{})[0].(string)) - case "x-internal-arrmap": - assert.Len(t, extension.Value, 2) - assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"]) - case "x-something-else": - // crazy times in the upside down. this API should be avoided for the higher up use cases. - // this is why we will need a higher level API to this model, this looks cool and all, but dude. - assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"]) - } + for key, extension := range doc.Tags.Value[0].Value.Extensions { + switch key.Value { + case "x-internal-ting": + assert.Equal(t, "somethingSpecial", extension.Value) + case "x-internal-tong": + assert.Equal(t, int64(1), extension.Value) + case "x-internal-tang": + assert.Equal(t, 1.2, extension.Value) + case "x-internal-tung": + assert.Equal(t, true, extension.Value) + case "x-internal-arr": + assert.Len(t, extension.Value, 2) + assert.Equal(t, "one", extension.Value.([]interface{})[0].(string)) + case "x-internal-arrmap": + assert.Len(t, extension.Value, 2) + assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"]) + case "x-something-else": + // crazy times in the upside down. this API should be avoided for the higher up use cases. + // this is why we will need a higher level API to this model, this looks cool and all, but dude. + assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"]) + } - } + } - /// tag2 - assert.Equal(t, "Dressing", doc.Tags.Value[1].Value.Name.Value) - assert.NotEmpty(t, doc.Tags.Value[1].Value.Description.Value) - assert.NotNil(t, doc.Tags.Value[1].Value.ExternalDocs.Value) - assert.Equal(t, "https://pb33f.io", doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value) - assert.NotEmpty(t, doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value) - assert.Len(t, doc.Tags.Value[1].Value.Extensions, 0) + /// tag2 + assert.Equal(t, "Dressing", doc.Tags.Value[1].Value.Name.Value) + assert.NotEmpty(t, doc.Tags.Value[1].Value.Description.Value) + assert.NotNil(t, doc.Tags.Value[1].Value.ExternalDocs.Value) + assert.Equal(t, "https://pb33f.io", doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value) + assert.NotEmpty(t, doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value) + assert.Len(t, doc.Tags.Value[1].Value.Extensions, 0) } func TestCreateDocument_Paths(t *testing.T) { - initTest() - assert.Len(t, doc.Paths.Value.PathItems, 5) - burgerId := doc.Paths.Value.FindPath("/burgers/{burgerId}") - assert.NotNil(t, burgerId) - assert.Len(t, burgerId.Value.Get.Value.Parameters.Value, 2) - param := burgerId.Value.Get.Value.Parameters.Value[1] - assert.Equal(t, "burgerHeader", param.Value.Name.Value) - prop := param.Value.Schema.Value.Schema().FindProperty("burgerTheme").Value - assert.Equal(t, "something about a theme goes in here?", prop.Schema().Description.Value) - assert.Equal(t, "big-mac", param.Value.Example.Value) + initTest() + assert.Len(t, doc.Paths.Value.PathItems, 5) + burgerId := doc.Paths.Value.FindPath("/burgers/{burgerId}") + assert.NotNil(t, burgerId) + assert.Len(t, burgerId.Value.Get.Value.Parameters.Value, 2) + param := burgerId.Value.Get.Value.Parameters.Value[1] + assert.Equal(t, "burgerHeader", param.Value.Name.Value) + prop := param.Value.Schema.Value.Schema().FindProperty("burgerTheme").Value + assert.Equal(t, "something about a theme goes in here?", prop.Schema().Description.Value) + assert.Equal(t, "big-mac", param.Value.Example.Value) - // check content - pContent := param.Value.FindContent("application/json") - assert.Equal(t, "somethingNice", pContent.Value.Example.Value) + // check content + pContent := param.Value.FindContent("application/json") + assert.Equal(t, "somethingNice", pContent.Value.Example.Value) - encoding := pContent.Value.FindPropertyEncoding("burgerTheme") - assert.NotNil(t, encoding.Value) - assert.Len(t, encoding.Value.Headers.Value, 1) + encoding := pContent.Value.FindPropertyEncoding("burgerTheme") + assert.NotNil(t, encoding.Value) + assert.Len(t, encoding.Value.Headers.Value, 1) - header := encoding.Value.FindHeader("someHeader") - assert.NotNil(t, header.Value) - assert.Equal(t, "this is a header", header.Value.Description.Value) - assert.Equal(t, "string", header.Value.Schema.Value.Schema().Type.Value.A) + header := encoding.Value.FindHeader("someHeader") + assert.NotNil(t, header.Value) + assert.Equal(t, "this is a header", header.Value.Description.Value) + assert.Equal(t, "string", header.Value.Schema.Value.Schema().Type.Value.A) - // check request body on operation - burgers := doc.Paths.Value.FindPath("/burgers") - assert.NotNil(t, burgers.Value.Post.Value) + // check request body on operation + burgers := doc.Paths.Value.FindPath("/burgers") + assert.NotNil(t, burgers.Value.Post.Value) - burgersPost := burgers.Value.Post.Value - assert.Equal(t, "createBurger", burgersPost.OperationId.Value) - assert.Equal(t, "Create a new burger", burgersPost.Summary.Value) - assert.NotEmpty(t, burgersPost.Description.Value) + burgersPost := burgers.Value.Post.Value + assert.Equal(t, "createBurger", burgersPost.OperationId.Value) + assert.Equal(t, "Create a new burger", burgersPost.Summary.Value) + assert.NotEmpty(t, burgersPost.Description.Value) - requestBody := burgersPost.RequestBody.Value + requestBody := burgersPost.RequestBody.Value - assert.NotEmpty(t, requestBody.Description.Value) - content := requestBody.FindContent("application/json").Value + assert.NotEmpty(t, requestBody.Description.Value) + content := requestBody.FindContent("application/json").Value - assert.NotNil(t, content) - assert.Len(t, content.Schema.Value.Schema().Properties.Value, 4) - assert.Len(t, content.GetAllExamples(), 2) + assert.NotNil(t, content) + assert.Len(t, content.Schema.Value.Schema().Properties.Value, 4) + assert.Len(t, content.GetAllExamples(), 2) - ex := content.FindExample("pbjBurger") - assert.NotNil(t, ex.Value) - assert.NotEmpty(t, ex.Value.Summary.Value) - assert.NotNil(t, ex.Value.Value.Value) + ex := content.FindExample("pbjBurger") + assert.NotNil(t, ex.Value) + assert.NotEmpty(t, ex.Value.Summary.Value) + assert.NotNil(t, ex.Value.Value.Value) - if n, ok := ex.Value.Value.Value.(map[string]interface{}); ok { - assert.Len(t, n, 2) - assert.Equal(t, 3, n["numPatties"]) - } else { - assert.Fail(t, "should easily be convertable. something changed!") - } + if n, ok := ex.Value.Value.Value.(map[string]interface{}); ok { + assert.Len(t, n, 2) + assert.Equal(t, 3, n["numPatties"]) + } else { + assert.Fail(t, "should easily be convertable. something changed!") + } - cb := content.FindExample("cakeBurger") - assert.NotNil(t, cb.Value) - assert.NotEmpty(t, cb.Value.Summary.Value) - assert.NotNil(t, cb.Value.Value.Value) + cb := content.FindExample("cakeBurger") + assert.NotNil(t, cb.Value) + assert.NotEmpty(t, cb.Value.Summary.Value) + assert.NotNil(t, cb.Value.Value.Value) - if n, ok := cb.Value.Value.Value.(map[string]interface{}); ok { - assert.Len(t, n, 2) - assert.Equal(t, "Chocolate Cake Burger", n["name"]) - assert.Equal(t, 5, n["numPatties"]) - } else { - assert.Fail(t, "should easily be convertable. something changed!") - } + if n, ok := cb.Value.Value.Value.(map[string]interface{}); ok { + assert.Len(t, n, 2) + assert.Equal(t, "Chocolate Cake Burger", n["name"]) + assert.Equal(t, 5, n["numPatties"]) + } else { + assert.Fail(t, "should easily be convertable. something changed!") + } - // check responses - responses := burgersPost.Responses.Value - assert.NotNil(t, responses) - assert.Len(t, responses.Codes, 3) + // check responses + responses := burgersPost.Responses.Value + assert.NotNil(t, responses) + assert.Len(t, responses.Codes, 3) - okCode := responses.FindResponseByCode("200") - assert.NotNil(t, okCode.Value) - assert.Equal(t, "A tasty burger for you to eat.", okCode.Value.Description.Value) + okCode := responses.FindResponseByCode("200") + assert.NotNil(t, okCode.Value) + assert.Equal(t, "A tasty burger for you to eat.", okCode.Value.Description.Value) - // check headers are populated - assert.Len(t, okCode.Value.Headers.Value, 1) - okheader := okCode.Value.FindHeader("UseOil") - assert.NotNil(t, okheader.Value) - assert.Equal(t, "this is a header example for UseOil", okheader.Value.Description.Value) + // check headers are populated + assert.Len(t, okCode.Value.Headers.Value, 1) + okheader := okCode.Value.FindHeader("UseOil") + assert.NotNil(t, okheader.Value) + assert.Equal(t, "this is a header example for UseOil", okheader.Value.Description.Value) - respContent := okCode.Value.FindContent("application/json").Value - assert.NotNil(t, respContent) + respContent := okCode.Value.FindContent("application/json").Value + assert.NotNil(t, respContent) - assert.NotNil(t, respContent.Schema.Value) - assert.Len(t, respContent.Schema.Value.Schema().Required.Value, 2) + assert.NotNil(t, respContent.Schema.Value) + assert.Len(t, respContent.Schema.Value.Schema().Required.Value, 2) - respExample := respContent.FindExample("quarterPounder") - assert.NotNil(t, respExample.Value) - assert.NotNil(t, respExample.Value.Value.Value) + respExample := respContent.FindExample("quarterPounder") + assert.NotNil(t, respExample.Value) + assert.NotNil(t, respExample.Value.Value.Value) - if n, ok := respExample.Value.Value.Value.(map[string]interface{}); ok { - assert.Len(t, n, 2) - assert.Equal(t, "Quarter Pounder with Cheese", n["name"]) - assert.Equal(t, 1, n["numPatties"]) - } else { - assert.Fail(t, "should easily be convertable. something changed!") - } + if n, ok := respExample.Value.Value.Value.(map[string]interface{}); ok { + assert.Len(t, n, 2) + assert.Equal(t, "Quarter Pounder with Cheese", n["name"]) + assert.Equal(t, 1, n["numPatties"]) + } else { + assert.Fail(t, "should easily be convertable. something changed!") + } - // check links - links := okCode.Value.Links - assert.NotNil(t, links.Value) - assert.Len(t, links.Value, 2) - assert.Equal(t, "locateBurger", okCode.Value.FindLink("LocateBurger").Value.OperationId.Value) + // check links + links := okCode.Value.Links + assert.NotNil(t, links.Value) + assert.Len(t, links.Value, 2) + assert.Equal(t, "locateBurger", okCode.Value.FindLink("LocateBurger").Value.OperationId.Value) - locateBurger := okCode.Value.FindLink("LocateBurger").Value + locateBurger := okCode.Value.FindLink("LocateBurger").Value - burgerIdParam := locateBurger.FindParameter("burgerId") - assert.NotNil(t, burgerIdParam) - assert.Equal(t, "$response.body#/id", burgerIdParam.Value) + burgerIdParam := locateBurger.FindParameter("burgerId") + assert.NotNil(t, burgerIdParam) + assert.Equal(t, "$response.body#/id", burgerIdParam.Value) - // check security requirements - oAuthReq := burgersPost.FindSecurityRequirement("OAuthScheme") - assert.Len(t, oAuthReq, 2) - assert.Equal(t, "read:burgers", oAuthReq[0].Value) + // check security requirements + oAuthReq := burgersPost.FindSecurityRequirement("OAuthScheme") + assert.Len(t, oAuthReq, 2) + assert.Equal(t, "read:burgers", oAuthReq[0].Value) - servers := burgersPost.Servers.Value - assert.NotNil(t, servers) - assert.Len(t, servers, 1) - assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value) + servers := burgersPost.Servers.Value + assert.NotNil(t, servers) + assert.Len(t, servers, 1) + assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value) } func TestCreateDocument_Components_Schemas(t *testing.T) { - initTest() + initTest() - components := doc.Components.Value - assert.NotNil(t, components) - assert.Len(t, components.Schemas.Value, 6) + components := doc.Components.Value + assert.NotNil(t, components) + assert.Len(t, components.Schemas.Value, 6) - burger := components.FindSchema("Burger").Value - assert.NotNil(t, burger) - assert.Equal(t, "The tastiest food on the planet you would love to eat everyday", burger.Schema().Description.Value) + burger := components.FindSchema("Burger").Value + assert.NotNil(t, burger) + assert.Equal(t, "The tastiest food on the planet you would love to eat everyday", burger.Schema().Description.Value) - er := components.FindSchema("Error") - assert.NotNil(t, er.Value) - assert.Equal(t, "Error defining what went wrong when providing a specification. The message should help "+ - "indicate the issue clearly.", er.Value.Schema().Description.Value) + er := components.FindSchema("Error") + assert.NotNil(t, er.Value) + assert.Equal(t, "Error defining what went wrong when providing a specification. The message should help "+ + "indicate the issue clearly.", er.Value.Schema().Description.Value) - fries := components.FindSchema("Fries") - assert.NotNil(t, fries.Value) + fries := components.FindSchema("Fries") + assert.NotNil(t, fries.Value) - assert.Len(t, fries.Value.Schema().Properties.Value, 3) - p := fries.Value.Schema().FindProperty("favoriteDrink") - assert.Equal(t, "a frosty cold beverage can be coke or sprite", - p.Value.Schema().Description.Value) + assert.Len(t, fries.Value.Schema().Properties.Value, 3) + p := fries.Value.Schema().FindProperty("favoriteDrink") + assert.Equal(t, "a frosty cold beverage can be coke or sprite", + p.Value.Schema().Description.Value) } func TestCreateDocument_Components_SecuritySchemes(t *testing.T) { - initTest() - components := doc.Components.Value - securitySchemes := components.SecuritySchemes.Value - assert.Len(t, securitySchemes, 3) + initTest() + components := doc.Components.Value + securitySchemes := components.SecuritySchemes.Value + assert.Len(t, securitySchemes, 3) - apiKey := components.FindSecurityScheme("APIKeyScheme").Value - assert.NotNil(t, apiKey) - assert.Equal(t, "an apiKey security scheme", apiKey.Description.Value) + apiKey := components.FindSecurityScheme("APIKeyScheme").Value + assert.NotNil(t, apiKey) + assert.Equal(t, "an apiKey security scheme", apiKey.Description.Value) - oAuth := components.FindSecurityScheme("OAuthScheme").Value - assert.NotNil(t, oAuth) - assert.Equal(t, "an oAuth security scheme", oAuth.Description.Value) - assert.NotNil(t, oAuth.Flows.Value.Implicit.Value) - assert.NotNil(t, oAuth.Flows.Value.AuthorizationCode.Value) + oAuth := components.FindSecurityScheme("OAuthScheme").Value + assert.NotNil(t, oAuth) + assert.Equal(t, "an oAuth security scheme", oAuth.Description.Value) + assert.NotNil(t, oAuth.Flows.Value.Implicit.Value) + assert.NotNil(t, oAuth.Flows.Value.AuthorizationCode.Value) - scopes := oAuth.Flows.Value.Implicit.Value.Scopes.Value - assert.NotNil(t, scopes) + scopes := oAuth.Flows.Value.Implicit.Value.Scopes.Value + assert.NotNil(t, scopes) - readScope := oAuth.Flows.Value.Implicit.Value.FindScope("write:burgers") - assert.NotNil(t, readScope) - assert.Equal(t, "modify and add new burgers", readScope.Value) + readScope := oAuth.Flows.Value.Implicit.Value.FindScope("write:burgers") + assert.NotNil(t, readScope) + assert.Equal(t, "modify and add new burgers", readScope.Value) - readScope = oAuth.Flows.Value.AuthorizationCode.Value.FindScope("write:burgers") - assert.NotNil(t, readScope) - assert.Equal(t, "modify burgers and stuff", readScope.Value) + readScope = oAuth.Flows.Value.AuthorizationCode.Value.FindScope("write:burgers") + assert.NotNil(t, readScope) + assert.Equal(t, "modify burgers and stuff", readScope.Value) } func TestCreateDocument_Components_Responses(t *testing.T) { - initTest() - components := doc.Components.Value - responses := components.Responses.Value - assert.Len(t, responses, 1) + initTest() + components := doc.Components.Value + responses := components.Responses.Value + assert.Len(t, responses, 1) - dressingResponse := components.FindResponse("DressingResponse") - assert.NotNil(t, dressingResponse.Value) - assert.Equal(t, "all the dressings for a burger.", dressingResponse.Value.Description.Value) - assert.Len(t, dressingResponse.Value.Content.Value, 1) + dressingResponse := components.FindResponse("DressingResponse") + assert.NotNil(t, dressingResponse.Value) + assert.Equal(t, "all the dressings for a burger.", dressingResponse.Value.Description.Value) + assert.Len(t, dressingResponse.Value.Content.Value, 1) } func TestCreateDocument_Components_Examples(t *testing.T) { - initTest() - components := doc.Components.Value - examples := components.Examples.Value - assert.Len(t, examples, 1) + initTest() + components := doc.Components.Value + examples := components.Examples.Value + assert.Len(t, examples, 1) - quarterPounder := components.FindExample("QuarterPounder") - assert.NotNil(t, quarterPounder.Value) - assert.Equal(t, "A juicy two hander sammich", quarterPounder.Value.Summary.Value) - assert.NotNil(t, quarterPounder.Value.Value.Value) + quarterPounder := components.FindExample("QuarterPounder") + assert.NotNil(t, quarterPounder.Value) + assert.Equal(t, "A juicy two hander sammich", quarterPounder.Value.Summary.Value) + assert.NotNil(t, quarterPounder.Value.Value.Value) } func TestCreateDocument_Components_RequestBodies(t *testing.T) { - initTest() - components := doc.Components.Value - requestBodies := components.RequestBodies.Value - assert.Len(t, requestBodies, 1) + initTest() + components := doc.Components.Value + requestBodies := components.RequestBodies.Value + assert.Len(t, requestBodies, 1) - burgerRequest := components.FindRequestBody("BurgerRequest") - assert.NotNil(t, burgerRequest.Value) - assert.Equal(t, "Give us the new burger!", burgerRequest.Value.Description.Value) - assert.Len(t, burgerRequest.Value.Content.Value, 1) + burgerRequest := components.FindRequestBody("BurgerRequest") + assert.NotNil(t, burgerRequest.Value) + assert.Equal(t, "Give us the new burger!", burgerRequest.Value.Description.Value) + assert.Len(t, burgerRequest.Value.Content.Value, 1) } func TestCreateDocument_Components_Headers(t *testing.T) { - initTest() - components := doc.Components.Value - headers := components.Headers.Value - assert.Len(t, headers, 1) + initTest() + components := doc.Components.Value + headers := components.Headers.Value + assert.Len(t, headers, 1) - useOil := components.FindHeader("UseOil") - assert.NotNil(t, useOil.Value) - assert.Equal(t, "this is a header example for UseOil", useOil.Value.Description.Value) - assert.Equal(t, "string", useOil.Value.Schema.Value.Schema().Type.Value.A) + useOil := components.FindHeader("UseOil") + assert.NotNil(t, useOil.Value) + assert.Equal(t, "this is a header example for UseOil", useOil.Value.Description.Value) + assert.Equal(t, "string", useOil.Value.Schema.Value.Schema().Type.Value.A) } func TestCreateDocument_Components_Links(t *testing.T) { - initTest() - components := doc.Components.Value - links := components.Links.Value - assert.Len(t, links, 2) + initTest() + components := doc.Components.Value + links := components.Links.Value + assert.Len(t, links, 2) - locateBurger := components.FindLink("LocateBurger") - assert.NotNil(t, locateBurger.Value) - assert.Equal(t, "Go and get a tasty burger", locateBurger.Value.Description.Value) + locateBurger := components.FindLink("LocateBurger") + assert.NotNil(t, locateBurger.Value) + assert.Equal(t, "Go and get a tasty burger", locateBurger.Value.Description.Value) - anotherLocateBurger := components.FindLink("AnotherLocateBurger") - assert.NotNil(t, anotherLocateBurger.Value) - assert.Equal(t, "Go and get another really tasty burger", anotherLocateBurger.Value.Description.Value) + anotherLocateBurger := components.FindLink("AnotherLocateBurger") + assert.NotNil(t, anotherLocateBurger.Value) + assert.Equal(t, "Go and get another really tasty burger", anotherLocateBurger.Value.Description.Value) } func TestCreateDocument_Doc_Security(t *testing.T) { - initTest() - d := doc - oAuth := d.FindSecurityRequirement("OAuthScheme") - assert.Len(t, oAuth, 2) + initTest() + d := doc + oAuth := d.FindSecurityRequirement("OAuthScheme") + assert.Len(t, oAuth, 2) } func TestCreateDocument_Callbacks(t *testing.T) { - initTest() - callbacks := doc.Components.Value.Callbacks.Value - assert.Len(t, callbacks, 1) + initTest() + callbacks := doc.Components.Value.Callbacks.Value + assert.Len(t, callbacks, 1) - bCallback := doc.Components.Value.FindCallback("BurgerCallback") - assert.NotNil(t, bCallback.Value) - assert.Len(t, callbacks, 1) + bCallback := doc.Components.Value.FindCallback("BurgerCallback") + assert.NotNil(t, bCallback.Value) + assert.Len(t, callbacks, 1) - exp := bCallback.Value.FindExpression("{$request.query.queryUrl}") - assert.NotNil(t, exp.Value) - assert.NotNil(t, exp.Value.Post.Value) - assert.Equal(t, "Callback payload", exp.Value.Post.Value.RequestBody.Value.Description.Value) + exp := bCallback.Value.FindExpression("{$request.query.queryUrl}") + assert.NotNil(t, exp.Value) + assert.NotNil(t, exp.Value.Post.Value) + assert.Equal(t, "Callback payload", exp.Value.Post.Value.RequestBody.Value.Description.Value) } func TestCreateDocument_Component_Discriminator(t *testing.T) { - initTest() + initTest() - components := doc.Components.Value - dsc := components.FindSchema("Drink").Value.Schema().Discriminator.Value - assert.NotNil(t, dsc) - assert.Equal(t, "drinkType", dsc.PropertyName.Value) - assert.Equal(t, "some value", dsc.FindMappingValue("drink").Value) - assert.Nil(t, dsc.FindMappingValue("don't exist")) - assert.NotNil(t, doc.GetExternalDocs()) - assert.Nil(t, doc.FindSecurityRequirement("scooby doo")) + components := doc.Components.Value + dsc := components.FindSchema("Drink").Value.Schema().Discriminator.Value + assert.NotNil(t, dsc) + assert.Equal(t, "drinkType", dsc.PropertyName.Value) + assert.Equal(t, "some value", dsc.FindMappingValue("drink").Value) + assert.Nil(t, dsc.FindMappingValue("don't exist")) + assert.NotNil(t, doc.GetExternalDocs()) + assert.Nil(t, doc.FindSecurityRequirement("scooby doo")) } func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) { - initTest() - components := doc.Components.Value - d := components.FindSchema("Dressing") - assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) - if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.SchemaProxy); ok { - assert.Equal(t, "something in here.", n.Schema().Description.Value) - } else { - assert.Fail(t, "should be a schema") - } + initTest() + components := doc.Components.Value + d := components.FindSchema("Dressing") + assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) + if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.SchemaProxy); ok { + assert.Equal(t, "something in here.", n.Schema().Description.Value) + } else { + assert.Fail(t, "should be a schema") + } } func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) { - initTest() - components := doc.Components.Value - d := components.FindSchema("Drink") - assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) - assert.True(t, d.Value.Schema().AdditionalProperties.Value.(bool)) + initTest() + components := doc.Components.Value + d := components.FindSchema("Drink") + assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) + assert.True(t, d.Value.Schema().AdditionalProperties.Value.(bool)) } func TestCreateDocument_Components_Error(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 components: schemas: bork: @@ -512,108 +519,108 @@ components: bark: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - assert.Len(t, err, 0) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + assert.Len(t, err, 0) - ob := doc.Components.Value.FindSchema("bork").Value - ob.Schema() - assert.Error(t, ob.GetBuildError()) + ob := doc.Components.Value.FindSchema("bork").Value + ob.Schema() + assert.Error(t, ob.GetBuildError()) } func TestCreateDocument_Webhooks_Error(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 webhooks: aHook: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - doc, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + assert.Len(t, err, 1) } func TestCreateDocument_Components_Error_Extract(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 components: parameters: bork: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - _, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + _, err = CreateDocument(info) + assert.Len(t, err, 1) } func TestCreateDocument_Paths_Errors(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 paths: /p: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - _, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + _, err = CreateDocument(info) + assert.Len(t, err, 1) } func TestCreateDocument_Tags_Errors(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 tags: - $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - _, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + _, err = CreateDocument(info) + assert.Len(t, err, 1) } func TestCreateDocument_Security_Error(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 security: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - _, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + _, err = CreateDocument(info) + assert.Len(t, err, 1) } func TestCreateDocument_ExternalDoc_Error(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 externalDocs: $ref: #bork` - info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error - _, err = CreateDocument(info) - assert.Len(t, err, 1) + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + _, err = CreateDocument(info) + assert.Len(t, err, 1) } func ExampleCreateDocument() { - // How to create a low-level OpenAPI 3 Document + // How to create a low-level OpenAPI 3 Document - // load petstore into bytes - petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + // load petstore into bytes + petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") - // read in specification - info, _ := datamodel.ExtractSpecInfo(petstoreBytes) + // read in specification + info, _ := datamodel.ExtractSpecInfo(petstoreBytes) - // build low-level document model - document, errors := CreateDocument(info) + // build low-level document model + document, errors := CreateDocument(info) - // if something went wrong, a slice of errors is returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %s\n", errors[i].Error()) - } - panic("cannot build document") - } + // if something went wrong, a slice of errors is returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %s\n", errors[i].Error()) + } + panic("cannot build document") + } - // print out email address from the info > contact object. - fmt.Print(document.Info.Value.Contact.Value.Email.Value) - // Output: apiteam@swagger.io + // print out email address from the info > contact object. + fmt.Print(document.Info.Value.Contact.Value.Email.Value) + // Output: apiteam@swagger.io } diff --git a/document_test.go b/document_test.go index bee9725..48ef9b4 100644 --- a/document_test.go +++ b/document_test.go @@ -4,303 +4,303 @@ package libopenapi import ( - "fmt" - "github.com/pb33f/libopenapi/utils" - "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" + "fmt" + "github.com/pb33f/libopenapi/utils" + "github.com/stretchr/testify/assert" + "io/ioutil" + "testing" ) func TestLoadDocument_Simple_V2(t *testing.T) { - yml := `swagger: 2.0.1` - doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) - assert.Equal(t, "2.0.1", doc.GetVersion()) + yml := `swagger: 2.0.1` + doc, err := NewDocument([]byte(yml)) + assert.NoError(t, err) + assert.Equal(t, "2.0.1", doc.GetVersion()) - v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, docErr, 0) - assert.NotNil(t, v2Doc) - assert.NotNil(t, doc.GetSpecInfo()) + v2Doc, docErr := doc.BuildV2Model() + assert.Len(t, docErr, 0) + assert.NotNil(t, v2Doc) + assert.NotNil(t, doc.GetSpecInfo()) - fmt.Print() + fmt.Print() } func TestLoadDocument_Simple_V2_Error(t *testing.T) { - yml := `swagger: 2.0` - doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) + yml := `swagger: 2.0` + doc, err := NewDocument([]byte(yml)) + assert.NoError(t, err) - v2Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 1) - assert.Nil(t, v2Doc) + v2Doc, docErr := doc.BuildV3Model() + assert.Len(t, docErr, 1) + assert.Nil(t, v2Doc) } func TestLoadDocument_Simple_V2_Error_BadSpec(t *testing.T) { - yml := `swagger: 2.0 + yml := `swagger: 2.0 definitions: thing: $ref: bork` - doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) + doc, err := NewDocument([]byte(yml)) + assert.NoError(t, err) - v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, docErr, 1) - assert.Nil(t, v2Doc) + v2Doc, docErr := doc.BuildV2Model() + assert.Len(t, docErr, 2) + assert.Nil(t, v2Doc) } func TestLoadDocument_Simple_V3_Error(t *testing.T) { - yml := `openapi: 3.0.1` - doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) + yml := `openapi: 3.0.1` + doc, err := NewDocument([]byte(yml)) + assert.NoError(t, err) - v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, docErr, 1) - assert.Nil(t, v2Doc) + v2Doc, docErr := doc.BuildV2Model() + assert.Len(t, docErr, 1) + assert.Nil(t, v2Doc) } func TestLoadDocument_Error_V2NoSpec(t *testing.T) { - doc := new(document) // not how this should be instantiated. - _, err := doc.BuildV2Model() - assert.Len(t, err, 1) + doc := new(document) // not how this should be instantiated. + _, err := doc.BuildV2Model() + assert.Len(t, err, 1) } func TestLoadDocument_Error_V3NoSpec(t *testing.T) { - doc := new(document) // not how this should be instantiated. - _, err := doc.BuildV3Model() - assert.Len(t, err, 1) + doc := new(document) // not how this should be instantiated. + _, err := doc.BuildV3Model() + assert.Len(t, err, 1) } func TestLoadDocument_Empty(t *testing.T) { - yml := `` - _, err := NewDocument([]byte(yml)) - assert.Error(t, err) + yml := `` + _, err := NewDocument([]byte(yml)) + assert.Error(t, err) } func TestLoadDocument_Simple_V3(t *testing.T) { - yml := `openapi: 3.0.1` - doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) - assert.Equal(t, "3.0.1", doc.GetVersion()) + yml := `openapi: 3.0.1` + doc, err := NewDocument([]byte(yml)) + assert.NoError(t, err) + assert.Equal(t, "3.0.1", doc.GetVersion()) - v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 0) - assert.NotNil(t, v3Doc) + v3Doc, docErr := doc.BuildV3Model() + assert.Len(t, docErr, 0) + assert.NotNil(t, v3Doc) } func TestLoadDocument_Simple_V3_Error_BadSpec(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 paths: "/some": $ref: bork` - doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) + doc, err := NewDocument([]byte(yml)) + assert.NoError(t, err) - v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 1) - assert.Nil(t, v3Doc) + v3Doc, docErr := doc.BuildV3Model() + assert.Len(t, docErr, 1) + assert.Nil(t, v3Doc) } func TestDocument_Serialize_Error(t *testing.T) { - doc := new(document) // not how this should be instantiated. - _, err := doc.Serialize() - assert.Error(t, err) + doc := new(document) // not how this should be instantiated. + _, err := doc.Serialize() + assert.Error(t, err) } func TestDocument_Serialize(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 info: title: The magic API ` - doc, _ := NewDocument([]byte(yml)) - serial, err := doc.Serialize() - assert.NoError(t, err) - assert.Equal(t, yml, string(serial)) + doc, _ := NewDocument([]byte(yml)) + serial, err := doc.Serialize() + assert.NoError(t, err) + assert.Equal(t, yml, string(serial)) } func TestDocument_Serialize_Modified(t *testing.T) { - yml := `openapi: 3.0 + yml := `openapi: 3.0 info: title: The magic API ` - ymlModified := `openapi: 3.0 + ymlModified := `openapi: 3.0 info: title: The magic API - but now, altered! ` - doc, _ := NewDocument([]byte(yml)) + doc, _ := NewDocument([]byte(yml)) - v3Doc, _ := doc.BuildV3Model() + v3Doc, _ := doc.BuildV3Model() - v3Doc.Model.Info.GoLow().Title.Mutate("The magic API - but now, altered!") + v3Doc.Model.Info.GoLow().Title.Mutate("The magic API - but now, altered!") - serial, err := doc.Serialize() - assert.NoError(t, err) - assert.Equal(t, ymlModified, string(serial)) + serial, err := doc.Serialize() + assert.NoError(t, err) + assert.Equal(t, ymlModified, string(serial)) } func TestDocument_Serialize_JSON_Modified(t *testing.T) { - json := `{ 'openapi': '3.0', + json := `{ 'openapi': '3.0', 'info': { 'title': 'The magic API' } } ` - jsonModified := `{"info":{"title":"The magic API - but now, altered!"},"openapi":"3.0"}` - doc, _ := NewDocument([]byte(json)) + jsonModified := `{"info":{"title":"The magic API - but now, altered!"},"openapi":"3.0"}` + doc, _ := NewDocument([]byte(json)) - v3Doc, _ := doc.BuildV3Model() + v3Doc, _ := doc.BuildV3Model() - // eventually this will be encapsulated up high. - // mutation does not replace low model, eventually pointers will be used. - newTitle := v3Doc.Model.Info.GoLow().Title.Mutate("The magic API - but now, altered!") - v3Doc.Model.Info.GoLow().Title = newTitle + // eventually this will be encapsulated up high. + // mutation does not replace low model, eventually pointers will be used. + newTitle := v3Doc.Model.Info.GoLow().Title.Mutate("The magic API - but now, altered!") + v3Doc.Model.Info.GoLow().Title = newTitle - assert.Equal(t, "The magic API - but now, altered!", v3Doc.Model.Info.GoLow().Title.Value) + assert.Equal(t, "The magic API - but now, altered!", v3Doc.Model.Info.GoLow().Title.Value) - serial, err := doc.Serialize() - assert.NoError(t, err) - assert.Equal(t, jsonModified, string(serial)) + serial, err := doc.Serialize() + assert.NoError(t, err) + assert.Equal(t, jsonModified, string(serial)) } func ExampleNewDocument_fromOpenAPI3Document() { - // How to read in an OpenAPI 3 Specification, into a Document. + // How to read in an OpenAPI 3 Specification, into a Document. - // load an OpenAPI 3 specification from bytes - petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json") + // load an OpenAPI 3 specification from bytes + petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json") - // create a new document from specification bytes - document, err := NewDocument(petstore) + // create a new document from specification bytes + document, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := document.BuildV3Model() + // because we know this is a v3 spec, we can build a ready to go model from it. + v3Model, errors := document.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // get a count of the number of paths and schemas. - paths := len(v3Model.Model.Paths.PathItems) - schemas := len(v3Model.Model.Components.Schemas) + // get a count of the number of paths and schemas. + paths := len(v3Model.Model.Paths.PathItems) + schemas := len(v3Model.Model.Components.Schemas) - // print the number of paths and schemas in the document - fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) - // Output: There are 13 paths and 8 schemas in the document + // print the number of paths and schemas in the document + fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) + // Output: There are 13 paths and 8 schemas in the document } func ExampleNewDocument_fromSwaggerDocument() { - // How to read in a Swagger / OpenAPI 2 Specification, into a Document. + // How to read in a Swagger / OpenAPI 2 Specification, into a Document. - // load a Swagger specification from bytes - petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json") + // load a Swagger specification from bytes + petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json") - // create a new document from specification bytes - document, err := NewDocument(petstore) + // create a new document from specification bytes + document, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v2 spec, we can build a ready to go model from it. - v2Model, errors := document.BuildV2Model() + // because we know this is a v2 spec, we can build a ready to go model from it. + v2Model, errors := document.BuildV2Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // get a count of the number of paths and schemas. - paths := len(v2Model.Model.Paths.PathItems) - schemas := len(v2Model.Model.Definitions.Definitions) + // get a count of the number of paths and schemas. + paths := len(v2Model.Model.Paths.PathItems) + schemas := len(v2Model.Model.Definitions.Definitions) - // print the number of paths and schemas in the document - fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) - // Output: There are 14 paths and 6 schemas in the document + // print the number of paths and schemas in the document + fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) + // Output: There are 14 paths and 6 schemas in the document } func ExampleNewDocument_fromUnknownVersion() { - // load an unknown version of an OpenAPI spec - petstore, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") + // load an unknown version of an OpenAPI spec + petstore, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") - // create a new document from specification bytes - document, err := NewDocument(petstore) + // create a new document from specification bytes + document, err := NewDocument(petstore) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - var paths, schemas int - var errors []error + var paths, schemas int + var errors []error - // We don't know which type of document this is, so we can use the spec info to inform us - if document.GetSpecInfo().SpecType == utils.OpenApi3 { - v3Model, errs := document.BuildV3Model() - if len(errs) > 0 { - errors = errs - } - if len(errors) <= 0 { - paths = len(v3Model.Model.Paths.PathItems) - schemas = len(v3Model.Model.Components.Schemas) - } - } - if document.GetSpecInfo().SpecType == utils.OpenApi2 { - v2Model, errs := document.BuildV2Model() - if len(errs) > 0 { - errors = errs - } - if len(errors) <= 0 { - paths = len(v2Model.Model.Paths.PathItems) - schemas = len(v2Model.Model.Definitions.Definitions) - } - } + // We don't know which type of document this is, so we can use the spec info to inform us + if document.GetSpecInfo().SpecType == utils.OpenApi3 { + v3Model, errs := document.BuildV3Model() + if len(errs) > 0 { + errors = errs + } + if len(errors) <= 0 { + paths = len(v3Model.Model.Paths.PathItems) + schemas = len(v3Model.Model.Components.Schemas) + } + } + if document.GetSpecInfo().SpecType == utils.OpenApi2 { + v2Model, errs := document.BuildV2Model() + if len(errs) > 0 { + errors = errs + } + if len(errors) <= 0 { + paths = len(v2Model.Model.Paths.PathItems) + schemas = len(v2Model.Model.Definitions.Definitions) + } + } - // if anything went wrong when building the model, report errors. - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the model, report errors. + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // print the number of paths and schemas in the document - fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) - // Output: There are 5 paths and 6 schemas in the document + // print the number of paths and schemas in the document + fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) + // Output: There are 5 paths and 6 schemas in the document } func ExampleNewDocument_mutateValuesAndSerialize() { - // How to mutate values in an OpenAPI Specification, without re-ordering original content. + // How to mutate values in an OpenAPI Specification, without re-ordering original content. - // create very small, and useless spec that does nothing useful, except showcase this feature. - spec := ` + // create very small, and useless spec that does nothing useful, except showcase this feature. + spec := ` openapi: 3.1.0 info: title: This is a title @@ -310,155 +310,155 @@ info: license: url: http://some-place-on-the-internet.com/license ` - // create a new document from specification bytes - document, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + document, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := document.BuildV3Model() + // because we know this is a v3 spec, we can build a ready to go model from it. + v3Model, errors := document.BuildV3Model() - // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %e\n", errors[i]) - } - panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) - } + // if anything went wrong when building the v3 model, a slice of errors will be returned + if len(errors) > 0 { + for i := range errors { + fmt.Printf("error: %e\n", errors[i]) + } + panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors))) + } - // mutate the title, to do this we currently need to drop down to the low-level API. - v3Model.Model.GoLow().Info.Value.Title.Mutate("A new title for a useless spec") + // mutate the title, to do this we currently need to drop down to the low-level API. + v3Model.Model.GoLow().Info.Value.Title.Mutate("A new title for a useless spec") - // mutate the email address in the contact object. - v3Model.Model.GoLow().Info.Value.Contact.Value.Email.Mutate("buckaroo@pb33f.io") + // mutate the email address in the contact object. + v3Model.Model.GoLow().Info.Value.Contact.Value.Email.Mutate("buckaroo@pb33f.io") - // mutate the name in the contact object. - v3Model.Model.GoLow().Info.Value.Contact.Value.Name.Mutate("Buckaroo") + // mutate the name in the contact object. + v3Model.Model.GoLow().Info.Value.Contact.Value.Name.Mutate("Buckaroo") - // mutate the URL for the license object. - v3Model.Model.GoLow().Info.Value.License.Value.URL.Mutate("https://pb33f.io/license") + // mutate the URL for the license object. + v3Model.Model.GoLow().Info.Value.License.Value.URL.Mutate("https://pb33f.io/license") - // serialize the document back into the original YAML or JSON - mutatedSpec, serialError := document.Serialize() + // serialize the document back into the original YAML or JSON + mutatedSpec, serialError := document.Serialize() - // if something went wrong serializing - if serialError != nil { - panic(fmt.Sprintf("cannot serialize document: %e", serialError)) - } + // if something went wrong serializing + if serialError != nil { + panic(fmt.Sprintf("cannot serialize document: %e", serialError)) + } - // print our modified spec! - fmt.Println(string(mutatedSpec)) - // Output: openapi: 3.1.0 - //info: - // title: A new title for a useless spec - // contact: - // name: Buckaroo - // email: buckaroo@pb33f.io - // license: - // url: https://pb33f.io/license + // print our modified spec! + fmt.Println(string(mutatedSpec)) + // Output: openapi: 3.1.0 + //info: + // title: A new title for a useless spec + // contact: + // name: Buckaroo + // email: buckaroo@pb33f.io + // license: + // url: https://pb33f.io/license } func ExampleCompareDocuments_openAPI() { - // How to compare two different OpenAPI specifications. + // How to compare two different OpenAPI specifications. - // load an original OpenAPI 3 specification from bytes - burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") + // load an original OpenAPI 3 specification from bytes + burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml") - // load an **updated** OpenAPI 3 specification from bytes - burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml") + // load an **updated** OpenAPI 3 specification from bytes + burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml") - // create a new document from original specification bytes - originalDoc, err := NewDocument(burgerShopOriginal) + // create a new document from original specification bytes + originalDoc, err := NewDocument(burgerShopOriginal) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // create a new document from updated specification bytes - updatedDoc, err := NewDocument(burgerShopUpdated) + // create a new document from updated specification bytes + updatedDoc, err := NewDocument(burgerShopUpdated) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // Compare documents for all changes made - documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) + // Compare documents for all changes made + documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) - // If anything went wrong when building models for documents. - if len(errs) > 0 { - for i := range errs { - fmt.Printf("error: %e\n", errs[i]) - } - panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) - } + // If anything went wrong when building models for documents. + if len(errs) > 0 { + for i := range errs { + fmt.Printf("error: %e\n", errs[i]) + } + panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) + } - // Extract SchemaChanges from components changes. - schemaChanges := documentChanges.ComponentsChanges.SchemaChanges + // Extract SchemaChanges from components changes. + schemaChanges := documentChanges.ComponentsChanges.SchemaChanges - // Print out some interesting stats about the OpenAPI document changes. - fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", - documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) - //Output: There are 67 changes, of which 17 are breaking. 5 schemas have changes. + // Print out some interesting stats about the OpenAPI document changes. + fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", + documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) + //Output: There are 67 changes, of which 17 are breaking. 5 schemas have changes. } func ExampleCompareDocuments_swagger() { - // How to compare two different Swagger specifications. + // How to compare two different Swagger specifications. - // load an original OpenAPI 3 specification from bytes - petstoreOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-complete.yaml") + // load an original OpenAPI 3 specification from bytes + petstoreOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-complete.yaml") - // load an **updated** OpenAPI 3 specification from bytes - petstoreUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-complete-modified.yaml") + // load an **updated** OpenAPI 3 specification from bytes + petstoreUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-complete-modified.yaml") - // create a new document from original specification bytes - originalDoc, err := NewDocument(petstoreOriginal) + // create a new document from original specification bytes + originalDoc, err := NewDocument(petstoreOriginal) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // create a new document from updated specification bytes - updatedDoc, err := NewDocument(petstoreUpdated) + // create a new document from updated specification bytes + updatedDoc, err := NewDocument(petstoreUpdated) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } - // Compare documents for all changes made - documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) + // Compare documents for all changes made + documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) - // If anything went wrong when building models for documents. - if len(errs) > 0 { - for i := range errs { - fmt.Printf("error: %e\n", errs[i]) - } - panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) - } + // If anything went wrong when building models for documents. + if len(errs) > 0 { + for i := range errs { + fmt.Printf("error: %e\n", errs[i]) + } + panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) + } - // Extract SchemaChanges from components changes. - schemaChanges := documentChanges.ComponentsChanges.SchemaChanges + // Extract SchemaChanges from components changes. + schemaChanges := documentChanges.ComponentsChanges.SchemaChanges - // Print out some interesting stats about the Swagger document changes. - fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", - documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) - //Output: There are 52 changes, of which 27 are breaking. 5 schemas have changes. + // Print out some interesting stats about the Swagger document changes. + fmt.Printf("There are %d changes, of which %d are breaking. %v schemas have changes.", + documentChanges.TotalChanges(), documentChanges.TotalBreakingChanges(), len(schemaChanges)) + //Output: There are 52 changes, of which 27 are breaking. 5 schemas have changes. } func TestDocument_Paths_As_Array(t *testing.T) { - // paths can now be wrapped in an array. - spec := `{ + // paths can now be wrapped in an array. + spec := `{ "openapi": "3.1.0", "paths": [ "/": { @@ -467,13 +467,13 @@ func TestDocument_Paths_As_Array(t *testing.T) { ] } ` - // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + // create a new document from specification bytes + doc, err := NewDocument([]byte(spec)) - // if anything went wrong, an error is thrown - if err != nil { - panic(fmt.Sprintf("cannot create new document: %e", err)) - } - v3Model, _ := doc.BuildV3Model() - assert.NotNil(t, v3Model) + // if anything went wrong, an error is thrown + if err != nil { + panic(fmt.Sprintf("cannot create new document: %e", err)) + } + v3Model, _ := doc.BuildV3Model() + assert.NotNil(t, v3Model) } diff --git a/test_specs/swagger-circular-tests.yaml b/test_specs/swagger-circular-tests.yaml new file mode 100644 index 0000000..9612165 --- /dev/null +++ b/test_specs/swagger-circular-tests.yaml @@ -0,0 +1,51 @@ +swagger: "2.0" +paths: + /burgers: + post: + responses: + 200: + schema: + $ref: '#/definitions/Nine' +definitions: + One: + description: "test one" + properties: + things: + "$ref": "#/definitions/Two" + Two: + description: "test two" + properties: + testThing: + "$ref": "#/definitions/One" + Three: + description: "test three" + properties: + tester: + "$ref": "#/definitions/Four" + bester: + "$ref": "#/definitions/Seven" + yester: + "$ref": "#/definitions/Seven" + Four: + description: "test four" + properties: + lemons: + "$ref": "#/definitions/Nine" + Five: + properties: + rice: + "$ref": "#/definitions/Six" + Six: + properties: + mints: + "$ref": "#/definitions/Nine" + Seven: + properties: + wow: + "$ref": "#/definitions/Three" + Nine: + description: done. + Ten: + properties: + yeah: + "$ref": "#/definitions/Ten" \ No newline at end of file