cleaned up path handling a little

Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
quobix
2024-01-03 14:13:23 -05:00
parent ee50403678
commit cbdaac7374
8 changed files with 105 additions and 80 deletions

View File

@@ -450,6 +450,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *nodes.NodeEntry) *ya
} else { } else {
// try an inline render if we can, otherwise there is no option but to default to the // try an inline render if we can, otherwise there is no option but to default to the
// full render. // full render.
if _, ko := r.(RenderableInline); ko { if _, ko := r.(RenderableInline); ko {
rawRender, _ = r.(RenderableInline).MarshalYAMLInline() rawRender, _ = r.(RenderableInline).MarshalYAMLInline()
} else { } else {

View File

@@ -840,7 +840,7 @@ func TestCreateDocument_YamlAnchor(t *testing.T) {
assert.NotNil(t, postParams) assert.NotNil(t, postParams)
assert.Equal(t, 1, len(getParams)) assert.Equal(t, 1, len(getParams))
assert.Equal(t, 1, len(postParams)) assert.Equal(t, 1, len(postParams))
assert.Equal(t, getParams, postParams) assert.Equal(t, getParams[0].ValueNode, postParams[0].ValueNode)
// check post request body // check post request body
responses := examplePath.GetValue().Get.GetValue().GetResponses().Value.(*Responses) responses := examplePath.GetValue().Get.GetValue().GetResponses().Value.(*Responses)

View File

@@ -4,15 +4,15 @@
package v3 package v3
import ( import (
"context" "context"
"crypto/sha256" "crypto/sha256"
"strings" "strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap" "github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Link represents a low-level OpenAPI 3+ Link object. // Link represents a low-level OpenAPI 3+ Link object.
@@ -28,71 +28,71 @@ import (
// in an operation and using them as parameters while invoking the linked operation. // in an operation and using them as parameters while invoking the linked operation.
// - https://spec.openapis.org/oas/v3.1.0#link-object // - https://spec.openapis.org/oas/v3.1.0#link-object
type Link struct { type Link struct {
OperationRef low.NodeReference[string] OperationRef low.NodeReference[string]
OperationId low.NodeReference[string] OperationId low.NodeReference[string]
Parameters low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] Parameters low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]
RequestBody low.NodeReference[string] RequestBody low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
Server low.NodeReference[*Server] Server low.NodeReference[*Server]
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
KeyNode *yaml.Node KeyNode *yaml.Node
RootNode *yaml.Node RootNode *yaml.Node
*low.Reference *low.Reference
} }
// GetExtensions returns all Link extensions and satisfies the low.HasExtensions interface. // GetExtensions returns all Link extensions and satisfies the low.HasExtensions interface.
func (l *Link) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] { func (l *Link) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
return l.Extensions return l.Extensions
} }
// FindParameter will attempt to locate a parameter string value, using a parameter name input. // FindParameter will attempt to locate a parameter string value, using a parameter name input.
func (l *Link) FindParameter(pName string) *low.ValueReference[string] { func (l *Link) FindParameter(pName string) *low.ValueReference[string] {
return low.FindItemInOrderedMap[string](pName, l.Parameters.Value) return low.FindItemInOrderedMap[string](pName, l.Parameters.Value)
} }
// FindExtension will attempt to locate an extension with a specific key // FindExtension will attempt to locate an extension with a specific key
func (l *Link) FindExtension(ext string) *low.ValueReference[*yaml.Node] { func (l *Link) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
return low.FindItemInOrderedMap(ext, l.Extensions) return low.FindItemInOrderedMap(ext, l.Extensions)
} }
// Build will extract extensions and servers from the node. // Build will extract extensions and servers from the node.
func (l *Link) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { func (l *Link) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error {
l.KeyNode = keyNode l.KeyNode = keyNode
root = utils.NodeAlias(root) root = utils.NodeAlias(root)
utils.CheckForMergeNodes(root) utils.CheckForMergeNodes(root)
l.Reference = new(low.Reference) l.Reference = new(low.Reference)
l.Extensions = low.ExtractExtensions(root) l.Extensions = low.ExtractExtensions(root)
// extract server. // extract server.
ser, sErr := low.ExtractObject[*Server](ctx, ServerLabel, root, idx) ser, sErr := low.ExtractObject[*Server](ctx, ServerLabel, root, idx)
if sErr != nil { if sErr != nil {
return sErr return sErr
} }
l.Server = ser l.Server = ser
return nil return nil
} }
// Hash will return a consistent SHA256 Hash of the Link object // Hash will return a consistent SHA256 Hash of the Link object
func (l *Link) Hash() [32]byte { func (l *Link) Hash() [32]byte {
var f []string var f []string
if l.Description.Value != "" { if l.Description.Value != "" {
f = append(f, l.Description.Value) f = append(f, l.Description.Value)
} }
if l.OperationRef.Value != "" { if l.OperationRef.Value != "" {
f = append(f, l.OperationRef.Value) f = append(f, l.OperationRef.Value)
} }
if l.OperationId.Value != "" { if l.OperationId.Value != "" {
f = append(f, l.OperationId.Value) f = append(f, l.OperationId.Value)
} }
if l.RequestBody.Value != "" { if l.RequestBody.Value != "" {
f = append(f, l.RequestBody.Value) f = append(f, l.RequestBody.Value)
} }
if l.Server.Value != nil { if l.Server.Value != nil {
f = append(f, low.GenerateHashString(l.Server.Value)) f = append(f, low.GenerateHashString(l.Server.Value))
} }
for pair := orderedmap.First(orderedmap.SortAlpha(l.Parameters.Value)); pair != nil; pair = pair.Next() { for pair := orderedmap.First(orderedmap.SortAlpha(l.Parameters.Value)); pair != nil; pair = pair.Next() {
f = append(f, pair.Value().Value) f = append(f, pair.Value().Value)
} }
f = append(f, low.HashExtensions(l.Extensions)...) f = append(f, low.HashExtensions(l.Extensions)...)
return sha256.Sum256([]byte(strings.Join(f, "|"))) return sha256.Sum256([]byte(strings.Join(f, "|")))
} }

View File

@@ -529,8 +529,8 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string,
} }
} }
seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1")) //seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1"))
//seenPath = append(seenPath, n.Value) seenPath = append(seenPath, n.Value)
prev = n.Value prev = n.Value
} }

