mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 12:37:48 +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
|
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) {
|
//func (t *Tag) MarshalYAML() (interface{}, error) {
|
||||||
// m := make(map[string]interface{})
|
// m := make(map[string]interface{})
|
||||||
// for i := range t.Extensions {
|
// for i := range t.Extensions {
|
||||||
|
|||||||
@@ -6,10 +6,13 @@ package v2
|
|||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ExamplesLabel = "examples"
|
||||||
|
)
|
||||||
|
|
||||||
type Examples struct {
|
type Examples struct {
|
||||||
Values map[low.KeyReference[string]]low.ValueReference[any]
|
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 {
|
func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||||
var keyNode, currNode *yaml.Node
|
var keyNode, currNode *yaml.Node
|
||||||
var err error
|
var err error
|
||||||
|
e.Values = make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
for i := range root.Content {
|
for i := range root.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
keyNode = root.Content[i]
|
keyNode = root.Content[i]
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
currNode = root.Content[i]
|
currNode = root.Content[i]
|
||||||
if utils.IsNodeMap(currNode) {
|
var n map[string]interface{}
|
||||||
var n map[string]interface{}
|
err = currNode.Decode(&n)
|
||||||
err = currNode.Decode(&n)
|
if err != nil {
|
||||||
|
var k []interface{}
|
||||||
|
err = currNode.Decode(&k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var k []interface{}
|
// lets just default to interface
|
||||||
err = currNode.Decode(&k)
|
var j interface{}
|
||||||
if err != nil {
|
_ = currNode.Decode(&j)
|
||||||
// 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
|
|
||||||
}
|
|
||||||
e.Values[low.KeyReference[string]{
|
e.Values[low.KeyReference[string]{
|
||||||
Value: keyNode.Value,
|
Value: keyNode.Value,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
Value: k,
|
Value: j,
|
||||||
ValueNode: currNode,
|
ValueNode: currNode,
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -55,10 +49,19 @@ func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
|||||||
Value: keyNode.Value,
|
Value: keyNode.Value,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
Value: n,
|
Value: k,
|
||||||
ValueNode: currNode,
|
ValueNode: currNode,
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
e.Values[low.KeyReference[string]{
|
||||||
|
Value: keyNode.Value,
|
||||||
|
KeyNode: keyNode,
|
||||||
|
}] = low.ValueReference[any]{
|
||||||
|
Value: n,
|
||||||
|
ValueNode: currNode,
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
i.Default.Value = low.NodeReference[any]{
|
i.Default = low.NodeReference[any]{
|
||||||
Value: n,
|
Value: n,
|
||||||
KeyNode: ln,
|
KeyNode: ln,
|
||||||
ValueNode: vn,
|
ValueNode: vn,
|
||||||
|
|||||||
@@ -4,189 +4,168 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"gopkg.in/yaml.v3"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"strings"
|
||||||
"gopkg.in/yaml.v3"
|
"sync"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type PathItem struct {
|
type PathItem struct {
|
||||||
Ref low.NodeReference[string]
|
Ref low.NodeReference[string]
|
||||||
Get low.NodeReference[*Operation]
|
Get low.NodeReference[*Operation]
|
||||||
Put low.NodeReference[*Operation]
|
Put low.NodeReference[*Operation]
|
||||||
Post low.NodeReference[*Operation]
|
Post low.NodeReference[*Operation]
|
||||||
Delete low.NodeReference[*Operation]
|
Delete low.NodeReference[*Operation]
|
||||||
Options low.NodeReference[*Operation]
|
Options low.NodeReference[*Operation]
|
||||||
Head low.NodeReference[*Operation]
|
Head low.NodeReference[*Operation]
|
||||||
Patch low.NodeReference[*Operation]
|
Patch low.NodeReference[*Operation]
|
||||||
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PathItem) FindExtension(ext 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 {
|
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
skip := false
|
skip := false
|
||||||
var currentNode *yaml.Node
|
var currentNode *yaml.Node
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var errors []error
|
var errors []error
|
||||||
|
|
||||||
var ops []low.NodeReference[*Operation]
|
var ops []low.NodeReference[*Operation]
|
||||||
|
|
||||||
// extract parameters
|
// extract parameters
|
||||||
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
|
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
|
||||||
if pErr != nil {
|
if pErr != nil {
|
||||||
return pErr
|
return pErr
|
||||||
}
|
}
|
||||||
if params != nil {
|
if params != nil {
|
||||||
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
|
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
|
||||||
Value: params,
|
Value: params,
|
||||||
KeyNode: ln,
|
KeyNode: ln,
|
||||||
ValueNode: vn,
|
ValueNode: vn,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, pathNode := range root.Content {
|
for i, pathNode := range root.Content {
|
||||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
|
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
|
||||||
skip = true
|
skip = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "parameters") {
|
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
|
||||||
skip = true
|
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
|
||||||
continue
|
// in swagger. Why this choice was made, I do not know.
|
||||||
}
|
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
|
||||||
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
|
rn := root.Content[i+1]
|
||||||
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
|
p.Ref = low.NodeReference[string]{
|
||||||
// in swagger. Why this choice was made, I do not know.
|
Value: rn.Value,
|
||||||
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
|
ValueNode: rn,
|
||||||
rn := root.Content[i+1]
|
KeyNode: pathNode,
|
||||||
p.Ref = low.NodeReference[string]{
|
}
|
||||||
Value: rn.Value,
|
skip = true
|
||||||
ValueNode: rn,
|
continue
|
||||||
KeyNode: pathNode,
|
}
|
||||||
}
|
if skip {
|
||||||
skip = true
|
skip = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if skip {
|
if i%2 == 0 {
|
||||||
skip = false
|
currentNode = pathNode
|
||||||
continue
|
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.
|
// the only thing we now care about is handling operations, filter out anything that's not a verb.
|
||||||
switch currentNode.Value {
|
switch currentNode.Value {
|
||||||
case GetLabel:
|
case GetLabel:
|
||||||
break
|
break
|
||||||
case PostLabel:
|
case PostLabel:
|
||||||
break
|
break
|
||||||
case PutLabel:
|
case PutLabel:
|
||||||
break
|
break
|
||||||
case PatchLabel:
|
case PatchLabel:
|
||||||
break
|
break
|
||||||
case DeleteLabel:
|
case DeleteLabel:
|
||||||
break
|
break
|
||||||
case HeadLabel:
|
case HeadLabel:
|
||||||
break
|
break
|
||||||
case OptionsLabel:
|
case OptionsLabel:
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
continue // ignore everything else.
|
continue // ignore everything else.
|
||||||
}
|
}
|
||||||
|
|
||||||
var op Operation
|
var op Operation
|
||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
|
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
||||||
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)
|
opRef := low.NodeReference[*Operation]{
|
||||||
|
Value: &op,
|
||||||
|
KeyNode: currentNode,
|
||||||
|
ValueNode: pathNode,
|
||||||
|
}
|
||||||
|
|
||||||
opRef := low.NodeReference[*Operation]{
|
ops = append(ops, opRef)
|
||||||
Value: &op,
|
|
||||||
KeyNode: currentNode,
|
|
||||||
ValueNode: pathNode,
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
//all operations have been superficially built,
|
||||||
case GetLabel:
|
//now we need to build out the operation, we will do this asynchronously for speed.
|
||||||
p.Get = opRef
|
opBuildChan := make(chan bool)
|
||||||
case PostLabel:
|
opErrorChan := make(chan error)
|
||||||
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,
|
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
|
||||||
//now we need to build out the operation, we will do this asynchronously for speed.
|
er := op.Value.Build(op.ValueNode, idx)
|
||||||
opBuildChan := make(chan bool)
|
if er != nil {
|
||||||
opErrorChan := make(chan error)
|
errCh <- er
|
||||||
|
}
|
||||||
|
ch <- true
|
||||||
|
}
|
||||||
|
|
||||||
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
|
if len(ops) <= 0 {
|
||||||
er := op.Value.Build(op.ValueNode, idx)
|
return nil // nothing to do.
|
||||||
if er != nil {
|
}
|
||||||
errCh <- er
|
|
||||||
}
|
|
||||||
ch <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(ops) <= 0 {
|
for _, op := range ops {
|
||||||
return nil // nothing to do.
|
go buildOpFunc(op, opBuildChan, opErrorChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, op := range ops {
|
n := 0
|
||||||
go buildOpFunc(op, opBuildChan, opErrorChan)
|
total := len(ops)
|
||||||
}
|
for n < total {
|
||||||
|
select {
|
||||||
|
case buildError := <-opErrorChan:
|
||||||
|
return buildError
|
||||||
|
case <-opBuildChan:
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
n := 0
|
// make sure we don't exit before the path is finished building.
|
||||||
total := len(ops)
|
if len(ops) > 0 {
|
||||||
for n < total {
|
wg.Wait()
|
||||||
select {
|
}
|
||||||
case buildError := <-opErrorChan:
|
|
||||||
return buildError
|
|
||||||
case <-opBuildChan:
|
|
||||||
n++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure we don't exit before the path is finished building.
|
return nil
|
||||||
if len(ops) > 0 {
|
|
||||||
wg.Wait()
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -21,7 +19,6 @@ const (
|
|||||||
DeleteLabel = "delete"
|
DeleteLabel = "delete"
|
||||||
OptionsLabel = "options"
|
OptionsLabel = "options"
|
||||||
HeadLabel = "head"
|
HeadLabel = "head"
|
||||||
TraceLabel = "trace"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Paths struct {
|
type Paths struct {
|
||||||
@@ -58,23 +55,6 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
bChan := make(chan pathBuildResult)
|
bChan := make(chan pathBuildResult)
|
||||||
eChan := make(chan error)
|
eChan := make(chan error)
|
||||||
var buildPathItem = func(cNode, pNode *yaml.Node, b chan<- pathBuildResult, e 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)
|
path := new(PathItem)
|
||||||
_ = low.BuildModel(pNode, path)
|
_ = low.BuildModel(pNode, path)
|
||||||
err := path.Build(pNode, idx)
|
err := path.Build(pNode, idx)
|
||||||
|
|||||||
@@ -40,6 +40,13 @@ func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
r.Schema = *s
|
r.Schema = *s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract examples
|
||||||
|
examples, expErr := low.ExtractObject[*Examples](ExamplesLabel, root, idx)
|
||||||
|
if expErr != nil {
|
||||||
|
return expErr
|
||||||
|
}
|
||||||
|
r.Examples = examples
|
||||||
|
|
||||||
//extract headers
|
//extract headers
|
||||||
headers, lN, kN, err := low.ExtractMapFlat[*Header](HeadersLabel, root, idx)
|
headers, lN, kN, err := low.ExtractMapFlat[*Header](HeadersLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
|||||||
case <-doneChan:
|
case <-doneChan:
|
||||||
completedExtractions++
|
completedExtractions++
|
||||||
case e := <-errChan:
|
case e := <-errChan:
|
||||||
|
completedExtractions++
|
||||||
errors = append(errors, e)
|
errors = append(errors, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,90 +4,153 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"fmt"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"io/ioutil"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var doc *Swagger
|
var doc *Swagger
|
||||||
|
|
||||||
func initTest() {
|
func initTest() {
|
||||||
if doc != nil {
|
if doc != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("broken something")
|
fmt.Print(err)
|
||||||
}
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCreateDocument(b *testing.B) {
|
func BenchmarkCreateDocument(b *testing.B) {
|
||||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
doc, _ = CreateDocument(info)
|
doc, _ = CreateDocument(info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument(t *testing.T) {
|
func TestCreateDocument(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
assert.Equal(t, "2.0", doc.SpecInfo.Version)
|
assert.Equal(t, "2.0", doc.SpecInfo.Version)
|
||||||
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
|
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
|
||||||
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
|
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
|
||||||
assert.Equal(t, "/v2", doc.BasePath.Value)
|
assert.Equal(t, "/v2", doc.BasePath.Value)
|
||||||
assert.Len(t, doc.Parameters.Value.Definitions, 1)
|
assert.Len(t, doc.Parameters.Value.Definitions, 1)
|
||||||
assert.Len(t, doc.Tags.Value, 3)
|
assert.Len(t, doc.Tags.Value, 3)
|
||||||
assert.Len(t, doc.Schemes.Value, 2)
|
assert.Len(t, doc.Schemes.Value, 2)
|
||||||
assert.Len(t, doc.Definitions.Value.Schemas, 6)
|
assert.Len(t, doc.Definitions.Value.Schemas, 6)
|
||||||
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 2)
|
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 2)
|
||||||
assert.Len(t, doc.Paths.Value.PathItems, 14)
|
assert.Len(t, doc.Paths.Value.PathItems, 15)
|
||||||
assert.Len(t, doc.Responses.Value.Definitions, 2)
|
assert.Len(t, doc.Responses.Value.Definitions, 2)
|
||||||
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
|
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.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Info(t *testing.T) {
|
func TestCreateDocument_Info(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
|
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, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
|
||||||
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
|
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Parameters(t *testing.T) {
|
func TestCreateDocument_Parameters(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
|
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
|
||||||
assert.NotNil(t, simpleParam)
|
assert.NotNil(t, simpleParam)
|
||||||
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
|
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
|
||||||
|
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Tags(t *testing.T) {
|
func TestCreateDocument_Tags(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
|
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, "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, "store", doc.Tags.Value[1].Value.Name.Value)
|
||||||
assert.Equal(t, "user", doc.Tags.Value[2].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)
|
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
|
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
|
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
|
||||||
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
|
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
|
||||||
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
|
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
|
||||||
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
|
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
|
||||||
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
|
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
|
||||||
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
|
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
|
||||||
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
|
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]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
Index *index.SpecIndex
|
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 (
|
const (
|
||||||
TagsLabel = "tags"
|
TagsLabel = "tags"
|
||||||
ExternalDocsLabel = "externalDocs"
|
ExternalDocsLabel = "externalDocs"
|
||||||
|
NameLabel = "name"
|
||||||
|
DescriptionLabel = "description"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
@@ -33,3 +35,24 @@ func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
t.ExternalDocs = extDocs
|
t.ExternalDocs = extDocs
|
||||||
return err
|
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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
high "github.com/pb33f/libopenapi/datamodel/high/3.0"
|
high "github.com/pb33f/libopenapi/datamodel/high/3.0"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
low "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
testData := `openapi: 3.0.1
|
testData := `openapi: 3.0.1
|
||||||
info:
|
info:
|
||||||
title: this is a title
|
title: this is a title
|
||||||
description: this is a description
|
description: this is a description
|
||||||
@@ -24,28 +24,28 @@ tags:
|
|||||||
description: coffee
|
description: coffee
|
||||||
x-code: hack`
|
x-code: hack`
|
||||||
|
|
||||||
data := []byte(testData)
|
data := []byte(testData)
|
||||||
_ = ioutil.WriteFile("sample.yaml", data, 0664)
|
_ = ioutil.WriteFile("sample.yaml", data, 0664)
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
lowDoc, err := low.CreateDocument(info)
|
lowDoc, err := low.CreateDocument(info)
|
||||||
if len(err) > 0 {
|
if len(err) > 0 {
|
||||||
for e := range err {
|
for e := range err {
|
||||||
fmt.Printf("%e\n", err[e])
|
fmt.Printf("%e\n", err[e])
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
highDoc := high.NewDocument(lowDoc)
|
highDoc := high.NewDocument(lowDoc)
|
||||||
fmt.Println(highDoc.Info.Title)
|
|
||||||
|
|
||||||
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)
|
//newTag := lowDoc.AddTag()
|
||||||
fmt.Println(string(modified))
|
//fmt.Println(newTag)
|
||||||
|
modified, _ := yaml.Marshal(info.RootNode)
|
||||||
|
fmt.Println(string(modified))
|
||||||
|
|
||||||
d, _ := yaml.Marshal(highDoc.Tags[0])
|
os.Remove("sample.yaml")
|
||||||
fmt.Println(d)
|
|
||||||
|
|
||||||
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
|
in: query
|
||||||
name: simple
|
name: simple
|
||||||
type: string
|
type: string
|
||||||
|
x-chicken: nuggets
|
||||||
tags:
|
tags:
|
||||||
- name: pet
|
- name: pet
|
||||||
description: Everything about your Pets
|
description: Everything about your Pets
|
||||||
@@ -37,7 +38,9 @@ schemes:
|
|||||||
- https
|
- https
|
||||||
- http
|
- http
|
||||||
paths:
|
paths:
|
||||||
|
x-minty: fresh
|
||||||
"/pet/{petId}/uploadImage":
|
"/pet/{petId}/uploadImage":
|
||||||
|
x-potato: man
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- pet
|
- pet
|
||||||
@@ -67,15 +70,6 @@ paths:
|
|||||||
type: file
|
type: file
|
||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
headers:
|
|
||||||
myHeader:
|
|
||||||
type: array
|
|
||||||
description: myHeader
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
collectionFormat: csv
|
|
||||||
default: [ one, two, three ]
|
|
||||||
enum: [99,100,101]
|
|
||||||
description: successful operation
|
description: successful operation
|
||||||
schema:
|
schema:
|
||||||
"$ref": "#/definitions/ApiResponse"
|
"$ref": "#/definitions/ApiResponse"
|
||||||
@@ -574,6 +568,28 @@ paths:
|
|||||||
default:
|
default:
|
||||||
description: successful operation
|
description: successful operation
|
||||||
"/user":
|
"/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:
|
post:
|
||||||
tags:
|
tags:
|
||||||
- user
|
- user
|
||||||
@@ -595,6 +611,8 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
default:
|
default:
|
||||||
description: successful operation
|
description: successful operation
|
||||||
|
"/ref":
|
||||||
|
$ref: "#/externalPaths/test"
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
api_key:
|
api_key:
|
||||||
type: apiKey
|
type: apiKey
|
||||||
@@ -609,7 +627,46 @@ securityDefinitions:
|
|||||||
write:pets: modify pets in your account
|
write:pets: modify pets in your account
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
|
x-coffee: morning
|
||||||
description: OK
|
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:
|
schema:
|
||||||
$ref: '#/definitions/ApiResponse'
|
$ref: '#/definitions/ApiResponse'
|
||||||
"500":
|
"500":
|
||||||
@@ -739,3 +796,7 @@ definitions:
|
|||||||
externalDocs:
|
externalDocs:
|
||||||
description: Find out more about Swagger
|
description: Find out more about Swagger
|
||||||
url: http://swagger.io
|
url: http://swagger.io
|
||||||
|
externalPaths:
|
||||||
|
# this is illegal
|
||||||
|
test:
|
||||||
|
- summary: this is illegal.
|
||||||
Reference in New Issue
Block a user