Components are in, writing new content for them and tests!

And, thats basically it (almost)
This commit is contained in:
Dave Shanley
2022-08-09 10:23:18 -04:00
parent ec87ddf8cc
commit 402a7c7e5a
9 changed files with 225 additions and 55 deletions

View File

@@ -1,19 +1,174 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"net/http" )
const (
ComponentsLabel = "components"
SchemasLabel = "schemas"
) )
type Components struct { type Components struct {
Node *yaml.Node Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]]
Schemas map[string]Schema Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
Responses map[string]Response Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]]
Parameters map[string]Parameter Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
Examples map[string]Example RequestBodies low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*RequestBody]]
RequestBodies map[string]RequestBody Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
Headers map[string]http.Header SecuritySchemes low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]]
SecuritySchemes map[string]SecurityScheme Links low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]]
Links map[string]Link Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]]
Callbacks map[string]Callback Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (co *Components) FindSchema(schema string) *low.ValueReference[*Schema] {
return FindItemInMap[*Schema](schema, co.Schemas.Value)
}
func (co *Components) FindResponse(response string) *low.ValueReference[*Response] {
return FindItemInMap[*Response](response, co.Responses.Value)
}
func (co *Components) Build(root *yaml.Node) error {
extensionMap, err := ExtractExtensions(root)
if err != nil {
return err
}
co.Extensions = extensionMap
// build out components asynchronously for speed. There could be some significant weight here.
skipChan := make(chan bool)
errorChan := make(chan error)
paramChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]])
schemaChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]])
responsesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]])
examplesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]])
requestBodiesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*RequestBody]])
headersChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]])
securitySchemesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]])
linkChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]])
callbackChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]])
go extractComponentValues[*Schema](SchemasLabel, root, skipChan, errorChan, schemaChan)
go extractComponentValues[*Parameter](ParametersLabel, root, skipChan, errorChan, paramChan)
go extractComponentValues[*Response](ResponsesLabel, root, skipChan, errorChan, responsesChan)
go extractComponentValues[*Example](ExamplesLabel, root, skipChan, errorChan, examplesChan)
go extractComponentValues[*RequestBody](RequestBodiesLabel, root, skipChan, errorChan, requestBodiesChan)
go extractComponentValues[*Header](HeadersLabel, root, skipChan, errorChan, headersChan)
go extractComponentValues[*SecurityScheme](SecuritySchemesLabel, root, skipChan, errorChan, securitySchemesChan)
go extractComponentValues[*Link](LinksLabel, root, skipChan, errorChan, linkChan)
go extractComponentValues[*Callback](CallbacksLabel, root, skipChan, errorChan, callbackChan)
n := 0
total := 9
allDone:
for {
select {
case buildError := <-errorChan:
return buildError
case <-skipChan:
n++
if n == total {
break allDone
}
case params := <-paramChan:
co.Parameters = params
n++
if n == total {
break allDone
}
case schemas := <-schemaChan:
co.Schemas = schemas
n++
if n == total {
break allDone
}
case responses := <-responsesChan:
co.Responses = responses
n++
if n == total {
break allDone
}
case examples := <-examplesChan:
co.Examples = examples
n++
if n == total {
break allDone
}
case reqBody := <-requestBodiesChan:
co.RequestBodies = reqBody
n++
if n == total {
break allDone
}
case headers := <-headersChan:
co.Headers = headers
n++
if n == total {
break allDone
}
case sScheme := <-securitySchemesChan:
co.SecuritySchemes = sScheme
n++
if n == total {
break allDone
}
case links := <-linkChan:
co.Links = links
n++
if n == total {
break allDone
}
case callbacks := <-callbackChan:
co.Callbacks = callbacks
n++
if n == total {
break allDone
}
}
}
return nil
}
func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.Node,
skip chan bool, errorChan chan<- error, resultChan chan<- low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]]) {
_, nodeLabel, nodeValue := utils.FindKeyNodeFull(label, root.Content)
if nodeValue == nil {
skip <- true
return
}
var currentLabel *yaml.Node
componentValues := make(map[low.KeyReference[string]]low.ValueReference[T])
for i, v := range nodeValue.Content {
if i%2 == 0 {
currentLabel = v
continue
}
var n T = new(N)
err := BuildModel(v, n)
if err != nil {
errorChan <- err
}
err = n.Build(v)
if err != nil {
errorChan <- err
}
componentValues[low.KeyReference[string]{
KeyNode: currentLabel,
Value: currentLabel.Value,
}] = low.ValueReference[T]{
Value: n,
ValueNode: v,
}
}
results := low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]]{
KeyNode: nodeLabel,
ValueNode: nodeValue,
Value: componentValues,
}
resultChan <- results
} }

View File

@@ -37,7 +37,7 @@ func ExtractSchema(root *yaml.Node) (*low.NodeReference[*Schema], error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = schema.Build(schNode, 0) err = schema.Build(schNode)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -8,6 +8,7 @@ import (
const ( const (
ParametersLabel = "parameters" ParametersLabel = "parameters"
RequestBodyLabel = "requestBody" RequestBodyLabel = "requestBody"
RequestBodiesLabel = "requestBodies"
ResponsesLabel = "responses" ResponsesLabel = "responses"
CallbacksLabel = "callbacks" CallbacksLabel = "callbacks"
) )

View File

@@ -189,9 +189,11 @@ allDone:
case buildError := <-opErrorChan: case buildError := <-opErrorChan:
return buildError return buildError
case <-opBuildChan: case <-opBuildChan:
if n == len(ops)-1 { n++
if n == len(ops) {
break allDone break allDone
} }
} }
} }

