Working in generics now to reduce code.

generics are a little funky still. sigh.
This commit is contained in:
Dave Shanley
2022-08-05 21:57:04 -04:00
parent 4bb6a9ad49
commit 13781cbbde
19 changed files with 615 additions and 268 deletions

View File

@@ -1,12 +1,12 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
type Callback struct { type Callback struct {
Node *yaml.Node Node *yaml.Node
Expression map[string]Path Expression map[string]PathItem
Extensions map[string]low.ObjectReference Extensions map[string]low.ObjectReference
} }

View File

@@ -1,15 +1,31 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
)
const (
EncodingLabel = "encoding"
) )
type Encoding struct { type Encoding struct {
Node *yaml.Node ContentType low.NodeReference[string]
ContentType low.NodeReference[string] Headers map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Header]
Headers map[string]Parameter Style low.NodeReference[string]
Style low.NodeReference[string] Explode low.NodeReference[bool]
Explode low.NodeReference[bool] AllowReserved low.NodeReference[bool]
AllowReserved low.NodeReference[bool] }
func (en Encoding) Build(root *yaml.Node) error {
headers, err := ExtractMap[*Header](HeadersLabel, root)
if err != nil {
return err
}
if headers != nil {
en.Headers = headers
}
return nil
} }

View File

@@ -1,15 +1,29 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
)
const (
ExamplesLabel = "examples"
ExampleLabel = "example"
) )
type Example struct { type Example struct {
Node *yaml.Node Summary low.NodeReference[string]
Summary low.NodeReference[string] Description low.NodeReference[string]
Description low.NodeReference[string] Value low.NodeReference[any]
Value low.ObjectReference ExternalValue low.NodeReference[string]
ExternalValue low.NodeReference[string] Extensions map[low.KeyReference[string]]low.ValueReference[any]
Extensions map[string]low.ObjectReference }
func (ex Example) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
ex.Extensions = extensionMap
return nil
} }

View File

@@ -2,10 +2,21 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3"
) )
type ExternalDoc struct { type ExternalDoc struct {
Description low.NodeReference[string] Description low.NodeReference[string]
URL low.NodeReference[string] URL low.NodeReference[string]
Extensions map[string]low.ObjectReference Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (ex *ExternalDoc) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
ex.Extensions = extensionMap
return nil
} }

View File

@@ -0,0 +1,123 @@
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"strconv"
"sync"
)
func ExtractSchema(root *yaml.Node) (*low.NodeReference[*Schema], error) {
_, schLabel, schNode := utils.FindKeyNodeFull(SchemaLabel, root.Content)
if schNode != nil {
var schema Schema
err := BuildModel(schNode, &schema)
if err != nil {
return nil, err
}
err = schema.Build(schNode, 0)
if err != nil {
return nil, err
}
return &low.NodeReference[*Schema]{Value: &schema, KeyNode: schLabel, ValueNode: schNode}, nil
}
return nil, nil
}
var mapLock sync.Mutex
func ExtractMap[PT low.Buildable[N], N any](label string, root *yaml.Node) (map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[PT], error) {
_, labelNode, valueNode := utils.FindKeyNodeFull(label, root.Content)
if valueNode != nil {
var currentLabelNode *yaml.Node
valueMap := make(map[low.KeyReference[string]]low.ValueReference[PT])
for i, en := range valueNode.Content {
if i%2 == 0 {
currentLabelNode = en
continue
}
var n PT = new(N)
err := BuildModel(valueNode, n)
if err != nil {
return nil, err
}
berr := n.Build(valueNode)
if berr != nil {
return nil, berr
}
valueMap[low.KeyReference[string]{
Value: currentLabelNode.Value,
KeyNode: currentLabelNode,
}] = low.ValueReference[PT]{
Value: n,
ValueNode: en,
}
}
resMap := make(map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[PT])
resMap[low.KeyReference[string]{
Value: labelNode.Value,
KeyNode: labelNode,
}] = valueMap
return resMap, nil
}
return nil, nil
}
func ExtractExtensions(root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[any], error) {
extensions := utils.FindExtensionNodes(root.Content)
extensionMap := make(map[low.KeyReference[string]]low.ValueReference[any])
for _, ext := range extensions {
if utils.IsNodeMap(ext.Value) {
var v interface{}
err := ext.Value.Decode(&v)
if err != nil {
return nil, err
}
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: v, ValueNode: ext.Value}
}
if utils.IsNodeStringValue(ext.Value) {
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: ext.Value.Value, ValueNode: ext.Value}
}
if utils.IsNodeFloatValue(ext.Value) {
fv, _ := strconv.ParseFloat(ext.Value.Value, 64)
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: fv, ValueNode: ext.Value}
}
if utils.IsNodeIntValue(ext.Value) {
iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64)
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: iv, ValueNode: ext.Value}
}
if utils.IsNodeBoolValue(ext.Value) {
bv, _ := strconv.ParseBool(ext.Value.Value)
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: bv, ValueNode: ext.Value}
}
if utils.IsNodeArray(ext.Value) {
var v []interface{}
err := ext.Value.Decode(&v)
if err != nil {
return nil, err
}
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: v, ValueNode: ext.Value}
}
}
return extensionMap, nil
}

