diff --git a/datamodel/high/3.0/document_test.go b/datamodel/high/3.0/document_test.go index 26d12ea..8d40331 100644 --- a/datamodel/high/3.0/document_test.go +++ b/datamodel/high/3.0/document_test.go @@ -121,13 +121,50 @@ func TestNewDocument_Tags(t *testing.T) { assert.Equal(t, 20, wentLower.Description.ValueNode.Column) } -func TestNewDocument_Components(t *testing.T) { +func TestNewDocument_Components_Links(t *testing.T) { h := NewDocument(doc) assert.Len(t, h.Components.Links, 2) assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId) 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.Equal(t, "Callback payload", 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) +} diff --git a/datamodel/high/3.0/schema.go b/datamodel/high/3.0/schema.go index 3e3e192..1602709 100644 --- a/datamodel/high/3.0/schema.go +++ b/datamodel/high/3.0/schema.go @@ -33,7 +33,7 @@ type Schema struct { OneOf []*Schema AnyOf []*Schema Not []*Schema - Items []*Schema + Items *Schema Properties map[string]*Schema AdditionalProperties any Description string @@ -94,7 +94,7 @@ func NewSchema(schema *low.Schema) *Schema { var enum []string 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 @@ -111,7 +111,7 @@ func NewSchema(schema *low.Schema) *Schema { bChan := make(chan *Schema) // 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 { bChan <- ss return @@ -122,7 +122,7 @@ func NewSchema(schema *low.Schema) *Schema { } totalSchemas := len(schemas) for v := range schemas { - go buildSchema(schemas[v], bChan) + go buildSchemaChild(schemas[v], bChan) } j := 0 for j < totalSchemas { @@ -178,15 +178,23 @@ func NewSchema(schema *low.Schema) *Schema { go buildOutSchema(schema.Not.Value, ¬, polyCompletedChan) } 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 completedProps := 0 totalProps := len(schema.Properties.Value) - totalPoly := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Not.Value) + - len(schema.Items.Value) - + totalPoly := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Not.Value) + if !schema.Items.IsEmpty() { + totalPoly++ // only a single item can be present. + } if totalProps+totalPoly > 0 { allDone: for true { @@ -208,10 +216,13 @@ func NewSchema(schema *low.Schema) *Schema { s.AnyOf = anyOf s.AllOf = allOf s.Not = not - s.Items = items + if len(items) > 0 { + s.Items = items[0] // there will only ever be one. + } + return s } func (s *Schema) GoLow() *low.Schema { return s.low -} \ No newline at end of file +} diff --git a/datamodel/low/3.0/components.go b/datamodel/low/3.0/components.go index 3571fd6..f57566c 100644 --- a/datamodel/low/3.0/components.go +++ b/datamodel/low/3.0/components.go @@ -9,6 +9,7 @@ import ( "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" + "strings" "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 { + // always ignore extensions if i%2 == 0 { currentLabel = v continue } + if strings.HasPrefix(strings.ToLower(currentLabel.Value), "x-") { + continue + } + totalComponents++ go buildComponent(currentLabel, v, bChan, errorChan) } - totalComponents := len(nodeValue.Content) / 2 completedComponents := 0 for completedComponents < totalComponents { select { diff --git a/datamodel/low/3.0/example.go b/datamodel/low/3.0/example.go index 44110e8..43b724d 100644 --- a/datamodel/low/3.0/example.go +++ b/datamodel/low/3.0/example.go @@ -8,6 +8,7 @@ import ( "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" + "strconv" ) const ( @@ -67,3 +68,19 @@ func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error { } 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 +} diff --git a/datamodel/low/3.0/schema.go b/datamodel/low/3.0/schema.go index aba43e8..ae20a6a 100644 --- a/datamodel/low/3.0/schema.go +++ b/datamodel/low/3.0/schema.go @@ -44,7 +44,7 @@ type Schema struct { OneOf low.NodeReference[[]low.NodeReference[*Schema]] AnyOf 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]] AdditionalProperties low.NodeReference[any] 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. _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) 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) @@ -265,8 +265,9 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er } } if len(items) > 0 { - s.Items = low.NodeReference[[]low.NodeReference[*Schema]]{ - Value: items, + // items can only be a single def, so only extract a single value + s.Items = low.NodeReference[*Schema]{ + Value: items[0].Value, KeyNode: itemsLabel, ValueNode: itemsValue, } diff --git a/datamodel/low/3.0/schema_test.go b/datamodel/low/3.0/schema_test.go index e621045..d1bb0fc 100644 --- a/datamodel/low/3.0/schema_test.go +++ b/datamodel/low/3.0/schema_test.go @@ -197,15 +197,15 @@ additionalProperties: true ` assert.Equal(t, "notBExp", v.Value.Example.Value) // check values Items - assert.Equal(t, "an items thing", sch.Items.Value[0].Value.Description.Value) - assert.Len(t, sch.Items.Value[0].Value.Properties.Value, 2) + assert.Equal(t, "an items thing", sch.Items.Value.Description.Value) + 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.Equal(t, "itemsA description", v.Value.Description.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.Equal(t, "itemsB description", v.Value.Description.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.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) + assert.Equal(t, desc, sch.Items.Value.Description.Value) } 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.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) + assert.Equal(t, desc, sch.Items.Value.Description.Value) } func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) { diff --git a/test_specs/burgershop.openapi.yaml b/test_specs/burgershop.openapi.yaml index 4262f69..52c4681 100644 --- a/test_specs/burgershop.openapi.yaml +++ b/test_specs/burgershop.openapi.yaml @@ -511,6 +511,7 @@ components: SomePayload: type: string description: some kind of payload for something. + x-screaming-baby: loud x-something-something: darkside externalDocs: description: "Find out more information about our products)"