Bumping coverage on schema to 100%

I think it should be pretty much bullet proof at the moment (until the next failure). Also added external index lookups from main index for external docs. found this bug after refining lookups in schemas.
This commit is contained in:
Dave Shanley
2022-08-17 08:09:01 -04:00
parent 7c2a65f640
commit 75c7c83e31
5 changed files with 845 additions and 85 deletions

View File

@@ -70,8 +70,18 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) error {
level++
if level > 10 {
return nil // we're done, son! too fricken deep.
if level > 30 {
return fmt.Errorf("schema is too nested to continue: %d levels deep, is too deep", level) // we're done, son! too fricken deep.
}
if h, _, _ := utils.IsNodeRefValue(root); h {
ref := low.LocateRefNode(root, idx)
if ref != nil {
root = ref
} else {
return fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
}
}
s.extractExtensions(root)
@@ -102,10 +112,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
_, discLabel, discNode := utils.FindKeyNodeFull(DiscriminatorLabel, root.Content)
if discNode != nil {
var discriminator Discriminator
err := low.BuildModel(discNode, &discriminator)
if err != nil {
return err
}
_ = low.BuildModel(discNode, &discriminator)
s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode}
}
@@ -113,14 +120,8 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
_, extDocLabel, extDocNode := utils.FindKeyNodeFull(ExternalDocsLabel, root.Content)
if extDocNode != nil {
var exDoc ExternalDoc
err := low.BuildModel(extDocNode, &exDoc)
if err != nil {
return err
}
err = exDoc.Build(extDocNode, idx)
if err != nil {
return err
}
_ = low.BuildModel(extDocNode, &exDoc)
_ = exDoc.Build(extDocNode, idx) // throws no errors, can't check for one.
s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode}
}
@@ -128,15 +129,9 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
_, xmlLabel, xmlNode := utils.FindKeyNodeFull(XMLLabel, root.Content)
if xmlNode != nil {
var xml XML
err := low.BuildModel(xmlNode, &xml)
if err != nil {
return err
}
_ = low.BuildModel(xmlNode, &xml)
// extract extensions if set.
err = xml.Build(xmlNode)
if err != nil {
return err
}
_ = xml.Build(xmlNode) // returns no errors, can't check for one.
s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode}
}
@@ -156,15 +151,15 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
ref := low.LocateRefNode(prop, idx)
if ref != nil {
prop = ref
} else {
return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d",
prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line)
}
}
var property Schema
err := low.BuildModel(prop, &property)
if err != nil {
return err
}
err = property.BuildLevel(prop, idx, level)
_ = low.BuildModel(prop, &property)
err := property.BuildLevel(prop, idx, level)
if err != nil {
return err
}
@@ -181,59 +176,60 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
KeyNode: propLabel,
ValueNode: propsNode,
}
}
// extract all sub-schemas
var errors []error
// extract all sub-schemas
var errors []error
var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema]
var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema]
// make this async at some point to speed things up.
allOfLabel, allOfValue := buildSchema(&allOf, AllOfLabel, root, level, &errors, idx)
anyOfLabel, anyOfValue := buildSchema(&anyOf, AnyOfLabel, root, level, &errors, idx)
oneOfLabel, oneOfValue := buildSchema(&oneOf, OneOfLabel, root, level, &errors, idx)
notLabel, notValue := buildSchema(&not, NotLabel, root, level, &errors, idx)
itemsLabel, itemsValue := buildSchema(&items, ItemsLabel, root, level, &errors, idx)
// make this async at some point to speed things up.
allOfLabel, allOfValue := buildSchema(&allOf, AllOfLabel, root, level, &errors, idx)
anyOfLabel, anyOfValue := buildSchema(&anyOf, AnyOfLabel, root, level, &errors, idx)
oneOfLabel, oneOfValue := buildSchema(&oneOf, OneOfLabel, root, level, &errors, idx)
notLabel, notValue := buildSchema(&not, NotLabel, root, level, &errors, idx)
itemsLabel, itemsValue := buildSchema(&items, ItemsLabel, root, level, &errors, idx)
if len(errors) > 0 {
// todo fix this
return errors[0]
}
if len(anyOf) > 0 {
s.AnyOf = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: anyOf,
KeyNode: anyOfLabel,
ValueNode: anyOfValue,
}
}
if len(oneOf) > 0 {
s.OneOf = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: oneOf,
KeyNode: oneOfLabel,
ValueNode: oneOfValue,
}
}
if len(allOf) > 0 {
s.AllOf = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: allOf,
KeyNode: allOfLabel,
ValueNode: allOfValue,
}
}
if len(not) > 0 {
s.Not = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: not,
KeyNode: notLabel,
ValueNode: notValue,
}
}
if len(items) > 0 {
s.Items = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: items,
KeyNode: itemsLabel,
ValueNode: itemsValue,
}
if len(errors) > 0 {
// todo fix this
return errors[0]
}
if len(anyOf) > 0 {
s.AnyOf = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: anyOf,
KeyNode: anyOfLabel,
ValueNode: anyOfValue,
}
}
if len(oneOf) > 0 {
s.OneOf = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: oneOf,
KeyNode: oneOfLabel,
ValueNode: oneOfValue,
}
}
if len(allOf) > 0 {
s.AllOf = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: allOf,
KeyNode: allOfLabel,
ValueNode: allOfValue,
}
}
if len(not) > 0 {
s.Not = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: not,
KeyNode: notLabel,
ValueNode: notValue,
}
}
if len(items) > 0 {
s.Items = low.NodeReference[[]low.NodeReference[*Schema]]{
Value: items,
KeyNode: itemsLabel,
ValueNode: itemsValue,
}
}
return nil
}
@@ -246,15 +242,27 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
_, labelNode, valueNode = utils.FindKeyNodeFull(attribute, rootNode.Content)
//wg.Add(1)
if valueNode == nil {
return nil, nil
}
if valueNode != nil {
var build = func(kn *yaml.Node, vn *yaml.Node) *low.NodeReference[*Schema] {
var schema Schema
err := low.BuildModel(vn, &schema)
if err != nil {
*errors = append(*errors, err)
return nil
if h, _, _ := utils.IsNodeRefValue(vn); h {
ref := low.LocateRefNode(vn, idx)
if ref != nil {
vn = ref
} else {
*errors = append(*errors, fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column))
return nil
}
}
err = schema.BuildLevel(vn, idx, level)
_ = low.BuildModel(vn, &schema)
err := schema.BuildLevel(vn, idx, level)
if err != nil {
*errors = append(*errors, err)
return nil
@@ -267,6 +275,17 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
}
if utils.IsNodeMap(valueNode) {
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
ref := low.LocateRefNode(valueNode, idx)
if ref != nil {
valueNode = ref
} else {
*errors = append(*errors, fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column))
return
}
}
schema := build(labelNode, valueNode)
if schema != nil {
*schemas = append(*schemas, *schema)
@@ -274,6 +293,17 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
}
if utils.IsNodeArray(valueNode) {
for _, vn := range valueNode.Content {
if h, _, _ := utils.IsNodeRefValue(vn); h {
ref := low.LocateRefNode(vn, idx)
if ref != nil {
vn = ref
} else {
*errors = append(*errors, fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column))
}
}
schema := build(vn, vn)
if schema != nil {
*schemas = append(*schemas, *schema)
@@ -316,11 +346,8 @@ func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*S
if schNode != nil {
var schema Schema
err := low.BuildModel(schNode, &schema)
if err != nil {
return nil, err
}
err = schema.Build(schNode, idx)
_ = low.BuildModel(schNode, &schema)
err := schema.Build(schNode, idx)
if err != nil {
return nil, err
}

View File

@@ -2,6 +2,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
@@ -218,3 +219,691 @@ additionalProperties: true `
mv = sch.Discriminator.Value.FindMappingValue("pizza")
assert.Equal(t, "party", mv.Value)
}
func TestSchema_BuildLevel_TooDeep(t *testing.T) {
// if you design data models like this, you're doing it fucking wrong. Seriously. why, what is so complex about a model
// that it needs to be 30+ levels deep? I have seen this shit in the wild, it's unreadable, un-parsable garbage.
yml := `type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object
properties:
aValue:
type: object`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Schema
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestSchema_Build_ErrorAdditionalProps(t *testing.T) {
yml := `additionalProperties:
$ref: #borko`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Schema
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestSchema_Build_PropsLookup(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
properties:
aValue:
$ref: '#/components/schemas/Something'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
var n Schema
err := n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Description.Value)
}
func TestSchema_Build_PropsLookup_Fail(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
properties:
aValue:
$ref: '#/bork'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
var n Schema
err := n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func Test_Schema_Polymorphism_Array_Ref(t *testing.T) {
doc := `components:
schemas:
Something:
type: object
description: poly thing
properties:
polyProp:
type: string
description: a property
example: anything`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
- $ref: '#/components/schemas/Something'
oneOf:
- $ref: '#/components/schemas/Something'
anyOf:
- $ref: '#/components/schemas/Something'
not:
- $ref: '#/components/schemas/Something'
items:
- $ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.NoError(t, schErr)
desc := "poly thing"
assert.Equal(t, desc, sch.OneOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.Not.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.Items.Value[0].Value.Description.Value)
}
func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) {
doc := `components:
schemas:
Something:
type: object
description: poly thing
properties:
polyProp:
type: string
description: a property
example: anything`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
- $ref: '#/components/schemas/Missing'
oneOf:
- $ref: '#/components/schemas/Something'
anyOf:
- $ref: '#/components/schemas/Something'
not:
- $ref: '#/components/schemas/Something'
items:
- $ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr)
}
func Test_Schema_Polymorphism_Map_Ref(t *testing.T) {
doc := `components:
schemas:
Something:
type: object
description: poly thing
properties:
polyProp:
type: string
description: a property
example: anything`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
$ref: '#/components/schemas/Something'
oneOf:
$ref: '#/components/schemas/Something'
anyOf:
$ref: '#/components/schemas/Something'
not:
$ref: '#/components/schemas/Something'
items:
$ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.NoError(t, schErr)
desc := "poly thing"
assert.Equal(t, desc, sch.OneOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.Not.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.Items.Value[0].Value.Description.Value)
}
func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) {
doc := `components:
schemas:
Something:
type: object
description: poly thing
properties:
polyProp:
type: string
description: a property
example: anything`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
$ref: '#/components/schemas/Missing'
oneOf:
$ref: '#/components/schemas/Something'
anyOf:
$ref: '#/components/schemas/Something'
not:
$ref: '#/components/schemas/Something'
items:
$ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr)
}
func Test_Schema_Polymorphism_BorkParent(t *testing.T) {
doc := `components:
schemas:
Something:
$ref: #borko`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
$ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr)
}
func Test_Schema_Polymorphism_BorkChild(t *testing.T) {
doc := `components:
schemas:
Something:
$ref: #borko`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
$ref: #borko`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr)
}
func Test_Schema_Polymorphism_RefMadness(t *testing.T) {
doc := `components:
schemas:
Something:
$ref: '#/components/schemas/Else'
Else:
description: madness`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
$ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.NoError(t, schErr)
desc := "madness"
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Description.Value)
}
func Test_Schema_Polymorphism_RefMadnessBork(t *testing.T) {
doc := `components:
schemas:
Something:
$ref: '#/components/schemas/Else'
Else:
$ref: #borko`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `type: object
allOf:
$ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr)
}
func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) {
// this does not work, but it won't error out.
doc := `components:
schemas:
Something:
$ref: '#/components/schemas/Else'
Else:
description: hey!`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `$ref: '#/components/schemas/Something'`
var sch Schema
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
err := low.BuildModel(&idxNode, &sch)
assert.NoError(t, err)
schErr := sch.Build(idxNode.Content[0], idx)
assert.NoError(t, schErr)
}
func TestExtractSchema(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `schema:
type: object
properties:
aValue:
$ref: '#/components/schemas/Something'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.NotNil(t, res.Value)
assert.Equal(t, "this is something", res.Value.FindProperty("aValue").Value.Description.Value)
}
func TestExtractSchema_Ref(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `schema:
$ref: '#/components/schemas/Something'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.NotNil(t, res.Value)
assert.Equal(t, "this is something", res.Value.Description.Value)
}
func TestExtractSchema_Ref_Fail(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `schema:
$ref: '#/components/schemas/Missing'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
_, err := ExtractSchema(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestExtractSchema_RefRoot(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `$ref: '#/components/schemas/Something'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.NotNil(t, res.Value)
assert.Equal(t, "this is something", res.Value.Description.Value)
}
func TestExtractSchema_RefRoot_Fail(t *testing.T) {
doc := `components:
schemas:
Something:
description: this is something
type: string`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `$ref: '#/components/schemas/Missing'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
_, err := ExtractSchema(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) {
doc := `components:
schemas:
Something:
$ref: #bork`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `$ref: '#/components/schemas/Something'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
_, err := ExtractSchema(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestExtractSchema_DoNothing(t *testing.T) {
doc := `components:
schemas:
Something:
$ref: #bork`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml := `please: do nothing.`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
res, err := ExtractSchema(idxNode.Content[0], idx)
assert.Nil(t, res)
assert.Nil(t, err)
}

View File

@@ -43,6 +43,31 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
idx.GetAllResponses,
idx.GetAllSecuritySchemes,
}
// if there are any external indexes being used by remote
// documents, then we need to search through them also.
externalIndexes := idx.GetAllExternalIndexes()
if len(externalIndexes) > 0 {
var extCollection []func() map[string]*index.Reference
for _, extIndex := range externalIndexes {
extCollection = []func() map[string]*index.Reference{
extIndex.GetAllSchemas,
extIndex.GetMappedReferences,
extIndex.GetAllExternalDocuments,
extIndex.GetAllParameters,
extIndex.GetAllHeaders,
extIndex.GetAllCallbacks,
extIndex.GetAllLinks,
extIndex.GetAllExternalDocuments,
extIndex.GetAllExamples,
extIndex.GetAllRequestBodies,
extIndex.GetAllResponses,
extIndex.GetAllSecuritySchemes,
}
collections = append(collections, extCollection...)
}
}
var found map[string]*index.Reference
for _, collection := range collections {
found = collection()
@@ -69,6 +94,16 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
}
func ExtractObjectRaw[T Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (T, error) {
if h, _, _ := utils.IsNodeRefValue(root); h {
ref := LocateRefNode(root, idx)
if ref != nil {
root = ref
} else {
return nil, fmt.Errorf("object extraction failed: reference cannot be found: %s, line %d, col %d",
root.Content[1].Value, root.Content[1].Line, root.Content[1].Column)
}
}
var n T = new(N)
err := BuildModel(root, n)
if err != nil {

View File

@@ -495,6 +495,11 @@ func (index *SpecIndex) GetAllOperationsServers() map[string]map[string][]*Refer
return index.opServersRefs
}
// GetAllExternalIndexes will return all indexes for external documents
func (index *SpecIndex) GetAllExternalIndexes() map[string]*SpecIndex {
return index.externalSpecIndex
}
func (index *SpecIndex) checkPolymorphicNode(name string) (bool, string) {
switch name {
case "anyOf":

View File

@@ -13,7 +13,11 @@ var doc *v3.Document
func init() {
data, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
doc, _ = CreateDocument(info)
var err []error
doc, err = CreateDocument(info)
if err != nil {
panic("broken something")
}
}
func BenchmarkCreateDocument(b *testing.B) {