mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Added NodeMap to all low level objects.
this sync map tracks all the nodes that apply to this object.
This commit is contained in:
@@ -150,7 +150,7 @@ components:
|
||||
assert.ErrorIs(t, unwrap[0], ErrInvalidModel)
|
||||
unwrapNext := utils.UnwrapErrors(unwrap[1])
|
||||
require.Len(t, unwrapNext, 2)
|
||||
assert.Equal(t, "component 'bork' does not exist in the specification", unwrapNext[0].Error())
|
||||
assert.Equal(t, "component `bork` does not exist in the specification", unwrapNext[0].Error())
|
||||
assert.Equal(t, "cannot resolve reference `bork`, it's missing: $bork [5:7]", unwrapNext[1].Error())
|
||||
|
||||
logEntries := strings.Split(byteBuf.String(), "\n")
|
||||
|
||||
@@ -72,6 +72,14 @@ func CreateSchemaProxyRef(ref string) *SchemaProxy {
|
||||
return &SchemaProxy{refStr: ref, lock: &sync.Mutex{}}
|
||||
}
|
||||
|
||||
// GetValueNode returns the value node of the SchemaProxy.
|
||||
func (sp *SchemaProxy) GetValueNode() *yaml.Node {
|
||||
if sp.schema != nil {
|
||||
return sp.schema.ValueNode
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one.
|
||||
// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access
|
||||
// to that building error.
|
||||
|
||||
@@ -443,7 +443,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *nodes.NodeEntry) *ya
|
||||
lr := lut.(low.IsReferenced)
|
||||
ut := reflect.ValueOf(lr)
|
||||
if !ut.IsNil() {
|
||||
if lut.(low.IsReferenced).IsReference() {
|
||||
if lr != nil && lr.IsReference() {
|
||||
if !n.Resolve {
|
||||
valueNode = n.renderReference(lut.(low.IsReferenced))
|
||||
break
|
||||
|
||||
@@ -155,6 +155,11 @@ func (d *Document) GoLow() *low.Document {
|
||||
return d.low
|
||||
}
|
||||
|
||||
// GoLowUntyped returns the low-level Document that was used to create the high level one, however, it's untyped.
|
||||
func (d *Document) GoLowUntyped() any {
|
||||
return d.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Document object as a byte slice.
|
||||
func (d *Document) Render() ([]byte, error) {
|
||||
return yaml.Marshal(d)
|
||||
|
||||
@@ -87,6 +87,7 @@ func TestNewDocument_Info(t *testing.T) {
|
||||
assert.Equal(t, "1.2", highDoc.Info.Version)
|
||||
assert.Equal(t, "https://pb33f.io/schema", highDoc.JsonSchemaDialect)
|
||||
|
||||
assert.NotNil(t, highDoc.GoLowUntyped())
|
||||
wentLow := highDoc.GoLow()
|
||||
assert.Equal(t, 1, wentLow.Version.ValueNode.Line)
|
||||
assert.Equal(t, 3, wentLow.Info.Value.Title.KeyNode.Line)
|
||||
|
||||
@@ -23,14 +23,14 @@ type Contact struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// Build is not implemented for Contact (there is nothing to build).
|
||||
func (c *Contact) Build(_ context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
func (c *Contact) Build(ctx context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
c.KeyNode = keyNode
|
||||
c.RootNode = root
|
||||
c.Reference = new(low.Reference)
|
||||
// not implemented.
|
||||
c.Nodes = low.ExtractNodes(ctx, root)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
30
datamodel/low/base/context.go
Normal file
30
datamodel/low/base/context.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley
|
||||
// https://pb33f.io
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
"golang.org/x/net/context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ModelContext is a struct that holds various persistent data structures for the model
|
||||
// that passes through the entire model building process.
|
||||
type ModelContext struct {
|
||||
SchemaCache *sync.Map
|
||||
}
|
||||
|
||||
// GetModelContext will return the ModelContext from a context.Context object
|
||||
// if it is available, otherwise it will return nil.
|
||||
func GetModelContext(ctx context.Context) *ModelContext {
|
||||
if ctx == nil {
|
||||
return nil
|
||||
}
|
||||
if ctx.Value("modelCtx") == nil {
|
||||
return nil
|
||||
}
|
||||
if c, ok := ctx.Value("modelCtx").(*ModelContext); ok {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -5,6 +5,7 @@ package base
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
@@ -23,7 +24,20 @@ import (
|
||||
type Discriminator struct {
|
||||
PropertyName low.NodeReference[string]
|
||||
Mapping low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode will return the root yaml node of the Discriminator object
|
||||
func (d *Discriminator) GetRootNode() *yaml.Node {
|
||||
return d.RootNode
|
||||
}
|
||||
|
||||
// GetKeyNode will return the key yaml node of the Discriminator object
|
||||
func (d *Discriminator) GetKeyNode() *yaml.Node {
|
||||
return d.KeyNode
|
||||
}
|
||||
|
||||
// FindMappingValue will return a ValueReference containing the string mapping value
|
||||
|
||||
@@ -28,6 +28,7 @@ type Example struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||
@@ -67,12 +68,13 @@ func (ex *Example) Hash() [32]byte {
|
||||
}
|
||||
|
||||
// Build extracts extensions and example value
|
||||
func (ex *Example) Build(_ context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
func (ex *Example) Build(ctx context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
ex.KeyNode = keyNode
|
||||
root = utils.NodeAlias(root)
|
||||
ex.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
ex.Reference = new(low.Reference)
|
||||
ex.Nodes = low.ExtractNodes(ctx, root)
|
||||
ex.Extensions = low.ExtractExtensions(root)
|
||||
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
||||
|
||||
@@ -82,6 +84,23 @@ func (ex *Example) Build(_ context.Context, keyNode, root *yaml.Node, _ *index.S
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
|
||||
// extract nodes for all value nodes down the tree.
|
||||
expChildNodes := low.ExtractNodesRecursive(ctx, vn)
|
||||
expChildNodes.Range(func(k, v interface{}) bool {
|
||||
if arr, ko := v.([]*yaml.Node); ko {
|
||||
if ext, ok := ex.Nodes.Load(k); ok {
|
||||
if extArr, kk := ext.([]*yaml.Node); kk {
|
||||
ex.Nodes.Store(k, append(extArr, arr...))
|
||||
} else {
|
||||
ex.Nodes.Store(k, arr)
|
||||
}
|
||||
} else {
|
||||
ex.Nodes.Store(k, arr)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -28,6 +28,7 @@ type ExternalDoc struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||
@@ -46,12 +47,13 @@ func (ex *ExternalDoc) GetKeyNode() *yaml.Node {
|
||||
}
|
||||
|
||||
// Build will extract extensions from the ExternalDoc instance.
|
||||
func (ex *ExternalDoc) Build(_ context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
func (ex *ExternalDoc) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
ex.KeyNode = keyNode
|
||||
root = utils.NodeAlias(root)
|
||||
ex.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
ex.Reference = new(low.Reference)
|
||||
ex.Nodes = low.ExtractNodes(ctx, root)
|
||||
ex.Extensions = low.ExtractExtensions(root)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type Info struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindExtension attempts to locate an extension with the supplied key
|
||||
@@ -64,6 +65,7 @@ func (i *Info) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.S
|
||||
i.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
i.Reference = new(low.Reference)
|
||||
i.Nodes = low.ExtractNodes(ctx, root)
|
||||
i.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
// extract contact
|
||||
|
||||
@@ -25,6 +25,7 @@ type License struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// Build out a license, complain if both a URL and identifier are present as they are mutually exclusive
|
||||
@@ -34,6 +35,8 @@ func (l *License) Build(ctx context.Context, keyNode, root *yaml.Node, idx *inde
|
||||
l.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
l.Reference = new(low.Reference)
|
||||
no := low.ExtractNodes(ctx, root)
|
||||
l.Nodes = no
|
||||
if l.URL.Value != "" && l.Identifier.Value != "" {
|
||||
return fmt.Errorf("license cannot have both a URL and an identifier, they are mutually exclusive")
|
||||
}
|
||||
|
||||
@@ -144,6 +144,7 @@ type Schema struct {
|
||||
Index *index.SpecIndex
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// Hash will calculate a SHA256 hash from the values of the schema, This allows equality checking against
|
||||
@@ -470,8 +471,11 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
root = utils.NodeAlias(root)
|
||||
utils.CheckForMergeNodes(root)
|
||||
s.Reference = new(low.Reference)
|
||||
no := low.ExtractNodes(ctx, root)
|
||||
s.Nodes = no
|
||||
s.Index = idx
|
||||
s.RootNode = root
|
||||
|
||||
if h, _, _ := utils.IsNodeRefValue(root); h {
|
||||
ref, _, err, fctx := low.LocateRefNodeWithContext(ctx, root, idx)
|
||||
if ref != nil {
|
||||
@@ -497,6 +501,20 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
|
||||
s.extractExtensions(root)
|
||||
|
||||
// if the schema has required values, extract the nodes for them.
|
||||
if s.Required.Value != nil {
|
||||
for _, r := range s.Required.Value {
|
||||
s.AddNode(r.ValueNode.Line, r.ValueNode)
|
||||
}
|
||||
}
|
||||
|
||||
// same thing with enums
|
||||
if s.Enum.Value != nil {
|
||||
for _, e := range s.Enum.Value {
|
||||
s.AddNode(e.ValueNode.Line, e.ValueNode)
|
||||
}
|
||||
}
|
||||
|
||||
// determine schema type, singular (3.0) or multiple (3.1), use a variable value
|
||||
_, typeLabel, typeValue := utils.FindKeyNodeFullTop(TypeLabel, root.Content)
|
||||
if typeValue != nil {
|
||||
@@ -630,6 +648,24 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
_, expLabel, expNode := utils.FindKeyNodeFullTop(ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
s.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode}
|
||||
|
||||
// extract nodes for all value nodes down the tree.
|
||||
expChildNodes := low.ExtractNodesRecursive(ctx, expNode)
|
||||
// map to the local schema
|
||||
expChildNodes.Range(func(k, v interface{}) bool {
|
||||
if arr, ko := v.([]*yaml.Node); ko {
|
||||
if ext, ok := s.Nodes.Load(k); ok {
|
||||
if extArr, kk := ext.([]*yaml.Node); kk {
|
||||
s.Nodes.Store(k, append(extArr, arr...))
|
||||
} else {
|
||||
s.Nodes.Store(k, arr)
|
||||
}
|
||||
} else {
|
||||
s.Nodes.Store(k, arr)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// handle examples if set.(3.1)
|
||||
@@ -645,6 +681,23 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
ValueNode: expArrNode,
|
||||
KeyNode: expArrLabel,
|
||||
}
|
||||
// extract nodes for all value nodes down the tree.
|
||||
expChildNodes := low.ExtractNodesRecursive(ctx, expArrNode)
|
||||
// map to the local schema
|
||||
expChildNodes.Range(func(k, v interface{}) bool {
|
||||
if arr, ko := v.([]*yaml.Node); ko {
|
||||
if ext, ok := s.Nodes.Load(k); ok {
|
||||
if extArr, kk := ext.([]*yaml.Node); kk {
|
||||
s.Nodes.Store(k, append(extArr, arr...))
|
||||
} else {
|
||||
s.Nodes.Store(k, arr)
|
||||
}
|
||||
} else {
|
||||
s.Nodes.Store(k, arr)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,7 +727,23 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
if discNode != nil {
|
||||
var discriminator Discriminator
|
||||
_ = low.BuildModel(discNode, &discriminator)
|
||||
discriminator.KeyNode = discLabel
|
||||
discriminator.RootNode = discNode
|
||||
discriminator.Nodes = low.ExtractNodes(ctx, discNode)
|
||||
s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode}
|
||||
// add discriminator nodes, because there is no build method.
|
||||
dn := low.ExtractNodesRecursive(ctx, discNode)
|
||||
dn.Range(func(key, val any) bool {
|
||||
if n, ok := val.([]*yaml.Node); ok {
|
||||
for _, g := range n {
|
||||
discriminator.AddNode(key.(int), g)
|
||||
}
|
||||
}
|
||||
if n, ok := val.(*yaml.Node); ok {
|
||||
discriminator.AddNode(key.(int), n)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// handle externalDocs if set.
|
||||
@@ -683,6 +752,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
var exDoc ExternalDoc
|
||||
_ = low.BuildModel(extDocNode, &exDoc)
|
||||
_ = exDoc.Build(ctx, extDocLabel, extDocNode, idx) // throws no errors, can't check for one.
|
||||
exDoc.Nodes = low.ExtractNodes(ctx, extDocNode)
|
||||
s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode}
|
||||
}
|
||||
|
||||
@@ -693,11 +763,12 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
_ = low.BuildModel(xmlNode, &xml)
|
||||
// extract extensions if set.
|
||||
_ = xml.Build(xmlNode, idx) // returns no errors, can't check for one.
|
||||
xml.Nodes = low.ExtractNodes(ctx, xmlNode)
|
||||
s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode}
|
||||
}
|
||||
|
||||
// handle properties
|
||||
props, err := buildPropertyMap(ctx, root, idx, PropertiesLabel)
|
||||
props, err := buildPropertyMap(ctx, s, root, idx, PropertiesLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -706,7 +777,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
}
|
||||
|
||||
// handle dependent schemas
|
||||
props, err = buildPropertyMap(ctx, root, idx, DependentSchemasLabel)
|
||||
props, err = buildPropertyMap(ctx, s, root, idx, DependentSchemasLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -715,7 +786,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
}
|
||||
|
||||
// handle pattern properties
|
||||
props, err = buildPropertyMap(ctx, root, idx, PatternPropertiesLabel)
|
||||
props, err = buildPropertyMap(ctx, s, root, idx, PatternPropertiesLabel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1013,7 +1084,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]], error) {
|
||||
func buildPropertyMap(ctx context.Context, parent *Schema, root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]], error) {
|
||||
_, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content)
|
||||
if propsNode != nil {
|
||||
propertyMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*SchemaProxy]]()
|
||||
@@ -1021,6 +1092,7 @@ func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex
|
||||
for i, prop := range propsNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentProp = prop
|
||||
parent.Nodes.Store(prop.Line, prop)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"log/slog"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
@@ -54,6 +55,7 @@ type SchemaProxy struct {
|
||||
rendered *Schema
|
||||
buildError error
|
||||
ctx context.Context
|
||||
*low.NodeMap
|
||||
}
|
||||
|
||||
// Build will prepare the SchemaProxy for rendering, it does not build the Schema, only sets up internal state.
|
||||
@@ -66,6 +68,8 @@ func (sp *SchemaProxy) Build(ctx context.Context, key, value *yaml.Node, idx *in
|
||||
if rf, _, r := utils.IsNodeRefValue(value); rf {
|
||||
sp.SetReference(r, value)
|
||||
}
|
||||
var m sync.Map
|
||||
sp.NodeMap = &low.NodeMap{Nodes: &m}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -93,6 +97,14 @@ func (sp *SchemaProxy) Schema() *Schema {
|
||||
}
|
||||
schema.ParentProxy = sp // https://github.com/pb33f/libopenapi/issues/29
|
||||
sp.rendered = schema
|
||||
|
||||
// for all the nodes added, copy them over to the schema
|
||||
if sp.NodeMap != nil {
|
||||
sp.NodeMap.Nodes.Range(func(key, value any) bool {
|
||||
schema.AddNode(key.(int), value.(*yaml.Node))
|
||||
return true
|
||||
})
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
@@ -158,3 +170,12 @@ func (sp *SchemaProxy) Hash() [32]byte {
|
||||
// hash reference value only, do not resolve!
|
||||
return sha256.Sum256([]byte(sp.GetReference()))
|
||||
}
|
||||
|
||||
// AddNode stores nodes in the underlying schema if rendered, otherwise holds in the proxy until build.
|
||||
func (sp *SchemaProxy) AddNode(key int, node *yaml.Node) {
|
||||
if sp.rendered != nil {
|
||||
sp.rendered.AddNode(key, node)
|
||||
} else {
|
||||
sp.Nodes.Store(key, node)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,15 +31,18 @@ type SecurityRequirement struct {
|
||||
RootNode *yaml.Node
|
||||
ContainsEmptyRequirement bool // if a requirement is empty (this means it's optional)
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// Build will extract security requirements from the node (the structure is odd, to be honest)
|
||||
func (s *SecurityRequirement) Build(_ context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
func (s *SecurityRequirement) Build(ctx context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
|
||||
s.KeyNode = keyNode
|
||||
root = utils.NodeAlias(root)
|
||||
s.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
s.Reference = new(low.Reference)
|
||||
s.Nodes = low.ExtractNodes(ctx, root)
|
||||
var labelNode *yaml.Node
|
||||
valueMap := orderedmap.New[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]()
|
||||
var arr []low.ValueReference[string]
|
||||
@@ -57,6 +60,7 @@ func (s *SecurityRequirement) Build(_ context.Context, keyNode, root *yaml.Node,
|
||||
Value: root.Content[i].Content[j].Value,
|
||||
ValueNode: root.Content[i].Content[j],
|
||||
})
|
||||
s.Nodes.Store(root.Content[i].Content[j].Line, root.Content[i].Content[j])
|
||||
}
|
||||
valueMap.Set(
|
||||
low.KeyReference[string]{
|
||||
@@ -76,6 +80,7 @@ func (s *SecurityRequirement) Build(_ context.Context, keyNode, root *yaml.Node,
|
||||
Value: valueMap,
|
||||
ValueNode: root,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ type Tag struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||
@@ -53,7 +54,9 @@ func (t *Tag) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.Sp
|
||||
t.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
t.Reference = new(low.Reference)
|
||||
t.Nodes = low.ExtractNodes(ctx, root)
|
||||
t.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, t.Extensions, t.Nodes)
|
||||
|
||||
// extract externalDocs
|
||||
extDocs, err := low.ExtractObject[*ExternalDoc](ctx, ExternalDocsLabel, root, idx)
|
||||
|
||||
@@ -30,6 +30,7 @@ type XML struct {
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// Build will extract extensions from the XML instance.
|
||||
@@ -38,6 +39,7 @@ func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
utils.CheckForMergeNodes(root)
|
||||
x.RootNode = root
|
||||
x.Reference = new(low.Reference)
|
||||
x.Nodes = low.ExtractNodes(nil, root)
|
||||
x.Extensions = low.ExtractExtensions(root)
|
||||
return nil
|
||||
}
|
||||
|
||||
148
datamodel/low/node_map.go
Normal file
148
datamodel/low/node_map.go
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley
|
||||
// https://pb33f.io
|
||||
// MIT License
|
||||
|
||||
package low
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// HasNodes is an interface that defines a method to get a map of nodes
|
||||
type HasNodes interface {
|
||||
GetNodes() map[int][]*yaml.Node
|
||||
}
|
||||
|
||||
// AddNodes is an interface that defined a method to add nodes.
|
||||
type AddNodes interface {
|
||||
AddNode(key int, node *yaml.Node)
|
||||
}
|
||||
|
||||
// NodeMap represents a map of yaml nodes
|
||||
type NodeMap struct {
|
||||
|
||||
// Nodes is a sync map of nodes for this object, and the key is the line number of the node
|
||||
// a line can contain many nodes (in JSON), so the value is a slice of *yaml.Node
|
||||
Nodes *sync.Map `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
// AddNode will add a node to the NodeMap
|
||||
func (nm *NodeMap) AddNode(key int, node *yaml.Node) {
|
||||
if existing, ok := nm.Nodes.Load(key); ok {
|
||||
if ext, ko := existing.(*yaml.Node); ko {
|
||||
nm.Nodes.Store(key, []*yaml.Node{ext, node})
|
||||
}
|
||||
if ext, ko := existing.([]*yaml.Node); ko {
|
||||
ext = append(ext, node)
|
||||
nm.Nodes.Store(key, ext)
|
||||
}
|
||||
} else {
|
||||
nm.Nodes.Store(key, []*yaml.Node{node})
|
||||
}
|
||||
}
|
||||
|
||||
// GetNodes will return the map of nodes
|
||||
func (nm *NodeMap) GetNodes() map[int][]*yaml.Node {
|
||||
composed := make(map[int][]*yaml.Node)
|
||||
nm.Nodes.Range(func(key, value interface{}) bool {
|
||||
if v, ok := value.([]*yaml.Node); ok {
|
||||
composed[key.(int)] = v
|
||||
}
|
||||
if v, ok := value.(*yaml.Node); ok {
|
||||
composed[key.(int)] = []*yaml.Node{v}
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
if len(composed) <= 0 {
|
||||
composed[0] = []*yaml.Node{} // return an empty slice if there are no nodes
|
||||
}
|
||||
return composed
|
||||
}
|
||||
|
||||
// ExtractNodes will iterate over a *yaml.Node and extract all nodes with a line number into a map
|
||||
func (nm *NodeMap) ExtractNodes(node *yaml.Node, recurse bool) {
|
||||
if node == nil {
|
||||
return
|
||||
}
|
||||
// if the node has content, iterate over it and extract every top level line number
|
||||
if node.Content != nil {
|
||||
for i := 0; i < len(node.Content); i++ {
|
||||
if node.Content[i].Line != 0 && len(node.Content[i].Content) <= 0 {
|
||||
nm.AddNode(node.Content[i].Line, node.Content[i])
|
||||
}
|
||||
if node.Content[i].Line != 0 && len(node.Content[i].Content) > 0 {
|
||||
if recurse {
|
||||
nm.AddNode(node.Content[i].Line, node.Content[i])
|
||||
nm.ExtractNodes(node.Content[i], recurse)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ContainsLine will return true if the NodeMap contains a node with the supplied line number
|
||||
func (nm *NodeMap) ContainsLine(line int, recurse bool) bool {
|
||||
if _, ok := nm.Nodes.Load(line); ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ExtractNodes will extract all nodes from a yaml.Node and return them in a map
|
||||
func ExtractNodes(_ context.Context, root *yaml.Node) *sync.Map {
|
||||
var syncMap sync.Map
|
||||
nm := &NodeMap{Nodes: &syncMap}
|
||||
nm.ExtractNodes(root, false)
|
||||
return nm.Nodes
|
||||
}
|
||||
|
||||
// ExtractNodesRecursive will extract all nodes from a yaml.Node and return them in a map, just like ExtractNodes
|
||||
// however, this version will dive-down the tree and extract all nodes from all child nodes as well until the tree
|
||||
// is done.
|
||||
func ExtractNodesRecursive(_ context.Context, root *yaml.Node) *sync.Map {
|
||||
var syncMap sync.Map
|
||||
nm := &NodeMap{Nodes: &syncMap}
|
||||
nm.ExtractNodes(root, true)
|
||||
return nm.Nodes
|
||||
}
|
||||
|
||||
// ExtractExtensionNodes will extract all extension nodes from a map of extensions, recursively.
|
||||
func ExtractExtensionNodes(_ context.Context,
|
||||
extensionMap *orderedmap.Map[KeyReference[string],
|
||||
ValueReference[*yaml.Node]], nodeMap *sync.Map) {
|
||||
|
||||
// range over the extension map and extract all nodes
|
||||
for extPairs := extensionMap.First(); extPairs != nil; extPairs = extPairs.Next() {
|
||||
k := extPairs.Key()
|
||||
v := extPairs.Value()
|
||||
|
||||
results := []*yaml.Node{k.KeyNode}
|
||||
var newNodeMap sync.Map
|
||||
nm := &NodeMap{Nodes: &newNodeMap}
|
||||
if len(v.ValueNode.Content) > 0 {
|
||||
nm.ExtractNodes(v.ValueNode, true)
|
||||
nm.Nodes.Range(func(key, value interface{}) bool {
|
||||
for _, n := range value.([]*yaml.Node) {
|
||||
results = append(results, n)
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
results = append(results, v.ValueNode)
|
||||
}
|
||||
if k.KeyNode.Line == v.ValueNode.Line {
|
||||
nodeMap.Store(k.KeyNode.Line, results)
|
||||
} else {
|
||||
nodeMap.Store(k.KeyNode.Line, results[0])
|
||||
gingo := results[1:]
|
||||
for _, y := range gingo {
|
||||
nodeMap.Store(y.Line, []*yaml.Node{y})
|
||||
}
|
||||
//nodeMap.Store(v.ValueNode.Line, gingo)
|
||||
}
|
||||
}
|
||||
}
|
||||
111
datamodel/low/node_map_test.go
Normal file
111
datamodel/low/node_map_test.go
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright 2023-2024 Princess Beef Heavy Industries, LLC / Dave Shanley
|
||||
// https://pb33f.io
|
||||
|
||||
package low
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_NodeMapExtractNodes(t *testing.T) {
|
||||
|
||||
yml := `one: hello
|
||||
two: there
|
||||
three: nice one
|
||||
four:
|
||||
shoes: yes
|
||||
socks: of course
|
||||
`
|
||||
|
||||
var root yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &root)
|
||||
var syncMap sync.Map
|
||||
nm := &NodeMap{Nodes: &syncMap}
|
||||
nm.ExtractNodes(root.Content[0], false)
|
||||
testTheThing(t, nm)
|
||||
|
||||
}
|
||||
|
||||
func testTheThing(t *testing.T, nm *NodeMap) {
|
||||
count := 0
|
||||
nm.Nodes.Range(func(key, value interface{}) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Equal(t, 4, count)
|
||||
|
||||
nodes := nm.GetNodes()
|
||||
|
||||
assert.Equal(t, 2, len(nodes[1]))
|
||||
assert.Equal(t, 2, len(nodes[2]))
|
||||
assert.Equal(t, 2, len(nodes[3]))
|
||||
assert.Equal(t, 1, len(nodes[4]))
|
||||
assert.Equal(t, "one", nodes[1][0].Value)
|
||||
assert.Equal(t, "hello", nodes[1][1].Value)
|
||||
assert.Equal(t, "two", nodes[2][0].Value)
|
||||
assert.Equal(t, "there", nodes[2][1].Value)
|
||||
assert.Equal(t, "three", nodes[3][0].Value)
|
||||
assert.Equal(t, "nice one", nodes[3][1].Value)
|
||||
assert.Equal(t, "four", nodes[4][0].Value)
|
||||
}
|
||||
|
||||
func testTheThingUnmarshalled(t *testing.T, nm *sync.Map) {
|
||||
n := &NodeMap{Nodes: nm}
|
||||
nodes := n.GetNodes()
|
||||
|
||||
assert.Equal(t, 2, len(nodes[1]))
|
||||
assert.Equal(t, 2, len(nodes[2]))
|
||||
assert.Equal(t, 2, len(nodes[3]))
|
||||
assert.Equal(t, 1, len(nodes[4]))
|
||||
assert.Equal(t, "one", nodes[1][0].Value)
|
||||
assert.Equal(t, "hello", nodes[1][1].Value)
|
||||
assert.Equal(t, "two", nodes[2][0].Value)
|
||||
assert.Equal(t, "there", nodes[2][1].Value)
|
||||
assert.Equal(t, "three", nodes[3][0].Value)
|
||||
assert.Equal(t, "nice one", nodes[3][1].Value)
|
||||
assert.Equal(t, "four", nodes[4][0].Value)
|
||||
}
|
||||
|
||||
func TestExtractNodes(t *testing.T) {
|
||||
|
||||
yml := `one: hello
|
||||
two: there
|
||||
three: nice one
|
||||
four:
|
||||
shoes: yes
|
||||
socks: of course
|
||||
`
|
||||
|
||||
var root yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &root)
|
||||
|
||||
nm := ExtractNodes(nil, root.Content[0])
|
||||
|
||||
count := 0
|
||||
nm.Range(func(key, value interface{}) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Equal(t, 4, count)
|
||||
testTheThingUnmarshalled(t, nm)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractNodes_Nil(t *testing.T) {
|
||||
var syncMap sync.Map
|
||||
nm := &NodeMap{Nodes: &syncMap}
|
||||
nm.ExtractNodes(nil, false)
|
||||
|
||||
count := 0
|
||||
nm.Nodes.Range(func(key, value interface{}) bool {
|
||||
count++
|
||||
return true
|
||||
})
|
||||
|
||||
assert.Equal(t, 0, count)
|
||||
}
|
||||
@@ -29,6 +29,7 @@ type Callback struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetExtensions returns all Callback extensions and satisfies the low.HasExtensions interface.
|
||||
@@ -58,14 +59,18 @@ func (cb *Callback) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
cb.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
cb.Reference = new(low.Reference)
|
||||
cb.Nodes = low.ExtractNodes(ctx, root)
|
||||
cb.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, cb.Extensions, cb.Nodes)
|
||||
|
||||
expressions, err := extractPathItemsMap(ctx, root, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cb.Expression = expressions
|
||||
|
||||
for xp := expressions.First(); xp != nil; xp = xp.Next() {
|
||||
cb.Nodes.Store(xp.Key().KeyNode.Line, xp.Key().KeyNode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
@@ -38,6 +39,7 @@ type Components struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
type componentBuildResult[T any] struct {
|
||||
@@ -139,7 +141,9 @@ func (co *Components) Build(ctx context.Context, root *yaml.Node, idx *index.Spe
|
||||
root = utils.NodeAlias(root)
|
||||
utils.CheckForMergeNodes(root)
|
||||
co.Reference = new(low.Reference)
|
||||
co.Nodes = low.ExtractNodes(ctx, root)
|
||||
co.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, co.Extensions, co.Nodes)
|
||||
co.RootNode = root
|
||||
co.KeyNode = root
|
||||
var reterr error
|
||||
@@ -156,55 +160,55 @@ func (co *Components) Build(ctx context.Context, root *yaml.Node, idx *index.Spe
|
||||
}
|
||||
|
||||
go func() {
|
||||
schemas, err := extractComponentValues[*base.SchemaProxy](ctx, SchemasLabel, root, idx)
|
||||
schemas, err := extractComponentValues[*base.SchemaProxy](ctx, SchemasLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Schemas = schemas
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
parameters, err := extractComponentValues[*Parameter](ctx, ParametersLabel, root, idx)
|
||||
parameters, err := extractComponentValues[*Parameter](ctx, ParametersLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Parameters = parameters
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
responses, err := extractComponentValues[*Response](ctx, ResponsesLabel, root, idx)
|
||||
responses, err := extractComponentValues[*Response](ctx, ResponsesLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Responses = responses
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
examples, err := extractComponentValues[*base.Example](ctx, base.ExamplesLabel, root, idx)
|
||||
examples, err := extractComponentValues[*base.Example](ctx, base.ExamplesLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Examples = examples
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
requestBodies, err := extractComponentValues[*RequestBody](ctx, RequestBodiesLabel, root, idx)
|
||||
requestBodies, err := extractComponentValues[*RequestBody](ctx, RequestBodiesLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.RequestBodies = requestBodies
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
headers, err := extractComponentValues[*Header](ctx, HeadersLabel, root, idx)
|
||||
headers, err := extractComponentValues[*Header](ctx, HeadersLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Headers = headers
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
securitySchemes, err := extractComponentValues[*SecurityScheme](ctx, SecuritySchemesLabel, root, idx)
|
||||
securitySchemes, err := extractComponentValues[*SecurityScheme](ctx, SecuritySchemesLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.SecuritySchemes = securitySchemes
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
links, err := extractComponentValues[*Link](ctx, LinksLabel, root, idx)
|
||||
links, err := extractComponentValues[*Link](ctx, LinksLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Links = links
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
callbacks, err := extractComponentValues[*Callback](ctx, CallbacksLabel, root, idx)
|
||||
callbacks, err := extractComponentValues[*Callback](ctx, CallbacksLabel, root, idx, co)
|
||||
captureError(err)
|
||||
co.Callbacks = callbacks
|
||||
wg.Done()
|
||||
@@ -217,12 +221,13 @@ func (co *Components) Build(ctx context.Context, root *yaml.Node, idx *index.Spe
|
||||
// extractComponentValues converts all the YAML nodes of a component type to
|
||||
// low level model.
|
||||
// Process each node in parallel.
|
||||
func extractComponentValues[T low.Buildable[N], N any](ctx context.Context, label string, root *yaml.Node, idx *index.SpecIndex) (low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]], error) {
|
||||
func extractComponentValues[T low.Buildable[N], N any](ctx context.Context, label string, root *yaml.Node, idx *index.SpecIndex, co *Components) (low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]], error) {
|
||||
var emptyResult low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]]
|
||||
_, nodeLabel, nodeValue := utils.FindKeyNodeFullTop(label, root.Content)
|
||||
if nodeValue == nil {
|
||||
return emptyResult, nil
|
||||
}
|
||||
co.Nodes.Store(nodeLabel.Line, nodeLabel)
|
||||
componentValues := orderedmap.New[low.KeyReference[string], low.ValueReference[T]]()
|
||||
if utils.IsNodeArray(nodeValue) {
|
||||
return emptyResult, fmt.Errorf("node is array, cannot be used in components: line %d, column %d", nodeValue.Line, nodeValue.Column)
|
||||
@@ -298,6 +303,21 @@ func extractComponentValues[T low.Buildable[N], N any](ctx context.Context, labe
|
||||
if err != nil {
|
||||
return componentBuildResult[T]{}, err
|
||||
}
|
||||
|
||||
nType := reflect.TypeOf(n)
|
||||
nValue := reflect.ValueOf(n)
|
||||
|
||||
// Check if the type implements low.HasKeyNode
|
||||
hasKeyNodeType := reflect.TypeOf((*low.HasKeyNode)(nil)).Elem()
|
||||
if nType.Implements(hasKeyNodeType) {
|
||||
r := nValue.Interface()
|
||||
if h, ok := r.(low.HasKeyNode); ok {
|
||||
if k, ko := r.(low.AddNodes); ko {
|
||||
k.AddNode(h.GetKeyNode().Line, h.GetKeyNode())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return componentBuildResult[T]{
|
||||
key: low.KeyReference[string]{
|
||||
KeyNode: currentLabel,
|
||||
@@ -315,6 +335,10 @@ func extractComponentValues[T low.Buildable[N], N any](ctx context.Context, labe
|
||||
return emptyResult, err
|
||||
}
|
||||
|
||||
//for rt := componentValues.First(); rt != nil; rt = rt.Next() {
|
||||
//
|
||||
//}
|
||||
|
||||
results := low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]]{
|
||||
KeyNode: nodeLabel,
|
||||
ValueNode: nodeValue,
|
||||
|
||||
@@ -36,7 +36,7 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur
|
||||
}
|
||||
version = low.NodeReference[string]{Value: versionNode.Value, KeyNode: labelNode, ValueNode: versionNode}
|
||||
doc := Document{Version: version}
|
||||
|
||||
doc.Nodes = low.ExtractNodes(nil, info.RootNode.Content[0])
|
||||
// create an index config and shadow the document configuration.
|
||||
idxConfig := index.CreateClosedAPIIndexConfig()
|
||||
idxConfig.SpecInfo = info
|
||||
@@ -130,7 +130,12 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur
|
||||
doc.Index = rolodex.GetRootIndex()
|
||||
var wg sync.WaitGroup
|
||||
|
||||
var cacheMap sync.Map
|
||||
modelContext := base.ModelContext{SchemaCache: &cacheMap}
|
||||
ctx := context.WithValue(context.Background(), "modelCtx", &modelContext)
|
||||
|
||||
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
|
||||
low.ExtractExtensionNodes(ctx, doc.Extensions, doc.Nodes)
|
||||
|
||||
// if set, extract jsonSchemaDialect (3.1)
|
||||
_, dialectLabel, dialectNode := utils.FindKeyNodeFull(JSONSchemaDialectLabel, info.RootNode.Content)
|
||||
@@ -161,8 +166,6 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur
|
||||
extractWebhooks,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
wg.Add(len(extractionFuncs))
|
||||
if config.Logger != nil {
|
||||
config.Logger.Debug("running extractions")
|
||||
@@ -310,6 +313,9 @@ func extractWebhooks(ctx context.Context, info *datamodel.SpecInfo, doc *Documen
|
||||
KeyNode: hooksL,
|
||||
ValueNode: hooksN,
|
||||
}
|
||||
for xj := hooks.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -86,6 +86,8 @@ type Document struct {
|
||||
|
||||
// Rolodex is a reference to the rolodex used when creating this document.
|
||||
Rolodex *index.Rolodex
|
||||
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name.
|
||||
|
||||
@@ -27,6 +27,7 @@ type Encoding struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindHeader attempts to locate a Header with the supplied name
|
||||
@@ -67,6 +68,7 @@ func (en *Encoding) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
root = utils.NodeAlias(root)
|
||||
en.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
en.Nodes = low.ExtractNodes(ctx, root)
|
||||
en.Reference = new(low.Reference)
|
||||
headers, hL, hN, err := low.ExtractMap[*Header](ctx, HeadersLabel, root, idx)
|
||||
if err != nil {
|
||||
@@ -78,6 +80,10 @@ func (en *Encoding) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: hL,
|
||||
ValueNode: hN,
|
||||
}
|
||||
en.Nodes.Store(hL.Line, hL)
|
||||
for xj := headers.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type Header struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindExtension will attempt to locate an extension with the supplied name
|
||||
@@ -104,8 +105,9 @@ func (h *Header) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index
|
||||
h.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
h.Reference = new(low.Reference)
|
||||
h.Nodes = low.ExtractNodes(ctx, root)
|
||||
h.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
low.ExtractExtensionNodes(ctx, h.Extensions, h.Nodes)
|
||||
// handle example if set.
|
||||
_, expLabel, expNode := utils.FindKeyNodeFull(base.ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
@@ -114,6 +116,12 @@ func (h *Header) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index
|
||||
ValueNode: expNode,
|
||||
KeyNode: expLabel,
|
||||
}
|
||||
h.Nodes.Store(expLabel.Line, expLabel)
|
||||
m := low.ExtractNodes(ctx, expNode)
|
||||
m.Range(func(key, value any) bool {
|
||||
h.Nodes.Store(key, value)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// handle examples if set.
|
||||
@@ -127,6 +135,7 @@ func (h *Header) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index
|
||||
KeyNode: expsL,
|
||||
ValueNode: expsN,
|
||||
}
|
||||
h.Nodes.Store(expsL.Line, expsL)
|
||||
}
|
||||
|
||||
// handle schema
|
||||
@@ -148,6 +157,9 @@ func (h *Header) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index
|
||||
KeyNode: cL,
|
||||
ValueNode: cN,
|
||||
}
|
||||
if cL != nil {
|
||||
h.Nodes.Store(cL.Line, cL)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ type Link struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetExtensions returns all Link extensions and satisfies the low.HasExtensions interface.
|
||||
@@ -72,7 +73,17 @@ func (l *Link) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.S
|
||||
l.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
l.Reference = new(low.Reference)
|
||||
l.Nodes = low.ExtractNodes(ctx, root)
|
||||
l.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, l.Extensions, l.Nodes)
|
||||
|
||||
// extract parameter nodes.
|
||||
if l.Parameters.Value != nil && l.Parameters.Value.Len() > 0 {
|
||||
for fk := l.Parameters.Value.First(); fk != nil; fk = fk.Next() {
|
||||
l.Nodes.Store(fk.Key().KeyNode.Line, fk.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
// extract server.
|
||||
ser, sErr := low.ExtractObject[*Server](ctx, ServerLabel, root, idx)
|
||||
if sErr != nil {
|
||||
|
||||
@@ -30,6 +30,7 @@ type MediaType struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetExtensions returns all MediaType extensions and satisfies the low.HasExtensions interface.
|
||||
@@ -74,12 +75,20 @@ func (mt *MediaType) Build(ctx context.Context, keyNode, root *yaml.Node, idx *i
|
||||
mt.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
mt.Reference = new(low.Reference)
|
||||
mt.Nodes = low.ExtractNodes(ctx, root)
|
||||
mt.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, mt.Extensions, mt.Nodes)
|
||||
|
||||
// handle example if set.
|
||||
_, expLabel, expNode := utils.FindKeyNodeFullTop(base.ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
mt.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode}
|
||||
mt.Nodes.Store(expLabel.Line, expLabel)
|
||||
m := low.ExtractNodesRecursive(ctx, expNode)
|
||||
m.Range(func(key, value any) bool {
|
||||
mt.Nodes.Store(key, value)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// handle schema
|
||||
@@ -97,11 +106,16 @@ func (mt *MediaType) Build(ctx context.Context, keyNode, root *yaml.Node, idx *i
|
||||
return eErr
|
||||
}
|
||||
if exps != nil && slices.Contains(root.Content, expsL) {
|
||||
mt.Nodes.Store(expsL.Line, expsL)
|
||||
for xj := exps.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
mt.Examples = low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{
|
||||
Value: exps,
|
||||
KeyNode: expsL,
|
||||
ValueNode: expsN,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// handle encoding
|
||||
@@ -115,6 +129,10 @@ func (mt *MediaType) Build(ctx context.Context, keyNode, root *yaml.Node, idx *i
|
||||
KeyNode: encsL,
|
||||
ValueNode: encsN,
|
||||
}
|
||||
mt.Nodes.Store(encsL.Line, encsL)
|
||||
for xj := encs.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ type OAuthFlows struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetExtensions returns all OAuthFlows extensions and satisfies the low.HasExtensions interface.
|
||||
@@ -56,6 +57,7 @@ func (o *OAuthFlows) Build(ctx context.Context, keyNode, root *yaml.Node, idx *i
|
||||
o.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
o.Reference = new(low.Reference)
|
||||
o.Nodes = low.ExtractNodes(ctx, root)
|
||||
o.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
v, vErr := low.ExtractObject[*OAuthFlow](ctx, ImplicitLabel, root, idx)
|
||||
@@ -113,6 +115,7 @@ type OAuthFlow struct {
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetExtensions returns all OAuthFlow extensions and satisfies the low.HasExtensions interface.
|
||||
@@ -136,9 +139,18 @@ func (o *OAuthFlow) GetRootNode() *yaml.Node {
|
||||
}
|
||||
|
||||
// Build will extract extensions from the node.
|
||||
func (o *OAuthFlow) Build(_ context.Context, _, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
func (o *OAuthFlow) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
o.Reference = new(low.Reference)
|
||||
o.Nodes = low.ExtractNodes(ctx, root)
|
||||
o.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, o.Extensions, o.Nodes)
|
||||
|
||||
if o.Scopes.Value != nil && o.Scopes.Value.Len() > 0 {
|
||||
for fk := o.Scopes.Value.First(); fk != nil; fk = fk.Next() {
|
||||
o.Nodes.Store(fk.Key().KeyNode.Line, fk.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
o.RootNode = root
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ type Operation struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// FindCallback will attempt to locate a Callback instance by the supplied name.
|
||||
@@ -77,7 +78,9 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
root = utils.NodeAlias(root)
|
||||
utils.CheckForMergeNodes(root)
|
||||
o.Reference = new(low.Reference)
|
||||
o.Nodes = low.ExtractNodes(ctx, root)
|
||||
o.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, o.Extensions, o.Nodes)
|
||||
|
||||
// extract externalDocs
|
||||
extDocs, dErr := low.ExtractObject[*base.ExternalDoc](ctx, base.ExternalDocsLabel, root, idx)
|
||||
@@ -97,6 +100,7 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
o.Nodes.Store(ln.Line, ln)
|
||||
}
|
||||
|
||||
// extract request body
|
||||
@@ -106,6 +110,17 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
}
|
||||
o.RequestBody = rBody
|
||||
|
||||
// extract tags, but only extract nodes, the model has already been built
|
||||
k, v := utils.FindKeyNode(TagsLabel, root.Content)
|
||||
if k != nil && v != nil {
|
||||
o.Nodes.Store(k.Line, k)
|
||||
nm := low.ExtractNodesRecursive(ctx, v)
|
||||
nm.Range(func(key, value interface{}) bool {
|
||||
o.Nodes.Store(key, value)
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
// extract responses
|
||||
respBody, respErr := low.ExtractObject[*Responses](ctx, ResponsesLabel, root, idx)
|
||||
if respErr != nil {
|
||||
@@ -124,6 +139,10 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: cbL,
|
||||
ValueNode: cbN,
|
||||
}
|
||||
o.Nodes.Store(cbL.Line, cbL)
|
||||
for xj := callbacks.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
// extract security
|
||||
@@ -139,6 +158,7 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: sln,
|
||||
ValueNode: svn,
|
||||
}
|
||||
o.Nodes.Store(sln.Line, sln)
|
||||
}
|
||||
|
||||
// if security is set, but no requirements are defined.
|
||||
@@ -149,6 +169,7 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: sln,
|
||||
ValueNode: svn,
|
||||
}
|
||||
o.Nodes.Store(sln.Line, svn)
|
||||
}
|
||||
|
||||
// extract servers
|
||||
@@ -162,6 +183,7 @@ func (o *Operation) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: sl,
|
||||
ValueNode: sn,
|
||||
}
|
||||
o.Nodes.Store(sl.Line, sl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ type Parameter struct {
|
||||
Content low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the Parameter object.
|
||||
@@ -79,12 +80,15 @@ func (p *Parameter) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
p.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
p.Reference = new(low.Reference)
|
||||
p.Nodes = low.ExtractNodes(ctx, root)
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, p.Extensions, p.Nodes)
|
||||
|
||||
// handle example if set.
|
||||
_, expLabel, expNode := utils.FindKeyNodeFullTop(base.ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
p.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode}
|
||||
p.Nodes.Store(expLabel.Line, expLabel)
|
||||
}
|
||||
|
||||
// handle schema
|
||||
@@ -108,6 +112,10 @@ func (p *Parameter) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: expsL,
|
||||
ValueNode: expsN,
|
||||
}
|
||||
p.Nodes.Store(expsL.Line, expsL)
|
||||
for xj := exps.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
// handle content, if set.
|
||||
@@ -120,6 +128,13 @@ func (p *Parameter) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
KeyNode: cL,
|
||||
ValueNode: cN,
|
||||
}
|
||||
if cL != nil {
|
||||
p.Nodes.Store(cL.Line, cL)
|
||||
for xj := con.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ type PathItem struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// Hash will return a consistent SHA256 Hash of the PathItem object
|
||||
@@ -121,13 +122,14 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
p.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
p.Reference = new(low.Reference)
|
||||
p.Nodes = low.ExtractNodes(ctx, root)
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, p.Extensions, p.Nodes)
|
||||
skip := false
|
||||
var currentNode *yaml.Node
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errors []error
|
||||
|
||||
var ops []low.NodeReference[*Operation]
|
||||
|
||||
// extract parameters
|
||||
@@ -141,6 +143,7 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
p.Nodes.Store(ln.Line, ln)
|
||||
}
|
||||
|
||||
_, ln, vn = utils.FindKeyNodeFullTop(ServersLabel, root.Content)
|
||||
@@ -163,6 +166,7 @@ func (p *PathItem) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
p.Nodes.Store(ln.Line, ln)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ type Paths struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the Paths object.
|
||||
@@ -82,7 +83,9 @@ func (p *Paths) Build(ctx context.Context, keyNode, root *yaml.Node, idx *index.
|
||||
p.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
p.Reference = new(low.Reference)
|
||||
p.Nodes = low.ExtractNodes(ctx, root)
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, p.Extensions, p.Nodes)
|
||||
|
||||
pathsMap, err := extractPathItemsMap(ctx, root, idx)
|
||||
if err != nil {
|
||||
|
||||
@@ -26,6 +26,7 @@ type RequestBody struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the RequestBody object.
|
||||
@@ -60,7 +61,9 @@ func (rb *RequestBody) Build(ctx context.Context, keyNode, root *yaml.Node, idx
|
||||
rb.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
rb.Reference = new(low.Reference)
|
||||
rb.Nodes = low.ExtractNodes(ctx, root)
|
||||
rb.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, rb.Extensions, rb.Nodes)
|
||||
|
||||
// handle content, if set.
|
||||
con, cL, cN, cErr := low.ExtractMap[*MediaType](ctx, ContentLabel, root, idx)
|
||||
@@ -73,6 +76,10 @@ func (rb *RequestBody) Build(ctx context.Context, keyNode, root *yaml.Node, idx
|
||||
KeyNode: cL,
|
||||
ValueNode: cN,
|
||||
}
|
||||
rb.Nodes.Store(cL.Line, cL)
|
||||
for xj := con.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ type Response struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the Response object.
|
||||
@@ -74,7 +75,9 @@ func (r *Response) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
r.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
r.Reference = new(low.Reference)
|
||||
r.Nodes = low.ExtractNodes(ctx, root)
|
||||
r.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, r.Extensions, r.Nodes)
|
||||
|
||||
// extract headers
|
||||
headers, lN, kN, err := low.ExtractMapExtensions[*Header](ctx, HeadersLabel, root, idx, true)
|
||||
@@ -87,6 +90,10 @@ func (r *Response) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
KeyNode: lN,
|
||||
ValueNode: kN,
|
||||
}
|
||||
r.Nodes.Store(lN.Line, lN)
|
||||
for xj := headers.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
con, clN, cN, cErr := low.ExtractMap[*MediaType](ctx, ContentLabel, root, idx)
|
||||
@@ -99,6 +106,10 @@ func (r *Response) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
KeyNode: clN,
|
||||
ValueNode: cN,
|
||||
}
|
||||
r.Nodes.Store(clN.Line, clN)
|
||||
for xj := con.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
// handle links if set
|
||||
@@ -112,6 +123,10 @@ func (r *Response) Build(ctx context.Context, keyNode, root *yaml.Node, idx *ind
|
||||
KeyNode: linkLabel,
|
||||
ValueNode: linkValue,
|
||||
}
|
||||
r.Nodes.Store(linkLabel.Line, linkLabel)
|
||||
for xj := links.First(); xj != nil; xj = xj.Next() {
|
||||
xj.Value().Value.Nodes.Store(xj.Key().KeyNode.Line, xj.Key().KeyNode)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ type Responses struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the Responses object.
|
||||
@@ -64,7 +65,9 @@ func (r *Responses) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
root = utils.NodeAlias(root)
|
||||
r.RootNode = root
|
||||
r.Reference = new(low.Reference)
|
||||
r.Nodes = low.ExtractNodes(ctx, root)
|
||||
r.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, r.Extensions, r.Nodes)
|
||||
utils.CheckForMergeNodes(root)
|
||||
if utils.IsNodeMap(root) {
|
||||
codes, err := low.ExtractMapNoLookup[*Response](ctx, root, idx)
|
||||
@@ -73,12 +76,17 @@ func (r *Responses) Build(ctx context.Context, keyNode, root *yaml.Node, idx *in
|
||||
}
|
||||
if codes != nil {
|
||||
r.Codes = codes
|
||||
for codePairs := codes.First(); codePairs != nil; codePairs = codePairs.Next() {
|
||||
code := codePairs.Key()
|
||||
r.Nodes.Store(code.KeyNode.Line, code.KeyNode)
|
||||
}
|
||||
}
|
||||
|
||||
def := r.getDefault()
|
||||
if def != nil {
|
||||
// default is bundled into codes, pull it out
|
||||
r.Default = *def
|
||||
r.Nodes.Store(def.KeyNode.Line, def.KeyNode)
|
||||
// remove default from codes
|
||||
r.deleteCode(DefaultLabel)
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ type SecurityScheme struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the SecurityScheme object.
|
||||
@@ -67,7 +68,9 @@ func (ss *SecurityScheme) Build(ctx context.Context, keyNode, root *yaml.Node, i
|
||||
ss.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
ss.Reference = new(low.Reference)
|
||||
ss.Nodes = low.ExtractNodes(ctx, root)
|
||||
ss.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, ss.Extensions, ss.Nodes)
|
||||
|
||||
oa, oaErr := low.ExtractObject[*OAuthFlows](ctx, OAuthFlowsLabel, root, idx)
|
||||
if oaErr != nil {
|
||||
|
||||
@@ -25,6 +25,7 @@ type Server struct {
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the Server object.
|
||||
@@ -43,13 +44,16 @@ func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVaria
|
||||
}
|
||||
|
||||
// Build will extract server variables from the supplied node.
|
||||
func (s *Server) Build(_ context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
func (s *Server) Build(ctx context.Context, keyNode, root *yaml.Node, _ *index.SpecIndex) error {
|
||||
s.KeyNode = keyNode
|
||||
root = utils.NodeAlias(root)
|
||||
s.RootNode = root
|
||||
utils.CheckForMergeNodes(root)
|
||||
s.Reference = new(low.Reference)
|
||||
s.Nodes = low.ExtractNodes(ctx, root)
|
||||
s.Extensions = low.ExtractExtensions(root)
|
||||
low.ExtractExtensionNodes(ctx, s.Extensions, s.Nodes)
|
||||
|
||||
kn, vars := utils.FindKeyNode(VariablesLabel, root.Content)
|
||||
if vars == nil {
|
||||
return nil
|
||||
@@ -57,20 +61,26 @@ func (s *Server) Build(_ context.Context, keyNode, root *yaml.Node, _ *index.Spe
|
||||
variablesMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*ServerVariable]]()
|
||||
if utils.IsNodeMap(vars) {
|
||||
var currentNode string
|
||||
var keyNode *yaml.Node
|
||||
var localKeyNode *yaml.Node
|
||||
for i, varNode := range vars.Content {
|
||||
if i%2 == 0 {
|
||||
currentNode = varNode.Value
|
||||
keyNode = varNode
|
||||
localKeyNode = varNode
|
||||
continue
|
||||
}
|
||||
variable := ServerVariable{}
|
||||
variable.Reference = new(low.Reference)
|
||||
_ = low.BuildModel(varNode, &variable)
|
||||
variable.Nodes = low.ExtractNodesRecursive(ctx, varNode)
|
||||
if localKeyNode != nil {
|
||||
variable.Nodes.Store(localKeyNode.Line, localKeyNode)
|
||||
}
|
||||
variable.RootNode = varNode
|
||||
variable.KeyNode = localKeyNode
|
||||
variablesMap.Set(
|
||||
low.KeyReference[string]{
|
||||
Value: currentNode,
|
||||
KeyNode: keyNode,
|
||||
KeyNode: localKeyNode,
|
||||
},
|
||||
low.ValueReference[*ServerVariable]{
|
||||
ValueNode: varNode,
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
@@ -19,7 +20,20 @@ type ServerVariable struct {
|
||||
Enum []low.NodeReference[string]
|
||||
Default low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
KeyNode *yaml.Node
|
||||
RootNode *yaml.Node
|
||||
*low.Reference
|
||||
low.NodeMap
|
||||
}
|
||||
|
||||
// GetRootNode returns the root yaml node of the ServerVariable object.
|
||||
func (s *ServerVariable) GetRootNode() *yaml.Node {
|
||||
return s.RootNode
|
||||
}
|
||||
|
||||
// GetKeyNode returns the key yaml node of the ServerVariable object.
|
||||
func (s *ServerVariable) GetKeyNode() *yaml.Node {
|
||||
return s.RootNode
|
||||
}
|
||||
|
||||
// Hash will return a consistent SHA256 Hash of the ServerVariable object
|
||||
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
// used by the library, this contains the top of the document tree that every single low model is based off.
|
||||
type SpecInfo struct {
|
||||
SpecType string `json:"type"`
|
||||
NumLines int `json:"numLines"`
|
||||
Version string `json:"version"`
|
||||
VersionNumeric float32 `json:"versionNumeric"`
|
||||
SpecFormat string `json:"format"`
|
||||
@@ -62,7 +63,8 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro
|
||||
// set original bytes
|
||||
specInfo.SpecBytes = &spec
|
||||
|
||||
runes := []rune(strings.TrimSpace(string(spec)))
|
||||
stringSpec := string(spec)
|
||||
runes := []rune(strings.TrimSpace(stringSpec))
|
||||
if len(runes) <= 0 {
|
||||
return specInfo, errors.New("there is nothing in the spec, it's empty - so there is nothing to be done")
|
||||
}
|
||||
@@ -73,6 +75,8 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro
|
||||
specInfo.SpecFileType = YAMLFileType
|
||||
}
|
||||
|
||||
specInfo.NumLines = strings.Count(stringSpec, "\n") + 1
|
||||
|
||||
err := yaml.Unmarshal(spec, &parsedSpec)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse specification: %s", err.Error())
|
||||
|
||||
@@ -365,7 +365,7 @@ func TestDocument_RenderAndReload_WithErrors(t *testing.T) {
|
||||
|
||||
_, _, _, errors := doc.RenderAndReload()
|
||||
assert.Len(t, errors, 2)
|
||||
assert.Equal(t, errors[0].Error(), "component '#/components/schemas/Pet' does not exist in the specification")
|
||||
assert.Equal(t, errors[0].Error(), "component `#/components/schemas/Pet` does not exist in the specification")
|
||||
}
|
||||
|
||||
func TestDocument_Render(t *testing.T) {
|
||||
|
||||
1
go.mod
1
go.mod
@@ -7,6 +7,7 @@ require (
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/vmware-labs/yaml-jsonpath v0.3.2
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
|
||||
@@ -274,7 +274,7 @@ paths:
|
||||
|
||||
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||
assert.Len(t, index.GetReferenceIndexErrors(), 1)
|
||||
assert.Equal(t, "component '#/paths/~1pet~1%$petId%7D/get/parameters' does not exist in the specification", index.GetReferenceIndexErrors()[0].Error())
|
||||
assert.Equal(t, "component `#/paths/~1pet~1%$petId%7D/get/parameters` does not exist in the specification", index.GetReferenceIndexErrors()[0].Error())
|
||||
}
|
||||
|
||||
func TestSpecIndex_LocateRemoteDocsWithEscapedCharacters(t *testing.T) {
|
||||
|
||||
@@ -121,6 +121,11 @@ func (r *Rolodex) GetRootIndex() *SpecIndex {
|
||||
return r.rootIndex
|
||||
}
|
||||
|
||||
// GetConfig returns the index configuration of the rolodex.
|
||||
func (r *Rolodex) GetConfig() *SpecIndexConfig {
|
||||
return r.indexConfig
|
||||
}
|
||||
|
||||
// GetRootNode returns the root index of the rolodex (the entry point, the main document)
|
||||
func (r *Rolodex) GetRootNode() *yaml.Node {
|
||||
return r.rootNode
|
||||
|
||||
@@ -31,6 +31,7 @@ func TestRolodex_NewRolodex(t *testing.T) {
|
||||
assert.Nil(t, rolo.GetRootIndex())
|
||||
assert.Len(t, rolo.GetIndexes(), 0)
|
||||
assert.Len(t, rolo.GetCaughtErrors(), 0)
|
||||
assert.NotNil(t, rolo.GetConfig())
|
||||
}
|
||||
|
||||
func TestRolodex_NoFS(t *testing.T) {
|
||||
|
||||
@@ -613,6 +613,12 @@ func IsJSON(testString string) bool {
|
||||
}
|
||||
|
||||
// IsYAML will tell you if a string is YAML or not.
|
||||
var (
|
||||
yamlKeyValuePattern = regexp.MustCompile(`(?m)^\s*[a-zA-Z0-9_-]+\s*:\s*.+$`)
|
||||
yamlListPattern = regexp.MustCompile(`(?m)^\s*-\s+.+$`)
|
||||
yamlHeaderPattern = regexp.MustCompile(`(?m)^---\s*$`)
|
||||
)
|
||||
|
||||
func IsYAML(testString string) bool {
|
||||
if testString == "" {
|
||||
return false
|
||||
@@ -620,13 +626,21 @@ func IsYAML(testString string) bool {
|
||||
if IsJSON(testString) {
|
||||
return false
|
||||
}
|
||||
var n interface{}
|
||||
err := yaml.Unmarshal([]byte(testString), &n)
|
||||
if err != nil {
|
||||
return false
|
||||
|
||||
// Trim leading and trailing whitespace
|
||||
s := strings.TrimSpace(testString)
|
||||
|
||||
// Fast checks for common YAML features
|
||||
if strings.Contains(s, ": ") || strings.Contains(s, "- ") || strings.Contains(s, "\n- ") {
|
||||
return true
|
||||
}
|
||||
_, err = yaml.Marshal(n)
|
||||
return err == nil
|
||||
|
||||
// Regular expressions for more robust detection
|
||||
if yamlKeyValuePattern.MatchString(s) || yamlListPattern.MatchString(s) || yamlHeaderPattern.MatchString(s) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ConvertYAMLtoJSON will do exactly what you think it will. It will deserialize YAML into serialized JSON.
|
||||
|
||||
@@ -672,10 +672,10 @@ func TestIsJSON(t *testing.T) {
|
||||
|
||||
func TestIsYAML(t *testing.T) {
|
||||
assert.True(t, IsYAML("hello:\n there:\n my-name: is quobix"))
|
||||
assert.True(t, IsYAML("potato shoes"))
|
||||
assert.False(t, IsYAML("potato shoes"))
|
||||
assert.False(t, IsYAML("{'hello':'there'}"))
|
||||
assert.False(t, IsYAML(""))
|
||||
assert.False(t, IsYAML("8908: hello: yeah: \n12309812: :123"))
|
||||
assert.True(t, IsYAML("8908: hello: yeah: \n12309812: :123"))
|
||||
}
|
||||
|
||||
func TestConvertYAMLtoJSON(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user