We-worked model to remove resolver.

lookups are performed inline now. keeps things simpler, however it has a performance knock, so it's time to refine async building were possible.
This commit is contained in:
Dave Shanley
2022-08-11 14:54:25 -04:00
parent 7f0e966bd3
commit 248b4daa80
28 changed files with 462 additions and 279 deletions

View File

@@ -2,6 +2,8 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -14,22 +16,28 @@ func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] {
return FindItemInMap[*PathItem](exp, cb.Expression.Value)
}
func (cb *Callback) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
cb.Extensions = extensionMap
func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error {
cb.Extensions = ExtractExtensions(root)
// handle callback
var currentCB *yaml.Node
callbacks := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
if ok, _, _ := utils.IsNodeRefValue(root); ok {
r := LocateRefNode(root, idx)
if r != nil {
root = r
} else {
return nil
}
}
for i, callbackNode := range root.Content {
if i%2 == 0 {
currentCB = callbackNode
continue
}
callback, eErr := ExtractObjectRaw[*PathItem](callbackNode)
callback, eErr := ExtractObjectRaw[*PathItem](callbackNode, idx)
if eErr != nil {
return eErr
}

View File

@@ -0,0 +1,36 @@
package v3
import (
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestCallback_Build_Success(t *testing.T) {
yml := `'{$request.query.queryUrl}':
post:
requestBody:
description: Callback payload
content:
'application/json':
schema:
type: string
responses:
'200':
description: callback successfully processed`
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var n Callback
err := BuildModel(rootNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(rootNode.Content[0], nil)
assert.NoError(t, err)
assert.Len(t, n.Expression.Value, 1)
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -56,12 +57,8 @@ func (co *Components) FindCallback(callback string) *low.ValueReference[*Callbac
return FindItemInMap[*Callback](callback, co.Callbacks.Value)
}
func (co *Components) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
co.Extensions = extensionMap
func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
co.Extensions = ExtractExtensions(root)
// build out components asynchronously for speed. There could be some significant weight here.
skipChan := make(chan bool)
@@ -76,15 +73,15 @@ func (co *Components) Build(root *yaml.Node) error {
linkChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]])
callbackChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]])
go extractComponentValues[*Schema](SchemasLabel, root, skipChan, errorChan, schemaChan)
go extractComponentValues[*Parameter](ParametersLabel, root, skipChan, errorChan, paramChan)
go extractComponentValues[*Response](ResponsesLabel, root, skipChan, errorChan, responsesChan)
go extractComponentValues[*Example](ExamplesLabel, root, skipChan, errorChan, examplesChan)
go extractComponentValues[*RequestBody](RequestBodiesLabel, root, skipChan, errorChan, requestBodiesChan)
go extractComponentValues[*Header](HeadersLabel, root, skipChan, errorChan, headersChan)
go extractComponentValues[*SecurityScheme](SecuritySchemesLabel, root, skipChan, errorChan, securitySchemesChan)
go extractComponentValues[*Link](LinksLabel, root, skipChan, errorChan, linkChan)
go extractComponentValues[*Callback](CallbacksLabel, root, skipChan, errorChan, callbackChan)
go extractComponentValues[*Schema](SchemasLabel, root, skipChan, errorChan, schemaChan, idx)
go extractComponentValues[*Parameter](ParametersLabel, root, skipChan, errorChan, paramChan, idx)
go extractComponentValues[*Response](ResponsesLabel, root, skipChan, errorChan, responsesChan, idx)
go extractComponentValues[*Example](ExamplesLabel, root, skipChan, errorChan, examplesChan, idx)
go extractComponentValues[*RequestBody](RequestBodiesLabel, root, skipChan, errorChan, requestBodiesChan, idx)
go extractComponentValues[*Header](HeadersLabel, root, skipChan, errorChan, headersChan, idx)
go extractComponentValues[*SecurityScheme](SecuritySchemesLabel, root, skipChan, errorChan, securitySchemesChan, idx)
go extractComponentValues[*Link](LinksLabel, root, skipChan, errorChan, linkChan, idx)
go extractComponentValues[*Callback](CallbacksLabel, root, skipChan, errorChan, callbackChan, idx)
n := 0
total := 9
@@ -159,7 +156,7 @@ allDone:
}
func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.Node,
skip chan bool, errorChan chan<- error, resultChan chan<- low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]]) {
skip chan bool, errorChan chan<- error, resultChan chan<- low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]], idx *index.SpecIndex) {
_, nodeLabel, nodeValue := utils.FindKeyNodeFull(label, root.Content)
if nodeValue == nil {
skip <- true
@@ -177,7 +174,7 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
if err != nil {
errorChan <- err
}
err = n.Build(v)
err = n.Build(v, idx)
if err != nil {
errorChan <- err
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
@@ -21,9 +22,9 @@ func (en *Encoding) FindHeader(hType string) *low.ValueReference[*Header] {
return FindItemInMap[*Header](hType, en.Headers.Value)
}
func (en *Encoding) Build(root *yaml.Node) error {
func (en *Encoding) Build(root *yaml.Node, idx *index.SpecIndex) error {
headers, hL, hN, err := ExtractMapFlat[*Header](HeadersLabel, root)
headers, hL, hN, err := ExtractMapFlat[*Header](HeadersLabel, root, idx)
if err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -20,19 +21,14 @@ type Example struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (ex *Example) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
ex.Extensions = extensionMap
func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
ex.Extensions = ExtractExtensions(root)
// extract value
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
if vn != nil {
var n map[string]interface{}
err = vn.Decode(&n)
err := vn.Decode(&n)
if err != nil {
return err
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
@@ -11,12 +12,7 @@ type ExternalDoc struct {
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
func (ex *ExternalDoc) Build(root *yaml.Node, idx *index.SpecIndex) error {
ex.Extensions = ExtractExtensions(root)
return nil
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"strconv"
@@ -9,6 +10,13 @@ import (
"sync"
)
var KnownSchemas map[string]low.NodeReference[*Schema]
func init() {
KnownSchemas = make(map[string]low.NodeReference[*Schema])
}
func FindItemInMap[T any](item string, collection map[low.KeyReference[string]]low.ValueReference[T]) *low.ValueReference[T] {
for n, o := range collection {
if n.Value == item {
@@ -18,15 +26,34 @@ func FindItemInMap[T any](item string, collection map[low.KeyReference[string]]l
return nil
}
func ExtractSchema(root *yaml.Node) (*low.NodeReference[*Schema], error) {
_, schLabel, schNode := utils.FindKeyNodeFull(SchemaLabel, root.Content)
func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*Schema], error) {
var schLabel, schNode *yaml.Node
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
// locate reference in index.
ref := LocateRefNode(root, idx)
if ref != nil {
schNode = ref
schLabel = rl
}
} else {
_, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content)
if schNode != nil {
if h, _, _ := utils.IsNodeRefValue(schNode); h {
ref := LocateRefNode(schNode, idx)
if ref != nil {
schNode = ref
}
}
}
}
if schNode != nil {
var schema Schema
err := BuildModel(schNode, &schema)
if err != nil {
return nil, err
}
err = schema.Build(schNode)
err = schema.Build(schNode, idx)
if err != nil {
return nil, err
}
@@ -37,21 +64,49 @@ func ExtractSchema(root *yaml.Node) (*low.NodeReference[*Schema], error) {
var mapLock sync.Mutex
func ExtractObjectRaw[T low.Buildable[N], N any](root *yaml.Node) (T, error) {
func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
if rf, _, rv := utils.IsNodeRefValue(root); rf {
found := idx.GetMappedReferences()
if found != nil && found[rv] != nil {
return found[rv].Node
}
}
return nil
}
func ExtractObjectRaw[T low.Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (T, error) {
var n T = new(N)
err := BuildModel(root, n)
if err != nil {
return n, err
}
err = n.Build(root)
err = n.Build(root, idx)
if err != nil {
return n, err
}
return n, nil
}
func ExtractObject[T low.Buildable[N], N any](label string, root *yaml.Node) (low.NodeReference[T], error) {
_, ln, vn := utils.FindKeyNodeFull(label, root.Content)
func ExtractObject[T low.Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (low.NodeReference[T], error) {
var ln, vn *yaml.Node
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
// locate reference in index.
ref := LocateRefNode(root, idx)
if ref != nil {
vn = ref
ln = rl
}
} else {
_, ln, vn = utils.FindKeyNodeFull(label, root.Content)
if vn != nil {
if h, _, _ := utils.IsNodeRefValue(vn); h {
ref := LocateRefNode(vn, idx)
if ref != nil {
vn = ref
}
}
}
}
var n T = new(N)
err := BuildModel(vn, n)
if err != nil {
@@ -60,7 +115,7 @@ func ExtractObject[T low.Buildable[N], N any](label string, root *yaml.Node) (lo
if ln == nil {
return low.NodeReference[T]{}, nil
}
err = n.Build(vn)
err = n.Build(vn, idx)
if err != nil {
return low.NodeReference[T]{}, err
}
@@ -71,17 +126,41 @@ func ExtractObject[T low.Buildable[N], N any](label string, root *yaml.Node) (lo
}, nil
}
func ExtractArray[T low.Buildable[N], N any](label string, root *yaml.Node) ([]low.ValueReference[T], *yaml.Node, *yaml.Node, error) {
_, ln, vn := utils.FindKeyNodeFull(label, root.Content)
func ExtractArray[T low.Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) ([]low.ValueReference[T], *yaml.Node, *yaml.Node, error) {
var ln, vn *yaml.Node
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
// locate reference in index.
ref := LocateRefNode(root, idx)
if ref != nil {
vn = ref
ln = rl
}
} else {
_, ln, vn = utils.FindKeyNodeFull(label, root.Content)
if vn != nil {
if h, _, _ := utils.IsNodeRefValue(vn); h {
ref := LocateRefNode(vn, idx)
if ref != nil {
vn = ref
}
}
}
}
var items []low.ValueReference[T]
if vn != nil && ln != nil {
for _, node := range vn.Content {
if rf, _, _ := utils.IsNodeRefValue(node); rf {
ref := LocateRefNode(node, idx)
if ref != nil {
node = ref
}
}
var n T = new(N)
err := BuildModel(node, n)
if err != nil {
return []low.ValueReference[T]{}, ln, vn, err
}
berr := n.Build(node)
berr := n.Build(node, idx)
if berr != nil {
return nil, ln, vn, berr
}
@@ -94,7 +173,7 @@ func ExtractArray[T low.Buildable[N], N any](label string, root *yaml.Node) ([]l
return items, ln, vn, nil
}
func ExtractMapFlatNoLookup[PT low.Buildable[N], N any](root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[PT], error) {
func ExtractMapFlatNoLookup[PT low.Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (map[low.KeyReference[string]]low.ValueReference[PT], error) {
valueMap := make(map[low.KeyReference[string]]low.ValueReference[PT])
if utils.IsNodeMap(root) {
var currentKey *yaml.Node
@@ -108,7 +187,7 @@ func ExtractMapFlatNoLookup[PT low.Buildable[N], N any](root *yaml.Node) (map[lo
if err != nil {
return nil, err
}
berr := n.Build(node)
berr := n.Build(node, idx)
if berr != nil {
return nil, berr
}
@@ -124,8 +203,26 @@ func ExtractMapFlatNoLookup[PT low.Buildable[N], N any](root *yaml.Node) (map[lo
return valueMap, nil
}
func ExtractMapFlat[PT low.Buildable[N], N any](label string, root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[PT], *yaml.Node, *yaml.Node, error) {
_, labelNode, valueNode := utils.FindKeyNodeFull(label, root.Content)
func ExtractMapFlat[PT low.Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (map[low.KeyReference[string]]low.ValueReference[PT], *yaml.Node, *yaml.Node, error) {
var labelNode, valueNode *yaml.Node
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
// locate reference in index.
ref := LocateRefNode(root, idx)
if ref != nil {
valueNode = ref
labelNode = rl
}
} else {
_, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content)
if valueNode != nil {
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
ref := LocateRefNode(valueNode, idx)
if ref != nil {
valueNode = ref
}
}
}
}
if valueNode != nil {
var currentLabelNode *yaml.Node
valueMap := make(map[low.KeyReference[string]]low.ValueReference[PT])
@@ -134,6 +231,15 @@ func ExtractMapFlat[PT low.Buildable[N], N any](label string, root *yaml.Node) (
currentLabelNode = en
continue
}
// check our valueNode isn't a reference still.
if h, _, _ := utils.IsNodeRefValue(en); h {
ref := LocateRefNode(en, idx)
if ref != nil {
en = ref
}
}
if strings.HasPrefix(strings.ToLower(currentLabelNode.Value), "x-") {
continue // yo, don't pay any attention to extensions, not here anyway.
}
@@ -142,7 +248,7 @@ func ExtractMapFlat[PT low.Buildable[N], N any](label string, root *yaml.Node) (
if err != nil {
return nil, labelNode, valueNode, err
}
berr := n.Build(en)
berr := n.Build(en, idx)
if berr != nil {
return nil, labelNode, valueNode, berr
}
@@ -159,8 +265,19 @@ func ExtractMapFlat[PT low.Buildable[N], N any](label string, root *yaml.Node) (
return nil, labelNode, valueNode, nil
}
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)
func ExtractMap[PT low.Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (map[low.KeyReference[string]]map[low.KeyReference[string]]low.ValueReference[PT], error) {
var labelNode, valueNode *yaml.Node
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
// locate reference in index.
ref := LocateRefNode(root, idx)
if ref != nil {
valueNode = ref
labelNode = rl
}
} else {
_, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content)
}
if valueNode != nil {
var currentLabelNode *yaml.Node
valueMap := make(map[low.KeyReference[string]]low.ValueReference[PT])
@@ -177,7 +294,7 @@ func ExtractMap[PT low.Buildable[N], N any](label string, root *yaml.Node) (map[
if err != nil {
return nil, err
}
berr := n.Build(en)
berr := n.Build(en, idx)
if berr != nil {
return nil, berr
}
@@ -199,16 +316,13 @@ func ExtractMap[PT low.Buildable[N], N any](label string, root *yaml.Node) (map[
return nil, nil
}
func ExtractExtensions(root *yaml.Node) (map[low.KeyReference[string]]low.ValueReference[any], error) {
func ExtractExtensions(root *yaml.Node) map[low.KeyReference[string]]low.ValueReference[any] {
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
}
_ = ext.Value.Decode(&v)
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
@@ -243,15 +357,12 @@ func ExtractExtensions(root *yaml.Node) (map[low.KeyReference[string]]low.ValueR
}
if utils.IsNodeArray(ext.Value) {
var v []interface{}
err := ext.Value.Decode(&v)
if err != nil {
return nil, err
}
_ = ext.Value.Decode(&v)
extensionMap[low.KeyReference[string]{
Value: ext.Key.Value,
KeyNode: ext.Key,
}] = low.ValueReference[any]{Value: v, ValueNode: ext.Value}
}
}
return extensionMap, nil
return extensionMap
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
@@ -24,16 +25,11 @@ type Header struct {
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
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
h.Extensions = ExtractExtensions(root)
// handle examples if set.
exps, eErr := ExtractMap[*Example](ExamplesLabel, root)
exps, eErr := ExtractMap[*Example](ExamplesLabel, root, idx)
if eErr != nil {
return eErr
}
@@ -42,7 +38,7 @@ func (h *Header) Build(root *yaml.Node) error {
}
// handle schema
sch, sErr := ExtractSchema(root)
sch, sErr := ExtractSchema(root, idx)
if sErr != nil {
return nil
}
@@ -51,7 +47,7 @@ func (h *Header) Build(root *yaml.Node) error {
}
// handle content, if set.
con, cErr := ExtractMap[*MediaType](ContentLabel, root)
con, cErr := ExtractMap[*MediaType](ContentLabel, root, idx)
if cErr != nil {
return cErr
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -20,12 +21,8 @@ func (l *Link) FindParameter(pName string) *low.ValueReference[string] {
return FindItemInMap[string](pName, l.Parameters.Value)
}
func (l *Link) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
l.Extensions = extensionMap
func (l *Link) Build(root *yaml.Node, idx *index.SpecIndex) error {
l.Extensions = ExtractExtensions(root)
// extract parameters
_, pl, pv := utils.FindKeyNodeFull(ParametersLabel, root.Content)

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -26,14 +27,8 @@ func (mt *MediaType) GetAllExamples() map[low.KeyReference[string]]low.ValueRefe
return mt.Examples.Value
}
func (mt *MediaType) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
mt.Extensions = extensionMap
func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error {
mt.Extensions = ExtractExtensions(root)
// handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
@@ -42,7 +37,7 @@ func (mt *MediaType) Build(root *yaml.Node) error {
}
// handle schema
sch, sErr := ExtractSchema(root)
sch, sErr := ExtractSchema(root, idx)
if sErr != nil {
return nil
}
@@ -51,7 +46,7 @@ func (mt *MediaType) Build(root *yaml.Node) error {
}
// handle examples if set.
exps, expsL, expsN, eErr := ExtractMapFlat[*Example](ExamplesLabel, root)
exps, expsL, expsN, eErr := ExtractMapFlat[*Example](ExamplesLabel, root, idx)
if eErr != nil {
return eErr
}
@@ -64,9 +59,9 @@ func (mt *MediaType) Build(root *yaml.Node) error {
}
// handle encoding
encs, encsL, encsN, encErr := ExtractMapFlat[*Encoding](EncodingLabel, root)
encs, encsL, encsN, encErr := ExtractMapFlat[*Encoding](EncodingLabel, root, idx)
if encErr != nil {
return err
return encErr
}
if encs != nil {
mt.Encoding = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]{

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -22,32 +23,28 @@ type OAuthFlows struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (o *OAuthFlows) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
o.Extensions = extensionMap
func (o *OAuthFlows) Build(root *yaml.Node, idx *index.SpecIndex) error {
o.Extensions = ExtractExtensions(root)
v, vErr := ExtractObject[*OAuthFlow](ImplicitLabel, root)
v, vErr := ExtractObject[*OAuthFlow](ImplicitLabel, root, idx)
if vErr != nil {
return vErr
}
o.Implicit = v
v, vErr = ExtractObject[*OAuthFlow](PasswordLabel, root)
v, vErr = ExtractObject[*OAuthFlow](PasswordLabel, root, idx)
if vErr != nil {
return vErr
}
o.Password = v
v, vErr = ExtractObject[*OAuthFlow](ClientCredentialsLabel, root)
v, vErr = ExtractObject[*OAuthFlow](ClientCredentialsLabel, root, idx)
if vErr != nil {
return vErr
}
o.ClientCredentials = v
v, vErr = ExtractObject[*OAuthFlow](AuthorizationCodeLabel, root)
v, vErr = ExtractObject[*OAuthFlow](AuthorizationCodeLabel, root, idx)
if vErr != nil {
return vErr
}
@@ -68,12 +65,8 @@ func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] {
return FindItemInMap[string](scope, o.Scopes.Value)
}
func (o *OAuthFlow) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
o.Extensions = extensionMap
func (o *OAuthFlow) Build(root *yaml.Node, idx *index.SpecIndex) error {
o.Extensions = ExtractExtensions(root)
var currSec *yaml.Node

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
@@ -29,22 +30,18 @@ type Operation struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (o *Operation) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
o.Extensions = extensionMap
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
o.Extensions = ExtractExtensions(root)
// extract externalDocs
extDocs, dErr := ExtractObject[*ExternalDoc](ExternalDocsLabel, root)
extDocs, dErr := ExtractObject[*ExternalDoc](ExternalDocsLabel, root, idx)
if dErr != nil {
return dErr
}
o.ExternalDocs = extDocs
// extract parameters
params, ln, vn, pErr := ExtractArray[*Parameter](ParametersLabel, root)
params, ln, vn, pErr := ExtractArray[*Parameter](ParametersLabel, root, idx)
if pErr != nil {
return pErr
}
@@ -57,21 +54,21 @@ func (o *Operation) Build(root *yaml.Node) error {
}
// extract request body
rBody, rErr := ExtractObject[*RequestBody](RequestBodyLabel, root)
rBody, rErr := ExtractObject[*RequestBody](RequestBodyLabel, root, idx)
if rErr != nil {
return rErr
}
o.RequestBody = rBody
// extract responses
respBody, respErr := ExtractObject[*Responses](ResponsesLabel, root)
respBody, respErr := ExtractObject[*Responses](ResponsesLabel, root, idx)
if respErr != nil {
return rErr
}
o.Responses = respBody
// extract callbacks
callbacks, cbL, cbN, cbErr := ExtractMapFlat[*Callback](CallbacksLabel, root)
callbacks, cbL, cbN, cbErr := ExtractMapFlat[*Callback](CallbacksLabel, root, idx)
if cbErr != nil {
return cbErr
}
@@ -84,14 +81,14 @@ func (o *Operation) Build(root *yaml.Node) error {
}
// extract security
sec, sErr := ExtractObject[*SecurityRequirement](SecurityLabel, root)
sec, sErr := ExtractObject[*SecurityRequirement](SecurityLabel, root, idx)
if sErr != nil {
return sErr
}
o.Security = sec
// extract servers
servers, sl, sn, serErr := ExtractArray[*Server](ServersLabel, root)
servers, sl, sn, serErr := ExtractArray[*Server](ServersLabel, root, idx)
if serErr != nil {
return serErr
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -36,14 +37,8 @@ func (p *Parameter) FindExample(eType string) *low.ValueReference[*Example] {
return FindItemInMap[*Example](eType, p.Examples.Value)
}
func (p *Parameter) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
p.Extensions = extensionMap
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = ExtractExtensions(root)
// handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
@@ -52,14 +47,16 @@ func (p *Parameter) Build(root *yaml.Node) error {
}
// handle schema
sch, sErr := ExtractSchema(root)
sch, sErr := ExtractSchema(root, idx)
if sErr != nil {
return sErr
}
if sch != nil {
p.Schema = *sch
}
// handle examples if set.
exps, expsL, expsN, eErr := ExtractMapFlat[*Example](ExamplesLabel, root)
exps, expsL, expsN, eErr := ExtractMapFlat[*Example](ExamplesLabel, root, idx)
if eErr != nil {
return eErr
}
@@ -72,7 +69,7 @@ func (p *Parameter) Build(root *yaml.Node) error {
}
// handle content, if set.
con, cL, cN, cErr := ExtractMapFlat[*MediaType](ContentLabel, root)
con, cL, cN, cErr := ExtractMapFlat[*MediaType](ContentLabel, root, idx)
if cErr != nil {
return cErr
}

View File

@@ -2,6 +2,8 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"strings"
"sync"
@@ -33,14 +35,8 @@ func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
return nil
}
func (p *Paths) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
p.Extensions = extensionMap
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = ExtractExtensions(root)
skip := false
var currentNode *yaml.Node
@@ -60,11 +56,11 @@ func (p *Paths) Build(root *yaml.Node) error {
continue
}
var path = PathItem{}
err = BuildModel(pathNode, &path)
err := BuildModel(pathNode, &path)
if err != nil {
}
err = path.Build(pathNode)
err = path.Build(pathNode, idx)
if err != nil {
return err
}
@@ -100,12 +96,8 @@ type PathItem struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (p *PathItem) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
p.Extensions = extensionMap
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = ExtractExtensions(root)
skip := false
var currentNode *yaml.Node
@@ -114,6 +106,15 @@ func (p *PathItem) Build(root *yaml.Node) error {
var ops []low.NodeReference[*Operation]
if ok, _, _ := utils.IsNodeRefValue(root); ok {
r := LocateRefNode(root, idx)
if r != nil {
root = r
} else {
return nil
}
}
for i, pathNode := range root.Content {
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
skip = true
@@ -161,7 +162,9 @@ func (p *PathItem) Build(root *yaml.Node) error {
}
}
wg.Wait()
if len(ops) > 0 {
//wg.Wait()
}
//all operations have been superficially built,
//now we need to build out the operation, we will do this asynchronously for speed.
@@ -171,8 +174,8 @@ func (p *PathItem) Build(root *yaml.Node) error {
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
//build out the operation.
er := op.Value.Build(op.ValueNode)
if err != nil {
er := op.Value.Build(op.ValueNode, idx)
if er != nil {
errCh <- er
}
ch <- true

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
@@ -16,18 +17,11 @@ func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType]
return FindItemInMap[*MediaType](cType, rb.Content.Value)
}
func (rb *RequestBody) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
if extensionMap != nil {
rb.Extensions = extensionMap
}
func (rb *RequestBody) Build(root *yaml.Node, idx *index.SpecIndex) error {
rb.Extensions = ExtractExtensions(root)
// handle content, if set.
con, cL, cN, cErr := ExtractMapFlat[*MediaType](ContentLabel, root)
con, cL, cN, cErr := ExtractMapFlat[*MediaType](ContentLabel, root, idx)
if cErr != nil {
return cErr
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -15,9 +16,9 @@ type Responses struct {
Default low.NodeReference[*Response]
}
func (r *Responses) Build(root *yaml.Node) error {
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
if utils.IsNodeMap(root) {
codes, err := ExtractMapFlatNoLookup[*Response](root)
codes, err := ExtractMapFlatNoLookup[*Response](root, idx)
if err != nil {
return err
}
@@ -52,15 +53,11 @@ func (r *Response) FindLink(hType string) *low.ValueReference[*Link] {
return FindItemInMap[*Link](hType, r.Links.Value)
}
func (r *Response) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
r.Extensions = extensionMap
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
r.Extensions = ExtractExtensions(root)
// extract headers
headers, lN, kN, err := ExtractMapFlat[*Header](HeadersLabel, root)
headers, lN, kN, err := ExtractMapFlat[*Header](HeadersLabel, root, idx)
if err != nil {
return err
}
@@ -73,7 +70,7 @@ func (r *Response) Build(root *yaml.Node) error {
}
// handle content, if set.
con, clN, cN, cErr := ExtractMapFlat[*MediaType](ContentLabel, root)
con, clN, cN, cErr := ExtractMapFlat[*MediaType](ContentLabel, root, idx)
if cErr != nil {
return cErr
}
@@ -86,7 +83,7 @@ func (r *Response) Build(root *yaml.Node) error {
}
// handle links if set
links, linkLabel, linkValue, lErr := ExtractMapFlat[*Link](LinksLabel, root)
links, linkLabel, linkValue, lErr := ExtractMapFlat[*Link](LinksLabel, root, idx)
if lErr != nil {
return lErr
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"strconv"
@@ -61,20 +62,17 @@ func (s *Schema) FindProperty(name string) *low.ValueReference[*Schema] {
return FindItemInMap[*Schema](name, s.Properties.Value)
}
func (s *Schema) Build(root *yaml.Node) error {
return s.BuildLevel(root, 0)
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
return s.BuildLevel(root, idx, 0)
}
func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) error {
level++
if level > 50 {
if level > 10 {
return nil // we're done, son! too fricken deep.
}
err := s.extractExtensions(root)
if err != nil {
return err
}
s.extractExtensions(root)
// handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
@@ -85,7 +83,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
_, addPLabel, addPNode := utils.FindKeyNodeFull(AdditionalPropertiesLabel, root.Content)
if addPNode != nil {
if utils.IsNodeMap(addPNode) {
schema, serr := ExtractObjectRaw[*Schema](addPNode)
schema, serr := ExtractObjectRaw[*Schema](addPNode, idx)
if serr != nil {
return serr
}
@@ -102,7 +100,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
_, discLabel, discNode := utils.FindKeyNodeFull(DiscriminatorLabel, root.Content)
if discNode != nil {
var discriminator Discriminator
err = BuildModel(discNode, &discriminator)
err := BuildModel(discNode, &discriminator)
if err != nil {
return err
}
@@ -113,11 +111,11 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
_, extDocLabel, extDocNode := utils.FindKeyNodeFull(ExternalDocsLabel, root.Content)
if extDocNode != nil {
var exDoc ExternalDoc
err = BuildModel(extDocNode, &exDoc)
err := BuildModel(extDocNode, &exDoc)
if err != nil {
return err
}
err = exDoc.Build(extDocNode)
err = exDoc.Build(extDocNode, idx)
if err != nil {
return err
}
@@ -128,7 +126,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
_, xmlLabel, xmlNode := utils.FindKeyNodeFull(XMLLabel, root.Content)
if xmlNode != nil {
var xml XML
err = BuildModel(xmlNode, &xml)
err := BuildModel(xmlNode, &xml)
if err != nil {
return err
}
@@ -150,12 +148,21 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
currentProp = prop
continue
}
// check our prop isn't reference
if h, _, _ := utils.IsNodeRefValue(prop); h {
ref := LocateRefNode(prop, idx)
if ref != nil {
prop = ref
}
}
var property Schema
err = BuildModel(prop, &property)
err := BuildModel(prop, &property)
if err != nil {
return err
}
err = property.BuildLevel(prop, level)
err = property.BuildLevel(prop, idx, level)
if err != nil {
return err
}
@@ -179,11 +186,11 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema]
// make this async at some point to speed things up.
allOfLabel, allOfValue := buildSchema(&allOf, AllOfLabel, root, level, &errors)
anyOfLabel, anyOfValue := buildSchema(&anyOf, AnyOfLabel, root, level, &errors)
oneOfLabel, oneOfValue := buildSchema(&oneOf, OneOfLabel, root, level, &errors)
notLabel, notValue := buildSchema(&not, NotLabel, root, level, &errors)
itemsLabel, itemsValue := buildSchema(&items, ItemsLabel, root, level, &errors)
allOfLabel, allOfValue := buildSchema(&allOf, AllOfLabel, root, level, &errors, idx)
anyOfLabel, anyOfValue := buildSchema(&anyOf, AnyOfLabel, root, level, &errors, idx)
oneOfLabel, oneOfValue := buildSchema(&oneOf, OneOfLabel, root, level, &errors, idx)
notLabel, notValue := buildSchema(&not, NotLabel, root, level, &errors, idx)
itemsLabel, itemsValue := buildSchema(&items, ItemsLabel, root, level, &errors, idx)
if len(errors) > 0 {
// todo fix this
@@ -228,16 +235,13 @@ func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
return nil
}
func (s *Schema) extractExtensions(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
s.Extensions = extensionMap
return err
func (s *Schema) extractExtensions(root *yaml.Node) {
s.Extensions = ExtractExtensions(root)
}
func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNode *yaml.Node, level int, errors *[]error) (labelNode *yaml.Node, valueNode *yaml.Node) {
func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNode *yaml.Node, level int,
errors *[]error, idx *index.SpecIndex) (labelNode *yaml.Node, valueNode *yaml.Node) {
_, labelNode, valueNode = utils.FindKeyNodeFull(attribute, rootNode.Content)
//wg.Add(1)
if valueNode != nil {
@@ -248,7 +252,7 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
*errors = append(*errors, err)
return nil
}
err = schema.BuildLevel(vn, level)
err = schema.BuildLevel(vn, idx, level)
if err != nil {
*errors = append(*errors, err)
return nil

View File

@@ -111,7 +111,7 @@ additionalProperties: true `
mbErr := BuildModel(&rootNode, &sch)
assert.NoError(t, mbErr)
schErr := sch.Build(rootNode.Content[0])
schErr := sch.Build(rootNode.Content[0], nil)
assert.NoError(t, schErr)
assert.Equal(t, "something object", sch.Description.Value)
assert.True(t, sch.AdditionalProperties.Value.(bool))

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -28,14 +29,10 @@ type SecurityRequirement struct {
Value []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
}
func (ss *SecurityScheme) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
ss.Extensions = extensionMap
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
ss.Extensions = ExtractExtensions(root)
oa, oaErr := ExtractObject[*OAuthFlows](OAuthFlowsLabel, root)
oa, oaErr := ExtractObject[*OAuthFlows](OAuthFlowsLabel, root, idx)
if oaErr != nil {
return oaErr
}
@@ -57,7 +54,7 @@ func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference
return nil
}
func (sr *SecurityRequirement) Build(root *yaml.Node) error {
func (sr *SecurityRequirement) Build(root *yaml.Node, idx *index.SpecIndex) error {
if utils.IsNodeArray(root) {
var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
for _, n := range root.Content {

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -17,7 +18,7 @@ type Server struct {
Variables low.NodeReference[map[string]low.NodeReference[*ServerVariable]]
}
func (s *Server) Build(root *yaml.Node) error {
func (s *Server) Build(root *yaml.Node, idx *index.SpecIndex) error {
kn, vars := utils.FindKeyNode(VariablesLabel, root.Content)
if vars == nil {
return nil

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
@@ -17,16 +18,11 @@ type Tag struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (t *Tag) Build(root *yaml.Node) error {
// extract extensions
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
t.Extensions = extensionMap
func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
t.Extensions = ExtractExtensions(root)
// extract externalDocs
extDocs, dErr := ExtractObject[*ExternalDoc](ExternalDocsLabel, root)
extDocs, dErr := ExtractObject[*ExternalDoc](ExternalDocsLabel, root, idx)
if dErr != nil {
return dErr
}

View File

@@ -15,10 +15,6 @@ type XML struct {
}
func (x *XML) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
x.Extensions = extensionMap
x.Extensions = ExtractExtensions(root)
return nil
}

View File

@@ -1,13 +1,16 @@
package low
import "gopkg.in/yaml.v3"
import (
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
)
type HasNode interface {
GetNode() *yaml.Node
}
type Buildable[T any] interface {
Build(node *yaml.Node) error
Build(node *yaml.Node, idx *index.SpecIndex) error
*T
}

View File

@@ -16,9 +16,9 @@ import (
)
const (
localResolve int = 0
httpResolve int = 1
fileResolve int = 2
LocalResolve int = 0
HttpResolve int = 1
FileResolve int = 2
)
// Reference is a wrapper around *yaml.Node results to make things more manageable when performing
@@ -72,7 +72,8 @@ type SpecIndex struct {
responsesRefs map[string]*Reference // top level responses
headersRefs map[string]*Reference // top level responses
examplesRefs map[string]*Reference // top level examples
linksRefs map[string]map[string][]*Reference // all links
callbacksRefs map[string]map[string][]*Reference // all links
linksRefs map[string]map[string][]*Reference // all callbacks
operationTagsRefs map[string]map[string][]*Reference // tags found in operations
operationDescriptionRefs map[string]map[string]*Reference // descriptions in operations.
operationSummaryRefs map[string]map[string]*Reference // summaries in operations
@@ -99,6 +100,7 @@ type SpecIndex struct {
globalHeadersCount int // component headers
globalExamplesCount int // component examples
globalLinksCount int // component links
globalCallbacksCount int // component callbacks
globalCallbacks int // component callbacks.
pathCount int // number of paths
operationCount int // number of operations
@@ -207,6 +209,7 @@ func NewSpecIndex(rootNode *yaml.Node) *SpecIndex {
index.responsesRefs = make(map[string]*Reference)
index.headersRefs = make(map[string]*Reference)
index.examplesRefs = make(map[string]*Reference)
index.callbacksRefs = make(map[string]map[string][]*Reference)
index.linksRefs = make(map[string]map[string][]*Reference)
index.callbackRefs = make(map[string]*Reference)
index.externalSpecIndex = make(map[string]*SpecIndex)
@@ -256,6 +259,7 @@ func NewSpecIndex(rootNode *yaml.Node) *SpecIndex {
index.GetInlineUniqueParamCount,
index.GetOperationTagsCount,
index.GetGlobalLinksCount,
index.GetGlobalCallbacksCount,
}
wg.Add(len(countFuncs))
@@ -872,13 +876,59 @@ func (index *SpecIndex) GetTotalTagsCount() int {
return index.totalTagsCount
}
// GetGlobalLinksCount for each response of each operation method, multiple links can be defined
// GetGlobalCallbacksCount for each response of each operation method, multiple links can be defined
func (index *SpecIndex) GetGlobalCallbacksCount() int {
if index.root == nil {
return -1
}
if index.globalCallbacksCount > 0 {
return index.globalCallbacksCount
}
index.pathRefsLock.Lock()
for path, p := range index.pathRefs {
for _, m := range p {
// look through method for callbacks
callbacks, _ := yamlpath.NewPath("$..callbacks")
res, _ := callbacks.Find(m.Node)
if len(res) > 0 {
for _, callback := range res[0].Content {
if utils.IsNodeMap(callback) {
ref := &Reference{
Definition: m.Name,
Name: m.Name,
Node: callback,
}
if index.callbacksRefs[path] == nil {
index.callbacksRefs[path] = make(map[string][]*Reference)
}
if len(index.callbacksRefs[path][m.Name]) > 0 {
index.callbacksRefs[path][m.Name] = append(index.callbacksRefs[path][m.Name], ref)
}
index.callbacksRefs[path][m.Name] = []*Reference{ref}
index.globalCallbacksCount++
}
}
}
}
}
index.pathRefsLock.Unlock()
return index.globalCallbacksCount
}
// GetGlobalLinksCount for each response of each operation method, multiple callbacks can be defined
func (index *SpecIndex) GetGlobalLinksCount() int {
if index.root == nil {
return -1
}
if index.globalLinksCount > 0 {
if index.globalCallbacksCount > 0 {
return index.globalLinksCount
}
@@ -900,7 +950,6 @@ func (index *SpecIndex) GetGlobalLinksCount() int {
Name: m.Name,
Node: link,
}
if index.linksRefs[path] == nil {
index.linksRefs[path] = make(map[string][]*Reference)
}
@@ -1421,17 +1470,17 @@ func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Re
return index.lookupFileReference(id)
}
switch determineReferenceResolveType(componentId) {
case localResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
return index.findComponentInRoot(componentId)
switch DetermineReferenceResolveType(componentId) {
case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
return index.FindComponentInRoot(componentId)
case httpResolve:
case HttpResolve:
uri := strings.Split(componentId, "#")
if len(uri) == 2 {
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
}
case fileResolve:
case FileResolve:
uri := strings.Split(componentId, "#")
if len(uri) == 2 {
return index.performExternalLookup(uri, componentId, fileLookup, parent)
@@ -1450,23 +1499,23 @@ func (index *SpecIndex) GetAllSummariesCount() int {
return len(index.allSummaries)
}
/* private */
func determineReferenceResolveType(ref string) int {
func DetermineReferenceResolveType(ref string) int {
if ref != "" && ref[0] == '#' {
return localResolve
return LocalResolve
}
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
return httpResolve
return HttpResolve
}
if strings.Contains(ref, ".json") ||
strings.Contains(ref, ".yaml") ||
strings.Contains(ref, ".yml") {
return fileResolve
return FileResolve
}
return -1
}
/* private */
func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) {
var name string
for i, schema := range schemasNode.Content {
@@ -1650,7 +1699,7 @@ func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
} else {
foundRef := externalSpecIndex.findComponentInRoot(uri[1])
foundRef := externalSpecIndex.FindComponentInRoot(uri[1])
if foundRef != nil {
foundNode = foundRef.Node
}
@@ -1670,7 +1719,7 @@ func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
return nil
}
func (index *SpecIndex) findComponentInRoot(componentId string) *Reference {
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)

View File

@@ -348,7 +348,7 @@ func TestSpecIndex_TestEmptyBrokenReferences(t *testing.T) {
assert.Equal(t, 2, index.GetGlobalTagsCount())
assert.Equal(t, 3, index.GetTotalTagsCount())
assert.Equal(t, 2, index.GetOperationTagsCount())
assert.Equal(t, 2, index.GetGlobalLinksCount())
assert.Equal(t, 4, index.GetGlobalLinksCount())
assert.Equal(t, 0, index.GetComponentParameterCount())
assert.Equal(t, 2, index.GetOperationsParameterCount())
assert.Equal(t, 1, index.GetInlineDuplicateParamCount())

View File

@@ -5,7 +5,6 @@ import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/resolver"
"github.com/pb33f/libopenapi/utils"
"sync"
)
@@ -15,26 +14,27 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}}
// build an index
rsolvr := resolver.NewResolver(index.NewSpecIndex(info.RootNode))
idx := index.NewSpecIndex(info.RootNode)
//rsolvr := resolver.NewResolver()
// todo handle errors
rsolvr.Resolve()
//rsolvr.Resolve()
var wg sync.WaitGroup
var errors []error
var runExtraction = func(info *datamodel.SpecInfo, doc *v3.Document,
runFunc func(i *datamodel.SpecInfo, d *v3.Document) error,
var runExtraction = func(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex,
runFunc func(i *datamodel.SpecInfo, d *v3.Document, idx *index.SpecIndex) error,
ers *[]error,
wg *sync.WaitGroup) {
if er := runFunc(info, doc); er != nil {
if er := runFunc(info, doc, idx); er != nil {
*ers = append(*ers, er)
}
wg.Done()
}
extractionFuncs := []func(i *datamodel.SpecInfo, d *v3.Document) error{
extractionFuncs := []func(i *datamodel.SpecInfo, d *v3.Document, idx *index.SpecIndex) error{
extractInfo,
extractServers,
extractTags,
@@ -45,7 +45,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
}
wg.Add(len(extractionFuncs))
for _, f := range extractionFuncs {
go runExtraction(info, &doc, f, &errors, &wg)
go runExtraction(info, &doc, idx, f, &errors, &wg)
}
wg.Wait()
@@ -56,7 +56,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
return &doc, nil
}
func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
func extractInfo(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
_, ln, vn := utils.FindKeyNodeFull(v3.InfoLabel, info.RootNode.Content)
if vn != nil {
ir := v3.Info{}
@@ -71,8 +71,8 @@ func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
return nil
}
func extractSecurity(info *datamodel.SpecInfo, doc *v3.Document) error {
sec, sErr := v3.ExtractObject[*v3.SecurityRequirement](v3.SecurityLabel, info.RootNode)
func extractSecurity(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
sec, sErr := v3.ExtractObject[*v3.SecurityRequirement](v3.SecurityLabel, info.RootNode, idx)
if sErr != nil {
return sErr
}
@@ -80,8 +80,8 @@ func extractSecurity(info *datamodel.SpecInfo, doc *v3.Document) error {
return nil
}
func extractExternalDocs(info *datamodel.SpecInfo, doc *v3.Document) error {
extDocs, dErr := v3.ExtractObject[*v3.ExternalDoc](v3.ExternalDocsLabel, info.RootNode)
func extractExternalDocs(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
extDocs, dErr := v3.ExtractObject[*v3.ExternalDoc](v3.ExternalDocsLabel, info.RootNode, idx)
if dErr != nil {
return dErr
}
@@ -89,7 +89,7 @@ func extractExternalDocs(info *datamodel.SpecInfo, doc *v3.Document) error {
return nil
}
func extractComponents(info *datamodel.SpecInfo, doc *v3.Document) error {
func extractComponents(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
_, ln, vn := utils.FindKeyNodeFull(v3.ComponentsLabel, info.RootNode.Content)
if vn != nil {
ir := v3.Components{}
@@ -97,14 +97,14 @@ func extractComponents(info *datamodel.SpecInfo, doc *v3.Document) error {
if err != nil {
return err
}
err = ir.Build(vn)
err = ir.Build(vn, idx)
nr := low.NodeReference[*v3.Components]{Value: &ir, ValueNode: vn, KeyNode: ln}
doc.Components = nr
}
return nil
}
func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
func extractServers(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
_, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content)
if vn != nil {
if utils.IsNodeArray(vn) {
@@ -116,7 +116,7 @@ func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
if err != nil {
return err
}
srvr.Build(srvN)
srvr.Build(srvN, idx)
servers = append(servers, low.ValueReference[*v3.Server]{
Value: &srvr,
ValueNode: srvN,
@@ -133,7 +133,7 @@ func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
return nil
}
func extractTags(info *datamodel.SpecInfo, doc *v3.Document) error {
func extractTags(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
_, ln, vn := utils.FindKeyNodeFull(v3.TagsLabel, info.RootNode.Content)
if vn != nil {
if utils.IsNodeArray(vn) {
@@ -145,7 +145,7 @@ func extractTags(info *datamodel.SpecInfo, doc *v3.Document) error {
if err != nil {
return err
}
tag.Build(tagN)
tag.Build(tagN, idx)
tags = append(tags, low.ValueReference[*v3.Tag]{
Value: &tag,
ValueNode: tagN,
@@ -162,11 +162,11 @@ func extractTags(info *datamodel.SpecInfo, doc *v3.Document) error {
return nil
}
func extractPaths(info *datamodel.SpecInfo, doc *v3.Document) error {
func extractPaths(info *datamodel.SpecInfo, doc *v3.Document, idx *index.SpecIndex) error {
_, ln, vn := utils.FindKeyNodeFull(v3.PathsLabel, info.RootNode.Content)
if vn != nil {
ir := v3.Paths{}
err := ir.Build(vn)
err := ir.Build(vn, idx)
if err != nil {
return err
}

View File

@@ -24,6 +24,17 @@ func BenchmarkCreateDocument(b *testing.B) {
}
}
func BenchmarkCreateDocument_Stripe(b *testing.B) {
data, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
for i := 0; i < b.N; i++ {
_, err := CreateDocument(info)
if err != nil {
panic("this should not error")
}
}
}
func TestCreateDocument(t *testing.T) {
assert.Equal(t, "3.0.1", doc.Version.Value)
assert.Equal(t, "Burger Shop", doc.Info.Value.Title.Value)
@@ -254,8 +265,9 @@ func TestCreateDocument_Components_Schemas(t *testing.T) {
assert.NotNil(t, fries.Value)
assert.Len(t, fries.Value.Properties.Value, 3)
p := fries.Value.FindProperty("favoriteDrink")
assert.Equal(t, "a frosty cold beverage can be coke or sprite",
fries.Value.FindProperty("favoriteDrink").Value.Description.Value)
p.Value.Description.Value)
}

View File

@@ -372,6 +372,17 @@ func IsNodeBoolValue(node *yaml.Node) bool {
return node.Tag == "!!bool"
}
func IsNodeRefValue(node *yaml.Node) (bool, *yaml.Node, string) {
for i, r := range node.Content {
if i%2 == 0 {
if r.Value == "$ref" {
return true, r, node.Content[i+1].Value
}
}
}
return false, nil, ""
}
// FixContext will clean up a JSONpath string to be correctly traversable.
func FixContext(context string) string {