fix: fix handling of ordered arrays when processing them async

This commit is contained in:
Tristan Cartledge
2023-01-10 10:37:20 +00:00
committed by Dave Shanley
parent f269259fcf
commit 52f9868d96
4 changed files with 228 additions and 133 deletions

View File

@@ -5,10 +5,11 @@ package base
import ( import (
"fmt" "fmt"
"sync"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"sync"
) )
// DynamicValue is used to hold multiple possible values for a schema property. There are two values, a left // DynamicValue is used to hold multiple possible values for a schema property. There are two values, a left
@@ -41,11 +42,10 @@ func (s *DynamicValue[A, B]) IsB() bool {
// mix, which has been confusing. So, instead of building a bunch of different models, we have compressed // mix, which has been confusing. So, instead of building a bunch of different models, we have compressed
// all variations into a single model that makes it easy to support multiple spec types. // all variations into a single model that makes it easy to support multiple spec types.
// //
// - v2 schema: https://swagger.io/specification/v2/#schemaObject // - v2 schema: https://swagger.io/specification/v2/#schemaObject
// - v3 schema: https://swagger.io/specification/#schema-object // - v3 schema: https://swagger.io/specification/#schema-object
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object // - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
type Schema struct { type Schema struct {
// 3.1 only, used to define a dialect for this schema, label is '$schema'. // 3.1 only, used to define a dialect for this schema, label is '$schema'.
SchemaTypeRef string SchemaTypeRef string
@@ -309,29 +309,40 @@ func NewSchema(schema *base.Schema) *Schema {
propsChan := make(chan bool) propsChan := make(chan bool)
errChan := make(chan error) errChan := make(chan error)
type buildResult struct {
idx int
s *SchemaProxy
}
// for every item, build schema async // for every item, build schema async
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], bChan chan *SchemaProxy) { buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: sch.ValueNode, ValueNode: sch.ValueNode,
Value: sch.Value, Value: sch.Value,
}} }}
bChan <- p
fmt.Println("building schema", idx, sch)
bChan <- buildResult{idx: idx, s: p}
} }
// schema async // schema async
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy, buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
doneChan chan bool, e chan error) { doneChan chan bool, e chan error,
bChan := make(chan *SchemaProxy) ) {
bChan := make(chan buildResult)
totalSchemas := len(schemas) totalSchemas := len(schemas)
for v := range schemas { for i := range schemas {
go buildSchema(schemas[v], bChan) fmt.Println("start build schema", i, schemas[i])
go buildSchema(schemas[i], i, bChan)
} }
j := 0 j := 0
for j < totalSchemas { for j < totalSchemas {
select { select {
case t := <-bChan: case r := <-bChan:
j++ j++
*items = append(*items, t) fmt.Println("got schema", r.idx, "of", totalSchemas)
(*items)[r.idx] = r.s
} }
} }
doneChan <- true doneChan <- true
@@ -339,8 +350,9 @@ func NewSchema(schema *base.Schema) *Schema {
// props async // props async
var plock sync.Mutex var plock sync.Mutex
var buildProps = func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool, buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
props map[string]*SchemaProxy, sw int) { props map[string]*SchemaProxy, sw int,
) {
plock.Lock() plock.Lock()
props[k.Value] = &SchemaProxy{ props[k.Value] = &SchemaProxy{
schema: &lowmodel.NodeReference[*base.SchemaProxy]{ schema: &lowmodel.NodeReference[*base.SchemaProxy]{
@@ -386,14 +398,17 @@ func NewSchema(schema *base.Schema) *Schema {
children := 0 children := 0
if !schema.AllOf.IsEmpty() { if !schema.AllOf.IsEmpty() {
children++ children++
allOf = make([]*SchemaProxy, len(schema.AllOf.Value))
go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan) go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
} }
if !schema.AnyOf.IsEmpty() { if !schema.AnyOf.IsEmpty() {
children++ children++
anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value))
go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan) go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
} }
if !schema.OneOf.IsEmpty() { if !schema.OneOf.IsEmpty() {
children++ children++
oneOf = make([]*SchemaProxy, len(schema.OneOf.Value))
go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan) go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan)
} }
if !schema.Not.IsEmpty() { if !schema.Not.IsEmpty() {
@@ -412,6 +427,7 @@ func NewSchema(schema *base.Schema) *Schema {
} }
if !schema.PrefixItems.IsEmpty() { if !schema.PrefixItems.IsEmpty() {
children++ children++
prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value))
go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan) go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan)
} }

