Added contains, minContains and maxContains to schema #28

Added support for missing 3.1 schema properties, however it does not cover the `boolean` case
This commit is contained in:
Dave Shanley
2022-12-03 14:08:14 -05:00
parent 4fcf45b813
commit d51d2fcd27
6 changed files with 79 additions and 5 deletions

View File

@@ -55,9 +55,14 @@ type Schema struct {
// in 3.1 prefixItems provides tuple validation support. // in 3.1 prefixItems provides tuple validation support.
PrefixItems []*SchemaProxy PrefixItems []*SchemaProxy
// In 3.1 contains is used by arrays to define a single schema
Contains *SchemaProxy
MinContains *int64
MaxContains *int64
// Compatible with all versions // Compatible with all versions
Not []*SchemaProxy Not []*SchemaProxy
Items []*SchemaProxy Items *SchemaProxy
Properties map[string]*SchemaProxy Properties map[string]*SchemaProxy
Title string Title string
MultipleOf *int64 MultipleOf *int64
@@ -139,6 +144,19 @@ func NewSchema(schema *base.Schema) *Schema {
if !schema.MinProperties.IsEmpty() { if !schema.MinProperties.IsEmpty() {
s.MinProperties = &schema.MinProperties.Value s.MinProperties = &schema.MinProperties.Value
} }
if !schema.MaxContains.IsEmpty() {
s.MaxContains = &schema.MaxContains.Value
}
if !schema.MinContains.IsEmpty() {
s.MinContains = &schema.MinContains.Value
}
if !schema.Contains.IsEmpty() {
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Contains.ValueNode,
Value: schema.Contains.Value,
}}
}
s.Pattern = schema.Pattern.Value s.Pattern = schema.Pattern.Value
s.Format = schema.Format.Value s.Format = schema.Format.Value
@@ -197,6 +215,8 @@ func NewSchema(schema *base.Schema) *Schema {
} }
s.Enum = enum s.Enum = enum
// build out
// async work. // async work.
// any polymorphic properties need to be handled in their own threads // any polymorphic properties need to be handled in their own threads
// any properties each need to be processed in their own thread. // any properties each need to be processed in their own thread.
@@ -312,7 +332,9 @@ func NewSchema(schema *base.Schema) *Schema {
s.OneOf = oneOf s.OneOf = oneOf
s.AnyOf = anyOf s.AnyOf = anyOf
s.AllOf = allOf s.AllOf = allOf
s.Items = items if len(items) > 0 {
s.Items = items[0]
}
s.PrefixItems = prefixItems s.PrefixItems = prefixItems
s.Not = not s.Not = not
return s return s

View File

@@ -191,7 +191,11 @@ minProperties: 1
nullable: true nullable: true
readOnly: true readOnly: true
writeOnly: false writeOnly: false
deprecated: true` deprecated: true
contains:
type: int
minContains: 1
maxContains: 10`
var compNode yaml.Node var compNode yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &compNode) _ = yaml.Unmarshal([]byte(testSpec), &compNode)
@@ -213,6 +217,11 @@ deprecated: true`
assert.NotNil(t, compiled) assert.NotNil(t, compiled)
assert.Nil(t, schemaProxy.GetBuildError()) assert.Nil(t, schemaProxy.GetBuildError())
// check contains
assert.Equal(t, "int", compiled.Contains.Schema().Type[0])
assert.Equal(t, int64(10), *compiled.MaxContains)
assert.Equal(t, int64(1), *compiled.MinContains)
wentLow := compiled.GoLow() wentLow := compiled.GoLow()
assert.Equal(t, 114, wentLow.AdditionalProperties.ValueNode.Line) assert.Equal(t, 114, wentLow.AdditionalProperties.ValueNode.Line)

View File

@@ -203,7 +203,7 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
assert.Equal(t, 445, b.Schema().GoLow().FindProperty("name").ValueNode.Line) assert.Equal(t, 445, b.Schema().GoLow().FindProperty("name").ValueNode.Line)
f := h.Components.Schemas["Fries"] f := h.Components.Schemas["Fries"]
assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items[0].Schema().Example) assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items.Schema().Example)
assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 2) assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 2)
d := h.Components.Schemas["Drink"] d := h.Components.Schemas["Drink"]

View File

@@ -18,6 +18,7 @@ const (
XMLLabel = "xml" XMLLabel = "xml"
ItemsLabel = "items" ItemsLabel = "items"
PrefixItemsLabel = "prefixItems" PrefixItemsLabel = "prefixItems"
ContainsLabel = "contains"
AllOfLabel = "allOf" AllOfLabel = "allOf"
AnyOfLabel = "anyOf" AnyOfLabel = "anyOf"
OneOfLabel = "oneOf" OneOfLabel = "oneOf"

View File

@@ -72,6 +72,10 @@ type Schema struct {
Examples low.NodeReference[[]low.ValueReference[any]] Examples low.NodeReference[[]low.ValueReference[any]]
// in 3.1 PrefixItems provides tuple validation using prefixItems. // in 3.1 PrefixItems provides tuple validation using prefixItems.
PrefixItems low.NodeReference[[]low.ValueReference[*SchemaProxy]] PrefixItems low.NodeReference[[]low.ValueReference[*SchemaProxy]]
// in 3.1 Contains is used by arrays and points to a Schema.
Contains low.NodeReference[*SchemaProxy]
MinContains low.NodeReference[int64]
MaxContains low.NodeReference[int64]
// Compatible with all versions // Compatible with all versions
Title low.NodeReference[string] Title low.NodeReference[string]
@@ -367,6 +371,17 @@ func (s *Schema) Hash() [32]byte {
if s.Example.Value != nil { if s.Example.Value != nil {
d = append(d, low.GenerateHashString(s.Example.Value)) d = append(d, low.GenerateHashString(s.Example.Value))
} }
// contains
if !s.Contains.IsEmpty() {
d = append(d, low.GenerateHashString(s.Contains.Value))
}
if !s.MinContains.IsEmpty() {
d = append(d, fmt.Sprint(s.MinContains.Value))
}
if !s.MaxContains.IsEmpty() {
d = append(d, fmt.Sprint(s.MaxContains.Value))
}
if !s.Examples.IsEmpty() { if !s.Examples.IsEmpty() {
var xph []string var xph []string
for w := range s.Examples.Value { for w := range s.Examples.Value {
@@ -632,6 +647,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
var allOf, anyOf, oneOf, not, items, prefixItems []low.ValueReference[*SchemaProxy] var allOf, anyOf, oneOf, not, items, prefixItems []low.ValueReference[*SchemaProxy]
var contains low.ValueReference[*SchemaProxy]
_, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content) _, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content)
_, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content) _, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content)
@@ -639,6 +655,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
_, notLabel, notValue := utils.FindKeyNodeFullTop(NotLabel, root.Content) _, notLabel, notValue := utils.FindKeyNodeFullTop(NotLabel, root.Content)
_, itemsLabel, itemsValue := utils.FindKeyNodeFullTop(ItemsLabel, root.Content) _, itemsLabel, itemsValue := utils.FindKeyNodeFullTop(ItemsLabel, root.Content)
_, prefixItemsLabel, prefixItemsValue := utils.FindKeyNodeFullTop(PrefixItemsLabel, root.Content) _, prefixItemsLabel, prefixItemsValue := utils.FindKeyNodeFullTop(PrefixItemsLabel, root.Content)
_, containsLabel, containsValue := utils.FindKeyNodeFullTop(ContainsLabel, root.Content)
errorChan := make(chan error) errorChan := make(chan error)
allOfChan := make(chan schemaProxyBuildResult) allOfChan := make(chan schemaProxyBuildResult)
@@ -647,6 +664,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
itemsChan := make(chan schemaProxyBuildResult) itemsChan := make(chan schemaProxyBuildResult)
prefixItemsChan := make(chan schemaProxyBuildResult) prefixItemsChan := make(chan schemaProxyBuildResult)
notChan := make(chan schemaProxyBuildResult) notChan := make(chan schemaProxyBuildResult)
containsChan := make(chan schemaProxyBuildResult)
totalBuilds := countSubSchemaItems(allOfValue) + totalBuilds := countSubSchemaItems(allOfValue) +
countSubSchemaItems(anyOfValue) + countSubSchemaItems(anyOfValue) +
@@ -673,6 +691,10 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
if notValue != nil { if notValue != nil {
go buildSchema(notChan, notLabel, notValue, errorChan, idx) go buildSchema(notChan, notLabel, notValue, errorChan, idx)
} }
if containsValue != nil {
totalBuilds++
go buildSchema(containsChan, containsLabel, containsValue, errorChan, idx)
}
completeCount := 0 completeCount := 0
for completeCount < totalBuilds { for completeCount < totalBuilds {
@@ -697,6 +719,9 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
case r := <-notChan: case r := <-notChan:
completeCount++ completeCount++
not = append(not, r.v) not = append(not, r.v)
case r := <-containsChan:
completeCount++
contains = r.v
} }
} }
@@ -743,6 +768,13 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
ValueNode: prefixItemsValue, ValueNode: prefixItemsValue,
} }
} }
if !contains.IsEmpty() {
s.Contains = low.NodeReference[*SchemaProxy]{
Value: contains.Value,
KeyNode: containsLabel,
ValueNode: containsValue,
}
}
return nil return nil
} }

View File

@@ -126,7 +126,11 @@ enum:
x-pizza: tasty x-pizza: tasty
examples: examples:
- hey - hey
- hi!` - hi!
contains:
type: int
maxContains: 10
minContains: 1`
} }
func Test_Schema(t *testing.T) { func Test_Schema(t *testing.T) {
@@ -271,6 +275,12 @@ func Test_Schema(t *testing.T) {
assert.Equal(t, "cat", mv.Value) assert.Equal(t, "cat", mv.Value)
mv = sch.Discriminator.Value.FindMappingValue("pizza") mv = sch.Discriminator.Value.FindMappingValue("pizza")
assert.Equal(t, "party", mv.Value) assert.Equal(t, "party", mv.Value)
// check contains
assert.Equal(t, "int", sch.Contains.Value.Schema().Type.Value.A)
assert.Equal(t, int64(1), sch.MinContains.Value)
assert.Equal(t, int64(10), sch.MaxContains.Value)
} }
func TestSchema_Hash(t *testing.T) { func TestSchema_Hash(t *testing.T) {