mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Refactor Paths to OrderedMap.
This commit is contained in:
@@ -4,15 +4,15 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v2low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
)
|
||||
|
||||
// Paths represents a high-level Swagger / OpenAPI Paths object, backed by a low-level one.
|
||||
type Paths struct {
|
||||
PathItems map[string]*PathItem
|
||||
PathItems orderedmap.Map[string, *PathItem]
|
||||
Extensions map[string]any
|
||||
low *v2low.Paths
|
||||
}
|
||||
@@ -22,19 +22,19 @@ func NewPaths(paths *v2low.Paths) *Paths {
|
||||
p := new(Paths)
|
||||
p.low = paths
|
||||
p.Extensions = high.ExtractExtensions(paths.Extensions)
|
||||
pathItems := make(map[string]*PathItem)
|
||||
pathItems := orderedmap.New[string, *PathItem]()
|
||||
|
||||
translateFunc := func(key low.KeyReference[string], value low.ValueReference[*v2low.PathItem]) (asyncResult[*PathItem], error) {
|
||||
translateFunc := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v2low.PathItem]]) (asyncResult[*PathItem], error) {
|
||||
return asyncResult[*PathItem]{
|
||||
key: key.Value,
|
||||
result: NewPathItem(value.Value),
|
||||
key: pair.Key().Value,
|
||||
result: NewPathItem(pair.Value().Value),
|
||||
}, nil
|
||||
}
|
||||
resultFunc := func(result asyncResult[*PathItem]) error {
|
||||
pathItems[result.key] = result.result
|
||||
pathItems.Set(result.key, result.result)
|
||||
return nil
|
||||
}
|
||||
_ = datamodel.TranslateMapParallel[low.KeyReference[string], low.ValueReference[*v2low.PathItem], asyncResult[*PathItem]](
|
||||
_ = orderedmap.TranslateMapParallel[low.KeyReference[string], low.ValueReference[*v2low.PathItem], asyncResult[*PathItem]](
|
||||
paths.PathItems, translateFunc, resultFunc,
|
||||
)
|
||||
p.PathItems = pathItems
|
||||
|
||||
@@ -204,7 +204,7 @@ func TestNewSwaggerDocument_Paths(t *testing.T) {
|
||||
highDoc := NewSwaggerDocument(doc)
|
||||
assert.Len(t, highDoc.Paths.PathItems, 15)
|
||||
|
||||
upload := highDoc.Paths.PathItems["/pet/{petId}/uploadImage"]
|
||||
upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage")
|
||||
assert.Equal(t, "man", upload.Extensions["x-potato"])
|
||||
assert.Nil(t, upload.Get)
|
||||
assert.Nil(t, upload.Put)
|
||||
@@ -262,7 +262,7 @@ func TestNewSwaggerDocument_Responses(t *testing.T) {
|
||||
|
||||
initTest()
|
||||
highDoc := NewSwaggerDocument(doc)
|
||||
upload := highDoc.Paths.PathItems["/pet/{petId}/uploadImage"].Post
|
||||
upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage").Post
|
||||
|
||||
assert.Len(t, upload.Responses.Codes, 1)
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
v2 "github.com/pb33f/libopenapi/datamodel/high/v2"
|
||||
lowv2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -330,7 +331,7 @@ func TestNewDocument_Paths(t *testing.T) {
|
||||
}
|
||||
|
||||
func testBurgerShop(t *testing.T, h *Document, checkLines bool) {
|
||||
burgersOp := h.Paths.PathItems["/burgers"]
|
||||
burgersOp := h.Paths.PathItems.GetOrZero("/burgers")
|
||||
|
||||
assert.Len(t, burgersOp.GetOperations(), 1)
|
||||
assert.Equal(t, "meaty", burgersOp.Extensions["x-burger-meta"])
|
||||
@@ -410,7 +411,7 @@ func TestAsanaAsDoc(t *testing.T) {
|
||||
}
|
||||
d := NewDocument(lowDoc)
|
||||
assert.NotNil(t, d)
|
||||
assert.Equal(t, 118, len(d.Paths.PathItems))
|
||||
assert.Equal(t, 118, orderedmap.Len(d.Paths.PathItems))
|
||||
}
|
||||
|
||||
func TestDigitalOceanAsDocFromSHA(t *testing.T) {
|
||||
@@ -434,7 +435,7 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) {
|
||||
}
|
||||
d := NewDocument(lowDoc)
|
||||
assert.NotNil(t, d)
|
||||
assert.Equal(t, 183, len(d.Paths.PathItems))
|
||||
assert.Equal(t, 183, orderedmap.Len(d.Paths.PathItems))
|
||||
|
||||
}
|
||||
|
||||
@@ -448,7 +449,7 @@ func TestPetstoreAsDoc(t *testing.T) {
|
||||
}
|
||||
d := NewDocument(lowDoc)
|
||||
assert.NotNil(t, d)
|
||||
assert.Equal(t, 13, len(d.Paths.PathItems))
|
||||
assert.Equal(t, 13, orderedmap.Len(d.Paths.PathItems))
|
||||
}
|
||||
|
||||
func TestCircularReferencesDoc(t *testing.T) {
|
||||
@@ -532,7 +533,7 @@ func TestDocument_MarshalJSON(t *testing.T) {
|
||||
lowDoc, _ = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration())
|
||||
newDoc := NewDocument(lowDoc)
|
||||
|
||||
assert.Equal(t, len(newDoc.Paths.PathItems), len(highDoc.Paths.PathItems))
|
||||
assert.Equal(t, orderedmap.Len(newDoc.Paths.PathItems), orderedmap.Len(highDoc.Paths.PathItems))
|
||||
assert.Equal(t, len(newDoc.Components.Schemas), len(highDoc.Components.Schemas))
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func TestMediaType_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
// create a new document and extract a media type object from it.
|
||||
d := NewDocument(lowDoc)
|
||||
mt := d.Paths.PathItems["/pet"].Put.RequestBody.Content["application/json"]
|
||||
mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content["application/json"]
|
||||
|
||||
// render out the media type
|
||||
yml, _ := mt.Render()
|
||||
@@ -118,7 +118,7 @@ func TestMediaType_MarshalYAML(t *testing.T) {
|
||||
|
||||
// create a new document and extract a media type object from it.
|
||||
d := NewDocument(lowDoc)
|
||||
mt := d.Paths.PathItems["/pet"].Put.RequestBody.Content["application/json"]
|
||||
mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content["application/json"]
|
||||
|
||||
// render out the media type
|
||||
yml, _ := mt.Render()
|
||||
|
||||
@@ -5,9 +5,11 @@ package v3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"io/ioutil"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
)
|
||||
|
||||
// An example of how to create a new high-level OpenAPI 3+ document from an OpenAPI specification.
|
||||
@@ -36,6 +38,6 @@ func Example_createHighLevelOpenAPIDocument() {
|
||||
|
||||
// Print out some details
|
||||
fmt.Printf("Petstore contains %d paths and %d component schemas",
|
||||
len(doc.Paths.PathItems), len(doc.Components.Schemas))
|
||||
orderedmap.Len(doc.Paths.PathItems), len(doc.Components.Schemas))
|
||||
// Output: Petstore contains 13 paths and 8 component schemas
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ package v3
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -21,8 +21,8 @@ import (
|
||||
// constraints.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#paths-object
|
||||
type Paths struct {
|
||||
PathItems map[string]*PathItem `json:"-" yaml:"-"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
PathItems orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *v3low.Paths
|
||||
}
|
||||
|
||||
@@ -31,21 +31,21 @@ func NewPaths(paths *v3low.Paths) *Paths {
|
||||
p := new(Paths)
|
||||
p.low = paths
|
||||
p.Extensions = high.ExtractExtensions(paths.Extensions)
|
||||
items := make(map[string]*PathItem)
|
||||
items := orderedmap.New[string, *PathItem]()
|
||||
|
||||
type pathItemResult struct {
|
||||
key string
|
||||
value *PathItem
|
||||
}
|
||||
|
||||
translateFunc := func(key low.KeyReference[string], value low.ValueReference[*v3low.PathItem]) (pathItemResult, error) {
|
||||
return pathItemResult{key: key.Value, value: NewPathItem(value.Value)}, nil
|
||||
translateFunc := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v3low.PathItem]]) (pathItemResult, error) {
|
||||
return pathItemResult{key: pair.Key().Value, value: NewPathItem(pair.Value().Value)}, nil
|
||||
}
|
||||
resultFunc := func(value pathItemResult) error {
|
||||
items[value.key] = value.value
|
||||
items.Set(value.key, value.value)
|
||||
return nil
|
||||
}
|
||||
_ = datamodel.TranslateMapParallel[low.KeyReference[string], low.ValueReference[*v3low.PathItem], pathItemResult](
|
||||
_ = orderedmap.TranslateMapParallel[low.KeyReference[string], low.ValueReference[*v3low.PathItem], pathItemResult](
|
||||
paths.PathItems, translateFunc, resultFunc,
|
||||
)
|
||||
p.PathItems = items
|
||||
@@ -84,7 +84,9 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
|
||||
}
|
||||
var mapped []*pathItem
|
||||
|
||||
for k, pi := range p.PathItems {
|
||||
action := func(pair orderedmap.Pair[string, *PathItem]) error {
|
||||
k := pair.Key()
|
||||
pi := pair.Value()
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
if p.low != nil {
|
||||
lpi := p.low.FindPath(k)
|
||||
@@ -93,7 +95,9 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, nil})
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.For[string, *PathItem](p.PathItems, action)
|
||||
|
||||
nb := high.NewNodeBuilder(p, p.low)
|
||||
extNode := nb.Render()
|
||||
@@ -138,7 +142,9 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
|
||||
}
|
||||
var mapped []*pathItem
|
||||
|
||||
for k, pi := range p.PathItems {
|
||||
action := func(pair orderedmap.Pair[string, *PathItem]) error {
|
||||
k := pair.Key()
|
||||
pi := pair.Value()
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
if p.low != nil {
|
||||
lpi := p.low.FindPath(k)
|
||||
@@ -147,7 +153,9 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, nil})
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.For[string, *PathItem](p.PathItems, action)
|
||||
|
||||
nb := high.NewNodeBuilder(p, p.low)
|
||||
nb.Resolve = true
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestPaths_MarshalYAML(t *testing.T) {
|
||||
|
||||
// mutate
|
||||
deprecated := true
|
||||
high.PathItems["/beer"].Get.Deprecated = &deprecated
|
||||
high.PathItems.GetOrZero("/beer").Get.Deprecated = &deprecated
|
||||
|
||||
yml = `/foo/bar/bizzle:
|
||||
get:
|
||||
@@ -100,7 +100,7 @@ func TestPaths_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
// mutate
|
||||
deprecated := true
|
||||
high.PathItems["/beer"].Get.Deprecated = &deprecated
|
||||
high.PathItems.GetOrZero("/beer").Get.Deprecated = &deprecated
|
||||
|
||||
yml = `/foo/bar/bizzle:
|
||||
get:
|
||||
|
||||
@@ -6,6 +6,7 @@ package v2
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -13,13 +14,14 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Paths represents a low-level Swagger / OpenAPI Paths object.
|
||||
type Paths struct {
|
||||
PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem]
|
||||
PathItems orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
@@ -29,23 +31,30 @@ func (p *Paths) GetExtensions() map[low.KeyReference[string]]low.ValueReference[
|
||||
}
|
||||
|
||||
// FindPath attempts to locate a PathItem instance, given a path key.
|
||||
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
|
||||
for k, j := range p.PathItems {
|
||||
if k.Value == path {
|
||||
return &j
|
||||
func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) {
|
||||
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error {
|
||||
if pair.Key().Value == path {
|
||||
result = pair.ValuePtr()
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
|
||||
return result
|
||||
}
|
||||
|
||||
// FindPathAndKey attempts to locate a PathItem instance, given a path key.
|
||||
func (p *Paths) FindPathAndKey(path string) (*low.KeyReference[string], *low.ValueReference[*PathItem]) {
|
||||
for k, j := range p.PathItems {
|
||||
if k.Value == path {
|
||||
return &k, &j
|
||||
func (p *Paths) FindPathAndKey(path string) (key *low.KeyReference[string], value *low.ValueReference[*PathItem]) {
|
||||
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error {
|
||||
if pair.Key().Value == path {
|
||||
key = pair.KeyPtr()
|
||||
value = pair.ValuePtr()
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil, nil
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
|
||||
return key, value
|
||||
}
|
||||
|
||||
// FindExtension will attempt to locate an extension value given a name.
|
||||
@@ -68,7 +77,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
currentNode *yaml.Node
|
||||
pathNode *yaml.Node
|
||||
}
|
||||
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
||||
pathsMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*PathItem]]()
|
||||
in := make(chan buildInput)
|
||||
out := make(chan pathBuildResult)
|
||||
done := make(chan struct{})
|
||||
@@ -115,7 +124,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
pathsMap[result.key] = result.value
|
||||
pathsMap.Set(result.key, result.value)
|
||||
}
|
||||
close(done)
|
||||
wg.Done()
|
||||
@@ -154,14 +163,19 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
// Hash will return a consistent SHA256 Hash of the PathItem object
|
||||
func (p *Paths) Hash() [32]byte {
|
||||
var f []string
|
||||
l := make([]string, len(p.PathItems))
|
||||
l := make([]string, orderedmap.Len(p.PathItems))
|
||||
keys := make(map[string]low.ValueReference[*PathItem])
|
||||
z := 0
|
||||
for k := range p.PathItems {
|
||||
keys[k.Value] = p.PathItems[k]
|
||||
l[z] = k.Value
|
||||
|
||||
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error {
|
||||
k := pair.Key().Value
|
||||
keys[k] = pair.Value()
|
||||
l[z] = k
|
||||
z++
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
|
||||
|
||||
sort.Strings(l)
|
||||
for k := range l {
|
||||
f = append(f, low.GenerateHashString(keys[l[k]].Value))
|
||||
|
||||
@@ -6,6 +6,7 @@ package v3
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -24,29 +26,36 @@ import (
|
||||
// constraints.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#paths-object
|
||||
type Paths struct {
|
||||
PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem]
|
||||
PathItems orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
// FindPath will attempt to locate a PathItem using the provided path string.
|
||||
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
|
||||
for k, j := range p.PathItems {
|
||||
if k.Value == path {
|
||||
return &j
|
||||
func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) {
|
||||
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error {
|
||||
if pair.Key().Value == path {
|
||||
result = pair.ValuePtr()
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
|
||||
return result
|
||||
}
|
||||
|
||||
// FindPathAndKey attempts to locate a PathItem instance, given a path key.
|
||||
func (p *Paths) FindPathAndKey(path string) (*low.KeyReference[string], *low.ValueReference[*PathItem]) {
|
||||
for k, j := range p.PathItems {
|
||||
if k.Value == path {
|
||||
return &k, &j
|
||||
func (p *Paths) FindPathAndKey(path string) (key *low.KeyReference[string], value *low.ValueReference[*PathItem]) {
|
||||
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error {
|
||||
if pair.Key().Value == path {
|
||||
key = pair.KeyPtr()
|
||||
value = pair.ValuePtr()
|
||||
return io.EOF
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return nil, nil
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
|
||||
return key, value
|
||||
}
|
||||
|
||||
// FindExtension will attempt to locate an extension using the specified string.
|
||||
@@ -75,7 +84,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
currentNode *yaml.Node
|
||||
pathNode *yaml.Node
|
||||
}
|
||||
pathsMap := make(map[low.KeyReference[string]]low.ValueReference[*PathItem])
|
||||
pathsMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*PathItem]]()
|
||||
in := make(chan buildInput)
|
||||
out := make(chan buildResult)
|
||||
done := make(chan struct{})
|
||||
@@ -122,7 +131,7 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
pathsMap[result.key] = result.value
|
||||
pathsMap.Set(result.key, result.value)
|
||||
}
|
||||
close(done)
|
||||
wg.Done()
|
||||
@@ -185,14 +194,19 @@ func (p *Paths) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
|
||||
// Hash will return a consistent SHA256 Hash of the PathItem object
|
||||
func (p *Paths) Hash() [32]byte {
|
||||
var f []string
|
||||
l := make([]string, len(p.PathItems))
|
||||
l := make([]string, orderedmap.Len(p.PathItems))
|
||||
keys := make(map[string]low.ValueReference[*PathItem])
|
||||
z := 0
|
||||
for k := range p.PathItems {
|
||||
keys[k.Value] = p.PathItems[k]
|
||||
l[z] = k.Value
|
||||
|
||||
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error {
|
||||
k := pair.Key().Value
|
||||
keys[k] = pair.Value()
|
||||
l[z] = k
|
||||
z++
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
|
||||
|
||||
sort.Strings(l)
|
||||
for k := range l {
|
||||
f = append(f, fmt.Sprintf("%s-%s", l[k], low.GenerateHashString(keys[l[k]].Value)))
|
||||
|
||||
@@ -5,12 +5,14 @@ package libopenapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
@@ -47,7 +49,7 @@ func ExampleNewDocument_fromOpenAPI3Document() {
|
||||
}
|
||||
|
||||
// get a count of the number of paths and schemas.
|
||||
paths := len(v3Model.Model.Paths.PathItems)
|
||||
paths := orderedmap.Len(v3Model.Model.Paths.PathItems)
|
||||
schemas := len(v3Model.Model.Components.Schemas)
|
||||
|
||||
// print the number of paths and schemas in the document
|
||||
@@ -153,7 +155,7 @@ func ExampleNewDocument_fromSwaggerDocument() {
|
||||
}
|
||||
|
||||
// get a count of the number of paths and schemas.
|
||||
paths := len(v2Model.Model.Paths.PathItems)
|
||||
paths := orderedmap.Len(v2Model.Model.Paths.PathItems)
|
||||
schemas := len(v2Model.Model.Definitions.Definitions)
|
||||
|
||||
// print the number of paths and schemas in the document
|
||||
@@ -184,7 +186,7 @@ func ExampleNewDocument_fromUnknownVersion() {
|
||||
errors = errs
|
||||
}
|
||||
if len(errors) <= 0 {
|
||||
paths = len(v3Model.Model.Paths.PathItems)
|
||||
paths = orderedmap.Len(v3Model.Model.Paths.PathItems)
|
||||
schemas = len(v3Model.Model.Components.Schemas)
|
||||
}
|
||||
}
|
||||
@@ -194,7 +196,7 @@ func ExampleNewDocument_fromUnknownVersion() {
|
||||
errors = errs
|
||||
}
|
||||
if len(errors) <= 0 {
|
||||
paths = len(v2Model.Model.Paths.PathItems)
|
||||
paths = orderedmap.Len(v2Model.Model.Paths.PathItems)
|
||||
schemas = len(v2Model.Model.Definitions.Definitions)
|
||||
}
|
||||
}
|
||||
@@ -641,10 +643,10 @@ func ExampleNewDocument_modifyAndReRender() {
|
||||
}
|
||||
|
||||
// capture original number of paths
|
||||
originalPaths := len(v3Model.Model.Paths.PathItems)
|
||||
originalPaths := orderedmap.Len(v3Model.Model.Paths.PathItems)
|
||||
|
||||
// add the path to the document
|
||||
v3Model.Model.Paths.PathItems["/new/path"] = newPath
|
||||
v3Model.Model.Paths.PathItems.Set("/new/path", newPath)
|
||||
|
||||
// render the document back to bytes and reload the model.
|
||||
rawBytes, _, newModel, errs := doc.RenderAndReload()
|
||||
@@ -655,7 +657,7 @@ func ExampleNewDocument_modifyAndReRender() {
|
||||
}
|
||||
|
||||
// capture new number of paths after re-rendering
|
||||
newPaths := len(newModel.Model.Paths.PathItems)
|
||||
newPaths := orderedmap.Len(newModel.Model.Paths.PathItems)
|
||||
|
||||
// print the number of paths and schemas in the document
|
||||
fmt.Printf("There were %d original paths. There are now %d paths in the document\n", originalPaths, newPaths)
|
||||
|
||||
@@ -4,13 +4,22 @@ package libopenapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
"github.com/pb33f/libopenapi/what-changed/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/what-changed/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoadDocument_Simple_V2(t *testing.T) {
|
||||
@@ -244,12 +253,12 @@ func TestDocument_RenderAndReload(t *testing.T) {
|
||||
|
||||
// mutate the model
|
||||
h := m.Model
|
||||
h.Paths.PathItems["/pet/findByStatus"].Get.OperationId = "findACakeInABakery"
|
||||
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description = "a nice bucket of mice"
|
||||
h.Paths.PathItems["/pet/findByTags"].Get.Tags =
|
||||
append(h.Paths.PathItems["/pet/findByTags"].Get.Tags, "gurgle", "giggle")
|
||||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId = "findACakeInABakery"
|
||||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes["400"].Description = "a nice bucket of mice"
|
||||
h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags =
|
||||
append(h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, "gurgle", "giggle")
|
||||
|
||||
h.Paths.PathItems["/pet/{petId}"].Delete.Security = append(h.Paths.PathItems["/pet/{petId}"].Delete.Security,
|
||||
h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security = append(h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security,
|
||||
&base.SecurityRequirement{Requirements: map[string][]string{
|
||||
"pizza-and-cake": {"read:abook", "write:asong"},
|
||||
}})
|
||||
@@ -262,13 +271,13 @@ func TestDocument_RenderAndReload(t *testing.T) {
|
||||
assert.NotNil(t, bytes)
|
||||
|
||||
h = newDocModel.Model
|
||||
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems["/pet/findByStatus"].Get.OperationId)
|
||||
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId)
|
||||
assert.Equal(t, "a nice bucket of mice",
|
||||
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description)
|
||||
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3)
|
||||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes["400"].Description)
|
||||
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
|
||||
|
||||
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3)
|
||||
yu := h.Paths.PathItems["/pet/{petId}"].Delete.Security
|
||||
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
|
||||
yu := h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security
|
||||
assert.Equal(t, "read:abook", yu[len(yu)-1].Requirements["pizza-and-cake"][0])
|
||||
assert.Equal(t, "I am a teapot, filled with love.",
|
||||
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example)
|
||||
@@ -479,7 +488,7 @@ paths:
|
||||
}
|
||||
|
||||
// extract operation.
|
||||
operation := result.Model.Paths.PathItems["/something"].Get
|
||||
operation := result.Model.Paths.PathItems.GetOrZero("/something").Get
|
||||
|
||||
// print it out.
|
||||
fmt.Printf("param1: %s, is reference? %t, original reference %s",
|
||||
@@ -647,7 +656,7 @@ paths:
|
||||
// panic(errs)
|
||||
// }
|
||||
//
|
||||
// assert.Equal(t, "crs", result.Model.Paths.PathItems["/test"].Get.Parameters[0].Name)
|
||||
// assert.Equal(t, "crs", result.Model.Paths.PathItems.GetOrZero("/test").Get.Parameters[0].Name)
|
||||
//}
|
||||
|
||||
func TestDocument_ExampleMap(t *testing.T) {
|
||||
@@ -858,3 +867,59 @@ components:
|
||||
assert.Len(t, m.Index.GetCircularReferences(), 0)
|
||||
|
||||
}
|
||||
|
||||
// Ensure document ordering is preserved after building and loading.
|
||||
func TestDocument_Render_PreserveOrder(t *testing.T) {
|
||||
t.Run("Paths", func(t *testing.T) {
|
||||
const pathCount = 100
|
||||
doc, err := NewDocument([]byte(`openapi: 3.1.0`))
|
||||
require.NoError(t, err)
|
||||
model, errs := doc.BuildV3Model()
|
||||
require.Empty(t, errs)
|
||||
pathItems := orderedmap.New[string, *v3high.PathItem]()
|
||||
model.Model.Paths = &v3high.Paths{
|
||||
PathItems: pathItems,
|
||||
}
|
||||
for i := 0; i < pathCount; i++ {
|
||||
pathItem := &v3high.PathItem{
|
||||
Get: &v3high.Operation{
|
||||
Parameters: make([]*v3high.Parameter, 0),
|
||||
},
|
||||
}
|
||||
pathName := fmt.Sprintf("/foobar/%d", i)
|
||||
pathItems.Set(pathName, pathItem)
|
||||
}
|
||||
|
||||
checkOrder := func(t *testing.T, doc Document) {
|
||||
model, errs := doc.BuildV3Model()
|
||||
require.Empty(t, errs)
|
||||
pathItems := model.Model.Paths.PathItems
|
||||
require.Equal(t, pathCount, orderedmap.Len(pathItems))
|
||||
|
||||
var i int
|
||||
_ = orderedmap.For(model.Model.Paths.PathItems, func(pair orderedmap.Pair[string, *v3high.PathItem]) error {
|
||||
pathName := fmt.Sprintf("/foobar/%d", i)
|
||||
assert.Equal(t, pathName, pair.Key())
|
||||
i++
|
||||
return nil
|
||||
})
|
||||
assert.Equal(t, pathCount, i)
|
||||
}
|
||||
|
||||
checkOrder(t, doc)
|
||||
yamlBytes, doc, _, errs := doc.RenderAndReload()
|
||||
require.Empty(t, errs)
|
||||
|
||||
t.Run("Unmarshalled YAML ordering", func(t *testing.T) {
|
||||
// Reload YAML into new Document, verify ordering.
|
||||
doc2, err := NewDocument(yamlBytes)
|
||||
require.NoError(t, err)
|
||||
checkOrder(t, doc2)
|
||||
})
|
||||
|
||||
t.Run("Reloaded document ordering", func(t *testing.T) {
|
||||
// Verify ordering of reloaded document after call to RenderAndReload().
|
||||
checkOrder(t, doc)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
)
|
||||
|
||||
// PathsChanges represents changes found between two Swagger or OpenAPI Paths Objects.
|
||||
@@ -75,12 +77,16 @@ func ComparePaths(l, r any) *PathsChanges {
|
||||
|
||||
lKeys := make(map[string]low.ValueReference[*v2.PathItem])
|
||||
rKeys := make(map[string]low.ValueReference[*v2.PathItem])
|
||||
for k := range lPath.PathItems {
|
||||
lKeys[k.Value] = lPath.PathItems[k]
|
||||
laction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v2.PathItem]]) error {
|
||||
lKeys[pair.Key().Value] = pair.Value()
|
||||
return nil
|
||||
}
|
||||
for k := range rPath.PathItems {
|
||||
rKeys[k.Value] = rPath.PathItems[k]
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*v2.PathItem]](lPath.PathItems, laction)
|
||||
raction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v2.PathItem]]) error {
|
||||
rKeys[pair.Key().Value] = pair.Value()
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*v2.PathItem]](rPath.PathItems, raction)
|
||||
|
||||
// run every comparison in a thread.
|
||||
var mLock sync.Mutex
|
||||
@@ -146,12 +152,16 @@ func ComparePaths(l, r any) *PathsChanges {
|
||||
|
||||
lKeys := make(map[string]low.ValueReference[*v3.PathItem])
|
||||
rKeys := make(map[string]low.ValueReference[*v3.PathItem])
|
||||
for k := range lPath.PathItems {
|
||||
lKeys[k.Value] = lPath.PathItems[k]
|
||||
laction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v3.PathItem]]) error {
|
||||
lKeys[pair.Key().Value] = pair.Value()
|
||||
return nil
|
||||
}
|
||||
for k := range rPath.PathItems {
|
||||
rKeys[k.Value] = rPath.PathItems[k]
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*v3.PathItem]](lPath.PathItems, laction)
|
||||
raction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v3.PathItem]]) error {
|
||||
rKeys[pair.Key().Value] = pair.Value()
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*v3.PathItem]](rPath.PathItems, raction)
|
||||
|
||||
// run every comparison in a thread.
|
||||
var mLock sync.Mutex
|
||||
|
||||
Reference in New Issue
Block a user