View File

@@ -10,6 +10,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base" lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -21,7 +22,6 @@ func TestDynamicValue_IsA(t *testing.T) {
} }
func TestNewSchemaProxy(t *testing.T) { func TestNewSchemaProxy(t *testing.T) {
// check proxy // check proxy
yml := `components: yml := `components:
schemas: schemas:
@@ -63,11 +63,9 @@ func TestNewSchemaProxy(t *testing.T) {
g, o := sch1.BuildSchema() g, o := sch1.BuildSchema()
assert.Nil(t, g) assert.Nil(t, g)
assert.Error(t, o) assert.Error(t, o)
} }
func TestNewSchemaProxy_WithObject(t *testing.T) { func TestNewSchemaProxy_WithObject(t *testing.T) {
testSpec := `type: object testSpec := `type: object
description: something object description: something object
discriminator: discriminator:
@@ -254,14 +252,64 @@ unevaluatedProperties:
assert.Equal(t, "string", compiled.PropertyNames.Schema().Type[0]) assert.Equal(t, "string", compiled.PropertyNames.Schema().Type[0])
assert.Equal(t, "boolean", compiled.UnevaluatedItems.Schema().Type[0]) assert.Equal(t, "boolean", compiled.UnevaluatedItems.Schema().Type[0])
assert.Equal(t, "integer", compiled.UnevaluatedProperties.Schema().Type[0]) assert.Equal(t, "integer", compiled.UnevaluatedProperties.Schema().Type[0])
assert.NotNil(t, compiled.Nullable)
assert.True(t, *compiled.Nullable)
wentLow := compiled.GoLow() wentLow := compiled.GoLow()
assert.Equal(t, 114, wentLow.AdditionalProperties.ValueNode.Line) assert.Equal(t, 114, wentLow.AdditionalProperties.ValueNode.Line)
}
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {
testSpec := test_get_allOf_schema_blob()
var compNode yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
// test data is a map with one node
mapContent := compNode.Content[0].Content
_, vn := utils.FindKeyNodeTop(lowbase.AllOfLabel, mapContent)
assert.True(t, utils.IsNodeArray(vn))
want := []string{}
// Go over every element in AllOf and grab description
// Odd: object
// Event: description
for i := range vn.Content {
assert.True(t, utils.IsNodeMap(vn.Content[i]))
_, vn := utils.FindKeyNodeTop("description", vn.Content[i].Content)
assert.True(t, utils.IsNodeStringValue(vn))
want = append(want, vn.Value)
}
sp := new(lowbase.SchemaProxy)
err := sp.Build(compNode.Content[0], nil)
assert.NoError(t, err)
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
Value: sp,
ValueNode: compNode.Content[0],
}
schemaProxy := NewSchemaProxy(&lowproxy)
compiled := schemaProxy.Schema()
assert.Equal(t, schemaProxy, compiled.ParentProxy)
assert.NotNil(t, compiled)
assert.Nil(t, schemaProxy.GetBuildError())
got := []string{}
for i := range compiled.AllOf {
v := compiled.AllOf[i]
got = append(got, v.Schema().Description)
}
assert.Equal(t, want, got)
} }
func TestNewSchemaProxy_WithObject_FinishPoly(t *testing.T) { func TestNewSchemaProxy_WithObject_FinishPoly(t *testing.T) {
testSpec := `type: object testSpec := `type: object
description: something object description: something object
discriminator: discriminator:
@@ -400,11 +448,9 @@ required: [cake, fish]`
wentLower := compiled.XML.GoLow() wentLower := compiled.XML.GoLow()
assert.Equal(t, 102, wentLower.Name.ValueNode.Line) assert.Equal(t, 102, wentLower.Name.ValueNode.Line)
} }
func TestSchemaProxy_GoLow(t *testing.T) { func TestSchemaProxy_GoLow(t *testing.T) {
const ymlComponents = `components: const ymlComponents = `components:
schemas: schemas:
rice: rice:
@@ -471,7 +517,6 @@ type: number
assert.Nil(t, highSchema.ExclusiveMinimum) assert.Nil(t, highSchema.ExclusiveMinimum)
assert.Nil(t, highSchema.Maximum) assert.Nil(t, highSchema.Maximum)
assert.Nil(t, highSchema.ExclusiveMaximum) assert.Nil(t, highSchema.ExclusiveMaximum)
} }
func TestSchemaNumberMultipleOf(t *testing.T) { func TestSchemaNumberMultipleOf(t *testing.T) {
@@ -574,7 +619,6 @@ examples:
} }
func ExampleNewSchema() { func ExampleNewSchema() {
// create an example schema object // create an example schema object
// this can be either JSON or YAML. // this can be either JSON or YAML.
yml := ` yml := `
@@ -601,11 +645,9 @@ properties:
// print out the description of 'aProperty' // print out the description of 'aProperty'
fmt.Print(highSchema.Properties["aProperty"].Schema().Description) fmt.Print(highSchema.Properties["aProperty"].Schema().Description)
// Output: this is an integer property // Output: this is an integer property
} }
func ExampleNewSchemaProxy() { func ExampleNewSchemaProxy() {
// create an example schema object // create an example schema object
// this can be either JSON or YAML. // this can be either JSON or YAML.
yml := ` yml := `
@@ -634,5 +676,26 @@ properties:
// print out the description of 'aProperty' // print out the description of 'aProperty'
fmt.Print(highSchema.Schema().Properties["aProperty"].Schema().Description) fmt.Print(highSchema.Schema().Properties["aProperty"].Schema().Description)
// Output: this is an integer property // Output: this is an integer property
}
func test_get_allOf_schema_blob() string {
return `type: object
description: allOf sequence check
allOf:
- type: object
description: allOf sequence check 1
- description: allOf sequence check 2
- type: object
description: allOf sequence check 3
- description: allOf sequence check 4
properties:
somethingBee:
type: number
somethingThree:
type: number
somethingTwo:
type: number
somethingOne:
type: number
`
} }

View File

@@ -3,13 +3,14 @@ package base
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"sort"
"strconv"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strconv"
"strings"
) )
// SchemaDynamicValue is used to hold multiple possible values for a schema property. There are two values, a left // SchemaDynamicValue is used to hold multiple possible values for a schema property. There are two values, a left
@@ -42,11 +43,10 @@ func (s SchemaDynamicValue[A, B]) IsB() bool {
// mix, which has been confusing. So, instead of building a bunch of different models, we have compressed // mix, which has been confusing. So, instead of building a bunch of different models, we have compressed
// all variations into a single model that makes it easy to support multiple spec types. // all variations into a single model that makes it easy to support multiple spec types.
// //
// - v2 schema: https://swagger.io/specification/v2/#schemaObject // - v2 schema: https://swagger.io/specification/v2/#schemaObject
// - v3 schema: https://swagger.io/specification/#schema-object // - v3 schema: https://swagger.io/specification/#schema-object
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object // - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
type Schema struct { 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]
@@ -445,27 +445,27 @@ func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference
// Build will perform a number of operations. // Build will perform a number of operations.
// Extraction of the following happens in this method: // Extraction of the following happens in this method:
// - Extensions // - Extensions
// - Type // - Type
// - ExclusiveMinimum and ExclusiveMaximum // - ExclusiveMinimum and ExclusiveMaximum
// - Examples // - Examples
// - AdditionalProperties // - AdditionalProperties
// - Discriminator // - Discriminator
// - ExternalDocs // - ExternalDocs
// - XML // - XML
// - Properties // - Properties
// - AllOf, OneOf, AnyOf // - AllOf, OneOf, AnyOf
// - Not // - Not
// - Items // - Items
// - PrefixItems // - PrefixItems
// - If // - If
// - Else // - Else
// - Then // - Then
// - DependentSchemas // - DependentSchemas
// - PatternProperties // - PatternProperties
// - PropertyNames // - PropertyNames
// - UnevaluatedItems // - UnevaluatedItems
// - UnevaluatedProperties // - UnevaluatedProperties
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
if h, _, _ := utils.IsNodeRefValue(root); h { if h, _, _ := utils.IsNodeRefValue(root); h {
ref, err := low.LocateRefNode(root, idx) ref, err := low.LocateRefNode(root, idx)
@@ -557,7 +557,8 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
_, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content) _, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFullTop(SchemaTypeLabel, root.Content)
if schemaRefNode != nil { if schemaRefNode != nil {
s.SchemaTypeRef = low.NodeReference[string]{ s.SchemaTypeRef = low.NodeReference[string]{
Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefLabel} Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefLabel,
}
} }
// handle example if set. (3.0) // handle example if set. (3.0)
@@ -914,12 +915,12 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]], error) { func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]], error) {
// for property, build in a new thread! // for property, build in a new thread!
bChan := make(chan schemaProxyBuildResult) bChan := make(chan schemaProxyBuildResult)
var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool, buildProperty := func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool,
refString string) { refString string,
) {
c <- schemaProxyBuildResult{ c <- schemaProxyBuildResult{
k: low.KeyReference[string]{ k: low.KeyReference[string]{
KeyNode: label, KeyNode: label,
@@ -1001,13 +1002,18 @@ func (s *Schema) extractExtensions(root *yaml.Node) {
// build out a child schema for parent schema. // build out a child schema for parent schema.
func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) { func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) {
if valueNode != nil { if valueNode != nil {
syncChan := make(chan *low.ValueReference[*SchemaProxy]) type buildResult struct {
res *low.ValueReference[*SchemaProxy]
idx int
}
syncChan := make(chan buildResult)
// build out a SchemaProxy for every sub-schema. // build out a SchemaProxy for every sub-schema.
build := func(kn *yaml.Node, vn *yaml.Node, c chan *low.ValueReference[*SchemaProxy], build := func(kn *yaml.Node, vn *yaml.Node, schemaIdx int, c chan buildResult,
isRef bool, refLocation string) { isRef bool, refLocation string,
) {
// a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can // a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can
// take on circular references through polymorphism. Like the resolver, if we try and follow these // take on circular references through polymorphism. Like the resolver, if we try and follow these
// journey's through hyperspace, we will end up creating endless amounts of threads, spinning off // journey's through hyperspace, we will end up creating endless amounts of threads, spinning off
@@ -1026,7 +1032,10 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
Value: sp, Value: sp,
ValueNode: vn, ValueNode: vn,
} }
c <- res c <- buildResult{
res: res,
idx: schemaIdx,
}
} }
isRef := false isRef := false
@@ -1046,7 +1055,7 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
// this only runs once, however to keep things consistent, it makes sense to use the same async method // this only runs once, however to keep things consistent, it makes sense to use the same async method
// that arrays will use. // that arrays will use.
go build(labelNode, valueNode, syncChan, isRef, refLocation) go build(labelNode, valueNode, -1, syncChan, isRef, refLocation)
select { select {
case r := <-syncChan: case r := <-syncChan:
schemas <- schemaProxyBuildResult{ schemas <- schemaProxyBuildResult{
@@ -1054,13 +1063,15 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
KeyNode: labelNode, KeyNode: labelNode,
Value: labelNode.Value, Value: labelNode.Value,
}, },
v: *r, v: *r.res,
} }
} }
} }
if utils.IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
refBuilds := 0 refBuilds := 0
for _, vn := range valueNode.Content { results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content))
for i, vn := range valueNode.Content {
isRef = false isRef = false
h := false h := false
if h, _, refLocation = utils.IsNodeRefValue(vn); h { if h, _, refLocation = utils.IsNodeRefValue(vn); h {
@@ -1076,20 +1087,25 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
} }
} }
refBuilds++ refBuilds++
go build(vn, vn, syncChan, isRef, refLocation) go build(vn, vn, i, syncChan, isRef, refLocation)
} }
completedBuilds := 0 completedBuilds := 0
for completedBuilds < refBuilds { for completedBuilds < refBuilds {
select { select {
case res := <-syncChan: case res := <-syncChan:
completedBuilds++ completedBuilds++
schemas <- schemaProxyBuildResult{ results[res.idx] = res.res
k: low.KeyReference[string]{ }
KeyNode: labelNode, }
Value: labelNode.Value,
}, for _, r := range results {
v: *res, schemas <- schemaProxyBuildResult{
} k: low.KeyReference[string]{
KeyNode: labelNode,
Value: labelNode.Value,
},
v: *r,
} }
} }
} }

View File

@@ -1,12 +1,14 @@
package base package base
import ( import (
"testing"
"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/resolver" "github.com/pb33f/libopenapi/resolver"
"github.com/pb33f/libopenapi/utils"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"testing"
) )
func test_get_schema_blob() string { func test_get_schema_blob() string {
@@ -306,12 +308,52 @@ func Test_Schema(t *testing.T) {
assert.Equal(t, "string", sch.PropertyNames.Value.Schema().Type.Value.A) assert.Equal(t, "string", sch.PropertyNames.Value.Schema().Type.Value.A)
assert.Equal(t, "boolean", sch.UnevaluatedItems.Value.Schema().Type.Value.A) assert.Equal(t, "boolean", sch.UnevaluatedItems.Value.Schema().Type.Value.A)
assert.Equal(t, "integer", sch.UnevaluatedProperties.Value.Schema().Type.Value.A) assert.Equal(t, "integer", sch.UnevaluatedProperties.Value.Schema().Type.Value.A)
}
func TestSchemaAllOfSequenceOrder(t *testing.T) {
testSpec := test_get_allOf_schema_blob()
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(testSpec), &rootNode)
assert.NoError(t, mErr)
// test data is a map with one node
mapContent := rootNode.Content[0].Content
_, vn := utils.FindKeyNodeTop(AllOfLabel, mapContent)
assert.True(t, utils.IsNodeArray(vn))
want := []string{}
// Go over every element in AllOf and grab description
// Odd: object
// Event: description
for i := range vn.Content {
assert.True(t, utils.IsNodeMap(vn.Content[i]))
_, vn := utils.FindKeyNodeTop("description", vn.Content[i].Content)
assert.True(t, utils.IsNodeStringValue(vn))
want = append(want, vn.Value)
}
sch := Schema{}
mbErr := low.BuildModel(rootNode.Content[0], &sch)
assert.NoError(t, mbErr)
schErr := sch.Build(rootNode.Content[0], nil)
assert.NoError(t, schErr)
assert.Equal(t, "allOf sequence check", sch.Description.Value)
got := []string{}
for i := range sch.AllOf.Value {
v := sch.AllOf.Value[i]
got = append(got, v.Value.Schema().Description.Value)
}
assert.Equal(t, want, got)
} }
func TestSchema_Hash(t *testing.T) { func TestSchema_Hash(t *testing.T) {
// create two versions
//create two versions
testSpec := test_get_schema_blob() testSpec := test_get_schema_blob()
var sc1n yaml.Node var sc1n yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &sc1n) _ = yaml.Unmarshal([]byte(testSpec), &sc1n)
@@ -326,12 +368,10 @@ func TestSchema_Hash(t *testing.T) {
_ = sch2.Build(sc2n.Content[0], nil) _ = sch2.Build(sc2n.Content[0], nil)
assert.Equal(t, sch1.Hash(), sch2.Hash()) assert.Equal(t, sch1.Hash(), sch2.Hash())
} }
func BenchmarkSchema_Hash(b *testing.B) { func BenchmarkSchema_Hash(b *testing.B) {
// create two versions
//create two versions
testSpec := test_get_schema_blob() testSpec := test_get_schema_blob()
var sc1n yaml.Node var sc1n yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &sc1n) _ = yaml.Unmarshal([]byte(testSpec), &sc1n)
@@ -348,7 +388,6 @@ func BenchmarkSchema_Hash(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
assert.Equal(b, sch1.Hash(), sch2.Hash()) assert.Equal(b, sch1.Hash(), sch2.Hash())
} }
} }
func Test_Schema_31(t *testing.T) { func Test_Schema_31(t *testing.T) {
@@ -390,7 +429,6 @@ examples:
assert.Equal(t, "fish/paste", sch.ContentMediaType.Value) assert.Equal(t, "fish/paste", sch.ContentMediaType.Value)
assert.True(t, sch.Items.Value.IsB()) assert.True(t, sch.Items.Value.IsB())
assert.True(t, sch.Items.Value.B) assert.True(t, sch.Items.Value.B)
} }
//func TestSchema_BuildLevel_TooDeep(t *testing.T) { //func TestSchema_BuildLevel_TooDeep(t *testing.T) {
@@ -512,7 +550,6 @@ examples:
//} //}
func TestSchema_Build_PropsLookup(t *testing.T) { func TestSchema_Build_PropsLookup(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -536,11 +573,9 @@ properties:
err := n.Build(idxNode.Content[0], idx) err := n.Build(idxNode.Content[0], idx)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Schema().Description.Value) assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Schema().Description.Value)
} }
func TestSchema_Build_PropsLookup_Fail(t *testing.T) { func TestSchema_Build_PropsLookup_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -563,11 +598,9 @@ properties:
var n Schema var n Schema
err := n.Build(idxNode.Content[0], idx) err := n.Build(idxNode.Content[0], idx)
assert.Error(t, err) assert.Error(t, err)
} }
func TestSchema_Build_DependentSchemas_Fail(t *testing.T) { func TestSchema_Build_DependentSchemas_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -590,11 +623,9 @@ dependentSchemas:
var n Schema var n Schema
err := n.Build(idxNode.Content[0], idx) err := n.Build(idxNode.Content[0], idx)
assert.Error(t, err) assert.Error(t, err)
} }
func TestSchema_Build_PatternProperties_Fail(t *testing.T) { func TestSchema_Build_PatternProperties_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -617,11 +648,9 @@ patternProperties:
var n Schema var n Schema
err := n.Build(idxNode.Content[0], idx) err := n.Build(idxNode.Content[0], idx)
assert.Error(t, err) assert.Error(t, err)
} }
func Test_Schema_Polymorphism_Array_Ref(t *testing.T) { func Test_Schema_Polymorphism_Array_Ref(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -669,7 +698,6 @@ items:
} }
func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) { func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -707,11 +735,9 @@ items:
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr) assert.Error(t, schErr)
} }
func Test_Schema_Polymorphism_Map_Ref(t *testing.T) { func Test_Schema_Polymorphism_Map_Ref(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -759,7 +785,6 @@ items:
} }
func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) { func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -797,11 +822,9 @@ items:
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr) assert.Error(t, schErr)
} }
func Test_Schema_Polymorphism_BorkParent(t *testing.T) { func Test_Schema_Polymorphism_BorkParent(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -825,11 +848,9 @@ allOf:
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr) assert.Error(t, schErr)
} }
func Test_Schema_Polymorphism_BorkChild(t *testing.T) { func Test_Schema_Polymorphism_BorkChild(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -853,11 +874,9 @@ allOf:
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr) assert.Error(t, schErr)
} }
func Test_Schema_Polymorphism_BorkChild_Array(t *testing.T) { func Test_Schema_Polymorphism_BorkChild_Array(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -885,11 +904,9 @@ allOf:
assert.NoError(t, schErr) assert.NoError(t, schErr)
assert.Nil(t, sch.AllOf.Value[0].Value.Schema()) // child can't be resolved, so this will be nil. assert.Nil(t, sch.AllOf.Value[0].Value.Schema()) // child can't be resolved, so this will be nil.
assert.Error(t, sch.AllOf.Value[0].Value.GetBuildError()) assert.Error(t, sch.AllOf.Value[0].Value.GetBuildError())
} }
func Test_Schema_Polymorphism_RefMadness(t *testing.T) { func Test_Schema_Polymorphism_RefMadness(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -918,11 +935,9 @@ allOf:
desc := "madness" desc := "madness"
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value) assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value)
} }
func Test_Schema_Polymorphism_RefMadnessBork(t *testing.T) { func Test_Schema_Polymorphism_RefMadnessBork(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -948,11 +963,9 @@ allOf:
err = sch.Build(idxNode.Content[0], idx) err = sch.Build(idxNode.Content[0], idx)
assert.Error(t, err) assert.Error(t, err)
} }
func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) { func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) {
// this does not work, but it won't error out. // this does not work, but it won't error out.
yml := `components: yml := `components:
@@ -978,11 +991,9 @@ func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) {
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.NoError(t, schErr) assert.NoError(t, schErr)
} }
func Test_Schema_RefMadnessIllegal_Circular(t *testing.T) { func Test_Schema_RefMadnessIllegal_Circular(t *testing.T) {
// this does not work, but it won't error out. // this does not work, but it won't error out.
yml := `components: yml := `components:
@@ -1012,11 +1023,9 @@ func Test_Schema_RefMadnessIllegal_Circular(t *testing.T) {
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr) assert.Error(t, schErr)
} }
func Test_Schema_RefMadnessIllegal_Nonexist(t *testing.T) { func Test_Schema_RefMadnessIllegal_Nonexist(t *testing.T) {
// this does not work, but it won't error out. // this does not work, but it won't error out.
yml := `components: yml := `components:
@@ -1046,11 +1055,9 @@ func Test_Schema_RefMadnessIllegal_Nonexist(t *testing.T) {
schErr := sch.Build(idxNode.Content[0], idx) schErr := sch.Build(idxNode.Content[0], idx)
assert.Error(t, schErr) assert.Error(t, schErr)
} }
func TestExtractSchema(t *testing.T) { func TestExtractSchema(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1079,7 +1086,6 @@ func TestExtractSchema(t *testing.T) {
} }
func TestExtractSchema_DefaultPrimitive(t *testing.T) { func TestExtractSchema_DefaultPrimitive(t *testing.T) {
yml := ` yml := `
schema: schema:
type: object type: object
@@ -1096,7 +1102,6 @@ schema:
} }
func TestExtractSchema_Ref(t *testing.T) { func TestExtractSchema_Ref(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1121,7 +1126,6 @@ func TestExtractSchema_Ref(t *testing.T) {
} }
func TestExtractSchema_Ref_Fail(t *testing.T) { func TestExtractSchema_Ref_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1144,7 +1148,6 @@ func TestExtractSchema_Ref_Fail(t *testing.T) {
} }
func TestExtractSchema_CheckChildPropCircular(t *testing.T) { func TestExtractSchema_CheckChildPropCircular(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1176,11 +1179,9 @@ func TestExtractSchema_CheckChildPropCircular(t *testing.T) {
props := res.Value.Schema().FindProperty("nothing") props := res.Value.Schema().FindProperty("nothing")
assert.NotNil(t, props) assert.NotNil(t, props)
} }
func TestExtractSchema_RefRoot(t *testing.T) { func TestExtractSchema_RefRoot(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1204,7 +1205,6 @@ func TestExtractSchema_RefRoot(t *testing.T) {
} }
func TestExtractSchema_RefRoot_Fail(t *testing.T) { func TestExtractSchema_RefRoot_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1223,11 +1223,9 @@ func TestExtractSchema_RefRoot_Fail(t *testing.T) {
_, err := ExtractSchema(idxNode.Content[0], idx) _, err := ExtractSchema(idxNode.Content[0], idx)
assert.Error(t, err) assert.Error(t, err)
} }
func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) { func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1248,7 +1246,6 @@ func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) {
} }
func TestExtractSchema_DoNothing(t *testing.T) { func TestExtractSchema_DoNothing(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Something: Something:
@@ -1267,11 +1264,9 @@ func TestExtractSchema_DoNothing(t *testing.T) {
res, err := ExtractSchema(idxNode.Content[0], idx) res, err := ExtractSchema(idxNode.Content[0], idx)
assert.Nil(t, res) assert.Nil(t, res)
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) { func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Nothing: Nothing:
@@ -1297,11 +1292,9 @@ func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) {
res, err := ExtractSchema(idxNode.Content[0], idx) res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema())
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestExtractSchema_OneOfRef(t *testing.T) { func TestExtractSchema_OneOfRef(t *testing.T) {
yml := `components: yml := `components:
schemas: schemas:
Error: Error:
@@ -1414,11 +1407,9 @@ func TestExtractSchema_OneOfRef(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "a frosty cold beverage can be coke or sprite", assert.Equal(t, "a frosty cold beverage can be coke or sprite",
res.Value.Schema().OneOf.Value[0].Value.Schema().Description.Value) res.Value.Schema().OneOf.Value[0].Value.Schema().Description.Value)
} }
func TestSchema_Hash_Equal(t *testing.T) { func TestSchema_Hash_Equal(t *testing.T) {
left := `schema: left := `schema:
$schema: https://athing.com $schema: https://athing.com
multipleOf: 1 multipleOf: 1
@@ -1515,11 +1506,9 @@ func TestSchema_Hash_Equal(t *testing.T) {
rHash := rDoc.Value.Schema().Hash() rHash := rDoc.Value.Schema().Hash()
assert.Equal(t, lHash, rHash) assert.Equal(t, lHash, rHash)
} }
func TestSchema_Hash_NotEqual(t *testing.T) { func TestSchema_Hash_NotEqual(t *testing.T) {
left := `schema: left := `schema:
title: an OK message - but different title: an OK message - but different
items: true items: true
@@ -1551,7 +1540,6 @@ func TestSchema_Hash_NotEqual(t *testing.T) {
} }
func TestSchema_Hash_EqualJumbled(t *testing.T) { func TestSchema_Hash_EqualJumbled(t *testing.T) {
left := `schema: left := `schema:
title: an OK message title: an OK message
description: a nice thing. description: a nice thing.
@@ -1585,5 +1573,17 @@ func TestSchema_Hash_EqualJumbled(t *testing.T) {
lDoc, _ := ExtractSchema(lNode.Content[0], nil) lDoc, _ := ExtractSchema(lNode.Content[0], nil)
rDoc, _ := ExtractSchema(rNode.Content[0], nil) rDoc, _ := ExtractSchema(rNode.Content[0], nil)
assert.True(t, low.AreEqual(lDoc.Value.Schema(), rDoc.Value.Schema())) assert.True(t, low.AreEqual(lDoc.Value.Schema(), rDoc.Value.Schema()))
}
func test_get_allOf_schema_blob() string {
return `type: object
description: allOf sequence check
allOf:
- type: object
description: allOf sequence check 1
- description: allOf sequence check 2
- type: object
description: allOf sequence check 3
- description: allOf sequence check 4
`
} }