View File

@@ -61,7 +61,11 @@ func (s *Schema) FindProperty(name string) *low.ValueReference[*Schema] {
return FindItemInMap[*Schema](name, s.Properties.Value) return FindItemInMap[*Schema](name, s.Properties.Value)
} }
func (s *Schema) Build(root *yaml.Node, level int) error { func (s *Schema) Build(root *yaml.Node) error {
return s.BuildLevel(root, 0)
}
func (s *Schema) BuildLevel(root *yaml.Node, level int) error {
level++ level++
if level > 50 { if level > 50 {
return nil // we're done, son! too fricken deep. return nil // we're done, son! too fricken deep.
@@ -152,7 +156,7 @@ func (s *Schema) Build(root *yaml.Node, level int) error {
if err != nil { if err != nil {
return err return err
} }
err = property.Build(prop, level) err = property.BuildLevel(prop, level)
if err != nil { if err != nil {
return err return err
} }
@@ -245,7 +249,7 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
*errors = append(*errors, err) *errors = append(*errors, err)
return nil return nil
} }
err = schema.Build(vn, level) err = schema.BuildLevel(vn, level)
if err != nil { if err != nil {
*errors = append(*errors, err) *errors = append(*errors, err)
return nil return nil

View File

@@ -111,7 +111,7 @@ additionalProperties: true `
mbErr := BuildModel(&rootNode, &sch) mbErr := BuildModel(&rootNode, &sch)
assert.NoError(t, mbErr) assert.NoError(t, mbErr)
schErr := sch.Build(rootNode.Content[0], 0) schErr := sch.Build(rootNode.Content[0])
assert.NoError(t, schErr) assert.NoError(t, schErr)
assert.Equal(t, "something object", sch.Description.Value) assert.Equal(t, "something object", sch.Description.Value)
assert.True(t, sch.AdditionalProperties.Value.(bool)) assert.True(t, sch.AdditionalProperties.Value.(bool))

View File

@@ -8,6 +8,7 @@ import (
const ( const (
SecurityLabel = "security" SecurityLabel = "security"
SecuritySchemesLabel = "securitySchemes"
) )
type SecurityScheme struct { type SecurityScheme struct {
@@ -47,39 +48,7 @@ func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference
} }
func (sr *SecurityRequirement) Build(root *yaml.Node) error { func (sr *SecurityRequirement) Build(root *yaml.Node) error {
//if utils.IsNodeArray(root) {
// var currSec *yaml.Node
// var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
// for i, n := range root.Content {
// if i%2 == 0 {
// currSec = n
// continue
// }
// if utils.IsNodeArray(n) {
// res := make(map[low.KeyReference[string]][]low.ValueReference[string])
// var dat []low.ValueReference[string]
// for _, r := range n.Content {
// dat = append(dat, low.ValueReference[string]{
// Value: r.Value,
// ValueNode: r,
// })
// }
// res[low.KeyReference[string]{
// Value: currSec.Value,
// KeyNode: currSec,
// }] = dat
// requirements = append(requirements, low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]{
// Value: res,
// ValueNode: n,
// })
// }
// }
// sr.Value = requirements
//}
if utils.IsNodeArray(root) { if utils.IsNodeArray(root) {
var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]] var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
for _, n := range root.Content { for _, n := range root.Content {
var currSec *yaml.Node var currSec *yaml.Node

View File

@@ -39,6 +39,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
extractServers, extractServers,
extractTags, extractTags,
extractPaths, extractPaths,
extractComponents,
} }
wg.Add(len(extractionFuncs)) wg.Add(len(extractionFuncs))
for _, f := range extractionFuncs { for _, f := range extractionFuncs {
@@ -68,6 +69,21 @@ func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
return nil return nil
} }
func extractComponents(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.ComponentsLabel, info.RootNode.Content)
if vn != nil {
ir := v3.Components{}
err := v3.BuildModel(vn, &ir)
if err != nil {
return err
}
err = ir.Build(vn)
nr := low.NodeReference[*v3.Components]{Value: &ir, ValueNode: vn, KeyNode: ln}
doc.Components = nr
}
return nil
}
func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error { func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content) _, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content)
if vn != nil { if vn != nil {

View File

@@ -235,3 +235,26 @@ func TestCreateDocument_Paths(t *testing.T) {
assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value) assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value)
} }
func TestCreateDocument_Components_Schemas(t *testing.T) {
components := doc.Components.Value
assert.NotNil(t, components)
assert.Len(t, components.Schemas.Value, 5)
burger := components.FindSchema("Burger")
assert.NotNil(t, burger.Value)
assert.Equal(t, "The tastiest food on the planet you would love to eat everyday", burger.Value.Description.Value)
er := components.FindSchema("Error")
assert.NotNil(t, er.Value)
assert.Equal(t, "Error defining what went wrong when providing a specification. The message should help indicate the issue clearly.", er.Value.Description.Value)
fries := components.FindSchema("Fries")
assert.NotNil(t, fries.Value)
assert.Len(t, fries.Value.Properties.Value, 3)
assert.Equal(t, "a frosty cold beverage can be coke or sprite",
fries.Value.FindProperty("favoriteDrink").Value.Description.Value)
}