mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
2.0 model at 90% coverage
Error handling still required, but a nice jump.
This commit is contained in:
@@ -36,6 +36,13 @@ func (t *Tag) GoLow() *low.Tag {
|
||||
return t.low
|
||||
}
|
||||
|
||||
func (t *Tag) SetName(value string) {
|
||||
t.GoLow().Name.ValueNode.Value = value
|
||||
}
|
||||
func (t *Tag) SetDescription(value string) {
|
||||
t.GoLow().Description.ValueNode.Value = value
|
||||
}
|
||||
|
||||
//func (t *Tag) MarshalYAML() (interface{}, error) {
|
||||
// m := make(map[string]interface{})
|
||||
// for i := range t.Extensions {
|
||||
|
||||
@@ -6,10 +6,13 @@ package v2
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
ExamplesLabel = "examples"
|
||||
)
|
||||
|
||||
type Examples struct {
|
||||
Values map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
@@ -17,36 +20,27 @@ type Examples struct {
|
||||
func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
var keyNode, currNode *yaml.Node
|
||||
var err error
|
||||
e.Values = make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
keyNode = root.Content[i]
|
||||
continue
|
||||
}
|
||||
currNode = root.Content[i]
|
||||
if utils.IsNodeMap(currNode) {
|
||||
var n map[string]interface{}
|
||||
err = currNode.Decode(&n)
|
||||
var n map[string]interface{}
|
||||
err = currNode.Decode(&n)
|
||||
if err != nil {
|
||||
var k []interface{}
|
||||
err = currNode.Decode(&k)
|
||||
if err != nil {
|
||||
var k []interface{}
|
||||
err = currNode.Decode(&k)
|
||||
if err != nil {
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = currNode.Decode(&j)
|
||||
e.Values[low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: j,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
continue
|
||||
}
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = currNode.Decode(&j)
|
||||
e.Values[low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: k,
|
||||
Value: j,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
continue
|
||||
@@ -55,10 +49,19 @@ func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: n,
|
||||
Value: k,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
continue
|
||||
}
|
||||
e.Values[low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: n,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
i.Default.Value = low.NodeReference[any]{
|
||||
i.Default = low.NodeReference[any]{
|
||||
Value: n,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
|
||||
@@ -4,189 +4,168 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"sync"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type PathItem struct {
|
||||
Ref low.NodeReference[string]
|
||||
Get low.NodeReference[*Operation]
|
||||
Put low.NodeReference[*Operation]
|
||||
Post low.NodeReference[*Operation]
|
||||
Delete low.NodeReference[*Operation]
|
||||
Options low.NodeReference[*Operation]
|
||||
Head low.NodeReference[*Operation]
|
||||
Patch low.NodeReference[*Operation]
|
||||
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Ref low.NodeReference[string]
|
||||
Get low.NodeReference[*Operation]
|
||||
Put low.NodeReference[*Operation]
|
||||
Post low.NodeReference[*Operation]
|
||||
Delete low.NodeReference[*Operation]
|
||||
Options low.NodeReference[*Operation]
|
||||
Head low.NodeReference[*Operation]
|
||||
Patch low.NodeReference[*Operation]
|
||||
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, p.Extensions)
|
||||
return low.FindItemInMap[any](ext, p.Extensions)
|
||||
}
|
||||
|
||||
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
skip := false
|
||||
var currentNode *yaml.Node
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
skip := false
|
||||
var currentNode *yaml.Node
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errors []error
|
||||
var wg sync.WaitGroup
|
||||
var errors []error
|
||||
|
||||
var ops []low.NodeReference[*Operation]
|
||||
var ops []low.NodeReference[*Operation]
|
||||
|
||||
// extract parameters
|
||||
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
|
||||
if pErr != nil {
|
||||
return pErr
|
||||
}
|
||||
if params != nil {
|
||||
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
|
||||
Value: params,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
}
|
||||
// extract parameters
|
||||
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
|
||||
if pErr != nil {
|
||||
return pErr
|
||||
}
|
||||
if params != nil {
|
||||
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
|
||||
Value: params,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
}
|
||||
|
||||
for i, pathNode := range root.Content {
|
||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "parameters") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
|
||||
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
|
||||
// in swagger. Why this choice was made, I do not know.
|
||||
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
|
||||
rn := root.Content[i+1]
|
||||
p.Ref = low.NodeReference[string]{
|
||||
Value: rn.Value,
|
||||
ValueNode: rn,
|
||||
KeyNode: pathNode,
|
||||
}
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
if i%2 == 0 {
|
||||
currentNode = pathNode
|
||||
continue
|
||||
}
|
||||
for i, pathNode := range root.Content {
|
||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
|
||||
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
|
||||
// in swagger. Why this choice was made, I do not know.
|
||||
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
|
||||
rn := root.Content[i+1]
|
||||
p.Ref = low.NodeReference[string]{
|
||||
Value: rn.Value,
|
||||
ValueNode: rn,
|
||||
KeyNode: pathNode,
|
||||
}
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
if i%2 == 0 {
|
||||
currentNode = pathNode
|
||||
continue
|
||||
}
|
||||
|
||||
// the only thing we now care about is handling operations, filter out anything that's not a verb.
|
||||
switch currentNode.Value {
|
||||
case GetLabel:
|
||||
break
|
||||
case PostLabel:
|
||||
break
|
||||
case PutLabel:
|
||||
break
|
||||
case PatchLabel:
|
||||
break
|
||||
case DeleteLabel:
|
||||
break
|
||||
case HeadLabel:
|
||||
break
|
||||
case OptionsLabel:
|
||||
break
|
||||
default:
|
||||
continue // ignore everything else.
|
||||
}
|
||||
// the only thing we now care about is handling operations, filter out anything that's not a verb.
|
||||
switch currentNode.Value {
|
||||
case GetLabel:
|
||||
break
|
||||
case PostLabel:
|
||||
break
|
||||
case PutLabel:
|
||||
break
|
||||
case PatchLabel:
|
||||
break
|
||||
case DeleteLabel:
|
||||
break
|
||||
case HeadLabel:
|
||||
break
|
||||
case OptionsLabel:
|
||||
break
|
||||
default:
|
||||
continue // ignore everything else.
|
||||
}
|
||||
|
||||
var op Operation
|
||||
var op Operation
|
||||
|
||||
wg.Add(1)
|
||||
wg.Add(1)
|
||||
|
||||
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
|
||||
r, err := low.LocateRefNode(pathNode, idx)
|
||||
if r != nil {
|
||||
pathNode = r
|
||||
if err != nil {
|
||||
if !idx.AllowCircularReferenceResolving() {
|
||||
return fmt.Errorf("build schema failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
||||
pathNode.Content[1].Value, pathNode.Content[1].Line, pathNode.Content[1].Column)
|
||||
}
|
||||
}
|
||||
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
||||
|
||||
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
||||
opRef := low.NodeReference[*Operation]{
|
||||
Value: &op,
|
||||
KeyNode: currentNode,
|
||||
ValueNode: pathNode,
|
||||
}
|
||||
|
||||
opRef := low.NodeReference[*Operation]{
|
||||
Value: &op,
|
||||
KeyNode: currentNode,
|
||||
ValueNode: pathNode,
|
||||
}
|
||||
ops = append(ops, opRef)
|
||||
|
||||
ops = append(ops, opRef)
|
||||
switch currentNode.Value {
|
||||
case GetLabel:
|
||||
p.Get = opRef
|
||||
case PostLabel:
|
||||
p.Post = opRef
|
||||
case PutLabel:
|
||||
p.Put = opRef
|
||||
case PatchLabel:
|
||||
p.Patch = opRef
|
||||
case DeleteLabel:
|
||||
p.Delete = opRef
|
||||
case HeadLabel:
|
||||
p.Head = opRef
|
||||
case OptionsLabel:
|
||||
p.Options = opRef
|
||||
}
|
||||
}
|
||||
|
||||
switch currentNode.Value {
|
||||
case GetLabel:
|
||||
p.Get = opRef
|
||||
case PostLabel:
|
||||
p.Post = opRef
|
||||
case PutLabel:
|
||||
p.Put = opRef
|
||||
case PatchLabel:
|
||||
p.Patch = opRef
|
||||
case DeleteLabel:
|
||||
p.Delete = opRef
|
||||
case HeadLabel:
|
||||
p.Head = opRef
|
||||
case OptionsLabel:
|
||||
p.Options = opRef
|
||||
}
|
||||
}
|
||||
//all operations have been superficially built,
|
||||
//now we need to build out the operation, we will do this asynchronously for speed.
|
||||
opBuildChan := make(chan bool)
|
||||
opErrorChan := make(chan error)
|
||||
|
||||
//all operations have been superficially built,
|
||||
//now we need to build out the operation, we will do this asynchronously for speed.
|
||||
opBuildChan := make(chan bool)
|
||||
opErrorChan := make(chan error)
|
||||
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
|
||||
er := op.Value.Build(op.ValueNode, idx)
|
||||
if er != nil {
|
||||
errCh <- er
|
||||
}
|
||||
ch <- true
|
||||
}
|
||||
|
||||
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
|
||||
er := op.Value.Build(op.ValueNode, idx)
|
||||
if er != nil {
|
||||
errCh <- er
|
||||
}
|
||||
ch <- true
|
||||
}
|
||||
if len(ops) <= 0 {
|
||||
return nil // nothing to do.
|
||||
}
|
||||
|
||||
if len(ops) <= 0 {
|
||||
return nil // nothing to do.
|
||||
}
|
||||
for _, op := range ops {
|
||||
go buildOpFunc(op, opBuildChan, opErrorChan)
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
go buildOpFunc(op, opBuildChan, opErrorChan)
|
||||
}
|
||||
n := 0
|
||||
total := len(ops)
|
||||
for n < total {
|
||||
select {
|
||||
case buildError := <-opErrorChan:
|
||||
return buildError
|
||||
case <-opBuildChan:
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
n := 0
|
||||
total := len(ops)
|
||||
for n < total {
|
||||
select {
|
||||
case buildError := <-opErrorChan:
|
||||
return buildError
|
||||
case <-opBuildChan:
|
||||
n++
|
||||
}
|
||||
}
|
||||
// make sure we don't exit before the path is finished building.
|
||||
if len(ops) > 0 {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// make sure we don't exit before the path is finished building.
|
||||
if len(ops) > 0 {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
)
|
||||
@@ -21,7 +19,6 @@ const (
|
||||
DeleteLabel = "delete"
|
||||
OptionsLabel = "options"
|
||||
HeadLabel = "head"
|
||||
TraceLabel = "trace"
|
||||
)
|
||||
|
||||
type Paths struct {
|
||||
@@ -58,23 +55,6 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
bChan := make(chan pathBuildResult)
|
||||
eChan := make(chan error)
|
||||
var buildPathItem = func(cNode, pNode *yaml.Node, b chan<- pathBuildResult, e chan<- error) {
|
||||
if ok, _, _ := utils.IsNodeRefValue(pNode); ok {
|
||||
r, err := low.LocateRefNode(pNode, idx)
|
||||
if r != nil {
|
||||
pNode = r
|
||||
if err != nil {
|
||||
if !idx.AllowCircularReferenceResolving() {
|
||||
e <- fmt.Errorf("path item build failed: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
e <- fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
||||
pNode.Content[1].Value, pNode.Content[1].Line, pNode.Content[1].Column)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
path := new(PathItem)
|
||||
_ = low.BuildModel(pNode, path)
|
||||
err := path.Build(pNode, idx)
|
||||
|
||||
@@ -40,6 +40,13 @@ func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
r.Schema = *s
|
||||
}
|
||||
|
||||
// extract examples
|
||||
examples, expErr := low.ExtractObject[*Examples](ExamplesLabel, root, idx)
|
||||
if expErr != nil {
|
||||
return expErr
|
||||
}
|
||||
r.Examples = examples
|
||||
|
||||
//extract headers
|
||||
headers, lN, kN, err := low.ExtractMapFlat[*Header](HeadersLabel, root, idx)
|
||||
if err != nil {
|
||||
|
||||
@@ -90,6 +90,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
||||
case <-doneChan:
|
||||
completedExtractions++
|
||||
case e := <-errChan:
|
||||
completedExtractions++
|
||||
errors = append(errors, e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,90 +4,153 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"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 {
|
||||
panic("broken something")
|
||||
}
|
||||
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()
|
||||
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, 2)
|
||||
assert.Len(t, doc.Paths.Value.PathItems, 14)
|
||||
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)
|
||||
initTest()
|
||||
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, 2)
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
// 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)
|
||||
|
||||
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("yourHeader")
|
||||
assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.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)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -21,3 +21,18 @@ type Document struct {
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Index *index.SpecIndex
|
||||
}
|
||||
|
||||
//
|
||||
//func (d *Document) AddTag() *base.Tag {
|
||||
// t := base.NewTag()
|
||||
// //d.Tags.KeyNode
|
||||
// t.Name.Value = "nice new tag"
|
||||
//
|
||||
// dat, _ := yaml.Marshal(t)
|
||||
// var inject yaml.Node
|
||||
// _ = yaml.Unmarshal(dat, &inject)
|
||||
//
|
||||
// d.Tags.ValueNode.Content = append(d.Tags.ValueNode.Content, inject.Content[0])
|
||||
//
|
||||
// return t
|
||||
//}
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
const (
|
||||
TagsLabel = "tags"
|
||||
ExternalDocsLabel = "externalDocs"
|
||||
NameLabel = "name"
|
||||
DescriptionLabel = "description"
|
||||
)
|
||||
|
||||
type Tag struct {
|
||||
@@ -33,3 +35,24 @@ func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
t.ExternalDocs = extDocs
|
||||
return err
|
||||
}
|
||||
|
||||
//func (t *Tag) MarshalYAML() (interface{}, error) {
|
||||
// m := make(map[string]interface{})
|
||||
// for i := range t.Extensions {
|
||||
// m[i.Value] = t.Extensions[i].Value
|
||||
// }
|
||||
// if t.Name.Value != "" {
|
||||
// m[NameLabel] = t.Name.Value
|
||||
// }
|
||||
// if t.Description.Value != "" {
|
||||
// m[DescriptionLabel] = t.Description.Value
|
||||
// }
|
||||
// if t.ExternalDocs.Value != nil {
|
||||
// m[ExternalDocsLabel] = t.ExternalDocs.Value
|
||||
// }
|
||||
// return m, nil
|
||||
//}
|
||||
//
|
||||
//func NewTag() *Tag {
|
||||
// return new(Tag)
|
||||
//}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
high "github.com/pb33f/libopenapi/datamodel/high/3.0"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
high "github.com/pb33f/libopenapi/datamodel/high/3.0"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
"gopkg.in/yaml.v3"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
testData := `openapi: 3.0.1
|
||||
testData := `openapi: 3.0.1
|
||||
info:
|
||||
title: this is a title
|
||||
description: this is a description
|
||||
@@ -24,28 +24,28 @@ tags:
|
||||
description: coffee
|
||||
x-code: hack`
|
||||
|
||||
data := []byte(testData)
|
||||
_ = ioutil.WriteFile("sample.yaml", data, 0664)
|
||||
data := []byte(testData)
|
||||
_ = ioutil.WriteFile("sample.yaml", data, 0664)
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
lowDoc, err := low.CreateDocument(info)
|
||||
if len(err) > 0 {
|
||||
for e := range err {
|
||||
fmt.Printf("%e\n", err[e])
|
||||
}
|
||||
return
|
||||
}
|
||||
highDoc := high.NewDocument(lowDoc)
|
||||
fmt.Println(highDoc.Info.Title)
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
lowDoc, err := low.CreateDocument(info)
|
||||
if len(err) > 0 {
|
||||
for e := range err {
|
||||
fmt.Printf("%e\n", err[e])
|
||||
}
|
||||
return
|
||||
}
|
||||
highDoc := high.NewDocument(lowDoc)
|
||||
|
||||
highDoc.Info.GoLow().Title.ValueNode.Value = "let's hack this shizzle"
|
||||
highDoc.Info.GoLow().Title.ValueNode.Value = "let's hack this"
|
||||
highDoc.Tags[0].SetName("We are a new name now")
|
||||
highDoc.Tags[0].SetDescription("and a new description")
|
||||
|
||||
modified, _ := yaml.Marshal(info.RootNode)
|
||||
fmt.Println(string(modified))
|
||||
//newTag := lowDoc.AddTag()
|
||||
//fmt.Println(newTag)
|
||||
modified, _ := yaml.Marshal(info.RootNode)
|
||||
fmt.Println(string(modified))
|
||||
|
||||
d, _ := yaml.Marshal(highDoc.Tags[0])
|
||||
fmt.Println(d)
|
||||
|
||||
os.Remove("sample.yaml")
|
||||
os.Remove("sample.yaml")
|
||||
|
||||
}
|
||||
|
||||
11
sample.yaml
11
sample.yaml
@@ -1,11 +0,0 @@
|
||||
openapi: 3.0.1
|
||||
info:
|
||||
title: this is a title
|
||||
description: this is a description
|
||||
tags:
|
||||
- name: Tag A
|
||||
description: cake
|
||||
x-hack: true
|
||||
- name: Tag B
|
||||
description: coffee
|
||||
x-code: hack
|
||||
@@ -20,6 +20,7 @@ parameters:
|
||||
in: query
|
||||
name: simple
|
||||
type: string
|
||||
x-chicken: nuggets
|
||||
tags:
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
@@ -37,7 +38,9 @@ schemes:
|
||||
- https
|
||||
- http
|
||||
paths:
|
||||
x-minty: fresh
|
||||
"/pet/{petId}/uploadImage":
|
||||
x-potato: man
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
@@ -67,15 +70,6 @@ paths:
|
||||
type: file
|
||||
responses:
|
||||
'200':
|
||||
headers:
|
||||
myHeader:
|
||||
type: array
|
||||
description: myHeader
|
||||
items:
|
||||
type: string
|
||||
collectionFormat: csv
|
||||
default: [ one, two, three ]
|
||||
enum: [99,100,101]
|
||||
description: successful operation
|
||||
schema:
|
||||
"$ref": "#/definitions/ApiResponse"
|
||||
@@ -574,6 +568,28 @@ paths:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user":
|
||||
borked:
|
||||
summary: not allowed
|
||||
options:
|
||||
summary: hola!
|
||||
responses:
|
||||
default:
|
||||
description: hello there
|
||||
delete:
|
||||
summary: sup!
|
||||
responses:
|
||||
default:
|
||||
description: hello there
|
||||
head:
|
||||
summary: yo!
|
||||
responses:
|
||||
default:
|
||||
description: hello there
|
||||
patch:
|
||||
summary: hi!
|
||||
responses:
|
||||
default:
|
||||
description: hello there
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
@@ -595,6 +611,8 @@ paths:
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/ref":
|
||||
$ref: "#/externalPaths/test"
|
||||
securityDefinitions:
|
||||
api_key:
|
||||
type: apiKey
|
||||
@@ -609,7 +627,46 @@ securityDefinitions:
|
||||
write:pets: modify pets in your account
|
||||
responses:
|
||||
"200":
|
||||
x-coffee: morning
|
||||
description: OK
|
||||
headers:
|
||||
noHeader:
|
||||
type: string
|
||||
x-empty: true
|
||||
items:
|
||||
type: string
|
||||
yourHeader:
|
||||
type: string
|
||||
default: yourHeader
|
||||
items:
|
||||
default: somethingSimple
|
||||
ourHeader:
|
||||
type: string
|
||||
default:
|
||||
funny: bunny
|
||||
myHeader:
|
||||
type: array
|
||||
description: myHeader
|
||||
items:
|
||||
type: string
|
||||
default:
|
||||
something: here
|
||||
items:
|
||||
default:
|
||||
- one
|
||||
- two
|
||||
collectionFormat: csv
|
||||
default: [ one, two, three ]
|
||||
enum: [ 99,100,101 ]
|
||||
examples:
|
||||
"application/json":
|
||||
one: two
|
||||
three: four
|
||||
"text/xml":
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
"text/plain": something else.
|
||||
schema:
|
||||
$ref: '#/definitions/ApiResponse'
|
||||
"500":
|
||||
@@ -739,3 +796,7 @@ definitions:
|
||||
externalDocs:
|
||||
description: Find out more about Swagger
|
||||
url: http://swagger.io
|
||||
externalPaths:
|
||||
# this is illegal
|
||||
test:
|
||||
- summary: this is illegal.
|
||||
Reference in New Issue
Block a user