Files
libopenapi/datamodel/low/v2/path_item.go
Dave Shanley 6214babaec v2 low-level docs are now in also.
That is now 6/6! Time to tune up the README again next.
2022-09-21 09:24:40 -04:00

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
}