View File

@@ -971,7 +971,7 @@ func (index *SpecIndex) GetOperationCount() int {
Definition: m.Value, Definition: m.Value,
Name: m.Value, Name: m.Value,
Node: method.Content[y+1], Node: method.Content[y+1],
Path: fmt.Sprintf("$.paths.%s.%s", p.Value, m.Value), Path: fmt.Sprintf("$.paths['%s'].%s", p.Value, m.Value),
ParentNode: m, ParentNode: m,
} }
if locatedPathRefs[p.Value] == nil { if locatedPathRefs[p.Value] == nil {
@@ -1039,7 +1039,7 @@ func (index *SpecIndex) GetOperationsParameterCount() int {
Name: serverRef.Value, Name: serverRef.Value,
Node: serverRef, Node: serverRef,
ParentNode: prop, ParentNode: prop,
Path: fmt.Sprintf("$.paths.%s.servers[%d]", pathItemNode.Value, i), Path: fmt.Sprintf("$.paths['%s'].servers[%d]", pathItemNode.Value, i),
} }
serverRefs = append(serverRefs, ref) serverRefs = append(serverRefs, ref)
} }
@@ -1123,7 +1123,7 @@ func (index *SpecIndex) GetOperationsParameterCount() int {
Name: "servers", Name: "servers",
Node: serverRef, Node: serverRef,
ParentNode: httpMethodProp, ParentNode: httpMethodProp,
Path: fmt.Sprintf("$.paths.%s.%s.servers[%d]", pathItemNode.Value, prop.Value, i), Path: fmt.Sprintf("$.paths['%s'].%s.servers[%d]", pathItemNode.Value, prop.Value, i),
} }
serverRefs = append(serverRefs, ref) serverRefs = append(serverRefs, ref)
} }

View File

