Schemas now working, still need to check polymorphic work

However the painbarrier seems to have been smashed, now all polymorphic references can be tracked to circular references. confidence is now up 100%.
This commit is contained in:
Dave Shanley
2022-08-22 11:13:43 -04:00
parent 1a71f449ff
commit 108c3a93f3
7 changed files with 95 additions and 23 deletions

View File

@@ -121,13 +121,50 @@ func TestNewDocument_Tags(t *testing.T) {
assert.Equal(t, 20, wentLower.Description.ValueNode.Column) assert.Equal(t, 20, wentLower.Description.ValueNode.Column)
} }
func TestNewDocument_Components(t *testing.T) { func TestNewDocument_Components_Links(t *testing.T) {
h := NewDocument(doc) h := NewDocument(doc)
assert.Len(t, h.Components.Links, 2) assert.Len(t, h.Components.Links, 2)
assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId) assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId)
assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"]) assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"])
}
func TestNewDocument_Components_Callbacks(t *testing.T) {
h := NewDocument(doc)
assert.Len(t, h.Components.Callbacks, 1) assert.Len(t, h.Components.Callbacks, 1)
assert.Equal(t, "Callback payload", assert.Equal(t, "Callback payload",
h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description) h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description)
} }
func TestNewDocument_Components_Schemas(t *testing.T) {
h := NewDocument(doc)
assert.Len(t, h.Components.Schemas, 6)
a := h.Components.Schemas["Error"]
assert.Equal(t, "No such burger as 'Big-Whopper'", a.Properties["message"].Example)
b := h.Components.Schemas["Burger"]
assert.Len(t, b.Required, 2)
assert.Equal(t, "golden slices of happy fun joy", b.Properties["fries"].Description)
assert.Equal(t, int64(2), b.Properties["numPatties"].Example)
f := h.Components.Schemas["Fries"]
assert.Equal(t, "salt", f.Properties["seasoning"].Items.Example)
assert.Len(t, f.Properties["favoriteDrink"].Properties["drinkType"].Enum, 2)
d := h.Components.Schemas["Drink"]
assert.Len(t, d.Required, 2)
assert.True(t, d.AdditionalProperties.(bool))
assert.Equal(t, "drinkType", d.Discriminator.PropertyName)
assert.Equal(t, "some value", d.Discriminator.Mapping["drink"])
ext := h.Components.Extensions
assert.Equal(t, "loud", ext["x-screaming-baby"])
}
func TestNewDocument_Components_Headers(t *testing.T) {
h := NewDocument(doc)
assert.Len(t, h.Components.Headers, 1)
assert.Equal(t, "this is a header", h.Components.Headers["UseOil"].Description)
}

View File

