mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
building out tests for extraction functions.
Same functions will power every other model. Found some opprotunities to add control over circular references, when and how to fire errors and what not. All in all, some robust code additions will improve the library significantly.
This commit is contained in:
@@ -59,9 +59,14 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
eChan := make(chan error)
|
eChan := make(chan error)
|
||||||
var buildPathItem = func(cNode, pNode *yaml.Node, b chan<- pathBuildResult, e chan<- error) {
|
var buildPathItem = func(cNode, pNode *yaml.Node, b chan<- pathBuildResult, e chan<- error) {
|
||||||
if ok, _, _ := utils.IsNodeRefValue(pNode); ok {
|
if ok, _, _ := utils.IsNodeRefValue(pNode); ok {
|
||||||
r := low.LocateRefNode(pNode, idx)
|
r, err := low.LocateRefNode(pNode, idx)
|
||||||
if r != nil {
|
if r != nil {
|
||||||
pNode = r
|
pNode = r
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
e <- fmt.Errorf("path item build failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
e <- fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
e <- fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
||||||
pNode.Content[1].Value, pNode.Content[1].Line, pNode.Content[1].Column)
|
pNode.Content[1].Value, pNode.Content[1].Line, pNode.Content[1].Column)
|
||||||
|
|||||||
@@ -124,9 +124,14 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
|
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
|
||||||
r := low.LocateRefNode(pathNode, idx)
|
r, err := low.LocateRefNode(pathNode, idx)
|
||||||
if r != nil {
|
if r != nil {
|
||||||
pathNode = r
|
pathNode = r
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
return fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
return fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
||||||
pathNode.Content[1].Value, pathNode.Content[1].Line, pathNode.Content[1].Column)
|
pathNode.Content[1].Value, pathNode.Content[1].Line, pathNode.Content[1].Column)
|
||||||
@@ -172,9 +177,14 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
|
|
||||||
//build out the operation.
|
//build out the operation.
|
||||||
if ok, _, _ := utils.IsNodeRefValue(op.ValueNode); ok {
|
if ok, _, _ := utils.IsNodeRefValue(op.ValueNode); ok {
|
||||||
r := low.LocateRefNode(op.ValueNode, idx)
|
r, err := low.LocateRefNode(op.ValueNode, idx)
|
||||||
if r != nil {
|
if r != nil {
|
||||||
op.ValueNode = r
|
op.ValueNode = r
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
errCh <- fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// any reference would be the second node.
|
// any reference would be the second node.
|
||||||
errCh <- fmt.Errorf("cannot extract reference: %s", op.ValueNode.Content[1].Value)
|
errCh <- fmt.Errorf("cannot extract reference: %s", op.ValueNode.Content[1].Value)
|
||||||
|
|||||||
@@ -66,9 +66,14 @@ func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] {
|
|||||||
|
|
||||||
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
if h, _, _ := utils.IsNodeRefValue(root); h {
|
if h, _, _ := utils.IsNodeRefValue(root); h {
|
||||||
ref := low.LocateRefNode(root, idx)
|
ref, err := low.LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
root = ref
|
root = ref
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
return fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d",
|
return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d",
|
||||||
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
|
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
|
||||||
@@ -156,9 +161,14 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
|
|
||||||
// check our prop isn't reference
|
// check our prop isn't reference
|
||||||
if h, _, _ := utils.IsNodeRefValue(prop); h {
|
if h, _, _ := utils.IsNodeRefValue(prop); h {
|
||||||
ref := low.LocateRefNode(prop, idx)
|
ref, err := low.LocateRefNode(prop, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
prop = ref
|
prop = ref
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
return fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d",
|
return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d",
|
||||||
prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line)
|
prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line)
|
||||||
@@ -291,11 +301,6 @@ func countSubSchemaItems(node *yaml.Node) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type schemaBuildResult struct {
|
|
||||||
k low.KeyReference[string]
|
|
||||||
v low.ValueReference[*Schema]
|
|
||||||
}
|
|
||||||
|
|
||||||
type schemaProxyBuildResult struct {
|
type schemaProxyBuildResult struct {
|
||||||
k low.KeyReference[string]
|
k low.KeyReference[string]
|
||||||
v low.ValueReference[*SchemaProxy]
|
v low.ValueReference[*SchemaProxy]
|
||||||
@@ -314,9 +319,14 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
|
|||||||
// build out a SchemaProxy for every sub-schema.
|
// build out a SchemaProxy for every sub-schema.
|
||||||
build := func(kn *yaml.Node, vn *yaml.Node, c chan *low.ValueReference[*SchemaProxy], e chan error) {
|
build := func(kn *yaml.Node, vn *yaml.Node, c chan *low.ValueReference[*SchemaProxy], e chan error) {
|
||||||
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
||||||
ref := low.LocateRefNode(vn, idx)
|
ref, cerr := low.LocateRefNode(vn, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
vn = ref
|
vn = ref
|
||||||
|
if cerr != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
e <- fmt.Errorf("build schema failed: %s", cerr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
||||||
vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column)
|
vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column)
|
||||||
@@ -344,9 +354,14 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
|
|||||||
|
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
|
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
|
||||||
ref := low.LocateRefNode(valueNode, idx)
|
ref, err := low.LocateRefNode(valueNode, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
valueNode = ref
|
valueNode = ref
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
errors <- fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
||||||
valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column)
|
valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column)
|
||||||
@@ -375,9 +390,14 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
|
|||||||
refBuilds := 0
|
refBuilds := 0
|
||||||
for _, vn := range valueNode.Content {
|
for _, vn := range valueNode.Content {
|
||||||
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
||||||
ref := low.LocateRefNode(vn, idx)
|
ref, err := low.LocateRefNode(vn, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
vn = ref
|
vn = ref
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
errors <- fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
||||||
vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column)
|
vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column)
|
||||||
@@ -412,10 +432,15 @@ func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*S
|
|||||||
errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d"
|
errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d"
|
||||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||||
// locate reference in index.
|
// locate reference in index.
|
||||||
ref := low.LocateRefNode(root, idx)
|
ref, err := low.LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
schNode = ref
|
schNode = ref
|
||||||
schLabel = rl
|
schLabel = rl
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
return nil, fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf(errStr,
|
return nil, fmt.Errorf(errStr,
|
||||||
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
|
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
|
||||||
@@ -424,9 +449,14 @@ func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*S
|
|||||||
_, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content)
|
_, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content)
|
||||||
if schNode != nil {
|
if schNode != nil {
|
||||||
if h, _, _ := utils.IsNodeRefValue(schNode); h {
|
if h, _, _ := utils.IsNodeRefValue(schNode); h {
|
||||||
ref := low.LocateRefNode(schNode, idx)
|
ref, err := low.LocateRefNode(schNode, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
schNode = ref
|
schNode = ref
|
||||||
|
if err != nil {
|
||||||
|
if !idx.AllowCircularReferenceResolving() {
|
||||||
|
return nil, fmt.Errorf("build schema failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf(errStr,
|
return nil, fmt.Errorf(errStr,
|
||||||
schNode.Content[1].Value, schNode.Content[1].Line, schNode.Content[1].Column)
|
schNode.Content[1].Value, schNode.Content[1].Line, schNode.Content[1].Column)
|
||||||
|
|||||||
@@ -740,8 +740,8 @@ allOf:
|
|||||||
err := low.BuildModel(&idxNode, &sch)
|
err := low.BuildModel(&idxNode, &sch)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
_ = sch.Build(idxNode.Content[0], idx)
|
err = sch.Build(idxNode.Content[0], idx)
|
||||||
assert.Nil(t, sch.AllOf.Value[0].Value.Schema())
|
assert.Error(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -916,12 +916,8 @@ func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) {
|
|||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
|
||||||
s, _ := ExtractSchema(idxNode.Content[0], idx)
|
_, err := ExtractSchema(idxNode.Content[0], idx)
|
||||||
|
assert.Error(t, err)
|
||||||
b := s.Value.Schema()
|
|
||||||
assert.Nil(t, b)
|
|
||||||
assert.Error(t, s.Value.GetBuildError())
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSchema_DoNothing(t *testing.T) {
|
func TestExtractSchema_DoNothing(t *testing.T) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||||
|
"go.uber.org/zap"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -22,7 +23,7 @@ func FindItemInMap[T any](item string, collection map[KeyReference[string]]Value
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
|
func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, error) {
|
||||||
if rf, _, rv := utils.IsNodeRefValue(root); rf {
|
if rf, _, rv := utils.IsNodeRefValue(root); rf {
|
||||||
// run through everything and return as soon as we find a match.
|
// run through everything and return as soon as we find a match.
|
||||||
// this operates as fast as possible as ever
|
// this operates as fast as possible as ever
|
||||||
@@ -69,7 +70,30 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
|
|||||||
for _, collection := range collections {
|
for _, collection := range collections {
|
||||||
found = collection()
|
found = collection()
|
||||||
if found != nil && found[rv] != nil {
|
if found != nil && found[rv] != nil {
|
||||||
return found[rv].Node
|
|
||||||
|
// if this is a ref node, we need to keep diving
|
||||||
|
// until we hit something that isn't a ref.
|
||||||
|
if jh, _, _ := utils.IsNodeRefValue(found[rv].Node); jh {
|
||||||
|
|
||||||
|
// if this node is circular, stop drop and roll.
|
||||||
|
if !IsCircular(found[rv].Node, idx) {
|
||||||
|
return LocateRefNode(found[rv].Node, idx)
|
||||||
|
} else {
|
||||||
|
Log.Error("circular reference found during lookup, and will remain un-resolved.",
|
||||||
|
zap.Int("line", found[rv].Node.Line),
|
||||||
|
zap.Int("column", found[rv].Node.Column),
|
||||||
|
zap.String("reference", found[rv].Definition),
|
||||||
|
zap.String("journey",
|
||||||
|
GetCircularReferenceResult(found[rv].Node, idx).GenerateJourneyPath()))
|
||||||
|
|
||||||
|
return found[rv].Node, fmt.Errorf("circular reference '%s' found during lookup at line %d, column %d, "+
|
||||||
|
"It cannot be resolved",
|
||||||
|
GetCircularReferenceResult(found[rv].Node, idx).GenerateJourneyPath(),
|
||||||
|
found[rv].Node.Line,
|
||||||
|
found[rv].Node.Column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found[rv].Node, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,23 +107,28 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
|
|||||||
nodes, fErr := path.Find(idx.GetRootNode())
|
nodes, fErr := path.Find(idx.GetRootNode())
|
||||||
if fErr == nil {
|
if fErr == nil {
|
||||||
if len(nodes) > 0 {
|
if len(nodes) > 0 {
|
||||||
return nodes[0]
|
return nodes[0], nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return nil, fmt.Errorf("reference '%s' at line %d, column %d was not found", root.Value, root.Line, root.Column)
|
||||||
}
|
}
|
||||||
return nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractObjectRaw[T Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (T, error) {
|
func ExtractObjectRaw[T Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (T, error) {
|
||||||
|
var circError error
|
||||||
if h, _, _ := utils.IsNodeRefValue(root); h {
|
if h, _, _ := utils.IsNodeRefValue(root); h {
|
||||||
ref := LocateRefNode(root, idx)
|
ref, err := LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
root = ref
|
root = ref
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("object extraction failed: reference cannot be found: %s, line %d, col %d",
|
if err != nil {
|
||||||
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
|
return nil, fmt.Errorf("object extraciton failed: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var n T = new(N)
|
var n T = new(N)
|
||||||
@@ -111,31 +140,42 @@ func ExtractObjectRaw[T Buildable[N], N any](root *yaml.Node, idx *index.SpecInd
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return n, err
|
return n, err
|
||||||
}
|
}
|
||||||
|
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||||
|
return n, circError
|
||||||
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractObject[T Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (NodeReference[T], error) {
|
func ExtractObject[T Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (NodeReference[T], error) {
|
||||||
var ln, vn *yaml.Node
|
var ln, vn *yaml.Node
|
||||||
|
var circError error
|
||||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||||
// locate reference in index.
|
ref, err := LocateRefNode(root, idx)
|
||||||
ref := LocateRefNode(root, idx)
|
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
vn = ref
|
vn = ref
|
||||||
ln = rl
|
ln = rl
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return NodeReference[T]{}, fmt.Errorf("object build failed: reference cannot be found: %s",
|
if err != nil {
|
||||||
root.Content[1].Value)
|
return NodeReference[T]{}, fmt.Errorf("object extraciton failed: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, ln, vn = utils.FindKeyNodeFull(label, root.Content)
|
_, ln, vn = utils.FindKeyNodeFull(label, root.Content)
|
||||||
if vn != nil {
|
if vn != nil {
|
||||||
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
||||||
ref := LocateRefNode(vn, idx)
|
ref, lerr := LocateRefNode(vn, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
vn = ref
|
vn = ref
|
||||||
|
if lerr != nil {
|
||||||
|
circError = lerr
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return NodeReference[T]{}, fmt.Errorf("object build failed: reference cannot be found: %s",
|
if lerr != nil {
|
||||||
vn.Content[1].Value)
|
return NodeReference[T]{}, fmt.Errorf("object extraciton failed: %s", lerr.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -152,22 +192,29 @@ func ExtractObject[T Buildable[N], N any](label string, root *yaml.Node, idx *in
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return NodeReference[T]{}, err
|
return NodeReference[T]{}, err
|
||||||
}
|
}
|
||||||
return NodeReference[T]{
|
res := NodeReference[T]{
|
||||||
Value: n,
|
Value: n,
|
||||||
KeyNode: ln,
|
KeyNode: ln,
|
||||||
ValueNode: vn,
|
ValueNode: vn,
|
||||||
}, nil
|
}
|
||||||
|
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) ([]ValueReference[T],
|
func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) ([]ValueReference[T],
|
||||||
*yaml.Node, *yaml.Node, error) {
|
*yaml.Node, *yaml.Node, error) {
|
||||||
var ln, vn *yaml.Node
|
var ln, vn *yaml.Node
|
||||||
|
var circError error
|
||||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||||
// locate reference in index.
|
ref, err := LocateRefNode(root, idx)
|
||||||
ref := LocateRefNode(root, idx)
|
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
vn = ref
|
vn = ref
|
||||||
ln = rl
|
ln = rl
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s",
|
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s",
|
||||||
root.Content[1].Value)
|
root.Content[1].Value)
|
||||||
@@ -176,12 +223,17 @@ func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *ind
|
|||||||
_, ln, vn = utils.FindKeyNodeFull(label, root.Content)
|
_, ln, vn = utils.FindKeyNodeFull(label, root.Content)
|
||||||
if vn != nil {
|
if vn != nil {
|
||||||
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
||||||
ref := LocateRefNode(vn, idx)
|
ref, err := LocateRefNode(vn, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
vn = ref
|
vn = ref
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s",
|
if err != nil {
|
||||||
root.Content[1].Value)
|
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s",
|
||||||
|
err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -193,12 +245,17 @@ func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *ind
|
|||||||
}
|
}
|
||||||
for _, node := range vn.Content {
|
for _, node := range vn.Content {
|
||||||
if rf, _, _ := utils.IsNodeRefValue(node); rf {
|
if rf, _, _ := utils.IsNodeRefValue(node); rf {
|
||||||
ref := LocateRefNode(node, idx)
|
ref, err := LocateRefNode(node, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
node = ref
|
node = ref
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s",
|
if err != nil {
|
||||||
root.Content[1].Value)
|
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed: reference cannot be found: %s",
|
||||||
|
err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var n T = new(N)
|
var n T = new(N)
|
||||||
@@ -216,6 +273,9 @@ func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *ind
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||||
|
return items, ln, vn, circError
|
||||||
|
}
|
||||||
return items, ln, vn, nil
|
return items, ln, vn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,6 +296,7 @@ func ExtractExample(expNode, expLabel *yaml.Node) NodeReference[any] {
|
|||||||
|
|
||||||
func ExtractMapFlatNoLookup[PT Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (map[KeyReference[string]]ValueReference[PT], error) {
|
func ExtractMapFlatNoLookup[PT Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (map[KeyReference[string]]ValueReference[PT], error) {
|
||||||
valueMap := make(map[KeyReference[string]]ValueReference[PT])
|
valueMap := make(map[KeyReference[string]]ValueReference[PT])
|
||||||
|
var circError error
|
||||||
if utils.IsNodeMap(root) {
|
if utils.IsNodeMap(root) {
|
||||||
var currentKey *yaml.Node
|
var currentKey *yaml.Node
|
||||||
skip := false
|
skip := false
|
||||||
@@ -254,11 +315,16 @@ func ExtractMapFlatNoLookup[PT Buildable[N], N any](root *yaml.Node, idx *index.
|
|||||||
}
|
}
|
||||||
// if value is a reference, we have to look it up in the index!
|
// if value is a reference, we have to look it up in the index!
|
||||||
if h, _, _ := utils.IsNodeRefValue(node); h {
|
if h, _, _ := utils.IsNodeRefValue(node); h {
|
||||||
ref := LocateRefNode(node, idx)
|
ref, err := LocateRefNode(node, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
node = ref
|
node = ref
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("map build failed: reference cannot be found: %s", root.Content[1].Value)
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("map build failed: reference cannot be found: %s", err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,6 +346,9 @@ func ExtractMapFlatNoLookup[PT Buildable[N], N any](root *yaml.Node, idx *index.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||||
|
return valueMap, circError
|
||||||
|
}
|
||||||
return valueMap, nil
|
return valueMap, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,12 +359,16 @@ type mappingResult[T any] struct {
|
|||||||
|
|
||||||
func ExtractMapFlat[PT Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (map[KeyReference[string]]ValueReference[PT], *yaml.Node, *yaml.Node, error) {
|
func ExtractMapFlat[PT Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (map[KeyReference[string]]ValueReference[PT], *yaml.Node, *yaml.Node, error) {
|
||||||
var labelNode, valueNode *yaml.Node
|
var labelNode, valueNode *yaml.Node
|
||||||
|
var circError error
|
||||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||||
// locate reference in index.
|
// locate reference in index.
|
||||||
ref := LocateRefNode(root, idx)
|
ref, err := LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
valueNode = ref
|
valueNode = ref
|
||||||
labelNode = rl
|
labelNode = rl
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s",
|
return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s",
|
||||||
root.Content[1].Value)
|
root.Content[1].Value)
|
||||||
@@ -304,12 +377,17 @@ func ExtractMapFlat[PT Buildable[N], N any](label string, root *yaml.Node, idx *
|
|||||||
_, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content)
|
_, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content)
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
|
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
|
||||||
ref := LocateRefNode(valueNode, idx)
|
ref, err := LocateRefNode(valueNode, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
valueNode = ref
|
valueNode = ref
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s",
|
if err != nil {
|
||||||
root.Content[1].Value)
|
return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s",
|
||||||
|
err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -349,12 +427,17 @@ func ExtractMapFlat[PT Buildable[N], N any](label string, root *yaml.Node, idx *
|
|||||||
}
|
}
|
||||||
// check our valueNode isn't a reference still.
|
// check our valueNode isn't a reference still.
|
||||||
if h, _, _ := utils.IsNodeRefValue(en); h {
|
if h, _, _ := utils.IsNodeRefValue(en); h {
|
||||||
ref := LocateRefNode(en, idx)
|
ref, err := LocateRefNode(en, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
en = ref
|
en = ref
|
||||||
|
if err != nil {
|
||||||
|
circError = err
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return nil, labelNode, valueNode, fmt.Errorf("flat map build failed: reference cannot be found: %s",
|
if err != nil {
|
||||||
root.Content[1].Value)
|
return nil, labelNode, valueNode, fmt.Errorf("flat map build failed: reference cannot be found: %s",
|
||||||
|
err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,85 +460,12 @@ func ExtractMapFlat[PT Buildable[N], N any](label string, root *yaml.Node, idx *
|
|||||||
}
|
}
|
||||||
return valueMap, labelNode, valueNode, nil
|
return valueMap, labelNode, valueNode, nil
|
||||||
}
|
}
|
||||||
|
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||||
|
return nil, labelNode, valueNode, circError
|
||||||
|
}
|
||||||
return nil, labelNode, valueNode, nil
|
return nil, labelNode, valueNode, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtractMap[PT Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (map[KeyReference[string]]map[KeyReference[string]]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 {
|
|
||||||
return nil, fmt.Errorf("map build failed: reference cannot be found: %s", root.Content[1].Value)
|
|
||||||
}
|
|
||||||
} 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
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("map build failed: reference cannot be found: %s",
|
|
||||||
root.Content[1].Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if valueNode != nil {
|
|
||||||
var currentLabelNode *yaml.Node
|
|
||||||
valueMap := make(map[KeyReference[string]]ValueReference[PT])
|
|
||||||
for i, en := range valueNode.Content {
|
|
||||||
if i%2 == 0 {
|
|
||||||
currentLabelNode = en
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasPrefix(strings.ToLower(currentLabelNode.Value), "x-") {
|
|
||||||
continue // yo, don't pay any attention to extensions, not here anyway.
|
|
||||||
}
|
|
||||||
|
|
||||||
// check our valueNode isn't a reference still.
|
|
||||||
if h, _, _ := utils.IsNodeRefValue(en); h {
|
|
||||||
ref := LocateRefNode(en, idx)
|
|
||||||
if ref != nil {
|
|
||||||
en = ref
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("map build failed: reference cannot be found: %s",
|
|
||||||
root.Content[1].Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var n PT = new(N)
|
|
||||||
err := BuildModel(en, n)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
berr := n.Build(en, idx)
|
|
||||||
if berr != nil {
|
|
||||||
return nil, berr
|
|
||||||
}
|
|
||||||
valueMap[KeyReference[string]{
|
|
||||||
Value: currentLabelNode.Value,
|
|
||||||
KeyNode: currentLabelNode,
|
|
||||||
}] = ValueReference[PT]{
|
|
||||||
Value: n,
|
|
||||||
ValueNode: en,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resMap := make(map[KeyReference[string]]map[KeyReference[string]]ValueReference[PT])
|
|
||||||
resMap[KeyReference[string]{
|
|
||||||
Value: labelNode.Value,
|
|
||||||
KeyNode: labelNode,
|
|
||||||
}] = valueMap
|
|
||||||
return resMap, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ExtractExtensions(root *yaml.Node) map[KeyReference[string]]ValueReference[any] {
|
func ExtractExtensions(root *yaml.Node) map[KeyReference[string]]ValueReference[any] {
|
||||||
extensions := utils.FindExtensionNodes(root.Content)
|
extensions := utils.FindExtensionNodes(root.Content)
|
||||||
extensionMap := make(map[KeyReference[string]]ValueReference[any])
|
extensionMap := make(map[KeyReference[string]]ValueReference[any])
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
package low
|
package low
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
|
"github.com/pb33f/libopenapi/resolver"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -47,7 +49,7 @@ func TestLocateRefNode(t *testing.T) {
|
|||||||
var cNode yaml.Node
|
var cNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
located := LocateRefNode(cNode.Content[0], idx)
|
located, _ := LocateRefNode(cNode.Content[0], idx)
|
||||||
assert.NotNil(t, located)
|
assert.NotNil(t, located)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -68,7 +70,316 @@ func TestLocateRefNode_Path(t *testing.T) {
|
|||||||
var cNode yaml.Node
|
var cNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
located := LocateRefNode(cNode.Content[0], idx)
|
located, _ := LocateRefNode(cNode.Content[0], idx)
|
||||||
assert.NotNil(t, located)
|
assert.NotNil(t, located)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLocateRefNode_Path_NotFound(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `paths:
|
||||||
|
/burger/time:
|
||||||
|
description: hello`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `$ref: '#/paths/~1burger~1time-somethingsomethingdarkside-somethingsomethingcomplete'`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
located, err := LocateRefNode(cNode.Content[0], idx)
|
||||||
|
assert.Nil(t, located)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type pizza struct {
|
||||||
|
Description NodeReference[string]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *pizza) Build(n *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
pizza:
|
||||||
|
description: hello`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `tags:
|
||||||
|
description: hello pizza`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
tag, err := ExtractObject[*pizza]("tags", &cNode, idx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, tag)
|
||||||
|
assert.Equal(t, "hello pizza", tag.Value.Description.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_Ref(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
pizza:
|
||||||
|
description: hello pizza`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `tags:
|
||||||
|
$ref: '#/components/schemas/pizza'`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
tag, err := ExtractObject[*pizza]("tags", &cNode, idx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, tag)
|
||||||
|
assert.Equal(t, "hello pizza", tag.Value.Description.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_DoubleRef(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
cake:
|
||||||
|
description: cake time!
|
||||||
|
pizza:
|
||||||
|
$ref: '#/components/schemas/cake'`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `tags:
|
||||||
|
$ref: '#/components/schemas/pizza'`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
tag, err := ExtractObject[*pizza]("tags", &cNode, idx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, tag)
|
||||||
|
assert.Equal(t, "cake time!", tag.Value.Description.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_DoubleRef_Circular(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
loopy:
|
||||||
|
$ref: '#/components/schemas/cake'
|
||||||
|
cake:
|
||||||
|
$ref: '#/components/schemas/loopy'
|
||||||
|
pizza:
|
||||||
|
$ref: '#/components/schemas/cake'`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
// circular references are detected by the resolver, so lets run it!
|
||||||
|
resolv := resolver.NewResolver(idx)
|
||||||
|
assert.Len(t, resolv.CheckForCircularReferences(), 1)
|
||||||
|
|
||||||
|
yml = `tags:
|
||||||
|
$ref: '#/components/schemas/pizza'`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
tag, err := ExtractObject[*pizza]("tags", &cNode, idx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, tag)
|
||||||
|
assert.Equal(t, "", tag.Value.Description.Value)
|
||||||
|
assert.Equal(t, "cake -> loopy -> cake", idx.GetCircularReferences()[0].GenerateJourneyPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_DoubleRef_Circular_Fail(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
loopy:
|
||||||
|
$ref: '#/components/schemas/cake'
|
||||||
|
cake:
|
||||||
|
$ref: '#/components/schemas/loopy'
|
||||||
|
pizza:
|
||||||
|
$ref: '#/components/schemas/cake'`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
// circular references are detected by the resolver, so lets run it!
|
||||||
|
resolv := resolver.NewResolver(idx)
|
||||||
|
assert.Len(t, resolv.CheckForCircularReferences(), 1)
|
||||||
|
|
||||||
|
yml = `tags:
|
||||||
|
$ref: #BORK`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
_, err := ExtractObject[*pizza]("tags", &cNode, idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_DoubleRef_Circular_Direct(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
loopy:
|
||||||
|
$ref: '#/components/schemas/cake'
|
||||||
|
cake:
|
||||||
|
$ref: '#/components/schemas/loopy'
|
||||||
|
pizza:
|
||||||
|
$ref: '#/components/schemas/cake'`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
// circular references are detected by the resolver, so lets run it!
|
||||||
|
resolv := resolver.NewResolver(idx)
|
||||||
|
assert.Len(t, resolv.CheckForCircularReferences(), 1)
|
||||||
|
|
||||||
|
yml = `$ref: '#/components/schemas/pizza'`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
tag, err := ExtractObject[*pizza]("tags", cNode.Content[0], idx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, tag)
|
||||||
|
assert.Equal(t, "", tag.Value.Description.Value)
|
||||||
|
assert.Equal(t, "cake -> loopy -> cake", idx.GetCircularReferences()[0].GenerateJourneyPath())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_DoubleRef_Circular_Direct_Fail(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
loopy:
|
||||||
|
$ref: '#/components/schemas/cake'
|
||||||
|
cake:
|
||||||
|
$ref: '#/components/schemas/loopy'
|
||||||
|
pizza:
|
||||||
|
$ref: '#/components/schemas/cake'`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
// circular references are detected by the resolver, so lets run it!
|
||||||
|
resolv := resolver.NewResolver(idx)
|
||||||
|
assert.Len(t, resolv.CheckForCircularReferences(), 1)
|
||||||
|
|
||||||
|
yml = `$ref: '#/components/schemas/why-did-westworld-have-to-end-so-poorly-ffs'`
|
||||||
|
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
_, err := ExtractObject[*pizza]("tags", cNode.Content[0], idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type test_noGood struct {
|
||||||
|
DontWork int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *test_noGood) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
return fmt.Errorf("I am always going to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
type test_almostGood struct {
|
||||||
|
AlmostWork NodeReference[int]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *test_almostGood) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
return fmt.Errorf("I am always going to fail")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_BadLowLevelModel(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
hey:`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `thing:
|
||||||
|
dontWork: 123`
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
_, err := ExtractObject[*test_noGood]("thing", &cNode, idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_BadBuild(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
hey:`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `thing:
|
||||||
|
dontWork: 123`
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
_, err := ExtractObject[*test_almostGood]("thing", &cNode, idx)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractObject_BadLabel(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `components:
|
||||||
|
schemas:
|
||||||
|
hey:`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
|
yml = `thing:
|
||||||
|
dontWork: 123`
|
||||||
|
var cNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
|
res, err := ExtractObject[*test_almostGood]("ding", &cNode, idx)
|
||||||
|
assert.Nil(t, res.Value)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,3 +67,16 @@ func IsCircular(node *yaml.Node, idx *index.SpecIndex) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCircularReferenceResult(node *yaml.Node, idx *index.SpecIndex) *index.CircularReferenceResult {
|
||||||
|
if idx == nil {
|
||||||
|
return nil // no index! nothing we can do.
|
||||||
|
}
|
||||||
|
refs := idx.GetCircularReferences()
|
||||||
|
for i := range idx.GetCircularReferences() {
|
||||||
|
if refs[i].LoopPoint.Node == node {
|
||||||
|
return refs[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -138,6 +138,7 @@ type SpecIndex struct {
|
|||||||
seenRemoteSources map[string]*yaml.Node
|
seenRemoteSources map[string]*yaml.Node
|
||||||
remoteLock sync.Mutex
|
remoteLock sync.Mutex
|
||||||
circularReferences []*CircularReferenceResult // only available when the resolver has been used.
|
circularReferences []*CircularReferenceResult // only available when the resolver has been used.
|
||||||
|
allowCircularReferences bool // decide if you want to error out, or allow circular references, default is false.
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExternalLookupFunction is for lookup functions that take a JSONSchema reference and tries to find that node in the
|
// ExternalLookupFunction is for lookup functions that take a JSONSchema reference and tries to find that node in the
|
||||||
@@ -503,6 +504,17 @@ func (index *SpecIndex) GetAllExternalIndexes() map[string]*SpecIndex {
|
|||||||
return index.externalSpecIndex
|
return index.externalSpecIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetAllowCircularReferenceResolving will flip a bit that can be used by any consumers to determine if they want
|
||||||
|
// to allow or disallow circular references to be resolved or visited
|
||||||
|
func (index *SpecIndex) SetAllowCircularReferenceResolving(allow bool) {
|
||||||
|
index.allowCircularReferences = allow
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowCircularReferenceResolving will return a bit that allows developers to determine what to do with circular refs.
|
||||||
|
func (index *SpecIndex) AllowCircularReferenceResolving() bool {
|
||||||
|
return index.allowCircularReferences
|
||||||
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) checkPolymorphicNode(name string) (bool, string) {
|
func (index *SpecIndex) checkPolymorphicNode(name string) (bool, string) {
|
||||||
switch name {
|
switch name {
|
||||||
case "anyOf":
|
case "anyOf":
|
||||||
|
|||||||
Reference in New Issue
Block a user