mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-11 04:20:24 +00:00
Building out 2.0 low model
This commit is contained in:
@@ -38,7 +38,9 @@ type Components struct {
|
||||
func NewComponents(comp *low.Components) *Components {
|
||||
c := new(Components)
|
||||
c.low = comp
|
||||
if len(comp.Extensions) > 0 {
|
||||
c.Extensions = high.ExtractExtensions(comp.Extensions)
|
||||
}
|
||||
cbMap := make(map[string]*Callback)
|
||||
linkMap := make(map[string]*Link)
|
||||
responseMap := make(map[string]*Response)
|
||||
|
||||
@@ -45,9 +45,15 @@ func NewDocument(document *low.Document) *Document {
|
||||
if !document.ExternalDocs.IsEmpty() {
|
||||
d.ExternalDocs = NewExternalDoc(document.ExternalDocs.Value)
|
||||
}
|
||||
if len(document.Extensions) > 0 {
|
||||
d.Extensions = high.ExtractExtensions(document.Extensions)
|
||||
}
|
||||
if !document.Components.IsEmpty() {
|
||||
d.Components = NewComponents(document.Components.Value)
|
||||
}
|
||||
if !document.Paths.IsEmpty() {
|
||||
d.Paths = NewPaths(document.Paths.Value)
|
||||
}
|
||||
d.Index = document.Index
|
||||
return d
|
||||
}
|
||||
|
||||
@@ -6,20 +6,199 @@ package v2
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
DefinitionsLabel = "definitions"
|
||||
)
|
||||
|
||||
type ParameterDefinitions struct {
|
||||
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]]
|
||||
Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter]
|
||||
}
|
||||
|
||||
type ResponsesDefinitions struct {
|
||||
Definitions map[low.KeyReference[string]]low.ValueReference[*Response]
|
||||
}
|
||||
|
||||
type SecurityDefinitions struct {
|
||||
Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]
|
||||
}
|
||||
|
||||
type Definitions struct {
|
||||
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
|
||||
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
|
||||
}
|
||||
|
||||
func (d *Definitions) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] {
|
||||
return low.FindItemInMap[*base.SchemaProxy](schema, d.Schemas.Value)
|
||||
return low.FindItemInMap[*base.SchemaProxy](schema, d.Schemas)
|
||||
}
|
||||
|
||||
func (pd *ParameterDefinitions) FindSchema(schema string) *low.ValueReference[*Parameter] {
|
||||
return low.FindItemInMap[*Parameter](schema, pd.Parameters.Value)
|
||||
func (pd *ParameterDefinitions) FindParameter(schema string) *low.ValueReference[*Parameter] {
|
||||
return low.FindItemInMap[*Parameter](schema, pd.Definitions)
|
||||
}
|
||||
|
||||
func (r *ResponsesDefinitions) FindResponse(schema string) *low.ValueReference[*Response] {
|
||||
return low.FindItemInMap[*Response](schema, r.Definitions)
|
||||
}
|
||||
|
||||
func (s *SecurityDefinitions) FindSecurityScheme(schema string) *low.ValueReference[*SecurityScheme] {
|
||||
return low.FindItemInMap[*SecurityScheme](schema, s.Definitions)
|
||||
}
|
||||
|
||||
func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
errorChan := make(chan error)
|
||||
resultChan := make(chan definitionResult)
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
defLabel = root.Content[i]
|
||||
continue
|
||||
}
|
||||
totalDefinitions++
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
|
||||
obj, err := low.ExtractObjectRaw[*base.SchemaProxy](value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
|
||||
}
|
||||
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
|
||||
}
|
||||
|
||||
completedDefs := 0
|
||||
results := make(map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy])
|
||||
for completedDefs < totalDefinitions {
|
||||
select {
|
||||
case err := <-errorChan:
|
||||
return err
|
||||
case sch := <-resultChan:
|
||||
results[low.KeyReference[string]{
|
||||
Value: sch.k.Value,
|
||||
KeyNode: sch.k,
|
||||
}] = sch.v.(low.ValueReference[*base.SchemaProxy])
|
||||
}
|
||||
}
|
||||
d.Schemas = results
|
||||
return nil
|
||||
}
|
||||
|
||||
func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
errorChan := make(chan error)
|
||||
resultChan := make(chan definitionResult)
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
defLabel = root.Content[i]
|
||||
continue
|
||||
}
|
||||
totalDefinitions++
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
|
||||
obj, err := low.ExtractObjectRaw[*Parameter](value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
|
||||
}
|
||||
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
|
||||
}
|
||||
|
||||
completedDefs := 0
|
||||
results := make(map[low.KeyReference[string]]low.ValueReference[*Parameter])
|
||||
for completedDefs < totalDefinitions {
|
||||
select {
|
||||
case err := <-errorChan:
|
||||
return err
|
||||
case sch := <-resultChan:
|
||||
results[low.KeyReference[string]{
|
||||
Value: sch.k.Value,
|
||||
KeyNode: sch.k,
|
||||
}] = sch.v.(low.ValueReference[*Parameter])
|
||||
}
|
||||
}
|
||||
pd.Definitions = results
|
||||
return nil
|
||||
}
|
||||
|
||||
type definitionResult struct {
|
||||
k *yaml.Node
|
||||
v any
|
||||
}
|
||||
|
||||
func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
errorChan := make(chan error)
|
||||
resultChan := make(chan definitionResult)
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
defLabel = root.Content[i]
|
||||
continue
|
||||
}
|
||||
totalDefinitions++
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
|
||||
obj, err := low.ExtractObjectRaw[*Response](value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
|
||||
}
|
||||
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
|
||||
}
|
||||
|
||||
completedDefs := 0
|
||||
results := make(map[low.KeyReference[string]]low.ValueReference[*Response])
|
||||
for completedDefs < totalDefinitions {
|
||||
select {
|
||||
case err := <-errorChan:
|
||||
return err
|
||||
case sch := <-resultChan:
|
||||
results[low.KeyReference[string]{
|
||||
Value: sch.k.Value,
|
||||
KeyNode: sch.k,
|
||||
}] = sch.v.(low.ValueReference[*Response])
|
||||
}
|
||||
}
|
||||
r.Definitions = results
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
errorChan := make(chan error)
|
||||
resultChan := make(chan definitionResult)
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
defLabel = root.Content[i]
|
||||
continue
|
||||
}
|
||||
totalDefinitions++
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult, e chan error) {
|
||||
obj, err := low.ExtractObjectRaw[*SecurityScheme](value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult{k: label, v: low.ValueReference[any]{Value: obj, ValueNode: value}}
|
||||
}
|
||||
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
|
||||
}
|
||||
|
||||
completedDefs := 0
|
||||
results := make(map[low.KeyReference[string]]low.ValueReference[*SecurityScheme])
|
||||
for completedDefs < totalDefinitions {
|
||||
select {
|
||||
case err := <-errorChan:
|
||||
return err
|
||||
case sch := <-resultChan:
|
||||
results[low.KeyReference[string]{
|
||||
Value: sch.k.Value,
|
||||
KeyNode: sch.k,
|
||||
}] = sch.v.(low.ValueReference[*SecurityScheme])
|
||||
}
|
||||
}
|
||||
s.Definitions = results
|
||||
return nil
|
||||
}
|
||||
|
||||
64
datamodel/low/2.0/examples.go
Normal file
64
datamodel/low/2.0/examples.go
Normal file
@@ -0,0 +1,64 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Examples struct {
|
||||
Values map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
var keyNode, currNode *yaml.Node
|
||||
var err error
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
keyNode = root.Content[i]
|
||||
continue
|
||||
}
|
||||
currNode = root.Content[i]
|
||||
if utils.IsNodeMap(currNode) {
|
||||
var n map[string]interface{}
|
||||
err = currNode.Decode(&n)
|
||||
if err != nil {
|
||||
var k []interface{}
|
||||
err = currNode.Decode(&k)
|
||||
if err != nil {
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = currNode.Decode(&j)
|
||||
e.Values[low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: j,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
continue
|
||||
}
|
||||
e.Values[low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: k,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
continue
|
||||
}
|
||||
e.Values[low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
}] = low.ValueReference[any]{
|
||||
Value: n,
|
||||
ValueNode: currNode,
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -9,6 +9,10 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
HeadersLabel = "headers"
|
||||
)
|
||||
|
||||
type Header struct {
|
||||
Type low.NodeReference[string]
|
||||
Format low.NodeReference[string]
|
||||
@@ -42,4 +46,3 @@ func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
72
datamodel/low/2.0/operation.go
Normal file
72
datamodel/low/2.0/operation.go
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Operation struct {
|
||||
Tags low.NodeReference[[]low.ValueReference[string]]
|
||||
Summary low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
ExternalDocs low.NodeReference[*base.ExternalDoc]
|
||||
OperationId low.NodeReference[string]
|
||||
Consumes low.NodeReference[[]low.ValueReference[string]]
|
||||
Produces low.NodeReference[[]low.ValueReference[string]]
|
||||
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
||||
Responses low.NodeReference[*Responses]
|
||||
Schemes low.NodeReference[[]low.ValueReference[string]]
|
||||
Deprecated low.NodeReference[bool]
|
||||
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
o.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
// extract externalDocs
|
||||
extDocs, dErr := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, root, idx)
|
||||
if dErr != nil {
|
||||
return dErr
|
||||
}
|
||||
o.ExternalDocs = extDocs
|
||||
|
||||
// extract parameters
|
||||
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
|
||||
if pErr != nil {
|
||||
return pErr
|
||||
}
|
||||
if params != nil {
|
||||
o.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
|
||||
Value: params,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
}
|
||||
|
||||
// extract responses
|
||||
respBody, respErr := low.ExtractObject[*Responses](ResponsesLabel, root, idx)
|
||||
if respErr != nil {
|
||||
return respErr
|
||||
}
|
||||
o.Responses = respBody
|
||||
|
||||
// extract parameters
|
||||
sec, sln, svn, sErr := low.ExtractArray[*SecurityRequirement](SecurityLabel, root, idx)
|
||||
if sErr != nil {
|
||||
return sErr
|
||||
}
|
||||
if sec != nil {
|
||||
o.Security = low.NodeReference[[]low.ValueReference[*SecurityRequirement]]{
|
||||
Value: sec,
|
||||
KeyNode: sln,
|
||||
ValueNode: svn,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -5,10 +5,15 @@ package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
ParametersLabel = "parameters"
|
||||
)
|
||||
|
||||
type Parameter struct {
|
||||
Name low.NodeReference[string]
|
||||
In low.NodeReference[string]
|
||||
@@ -17,6 +22,7 @@ type Parameter struct {
|
||||
Description low.NodeReference[string]
|
||||
Required low.NodeReference[bool]
|
||||
AllowEmptyValue low.NodeReference[bool]
|
||||
Schema low.NodeReference[*base.SchemaProxy]
|
||||
Items low.NodeReference[*Items]
|
||||
CollectionFormat low.NodeReference[string]
|
||||
Default low.NodeReference[any]
|
||||
|
||||
179
datamodel/low/2.0/path_item.go
Normal file
179
datamodel/low/2.0/path_item.go
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type PathItem struct {
|
||||
Ref low.NodeReference[string]
|
||||
Get low.NodeReference[*Operation]
|
||||
Put low.NodeReference[*Operation]
|
||||
Post low.NodeReference[*Operation]
|
||||
Delete low.NodeReference[*Operation]
|
||||
Options low.NodeReference[*Operation]
|
||||
Head low.NodeReference[*Operation]
|
||||
Patch low.NodeReference[*Operation]
|
||||
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, p.Extensions)
|
||||
}
|
||||
|
||||
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
skip := false
|
||||
var currentNode *yaml.Node
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errors []error
|
||||
|
||||
var ops []low.NodeReference[*Operation]
|
||||
|
||||
// extract parameters
|
||||
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
|
||||
if pErr != nil {
|
||||
return pErr
|
||||
}
|
||||
if params != nil {
|
||||
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
|
||||
Value: params,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
}
|
||||
|
||||
for i, pathNode := range root.Content {
|
||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "parameters") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
if i%2 == 0 {
|
||||
currentNode = pathNode
|
||||
continue
|
||||
}
|
||||
|
||||
// the only thing we now care about is handling operations, filter out anything that's not a verb.
|
||||
switch currentNode.Value {
|
||||
case GetLabel:
|
||||
break
|
||||
case PostLabel:
|
||||
break
|
||||
case PutLabel:
|
||||
break
|
||||
case PatchLabel:
|
||||
break
|
||||
case DeleteLabel:
|
||||
break
|
||||
case HeadLabel:
|
||||
break
|
||||
case OptionsLabel:
|
||||
break
|
||||
default:
|
||||
continue // ignore everything else.
|
||||
}
|
||||
|
||||
var op Operation
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
|
||||
r, err := low.LocateRefNode(pathNode, idx)
|
||||
if r != nil {
|
||||
pathNode = r
|
||||
if err != nil {
|
||||
if !idx.AllowCircularReferenceResolving() {
|
||||
return fmt.Errorf("build schema failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
||||
pathNode.Content[1].Value, pathNode.Content[1].Line, pathNode.Content[1].Column)
|
||||
}
|
||||
}
|
||||
|
||||
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
|
||||
|
||||
opRef := low.NodeReference[*Operation]{
|
||||
Value: &op,
|
||||
KeyNode: currentNode,
|
||||
ValueNode: pathNode,
|
||||
}
|
||||
|
||||
ops = append(ops, opRef)
|
||||
|
||||
switch currentNode.Value {
|
||||
case GetLabel:
|
||||
p.Get = opRef
|
||||
case PostLabel:
|
||||
p.Post = opRef
|
||||
case PutLabel:
|
||||
p.Put = opRef
|
||||
case PatchLabel:
|
||||
p.Patch = opRef
|
||||
case DeleteLabel:
|
||||
p.Delete = opRef
|
||||
case HeadLabel:
|
||||
p.Head = opRef
|
||||
case OptionsLabel:
|
||||
p.Options = opRef
|
||||
}
|
||||
}
|
||||
|
||||
//all operations have been superficially built,
|
||||
//now we need to build out the operation, we will do this asynchronously for speed.
|
||||
opBuildChan := make(chan bool)
|
||||
opErrorChan := make(chan error)
|
||||
|
||||
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
|
||||
er := op.Value.Build(op.ValueNode, idx)
|
||||
if er != nil {
|
||||
errCh <- er
|
||||
}
|
||||
ch <- true
|
||||
}
|
||||
|
||||
if len(ops) <= 0 {
|
||||
return nil // nothing to do.
|
||||
}
|
||||
|
||||
for _, op := range ops {
|
||||
go buildOpFunc(op, opBuildChan, opErrorChan)
|
||||
}
|
||||
|
||||
n := 0
|
||||
total := len(ops)
|
||||
for n < total {
|
||||
select {
|
||||
case buildError := <-opErrorChan:
|
||||
return buildError
|
||||
case <-opBuildChan:
|
||||
n++
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we don't exit before the path is finished building.
|
||||
if len(ops) > 0 {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
125
datamodel/low/2.0/paths.go
Normal file
125
datamodel/low/2.0/paths.go
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PathsLabel = "paths"
|
||||
GetLabel = "get"
|
||||
PostLabel = "post"
|
||||
PatchLabel = "patch"
|
||||
PutLabel = "put"
|
||||
DeleteLabel = "delete"
|
||||
OptionsLabel = "options"
|
||||
HeadLabel = "head"
|
||||
TraceLabel = "trace"
|
||||
)
|
||||
|
||||
type Paths struct {
|
||||
PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
|
||||
for k, j := range p.PathItems {
|
||||
if k.Value == path {
|
||||
return &j
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Paths) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, p.Extensions)
|
||||
}
|
||||
|
||||
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
p.Extensions = low.ExtractExtensions(root)
|
||||
skip := false
|
||||
var currentNode *yaml.Node
|
||||
|
||||
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
||||
|
||||
// build each new path, in a new thread.
|
||||
type pathBuildResult struct {
|
||||
k low.KeyReference[string]
|
||||
v low.ValueReference[*PathItem]
|
||||
}
|
||||
|
||||
bChan := make(chan pathBuildResult)
|
||||
eChan := make(chan error)
|
||||
var buildPathItem = func(cNode, pNode *yaml.Node, b chan<- pathBuildResult, e chan<- error) {
|
||||
if ok, _, _ := utils.IsNodeRefValue(pNode); ok {
|
||||
r, err := low.LocateRefNode(pNode, idx)
|
||||
if r != nil {
|
||||
pNode = r
|
||||
if err != nil {
|
||||
if !idx.AllowCircularReferenceResolving() {
|
||||
e <- fmt.Errorf("path item build failed: %s", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
e <- fmt.Errorf("path item build failed: cannot find reference: %s at line %d, col %d",
|
||||
pNode.Content[1].Value, pNode.Content[1].Line, pNode.Content[1].Column)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
path := new(PathItem)
|
||||
_ = low.BuildModel(pNode, path)
|
||||
err := path.Build(pNode, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
b <- pathBuildResult{
|
||||
k: low.KeyReference[string]{
|
||||
Value: cNode.Value,
|
||||
KeyNode: cNode,
|
||||
},
|
||||
v: low.ValueReference[*PathItem]{
|
||||
Value: path,
|
||||
ValueNode: pNode,
|
||||
},
|
||||
}
|
||||
}
|
||||
pathCount := 0
|
||||
for i, pathNode := range root.Content {
|
||||
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
|
||||
skip = true
|
||||
continue
|
||||
}
|
||||
if skip {
|
||||
skip = false
|
||||
continue
|
||||
}
|
||||
if i%2 == 0 {
|
||||
currentNode = pathNode
|
||||
continue
|
||||
}
|
||||
pathCount++
|
||||
go buildPathItem(currentNode, pathNode, bChan, eChan)
|
||||
}
|
||||
completedItems := 0
|
||||
for completedItems < pathCount {
|
||||
select {
|
||||
case err := <-eChan:
|
||||
return err
|
||||
case res := <-bChan:
|
||||
completedItems++
|
||||
pathsMap[res.k] = res.v
|
||||
}
|
||||
}
|
||||
p.PathItems = pathsMap
|
||||
return nil
|
||||
}
|
||||
@@ -6,11 +6,50 @@ package v2
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
ResponsesLabel = "responses"
|
||||
)
|
||||
|
||||
type Response struct {
|
||||
Description low.NodeReference[string]
|
||||
Schema low.NodeReference[*base.SchemaProxy]
|
||||
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
|
||||
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[any]]
|
||||
Examples low.NodeReference[*Examples]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (r *Response) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, r.Extensions)
|
||||
}
|
||||
|
||||
func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
|
||||
return low.FindItemInMap[*Header](hType, r.Headers.Value)
|
||||
}
|
||||
|
||||
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
r.Extensions = low.ExtractExtensions(root)
|
||||
s, err := base.ExtractSchema(root, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Schema = *s
|
||||
|
||||
//extract headers
|
||||
headers, lN, kN, err := low.ExtractMapFlat[*Header](HeadersLabel, root, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if headers != nil {
|
||||
r.Headers = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]{
|
||||
Value: headers,
|
||||
KeyNode: lN,
|
||||
ValueNode: kN,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
47
datamodel/low/2.0/responses.go
Normal file
47
datamodel/low/2.0/responses.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Responses struct {
|
||||
Codes map[low.KeyReference[string]]low.ValueReference[*Response]
|
||||
Default low.NodeReference[*Response]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
r.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
if utils.IsNodeMap(root) {
|
||||
codes, err := low.ExtractMapFlatNoLookup[*Response](root, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if codes != nil {
|
||||
r.Codes = codes
|
||||
}
|
||||
|
||||
def, derr := low.ExtractObject[*Response](DefaultLabel, root, idx)
|
||||
if derr != nil {
|
||||
return derr
|
||||
}
|
||||
if def.Value != nil {
|
||||
r.Default = def
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("responses build failed: vn node is not a map! line %d, col %d", root.Line, root.Column)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] {
|
||||
return low.FindItemInMap[*Response](code, r.Codes)
|
||||
}
|
||||
@@ -3,8 +3,21 @@
|
||||
|
||||
package v2
|
||||
|
||||
import "github.com/pb33f/libopenapi/datamodel/low"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
SecurityLabel = "security"
|
||||
)
|
||||
|
||||
type SecurityRequirement struct {
|
||||
Values low.NodeReference[[]low.ValueReference[string]]
|
||||
}
|
||||
|
||||
func (s *SecurityRequirement) Build(_ *yaml.Node, _ *index.SpecIndex) error {
|
||||
// not implemented.
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
|
||||
package v2
|
||||
|
||||
import "github.com/pb33f/libopenapi/datamodel/low"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type SecurityScheme struct {
|
||||
Type low.NodeReference[string]
|
||||
@@ -16,3 +20,10 @@ type SecurityScheme struct {
|
||||
Scopes low.NodeReference[*Scopes]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
ss.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
// TODO: scopes
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,27 +4,132 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/resolver"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error)
|
||||
|
||||
type Swagger struct {
|
||||
Swagger low.NodeReference[string]
|
||||
Swagger low.ValueReference[string]
|
||||
Info low.NodeReference[*base.Info]
|
||||
Host low.NodeReference[string]
|
||||
BasePath low.NodeReference[string]
|
||||
Schemes low.NodeReference[[]low.ValueReference[string]]
|
||||
Consumes low.NodeReference[[]low.ValueReference[string]]
|
||||
Produces low.NodeReference[[]low.ValueReference[string]]
|
||||
|
||||
// TODO: paths
|
||||
|
||||
Definitions low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
|
||||
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]]
|
||||
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
|
||||
SecurityDefinitions low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]]
|
||||
Security low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityRequirement]]
|
||||
Tags low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Tag]]
|
||||
Paths low.NodeReference[*Paths]
|
||||
Definitions low.NodeReference[*Definitions]
|
||||
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
|
||||
Parameters low.NodeReference[*ParameterDefinitions]
|
||||
ResponsesDefinitions low.NodeReference[*ResponsesDefinitions]
|
||||
Responses low.NodeReference[*Responses]
|
||||
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
|
||||
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
|
||||
ExternalDocs low.NodeReference[*base.ExternalDoc]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Index *index.SpecIndex
|
||||
}
|
||||
|
||||
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
||||
|
||||
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
|
||||
|
||||
// build an index
|
||||
idx := index.NewSpecIndex(info.RootNode)
|
||||
doc.Index = idx
|
||||
|
||||
// create resolver and check for circular references.
|
||||
resolve := resolver.NewResolver(idx)
|
||||
_ = resolve.CheckForCircularReferences()
|
||||
|
||||
var errors []error
|
||||
|
||||
extractionFuncs := []documentFunction{
|
||||
extractInfo,
|
||||
extractPaths,
|
||||
extractDefinitions,
|
||||
extractParamDefinitions,
|
||||
extractResponsesDefinitions,
|
||||
extractSecurityDefinitions,
|
||||
}
|
||||
doneChan := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
for i := range extractionFuncs {
|
||||
go extractionFuncs[i](info.RootNode, &doc, idx, doneChan, errChan)
|
||||
}
|
||||
completedExtractions := 0
|
||||
for completedExtractions < len(extractionFuncs) {
|
||||
select {
|
||||
case <-doneChan:
|
||||
completedExtractions++
|
||||
case e := <-errChan:
|
||||
errors = append(errors, e)
|
||||
}
|
||||
}
|
||||
|
||||
return &doc, errors
|
||||
}
|
||||
|
||||
func extractInfo(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Info = info
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Paths = paths
|
||||
c <- true
|
||||
}
|
||||
func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Definitions = def
|
||||
c <- true
|
||||
}
|
||||
func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Parameters = param
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.ResponsesDefinitions = resp
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.SecurityDefinitions = sec
|
||||
c <- true
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) {
|
||||
|
||||
// clean state
|
||||
doc := Document{Version: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||
|
||||
// build an index
|
||||
|
||||
@@ -31,7 +31,6 @@ func (ex *Example) FindExtension(ext string) *low.ValueReference[any] {
|
||||
|
||||
func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
ex.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
||||
|
||||
if vn != nil {
|
||||
|
||||
Reference in New Issue
Block a user