mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-07 12:37:48 +00:00
181 lines
4.3 KiB
Go
181 lines
4.3 KiB
Go
// 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"
|
|
"gopkg.in/yaml.v3"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// PathItem represents a low-level Swagger / OpenAPI 2 PathItem object.
|
|
//
|
|
// Describes the operations available on a single path. A Path Item may be empty, due to ACL constraints.
|
|
// The path itself is still exposed to the tooling, but will not know which operations and parameters
|
|
// are available.
|
|
// - https://swagger.io/specification/v2/#pathItemObject
|
|
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]
|
|
}
|
|
|
|
// FindExtension will attempt to locate an extension given a name.
|
|
func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
|
|
return low.FindItemInMap[any](ext, p.Extensions)
|
|
}
|
|
|
|
// Build will extract extensions, parameters and operations for all methods. Every method is handled
|
|
// asynchronously, in order to keep things moving quickly for complex operations.
|
|
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
|
|
}
|
|
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
|
|
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
|
|
// in swagger. Why this choice was made, I do not know.
|
|
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
|
|
rn := root.Content[i+1]
|
|
p.Ref = low.NodeReference[string]{
|
|
Value: rn.Value,
|
|
ValueNode: rn,
|
|
KeyNode: pathNode,
|
|
}
|
|
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)
|
|
|
|
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
|
|
}
|