View File

@@ -0,0 +1,61 @@
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3"
)
const (
HeadersLabel = "headers"
)
type Header struct {
Description low.NodeReference[string]
Required low.NodeReference[bool]
Deprecated low.NodeReference[bool]
AllowEmptyValue low.NodeReference[bool]
Style low.NodeReference[string]
Explode low.NodeReference[bool]
AllowReserved low.NodeReference[bool]
Schema low.NodeReference[*Schema]
Example low.NodeReference[any]
Examples map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Example]
Content map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*MediaType]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (h *Header) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
h.Extensions = extensionMap
// handle examples if set.
exps, eErr := ExtractMap[*Example](ExamplesLabel, root)
if eErr != nil {
return eErr
}
if exps != nil {
h.Examples = exps
}
// handle schema
sch, sErr := ExtractSchema(root)
if sErr != nil {
return nil
}
if sch != nil {
h.Schema = *sch
}
// handle content, if set.
con, cErr := ExtractMap[*MediaType](ContentLabel, root)
if cErr != nil {
return cErr
}
h.Content = con
return nil
}

View File

@@ -1,14 +1,59 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
) )
type MediaType struct { type MediaType struct {
Node *yaml.Node Schema low.NodeReference[*Schema]
Schema Schema Example low.NodeReference[any]
Example low.ObjectReference Examples map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Example]
Examples map[string]Example Encoding map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Encoding]
Encoding map[string]Encoding Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (mt *MediaType) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
mt.Extensions = extensionMap
// handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
if expNode != nil {
mt.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
}
// handle schema
sch, sErr := ExtractSchema(root)
if sErr != nil {
return nil
}
if sch != nil {
mt.Schema = *sch
}
// handle examples if set.
exps, eErr := ExtractMap[*Example](ExamplesLabel, root)
if eErr != nil {
return eErr
}
if exps != nil {
mt.Examples = exps
}
// handle encoding
encs, encErr := ExtractMap[*Encoding](EncodingLabel, root)
if encErr != nil {
return err
}
if encs != nil {
mt.Encoding = encs
}
return nil
} }

View File

