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
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"net/http"
)
const (
ComponentsLabel = "components"
SchemasLabel = "schemas"
)
type Components struct {
Node *yaml.Node
Schemas map[string]Schema
Responses map[string]Response
Parameters map[string]Parameter
Examples map[string]Example
RequestBodies map[string]RequestBody
Headers map[string]http.Header
SecuritySchemes map[string]SecurityScheme
Links map[string]Link
Callbacks map[string]Callback
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]]
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
RequestBodies low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*RequestBody]]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
SecuritySchemes low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]]
Links low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]]
Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*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 {
return nil, err
}
err = schema.Build(schNode, 0)
err = schema.Build(schNode)
if err != nil {
return nil, err
}

View File

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

View File

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

View File

@@ -61,7 +61,11 @@ func (s *Schema) FindProperty(name string) *low.ValueReference[*Schema] {
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++
if level > 50 {
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 {
return err
}
err = property.Build(prop, level)
err = property.BuildLevel(prop, level)
if err != nil {
return err
}
@@ -245,7 +249,7 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
*errors = append(*errors, err)
return nil
}
err = schema.Build(vn, level)
err = schema.BuildLevel(vn, level)
if err != nil {
*errors = append(*errors, err)
return nil

View File

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

View File

@@ -8,6 +8,7 @@ import (
const (
SecurityLabel = "security"
SecuritySchemesLabel = "securitySchemes"
)
type SecurityScheme struct {
@@ -47,39 +48,7 @@ func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference
}
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) {
var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
for _, n := range root.Content {
var currSec *yaml.Node

View File

@@ -39,6 +39,7 @@ func CreateDocument(info *datamodel.SpecInfo) (*v3.Document, error) {
extractServers,
extractTags,
extractPaths,
extractComponents,
}
wg.Add(len(extractionFuncs))
for _, f := range extractionFuncs {
@@ -68,6 +69,21 @@ func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
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 {
_, ln, vn := utils.FindKeyNodeFull(v3.ServersLabel, info.RootNode.Content)
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)
}
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)
}