@@ -33,7 +33,7 @@ type Schema struct {
OneOf []*Schema OneOf []*Schema
AnyOf []*Schema AnyOf []*Schema
Not []*Schema Not []*Schema
Items []*Schema Items *Schema
Properties map[string]*Schema Properties map[string]*Schema
AdditionalProperties any AdditionalProperties any
Description string Description string
@@ -94,7 +94,7 @@ func NewSchema(schema *low.Schema) *Schema {
var enum []string var enum []string
for i := range schema.Enum.Value { for i := range schema.Enum.Value {
enum = append(req, schema.Enum.Value[i].Value) enum = append(enum, schema.Enum.Value[i].Value)
} }
s.Enum = enum s.Enum = enum
@@ -111,7 +111,7 @@ func NewSchema(schema *low.Schema) *Schema {
bChan := make(chan *Schema) bChan := make(chan *Schema)
// for every item, build schema async // for every item, build schema async
buildSchema := func(sch lowmodel.NodeReference[*low.Schema], bChan chan *Schema) { buildSchemaChild := func(sch lowmodel.NodeReference[*low.Schema], bChan chan *Schema) {
if ss := getSeenSchema(sch.GenerateMapKey()); ss != nil { if ss := getSeenSchema(sch.GenerateMapKey()); ss != nil {
bChan <- ss bChan <- ss
return return
@@ -122,7 +122,7 @@ func NewSchema(schema *low.Schema) *Schema {
} }
totalSchemas := len(schemas) totalSchemas := len(schemas)
for v := range schemas { for v := range schemas {
go buildSchema(schemas[v], bChan) go buildSchemaChild(schemas[v], bChan)
} }
j := 0 j := 0
for j < totalSchemas { for j < totalSchemas {
@@ -178,15 +178,23 @@ func NewSchema(schema *low.Schema) *Schema {
go buildOutSchema(schema.Not.Value, &not, polyCompletedChan) go buildOutSchema(schema.Not.Value, &not, polyCompletedChan)
} }
if !schema.Items.IsEmpty() { if !schema.Items.IsEmpty() {
go buildOutSchema(schema.Items.Value, &items, polyCompletedChan) // items is only a single prop, however the method uses an array, so pack it up in one.
var itms []lowmodel.NodeReference[*low.Schema]
itms = append(itms, lowmodel.NodeReference[*low.Schema]{
Value: schema.Items.Value,
KeyNode: schema.Items.KeyNode,
ValueNode: schema.Items.ValueNode,
})
go buildOutSchema(itms, &items, polyCompletedChan)
} }
completePoly := 0 completePoly := 0
completedProps := 0 completedProps := 0
totalProps := len(schema.Properties.Value) totalProps := len(schema.Properties.Value)
totalPoly := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Not.Value) + totalPoly := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Not.Value)
len(schema.Items.Value) if !schema.Items.IsEmpty() {
totalPoly++ // only a single item can be present.
}
if totalProps+totalPoly > 0 { if totalProps+totalPoly > 0 {
allDone: allDone:
for true { for true {
@@ -208,7 +216,10 @@ func NewSchema(schema *low.Schema) *Schema {
s.AnyOf = anyOf s.AnyOf = anyOf
s.AllOf = allOf s.AllOf = allOf
s.Not = not s.Not = not
s.Items = items if len(items) > 0 {
s.Items = items[0] // there will only ever be one.
}
return s return s
} }

View File

@@ -9,6 +9,7 @@ import (
"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"
"strings"
"sync" "sync"
) )
@@ -205,16 +206,20 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
}, },
} }
} }
totalComponents := 0
for i, v := range nodeValue.Content { for i, v := range nodeValue.Content {
// always ignore extensions
if i%2 == 0 { if i%2 == 0 {
currentLabel = v currentLabel = v
continue continue
} }
if strings.HasPrefix(strings.ToLower(currentLabel.Value), "x-") {
continue
}
totalComponents++
go buildComponent(currentLabel, v, bChan, errorChan) go buildComponent(currentLabel, v, bChan, errorChan)
} }
totalComponents := len(nodeValue.Content) / 2
completedComponents := 0 completedComponents := 0
for completedComponents < totalComponents { for completedComponents < totalComponents {
select { select {

View File

@@ -8,6 +8,7 @@ import (
"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"
"strconv"
) )
const ( const (
@@ -67,3 +68,19 @@ func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
return nil return nil
} }
func ExtractExampleValue(exp *yaml.Node) any {
if utils.IsNodeBoolValue(exp) {
v, _ := strconv.ParseBool(exp.Value)
return v
}
if utils.IsNodeIntValue(exp) {
v, _ := strconv.ParseInt(exp.Value, 10, 64)
return v
}
if utils.IsNodeFloatValue(exp) {
v, _ := strconv.ParseFloat(exp.Value, 64)
return v
}
return exp.Value
}

View File

@@ -44,7 +44,7 @@ type Schema struct {
OneOf low.NodeReference[[]low.NodeReference[*Schema]] OneOf low.NodeReference[[]low.NodeReference[*Schema]]
AnyOf low.NodeReference[[]low.NodeReference[*Schema]] AnyOf low.NodeReference[[]low.NodeReference[*Schema]]
Not low.NodeReference[[]low.NodeReference[*Schema]] Not low.NodeReference[[]low.NodeReference[*Schema]]
Items low.NodeReference[[]low.NodeReference[*Schema]] Items low.NodeReference[*Schema]
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]] Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]]
AdditionalProperties low.NodeReference[any] AdditionalProperties low.NodeReference[any]
Description low.NodeReference[string] Description low.NodeReference[string]
@@ -93,7 +93,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
// handle example if set. // handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
if expNode != nil { if expNode != nil {
s.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode} s.Example = low.NodeReference[any]{Value: ExtractExampleValue(expNode), KeyNode: expLabel, ValueNode: expNode}
} }
_, addPLabel, addPNode := utils.FindKeyNodeFull(AdditionalPropertiesLabel, root.Content) _, addPLabel, addPNode := utils.FindKeyNodeFull(AdditionalPropertiesLabel, root.Content)
@@ -265,8 +265,9 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
} }
} }
if len(items) > 0 { if len(items) > 0 {
s.Items = low.NodeReference[[]low.NodeReference[*Schema]]{ // items can only be a single def, so only extract a single value
Value: items, s.Items = low.NodeReference[*Schema]{
Value: items[0].Value,
KeyNode: itemsLabel, KeyNode: itemsLabel,
ValueNode: itemsValue, ValueNode: itemsValue,
} }

View File

@@ -197,15 +197,15 @@ additionalProperties: true `
assert.Equal(t, "notBExp", v.Value.Example.Value) assert.Equal(t, "notBExp", v.Value.Example.Value)
// check values Items // check values Items
assert.Equal(t, "an items thing", sch.Items.Value[0].Value.Description.Value) assert.Equal(t, "an items thing", sch.Items.Value.Description.Value)
assert.Len(t, sch.Items.Value[0].Value.Properties.Value, 2) assert.Len(t, sch.Items.Value.Properties.Value, 2)
v = sch.Items.Value[0].Value.FindProperty("itemsA") v = sch.Items.Value.FindProperty("itemsA")
assert.NotNil(t, v) assert.NotNil(t, v)
assert.Equal(t, "itemsA description", v.Value.Description.Value) assert.Equal(t, "itemsA description", v.Value.Description.Value)
assert.Equal(t, "itemsAExp", v.Value.Example.Value) assert.Equal(t, "itemsAExp", v.Value.Example.Value)
v = sch.Items.Value[0].Value.FindProperty("itemsB") v = sch.Items.Value.FindProperty("itemsB")
assert.NotNil(t, v) assert.NotNil(t, v)
assert.Equal(t, "itemsB description", v.Value.Description.Value) assert.Equal(t, "itemsB description", v.Value.Description.Value)
assert.Equal(t, "itemsBExp", v.Value.Example.Value) assert.Equal(t, "itemsBExp", v.Value.Example.Value)
@@ -456,7 +456,7 @@ items:
assert.Equal(t, desc, sch.AnyOf.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.AllOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.Not.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) assert.Equal(t, desc, sch.Items.Value.Description.Value)
} }
func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) { func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) {
@@ -546,7 +546,7 @@ items:
assert.Equal(t, desc, sch.AnyOf.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.AllOf.Value[0].Value.Description.Value)
assert.Equal(t, desc, sch.Not.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) assert.Equal(t, desc, sch.Items.Value.Description.Value)
} }
func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) { func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) {

View File

@@ -511,6 +511,7 @@ components:
SomePayload: SomePayload:
type: string type: string
description: some kind of payload for something. description: some kind of payload for something.
x-screaming-baby: loud
x-something-something: darkside x-something-something: darkside
externalDocs: externalDocs:
description: "Find out more information about our products)" description: "Find out more information about our products)"