Paths test coverage is completed.

Found some good gaps a holes in existing overage.
This commit is contained in:
Dave Shanley
2022-08-15 17:52:32 -04:00
parent 7ce5f5438f
commit 10bf40ad97
16 changed files with 861 additions and 85 deletions

View File

@@ -111,54 +111,3 @@ func TestCallback_Build_Using_InlineRef(t *testing.T) {
assert.Equal(t, "this is something", exp.Value.Post.Value.RequestBody.Value.Description.Value)
}
func TestCallback_Build_Using_Ref(t *testing.T) {
// first we need an index.
doc := `paths:
'/something/here':
post:
requestBody:
$ref: '#/components/requestBodies/rbOne'
responses:
"200":
$ref: '#/components/schemas/AThing'
components:
schemas:
AThing:
description: a thing.
type: string
requestBodies:
rbOne:
description: a request body.
type: string`
var idxNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &idxNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&idxNode)
yml := `'{$request.query.queryUrl}':
$ref: '#/paths/~1something~1here'`
var rootNode yaml.Node
mErr = yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var n Callback
err := low.BuildModel(rootNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(rootNode.Content[0], idx)
assert.NoError(t, err)
assert.Len(t, n.Expression.Value, 1)
exp := n.FindExpression("{$request.query.queryUrl}")
assert.NotNil(t, exp.Value)
assert.NotNil(t, exp.Value.Post.Value)
assert.Equal(t, "a request body.", exp.Value.Post.Value.RequestBody.Value.Description.Value)
okCode := exp.Value.Post.Value.Responses.Value.FindResponseByCode("200")
assert.Equal(t, "a thing.", okCode.Value.Description.Value)
}

View File

