mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Added more support for YAML merge nodes, anchors and aliases
And added deeper support for Aliases. Also added in local file handling through renamed `FSHandler` configuration property for the index. Also re-ran `go fmt` Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
@@ -23,7 +23,7 @@ type Contact struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build is not implemented for Contact (there is nothing to build).
|
// Build is not implemented for Contact (there is nothing to build).
|
||||||
func (c *Contact) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (c *Contact) Build(_ *yaml.Node, _ *index.SpecIndex) error {
|
||||||
c.Reference = new(low.Reference)
|
c.Reference = new(low.Reference)
|
||||||
// not implemented.
|
// not implemented.
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ func (ex *Example) Hash() [32]byte {
|
|||||||
|
|
||||||
// Build extracts extensions and example value
|
// Build extracts extensions and example value
|
||||||
func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
ex.Reference = new(low.Reference)
|
ex.Reference = new(low.Reference)
|
||||||
ex.Extensions = low.ExtractExtensions(root)
|
ex.Extensions = low.ExtractExtensions(root)
|
||||||
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ x-cake: hot`
|
|||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
var n Example
|
var n Example
|
||||||
err := low.BuildModel(idxNode.Content[0], &n)
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
@@ -46,7 +46,7 @@ x-cake: hot`
|
|||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
var n Example
|
var n Example
|
||||||
err := low.BuildModel(idxNode.Content[0], &n)
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
@@ -73,7 +73,7 @@ value:
|
|||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
var n Example
|
var n Example
|
||||||
err := low.BuildModel(idxNode.Content[0], &n)
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
@@ -104,7 +104,39 @@ value:
|
|||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
|
var n Example
|
||||||
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
err = n.Build(idxNode.Content[0], idx)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "hot", n.Summary.Value)
|
||||||
|
assert.Equal(t, "cakes", n.Description.Value)
|
||||||
|
|
||||||
|
if v, ok := n.Value.Value.([]interface{}); ok {
|
||||||
|
assert.Equal(t, "wow", v[0])
|
||||||
|
assert.Equal(t, "such array", v[1])
|
||||||
|
} else {
|
||||||
|
assert.Fail(t, "failed to decode correctly.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExample_Build_Success_MergeNode(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `x-things: &things
|
||||||
|
summary: hot
|
||||||
|
description: cakes
|
||||||
|
value:
|
||||||
|
- wow
|
||||||
|
- such array
|
||||||
|
<<: *things`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
assert.NoError(t, mErr)
|
||||||
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
|
||||||
|
|
||||||
var n Example
|
var n Example
|
||||||
err := low.BuildModel(idxNode.Content[0], &n)
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -33,6 +34,8 @@ func (ex *ExternalDoc) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
|
|
||||||
// Build will extract extensions from the ExternalDoc instance.
|
// Build will extract extensions from the ExternalDoc instance.
|
||||||
func (ex *ExternalDoc) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ex *ExternalDoc) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
ex.Reference = new(low.Reference)
|
ex.Reference = new(low.Reference)
|
||||||
ex.Extensions = low.ExtractExtensions(root)
|
ex.Extensions = low.ExtractExtensions(root)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package base
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -45,6 +46,8 @@ func (i *Info) GetExtensions() map[low.KeyReference[string]]low.ValueReference[a
|
|||||||
|
|
||||||
// Build will extract out the Contact and Info objects from the supplied root node.
|
// Build will extract out the Contact and Info objects from the supplied root node.
|
||||||
func (i *Info) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (i *Info) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
i.Reference = new(low.Reference)
|
i.Reference = new(low.Reference)
|
||||||
i.Extensions = low.ExtractExtensions(root)
|
i.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -25,6 +26,8 @@ type License struct {
|
|||||||
|
|
||||||
// Build out a license, complain if both a URL and identifier are present as they are mutually exclusive
|
// Build out a license, complain if both a URL and identifier are present as they are mutually exclusive
|
||||||
func (l *License) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (l *License) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
l.Reference = new(low.Reference)
|
l.Reference = new(low.Reference)
|
||||||
if l.URL.Value != "" && l.Identifier.Value != "" {
|
if l.URL.Value != "" && l.Identifier.Value != "" {
|
||||||
return fmt.Errorf("license cannot have both a URL and an identifier, they are mutually exclusive")
|
return fmt.Errorf("license cannot have both a URL and an identifier, they are mutually exclusive")
|
||||||
|
|||||||
@@ -62,11 +62,11 @@ type Schema struct {
|
|||||||
// Reference to the '$schema' dialect setting (3.1 only)
|
// Reference to the '$schema' dialect setting (3.1 only)
|
||||||
SchemaTypeRef low.NodeReference[string]
|
SchemaTypeRef low.NodeReference[string]
|
||||||
|
|
||||||
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
||||||
ExclusiveMaximum low.NodeReference[*SchemaDynamicValue[bool, float64]]
|
ExclusiveMaximum low.NodeReference[*SchemaDynamicValue[bool, float64]]
|
||||||
|
|
||||||
// In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean.
|
// In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean.
|
||||||
ExclusiveMinimum low.NodeReference[*SchemaDynamicValue[bool, float64]]
|
ExclusiveMinimum low.NodeReference[*SchemaDynamicValue[bool, float64]]
|
||||||
|
|
||||||
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
||||||
// in version 3.1, Type can be multiple values
|
// in version 3.1, Type can be multiple values
|
||||||
@@ -103,37 +103,37 @@ type Schema struct {
|
|||||||
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]
|
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]
|
||||||
Anchor low.NodeReference[string]
|
Anchor low.NodeReference[string]
|
||||||
|
|
||||||
// Compatible with all versions
|
// Compatible with all versions
|
||||||
Title low.NodeReference[string]
|
Title low.NodeReference[string]
|
||||||
MultipleOf low.NodeReference[float64]
|
MultipleOf low.NodeReference[float64]
|
||||||
Maximum low.NodeReference[float64]
|
Maximum low.NodeReference[float64]
|
||||||
Minimum low.NodeReference[float64]
|
Minimum low.NodeReference[float64]
|
||||||
MaxLength low.NodeReference[int64]
|
MaxLength low.NodeReference[int64]
|
||||||
MinLength low.NodeReference[int64]
|
MinLength low.NodeReference[int64]
|
||||||
Pattern low.NodeReference[string]
|
Pattern low.NodeReference[string]
|
||||||
Format low.NodeReference[string]
|
Format low.NodeReference[string]
|
||||||
MaxItems low.NodeReference[int64]
|
MaxItems low.NodeReference[int64]
|
||||||
MinItems low.NodeReference[int64]
|
MinItems low.NodeReference[int64]
|
||||||
UniqueItems low.NodeReference[bool]
|
UniqueItems low.NodeReference[bool]
|
||||||
MaxProperties low.NodeReference[int64]
|
MaxProperties low.NodeReference[int64]
|
||||||
MinProperties low.NodeReference[int64]
|
MinProperties low.NodeReference[int64]
|
||||||
Required low.NodeReference[[]low.ValueReference[string]]
|
Required low.NodeReference[[]low.ValueReference[string]]
|
||||||
Enum low.NodeReference[[]low.ValueReference[any]]
|
Enum low.NodeReference[[]low.ValueReference[any]]
|
||||||
Not low.NodeReference[*SchemaProxy]
|
Not low.NodeReference[*SchemaProxy]
|
||||||
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
||||||
AdditionalProperties low.NodeReference[any]
|
AdditionalProperties low.NodeReference[any]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
ContentEncoding low.NodeReference[string]
|
ContentEncoding low.NodeReference[string]
|
||||||
ContentMediaType low.NodeReference[string]
|
ContentMediaType low.NodeReference[string]
|
||||||
Default low.NodeReference[any]
|
Default low.NodeReference[any]
|
||||||
Nullable low.NodeReference[bool]
|
Nullable low.NodeReference[bool]
|
||||||
ReadOnly low.NodeReference[bool]
|
ReadOnly low.NodeReference[bool]
|
||||||
WriteOnly low.NodeReference[bool]
|
WriteOnly low.NodeReference[bool]
|
||||||
XML low.NodeReference[*XML]
|
XML low.NodeReference[*XML]
|
||||||
ExternalDocs low.NodeReference[*ExternalDoc]
|
ExternalDocs low.NodeReference[*ExternalDoc]
|
||||||
Example low.NodeReference[any]
|
Example low.NodeReference[any]
|
||||||
Deprecated low.NodeReference[bool]
|
Deprecated low.NodeReference[bool]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
|
|
||||||
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
||||||
ParentProxy *SchemaProxy
|
ParentProxy *SchemaProxy
|
||||||
@@ -531,6 +531,8 @@ func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference
|
|||||||
// - UnevaluatedProperties
|
// - UnevaluatedProperties
|
||||||
// - Anchor
|
// - Anchor
|
||||||
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
s.Reference = new(low.Reference)
|
s.Reference = new(low.Reference)
|
||||||
if h, _, _ := utils.IsNodeRefValue(root); h {
|
if h, _, _ := utils.IsNodeRefValue(root); h {
|
||||||
ref, err := low.LocateRefNode(root, idx)
|
ref, err := low.LocateRefNode(root, idx)
|
||||||
@@ -581,47 +583,47 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine exclusive minimum type, bool (3.0) or int (3.1)
|
// determine exclusive minimum type, bool (3.0) or int (3.1)
|
||||||
_, exMinLabel, exMinValue := utils.FindKeyNodeFullTop(ExclusiveMinimumLabel, root.Content)
|
_, exMinLabel, exMinValue := utils.FindKeyNodeFullTop(ExclusiveMinimumLabel, root.Content)
|
||||||
if exMinValue != nil {
|
if exMinValue != nil {
|
||||||
if utils.IsNodeBoolValue(exMinValue) {
|
if utils.IsNodeBoolValue(exMinValue) {
|
||||||
val, _ := strconv.ParseBool(exMinValue.Value)
|
val, _ := strconv.ParseBool(exMinValue.Value)
|
||||||
s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
||||||
KeyNode: exMinLabel,
|
KeyNode: exMinLabel,
|
||||||
ValueNode: exMinValue,
|
ValueNode: exMinValue,
|
||||||
Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val},
|
Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if utils.IsNodeIntValue(exMinValue) {
|
if utils.IsNodeIntValue(exMinValue) {
|
||||||
val, _ := strconv.ParseFloat(exMinValue.Value, 64)
|
val, _ := strconv.ParseFloat(exMinValue.Value, 64)
|
||||||
s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
s.ExclusiveMinimum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
||||||
KeyNode: exMinLabel,
|
KeyNode: exMinLabel,
|
||||||
ValueNode: exMinValue,
|
ValueNode: exMinValue,
|
||||||
Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val},
|
Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine exclusive maximum type, bool (3.0) or int (3.1)
|
// determine exclusive maximum type, bool (3.0) or int (3.1)
|
||||||
_, exMaxLabel, exMaxValue := utils.FindKeyNodeFullTop(ExclusiveMaximumLabel, root.Content)
|
_, exMaxLabel, exMaxValue := utils.FindKeyNodeFullTop(ExclusiveMaximumLabel, root.Content)
|
||||||
if exMaxValue != nil {
|
if exMaxValue != nil {
|
||||||
if utils.IsNodeBoolValue(exMaxValue) {
|
if utils.IsNodeBoolValue(exMaxValue) {
|
||||||
val, _ := strconv.ParseBool(exMaxValue.Value)
|
val, _ := strconv.ParseBool(exMaxValue.Value)
|
||||||
s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
||||||
KeyNode: exMaxLabel,
|
KeyNode: exMaxLabel,
|
||||||
ValueNode: exMaxValue,
|
ValueNode: exMaxValue,
|
||||||
Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val},
|
Value: &SchemaDynamicValue[bool, float64]{N: 0, A: val},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if utils.IsNodeIntValue(exMaxValue) {
|
if utils.IsNodeIntValue(exMaxValue) {
|
||||||
val, _ := strconv.ParseFloat(exMaxValue.Value, 64)
|
val, _ := strconv.ParseFloat(exMaxValue.Value, 64)
|
||||||
s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
s.ExclusiveMaximum = low.NodeReference[*SchemaDynamicValue[bool, float64]]{
|
||||||
KeyNode: exMaxLabel,
|
KeyNode: exMaxLabel,
|
||||||
ValueNode: exMaxValue,
|
ValueNode: exMaxValue,
|
||||||
Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val},
|
Value: &SchemaDynamicValue[bool, float64]{N: 1, B: val},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle schema reference type if set. (3.1)
|
// handle schema reference type if set. (3.1)
|
||||||
_, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content)
|
_, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content)
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ func (sp *SchemaProxy) Schema() *Schema {
|
|||||||
return sp.rendered
|
return sp.rendered
|
||||||
}
|
}
|
||||||
schema := new(Schema)
|
schema := new(Schema)
|
||||||
|
utils.CheckForMergeNodes(sp.vn)
|
||||||
err := schema.Build(sp.vn, sp.idx)
|
err := schema.Build(sp.vn, sp.idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sp.buildError = err
|
sp.buildError = err
|
||||||
|
|||||||
@@ -73,3 +73,24 @@ func TestSchemaProxy_Build_HashInline(t *testing.T) {
|
|||||||
assert.Equal(t, "6da88c34ba124c41f977db66a4fc5c1a951708d285c81bb0d47c3206f4c27ca8",
|
assert.Equal(t, "6da88c34ba124c41f977db66a4fc5c1a951708d285c81bb0d47c3206f4c27ca8",
|
||||||
low.GenerateHashString(&sch))
|
low.GenerateHashString(&sch))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSchemaProxy_Build_UsingMergeNodes(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `
|
||||||
|
x-common-definitions:
|
||||||
|
life_cycle_types: &life_cycle_types_def
|
||||||
|
type: string
|
||||||
|
enum: ["Onboarding", "Monitoring", "Re-Assessment"]
|
||||||
|
description: The type of life cycle
|
||||||
|
<<: *life_cycle_types_def`
|
||||||
|
|
||||||
|
var sch SchemaProxy
|
||||||
|
var idxNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
|
||||||
|
err := sch.Build(idxNode.Content[0], nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Len(t, sch.Schema().Enum.Value, 3)
|
||||||
|
assert.Equal(t, "The type of life cycle", sch.Schema().Description.Value)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -28,6 +29,8 @@ type SecurityRequirement struct {
|
|||||||
|
|
||||||
// Build will extract security requirements from the node (the structure is odd, to be honest)
|
// Build will extract security requirements from the node (the structure is odd, to be honest)
|
||||||
func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
s.Reference = new(low.Reference)
|
s.Reference = new(low.Reference)
|
||||||
var labelNode *yaml.Node
|
var labelNode *yaml.Node
|
||||||
valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
|
valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]])
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -34,6 +35,8 @@ func (t *Tag) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
|
|
||||||
// Build will extract extensions and external docs for the Tag.
|
// Build will extract extensions and external docs for the Tag.
|
||||||
func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
t.Reference = new(low.Reference)
|
t.Reference = new(low.Reference)
|
||||||
t.Extensions = low.ExtractExtensions(root)
|
t.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -31,6 +32,8 @@ type XML struct {
|
|||||||
|
|
||||||
// Build will extract extensions from the XML instance.
|
// Build will extract extensions from the XML instance.
|
||||||
func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
x.Reference = new(low.Reference)
|
x.Reference = new(low.Reference)
|
||||||
x.Extensions = low.ExtractExtensions(root)
|
x.Extensions = low.ExtractExtensions(root)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -6,14 +6,13 @@ package low
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindItemInMap accepts a string key and a collection of KeyReference[string] and ValueReference[T]. Every
|
// FindItemInMap accepts a string key and a collection of KeyReference[string] and ValueReference[T]. Every
|
||||||
@@ -86,14 +85,14 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, error) {
|
|||||||
found[rv].Node.Column)
|
found[rv].Node.Column)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return found[rv].Node, nil
|
return utils.NodeAlias(found[rv].Node), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform a search for the reference in the index
|
// perform a search for the reference in the index
|
||||||
foundRefs := idx.SearchIndexForReference(rv)
|
foundRefs := idx.SearchIndexForReference(rv)
|
||||||
if len(foundRefs) > 0 {
|
if len(foundRefs) > 0 {
|
||||||
return foundRefs[0].Node, nil
|
return utils.NodeAlias(foundRefs[0].Node), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// let's try something else to find our references.
|
// let's try something else to find our references.
|
||||||
@@ -106,7 +105,7 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, error) {
|
|||||||
nodes, fErr := path.Find(idx.GetRootNode())
|
nodes, fErr := path.Find(idx.GetRootNode())
|
||||||
if fErr == nil {
|
if fErr == nil {
|
||||||
if len(nodes) > 0 {
|
if len(nodes) > 0 {
|
||||||
return nodes[0], nil
|
return utils.NodeAlias(nodes[0]), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,6 +122,7 @@ func ExtractObjectRaw[T Buildable[N], N any](root *yaml.Node, idx *index.SpecInd
|
|||||||
var circError error
|
var circError error
|
||||||
var isReference bool
|
var isReference bool
|
||||||
var referenceValue string
|
var referenceValue string
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
if h, _, rv := utils.IsNodeRefValue(root); h {
|
if h, _, rv := utils.IsNodeRefValue(root); h {
|
||||||
ref, err := LocateRefNode(root, idx)
|
ref, err := LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
@@ -167,6 +167,7 @@ func ExtractObject[T Buildable[N], N any](label string, root *yaml.Node, idx *in
|
|||||||
var circError error
|
var circError error
|
||||||
var isReference bool
|
var isReference bool
|
||||||
var referenceValue string
|
var referenceValue string
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
if rf, rl, refVal := utils.IsNodeRefValue(root); rf {
|
if rf, rl, refVal := utils.IsNodeRefValue(root); rf {
|
||||||
ref, err := LocateRefNode(root, idx)
|
ref, err := LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
@@ -251,6 +252,7 @@ func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *ind
|
|||||||
) {
|
) {
|
||||||
var ln, vn *yaml.Node
|
var ln, vn *yaml.Node
|
||||||
var circError error
|
var circError error
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||||
ref, err := LocateRefNode(root, idx)
|
ref, err := LocateRefNode(root, idx)
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
@@ -370,7 +372,10 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
|
|||||||
if utils.IsNodeMap(root) {
|
if utils.IsNodeMap(root) {
|
||||||
var currentKey *yaml.Node
|
var currentKey *yaml.Node
|
||||||
skip := false
|
skip := false
|
||||||
for i, node := range root.Content {
|
rlen := len(root.Content)
|
||||||
|
|
||||||
|
for i := 0; i < rlen; i++ {
|
||||||
|
node := root.Content[i]
|
||||||
if !includeExtensions {
|
if !includeExtensions {
|
||||||
if strings.HasPrefix(strings.ToLower(node.Value), "x-") {
|
if strings.HasPrefix(strings.ToLower(node.Value), "x-") {
|
||||||
skip = true
|
skip = true
|
||||||
@@ -386,6 +391,14 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if currentKey.Tag == "!!merge" && currentKey.Value == "<<" {
|
||||||
|
root.Content = append(root.Content, utils.NodeAlias(node).Content...)
|
||||||
|
rlen = len(root.Content)
|
||||||
|
currentKey = nil
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node = utils.NodeAlias(node)
|
||||||
|
|
||||||
var isReference bool
|
var isReference bool
|
||||||
var referenceValue string
|
var referenceValue string
|
||||||
// if value is a reference, we have to look it up in the index!
|
// if value is a reference, we have to look it up in the index!
|
||||||
@@ -470,6 +483,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
|
|||||||
var referenceValue string
|
var referenceValue string
|
||||||
var labelNode, valueNode *yaml.Node
|
var labelNode, valueNode *yaml.Node
|
||||||
var circError error
|
var circError error
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
if rf, rl, rv := utils.IsNodeRefValue(root); rf {
|
if rf, rl, rv := utils.IsNodeRefValue(root); rf {
|
||||||
// locate reference in index.
|
// locate reference in index.
|
||||||
ref, err := LocateRefNode(root, idx)
|
ref, err := LocateRefNode(root, idx)
|
||||||
@@ -515,6 +529,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
|
|||||||
|
|
||||||
buildMap := func(label *yaml.Node, value *yaml.Node, c chan mappingResult[PT], ec chan<- error, ref string) {
|
buildMap := func(label *yaml.Node, value *yaml.Node, c chan mappingResult[PT], ec chan<- error, ref string) {
|
||||||
var n PT = new(N)
|
var n PT = new(N)
|
||||||
|
value = utils.NodeAlias(value)
|
||||||
_ = BuildModel(value, n)
|
_ = BuildModel(value, n)
|
||||||
err := n.Build(value, idx)
|
err := n.Build(value, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -544,6 +559,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
|
|||||||
|
|
||||||
totalKeys := 0
|
totalKeys := 0
|
||||||
for i, en := range valueNode.Content {
|
for i, en := range valueNode.Content {
|
||||||
|
en = utils.NodeAlias(en)
|
||||||
referenceValue = ""
|
referenceValue = ""
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
currentLabelNode = en
|
currentLabelNode = en
|
||||||
@@ -620,6 +636,7 @@ func ExtractMap[PT Buildable[N], N any](
|
|||||||
//
|
//
|
||||||
// int64, float64, bool, string
|
// int64, float64, bool, string
|
||||||
func ExtractExtensions(root *yaml.Node) map[KeyReference[string]]ValueReference[any] {
|
func ExtractExtensions(root *yaml.Node) map[KeyReference[string]]ValueReference[any] {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
extensions := utils.FindExtensionNodes(root.Content)
|
extensions := utils.FindExtensionNodes(root.Content)
|
||||||
extensionMap := make(map[KeyReference[string]]ValueReference[any])
|
extensionMap := make(map[KeyReference[string]]ValueReference[any])
|
||||||
for _, ext := range extensions {
|
for _, ext := range extensions {
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ func BuildModel(node *yaml.Node, model interface{}) error {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
node = utils.NodeAlias(node)
|
||||||
|
utils.CheckForMergeNodes(node)
|
||||||
|
|
||||||
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
||||||
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
||||||
@@ -51,6 +53,7 @@ func BuildModel(node *yaml.Node, model interface{}) error {
|
|||||||
kind := field.Kind()
|
kind := field.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
||||||
|
vn = utils.NodeAlias(vn)
|
||||||
err := SetField(&field, vn, kn)
|
err := SetField(&field, vn, kn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -213,31 +216,31 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
|||||||
|
|
||||||
case reflect.TypeOf(NodeReference[float32]{}):
|
case reflect.TypeOf(NodeReference[float32]{}):
|
||||||
|
|
||||||
if utils.IsNodeNumberValue(valueNode) {
|
if utils.IsNodeNumberValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.ParseFloat(valueNode.Value, 32)
|
fv, _ := strconv.ParseFloat(valueNode.Value, 32)
|
||||||
nr := NodeReference[float32]{
|
nr := NodeReference[float32]{
|
||||||
Value: float32(fv),
|
Value: float32(fv),
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[float64]{}):
|
case reflect.TypeOf(NodeReference[float64]{}):
|
||||||
|
|
||||||
if utils.IsNodeNumberValue(valueNode) {
|
if utils.IsNodeNumberValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.ParseFloat(valueNode.Value, 64)
|
fv, _ := strconv.ParseFloat(valueNode.Value, 64)
|
||||||
nr := NodeReference[float64]{
|
nr := NodeReference[float64]{
|
||||||
Value: fv,
|
Value: fv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[string]{}):
|
case reflect.TypeOf([]NodeReference[string]{}):
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -71,6 +72,8 @@ func (s *SecurityDefinitions) FindSecurityDefinition(securityDef string) *low.Va
|
|||||||
|
|
||||||
// Build will extract all definitions into SchemaProxy instances.
|
// Build will extract all definitions into SchemaProxy instances.
|
||||||
func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
errorChan := make(chan error)
|
errorChan := make(chan error)
|
||||||
resultChan := make(chan definitionResult[*base.SchemaProxy])
|
resultChan := make(chan definitionResult[*base.SchemaProxy])
|
||||||
var defLabel *yaml.Node
|
var defLabel *yaml.Node
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -27,6 +28,8 @@ func (e *Examples) FindExample(name string) *low.ValueReference[any] {
|
|||||||
|
|
||||||
// Build will extract all examples and will attempt to unmarshal content into a map or slice based on type.
|
// Build will extract all examples and will attempt to unmarshal content into a map or slice based on type.
|
||||||
func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
var keyNode, currNode *yaml.Node
|
var keyNode, currNode *yaml.Node
|
||||||
var err error
|
var err error
|
||||||
e.Values = make(map[low.KeyReference[string]]low.ValueReference[any])
|
e.Values = make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ func (h *Header) GetExtensions() map[low.KeyReference[string]]low.ValueReference
|
|||||||
|
|
||||||
// Build will build out items, extensions and default value from the supplied node.
|
// Build will build out items, extensions and default value from the supplied node.
|
||||||
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
h.Extensions = low.ExtractExtensions(root)
|
h.Extensions = low.ExtractExtensions(root)
|
||||||
items, err := low.ExtractObject[*Items](ItemsLabel, root, idx)
|
items, err := low.ExtractObject[*Items](ItemsLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ func (i *Items) Hash() [32]byte {
|
|||||||
|
|
||||||
// Build will build out items and default value.
|
// Build will build out items and default value.
|
||||||
func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
i.Extensions = low.ExtractExtensions(root)
|
i.Extensions = low.ExtractExtensions(root)
|
||||||
items, iErr := low.ExtractObject[*Items](ItemsLabel, root, idx)
|
items, iErr := low.ExtractObject[*Items](ItemsLabel, root, idx)
|
||||||
if iErr != nil {
|
if iErr != nil {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -36,6 +37,8 @@ type Operation struct {
|
|||||||
|
|
||||||
// Build will extract external docs, extensions, parameters, responses and security requirements.
|
// Build will extract external docs, extensions, parameters, responses and security requirements.
|
||||||
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
o.Extensions = low.ExtractExtensions(root)
|
o.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
// extract externalDocs
|
// extract externalDocs
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ func (p *Parameter) GetExtensions() map[low.KeyReference[string]]low.ValueRefere
|
|||||||
|
|
||||||
// Build will extract out extensions, schema, items and default value
|
// Build will extract out extensions, schema, items and default value
|
||||||
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
sch, sErr := base.ExtractSchema(root, idx)
|
sch, sErr := base.ExtractSchema(root, idx)
|
||||||
if sErr != nil {
|
if sErr != nil {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -47,6 +48,8 @@ func (p *PathItem) GetExtensions() map[low.KeyReference[string]]low.ValueReferen
|
|||||||
// Build will extract extensions, parameters and operations for all methods. Every method is handled
|
// Build will extract extensions, parameters and operations for all methods. Every method is handled
|
||||||
// asynchronously, in order to keep things moving quickly for complex operations.
|
// asynchronously, in order to keep things moving quickly for complex operations.
|
||||||
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
skip := false
|
skip := false
|
||||||
var currentNode *yaml.Node
|
var currentNode *yaml.Node
|
||||||
@@ -120,7 +123,7 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
|
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
|
|
||||||
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
||||||
|
|
||||||
opRef := low.NodeReference[*Operation]{
|
opRef := low.NodeReference[*Operation]{
|
||||||
Value: &op,
|
Value: &op,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -51,6 +52,8 @@ func (p *Paths) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
|
|
||||||
// Build will extract extensions and paths from node.
|
// Build will extract extensions and paths from node.
|
||||||
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
skip := false
|
skip := false
|
||||||
var currentNode *yaml.Node
|
var currentNode *yaml.Node
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -43,6 +44,8 @@ func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
|
|||||||
|
|
||||||
// Build will extract schema, extensions, examples and headers from node
|
// Build will extract schema, extensions, examples and headers from node
|
||||||
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
r.Extensions = low.ExtractExtensions(root)
|
r.Extensions = low.ExtractExtensions(root)
|
||||||
s, err := base.ExtractSchema(root, idx)
|
s, err := base.ExtractSchema(root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ func (r *Responses) GetExtensions() map[low.KeyReference[string]]low.ValueRefere
|
|||||||
|
|
||||||
// Build will extract default value and extensions from node.
|
// Build will extract default value and extensions from node.
|
||||||
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
r.Extensions = low.ExtractExtensions(root)
|
r.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
if utils.IsNodeMap(root) {
|
if utils.IsNodeMap(root) {
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ func (s *Scopes) FindScope(scope string) *low.ValueReference[string] {
|
|||||||
|
|
||||||
// Build will extract scope values and extensions from node.
|
// Build will extract scope values and extensions from node.
|
||||||
func (s *Scopes) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (s *Scopes) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
s.Extensions = low.ExtractExtensions(root)
|
s.Extensions = low.ExtractExtensions(root)
|
||||||
valueMap := make(map[low.KeyReference[string]]low.ValueReference[string])
|
valueMap := make(map[low.KeyReference[string]]low.ValueReference[string])
|
||||||
if utils.IsNodeMap(root) {
|
if utils.IsNodeMap(root) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -38,6 +39,8 @@ func (ss *SecurityScheme) GetExtensions() map[low.KeyReference[string]]low.Value
|
|||||||
|
|
||||||
// Build will extract extensions and scopes from the node.
|
// Build will extract extensions and scopes from the node.
|
||||||
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
ss.Extensions = low.ExtractExtensions(root)
|
ss.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
scopes, sErr := low.ExtractObject[*Scopes](ScopesLabel, root, idx)
|
scopes, sErr := low.ExtractObject[*Scopes](ScopesLabel, root, idx)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package v3
|
|||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -39,6 +40,8 @@ func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] {
|
|||||||
|
|
||||||
// Build will extract extensions, expressions and PathItem objects for Callback
|
// Build will extract extensions, expressions and PathItem objects for Callback
|
||||||
func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
cb.Reference = new(low.Reference)
|
cb.Reference = new(low.Reference)
|
||||||
cb.Extensions = low.ExtractExtensions(root)
|
cb.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -127,6 +127,8 @@ func (co *Components) FindCallback(callback string) *low.ValueReference[*Callbac
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
co.Reference = new(low.Reference)
|
co.Reference = new(low.Reference)
|
||||||
co.Extensions = low.ExtractExtensions(root)
|
co.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -58,6 +59,8 @@ func (en *Encoding) Hash() [32]byte {
|
|||||||
|
|
||||||
// Build will extract all Header objects from supplied node.
|
// Build will extract all Header objects from supplied node.
|
||||||
func (en *Encoding) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (en *Encoding) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
en.Reference = new(low.Reference)
|
en.Reference = new(low.Reference)
|
||||||
headers, hL, hN, err := low.ExtractMap[*Header](HeadersLabel, root, idx)
|
headers, hL, hN, err := low.ExtractMap[*Header](HeadersLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -96,6 +96,8 @@ func (h *Header) Hash() [32]byte {
|
|||||||
|
|
||||||
// Build will extract extensions, examples, schema and content/media types from node.
|
// Build will extract extensions, examples, schema and content/media types from node.
|
||||||
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
h.Reference = new(low.Reference)
|
h.Reference = new(low.Reference)
|
||||||
h.Extensions = low.ExtractExtensions(root)
|
h.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -53,6 +54,8 @@ func (l *Link) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
|
|
||||||
// Build will extract extensions and servers from the node.
|
// Build will extract extensions and servers from the node.
|
||||||
func (l *Link) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (l *Link) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(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.
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ func (mt *MediaType) GetAllExamples() map[low.KeyReference[string]]low.ValueRefe
|
|||||||
|
|
||||||
// Build will extract examples, extensions, schema and encoding from node.
|
// Build will extract examples, extensions, schema and encoding from node.
|
||||||
func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
mt.Reference = new(low.Reference)
|
mt.Reference = new(low.Reference)
|
||||||
mt.Extensions = low.ExtractExtensions(root)
|
mt.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -36,6 +37,8 @@ func (o *OAuthFlows) FindExtension(ext string) *low.ValueReference[any] {
|
|||||||
|
|
||||||
// Build will extract extensions and all OAuthFlow types from the supplied node.
|
// Build will extract extensions and all OAuthFlow types from the supplied node.
|
||||||
func (o *OAuthFlows) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (o *OAuthFlows) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
o.Reference = new(low.Reference)
|
o.Reference = new(low.Reference)
|
||||||
o.Extensions = low.ExtractExtensions(root)
|
o.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -55,6 +56,8 @@ func (o *Operation) FindSecurityRequirement(name string) []low.ValueReference[st
|
|||||||
|
|
||||||
// Build will extract external docs, parameters, request body, responses, callbacks, security and servers.
|
// Build will extract external docs, parameters, request body, responses, callbacks, security and servers.
|
||||||
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
o.Reference = new(low.Reference)
|
o.Reference = new(low.Reference)
|
||||||
o.Extensions = low.ExtractExtensions(root)
|
o.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ func (p *Parameter) GetExtensions() map[low.KeyReference[string]]low.ValueRefere
|
|||||||
|
|
||||||
// Build will extract examples, extensions and content/media types.
|
// Build will extract examples, extensions and content/media types.
|
||||||
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
p.Reference = new(low.Reference)
|
p.Reference = new(low.Reference)
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ func (p *PathItem) GetExtensions() map[low.KeyReference[string]]low.ValueReferen
|
|||||||
// Build extracts extensions, parameters, servers and each http method defined.
|
// Build extracts extensions, parameters, servers and each http method defined.
|
||||||
// everything is extracted asynchronously for speed.
|
// everything is extracted asynchronously for speed.
|
||||||
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
p.Reference = new(low.Reference)
|
p.Reference = new(low.Reference)
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
skip := false
|
skip := false
|
||||||
@@ -232,7 +234,7 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
||||||
|
|
||||||
opRef := low.NodeReference[*Operation]{
|
opRef := low.NodeReference[*Operation]{
|
||||||
Value: &op,
|
Value: &op,
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ func (p *Paths) GetExtensions() map[low.KeyReference[string]]low.ValueReference[
|
|||||||
|
|
||||||
// Build will extract extensions and all PathItems. This happens asynchronously for speed.
|
// Build will extract extensions and all PathItems. This happens asynchronously for speed.
|
||||||
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
p.Reference = new(low.Reference)
|
p.Reference = new(low.Reference)
|
||||||
p.Extensions = low.ExtractExtensions(root)
|
p.Extensions = low.ExtractExtensions(root)
|
||||||
skip := false
|
skip := false
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -40,6 +41,8 @@ func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType]
|
|||||||
|
|
||||||
// Build will extract extensions and MediaType objects from the node.
|
// Build will extract extensions and MediaType objects from the node.
|
||||||
func (rb *RequestBody) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (rb *RequestBody) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
rb.Reference = new(low.Reference)
|
rb.Reference = new(low.Reference)
|
||||||
rb.Extensions = low.ExtractExtensions(root)
|
rb.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -54,6 +55,8 @@ func (r *Response) FindLink(hType string) *low.ValueReference[*Link] {
|
|||||||
|
|
||||||
// Build will extract headers, extensions, content and links from node.
|
// Build will extract headers, extensions, content and links from node.
|
||||||
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
r.Reference = new(low.Reference)
|
r.Reference = new(low.Reference)
|
||||||
r.Extensions = low.ExtractExtensions(root)
|
r.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,10 @@ func (r *Responses) GetExtensions() map[low.KeyReference[string]]low.ValueRefere
|
|||||||
|
|
||||||
// Build will extract default response and all Response objects for each code
|
// Build will extract default response and all Response objects for each code
|
||||||
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
r.Reference = new(low.Reference)
|
r.Reference = new(low.Reference)
|
||||||
r.Extensions = low.ExtractExtensions(root)
|
r.Extensions = low.ExtractExtensions(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
if utils.IsNodeMap(root) {
|
if utils.IsNodeMap(root) {
|
||||||
codes, err := low.ExtractMapNoLookup[*Response](root, idx)
|
codes, err := low.ExtractMapNoLookup[*Response](root, idx)
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"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/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -48,6 +49,8 @@ func (ss *SecurityScheme) GetExtensions() map[low.KeyReference[string]]low.Value
|
|||||||
|
|
||||||
// Build will extract OAuthFlows and extensions from the node.
|
// Build will extract OAuthFlows and extensions from the node.
|
||||||
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
ss.Reference = new(low.Reference)
|
ss.Reference = new(low.Reference)
|
||||||
ss.Extensions = low.ExtractExtensions(root)
|
ss.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVaria
|
|||||||
|
|
||||||
// Build will extract server variables from the supplied node.
|
// Build will extract server variables from the supplied node.
|
||||||
func (s *Server) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (s *Server) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
|
root = utils.NodeAlias(root)
|
||||||
|
utils.CheckForMergeNodes(root)
|
||||||
s.Reference = new(low.Reference)
|
s.Reference = new(low.Reference)
|
||||||
s.Extensions = low.ExtractExtensions(root)
|
s.Extensions = low.ExtractExtensions(root)
|
||||||
kn, vars := utils.FindKeyNode(VariablesLabel, root.Content)
|
kn, vars := utils.FindKeyNode(VariablesLabel, root.Content)
|
||||||
|
|||||||
@@ -505,33 +505,34 @@ paths:
|
|||||||
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_RemoteWithoutBaseURL(t *testing.T) {
|
// disabled for now as the host is timing out
|
||||||
|
//func TestDocument_RemoteWithoutBaseURL(t *testing.T) {
|
||||||
// This test will push the index to do try and locate remote references that use relative references
|
//
|
||||||
spec := `openapi: 3.0.2
|
// // This test will push the index to do try and locate remote references that use relative references
|
||||||
info:
|
// spec := `openapi: 3.0.2
|
||||||
title: Test
|
//info:
|
||||||
version: 1.0.0
|
// title: Test
|
||||||
paths:
|
// version: 1.0.0
|
||||||
/test:
|
//paths:
|
||||||
get:
|
// /test:
|
||||||
parameters:
|
// get:
|
||||||
- $ref: "https://schemas.opengis.net/ogcapi/features/part2/1.0/openapi/ogcapi-features-2.yaml#/components/parameters/crs"`
|
// parameters:
|
||||||
|
// - $ref: "https://schemas.opengis.net/ogcapi/features/part2/1.0/openapi/ogcapi-features-2.yaml#/components/parameters/crs"`
|
||||||
config := datamodel.NewOpenDocumentConfiguration()
|
//
|
||||||
|
// config := datamodel.NewOpenDocumentConfiguration()
|
||||||
doc, err := NewDocumentWithConfiguration([]byte(spec), config)
|
//
|
||||||
if err != nil {
|
// doc, err := NewDocumentWithConfiguration([]byte(spec), config)
|
||||||
panic(err)
|
// if err != nil {
|
||||||
}
|
// panic(err)
|
||||||
|
// }
|
||||||
result, errs := doc.BuildV3Model()
|
//
|
||||||
if len(errs) > 0 {
|
// result, errs := doc.BuildV3Model()
|
||||||
panic(errs)
|
// if len(errs) > 0 {
|
||||||
}
|
// panic(errs)
|
||||||
|
// }
|
||||||
assert.Equal(t, "crs", result.Model.Paths.PathItems["/test"].Get.Parameters[0].Name)
|
//
|
||||||
}
|
// assert.Equal(t, "crs", result.Model.Paths.PathItems["/test"].Get.Parameters[0].Name)
|
||||||
|
//}
|
||||||
|
|
||||||
func TestDocument_ExampleMap(t *testing.T) {
|
func TestDocument_ExampleMap(t *testing.T) {
|
||||||
var d = `openapi: "3.1"
|
var d = `openapi: "3.1"
|
||||||
|
|||||||
@@ -4,86 +4,86 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindComponent will locate a component by its reference, returns nil if nothing is found.
|
// FindComponent will locate a component by its reference, returns nil if nothing is found.
|
||||||
// This method will recurse through remote, local and file references. For each new external reference
|
// This method will recurse through remote, local and file references. For each new external reference
|
||||||
// a new index will be created. These indexes can then be traversed recursively.
|
// a new index will be created. These indexes can then be traversed recursively.
|
||||||
func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference {
|
func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference {
|
||||||
if index.root == nil {
|
if index.root == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
||||||
if index.config.AllowRemoteLookup {
|
if index.config.AllowRemoteLookup {
|
||||||
return index.lookupRemoteReference(id)
|
return index.lookupRemoteReference(id)
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, fmt.Errorf("remote lookups are not permitted, " +
|
return nil, nil, fmt.Errorf("remote lookups are not permitted, " +
|
||||||
"please set AllowRemoteLookup to true in the configuration")
|
"please set AllowRemoteLookup to true in the configuration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
||||||
if index.config.AllowFileLookup {
|
if index.config.AllowFileLookup {
|
||||||
return index.lookupFileReference(id)
|
return index.lookupFileReference(id)
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, fmt.Errorf("local lookups are not permitted, " +
|
return nil, nil, fmt.Errorf("local lookups are not permitted, " +
|
||||||
"please set AllowFileLookup to true in the configuration")
|
"please set AllowFileLookup to true in the configuration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch DetermineReferenceResolveType(componentId) {
|
switch DetermineReferenceResolveType(componentId) {
|
||||||
case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
|
case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
|
||||||
return index.FindComponentInRoot(componentId)
|
return index.FindComponentInRoot(componentId)
|
||||||
|
|
||||||
case HttpResolve:
|
case HttpResolve:
|
||||||
uri := strings.Split(componentId, "#")
|
uri := strings.Split(componentId, "#")
|
||||||
if len(uri) >= 2 {
|
if len(uri) >= 2 {
|
||||||
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
||||||
}
|
}
|
||||||
if len(uri) == 1 {
|
if len(uri) == 1 {
|
||||||
// if there is no reference, second segment is empty / has no name
|
// if there is no reference, second segment is empty / has no name
|
||||||
// this means there is no component to look-up and the entire file should be pulled in.
|
// this means there is no component to look-up and the entire file should be pulled in.
|
||||||
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
||||||
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
||||||
// described in https://github.com/pb33f/libopenapi/issues/37
|
// described in https://github.com/pb33f/libopenapi/issues/37
|
||||||
componentId = fmt.Sprintf("%s#", componentId)
|
componentId = fmt.Sprintf("%s#", componentId)
|
||||||
uri = append(uri, "")
|
uri = append(uri, "")
|
||||||
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
case FileResolve:
|
case FileResolve:
|
||||||
uri := strings.Split(componentId, "#")
|
uri := strings.Split(componentId, "#")
|
||||||
if len(uri) == 2 {
|
if len(uri) == 2 {
|
||||||
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
||||||
}
|
}
|
||||||
if len(uri) == 1 {
|
if len(uri) == 1 {
|
||||||
// if there is no reference, second segment is empty / has no name
|
// if there is no reference, second segment is empty / has no name
|
||||||
// this means there is no component to look-up and the entire file should be pulled in.
|
// this means there is no component to look-up and the entire file should be pulled in.
|
||||||
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
||||||
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
||||||
// described in https://github.com/pb33f/libopenapi/issues/37
|
// described in https://github.com/pb33f/libopenapi/issues/37
|
||||||
//
|
//
|
||||||
// ^^ this same issue was re-reported in file based lookups in vacuum.
|
// ^^ this same issue was re-reported in file based lookups in vacuum.
|
||||||
// more info here: https://github.com/daveshanley/vacuum/issues/225
|
// more info here: https://github.com/daveshanley/vacuum/issues/225
|
||||||
componentId = fmt.Sprintf("%s#", componentId)
|
componentId = fmt.Sprintf("%s#", componentId)
|
||||||
uri = append(uri, "")
|
uri = append(uri, "")
|
||||||
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
||||||
@@ -91,339 +91,357 @@ var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
|||||||
type RemoteURLHandler = func(url string) (*http.Response, error)
|
type RemoteURLHandler = func(url string) (*http.Response, error)
|
||||||
|
|
||||||
func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) {
|
func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) {
|
||||||
resp, err := g(u)
|
resp, err := g(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
close(e)
|
close(e)
|
||||||
close(d)
|
close(d)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body []byte
|
var body []byte
|
||||||
body, _ = io.ReadAll(resp.Body)
|
body, _ = io.ReadAll(resp.Body)
|
||||||
d <- body
|
d <- body
|
||||||
close(e)
|
close(e)
|
||||||
close(d)
|
close(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
||||||
// split string to remove file reference
|
// split string to remove file reference
|
||||||
uri := strings.Split(ref, "#")
|
uri := strings.Split(ref, "#")
|
||||||
|
|
||||||
// have we already seen this remote source?
|
// have we already seen this remote source?
|
||||||
var parsedRemoteDocument *yaml.Node
|
var parsedRemoteDocument *yaml.Node
|
||||||
alreadySeen, foundDocument := index.CheckForSeenRemoteSource(uri[0])
|
alreadySeen, foundDocument := index.CheckForSeenRemoteSource(uri[0])
|
||||||
|
|
||||||
if alreadySeen {
|
if alreadySeen {
|
||||||
parsedRemoteDocument = foundDocument
|
parsedRemoteDocument = foundDocument
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
d := make(chan bool)
|
d := make(chan bool)
|
||||||
var body []byte
|
var body []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
go func(uri string) {
|
go func(uri string) {
|
||||||
bc := make(chan []byte)
|
bc := make(chan []byte)
|
||||||
ec := make(chan error)
|
ec := make(chan error)
|
||||||
var getter RemoteURLHandler = httpClient.Get
|
var getter RemoteURLHandler = httpClient.Get
|
||||||
if index.config != nil && index.config.RemoteURLHandler != nil {
|
if index.config != nil && index.config.RemoteURLHandler != nil {
|
||||||
getter = index.config.RemoteURLHandler
|
getter = index.config.RemoteURLHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have a remote handler, use it instead of the default.
|
// if we have a remote handler, use it instead of the default.
|
||||||
if index.config != nil && index.config.RemoteHandler != nil {
|
if index.config != nil && index.config.FSHandler != nil {
|
||||||
go func() {
|
go func() {
|
||||||
remoteFS := index.config.RemoteHandler
|
remoteFS := index.config.FSHandler
|
||||||
remoteFile, rErr := remoteFS.Open(uri)
|
remoteFile, rErr := remoteFS.Open(uri)
|
||||||
if rErr != nil {
|
if rErr != nil {
|
||||||
e := fmt.Errorf("unable to open remote file: %s", rErr)
|
e := fmt.Errorf("unable to open remote file: %s", rErr)
|
||||||
ec <- e
|
ec <- e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b, ioErr := io.ReadAll(remoteFile)
|
b, ioErr := io.ReadAll(remoteFile)
|
||||||
if ioErr != nil {
|
if ioErr != nil {
|
||||||
e := fmt.Errorf("unable to read remote file bytes: %s", ioErr)
|
e := fmt.Errorf("unable to read remote file bytes: %s", ioErr)
|
||||||
ec <- e
|
ec <- e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bc <- b
|
bc <- b
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
go getRemoteDoc(getter, uri, bc, ec)
|
go getRemoteDoc(getter, uri, bc, ec)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case v := <-bc:
|
case v := <-bc:
|
||||||
body = v
|
body = v
|
||||||
break
|
break
|
||||||
case er := <-ec:
|
case er := <-ec:
|
||||||
err = er
|
err = er
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(body) > 0 {
|
if len(body) > 0 {
|
||||||
var remoteDoc yaml.Node
|
var remoteDoc yaml.Node
|
||||||
er := yaml.Unmarshal(body, &remoteDoc)
|
er := yaml.Unmarshal(body, &remoteDoc)
|
||||||
if er != nil {
|
if er != nil {
|
||||||
err = er
|
err = er
|
||||||
d <- true
|
d <- true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parsedRemoteDocument = &remoteDoc
|
parsedRemoteDocument = &remoteDoc
|
||||||
if index.config != nil {
|
if index.config != nil {
|
||||||
index.config.seenRemoteSources.Store(uri, &remoteDoc)
|
index.config.seenRemoteSources.Store(uri, &remoteDoc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d <- true
|
d <- true
|
||||||
}(uri[0])
|
}(uri[0])
|
||||||
|
|
||||||
// wait for double go fun.
|
// wait for double go fun.
|
||||||
<-d
|
<-d
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// no bueno.
|
// no bueno.
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup item from reference by using a path query.
|
// lookup item from reference by using a path query.
|
||||||
var query string
|
var query string
|
||||||
if len(uri) >= 2 {
|
if len(uri) >= 2 {
|
||||||
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
||||||
} else {
|
} else {
|
||||||
query = "$"
|
query = "$"
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any URL encoding
|
// remove any URL encoding
|
||||||
query = strings.Replace(query, "~1", "./", 1)
|
query = strings.Replace(query, "~1", "./", 1)
|
||||||
query = strings.ReplaceAll(query, "~1", "/")
|
query = strings.ReplaceAll(query, "~1", "/")
|
||||||
|
|
||||||
path, err := yamlpath.NewPath(query)
|
path, err := yamlpath.NewPath(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
result, _ := path.Find(parsedRemoteDocument)
|
result, _ := path.Find(parsedRemoteDocument)
|
||||||
if len(result) == 1 {
|
if len(result) == 1 {
|
||||||
return result[0], parsedRemoteDocument, nil
|
return result[0], parsedRemoteDocument, nil
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) lookupFileReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
func (index *SpecIndex) lookupFileReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
||||||
// split string to remove file reference
|
// split string to remove file reference
|
||||||
uri := strings.Split(ref, "#")
|
uri := strings.Split(ref, "#")
|
||||||
file := strings.ReplaceAll(uri[0], "file:", "")
|
file := strings.ReplaceAll(uri[0], "file:", "")
|
||||||
filePath := filepath.Dir(file)
|
filePath := filepath.Dir(file)
|
||||||
fileName := filepath.Base(file)
|
fileName := filepath.Base(file)
|
||||||
|
|
||||||
var parsedRemoteDocument *yaml.Node
|
var parsedRemoteDocument *yaml.Node
|
||||||
|
|
||||||
if index.seenRemoteSources[file] != nil {
|
if index.seenRemoteSources[file] != nil {
|
||||||
parsedRemoteDocument = index.seenRemoteSources[file]
|
parsedRemoteDocument = index.seenRemoteSources[file]
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
base := index.config.BasePath
|
base := index.config.BasePath
|
||||||
fileToRead := filepath.Join(base, filePath, fileName)
|
fileToRead := filepath.Join(base, filePath, fileName)
|
||||||
|
var body []byte
|
||||||
|
var err error
|
||||||
|
|
||||||
// try and read the file off the local file system, if it fails
|
// if we have an FS handler, use it instead of the default behavior
|
||||||
// check for a baseURL and then ask our remote lookup function to go try and get it.
|
if index.config != nil && index.config.FSHandler != nil {
|
||||||
body, err := os.ReadFile(fileToRead)
|
remoteFS := index.config.FSHandler
|
||||||
|
remoteFile, rErr := remoteFS.Open(fileToRead)
|
||||||
|
if rErr != nil {
|
||||||
|
e := fmt.Errorf("unable to open file: %s", rErr)
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
body, err = io.ReadAll(remoteFile)
|
||||||
|
if err != nil {
|
||||||
|
e := fmt.Errorf("unable to read file bytes: %s", err)
|
||||||
|
return nil, nil, e
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
} else {
|
||||||
|
|
||||||
// if we have a baseURL, then we can try and get the file from there.
|
// try and read the file off the local file system, if it fails
|
||||||
if index.config != nil && index.config.BaseURL != nil {
|
// check for a baseURL and then ask our remote lookup function to go try and get it.
|
||||||
|
body, err = os.ReadFile(fileToRead)
|
||||||
|
|
||||||
u := index.config.BaseURL
|
if err != nil {
|
||||||
remoteRef := GenerateCleanSpecConfigBaseURL(u, ref, true)
|
|
||||||
a, b, e := index.lookupRemoteReference(remoteRef)
|
|
||||||
if e != nil {
|
|
||||||
// give up, we can't find the file, not locally, not remotely. It's toast.
|
|
||||||
return nil, nil, e
|
|
||||||
}
|
|
||||||
return a, b, nil
|
|
||||||
|
|
||||||
} else {
|
// if we have a baseURL, then we can try and get the file from there.
|
||||||
// no baseURL? then we can't do anything, give up.
|
if index.config != nil && index.config.BaseURL != nil {
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var remoteDoc yaml.Node
|
u := index.config.BaseURL
|
||||||
err = yaml.Unmarshal(body, &remoteDoc)
|
remoteRef := GenerateCleanSpecConfigBaseURL(u, ref, true)
|
||||||
if err != nil {
|
a, b, e := index.lookupRemoteReference(remoteRef)
|
||||||
return nil, nil, err
|
if e != nil {
|
||||||
}
|
// give up, we can't find the file, not locally, not remotely. It's toast.
|
||||||
parsedRemoteDocument = &remoteDoc
|
return nil, nil, e
|
||||||
if index.seenLocalSources != nil {
|
}
|
||||||
index.sourceLock.Lock()
|
return a, b, nil
|
||||||
index.seenLocalSources[file] = &remoteDoc
|
|
||||||
index.sourceLock.Unlock()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup item from reference by using a path query.
|
} else {
|
||||||
var query string
|
// no baseURL? then we can't do anything, give up.
|
||||||
if len(uri) >= 2 {
|
return nil, nil, err
|
||||||
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
}
|
||||||
} else {
|
}
|
||||||
query = "$"
|
}
|
||||||
}
|
var remoteDoc yaml.Node
|
||||||
|
err = yaml.Unmarshal(body, &remoteDoc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
parsedRemoteDocument = &remoteDoc
|
||||||
|
if index.seenLocalSources != nil {
|
||||||
|
index.sourceLock.Lock()
|
||||||
|
index.seenLocalSources[file] = &remoteDoc
|
||||||
|
index.sourceLock.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// remove any URL encoding
|
// lookup item from reference by using a path query.
|
||||||
query = strings.Replace(query, "~1", "./", 1)
|
var query string
|
||||||
query = strings.ReplaceAll(query, "~1", "/")
|
if len(uri) >= 2 {
|
||||||
|
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
||||||
|
} else {
|
||||||
|
query = "$"
|
||||||
|
}
|
||||||
|
|
||||||
path, err := yamlpath.NewPath(query)
|
// remove any URL encoding
|
||||||
if err != nil {
|
query = strings.Replace(query, "~1", "./", 1)
|
||||||
return nil, nil, err
|
query = strings.ReplaceAll(query, "~1", "/")
|
||||||
}
|
|
||||||
result, _ := path.Find(parsedRemoteDocument)
|
|
||||||
if len(result) == 1 {
|
|
||||||
return result[0], parsedRemoteDocument, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil, parsedRemoteDocument, nil
|
path, err := yamlpath.NewPath(query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
result, _ := path.Find(parsedRemoteDocument)
|
||||||
|
if len(result) == 1 {
|
||||||
|
return result[0], parsedRemoteDocument, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, parsedRemoteDocument, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
|
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
|
||||||
if index.root != nil {
|
if index.root != nil {
|
||||||
|
|
||||||
// check component for url encoding.
|
// check component for url encoding.
|
||||||
if strings.Contains(componentId, "%") {
|
if strings.Contains(componentId, "%") {
|
||||||
// decode the url.
|
// decode the url.
|
||||||
componentId, _ = url.QueryUnescape(componentId)
|
componentId, _ = url.QueryUnescape(componentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
|
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
|
||||||
path, err := yamlpath.NewPath(friendlySearch)
|
path, err := yamlpath.NewPath(friendlySearch)
|
||||||
if path == nil || err != nil {
|
if path == nil || err != nil {
|
||||||
return nil // no component found
|
return nil // no component found
|
||||||
}
|
}
|
||||||
res, _ := path.Find(index.root)
|
res, _ := path.Find(index.root)
|
||||||
|
|
||||||
if len(res) == 1 {
|
if len(res) == 1 {
|
||||||
resNode := res[0]
|
resNode := res[0]
|
||||||
if res[0].Kind == yaml.DocumentNode {
|
if res[0].Kind == yaml.DocumentNode {
|
||||||
resNode = res[0].Content[0]
|
resNode = res[0].Content[0]
|
||||||
}
|
}
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: componentId,
|
Definition: componentId,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: resNode,
|
Node: resNode,
|
||||||
Path: friendlySearch,
|
Path: friendlySearch,
|
||||||
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(res[0], map[string][]string{}),
|
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(res[0], map[string][]string{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
|
func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
|
||||||
lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference {
|
lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference {
|
||||||
if len(uri) > 0 {
|
if len(uri) > 0 {
|
||||||
index.externalLock.RLock()
|
index.externalLock.RLock()
|
||||||
externalSpecIndex := index.externalSpecIndex[uri[0]]
|
externalSpecIndex := index.externalSpecIndex[uri[0]]
|
||||||
index.externalLock.RUnlock()
|
index.externalLock.RUnlock()
|
||||||
|
|
||||||
if externalSpecIndex == nil {
|
if externalSpecIndex == nil {
|
||||||
_, newRoot, err := lookupFunction(componentId)
|
_, newRoot, err := lookupFunction(componentId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
indexError := &IndexingError{
|
indexError := &IndexingError{
|
||||||
Err: err,
|
Err: err,
|
||||||
Node: parent,
|
Node: parent,
|
||||||
Path: componentId,
|
Path: componentId,
|
||||||
}
|
}
|
||||||
index.errorLock.Lock()
|
index.errorLock.Lock()
|
||||||
index.refErrors = append(index.refErrors, indexError)
|
index.refErrors = append(index.refErrors, indexError)
|
||||||
index.errorLock.Unlock()
|
index.errorLock.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cool, cool, lets index this spec also. This is a recursive action and will keep going
|
// cool, cool, lets index this spec also. This is a recursive action and will keep going
|
||||||
// until all remote references have been found.
|
// until all remote references have been found.
|
||||||
var bp *url.URL
|
var bp *url.URL
|
||||||
var bd string
|
var bd string
|
||||||
|
|
||||||
if index.config.BaseURL != nil {
|
if index.config.BaseURL != nil {
|
||||||
bp = index.config.BaseURL
|
bp = index.config.BaseURL
|
||||||
}
|
}
|
||||||
if index.config.BasePath != "" {
|
if index.config.BasePath != "" {
|
||||||
bd = index.config.BasePath
|
bd = index.config.BasePath
|
||||||
}
|
}
|
||||||
|
|
||||||
var path, newBasePath string
|
var path, newBasePath string
|
||||||
var newUrl *url.URL
|
var newUrl *url.URL
|
||||||
|
|
||||||
if bp != nil {
|
if bp != nil {
|
||||||
path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false)
|
path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false)
|
||||||
newUrl, _ = url.Parse(path)
|
newUrl, _ = url.Parse(path)
|
||||||
newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path)))
|
newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path)))
|
||||||
}
|
}
|
||||||
if bd != "" {
|
if bd != "" {
|
||||||
if len(uri[0]) > 0 {
|
if len(uri[0]) > 0 {
|
||||||
// if there is no base url defined, but we can know we have been requested remotely,
|
// if there is no base url defined, but we can know we have been requested remotely,
|
||||||
// set the base url to the remote url base path.
|
// set the base url to the remote url base path.
|
||||||
// first check if the first param is actually a URL
|
// first check if the first param is actually a URL
|
||||||
io, er := url.ParseRequestURI(uri[0])
|
io, er := url.ParseRequestURI(uri[0])
|
||||||
if er != nil {
|
if er != nil {
|
||||||
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
||||||
} else {
|
} else {
|
||||||
if newUrl == nil || newUrl.String() != io.String() {
|
if newUrl == nil || newUrl.String() != io.String() {
|
||||||
newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path)))
|
newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path)))
|
||||||
}
|
}
|
||||||
newBasePath = filepath.Dir(filepath.Join(bd, uri[1]))
|
newBasePath = filepath.Dir(filepath.Join(bd, uri[1]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newUrl != nil || newBasePath != "" {
|
if newUrl != nil || newBasePath != "" {
|
||||||
newConfig := &SpecIndexConfig{
|
newConfig := &SpecIndexConfig{
|
||||||
BaseURL: newUrl,
|
BaseURL: newUrl,
|
||||||
BasePath: newBasePath,
|
BasePath: newBasePath,
|
||||||
AllowRemoteLookup: index.config.AllowRemoteLookup,
|
AllowRemoteLookup: index.config.AllowRemoteLookup,
|
||||||
AllowFileLookup: index.config.AllowFileLookup,
|
AllowFileLookup: index.config.AllowFileLookup,
|
||||||
ParentIndex: index,
|
ParentIndex: index,
|
||||||
seenRemoteSources: index.config.seenRemoteSources,
|
seenRemoteSources: index.config.seenRemoteSources,
|
||||||
remoteLock: index.config.remoteLock,
|
remoteLock: index.config.remoteLock,
|
||||||
uri: uri,
|
uri: uri,
|
||||||
}
|
}
|
||||||
|
|
||||||
var newIndex *SpecIndex
|
var newIndex *SpecIndex
|
||||||
seen := index.SearchAncestryForSeenURI(uri[0])
|
seen := index.SearchAncestryForSeenURI(uri[0])
|
||||||
if seen == nil {
|
if seen == nil {
|
||||||
|
|
||||||
newIndex = NewSpecIndexWithConfig(newRoot, newConfig)
|
newIndex = NewSpecIndexWithConfig(newRoot, newConfig)
|
||||||
index.refLock.Lock()
|
index.refLock.Lock()
|
||||||
index.externalLock.Lock()
|
index.externalLock.Lock()
|
||||||
index.externalSpecIndex[uri[0]] = newIndex
|
index.externalSpecIndex[uri[0]] = newIndex
|
||||||
index.externalLock.Unlock()
|
index.externalLock.Unlock()
|
||||||
newIndex.relativePath = path
|
newIndex.relativePath = path
|
||||||
newIndex.parentIndex = index
|
newIndex.parentIndex = index
|
||||||
index.AddChild(newIndex)
|
index.AddChild(newIndex)
|
||||||
index.refLock.Unlock()
|
index.refLock.Unlock()
|
||||||
externalSpecIndex = newIndex
|
externalSpecIndex = newIndex
|
||||||
} else {
|
} else {
|
||||||
externalSpecIndex = seen
|
externalSpecIndex = seen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if externalSpecIndex != nil {
|
if externalSpecIndex != nil {
|
||||||
foundRef := externalSpecIndex.FindComponentInRoot(uri[1])
|
foundRef := externalSpecIndex.FindComponentInRoot(uri[1])
|
||||||
if foundRef != nil {
|
if foundRef != nil {
|
||||||
nameSegs := strings.Split(uri[1], "/")
|
nameSegs := strings.Split(uri[1], "/")
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: componentId,
|
Definition: componentId,
|
||||||
Name: nameSegs[len(nameSegs)-1],
|
Name: nameSegs[len(nameSegs)-1],
|
||||||
Node: foundRef.Node,
|
Node: foundRef.Node,
|
||||||
IsRemote: true,
|
IsRemote: true,
|
||||||
RemoteLocation: componentId,
|
RemoteLocation: componentId,
|
||||||
Path: foundRef.Path,
|
Path: foundRef.Path,
|
||||||
}
|
}
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ paths:
|
|||||||
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
||||||
|
|
||||||
c := CreateOpenAPIIndexConfig()
|
c := CreateOpenAPIIndexConfig()
|
||||||
c.RemoteHandler = FS{}
|
c.FSHandler = FS{}
|
||||||
|
|
||||||
index := NewSpecIndexWithConfig(&rootNode, c)
|
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||||
|
|
||||||
@@ -301,6 +301,35 @@ paths:
|
|||||||
assert.Equal(t, "query", crsParam.Node.Content[5].Value)
|
assert.Equal(t, "query", crsParam.Node.Content[5].Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSpecIndex_UseFileHandler(t *testing.T) {
|
||||||
|
|
||||||
|
spec := `openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: Test Remote Handler
|
||||||
|
version: 1.0.0
|
||||||
|
paths:
|
||||||
|
/test:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- $ref: "some-file-that-does-not-exist.yaml"`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
||||||
|
|
||||||
|
c := CreateOpenAPIIndexConfig()
|
||||||
|
c.FSHandler = FS{}
|
||||||
|
|
||||||
|
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||||
|
|
||||||
|
// extract crs param from index
|
||||||
|
crsParam := index.GetMappedReferences()["some-file-that-does-not-exist.yaml"]
|
||||||
|
assert.NotNil(t, crsParam)
|
||||||
|
assert.True(t, crsParam.IsRemote)
|
||||||
|
assert.Equal(t, "string", crsParam.Node.Content[1].Value)
|
||||||
|
assert.Equal(t, "something", crsParam.Node.Content[3].Value)
|
||||||
|
assert.Equal(t, "query", crsParam.Node.Content[5].Value)
|
||||||
|
}
|
||||||
|
|
||||||
func TestSpecIndex_UseRemoteHandler_Error_Open(t *testing.T) {
|
func TestSpecIndex_UseRemoteHandler_Error_Open(t *testing.T) {
|
||||||
|
|
||||||
spec := `openapi: 3.1.0
|
spec := `openapi: 3.1.0
|
||||||
@@ -317,7 +346,7 @@ paths:
|
|||||||
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
||||||
|
|
||||||
c := CreateOpenAPIIndexConfig()
|
c := CreateOpenAPIIndexConfig()
|
||||||
c.RemoteHandler = FSBadOpen{}
|
c.FSHandler = FSBadOpen{}
|
||||||
c.RemoteURLHandler = httpClient.Get
|
c.RemoteURLHandler = httpClient.Get
|
||||||
|
|
||||||
index := NewSpecIndexWithConfig(&rootNode, c)
|
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||||
@@ -327,6 +356,32 @@ paths:
|
|||||||
assert.Equal(t, "component 'https://-i-cannot-be-opened.com' does not exist in the specification", index.GetReferenceIndexErrors()[1].Error())
|
assert.Equal(t, "component 'https://-i-cannot-be-opened.com' does not exist in the specification", index.GetReferenceIndexErrors()[1].Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSpecIndex_UseFileHandler_Error_Open(t *testing.T) {
|
||||||
|
|
||||||
|
spec := `openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: Test File Handler
|
||||||
|
version: 1.0.0
|
||||||
|
paths:
|
||||||
|
/test:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- $ref: "I-can-never-be-opened.yaml"`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
||||||
|
|
||||||
|
c := CreateOpenAPIIndexConfig()
|
||||||
|
c.FSHandler = FSBadOpen{}
|
||||||
|
c.RemoteURLHandler = httpClient.Get
|
||||||
|
|
||||||
|
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||||
|
|
||||||
|
assert.Len(t, index.GetReferenceIndexErrors(), 2)
|
||||||
|
assert.Equal(t, "unable to open file: bad file open", index.GetReferenceIndexErrors()[0].Error())
|
||||||
|
assert.Equal(t, "component 'I-can-never-be-opened.yaml' does not exist in the specification", index.GetReferenceIndexErrors()[1].Error())
|
||||||
|
}
|
||||||
|
|
||||||
func TestSpecIndex_UseRemoteHandler_Error_Read(t *testing.T) {
|
func TestSpecIndex_UseRemoteHandler_Error_Read(t *testing.T) {
|
||||||
|
|
||||||
spec := `openapi: 3.1.0
|
spec := `openapi: 3.1.0
|
||||||
@@ -343,7 +398,7 @@ paths:
|
|||||||
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
||||||
|
|
||||||
c := CreateOpenAPIIndexConfig()
|
c := CreateOpenAPIIndexConfig()
|
||||||
c.RemoteHandler = FSBadRead{}
|
c.FSHandler = FSBadRead{}
|
||||||
c.RemoteURLHandler = httpClient.Get
|
c.RemoteURLHandler = httpClient.Get
|
||||||
|
|
||||||
index := NewSpecIndexWithConfig(&rootNode, c)
|
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||||
@@ -352,3 +407,29 @@ paths:
|
|||||||
assert.Equal(t, "unable to read remote file bytes: bad file read", index.GetReferenceIndexErrors()[0].Error())
|
assert.Equal(t, "unable to read remote file bytes: bad file read", index.GetReferenceIndexErrors()[0].Error())
|
||||||
assert.Equal(t, "component 'https://-i-cannot-be-opened.com' does not exist in the specification", index.GetReferenceIndexErrors()[1].Error())
|
assert.Equal(t, "component 'https://-i-cannot-be-opened.com' does not exist in the specification", index.GetReferenceIndexErrors()[1].Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSpecIndex_UseFileHandler_Error_Read(t *testing.T) {
|
||||||
|
|
||||||
|
spec := `openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: Test File Handler
|
||||||
|
version: 1.0.0
|
||||||
|
paths:
|
||||||
|
/test:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- $ref: "I-am-impossible-to-open-forever.yaml"`
|
||||||
|
|
||||||
|
var rootNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(spec), &rootNode)
|
||||||
|
|
||||||
|
c := CreateOpenAPIIndexConfig()
|
||||||
|
c.FSHandler = FSBadRead{}
|
||||||
|
c.RemoteURLHandler = httpClient.Get
|
||||||
|
|
||||||
|
index := NewSpecIndexWithConfig(&rootNode, c)
|
||||||
|
|
||||||
|
assert.Len(t, index.GetReferenceIndexErrors(), 2)
|
||||||
|
assert.Equal(t, "unable to read file bytes: bad file read", index.GetReferenceIndexErrors()[0].Error())
|
||||||
|
assert.Equal(t, "component 'I-am-impossible-to-open-forever.yaml' does not exist in the specification", index.GetReferenceIndexErrors()[1].Error())
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,10 +67,19 @@ type SpecIndexConfig struct {
|
|||||||
// Resolves [#132]: https://github.com/pb33f/libopenapi/issues/132
|
// Resolves [#132]: https://github.com/pb33f/libopenapi/issues/132
|
||||||
RemoteURLHandler func(url string) (*http.Response, error)
|
RemoteURLHandler func(url string) (*http.Response, error)
|
||||||
|
|
||||||
// RemoteHandler is a function that will be used to fetch remote documents, it trumps the RemoteURLHandler
|
// FSHandler is an entity that implements the `fs.FS` interface that will be used to fetch local or remote documents.
|
||||||
// and will be used instead if it is set.
|
// This is useful if you want to use a custom file system handler, or if you want to use a custom http client or
|
||||||
|
// custom network implementation for a lookup.
|
||||||
|
//
|
||||||
|
// libopenapi will pass the path to the FSHandler, and it will be up to the handler to determine how to fetch
|
||||||
|
// the document. This is really useful if your application has a custom file system or uses a database for storing
|
||||||
|
// documents.
|
||||||
|
//
|
||||||
|
// Is the FSHandler is set, it will be used for all lookups, regardless of whether they are local or remote.
|
||||||
|
// it also overrides the RemoteURLHandler if set.
|
||||||
|
//
|
||||||
// Resolves[#85] https://github.com/pb33f/libopenapi/issues/85
|
// Resolves[#85] https://github.com/pb33f/libopenapi/issues/85
|
||||||
RemoteHandler fs.FS
|
FSHandler fs.FS
|
||||||
|
|
||||||
// If resolving locally, the BasePath will be the root from which relative references will be resolved from
|
// If resolving locally, the BasePath will be the root from which relative references will be resolved from
|
||||||
BasePath string // set the Base Path for resolving relative references if the spec is exploded.
|
BasePath string // set the Base Path for resolving relative references if the spec is exploded.
|
||||||
|
|||||||
@@ -196,9 +196,9 @@ func FindFirstKeyNode(key string, nodes []*yaml.Node, depth int) (keyNode *yaml.
|
|||||||
for i, v := range nodes {
|
for i, v := range nodes {
|
||||||
if key != "" && key == v.Value {
|
if key != "" && key == v.Value {
|
||||||
if i+1 >= len(nodes) {
|
if i+1 >= len(nodes) {
|
||||||
return v, nodes[i] // this is the node we need.
|
return v, NodeAlias(nodes[i]) // this is the node we need.
|
||||||
}
|
}
|
||||||
return v, nodes[i+1] // next node is what we need.
|
return v, NodeAlias(nodes[i+1]) // next node is what we need.
|
||||||
}
|
}
|
||||||
if len(v.Content) > 0 {
|
if len(v.Content) > 0 {
|
||||||
depth++
|
depth++
|
||||||
@@ -283,12 +283,12 @@ func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelN
|
|||||||
if key == v.Content[x].Value {
|
if key == v.Content[x].Value {
|
||||||
if IsNodeMap(v) {
|
if IsNodeMap(v) {
|
||||||
if x+1 == len(v.Content) {
|
if x+1 == len(v.Content) {
|
||||||
return v, v.Content[x], v.Content[x]
|
return v, v.Content[x], NodeAlias(v.Content[x])
|
||||||
}
|
}
|
||||||
return v, v.Content[x], v.Content[x+1]
|
return v, v.Content[x], NodeAlias(v.Content[x+1])
|
||||||
}
|
}
|
||||||
if IsNodeArray(v) {
|
if IsNodeArray(v) {
|
||||||
return v, v.Content[x], v.Content[x]
|
return v, v.Content[x], NodeAlias(v.Content[x])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,7 +304,7 @@ func FindKeyNodeFullTop(key string, nodes []*yaml.Node) (keyNode *yaml.Node, lab
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if i%2 == 0 && key == nodes[i].Value {
|
if i%2 == 0 && key == nodes[i].Value {
|
||||||
return nodes[i], nodes[i], nodes[i+1] // next node is what we need.
|
return nodes[i], nodes[i], NodeAlias(nodes[i+1]) // next node is what we need.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
@@ -322,7 +322,7 @@ func FindExtensionNodes(nodes []*yaml.Node) []*ExtensionNode {
|
|||||||
if i+1 < len(nodes) {
|
if i+1 < len(nodes) {
|
||||||
extensions = append(extensions, &ExtensionNode{
|
extensions = append(extensions, &ExtensionNode{
|
||||||
Key: v,
|
Key: v,
|
||||||
Value: nodes[i+1],
|
Value: NodeAlias(nodes[i+1]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -363,12 +363,38 @@ func IsNodeMap(node *yaml.Node) bool {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!map"
|
n := NodeAlias(node)
|
||||||
|
return n.Tag == "!!map"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNodeAlias checks if the node is an alias, and lifts out the anchor
|
||||||
|
func IsNodeAlias(node *yaml.Node) (*yaml.Node, bool) {
|
||||||
|
if node == nil {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
if node.Kind == yaml.AliasNode {
|
||||||
|
node = node.Alias
|
||||||
|
return node, true
|
||||||
|
}
|
||||||
|
return node, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeAlias checks if the node is an alias, and lifts out the anchor
|
||||||
|
func NodeAlias(node *yaml.Node) *yaml.Node {
|
||||||
|
if node == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if node.Kind == yaml.AliasNode {
|
||||||
|
node = node.Alias
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
return node
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodePolyMorphic will return true if the node contains polymorphic keys.
|
// IsNodePolyMorphic will return true if the node contains polymorphic keys.
|
||||||
func IsNodePolyMorphic(node *yaml.Node) bool {
|
func IsNodePolyMorphic(node *yaml.Node) bool {
|
||||||
for i, v := range node.Content {
|
n := NodeAlias(node)
|
||||||
|
for i, v := range n.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
if v.Value == "anyOf" || v.Value == "oneOf" || v.Value == "allOf" {
|
if v.Value == "anyOf" || v.Value == "oneOf" || v.Value == "allOf" {
|
||||||
return true
|
return true
|
||||||
@@ -383,7 +409,8 @@ func IsNodeArray(node *yaml.Node) bool {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!seq"
|
n := NodeAlias(node)
|
||||||
|
return n.Tag == "!!seq"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeStringValue checks if a node is a string value
|
// IsNodeStringValue checks if a node is a string value
|
||||||
@@ -391,7 +418,8 @@ func IsNodeStringValue(node *yaml.Node) bool {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!str"
|
n := NodeAlias(node)
|
||||||
|
return n.Tag == "!!str"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeIntValue will check if a node is an int value
|
// IsNodeIntValue will check if a node is an int value
|
||||||
@@ -399,7 +427,8 @@ func IsNodeIntValue(node *yaml.Node) bool {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!int"
|
n := NodeAlias(node)
|
||||||
|
return n.Tag == "!!int"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeFloatValue will check is a node is a float value.
|
// IsNodeFloatValue will check is a node is a float value.
|
||||||
@@ -407,7 +436,8 @@ func IsNodeFloatValue(node *yaml.Node) bool {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!float"
|
n := NodeAlias(node)
|
||||||
|
return n.Tag == "!!float"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsNodeNumberValue will check if a node can be parsed as a float value.
|
// IsNodeNumberValue will check if a node can be parsed as a float value.
|
||||||
@@ -423,18 +453,20 @@ func IsNodeBoolValue(node *yaml.Node) bool {
|
|||||||
if node == nil {
|
if node == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return node.Tag == "!!bool"
|
n := NodeAlias(node)
|
||||||
|
return n.Tag == "!!bool"
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsNodeRefValue(node *yaml.Node) (bool, *yaml.Node, string) {
|
func IsNodeRefValue(node *yaml.Node) (bool, *yaml.Node, string) {
|
||||||
|
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return false, nil, ""
|
return false, nil, ""
|
||||||
}
|
}
|
||||||
|
n := NodeAlias(node)
|
||||||
for i, r := range node.Content {
|
for i, r := range n.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
if r.Value == "$ref" {
|
if r.Value == "$ref" {
|
||||||
return true, r, node.Content[i+1].Value
|
return true, r, n.Content[i+1].Value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -691,3 +723,26 @@ func DetermineWhitespaceLength(input string) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckForMergeNodes will check the top level of the schema for merge nodes. If any are found, then the merged nodes
|
||||||
|
// will be appended to the end of the rest of the nodes in the schema.
|
||||||
|
// Note: this is a destructive operation, so the in-memory node structure will be modified
|
||||||
|
func CheckForMergeNodes(node *yaml.Node) {
|
||||||
|
if node == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
total := len(node.Content)
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
mn := node.Content[i]
|
||||||
|
if i%2 == 0 {
|
||||||
|
if mn.Tag == "!!merge" {
|
||||||
|
an := node.Content[i+1].Alias
|
||||||
|
if an != nil {
|
||||||
|
node.Content = append(node.Content, an.Content...) // append the merged nodes
|
||||||
|
total = len(node.Content)
|
||||||
|
i += 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -571,20 +571,20 @@ func TestIsNodeFloatValue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestIsNodeNumberValue(t *testing.T) {
|
func TestIsNodeNumberValue(t *testing.T) {
|
||||||
n := &yaml.Node{
|
n := &yaml.Node{
|
||||||
Tag: "!!float",
|
Tag: "!!float",
|
||||||
}
|
}
|
||||||
assert.True(t, IsNodeNumberValue(n))
|
assert.True(t, IsNodeNumberValue(n))
|
||||||
n.Tag = "!!pizza"
|
n.Tag = "!!pizza"
|
||||||
assert.False(t, IsNodeNumberValue(n))
|
assert.False(t, IsNodeNumberValue(n))
|
||||||
|
|
||||||
n = &yaml.Node{
|
n = &yaml.Node{
|
||||||
Tag: "!!int",
|
Tag: "!!int",
|
||||||
}
|
}
|
||||||
assert.True(t, IsNodeNumberValue(n))
|
assert.True(t, IsNodeNumberValue(n))
|
||||||
n.Tag = "!!pizza"
|
n.Tag = "!!pizza"
|
||||||
assert.False(t, IsNodeNumberValue(n))
|
assert.False(t, IsNodeNumberValue(n))
|
||||||
assert.False(t, IsNodeNumberValue(nil))
|
assert.False(t, IsNodeNumberValue(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsNodeFloatValue_Nil(t *testing.T) {
|
func TestIsNodeFloatValue_Nil(t *testing.T) {
|
||||||
@@ -767,6 +767,69 @@ func TestIsNodeRefValue(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsNodeAlias(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `things:
|
||||||
|
&anchorA
|
||||||
|
- Stuff
|
||||||
|
- Junk
|
||||||
|
thangs: *anchorA`
|
||||||
|
|
||||||
|
var node yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &node)
|
||||||
|
|
||||||
|
ref, a := IsNodeAlias(node.Content[0].Content[3])
|
||||||
|
|
||||||
|
assert.True(t, a)
|
||||||
|
assert.Len(t, ref.Content, 2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNodeAlias(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `things:
|
||||||
|
&anchorA
|
||||||
|
- Stuff
|
||||||
|
- Junk
|
||||||
|
thangs: *anchorA`
|
||||||
|
|
||||||
|
var node yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &node)
|
||||||
|
|
||||||
|
ref := NodeAlias(node.Content[0].Content[3])
|
||||||
|
|
||||||
|
assert.Len(t, ref.Content, 2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckForMergeNodes(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `x-common-definitions:
|
||||||
|
life_cycle_types: &life_cycle_types_def
|
||||||
|
type: string
|
||||||
|
enum: ["Onboarding", "Monitoring", "Re-Assessment"]
|
||||||
|
description: The type of life cycle
|
||||||
|
<<: *life_cycle_types_def`
|
||||||
|
|
||||||
|
var node yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &node)
|
||||||
|
|
||||||
|
mainNode := node.Content[0]
|
||||||
|
|
||||||
|
CheckForMergeNodes(mainNode)
|
||||||
|
|
||||||
|
_, _, enumVal := FindKeyNodeFullTop("enum", mainNode.Content)
|
||||||
|
_, _, descriptionVal := FindKeyNodeFullTop("description", mainNode.Content)
|
||||||
|
|
||||||
|
assert.Equal(t, "The type of life cycle", descriptionVal.Value)
|
||||||
|
assert.Len(t, enumVal.Content, 3)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckForMergeNodes_Empty_NoPanic(t *testing.T) {
|
||||||
|
CheckForMergeNodes(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsNodeRefValue_False(t *testing.T) {
|
func TestIsNodeRefValue_False(t *testing.T) {
|
||||||
|
|
||||||
f := &yaml.Node{
|
f := &yaml.Node{
|
||||||
|
|||||||
Reference in New Issue
Block a user