diff --git a/datamodel/high/node_builder.go b/datamodel/high/node_builder.go index aead8b4..118048a 100644 --- a/datamodel/high/node_builder.go +++ b/datamodel/high/node_builder.go @@ -450,6 +450,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *nodes.NodeEntry) *ya } else { // try an inline render if we can, otherwise there is no option but to default to the // full render. + if _, ko := r.(RenderableInline); ko { rawRender, _ = r.(RenderableInline).MarshalYAMLInline() } else { diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 67ca276..58158b1 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -840,7 +840,7 @@ func TestCreateDocument_YamlAnchor(t *testing.T) { assert.NotNil(t, postParams) assert.Equal(t, 1, len(getParams)) assert.Equal(t, 1, len(postParams)) - assert.Equal(t, getParams, postParams) + assert.Equal(t, getParams[0].ValueNode, postParams[0].ValueNode) // check post request body responses := examplePath.GetValue().Get.GetValue().GetResponses().Value.(*Responses) diff --git a/datamodel/low/v3/link.go b/datamodel/low/v3/link.go index f9a102a..ebae15a 100644 --- a/datamodel/low/v3/link.go +++ b/datamodel/low/v3/link.go @@ -4,15 +4,15 @@ package v3 import ( - "context" - "crypto/sha256" - "strings" + "context" + "crypto/sha256" + "strings" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/orderedmap" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/orderedmap" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" ) // 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. // - https://spec.openapis.org/oas/v3.1.0#link-object type Link struct { - OperationRef low.NodeReference[string] - OperationId low.NodeReference[string] - Parameters low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] - RequestBody low.NodeReference[string] - Description low.NodeReference[string] - Server low.NodeReference[*Server] - Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] - KeyNode *yaml.Node - RootNode *yaml.Node - *low.Reference + OperationRef low.NodeReference[string] + OperationId low.NodeReference[string] + Parameters low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] + RequestBody low.NodeReference[string] + Description low.NodeReference[string] + Server low.NodeReference[*Server] + Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] + KeyNode *yaml.Node + RootNode *yaml.Node + *low.Reference } // GetExtensions returns all Link extensions and satisfies the low.HasExtensions interface. 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. 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 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. func (l *Link) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error { - l.KeyNode = keyNode - root = utils.NodeAlias(root) - - utils.CheckForMergeNodes(root) - l.Reference = new(low.Reference) - l.Extensions = low.ExtractExtensions(root) - // extract server. - ser, sErr := low.ExtractObject[*Server](ctx, ServerLabel, root, idx) - if sErr != nil { - return sErr - } - l.Server = ser - return nil + l.KeyNode = keyNode + root = utils.NodeAlias(root) + + utils.CheckForMergeNodes(root) + l.Reference = new(low.Reference) + l.Extensions = low.ExtractExtensions(root) + // extract server. + ser, sErr := low.ExtractObject[*Server](ctx, ServerLabel, root, idx) + if sErr != nil { + return sErr + } + l.Server = ser + return nil } // Hash will return a consistent SHA256 Hash of the Link object func (l *Link) Hash() [32]byte { - var f []string - if l.Description.Value != "" { - f = append(f, l.Description.Value) - } - if l.OperationRef.Value != "" { - f = append(f, l.OperationRef.Value) - } - if l.OperationId.Value != "" { - f = append(f, l.OperationId.Value) - } - if l.RequestBody.Value != "" { - f = append(f, l.RequestBody.Value) - } - if l.Server.Value != nil { - f = append(f, low.GenerateHashString(l.Server.Value)) - } - for pair := orderedmap.First(orderedmap.SortAlpha(l.Parameters.Value)); pair != nil; pair = pair.Next() { - f = append(f, pair.Value().Value) - } - f = append(f, low.HashExtensions(l.Extensions)...) - return sha256.Sum256([]byte(strings.Join(f, "|"))) + var f []string + if l.Description.Value != "" { + f = append(f, l.Description.Value) + } + if l.OperationRef.Value != "" { + f = append(f, l.OperationRef.Value) + } + if l.OperationId.Value != "" { + f = append(f, l.OperationId.Value) + } + if l.RequestBody.Value != "" { + f = append(f, l.RequestBody.Value) + } + if l.Server.Value != nil { + f = append(f, low.GenerateHashString(l.Server.Value)) + } + for pair := orderedmap.First(orderedmap.SortAlpha(l.Parameters.Value)); pair != nil; pair = pair.Next() { + f = append(f, pair.Value().Value) + } + f = append(f, low.HashExtensions(l.Extensions)...) + return sha256.Sum256([]byte(strings.Join(f, "|"))) } diff --git a/index/extract_refs.go b/index/extract_refs.go index e3eff68..6870606 100644 --- a/index/extract_refs.go +++ b/index/extract_refs.go @@ -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, n.Value) + //seenPath = append(seenPath, strings.ReplaceAll(n.Value, "/", "~1")) + seenPath = append(seenPath, n.Value) prev = n.Value } diff --git a/index/spec_index.go b/index/spec_index.go index 7a1cd43..1ebb1d8 100644 --- a/index/spec_index.go +++ b/index/spec_index.go @@ -971,7 +971,7 @@ func (index *SpecIndex) GetOperationCount() int { Definition: m.Value, Name: m.Value, 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, } if locatedPathRefs[p.Value] == nil { @@ -1039,7 +1039,7 @@ func (index *SpecIndex) GetOperationsParameterCount() int { Name: serverRef.Value, Node: serverRef, 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) } @@ -1123,7 +1123,7 @@ func (index *SpecIndex) GetOperationsParameterCount() int { Name: "servers", Node: serverRef, 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) } diff --git a/index/spec_index_test.go b/index/spec_index_test.go index 0c7e85f..69a459b 100644 --- a/index/spec_index_test.go +++ b/index/spec_index_test.go @@ -1214,7 +1214,7 @@ components: assert.Equal(t, "$.components.parameters.param2", params["/"]["get"]["#/components/parameters/param2"][0].Path) } 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 = "" } - 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 { 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() - 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, "$.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, "$.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, "$.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) } diff --git a/index/utility_methods.go b/index/utility_methods.go index d1a918a..4ab941b 100644 --- a/index/utility_methods.go +++ b/index/utility_methods.go @@ -30,7 +30,7 @@ func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pat Definition: def, Name: name, Node: schema, - Path: fmt.Sprintf("$.components.schemas.%s", name), + Path: fmt.Sprintf("$.components.schemas['%s']", name), ParentNode: schemasNode, 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 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" { - 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{ @@ -408,9 +408,9 @@ func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *y // param is inline. _, 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" { - path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i) + path = fmt.Sprintf("$.paths['%s'].parameters[%d]", pathItemNode.Value, i) } 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 { - 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" { - 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{ diff --git a/utils/utils.go b/utils/utils.go index 67a205a..8acb272 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,17 +1,18 @@ package utils import ( + "context" "encoding/json" "fmt" + "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" + "gopkg.in/yaml.v3" "net/http" "net/url" "regexp" "sort" "strconv" "strings" - - "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" - "gopkg.in/yaml.v3" + "time" ) type Case int8 @@ -106,11 +107,34 @@ func FindNodesWithoutDeserializing(node *yaml.Node, jsonPath string) ([]*yaml.No jsonPath = FixContext(jsonPath) path, err := yamlpath.NewPath(jsonPath) + if err != nil { 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.