@@ -377,60 +377,3 @@ func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, error
} }
lwg.Done() lwg.Done()
} }
func ExtractExtensions(root *yaml.Node) (map[low.NodeReference[string]]low.NodeReference[any], error) {
extensions := utils.FindExtensionNodes(root.Content)
extensionMap := make(map[low.NodeReference[string]]low.NodeReference[any])
for _, ext := range extensions {
if utils.IsNodeMap(ext.Value) {
var v interface{}
err := ext.Value.Decode(&v)
if err != nil {
return nil, err
}
extensionMap[low.NodeReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.NodeReference[any]{Value: v, KeyNode: ext.Key}
}
if utils.IsNodeStringValue(ext.Value) {
extensionMap[low.NodeReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.NodeReference[any]{Value: ext.Value.Value, ValueNode: ext.Value}
}
if utils.IsNodeFloatValue(ext.Value) {
fv, _ := strconv.ParseFloat(ext.Value.Value, 64)
extensionMap[low.NodeReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.NodeReference[any]{Value: fv, ValueNode: ext.Value}
}
if utils.IsNodeIntValue(ext.Value) {
iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64)
extensionMap[low.NodeReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.NodeReference[any]{Value: iv, ValueNode: ext.Value}
}
if utils.IsNodeBoolValue(ext.Value) {
bv, _ := strconv.ParseBool(ext.Value.Value)
extensionMap[low.NodeReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.NodeReference[any]{Value: bv, ValueNode: ext.Value}
}
if utils.IsNodeArray(ext.Value) {
var v []interface{}
err := ext.Value.Decode(&v)
if err != nil {
return nil, err
}
extensionMap[low.NodeReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.NodeReference[any]{Value: v, ValueNode: ext.Value}
}
}
return extensionMap, nil
}

View File

@@ -2,7 +2,6 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -15,19 +14,19 @@ type Operation struct {
Tags []low.NodeReference[string] Tags []low.NodeReference[string]
Summary low.NodeReference[string] Summary low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
ExternalDocs *low.NodeReference[*ExternalDoc] ExternalDocs low.NodeReference[*ExternalDoc]
OperationId low.NodeReference[string] OperationId low.NodeReference[string]
Parameters []low.NodeReference[*Parameter] Parameters []low.NodeReference[*Parameter]
RequestBody *low.NodeReference[*RequestBody] RequestBody low.NodeReference[*RequestBody]
Responses *low.NodeReference[*Responses] Responses low.NodeReference[*Responses]
Callbacks map[low.NodeReference[string]]low.NodeReference[*Callback] Callbacks map[low.KeyReference[string]]low.ValueReference[*Callback]
Deprecated *low.NodeReference[bool] Deprecated low.NodeReference[bool]
Security []low.NodeReference[*SecurityRequirement] Security []low.NodeReference[*SecurityRequirement]
Servers []low.NodeReference[*Server] Servers []low.NodeReference[*Server]
Extensions map[low.NodeReference[string]]low.NodeReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error { func (o *Operation) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root) extensionMap, err := ExtractExtensions(root)
if err != nil { if err != nil {
@@ -43,7 +42,7 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
if err != nil { if err != nil {
return err return err
} }
o.ExternalDocs = &low.NodeReference[*ExternalDoc]{ o.ExternalDocs = low.NodeReference[*ExternalDoc]{
Value: &externalDoc, Value: &externalDoc,
KeyNode: ln, KeyNode: ln,
ValueNode: exDocs, ValueNode: exDocs,
@@ -61,7 +60,7 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
if err != nil { if err != nil {
return err return err
} }
err = param.Build(pN, idx) err = param.Build(pN)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -2,13 +2,13 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( const (
SchemaLabel = "schema" SchemaLabel = "schema"
ContentLabel = "content"
) )
type Parameter struct { type Parameter struct {
@@ -23,11 +23,23 @@ type Parameter struct {
AllowReserved low.NodeReference[bool] AllowReserved low.NodeReference[bool]
Schema low.NodeReference[*Schema] Schema low.NodeReference[*Schema]
Example low.NodeReference[any] Example low.NodeReference[any]
Examples map[low.NodeReference[string]]low.NodeReference[*Example] Examples map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*Example]
Extensions map[low.NodeReference[string]]low.NodeReference[any] Content map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[*MediaType]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error { func (p *Parameter) GetContent(cType string) *low.ValueReference[*MediaType] {
for _, c := range p.Content {
for n, o := range c {
if n.Value == cType {
return &o
}
}
}
return nil
}
func (p *Parameter) Build(root *yaml.Node) error {
// extract extensions // extract extensions
extensionMap, err := ExtractExtensions(root) extensionMap, err := ExtractExtensions(root)
@@ -36,27 +48,24 @@ func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
p.Extensions = extensionMap p.Extensions = extensionMap
// handle schema // handle example if set.
_, schLabel, schNode := utils.FindKeyNodeFull(SchemaLabel, root.Content) _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
if schNode != nil { if expNode != nil {
// deal with schema flat props p.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
var schema Schema
err = BuildModel(schNode, &schema)
if err != nil {
return err
}
// now comes the part where things may get hairy, schemas are recursive.
// which means we could be here forever if our resolver has some unknown bug in it.
// in order to prevent this from happening, we will add a counter that tracks the depth
// and will hard stop once we reach 50 levels. That's too deep for any data structure IMHO.
err = schema.Build(schNode, idx, 0)
if err != nil {
return err
}
p.Schema = low.NodeReference[*Schema]{Value: &schema, KeyNode: schLabel, ValueNode: schNode}
} }
// handle schema
sch, sErr := ExtractSchema(root)
if sErr != nil {
return sErr
}
p.Schema = *sch
// handle content, if set.
con, cErr := ExtractMap[*MediaType](ContentLabel, root)
if cErr != nil {
return cErr
}
p.Content = con
return nil return nil
} }

View File

@@ -2,7 +2,6 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strings" "strings"
"sync" "sync"
@@ -21,19 +20,20 @@ const (
) )
type Paths struct { type Paths struct {
Paths map[low.NodeReference[string]]low.NodeReference[*Path] PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem]
Extensions map[low.NodeReference[string]]low.NodeReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
func (p *Paths) GetPathMap() map[string]*Path { func (p *Paths) GetPath(path string) *low.ValueReference[*PathItem] {
pMap := make(map[string]*Path) for k, p := range p.PathItems {
for i, pv := range p.Paths { if k.Value == path {
pMap[i.Value] = pv.Value return &p
}
} }
return pMap return nil
} }
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { func (p *Paths) Build(root *yaml.Node) error {
// extract extensions // extract extensions
extensionMap, err := ExtractExtensions(root) extensionMap, err := ExtractExtensions(root)
@@ -44,7 +44,7 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
skip := false skip := false
var currentNode *yaml.Node var currentNode *yaml.Node
pathsMap := make(map[low.NodeReference[string]]low.NodeReference[*Path]) pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
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-") {
@@ -59,32 +59,32 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
currentNode = pathNode currentNode = pathNode
continue continue
} }
var path = Path{} var path = PathItem{}
err = BuildModel(pathNode, &path) err = BuildModel(pathNode, &path)
if err != nil { if err != nil {
} }
err = path.Build(pathNode, idx) err = path.Build(pathNode)
if err != nil { if err != nil {
return err return err
} }
// add bulk here // add bulk here
pathsMap[low.NodeReference[string]{ pathsMap[low.KeyReference[string]{
Value: currentNode.Value, Value: currentNode.Value,
KeyNode: currentNode, KeyNode: currentNode,
}] = low.NodeReference[*Path]{ }] = low.ValueReference[*PathItem]{
Value: &path, Value: &path,
ValueNode: pathNode, ValueNode: pathNode,
} }
} }
p.Paths = pathsMap p.PathItems = pathsMap
return nil return nil
} }
type Path struct { type PathItem struct {
Description low.NodeReference[string] Description low.NodeReference[string]
Summary low.NodeReference[string] Summary low.NodeReference[string]
Get *low.NodeReference[*Operation] Get *low.NodeReference[*Operation]
@@ -97,10 +97,10 @@ type Path struct {
Trace *low.NodeReference[*Operation] Trace *low.NodeReference[*Operation]
Servers []*low.NodeReference[*Server] Servers []*low.NodeReference[*Server]
Parameters []*low.NodeReference[*Parameter] Parameters []*low.NodeReference[*Parameter]
Extensions map[low.NodeReference[string]]low.NodeReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
func (p *Path) Build(root *yaml.Node, idx *index.SpecIndex) error { func (p *PathItem) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root) extensionMap, err := ExtractExtensions(root)
if err != nil { if err != nil {
return err return err
@@ -171,7 +171,7 @@ func (p *Path) Build(root *yaml.Node, idx *index.SpecIndex) error {
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) { var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
// build out the operation. // build out the operation.
er := op.Value.Build(op.ValueNode, idx) er := op.Value.Build(op.ValueNode)
if err != nil { if err != nil {
errCh <- er errCh <- er
} }

View File

@@ -2,7 +2,6 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strconv" "strconv"
@@ -12,7 +11,7 @@ import (
const ( const (
PropertiesLabel = "properties" PropertiesLabel = "properties"
AdditionalPropertiesLabel = "additionalProperties" AdditionalPropertiesLabel = "additionalProperties"
ExampleLabel = "example" XMLLabel = "xml"
ItemsLabel = "items" ItemsLabel = "items"
AllOfLabel = "allOf" AllOfLabel = "allOf"
AnyOfLabel = "anyOf" AnyOfLabel = "anyOf"
@@ -44,7 +43,7 @@ type Schema struct {
AnyOf []low.NodeReference[*Schema] AnyOf []low.NodeReference[*Schema]
Not []low.NodeReference[*Schema] Not []low.NodeReference[*Schema]
Items []low.NodeReference[*Schema] Items []low.NodeReference[*Schema]
Properties map[low.NodeReference[string]]*low.NodeReference[*Schema] Properties map[low.KeyReference[string]]*low.ValueReference[*Schema]
AdditionalProperties low.NodeReference[any] AdditionalProperties low.NodeReference[any]
Description low.NodeReference[string] Description low.NodeReference[string]
Default low.NodeReference[any] Default low.NodeReference[any]
@@ -52,14 +51,14 @@ type Schema struct {
Discriminator low.NodeReference[*Discriminator] Discriminator low.NodeReference[*Discriminator]
ReadOnly low.NodeReference[bool] ReadOnly low.NodeReference[bool]
WriteOnly low.NodeReference[bool] WriteOnly low.NodeReference[bool]
XML *low.NodeReference[*XML] XML low.NodeReference[*XML]
ExternalDocs *low.NodeReference[*ExternalDoc] ExternalDocs low.NodeReference[*ExternalDoc]
Example low.NodeReference[any] Example low.NodeReference[any]
Deprecated low.NodeReference[bool] Deprecated low.NodeReference[bool]
Extensions map[low.NodeReference[string]]low.NodeReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
func (s *Schema) FindProperty(name string) *low.NodeReference[*Schema] { func (s *Schema) FindProperty(name string) *low.ValueReference[*Schema] {
for k, v := range s.Properties { for k, v := range s.Properties {
if k.Value == name { if k.Value == name {
return v return v
@@ -68,17 +67,16 @@ func (s *Schema) FindProperty(name string) *low.NodeReference[*Schema] {
return nil return nil
} }
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error { func (s *Schema) Build(root *yaml.Node, level int) error {
level++ level++
if level > 50 { if level > 50 {
return nil // we're done, son! too fricken deep. return nil // we're done, son! too fricken deep.
} }
extensionMap, err := ExtractExtensions(root) err := s.extractExtensions(root)
if err != nil { if err != nil {
return err return err
} }
s.Extensions = extensionMap
// handle example if set. // handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
@@ -90,7 +88,10 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error {
if addPNode != nil { if addPNode != nil {
if utils.IsNodeMap(addPNode) { if utils.IsNodeMap(addPNode) {
var props map[string]interface{} var props map[string]interface{}
addPNode.Decode(&props) err = addPNode.Decode(&props)
if err != nil {
return err
}
s.AdditionalProperties = low.NodeReference[any]{Value: props, KeyNode: addPLabel, ValueNode: addPNode} s.AdditionalProperties = low.NodeReference[any]{Value: props, KeyNode: addPLabel, ValueNode: addPNode}
} }
@@ -111,11 +112,43 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error {
s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode}
} }
// handle externalDocs if set.
_, extDocLabel, extDocNode := utils.FindKeyNodeFull(ExternalDocsLabel, root.Content)
if extDocNode != nil {
var exDoc ExternalDoc
err = BuildModel(extDocNode, &exDoc)
if err != nil {
return err
}
err = exDoc.Build(extDocNode)
if err != nil {
return err
}
s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode}
}
// handle xml if set.
_, xmlLabel, xmlNode := utils.FindKeyNodeFull(XMLLabel, root.Content)
if xmlNode != nil {
var xml XML
err = BuildModel(xmlNode, &xml)
if err != nil {
return err
}
// extract extensions if set.
err = xml.Build(xmlNode)
if err != nil {
return err
}
s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode}
}
// handle properties // handle properties
_, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content) _, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content)
if propsNode != nil { if propsNode != nil {
propertyMap := make(map[low.NodeReference[string]]*low.NodeReference[*Schema]) propertyMap := make(map[low.KeyReference[string]]*low.ValueReference[*Schema])
var currentProp *yaml.Node var currentProp *yaml.Node
var wg sync.WaitGroup
for i, prop := range propsNode.Content { for i, prop := range propsNode.Content {
if i%2 == 0 { if i%2 == 0 {
currentProp = prop currentProp = prop
@@ -127,17 +160,15 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error {
if err != nil { if err != nil {
return err return err
} }
err = property.Build(prop, idx, level) err = property.Build(prop, level)
if err != nil { if err != nil {
return err return err
} }
propertyMap[low.NodeReference[string]{ propertyMap[low.KeyReference[string]{
Value: currentProp.Value, Value: currentProp.Value,
KeyNode: propLabel, KeyNode: propLabel,
ValueNode: propsNode, }] = &low.ValueReference[*Schema]{
}] = &low.NodeReference[*Schema]{
Value: &property, Value: &property,
KeyNode: currentProp,
ValueNode: prop, ValueNode: prop,
} }
} }
@@ -145,16 +176,15 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error {
// extract all sub-schemas // extract all sub-schemas
var errors []error var errors []error
var wg sync.WaitGroup
var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema] var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema]
// make this async at some point to speed things up. // make this async at some point to speed things up.
buildSchema(&allOf, AllOfLabel, idx, root, level, &errors, &wg) buildSchema(&allOf, AllOfLabel, root, level, &errors, &wg)
buildSchema(&anyOf, AnyOfLabel, idx, root, level, &errors, &wg) buildSchema(&anyOf, AnyOfLabel, root, level, &errors, &wg)
buildSchema(&oneOf, OneOfLabel, idx, root, level, &errors, &wg) buildSchema(&oneOf, OneOfLabel, root, level, &errors, &wg)
buildSchema(&not, NotLabel, idx, root, level, &errors, &wg) buildSchema(&not, NotLabel, root, level, &errors, &wg)
buildSchema(&items, ItemsLabel, idx, root, level, &errors, &wg) buildSchema(&items, ItemsLabel, root, level, &errors, &wg)
//wg.Wait() //wg.Wait()
if len(errors) > 0 { if len(errors) > 0 {
@@ -182,7 +212,16 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex, level int) error {
return nil return nil
} }
func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, idx *index.SpecIndex, rootNode *yaml.Node, level int, errors *[]error, wg *sync.WaitGroup) { func (s *Schema) extractExtensions(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
s.Extensions = extensionMap
return err
}
func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNode *yaml.Node, level int, errors *[]error, wg *sync.WaitGroup) {
_, labelNode, valueNode := utils.FindKeyNodeFull(attribute, rootNode.Content) _, labelNode, valueNode := utils.FindKeyNodeFull(attribute, rootNode.Content)
//wg.Add(1) //wg.Add(1)
if valueNode != nil { if valueNode != nil {
@@ -194,7 +233,7 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, idx *i
*errors = append(*errors, err) *errors = append(*errors, err)
return nil return nil
} }
err = schema.Build(vn, idx, level) err = schema.Build(vn, level)
if err != nil { if err != nil {
*errors = append(*errors, err) *errors = append(*errors, err)
return nil return nil

View File

@@ -83,15 +83,25 @@ properties:
somethingB: somethingB:
type: object type: object
description: an object description: an object
externalDocs:
description: the best docs
url: https://pb33f.io
properties: properties:
somethingBProp: somethingBProp:
type: string type: string
description: something b subprop description: something b subprop
example: picnics are nice. example: picnics are nice.
xml:
name: an xml thing
namespace: an xml namespace
prefix: a prefix
attribute: true
wrapped: false
x-pizza: love
additionalProperties: additionalProperties:
why: yes why: yes
thatIs: true thatIs: true
additionalProperties: true` additionalProperties: true `
var rootNode yaml.Node var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(testSpec), &rootNode) mErr := yaml.Unmarshal([]byte(testSpec), &rootNode)
@@ -101,13 +111,26 @@ additionalProperties: true`
mbErr := BuildModel(&rootNode, &sch) mbErr := BuildModel(&rootNode, &sch)
assert.NoError(t, mbErr) assert.NoError(t, mbErr)
schErr := sch.Build(rootNode.Content[0], nil, 0) schErr := sch.Build(rootNode.Content[0], 0)
assert.NoError(t, schErr) assert.NoError(t, schErr)
assert.Equal(t, "something object", sch.Description.Value) assert.Equal(t, "something object", sch.Description.Value)
assert.True(t, sch.AdditionalProperties.Value.(bool)) assert.True(t, sch.AdditionalProperties.Value.(bool))
assert.Len(t, sch.Properties, 2) assert.Len(t, sch.Properties, 2)
v := sch.FindProperty("somethingB") v := sch.FindProperty("somethingB")
assert.Equal(t, "https://pb33f.io", v.Value.ExternalDocs.Value.URL.Value)
assert.Equal(t, "the best docs", v.Value.ExternalDocs.Value.Description.Value)
j := v.Value.FindProperty("somethingBProp")
assert.NotNil(t, j.Value)
assert.NotNil(t, j.Value.XML.Value)
assert.Equal(t, "an xml thing", j.Value.XML.Value.Name.Value)
assert.Equal(t, "an xml namespace", j.Value.XML.Value.Namespace.Value)
assert.Equal(t, "a prefix", j.Value.XML.Value.Prefix.Value)
assert.Equal(t, true, j.Value.XML.Value.Attribute.Value)
assert.Len(t, j.Value.XML.Value.Extensions, 1)
assert.NotNil(t, v.Value.AdditionalProperties.Value) assert.NotNil(t, v.Value.AdditionalProperties.Value)
var addProps map[string]interface{} var addProps map[string]interface{}

View File

@@ -15,7 +15,7 @@ type Tag struct {
Name low.NodeReference[string] Name low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
ExternalDocs low.NodeReference[*ExternalDoc] ExternalDocs low.NodeReference[*ExternalDoc]
Extensions map[low.NodeReference[string]]low.NodeReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
func (t *Tag) Build(root *yaml.Node) error { func (t *Tag) Build(root *yaml.Node) error {

View File

@@ -1,16 +1,24 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
type XML struct { type XML struct {
Node *yaml.Node Name low.NodeReference[string]
Name low.NodeReference[string] Namespace low.NodeReference[string]
Namespace low.NodeReference[string] Prefix low.NodeReference[string]
Prefix low.NodeReference[string] Attribute low.NodeReference[bool]
Attribute low.NodeReference[string] Wrapped low.NodeReference[bool]
Wrapped low.NodeReference[bool] Extensions map[low.KeyReference[string]]low.ValueReference[any]
Extensions map[string]low.ObjectReference }
func (x *XML) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
x.Extensions = extensionMap
return nil
} }

View File

@@ -6,8 +6,9 @@ type HasNode interface {
GetNode() *yaml.Node GetNode() *yaml.Node
} }
type Buildable interface { type Buildable[T any] interface {
Build(node *yaml.Node) error Build(node *yaml.Node) error
*T
} }
type NodeReference[T any] struct { type NodeReference[T any] struct {
@@ -16,6 +17,16 @@ type NodeReference[T any] struct {
KeyNode *yaml.Node KeyNode *yaml.Node
} }
type KeyReference[T any] struct {
Value T
KeyNode *yaml.Node
}
type ValueReference[T any] struct {
Value T
ValueNode *yaml.Node
}
type ObjectReference struct { type ObjectReference struct {
Value interface{} Value interface{}
ValueNode *yaml.Node ValueNode *yaml.Node
@@ -25,3 +36,11 @@ type ObjectReference struct {
func (n NodeReference[T]) IsEmpty() bool { func (n NodeReference[T]) IsEmpty() bool {
return n.KeyNode == nil && n.ValueNode == nil return n.KeyNode == nil && n.ValueNode == nil
} }
func (n NodeReference[T]) IsMapKeyNode() bool {
return n.KeyNode != nil && n.ValueNode == nil
}
func (n NodeReference[T]) IsMapValueNode() bool {
return n.KeyNode == nil && n.ValueNode != nil
}

View File

@@ -15,8 +15,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}} doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}}
// build an index // build an index
idx := index.NewSpecIndex(info.RootNode) rsolvr := resolver.NewResolver(index.NewSpecIndex(info.RootNode))
rsolvr := resolver.NewResolver(idx)
// todo handle errors // todo handle errors
rsolvr.Resolve() rsolvr.Resolve()
@@ -24,18 +23,18 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
var wg sync.WaitGroup var wg sync.WaitGroup
var errors []error var errors []error
var runExtraction = func(info *datamodel.SpecInfo, doc *v3.Document, var runExtraction = func(info *datamodel.SpecInfo, doc *v3.Document,
runFunc func(i *datamodel.SpecInfo, d *v3.Document, idx *index.SpecIndex) error, runFunc func(i *datamodel.SpecInfo, d *v3.Document) error,
ers *[]error, ers *[]error,
wg *sync.WaitGroup) { wg *sync.WaitGroup) {
if er := runFunc(info, doc, idx); er != nil { if er := runFunc(info, doc); er != nil {
*ers = append(*ers, er) *ers = append(*ers, er)
} }
wg.Done() wg.Done()
} }
extractionFuncs := []func(i *datamodel.SpecInfo, d *v3.Document, idx *index.SpecIndex) error{ extractionFuncs := []func(i *datamodel.SpecInfo, d *v3.Document) error{
extractInfo, extractInfo,
extractServers, extractServers,
extractTags, extractTags,
@@ -54,7 +53,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
return &doc, nil return &doc, nil
} }
func extractInfo(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.InfoLabel, info.RootNode.Content) _, ln, vn := utils.FindKeyNodeFull(v3.InfoLabel, info.RootNode.Content)
if vn != nil { if vn != nil {
ir := v3.Info{} ir := v3.Info{}
@@ -69,7 +68,7 @@ func extractInfo(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecInde
return nil return nil
} }
func extractServers(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content) _, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content)
if vn != nil { if vn != nil {
if utils.IsNodeArray(vn) { if utils.IsNodeArray(vn) {
@@ -95,7 +94,7 @@ func extractServers(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecI
return nil return nil
} }
func extractTags(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { func extractTags(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.TagsLabel, info.RootNode.Content) _, ln, vn := utils.FindKeyNodeFull(v3.TagsLabel, info.RootNode.Content)
if vn != nil { if vn != nil {
if utils.IsNodeArray(vn) { if utils.IsNodeArray(vn) {
@@ -121,11 +120,11 @@ func extractTags(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecInde
return nil return nil
} }
func extractPaths(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error { func extractPaths(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.PathsLabel, info.RootNode.Content) _, ln, vn := utils.FindKeyNodeFull(v3.PathsLabel, info.RootNode.Content)
if vn != nil { if vn != nil {
ir := v3.Paths{} ir := v3.Paths{}
err := ir.Build(vn, idx) err := ir.Build(vn)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -1,109 +1,126 @@
package openapi package openapi
import ( import (
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"io/ioutil" "io/ioutil"
"testing" "testing"
) )
var doc *v3.Document var doc *v3.Document
func init() { func init() {
data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
doc, _ = CreateDocument(info) doc, _ = CreateDocument(info)
} }
func BenchmarkCreateDocument(b *testing.B) { func BenchmarkCreateDocument(b *testing.B) {
data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.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) {
assert.Equal(t, "3.0.1", doc.Version.Value) assert.Equal(t, "3.0.1", doc.Version.Value)
assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value) assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value)
assert.NotEmpty(t, doc.Info.Value.Title.Value) assert.NotEmpty(t, doc.Info.Value.Title.Value)
} }
func TestCreateDocument_Info(t *testing.T) { func TestCreateDocument_Info(t *testing.T) {
assert.Equal(t, "https://pb33f.io", doc.Info.Value.TermsOfService.Value) 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, "pb33f", doc.Info.Value.Contact.Value.Name.Value)
assert.Equal(t, "buckaroo@pb33f.io", doc.Info.Value.Contact.Value.Email.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, "https://pb33f.io", doc.Info.Value.Contact.Value.URL.Value)
assert.Equal(t, "pb33f", doc.Info.Value.License.Value.Name.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) assert.Equal(t, "https://pb33f.io/made-up", doc.Info.Value.License.Value.URL.Value)
} }
func TestCreateDocument_Servers(t *testing.T) { func TestCreateDocument_Servers(t *testing.T) {
assert.Len(t, doc.Servers, 2) assert.Len(t, doc.Servers, 2)
server1 := doc.Servers[0] server1 := doc.Servers[0]
server2 := doc.Servers[1] server2 := doc.Servers[1]
// server 1 // server 1
assert.Equal(t, "{scheme}://api.pb33f.io", server1.Value.URL.Value) assert.Equal(t, "{scheme}://api.pb33f.io", server1.Value.URL.Value)
assert.NotEmpty(t, server1.Value.Description.Value) assert.NotEmpty(t, server1.Value.Description.Value)
assert.Len(t, server1.Value.Variables.Value, 1) assert.Len(t, server1.Value.Variables.Value, 1)
assert.Len(t, server1.Value.Variables.Value["scheme"].Value.Enum, 2) assert.Len(t, server1.Value.Variables.Value["scheme"].Value.Enum, 2)
assert.Equal(t, server1.Value.Variables.Value["scheme"].Value.Default.Value, "https") assert.Equal(t, server1.Value.Variables.Value["scheme"].Value.Default.Value, "https")
assert.NotEmpty(t, server1.Value.Variables.Value["scheme"].Value.Description.Value) assert.NotEmpty(t, server1.Value.Variables.Value["scheme"].Value.Description.Value)
// server 2 // server 2
assert.Equal(t, "https://{domain}.{host}.com", server2.Value.URL.Value) assert.Equal(t, "https://{domain}.{host}.com", server2.Value.URL.Value)
assert.NotEmpty(t, server2.Value.Description.Value) assert.NotEmpty(t, server2.Value.Description.Value)
assert.Len(t, server2.Value.Variables.Value, 2) assert.Len(t, server2.Value.Variables.Value, 2)
assert.Equal(t, server2.Value.Variables.Value["domain"].Value.Default.Value, "api") assert.Equal(t, server2.Value.Variables.Value["domain"].Value.Default.Value, "api")
assert.NotEmpty(t, server2.Value.Variables.Value["domain"].Value.Description.Value) assert.NotEmpty(t, server2.Value.Variables.Value["domain"].Value.Description.Value)
assert.NotEmpty(t, server2.Value.Variables.Value["host"].Value.Description.Value) assert.NotEmpty(t, server2.Value.Variables.Value["host"].Value.Description.Value)
assert.Equal(t, server2.Value.Variables.Value["host"].Value.Default.Value, "pb33f.io") assert.Equal(t, server2.Value.Variables.Value["host"].Value.Default.Value, "pb33f.io")
assert.Equal(t, "1.2", doc.Info.Value.Version.Value) assert.Equal(t, "1.2", doc.Info.Value.Version.Value)
} }
func TestCreateDocument_Tags(t *testing.T) { func TestCreateDocument_Tags(t *testing.T) {
assert.Len(t, doc.Tags, 2) assert.Len(t, doc.Tags, 2)
// tag1 // tag1
assert.Equal(t, "Burgers", doc.Tags[0].Value.Name.Value) assert.Equal(t, "Burgers", doc.Tags[0].Value.Name.Value)
assert.NotEmpty(t, doc.Tags[0].Value.Description.Value) assert.NotEmpty(t, doc.Tags[0].Value.Description.Value)
assert.NotNil(t, doc.Tags[0].Value.ExternalDocs.Value) assert.NotNil(t, doc.Tags[0].Value.ExternalDocs.Value)
assert.Equal(t, "https://pb33f.io", doc.Tags[0].Value.ExternalDocs.Value.URL.Value) assert.Equal(t, "https://pb33f.io", doc.Tags[0].Value.ExternalDocs.Value.URL.Value)
assert.NotEmpty(t, doc.Tags[0].Value.ExternalDocs.Value.URL.Value) assert.NotEmpty(t, doc.Tags[0].Value.ExternalDocs.Value.URL.Value)
assert.Len(t, doc.Tags[0].Value.Extensions, 7) assert.Len(t, doc.Tags[0].Value.Extensions, 7)
for key, extension := range doc.Tags[0].Value.Extensions { for key, extension := range doc.Tags[0].Value.Extensions {
switch key.Value { switch key.Value {
case "x-internal-ting": case "x-internal-ting":
assert.Equal(t, "somethingSpecial", extension.Value) assert.Equal(t, "somethingSpecial", extension.Value)
case "x-internal-tong": case "x-internal-tong":
assert.Equal(t, int64(1), extension.Value) assert.Equal(t, int64(1), extension.Value)
case "x-internal-tang": case "x-internal-tang":
assert.Equal(t, 1.2, extension.Value) assert.Equal(t, 1.2, extension.Value)
case "x-internal-tung": case "x-internal-tung":
assert.Equal(t, true, extension.Value) assert.Equal(t, true, extension.Value)
case "x-internal-arr": case "x-internal-arr":
assert.Len(t, extension.Value, 2) assert.Len(t, extension.Value, 2)
assert.Equal(t, "one", extension.Value.([]interface{})[0].(string)) assert.Equal(t, "one", extension.Value.([]interface{})[0].(string))
case "x-internal-arrmap": case "x-internal-arrmap":
assert.Len(t, extension.Value, 2) assert.Len(t, extension.Value, 2)
assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"]) assert.Equal(t, "now", extension.Value.([]interface{})[0].(map[string]interface{})["what"])
case "x-something-else": case "x-something-else":
// crazy times in the upside down. this API should be avoided for the higher up use cases. // 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. // 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"]) assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"])
} }
} }
/// tag2 /// tag2
assert.Equal(t, "Dressing", doc.Tags[1].Value.Name.Value) assert.Equal(t, "Dressing", doc.Tags[1].Value.Name.Value)
assert.NotEmpty(t, doc.Tags[1].Value.Description.Value) assert.NotEmpty(t, doc.Tags[1].Value.Description.Value)
assert.NotNil(t, doc.Tags[1].Value.ExternalDocs.Value) assert.NotNil(t, doc.Tags[1].Value.ExternalDocs.Value)
assert.Equal(t, "https://pb33f.io", doc.Tags[1].Value.ExternalDocs.Value.URL.Value) assert.Equal(t, "https://pb33f.io", doc.Tags[1].Value.ExternalDocs.Value.URL.Value)
assert.NotEmpty(t, doc.Tags[1].Value.ExternalDocs.Value.URL.Value) assert.NotEmpty(t, doc.Tags[1].Value.ExternalDocs.Value.URL.Value)
assert.Len(t, doc.Tags[1].Value.Extensions, 0) assert.Len(t, doc.Tags[1].Value.Extensions, 0)
}
func TestCreateDocument_Paths(t *testing.T) {
assert.Len(t, doc.Paths.Value.PathItems, 6)
burgerId := doc.Paths.Value.GetPath("/burgers/{burgerId}")
assert.NotNil(t, burgerId)
assert.Len(t, burgerId.Value.Get.Value.Parameters, 2)
param := burgerId.Value.Get.Value.Parameters[1]
assert.Equal(t, "burgerHeader", param.Value.Name.Value)
prop := param.Value.Schema.Value.FindProperty("burgerTheme")
assert.Equal(t, "something about a theme?", prop.Value.Description.Value)
assert.Equal(t, "big-mac", param.Value.Example.Value)
// check content
pContent := param.Value.GetContent("application/json")
assert.Equal(t, "somethingNice", pContent.Value.Example.Value)
} }

View File

@@ -321,6 +321,27 @@ components:
example: big-mac example: big-mac
description: the name of the burger. use this to order your food description: the name of the burger. use this to order your food
required: true required: true
content:
application/json:
example: somethingNice
encoding:
burgerTheme:
contentType: text/plain
headers:
someHeader:
description: this is a header
schema:
type: string
schema:
type: object
required: [burgerTheme, burgerTime]
properties:
burgerTheme:
type: string
description: something about a theme?
burgerTime:
type: number
description: number of burgers ordered this year.
BurgerId: BurgerId:
in: path in: path
name: burgerId name: burgerId