@@ -6,6 +6,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -53,6 +54,12 @@ func (h *Header) FindContent(ext string) *low.ValueReference[*MediaType] {
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
h.Extensions = low.ExtractExtensions(root)
// handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
if expNode != nil {
h.Example = low.ExtractExample(expNode, expLabel)
}
// handle examples if set.
exps, eErr := low.ExtractMap[*Example](ExamplesLabel, root, idx)
if eErr != nil {

View File

@@ -12,7 +12,6 @@ import (
)
func TestMediaType_Build(t *testing.T) {
yml := `schema:
type: string
example: hello
@@ -46,7 +45,6 @@ x-rock: and roll`
}
func TestMediaType_Build_Fail_Schema(t *testing.T) {
yml := `schema:
$ref: #bork`
@@ -60,11 +58,9 @@ func TestMediaType_Build_Fail_Schema(t *testing.T) {
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestMediaType_Build_Fail_Examples(t *testing.T) {
yml := `examples:
waff:
$ref: #bork`
@@ -83,7 +79,6 @@ func TestMediaType_Build_Fail_Examples(t *testing.T) {
}
func TestMediaType_Build_Fail_Encoding(t *testing.T) {
yml := `encoding:
wiff:
$ref: #bork`
@@ -98,5 +93,4 @@ func TestMediaType_Build_Fail_Encoding(t *testing.T) {
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
@@ -15,7 +18,7 @@ const (
)
type Operation struct {
Tags low.NodeReference[low.NodeReference[string]]
Tags low.NodeReference[[]low.ValueReference[string]]
Summary low.NodeReference[string]
Description low.NodeReference[string]
ExternalDocs low.NodeReference[*ExternalDoc]
@@ -30,6 +33,10 @@ type Operation struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (o *Operation) FindCallback(callback string) *low.ValueReference[*Callback] {
return low.FindItemInMap[*Callback](callback, o.Callbacks.Value)
}
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
o.Extensions = low.ExtractExtensions(root)
@@ -63,7 +70,7 @@ func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
// extract responses
respBody, respErr := low.ExtractObject[*Responses](ResponsesLabel, root, idx)
if respErr != nil {
return rErr
return respErr
}
o.Responses = respBody

View File

@@ -0,0 +1,193 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestOperation_Build(t *testing.T) {
yml := `tags:
- meddy
- maddy
summary: building a business
description: takes hard work
externalDocs:
description: some docs
operationId: beefyBeef
parameters:
- name: pizza
- name: cake
requestBody:
description: a requestBody
responses:
"200":
description: an OK response
callbacks:
niceCallback:
ohISee:
description: a nice callback
deprecated: true
security:
- books:
- read:books
- write:books
servers:
- url: https://pb33f.io`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.Len(t, n.Tags.Value, 2)
assert.Equal(t, "building a business", n.Summary.Value)
assert.Equal(t, "takes hard work", n.Description.Value)
assert.Equal(t, "some docs", n.ExternalDocs.Value.Description.Value)
assert.Equal(t, "beefyBeef", n.OperationId.Value)
assert.Len(t, n.Parameters.Value, 2)
assert.Equal(t, "a requestBody", n.RequestBody.Value.Description.Value)
assert.Len(t, n.Responses.Value.Codes, 1)
assert.Equal(t, "an OK response", n.Responses.Value.FindResponseByCode("200").Value.Description.Value)
assert.Len(t, n.Callbacks.Value, 1)
assert.Equal(t, "a nice callback",
n.FindCallback("niceCallback").Value.FindExpression("ohISee").Value.Description.Value)
assert.True(t, n.Deprecated.Value)
assert.Len(t, n.Security.Value.ValueRequirements, 1)
assert.Len(t, n.Security.Value.FindRequirement("books"), 2)
assert.Equal(t, "read:books", n.Security.Value.FindRequirement("books")[0].Value)
assert.Equal(t, "write:books", n.Security.Value.FindRequirement("books")[1].Value)
assert.Len(t, n.Servers.Value, 1)
assert.Equal(t, "https://pb33f.io", n.Servers.Value[0].Value.URL.Value)
}
func TestOperation_Build_FailDocs(t *testing.T) {
yml := `externalDocs:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestOperation_Build_FailParams(t *testing.T) {
yml := `parameters:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestOperation_Build_FailRequestBody(t *testing.T) {
yml := `requestBody:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestOperation_Build_FailResponses(t *testing.T) {
yml := `responses:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestOperation_Build_FailCallbacks(t *testing.T) {
yml := `callbacks:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestOperation_Build_FailSecurity(t *testing.T) {
yml := `security:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestOperation_Build_FailServers(t *testing.T) {
yml := `servers:
$ref: #borked`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Operation
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
@@ -37,13 +40,17 @@ func (p *Parameter) FindExample(eType string) *low.ValueReference[*Example] {
return low.FindItemInMap[*Example](eType, p.Examples.Value)
}
func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, p.Extensions)
}
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = low.ExtractExtensions(root)
// handle example if set.
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
if expNode != nil {
p.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
p.Example = low.ExtractExample(expNode, expLabel)
}
// handle schema

View File

@@ -0,0 +1,169 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestParameter_Build(t *testing.T) {
yml := `description: michelle, meddy and maddy
required: true
deprecated: false
name: happy
in: path
allowEmptyValue: false
style: beautiful
explode: true
allowReserved: true
schema:
type: object
description: my triple M, my loves
properties:
michelle:
type: string
description: she is my heart.
meddy:
type: string
description: she is my song.
maddy:
type: string
description: he is my champion.
x-family-love: strong
example:
michelle: my love.
maddy: my champion.
meddy: my song.
content:
family/love:
schema:
type: string
description: family love.`
var idxNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&idxNode)
var n Parameter
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
assert.Equal(t, "michelle, meddy and maddy", n.Description.Value)
assert.True(t, n.AllowReserved.Value)
assert.True(t, n.Explode.Value)
assert.True(t, n.Required.Value)
assert.False(t, n.Deprecated.Value)
assert.Equal(t, "happy", n.Name.Value)
assert.Equal(t, "path", n.In.Value)
assert.NotNil(t, n.Schema.Value)
assert.Equal(t, "my triple M, my loves", n.Schema.Value.Description.Value)
assert.NotNil(t, n.Schema.Value.Properties.Value)
assert.Equal(t, "she is my heart.", n.Schema.Value.FindProperty("michelle").Value.Description.Value)
assert.Equal(t, "she is my song.", n.Schema.Value.FindProperty("meddy").Value.Description.Value)
assert.Equal(t, "he is my champion.", n.Schema.Value.FindProperty("maddy").Value.Description.Value)
if v, ok := n.Example.Value.(map[string]interface{}); ok {
assert.Equal(t, "my love.", v["michelle"])
assert.Equal(t, "my song.", v["meddy"])
assert.Equal(t, "my champion.", v["maddy"])
} else {
assert.Fail(t, "should not fail")
}
con := n.FindContent("family/love").Value
assert.NotNil(t, con)
assert.Equal(t, "family love.", con.Schema.Value.Description.Value)
assert.Nil(t, n.FindContent("unknown"))
ext := n.FindExtension("x-family-love").Value
assert.Equal(t, "strong", ext)
}
func TestParameter_Build_Success_Examples(t *testing.T) {
yml := `examples:
family:
value:
michelle: my love.
maddy: my champion.
meddy: my song.`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Parameter
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
exp := n.FindExample("family").Value
assert.NotNil(t, exp)
if v, ok := exp.Value.Value.(map[string]interface{}); ok {
assert.Equal(t, "my love.", v["michelle"])
assert.Equal(t, "my song.", v["meddy"])
assert.Equal(t, "my champion.", v["maddy"])
} else {
assert.Fail(t, "should not fail")
}
}
func TestParameter_Build_Fail_Examples(t *testing.T) {
yml := `examples:
family:
$ref: I AM BORKED`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Parameter
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestParameter_Build_Fail_Schema(t *testing.T) {
yml := `schema:
$ref: I will fail.`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Parameter
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestParameter_Build_Fail_Content(t *testing.T) {
yml := `content:
ohMyStars:
$ref: fail!`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Parameter
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
@@ -36,6 +39,10 @@ func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
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
@@ -56,12 +63,20 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
currentNode = pathNode
continue
}
var path = PathItem{}
err := low.BuildModel(pathNode, &path)
if err != nil {
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
r := low.LocateRefNode(pathNode, idx)
if r != nil {
pathNode = r
} 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)
}
}
err = path.Build(pathNode, idx)
var path = PathItem{}
_ = low.BuildModel(pathNode, &path)
err := path.Build(pathNode, idx)
if err != nil {
return err
}
@@ -92,11 +107,15 @@ type PathItem struct {
Head low.NodeReference[*Operation]
Patch low.NodeReference[*Operation]
Trace low.NodeReference[*Operation]
Servers []low.NodeReference[*Server]
Parameters []low.NodeReference[*Parameter]
Servers low.NodeReference[[]low.ValueReference[*Server]]
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
@@ -107,12 +126,16 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
var ops []low.NodeReference[*Operation]
if ok, _, _ := utils.IsNodeRefValue(root); ok {
r := low.LocateRefNode(root, idx)
if r != nil {
root = r
} else {
return nil
// 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,
}
}
@@ -121,6 +144,10 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
skip = true
continue
}
if strings.HasPrefix(strings.ToLower(pathNode.Value), "parameters") {
skip = true
continue
}
if skip {
skip = false
continue
@@ -130,9 +157,42 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
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
case TraceLabel:
break
default:
continue // ignore everything else.
}
var op Operation
wg.Add(1)
if ok, _, _ := utils.IsNodeRefValue(pathNode); ok {
r := low.LocateRefNode(pathNode, idx)
if r != nil {
pathNode = r
} 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]{
@@ -163,10 +223,6 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
}
}
if len(ops) > 0 {
//wg.Wait()
}
//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)
@@ -215,5 +271,10 @@ allDone:
}
}
// make sure we don't exit before the path is finished building.
if len(ops) > 0 {
wg.Wait()
}
return nil
}

View File

@@ -0,0 +1,338 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestPaths_Build(t *testing.T) {
yml := `"/some/path":
get:
description: get method
post:
description: post method
put:
description: put method
delete:
description: delete method
options:
description: options method
patch:
description: patch method
head:
description: head method
trace:
description: trace method
servers:
url: https://pb33f.io
parameters:
- name: hello
x-cake: yummy
x-milk: cold`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
path := n.FindPath("/some/path").Value
assert.NotNil(t, path)
assert.Equal(t, "get method", path.Get.Value.Description.Value)
assert.Equal(t, "yummy", path.FindExtension("x-cake").Value)
assert.Equal(t, "post method", path.Post.Value.Description.Value)
assert.Equal(t, "put method", path.Put.Value.Description.Value)
assert.Equal(t, "patch method", path.Patch.Value.Description.Value)
assert.Equal(t, "delete method", path.Delete.Value.Description.Value)
assert.Equal(t, "head method", path.Head.Value.Description.Value)
assert.Equal(t, "trace method", path.Trace.Value.Description.Value)
assert.Len(t, path.Parameters.Value, 1)
assert.Equal(t, "cold", n.FindExtension("x-milk").Value)
assert.Equal(t, "hello", path.Parameters.Value[0].Value.Name.Value)
}
func TestPaths_Build_Fail(t *testing.T) {
yml := `"/some/path":
$ref: $bork`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(&idxNode, &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestPaths_Build_FailRef(t *testing.T) {
// this is kinda nuts, and, it's completely illegal, but you never know!
yml := `"/some/path":
description: this is some path
get:
description: bloody dog ate my biscuit.
post:
description: post method
"/another/path":
$ref: '#/~1some~1path'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
somePath := n.FindPath("/some/path").Value
anotherPath := n.FindPath("/another/path").Value
badPath := n.FindPath("/does/not/exist")
assert.NotNil(t, somePath)
assert.NotNil(t, anotherPath)
assert.Nil(t, badPath)
assert.Equal(t, "this is some path", somePath.Description.Value)
assert.Equal(t, "bloody dog ate my biscuit.", somePath.Get.Value.Description.Value)
assert.Equal(t, "post method", somePath.Post.Value.Description.Value)
assert.Equal(t, "bloody dog ate my biscuit.", anotherPath.Get.Value.Description.Value)
}
func TestPaths_Build_FailRefDeadEnd(t *testing.T) {
// this is nuts.
yml := `"/no/path":
get:
$ref: '#/nowhere'
"/some/path":
get:
$ref: '#/~1some~1path/get'
"/another/path":
$ref: '#/~1some~1path'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestPaths_Build_SuccessRef(t *testing.T) {
// this is kinda nuts, it's also not illegal, however the mechanics still need to work.
yml := `"/some/path":
description: this is some path
get:
$ref: '#/~1another~1path/get'
post:
description: post method
"/another/path":
description: this is another path of some kind.
get:
description: get method from /another/path`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
somePath := n.FindPath("/some/path").Value
anotherPath := n.FindPath("/another/path").Value
badPath := n.FindPath("/does/not/exist")
assert.NotNil(t, somePath)
assert.NotNil(t, anotherPath)
assert.Nil(t, badPath)
assert.Equal(t, "this is some path", somePath.Description.Value)
assert.Equal(t, "get method from /another/path", somePath.Get.Value.Description.Value)
assert.Equal(t, "post method", somePath.Post.Value.Description.Value)
assert.Equal(t, "get method from /another/path", anotherPath.Get.Value.Description.Value)
}
func TestPaths_Build_BadParams(t *testing.T) {
yml := `"/some/path":
parameters:
this: shouldFail`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestPaths_Build_BadRef(t *testing.T) {
// this is kinda nuts, it's also not illegal, however the mechanics still need to work.
yml := `"/some/path":
description: this is some path
get:
$ref: '#/no-where'
post:
description: post method
"/another/path":
description: this is another path of some kind.
get:
description: get method from /another/path`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestPathItem_Build_GoodRef(t *testing.T) {
// this is kinda nuts, it's also not illegal, however the mechanics still need to work.
yml := `"/some/path":
description: this is some path
get:
$ref: '#/~1another~1path/get'
post:
description: post method
"/another/path":
description: this is another path of some kind.
get:
$ref: '#/~1cakes/get'
"/cakes":
description: cakes are awesome
get:
description: get method from /cakes`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
}
func TestPathItem_Build_BadRef(t *testing.T) {
// this is kinda nuts, it's also not illegal, however the mechanics still need to work.
yml := `"/some/path":
description: this is some path
get:
$ref: '#/~1another~1path/get'
post:
description: post method
"/another/path":
description: this is another path of some kind.
get:
$ref: '#/~1cakes/NotFound'
"/cakes":
description: cakes are awesome
get:
description: get method from /cakes`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestPathNoOps(t *testing.T) {
// this is kinda nuts, it's also not illegal, however the mechanics still need to work.
yml := `"/some/path":
"/cakes":`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Paths
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.NoError(t, err)
}
func TestPathItem_Build_Using_Ref(t *testing.T) {
// first we need an index.
doc := `paths:
'/something/here':
post:
description: there is something here!`
var idxNode yaml.Node
mErr := yaml.Unmarshal([]byte(doc), &idxNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&idxNode)
yml := `"/some/path":
description: this is some path
get:
$ref: '#/paths/~1something~1here/post'`
var rootNode yaml.Node
mErr = yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var n Paths
err := low.BuildModel(rootNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(rootNode.Content[0], idx)
assert.NoError(t, err)
somePath := n.FindPath("/a/path")
assert.Nil(t, somePath)
somePath = n.FindPath("/some/path")
assert.NotNil(t, somePath.Value)
assert.Equal(t, "this is some path", somePath.Value.Description.Value)
assert.Equal(t, "there is something here!", somePath.Value.Get.Value.Description.Value)
}

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (
@@ -26,7 +29,7 @@ type SecurityScheme struct {
}
type SecurityRequirement struct {
Value []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
ValueRequirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]]
}
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
@@ -44,7 +47,7 @@ func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
}
func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] {
for _, r := range sr.Value {
for _, r := range sr.ValueRequirements {
for k, v := range r.Value {
if k.Value == name {
return v
@@ -89,7 +92,7 @@ func (sr *SecurityRequirement) Build(root *yaml.Node, idx *index.SpecIndex) erro
})
}
}
sr.Value = requirements
sr.ValueRequirements = requirements
}
return nil

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v3
import (

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package low
import (
@@ -49,8 +52,9 @@ func LocateRefNode(root *yaml.Node, idx *index.SpecIndex) *yaml.Node {
}
// cant be found? last resort is to try a path lookup
cleaned := strings.ReplaceAll(rv, "~1", "/")
cleaned = strings.ReplaceAll(cleaned, "#/paths/", "")
cleaned := strings.ReplaceAll(rv, "#/paths/", "")
cleaned = strings.ReplaceAll(cleaned, "/", ".")
cleaned = strings.ReplaceAll(cleaned, "~1", "/")
path, err := yamlpath.NewPath(fmt.Sprintf("$.paths.%s", cleaned))
if err == nil {
nodes, fErr := path.Find(idx.GetRootNode())
@@ -151,6 +155,9 @@ func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *ind
}
var items []ValueReference[T]
if vn != nil && ln != nil {
if !utils.IsNodeArray(vn) {
return []ValueReference[T]{}, nil, nil, fmt.Errorf("array build failed, input is not an array, line %d, column %d", vn.Line, vn.Column)
}
for _, node := range vn.Content {
if rf, _, _ := utils.IsNodeRefValue(node); rf {
ref := LocateRefNode(node, idx)
@@ -179,16 +186,39 @@ func ExtractArray[T Buildable[N], N any](label string, root *yaml.Node, idx *ind
return items, ln, vn, nil
}
func ExtractExample(expNode, expLabel *yaml.Node) NodeReference[any] {
ref := NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
if utils.IsNodeMap(expNode) {
var decoded map[string]interface{}
_ = expNode.Decode(&decoded)
ref.Value = decoded
}
if utils.IsNodeArray(expNode) {
var decoded []interface{}
_ = expNode.Decode(&decoded)
ref.Value = decoded
}
return ref
}
func ExtractMapFlatNoLookup[PT Buildable[N], N any](root *yaml.Node, idx *index.SpecIndex) (map[KeyReference[string]]ValueReference[PT], error) {
valueMap := make(map[KeyReference[string]]ValueReference[PT])
if utils.IsNodeMap(root) {
var currentKey *yaml.Node
skip := false
for i, node := range root.Content {
if strings.HasPrefix(strings.ToLower(node.Value), "x-") {
skip = true
continue
}
if skip {
skip = false
continue
}
if i%2 == 0 {
currentKey = node
continue
}
// if value is a reference, we have to look it up in the index!
if h, _, _ := utils.IsNodeRefValue(node); h {
ref := LocateRefNode(node, idx)

View File

@@ -1,3 +1,6 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package low
import (
@@ -23,6 +26,14 @@ func BuildModel(node *yaml.Node, model interface{}) error {
fName := v.Type().Field(i).Name
if fName == "Extensions" {
continue // internal construct
}
if fName == "PathItems" {
continue // internal construct
}
// we need to find a matching field in the YAML, the cases may be off, so take no chances.
cases := []utils.Case{utils.PascalCase, utils.CamelCase, utils.ScreamingSnakeCase,
utils.SnakeCase, utils.KebabCase, utils.RegularCase}

View File

@@ -1722,7 +1722,7 @@ func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
friendlySearch = strings.ReplaceAll(friendlySearch, "~1", "/")
path, _ := yamlpath.NewPath(friendlySearch)
res, _ := path.Find(index.root)

View File

@@ -131,6 +131,7 @@ func TestCreateDocument_Tags(t *testing.T) {
}
func TestCreateDocument_Paths(t *testing.T) {
doc := doc
assert.Len(t, doc.Paths.Value.PathItems, 6)
burgerId := doc.Paths.Value.FindPath("/burgers/{burgerId}")
assert.NotNil(t, burgerId)
@@ -245,7 +246,7 @@ func TestCreateDocument_Paths(t *testing.T) {
// check security requirements
security := burgersPost.Security.Value
assert.NotNil(t, security)
assert.Len(t, security.Value, 1)
assert.Len(t, security.ValueRequirements, 1)
oAuthReq := security.FindRequirement("OAuthScheme")
assert.Len(t, oAuthReq, 2)
@@ -372,7 +373,7 @@ func TestCreateDocument_Components_Links(t *testing.T) {
func TestCreateDocument_Doc_Security(t *testing.T) {
security := doc.Security.Value
assert.NotNil(t, security)
assert.Len(t, security.Value, 1)
assert.Len(t, security.ValueRequirements, 1)
oAuth := security.FindRequirement("OAuthScheme")
assert.Len(t, oAuth, 2)