diff --git a/datamodel/high/3.0/document.go b/datamodel/high/3.0/document.go index 62f9670..3db0b56 100644 --- a/datamodel/high/3.0/document.go +++ b/datamodel/high/3.0/document.go @@ -39,9 +39,9 @@ func NewDocument(document *low.Document) *Document { if !document.ExternalDocs.IsEmpty() { d.ExternalDocs = NewExternalDoc(document.ExternalDocs.Value) } - d.Paths = NewPaths(document.Paths.Value) d.Extensions = high.ExtractExtensions(document.Extensions) d.Components = NewComponents(document.Components.Value) + d.Paths = NewPaths(document.Paths.Value) return d } diff --git a/datamodel/high/3.0/document_test.go b/datamodel/high/3.0/document_test.go index 6ff0f57..71199f3 100644 --- a/datamodel/high/3.0/document_test.go +++ b/datamodel/high/3.0/document_test.go @@ -4,6 +4,7 @@ package v3 import ( + "fmt" "github.com/pb33f/libopenapi/datamodel" lowv3 "github.com/pb33f/libopenapi/datamodel/low/3.0" "github.com/stretchr/testify/assert" @@ -183,6 +184,12 @@ func TestNewDocument_Components_Schemas(t *testing.T) { assert.True(t, d.AdditionalProperties.(bool)) assert.Equal(t, "drinkType", d.Discriminator.PropertyName) assert.Equal(t, "some value", d.Discriminator.Mapping["drink"]) + assert.Equal(t, 511, d.Discriminator.GoLow().PropertyName.ValueNode.Line) + assert.Equal(t, 23, d.Discriminator.GoLow().PropertyName.ValueNode.Column) + + pl := h.Components.Schemas["SomePayload"] + assert.Equal(t, "is html programming? yes.", pl.XML.Name) + assert.Equal(t, 518, pl.XML.GoLow().Name.ValueNode.Line) ext := h.Components.Extensions assert.Equal(t, "loud", ext["x-screaming-baby"]) @@ -220,7 +227,7 @@ func TestNewDocument_Components_Responses(t *testing.T) { 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.Type) assert.Equal(t, 347, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Line) - assert.Equal(t, 7, h.Components.Examples["QuarterPounder"].GoLow().Summary.KeyNode.Column) + assert.Equal(t, 7, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Column) } func TestNewDocument_Components_SecuritySchemes(t *testing.T) { @@ -265,6 +272,8 @@ func TestNewDocument_Components_Parameters(t *testing.T) { assert.Equal(t, "this is a header", bh.Content["application/json"].Encoding["burgerTheme"].Headers["someHeader"].Description) assert.Len(t, bh.Content["application/json"].Schema.Properties, 2) + assert.Equal(t, 404, bh.Content["application/json"].Encoding["burgerTheme"].GoLow().ContentType.ValueNode.Line) + } func TestNewDocument_Paths(t *testing.T) { @@ -285,6 +294,7 @@ func TestNewDocument_Paths(t *testing.T) { 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) @@ -293,6 +303,11 @@ func TestNewDocument_Paths(t *testing.T) { 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, 77, okResp.GoLow().Description.KeyNode.Line) assert.Len(t, okResp.Links, 2) assert.Equal(t, "locateBurger", okResp.Links["LocateBurger"].OperationId) @@ -305,3 +320,16 @@ func TestNewDocument_Paths(t *testing.T) { 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 + doc, err = lowv3.CreateDocument(info) + if err != nil { + panic("broken something") + } + d := NewDocument(doc) + fmt.Println(d) +} diff --git a/datamodel/high/3.0/info.go b/datamodel/high/3.0/info.go index 878b2d6..0d5f861 100644 --- a/datamodel/high/3.0/info.go +++ b/datamodel/high/3.0/info.go @@ -21,8 +21,12 @@ func NewInfo(info *low.Info) *Info { i.Title = info.Title.Value i.Description = info.Description.Value i.TermsOfService = info.TermsOfService.Value - i.Contact = NewContact(info.Contact.Value) - i.License = NewLicense(info.License.Value) + if !info.Contact.IsEmpty() { + i.Contact = NewContact(info.Contact.Value) + } + if !info.License.IsEmpty() { + i.License = NewLicense(info.License.Value) + } i.Version = info.Version.Value return i } diff --git a/datamodel/high/3.0/license.go b/datamodel/high/3.0/license.go index 37bce50..152ebb8 100644 --- a/datamodel/high/3.0/license.go +++ b/datamodel/high/3.0/license.go @@ -14,8 +14,12 @@ type License struct { func NewLicense(license *low.License) *License { l := new(License) l.low = license - l.URL = license.URL.Value - l.Name = license.Name.Value + if !license.URL.IsEmpty() { + l.URL = license.URL.Value + } + if !license.Name.IsEmpty() { + l.Name = license.Name.Value + } return l } diff --git a/datamodel/high/3.0/operation_test.go b/datamodel/high/3.0/operation_test.go new file mode 100644 index 0000000..de1fd01 --- /dev/null +++ b/datamodel/high/3.0/operation_test.go @@ -0,0 +1,38 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" +) + +// this test exists because the sample contract doesn't contain an +// operation with *everything* populated, I had already written a ton of tests +// with hard coded line and column numbers in them, changing the spec above the bottom will +// create pointless test changes. So here is a standalone test. you know... for science. + +func TestOperation(t *testing.T) { + + yml := `externalDocs: + url: https://pb33f.io` + + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) + + var n v3.Operation + _ = low.BuildModel(&idxNode, &n) + _ = n.Build(idxNode.Content[0], idx) + + r := NewOperation(&n) + + assert.Equal(t, "https://pb33f.io", r.ExternalDocs.URL) + assert.Equal(t, 1, r.GoLow().ExternalDocs.KeyNode.Line) + +} diff --git a/datamodel/high/3.0/path_item_test.go b/datamodel/high/3.0/path_item_test.go new file mode 100644 index 0000000..617e7af --- /dev/null +++ b/datamodel/high/3.0/path_item_test.go @@ -0,0 +1,37 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" +) + +// this test exists because the sample contract doesn't contain a +// response with *everything* populated, I had already written a ton of tests +// with hard coded line and column numbers in them, changing the spec above the bottom will +// create pointless test changes. So here is a standalone test. you know... for science. + +func TestPathItem(t *testing.T) { + yml := `servers: + - description: so many options for things in places.` + + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) + + var n v3.PathItem + _ = low.BuildModel(&idxNode, &n) + _ = n.Build(idxNode.Content[0], idx) + + r := NewPathItem(&n) + + assert.Len(t, r.Servers, 1) + assert.Equal(t, "so many options for things in places.", r.Servers[0].Description) + assert.Equal(t, 1, r.GoLow().Servers.KeyNode.Line) +} diff --git a/datamodel/high/3.0/responses_test.go b/datamodel/high/3.0/responses_test.go new file mode 100644 index 0000000..fae9d0c --- /dev/null +++ b/datamodel/high/3.0/responses_test.go @@ -0,0 +1,38 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" +) + +// this test exists because the sample contract doesn't contain a +// responses with *everything* populated, I had already written a ton of tests +// with hard coded line and column numbers in them, changing the spec above the bottom will +// create pointless test changes. So here is a standalone test. you know... for science. + +func TestNewResponses(t *testing.T) { + + yml := `default: + description: default response` + + var idxNode yaml.Node + _ = yaml.Unmarshal([]byte(yml), &idxNode) + idx := index.NewSpecIndex(&idxNode) + + var n v3.Responses + _ = low.BuildModel(&idxNode, &n) + _ = n.Build(idxNode.Content[0], idx) + + r := NewResponses(&n) + + assert.Equal(t, "default response", r.Default.Description) + assert.Equal(t, 1, r.GoLow().Default.KeyNode.Line) + +} diff --git a/datamodel/high/3.0/schema_test.go b/datamodel/high/3.0/schema_test.go new file mode 100644 index 0000000..075f04e --- /dev/null +++ b/datamodel/high/3.0/schema_test.go @@ -0,0 +1,53 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "github.com/pb33f/libopenapi/datamodel/low" + v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" +) + +func TestNewSchema(t *testing.T) { + + // tests async schema lookup, by essentially running it twice, without a cache cleanup. + yml := `components: + schemas: + rice: + type: string + nice: + properties: + rice: + $ref: '#/components/schemas/rice' + ice: + properties: + rice: + $ref: '#/components/schemas/rice'` + + var idxNode, compNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &idxNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&idxNode) + + yml = `properties: + rice: + $ref: '#/components/schemas/rice'` + + var n v3.Schema + _ = yaml.Unmarshal([]byte(yml), &compNode) + err := low.BuildModel(&idxNode, &n) + assert.NoError(t, err) + + err = n.Build(idxNode.Content[0], idx) + assert.NoError(t, err) + + sch1 := NewSchema(&n) + sch2 := NewSchema(&n) + + assert.Equal(t, sch1, sch2) + +} \ No newline at end of file diff --git a/datamodel/low/3.0/components_test.go b/datamodel/low/3.0/components_test.go index 9ed6fa5..46117c3 100644 --- a/datamodel/low/3.0/components_test.go +++ b/datamodel/low/3.0/components_test.go @@ -156,3 +156,24 @@ func TestComponents_Build_Fail_TypeFail(t *testing.T) { assert.Error(t, err) } + +func TestComponents_Build_ExtensionTest(t *testing.T) { + + yml := `x-curry: seagull +headers: + x-curry-gull: vinadloo` + + var idxNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &idxNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&idxNode) + + var n Components + err := low.BuildModel(&idxNode, &n) + assert.NoError(t, err) + + err = n.Build(idxNode.Content[0], idx) + assert.NoError(t, err) + assert.Equal(t, "seagull", n.FindExtension("x-curry").Value) + +} diff --git a/datamodel/low/3.0/create_document_test.go b/datamodel/low/3.0/create_document_test.go index 6289572..31258ca 100644 --- a/datamodel/low/3.0/create_document_test.go +++ b/datamodel/low/3.0/create_document_test.go @@ -468,3 +468,17 @@ func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) { assert.NotNil(t, d.Value.AdditionalProperties.Value) assert.True(t, d.Value.AdditionalProperties.Value.(bool)) } + +func TestCreateDocument_Components_Error(t *testing.T) { + yml := `components: + schemas: + bork: + properties: + bark: + $ref: #bork` + + info, _ := datamodel.ExtractSpecInfo([]byte(yml)) + var err []error + doc, err = CreateDocument(info) + assert.Len(t, err, 1) +} diff --git a/datamodel/low/3.0/license.go b/datamodel/low/3.0/license.go index e76fb57..eeea0e9 100644 --- a/datamodel/low/3.0/license.go +++ b/datamodel/low/3.0/license.go @@ -15,6 +15,5 @@ type License struct { } func (l *License) Build(root *yaml.Node, idx *index.SpecIndex) error { - // not implemented. return nil } diff --git a/datamodel/low/3.0/path_item.go b/datamodel/low/3.0/path_item.go index 471b657..460a2ba 100644 --- a/datamodel/low/3.0/path_item.go +++ b/datamodel/low/3.0/path_item.go @@ -56,6 +56,29 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error { } } + _, ln, vn = utils.FindKeyNodeFull(ServersLabel, root.Content) + if vn != nil { + if utils.IsNodeArray(vn) { + var servers []low.ValueReference[*Server] + for _, srvN := range vn.Content { + if utils.IsNodeMap(srvN) { + srvr := new(Server) + _ = low.BuildModel(srvN, srvr) + srvr.Build(srvN, idx) + servers = append(servers, low.ValueReference[*Server]{ + Value: srvr, + ValueNode: srvN, + }) + } + } + p.Servers = low.NodeReference[[]low.ValueReference[*Server]]{ + Value: servers, + KeyNode: ln, + ValueNode: vn, + } + } + } + for i, pathNode := range root.Content { if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") { skip = true diff --git a/datamodel/low/3.0/tag.go b/datamodel/low/3.0/tag.go index a38e266..60aa36e 100644 --- a/datamodel/low/3.0/tag.go +++ b/datamodel/low/3.0/tag.go @@ -29,11 +29,7 @@ func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error { t.Extensions = low.ExtractExtensions(root) // extract externalDocs - extDocs, dErr := low.ExtractObject[*ExternalDoc](ExternalDocsLabel, root, idx) - if dErr != nil { - return dErr - } + extDocs, err := low.ExtractObject[*ExternalDoc](ExternalDocsLabel, root, idx) t.ExternalDocs = extDocs - - return nil + return err }