mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-08 20:47:43 +00:00
Components are in, writing new content for them and tests!
And, thats basically it (almost)
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
const (
|
||||
ParametersLabel = "parameters"
|
||||
RequestBodyLabel = "requestBody"
|
||||
RequestBodiesLabel = "requestBodies"
|
||||
ResponsesLabel = "responses"
|
||||
CallbacksLabel = "callbacks"
|
||||
)
|
||||
|
||||
@@ -189,9 +189,11 @@ allDone:
|
||||
case buildError := <-opErrorChan:
|
||||
return buildError
|
||||
case <-opBuildChan:
|
||||
if n == len(ops)-1 {
|
||||
n++
|
||||
if n == len(ops) {
|
||||
break allDone
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user