@@ -1214,7 +1214,7 @@ components:
assert.Equal(t, "$.components.parameters.param2", params["/"]["get"]["#/components/parameters/param2"][0].Path) assert.Equal(t, "$.components.parameters.param2", params["/"]["get"]["#/components/parameters/param2"][0].Path)
} }
if assert.Contains(t, params["/"]["get"], "test") { if assert.Contains(t, params["/"]["get"], "test") {
assert.Equal(t, "$.paths./.get.parameters[2]", params["/"]["get"]["test"][0].Path) assert.Equal(t, "$.paths['/'].get.parameters[2]", params["/"]["get"]["test"][0].Path)
} }
} }
} }
@@ -1255,7 +1255,7 @@ paths:
opPath = "" opPath = ""
} }
assert.Equal(t, fmt.Sprintf("$.paths.%s%s.servers[%d]", path, opPath, i), server.Path) assert.Equal(t, fmt.Sprintf("$.paths['%s']%s.servers[%d]", path, opPath, i), server.Path)
} }
} }
} }
@@ -1278,7 +1278,7 @@ func TestSpecIndex_schemaComponentsHaveParentsAndPaths(t *testing.T) {
for _, schema := range schemas { for _, schema := range schemas {
assert.NotNil(t, schema.ParentNode) assert.NotNil(t, schema.ParentNode)
assert.Equal(t, fmt.Sprintf("$.components.schemas.%s", schema.Name), schema.Path) assert.Equal(t, fmt.Sprintf("$.components.schemas['%s']", schema.Name), schema.Path)
} }
} }
@@ -1497,12 +1497,12 @@ paths:
paths := idx.GetAllPaths() paths := idx.GetAllPaths()
assert.Equal(t, "$.paths./test.get", paths["/test"]["get"].Path) assert.Equal(t, "$.paths['/test'].get", paths["/test"]["get"].Path)
assert.Equal(t, 9, paths["/test"]["get"].ParentNode.Line) assert.Equal(t, 9, paths["/test"]["get"].ParentNode.Line)
assert.Equal(t, "$.paths./test.post", paths["/test"]["post"].Path) assert.Equal(t, "$.paths['/test'].post", paths["/test"]["post"].Path)
assert.Equal(t, 13, paths["/test"]["post"].ParentNode.Line) assert.Equal(t, 13, paths["/test"]["post"].ParentNode.Line)
assert.Equal(t, "$.paths./test2.delete", paths["/test2"]["delete"].Path) assert.Equal(t, "$.paths['/test2'].delete", paths["/test2"]["delete"].Path)
assert.Equal(t, 18, paths["/test2"]["delete"].ParentNode.Line) assert.Equal(t, 18, paths["/test2"]["delete"].ParentNode.Line)
assert.Equal(t, "$.paths./test2.put", paths["/test2"]["put"].Path) assert.Equal(t, "$.paths['/test2'].put", paths["/test2"]["put"].Path)
assert.Equal(t, 22, paths["/test2"]["put"].ParentNode.Line) assert.Equal(t, 22, paths["/test2"]["put"].ParentNode.Line)
} }

View File

@@ -30,7 +30,7 @@ func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pat
Definition: def, Definition: def,
Name: name, Name: name,
Node: schema, Node: schema,
Path: fmt.Sprintf("$.components.schemas.%s", name), Path: fmt.Sprintf("$.components.schemas['%s']", name),
ParentNode: schemasNode, ParentNode: schemasNode,
RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef, index), RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef, index),
} }
@@ -384,9 +384,9 @@ func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *y
// if this is a duplicate, add an error and ignore it // if this is a duplicate, add an error and ignore it
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil { if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) path := fmt.Sprintf("$.paths['%s'].%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" { if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) path = fmt.Sprintf("$.paths['%s'].parameters[%d]", pathItemNode.Value, i)
} }
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
@@ -408,9 +408,9 @@ func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *y
// param is inline. // param is inline.
_, vn := utils.FindKeyNode("name", param.Content) _, vn := utils.FindKeyNode("name", param.Content)
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) path := fmt.Sprintf("$.paths['%s'].%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" { if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) path = fmt.Sprintf("$.paths['%s'].parameters[%d]", pathItemNode.Value, i)
} }
if vn == nil { if vn == nil {
@@ -452,9 +452,9 @@ func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *y
if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value { if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value {
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i) path := fmt.Sprintf("$.paths['%s'].%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" { if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) path = fmt.Sprintf("$.paths['%s'].parameters[%d]", pathItemNode.Value, i)
} }
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{ index.operationParamErrors = append(index.operationParamErrors, &IndexingError{

View File

@@ -1,17 +1,18 @@
package utils package utils
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
"net/http" "net/http"
"net/url" "net/url"
"regexp" "regexp"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
) )
type Case int8 type Case int8
@@ -106,11 +107,34 @@ func FindNodesWithoutDeserializing(node *yaml.Node, jsonPath string) ([]*yaml.No
jsonPath = FixContext(jsonPath) jsonPath = FixContext(jsonPath)
path, err := yamlpath.NewPath(jsonPath) path, err := yamlpath.NewPath(jsonPath)
if err != nil { if err != nil {
return nil, err return nil, err
} }
results, _ := path.Find(node)
return results, nil // this can spin out, to lets gatekeep it.
done := make(chan bool)
eChan := make(chan error)
var results []*yaml.Node
timeout, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()
go func(d chan bool, e chan error) {
var er error
results, er = path.Find(node)
if er != nil {
e <- er
}
done <- true
}(done, eChan)
select {
case <-done:
return results, nil
case er := <-eChan:
return nil, er
case <-timeout.Done():
return nil, fmt.Errorf("node lookup timeout exceeded")
}
} }
// ConvertInterfaceIntoStringMap will convert an unknown input into a string map. // ConvertInterfaceIntoStringMap will convert an unknown input into a string map.