Monster refactor of map to orderedmap.Map data type.

This commit is contained in:
Shawn Poulson
2023-08-30 13:12:03 -04:00
parent 652e818456
commit f389fedadd
91 changed files with 1264 additions and 1192 deletions

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/base" low "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -57,10 +58,10 @@ func (e *Example) MarshalYAML() (interface{}, error) {
// ExtractExamples will convert a low-level example map, into a high level one that is simple to navigate. // ExtractExamples will convert a low-level example map, into a high level one that is simple to navigate.
// no fidelity is lost, everything is still available via GoLow() // no fidelity is lost, everything is still available via GoLow()
func ExtractExamples(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Example]) map[string]*Example { func ExtractExamples(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Example]]) orderedmap.Map[string, *Example] {
extracted := make(map[string]*Example) extracted := orderedmap.New[string, *Example]()
for k, v := range elements { for pair := orderedmap.First(elements); pair != nil; pair = pair.Next() {
extracted[k.Value] = NewExample(v.Value) extracted.Set(pair.Key().Value, NewExample(pair.Value().Value))
} }
return extracted return extracted
} }

View File

@@ -5,12 +5,14 @@ package base
import ( import (
"fmt" "fmt"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings" "strings"
"testing" "testing"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
) )
func TestNewExample(t *testing.T) { func TestNewExample(t *testing.T) {
@@ -61,14 +63,13 @@ func TestExtractExamples(t *testing.T) {
_ = lowExample.Build(nil, cNode.Content[0], nil) _ = lowExample.Build(nil, cNode.Content[0], nil)
examplesMap := make(map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*lowbase.Example]) examplesMap := orderedmap.New[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowbase.Example]]()
examplesMap[lowmodel.KeyReference[string]{ examplesMap.Set(
Value: "green", lowmodel.KeyReference[string]{Value: "green"},
}] = lowmodel.ValueReference[*lowbase.Example]{ lowmodel.ValueReference[*lowbase.Example]{Value: &lowExample},
Value: &lowExample, )
}
assert.Equal(t, "herbs", ExtractExamples(examplesMap)["green"].Summary) assert.Equal(t, "herbs", ExtractExamples(examplesMap).GetOrZero("green").Summary)
} }

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -50,16 +51,16 @@ type Schema struct {
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"` PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
// 3.1 Specific properties // 3.1 Specific properties
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"` Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"` MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"` MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"` If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"` Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"` Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"` DependentSchemas orderedmap.Map[string, *SchemaProxy] `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"` PatternProperties orderedmap.Map[string, *SchemaProxy] `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"` PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"` UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
// in 3.1 UnevaluatedProperties can be a Schema or a boolean // in 3.1 UnevaluatedProperties can be a Schema or a boolean
// https://github.com/pb33f/libopenapi/issues/118 // https://github.com/pb33f/libopenapi/issues/118
@@ -72,35 +73,35 @@ type Schema struct {
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"` Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
// Compatible with all versions // Compatible with all versions
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"` Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"` Properties orderedmap.Map[string, *SchemaProxy] `json:"properties,omitempty" yaml:"properties,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"` Title string `json:"title,omitempty" yaml:"title,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"` Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"` AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"` Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"` Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"`
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"`
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *base.Schema low *base.Schema
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
@@ -356,14 +357,13 @@ func NewSchema(schema *base.Schema) *Schema {
// props async // props async
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy],
props map[string]*SchemaProxy, sw int, props orderedmap.Map[string, *SchemaProxy], sw int,
) { ) {
props[k.Value] = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ props.Set(k.Value, NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
Value: v.Value, Value: v.Value,
KeyNode: k.KeyNode, KeyNode: k.KeyNode,
ValueNode: v.ValueNode, ValueNode: v.ValueNode,
}, }))
)
switch sw { switch sw {
case 0: case 0:
@@ -375,18 +375,18 @@ func NewSchema(schema *base.Schema) *Schema {
} }
} }
props := make(map[string]*SchemaProxy) props := orderedmap.New[string, *SchemaProxy]()
for k, v := range schema.Properties.Value { for pair := orderedmap.First(schema.Properties.Value); pair != nil; pair = pair.Next() {
buildProps(k, v, props, 0) buildProps(pair.Key(), pair.Value(), props, 0)
} }
dependents := make(map[string]*SchemaProxy) dependents := orderedmap.New[string, *SchemaProxy]()
for k, v := range schema.DependentSchemas.Value { for pair := orderedmap.First(schema.DependentSchemas.Value); pair != nil; pair = pair.Next() {
buildProps(k, v, dependents, 1) buildProps(pair.Key(), pair.Value(), dependents, 1)
} }
patternProps := make(map[string]*SchemaProxy) patternProps := orderedmap.New[string, *SchemaProxy]()
for k, v := range schema.PatternProperties.Value { for pair := orderedmap.First(schema.PatternProperties.Value); pair != nil; pair = pair.Next() {
buildProps(k, v, patternProps, 2) buildProps(pair.Key(), pair.Value(), patternProps, 2)
} }
var allOf []*SchemaProxy var allOf []*SchemaProxy

View File

@@ -303,8 +303,8 @@ $anchor: anchor`
assert.Equal(t, "string", compiled.If.Schema().Type[0]) assert.Equal(t, "string", compiled.If.Schema().Type[0])
assert.Equal(t, "integer", compiled.Else.Schema().Type[0]) assert.Equal(t, "integer", compiled.Else.Schema().Type[0])
assert.Equal(t, "boolean", compiled.Then.Schema().Type[0]) assert.Equal(t, "boolean", compiled.Then.Schema().Type[0])
assert.Equal(t, "string", compiled.PatternProperties["patternOne"].Schema().Type[0]) assert.Equal(t, "string", compiled.PatternProperties.GetOrZero("patternOne").Schema().Type[0])
assert.Equal(t, "string", compiled.DependentSchemas["schemaOne"].Schema().Type[0]) assert.Equal(t, "string", compiled.DependentSchemas.GetOrZero("schemaOne").Schema().Type[0])
assert.Equal(t, "string", compiled.PropertyNames.Schema().Type[0]) assert.Equal(t, "string", compiled.PropertyNames.Schema().Type[0])
assert.Equal(t, "boolean", compiled.UnevaluatedItems.Schema().Type[0]) assert.Equal(t, "boolean", compiled.UnevaluatedItems.Schema().Type[0])
assert.Equal(t, "integer", compiled.UnevaluatedProperties.A.Schema().Type[0]) assert.Equal(t, "integer", compiled.UnevaluatedProperties.A.Schema().Type[0])
@@ -416,7 +416,7 @@ anyOf:
anyOfB: anyOfB:
type: string type: string
description: anyOfB description description: anyOfB description
example: 'anyOfBExp' example: 'anyOfBExp'
not: not:
type: object type: object
description: a not thing description: a not thing
@@ -428,7 +428,7 @@ not:
notB: notB:
type: string type: string
description: notB description description: notB description
example: 'notBExp' example: 'notBExp'
items: items:
type: object type: object
description: an items thing description: an items thing
@@ -454,7 +454,7 @@ properties:
somethingBProp: somethingBProp:
exclusiveMinimum: 3 exclusiveMinimum: 3
exclusiveMaximum: 120 exclusiveMaximum: 120
type: type:
- string - string
- null - null
description: something b subprop description: something b subprop
@@ -466,9 +466,9 @@ properties:
attribute: true attribute: true
wrapped: false wrapped: false
x-pizza: love x-pizza: love
additionalProperties: additionalProperties:
why: yes why: yes
thatIs: true thatIs: true
additionalProperties: additionalProperties:
type: string type: string
description: nice description: nice
@@ -500,9 +500,9 @@ required: [cake, fish]`
assert.Nil(t, schemaProxy.GetBuildError()) assert.Nil(t, schemaProxy.GetBuildError())
assert.True(t, compiled.ExclusiveMaximum.A) assert.True(t, compiled.ExclusiveMaximum.A)
assert.Equal(t, float64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum.B) assert.Equal(t, float64(123), compiled.Properties.GetOrZero("somethingB").Schema().ExclusiveMinimum.B)
assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B) assert.Equal(t, float64(334), compiled.Properties.GetOrZero("somethingB").Schema().ExclusiveMaximum.B)
assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) assert.Len(t, compiled.Properties.GetOrZero("somethingB").Schema().Properties.GetOrZero("somethingBProp").Schema().Type, 2)
assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description) assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description)
@@ -734,7 +734,7 @@ properties:
highSchema := NewSchema(&lowSchema) highSchema := NewSchema(&lowSchema)
// print out the description of 'aProperty' // print out the description of 'aProperty'
fmt.Print(highSchema.Properties["aProperty"].Schema().Description) fmt.Print(highSchema.Properties.GetOrZero("aProperty").Schema().Description)
// Output: this is an integer property // Output: this is an integer property
} }
@@ -765,7 +765,7 @@ properties:
}) })
// print out the description of 'aProperty' // print out the description of 'aProperty'
fmt.Print(highSchema.Schema().Properties["aProperty"].Schema().Description) fmt.Print(highSchema.Schema().Properties.GetOrZero("aProperty").Schema().Description)
// Output: this is an integer property // Output: this is an integer property
} }

View File

@@ -4,11 +4,13 @@
package base package base
import ( import (
"sort"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
) )
// SecurityRequirement is a high-level representation of a Swagger / OpenAPI 3 SecurityRequirement object. // SecurityRequirement is a high-level representation of a Swagger / OpenAPI 3 SecurityRequirement object.
@@ -19,7 +21,7 @@ import (
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions // The name used for each property MUST correspond to a security scheme declared in the Security Definitions
// - https://swagger.io/specification/v2/#securityDefinitionsObject // - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityRequirement struct { type SecurityRequirement struct {
Requirements map[string][]string `json:"-" yaml:"-"` Requirements orderedmap.Map[string, []string] `json:"-" yaml:"-"`
low *base.SecurityRequirement low *base.SecurityRequirement
} }
@@ -27,14 +29,14 @@ type SecurityRequirement struct {
func NewSecurityRequirement(req *base.SecurityRequirement) *SecurityRequirement { func NewSecurityRequirement(req *base.SecurityRequirement) *SecurityRequirement {
r := new(SecurityRequirement) r := new(SecurityRequirement)
r.low = req r.low = req
values := make(map[string][]string) values := orderedmap.New[string, []string]()
// to keep things fast, avoiding copying anything - makes it a little hard to read. // to keep things fast, avoiding copying anything - makes it a little hard to read.
for reqK := range req.Requirements.Value { for pair := orderedmap.First(req.Requirements.Value); pair != nil; pair = pair.Next() {
var vals []string var vals []string
for valK := range req.Requirements.Value[reqK].Value { for valK := range pair.Value().Value {
vals = append(vals, req.Requirements.Value[reqK].Value[valK].Value) vals = append(vals, pair.Value().Value[valK].Value)
} }
values[reqK.Value] = vals values.Set(pair.Key().Value, vals)
} }
r.Requirements = values r.Requirements = values
return r return r
@@ -67,24 +69,23 @@ func (s *SecurityRequirement) MarshalYAML() (interface{}, error) {
} }
m := utils.CreateEmptyMapNode() m := utils.CreateEmptyMapNode()
keys := make([]*req, len(s.Requirements)) keys := make([]*req, orderedmap.Len(s.Requirements))
i := 0 i := 0
for k := range s.Requirements { for pair := orderedmap.First(s.Requirements); pair != nil; pair = pair.Next() {
keys[i] = &req{key: k, val: s.Requirements[k]} keys[i] = &req{key: pair.Key(), val: pair.Value()}
i++ i++
} }
i = 0 i = 0
if s.low != nil { if s.low != nil {
for o := range keys { for o := range keys {
kv := keys[o].key kv := keys[o].key
for k := range s.low.Requirements.Value { for pair := orderedmap.First(s.low.Requirements.Value); pair != nil; pair = pair.Next() {
if k.Value == kv { if pair.Key().Value == kv {
gh := s.low.Requirements.Value[k] keys[o].line = pair.Key().KeyNode.Line
keys[o].line = k.KeyNode.Line keys[o].lowKey = pair.KeyPtr()
keys[o].lowKey = &k keys[o].lowVal = pair.ValuePtr()
keys[o].lowVal = &gh
} }
i++ i++
} }

View File

@@ -4,12 +4,14 @@
package base package base
import ( import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings" "strings"
"testing" "testing"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
) )
func TestNewSecurityRequirement(t *testing.T) { func TestNewSecurityRequirement(t *testing.T) {
@@ -32,11 +34,11 @@ cake:
highExt := NewSecurityRequirement(&lowExt) highExt := NewSecurityRequirement(&lowExt)
assert.Len(t, highExt.Requirements["pizza"], 2) assert.Len(t, highExt.Requirements.GetOrZero("pizza"), 2)
assert.Len(t, highExt.Requirements["cake"], 2) assert.Len(t, highExt.Requirements.GetOrZero("cake"), 2)
wentLow := highExt.GoLow() wentLow := highExt.GoLow()
assert.Len(t, wentLow.Requirements.Value, 2) assert.Equal(t, 2, orderedmap.Len(wentLow.Requirements.Value))
assert.NotNil(t, highExt.GoLowUntyped()) assert.NotNil(t, highExt.GoLowUntyped())
// render the high-level object as YAML // render the high-level object as YAML

View File

@@ -5,14 +5,15 @@ package high
import ( import (
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"reflect" "reflect"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"unicode" "unicode"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
) )
// NodeEntry represents a single node used by NodeBuilder. // NodeEntry represents a single node used by NodeBuilder.
@@ -659,12 +660,12 @@ func (n *NodeBuilder) extractLowMapKeys(fg reflect.Value, x string, found bool,
return found, orderedCollection return found, orderedCollection
} }
// Renderable is an interface that can be implemented by types that provide a custom MarshaYAML method. // Renderable is an interface that can be implemented by types that provide a custom MarshalYAML method.
type Renderable interface { type Renderable interface {
MarshalYAML() (interface{}, error) MarshalYAML() (interface{}, error)
} }
// RenderableInline is an interface that can be implemented by types that provide a custom MarshaYAML method. // RenderableInline is an interface that can be implemented by types that provide a custom MarshalYAML method.
type RenderableInline interface { type RenderableInline interface {
MarshalYAMLInline() (interface{}, error) MarshalYAMLInline() (interface{}, error)
} }

View File

@@ -0,0 +1,6 @@
package v2
type asyncResult[T any] struct {
key string
result T
}

View File

@@ -8,6 +8,7 @@ import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base" lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
low "github.com/pb33f/libopenapi/datamodel/low/v2" low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
) )
// Definitions is a high-level represents of a Swagger / OpenAPI 2 Definitions object, backed by a low-level one. // Definitions is a high-level represents of a Swagger / OpenAPI 2 Definitions object, backed by a low-level one.
@@ -16,7 +17,7 @@ import (
// arrays or models. // arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject // - https://swagger.io/specification/v2/#definitionsObject
type Definitions struct { type Definitions struct {
Definitions map[string]*highbase.SchemaProxy Definitions orderedmap.Map[string, *highbase.SchemaProxy]
low *low.Definitions low *low.Definitions
} }
@@ -24,12 +25,20 @@ type Definitions struct {
func NewDefinitions(definitions *low.Definitions) *Definitions { func NewDefinitions(definitions *low.Definitions) *Definitions {
rd := new(Definitions) rd := new(Definitions)
rd.low = definitions rd.low = definitions
defs := make(map[string]*highbase.SchemaProxy) defs := orderedmap.New[string, *highbase.SchemaProxy]()
for k := range definitions.Schemas { translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowbase.SchemaProxy]]) (asyncResult[*highbase.SchemaProxy], error) {
defs[k.Value] = highbase.NewSchemaProxy(&lowmodel.NodeReference[*lowbase.SchemaProxy]{ return asyncResult[*highbase.SchemaProxy]{
Value: definitions.Schemas[k].Value, key: pair.Key().Value,
}) result: highbase.NewSchemaProxy(&lowmodel.NodeReference[*lowbase.SchemaProxy]{
Value: pair.Value().Value,
}),
}, nil
} }
resultFunc := func(value asyncResult[*highbase.SchemaProxy]) error {
defs.Set(value.key, value.result)
return nil
}
_ = orderedmap.TranslateMapParallel(definitions.Schemas, translateFunc, resultFunc)
rd.Definitions = defs rd.Definitions = defs
return rd return rd
} }

View File

@@ -3,13 +3,16 @@
package v2 package v2
import low "github.com/pb33f/libopenapi/datamodel/low/v2" import (
low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
)
// Example represents a high-level Swagger / OpenAPI 2 Example object, backed by a low level one. // Example represents a high-level Swagger / OpenAPI 2 Example object, backed by a low level one.
// Allows sharing examples for operation responses // Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject // - https://swagger.io/specification/v2/#exampleObject
type Example struct { type Example struct {
Values map[string]any Values orderedmap.Map[string, any]
low *low.Examples low *low.Examples
} }
@@ -17,10 +20,10 @@ type Example struct {
func NewExample(examples *low.Examples) *Example { func NewExample(examples *low.Examples) *Example {
e := new(Example) e := new(Example)
e.low = examples e.low = examples
if len(examples.Values) > 0 { if orderedmap.Len(examples.Values) > 0 {
values := make(map[string]any) values := orderedmap.New[string, any]()
for k := range examples.Values { for pair := orderedmap.First(examples.Values); pair != nil; pair = pair.Next() {
values[k.Value] = examples.Values[k].Value values.Set(pair.Key().Value, pair.Value().Value)
} }
e.Values = values e.Values = values
} }

View File

@@ -3,7 +3,11 @@
package v2 package v2
import low "github.com/pb33f/libopenapi/datamodel/low/v2" import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
)
// ParameterDefinitions is a high-level representation of a Swagger / OpenAPI 2 Parameters Definitions object // ParameterDefinitions is a high-level representation of a Swagger / OpenAPI 2 Parameters Definitions object
// that is backed by a low-level one. // that is backed by a low-level one.
@@ -12,7 +16,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
// referenced to the ones defined here. It does not define global operation parameters // referenced to the ones defined here. It does not define global operation parameters
// - https://swagger.io/specification/v2/#parametersDefinitionsObject // - https://swagger.io/specification/v2/#parametersDefinitionsObject
type ParameterDefinitions struct { type ParameterDefinitions struct {
Definitions map[string]*Parameter Definitions orderedmap.Map[string, *Parameter]
low *low.ParameterDefinitions low *low.ParameterDefinitions
} }
@@ -21,26 +25,18 @@ type ParameterDefinitions struct {
func NewParametersDefinitions(parametersDefinitions *low.ParameterDefinitions) *ParameterDefinitions { func NewParametersDefinitions(parametersDefinitions *low.ParameterDefinitions) *ParameterDefinitions {
pd := new(ParameterDefinitions) pd := new(ParameterDefinitions)
pd.low = parametersDefinitions pd.low = parametersDefinitions
params := make(map[string]*Parameter) params := orderedmap.New[string, *Parameter]()
var buildParam = func(name string, param *low.Parameter, rChan chan<- asyncResult[*Parameter]) { translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Parameter]]) (asyncResult[*Parameter], error) {
rChan <- asyncResult[*Parameter]{ return asyncResult[*Parameter]{
key: name, key: pair.Key().Value,
result: NewParameter(param), result: NewParameter(pair.Value().Value),
} }, nil
} }
resChan := make(chan asyncResult[*Parameter]) resultFunc := func(value asyncResult[*Parameter]) error {
for k := range parametersDefinitions.Definitions { params.Set(value.key, value.result)
go buildParam(k.Value, parametersDefinitions.Definitions[k].Value, resChan) return nil
}
totalParams := len(parametersDefinitions.Definitions)
completedParams := 0
for completedParams < totalParams {
select {
case r := <-resChan:
completedParams++
params[r.key] = r.result
}
} }
_ = orderedmap.TranslateMapParallel(parametersDefinitions.Definitions, translateFunc, resultFunc)
pd.Definitions = params pd.Definitions = params
return pd return pd
} }

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
low "github.com/pb33f/libopenapi/datamodel/low/v2" low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
) )
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one. // Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
@@ -15,7 +16,7 @@ import (
type Response struct { type Response struct {
Description string Description string
Schema *base.SchemaProxy Schema *base.SchemaProxy
Headers map[string]*Header Headers orderedmap.Map[string, *Header]
Examples *Example Examples *Example
Extensions map[string]any Extensions map[string]any
low *low.Response low *low.Response
@@ -33,9 +34,9 @@ func NewResponse(response *low.Response) *Response {
r.Schema = base.NewSchemaProxy(&response.Schema) r.Schema = base.NewSchemaProxy(&response.Schema)
} }
if !response.Headers.IsEmpty() { if !response.Headers.IsEmpty() {
headers := make(map[string]*Header) headers := orderedmap.New[string, *Header]()
for k := range response.Headers.Value { for pair := orderedmap.First(response.Headers.Value); pair != nil; pair = pair.Next() {
headers[k.Value] = NewHeader(response.Headers.Value[k].Value) headers.Set(pair.Key().Value, NewHeader(pair.Value().Value))
} }
r.Headers = headers r.Headers = headers
} }

View File

@@ -5,12 +5,14 @@ package v2
import ( import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v2" low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
) )
// Responses is a high-level representation of a Swagger / OpenAPI 2 Responses object, backed by a low level one. // Responses is a high-level representation of a Swagger / OpenAPI 2 Responses object, backed by a low level one.
type Responses struct { type Responses struct {
Codes map[string]*Response Codes orderedmap.Map[string, *Response]
Default *Response Default *Response
Extensions map[string]any Extensions map[string]any
low *low.Responses low *low.Responses
@@ -22,36 +24,26 @@ func NewResponses(responses *low.Responses) *Responses {
r.low = responses r.low = responses
r.Extensions = high.ExtractExtensions(responses.Extensions) r.Extensions = high.ExtractExtensions(responses.Extensions)
// async function.
var buildPath = func(code string, pi *low.Response, rChan chan<- asyncResult[*Response]) {
rChan <- asyncResult[*Response]{
key: code,
result: NewResponse(pi),
}
}
if !responses.Default.IsEmpty() { if !responses.Default.IsEmpty() {
r.Default = NewResponse(responses.Default.Value) r.Default = NewResponse(responses.Default.Value)
} }
// run everything async. lots of responses with lots of data are possible. if orderedmap.Len(responses.Codes) > 0 {
if len(responses.Codes) > 0 { resp := orderedmap.New[string, *Response]()
resultChan := make(chan asyncResult[*Response]) translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Response]]) (asyncResult[*Response], error) {
for k := range responses.Codes { return asyncResult[*Response]{
go buildPath(k.Value, responses.Codes[k].Value, resultChan) key: pair.Key().Value,
result: NewResponse(pair.Value().Value),
}, nil
} }
resp := make(map[string]*Response) resultFunc := func(value asyncResult[*Response]) error {
totalResponses := len(responses.Codes) resp.Set(value.key, value.result)
completedResponses := 0 return nil
for completedResponses < totalResponses {
select {
case res := <-resultChan:
completedResponses++
resp[res.key] = res.result
}
} }
_ = orderedmap.TranslateMapParallel(responses.Codes, translateFunc, resultFunc)
r.Codes = resp r.Codes = resp
} }
return r return r
} }

View File

@@ -3,7 +3,11 @@
package v2 package v2
import low "github.com/pb33f/libopenapi/datamodel/low/v2" import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
)
// ResponsesDefinitions is a high-level representation of a Swagger / OpenAPI 2 Responses Definitions object. // ResponsesDefinitions is a high-level representation of a Swagger / OpenAPI 2 Responses Definitions object.
// that is backed by a low-level one. // that is backed by a low-level one.
@@ -12,7 +16,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
// referenced to the ones defined here. It does not define global operation responses // referenced to the ones defined here. It does not define global operation responses
// - https://swagger.io/specification/v2/#responsesDefinitionsObject // - https://swagger.io/specification/v2/#responsesDefinitionsObject
type ResponsesDefinitions struct { type ResponsesDefinitions struct {
Definitions map[string]*Response Definitions orderedmap.Map[string, *Response]
low *low.ResponsesDefinitions low *low.ResponsesDefinitions
} }
@@ -20,28 +24,19 @@ type ResponsesDefinitions struct {
func NewResponsesDefinitions(responsesDefinitions *low.ResponsesDefinitions) *ResponsesDefinitions { func NewResponsesDefinitions(responsesDefinitions *low.ResponsesDefinitions) *ResponsesDefinitions {
rd := new(ResponsesDefinitions) rd := new(ResponsesDefinitions)
rd.low = responsesDefinitions rd.low = responsesDefinitions
responses := orderedmap.New[string, *Response]()
translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Response]]) (asyncResult[*Response], error) {
return asyncResult[*Response]{
key: pair.Key().Value,
result: NewResponse(pair.Value().Value),
}, nil
}
resultFunc := func(value asyncResult[*Response]) error {
responses.Set(value.key, value.result)
return nil
}
// build everything async. _ = orderedmap.TranslateMapParallel(responsesDefinitions.Definitions, translateFunc, resultFunc)
responses := make(map[string]*Response)
var buildResp = func(name string, resp *low.Response, rChan chan<- asyncResult[*Response]) {
rChan <- asyncResult[*Response]{
key: name,
result: NewResponse(resp),
}
}
resChan := make(chan asyncResult[*Response])
for k := range responsesDefinitions.Definitions {
go buildResp(k.Value, responsesDefinitions.Definitions[k].Value, resChan)
}
totalResponses := len(responsesDefinitions.Definitions)
completedResponses := 0
for completedResponses < totalResponses {
select {
case r := <-resChan:
completedResponses++
responses[r.key] = r.result
}
}
rd.Definitions = responses rd.Definitions = responses
return rd return rd
} }

View File

@@ -5,6 +5,7 @@ package v2
import ( import (
low "github.com/pb33f/libopenapi/datamodel/low/v2" low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
) )
// Scopes is a high-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object, that is backed by a low-level one. // Scopes is a high-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object, that is backed by a low-level one.
@@ -12,7 +13,7 @@ import (
// Scopes lists the available scopes for an OAuth2 security scheme. // Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject // - https://swagger.io/specification/v2/#scopesObject
type Scopes struct { type Scopes struct {
Values map[string]string Values orderedmap.Map[string, string]
low *low.Scopes low *low.Scopes
} }
@@ -20,9 +21,9 @@ type Scopes struct {
func NewScopes(scopes *low.Scopes) *Scopes { func NewScopes(scopes *low.Scopes) *Scopes {
s := new(Scopes) s := new(Scopes)
s.low = scopes s.low = scopes
scopeValues := make(map[string]string) scopeValues := orderedmap.New[string, string]()
for k := range scopes.Values { for pair := orderedmap.First(scopes.Values); pair != nil; pair = pair.Next() {
scopeValues[k.Value] = scopes.Values[k].Value scopeValues.Set(pair.Key().Value, pair.Value().Value)
} }
s.Values = scopeValues s.Values = scopeValues
return s return s

View File

@@ -3,7 +3,11 @@
package v2 package v2
import low "github.com/pb33f/libopenapi/datamodel/low/v2" import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
)
// SecurityDefinitions is a high-level representation of a Swagger / OpenAPI 2 Security Definitions object, that // SecurityDefinitions is a high-level representation of a Swagger / OpenAPI 2 Security Definitions object, that
// is backed by a low-level one. // is backed by a low-level one.
@@ -12,7 +16,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
// schemes on the operations and only serves to provide the relevant details for each scheme // schemes on the operations and only serves to provide the relevant details for each scheme
// - https://swagger.io/specification/v2/#securityDefinitionsObject // - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityDefinitions struct { type SecurityDefinitions struct {
Definitions map[string]*SecurityScheme Definitions orderedmap.Map[string, *SecurityScheme]
low *low.SecurityDefinitions low *low.SecurityDefinitions
} }
@@ -20,10 +24,23 @@ type SecurityDefinitions struct {
func NewSecurityDefinitions(definitions *low.SecurityDefinitions) *SecurityDefinitions { func NewSecurityDefinitions(definitions *low.SecurityDefinitions) *SecurityDefinitions {
sd := new(SecurityDefinitions) sd := new(SecurityDefinitions)
sd.low = definitions sd.low = definitions
schemes := make(map[string]*SecurityScheme) schemes := orderedmap.New[string, *SecurityScheme]()
for k := range definitions.Definitions { translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.SecurityScheme]]) (asyncResult[*SecurityScheme], error) {
schemes[k.Value] = NewSecurityScheme(definitions.Definitions[k].Value) return asyncResult[*SecurityScheme]{
key: pair.Key().Value,
result: NewSecurityScheme(pair.Value().Value),
}, nil
} }
resultFunc := func(value asyncResult[*SecurityScheme]) error {
schemes.Set(value.key, value.result)
return nil
}
_ = orderedmap.TranslateMapParallel(definitions.Definitions, translateFunc, resultFunc)
// schemes := make(map[string]*SecurityScheme)
// for k := range definitions.Definitions {
// schemes[k.Value] = NewSecurityScheme(definitions.Definitions[k].Value)
// }
sd.Definitions = schemes sd.Definitions = schemes
return sd return sd
} }

View File

@@ -173,9 +173,3 @@ func NewSwaggerDocument(document *low.Swagger) *Swagger {
func (s *Swagger) GoLow() *low.Swagger { func (s *Swagger) GoLow() *low.Swagger {
return s.low return s.low
} }
// everything is build async, this little gem holds the results.
type asyncResult[T any] struct {
key string
result T
}

View File

@@ -6,6 +6,7 @@ package v2
import ( import (
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"io/ioutil" "io/ioutil"
@@ -80,17 +81,17 @@ func TestNewSwaggerDocument_Parameters(t *testing.T) {
initTest() initTest()
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
params := highDoc.Parameters params := highDoc.Parameters
assert.Len(t, params.Definitions, 1) assert.Equal(t, 1, orderedmap.Len(params.Definitions))
assert.Equal(t, "query", params.Definitions["simpleParam"].In) assert.Equal(t, "query", params.Definitions.GetOrZero("simpleParam").In)
assert.Equal(t, "simple", params.Definitions["simpleParam"].Name) assert.Equal(t, "simple", params.Definitions.GetOrZero("simpleParam").Name)
assert.Equal(t, "string", params.Definitions["simpleParam"].Type) assert.Equal(t, "string", params.Definitions.GetOrZero("simpleParam").Type)
assert.Equal(t, "nuggets", params.Definitions["simpleParam"].Extensions["x-chicken"]) assert.Equal(t, "nuggets", params.Definitions.GetOrZero("simpleParam").Extensions["x-chicken"])
wentLow := params.GoLow() wentLow := params.GoLow()
assert.Equal(t, 20, wentLow.FindParameter("simpleParam").ValueNode.Line) assert.Equal(t, 20, wentLow.FindParameter("simpleParam").ValueNode.Line)
assert.Equal(t, 5, wentLow.FindParameter("simpleParam").ValueNode.Column) assert.Equal(t, 5, wentLow.FindParameter("simpleParam").ValueNode.Column)
wentLower := params.Definitions["simpleParam"].GoLow() wentLower := params.Definitions.GetOrZero("simpleParam").GoLow()
assert.Equal(t, 21, wentLower.Name.ValueNode.Line) assert.Equal(t, 21, wentLower.Name.ValueNode.Line)
assert.Equal(t, 11, wentLower.Name.ValueNode.Column) assert.Equal(t, 11, wentLower.Name.ValueNode.Column)
@@ -100,7 +101,7 @@ func TestNewSwaggerDocument_Security(t *testing.T) {
initTest() initTest()
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.Security, 1) assert.Len(t, highDoc.Security, 1)
assert.Len(t, highDoc.Security[0].Requirements["global_auth"], 2) assert.Len(t, highDoc.Security[0].Requirements.GetOrZero("global_auth"), 2)
wentLow := highDoc.Security[0].GoLow() wentLow := highDoc.Security[0].GoLow()
assert.Equal(t, 25, wentLow.Requirements.ValueNode.Line) assert.Equal(t, 25, wentLow.Requirements.ValueNode.Line)
@@ -111,23 +112,23 @@ func TestNewSwaggerDocument_Security(t *testing.T) {
func TestNewSwaggerDocument_Definitions_Security(t *testing.T) { func TestNewSwaggerDocument_Definitions_Security(t *testing.T) {
initTest() initTest()
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.SecurityDefinitions.Definitions, 3) assert.Equal(t, 3, orderedmap.Len(highDoc.SecurityDefinitions.Definitions))
assert.Equal(t, "oauth2", highDoc.SecurityDefinitions.Definitions["petstore_auth"].Type) assert.Equal(t, "oauth2", highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Type)
assert.Equal(t, "https://petstore.swagger.io/oauth/authorize", assert.Equal(t, "https://petstore.swagger.io/oauth/authorize",
highDoc.SecurityDefinitions.Definitions["petstore_auth"].AuthorizationUrl) highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").AuthorizationUrl)
assert.Equal(t, "implicit", highDoc.SecurityDefinitions.Definitions["petstore_auth"].Flow) assert.Equal(t, "implicit", highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Flow)
assert.Len(t, highDoc.SecurityDefinitions.Definitions["petstore_auth"].Scopes.Values, 2) assert.Equal(t, 2, orderedmap.Len(highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Scopes.Values))
goLow := highDoc.SecurityDefinitions.GoLow() goLow := highDoc.SecurityDefinitions.GoLow()
assert.Equal(t, 661, goLow.FindSecurityDefinition("petstore_auth").ValueNode.Line) assert.Equal(t, 661, goLow.FindSecurityDefinition("petstore_auth").ValueNode.Line)
assert.Equal(t, 5, goLow.FindSecurityDefinition("petstore_auth").ValueNode.Column) assert.Equal(t, 5, goLow.FindSecurityDefinition("petstore_auth").ValueNode.Column)
goLower := highDoc.SecurityDefinitions.Definitions["petstore_auth"].GoLow() goLower := highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").GoLow()
assert.Equal(t, 664, goLower.Scopes.KeyNode.Line) assert.Equal(t, 664, goLower.Scopes.KeyNode.Line)
assert.Equal(t, 5, goLower.Scopes.KeyNode.Column) assert.Equal(t, 5, goLower.Scopes.KeyNode.Column)
goLowest := highDoc.SecurityDefinitions.Definitions["petstore_auth"].Scopes.GoLow() goLowest := highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Scopes.GoLow()
assert.Equal(t, 665, goLowest.FindScope("read:pets").ValueNode.Line) assert.Equal(t, 665, goLowest.FindScope("read:pets").ValueNode.Line)
assert.Equal(t, 18, goLowest.FindScope("read:pets").ValueNode.Column) assert.Equal(t, 18, goLowest.FindScope("read:pets").ValueNode.Column)
} }
@@ -135,34 +136,34 @@ func TestNewSwaggerDocument_Definitions_Security(t *testing.T) {
func TestNewSwaggerDocument_Definitions_Responses(t *testing.T) { func TestNewSwaggerDocument_Definitions_Responses(t *testing.T) {
initTest() initTest()
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.Responses.Definitions, 2) assert.Equal(t, 2, orderedmap.Len(highDoc.Responses.Definitions))
defs := highDoc.Responses.Definitions defs := highDoc.Responses.Definitions
assert.Equal(t, "morning", defs["200"].Extensions["x-coffee"]) assert.Equal(t, "morning", defs.GetOrZero("200").Extensions["x-coffee"])
assert.Equal(t, "OK", defs["200"].Description) assert.Equal(t, "OK", defs.GetOrZero("200").Description)
assert.Equal(t, "a generic API response object", assert.Equal(t, "a generic API response object",
defs["200"].Schema.Schema().Description) defs.GetOrZero("200").Schema.Schema().Description)
assert.Len(t, defs["200"].Examples.Values, 3) assert.Equal(t, 3, orderedmap.Len(defs.GetOrZero("200").Examples.Values))
exp := defs["200"].Examples.Values["application/json"] exp := defs.GetOrZero("200").Examples.Values.GetOrZero("application/json")
assert.Len(t, exp.(map[string]interface{}), 2) assert.Len(t, exp.(map[string]interface{}), 2)
assert.Equal(t, "two", exp.(map[string]interface{})["one"]) assert.Equal(t, "two", exp.(map[string]interface{})["one"])
exp = defs["200"].Examples.Values["text/xml"] exp = defs.GetOrZero("200").Examples.Values.GetOrZero("text/xml")
assert.Len(t, exp.([]interface{}), 3) assert.Len(t, exp.([]interface{}), 3)
assert.Equal(t, "two", exp.([]interface{})[1]) assert.Equal(t, "two", exp.([]interface{})[1])
exp = defs["200"].Examples.Values["text/plain"] exp = defs.GetOrZero("200").Examples.Values.GetOrZero("text/plain")
assert.Equal(t, "something else.", exp) assert.Equal(t, "something else.", exp)
expWentLow := defs["200"].Examples.GoLow() expWentLow := defs.GetOrZero("200").Examples.GoLow()
assert.Equal(t, 702, expWentLow.FindExample("application/json").ValueNode.Line) assert.Equal(t, 702, expWentLow.FindExample("application/json").ValueNode.Line)
assert.Equal(t, 9, expWentLow.FindExample("application/json").ValueNode.Column) assert.Equal(t, 9, expWentLow.FindExample("application/json").ValueNode.Column)
wentLow := highDoc.Responses.GoLow() wentLow := highDoc.Responses.GoLow()
assert.Equal(t, 669, wentLow.FindResponse("200").ValueNode.Line) assert.Equal(t, 669, wentLow.FindResponse("200").ValueNode.Line)
y := defs["500"].Headers["someHeader"] y := defs.GetOrZero("500").Headers.GetOrZero("someHeader")
assert.Len(t, y.Enum, 2) assert.Len(t, y.Enum, 2)
x := y.Items x := y.Items
@@ -192,7 +193,7 @@ func TestNewSwaggerDocument_Definitions(t *testing.T) {
initTest() initTest()
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.Definitions.Definitions, 6) assert.Equal(t, 6, orderedmap.Len(highDoc.Definitions.Definitions))
wentLow := highDoc.Definitions.GoLow() wentLow := highDoc.Definitions.GoLow()
assert.Equal(t, 848, wentLow.FindSchema("User").ValueNode.Line) assert.Equal(t, 848, wentLow.FindSchema("User").ValueNode.Line)
@@ -202,7 +203,7 @@ func TestNewSwaggerDocument_Definitions(t *testing.T) {
func TestNewSwaggerDocument_Paths(t *testing.T) { func TestNewSwaggerDocument_Paths(t *testing.T) {
initTest() initTest()
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.Paths.PathItems, 15) assert.Equal(t, 15, orderedmap.Len(highDoc.Paths.PathItems))
upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage") upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage")
assert.Equal(t, "man", upload.Extensions["x-potato"]) assert.Equal(t, "man", upload.Extensions["x-potato"])
@@ -264,9 +265,9 @@ func TestNewSwaggerDocument_Responses(t *testing.T) {
highDoc := NewSwaggerDocument(doc) highDoc := NewSwaggerDocument(doc)
upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage").Post upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage").Post
assert.Len(t, upload.Responses.Codes, 1) assert.Equal(t, 1, orderedmap.Len(upload.Responses.Codes))
OK := upload.Responses.Codes["200"] OK := upload.Responses.Codes.GetOrZero("200")
assert.Equal(t, "successful operation", OK.Description) assert.Equal(t, "successful operation", OK.Description)
assert.Equal(t, "a generic API response object", OK.Schema.Schema().Description) assert.Equal(t, "a generic API response object", OK.Schema.Schema().Description)

View File

@@ -0,0 +1,6 @@
package v3
type asyncResult[T any] struct {
key string
result T
}

View File

@@ -4,11 +4,13 @@
package v3 package v3
import ( import (
"sort"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
) )
// Callback represents a high-level Callback object for OpenAPI 3+. // Callback represents a high-level Callback object for OpenAPI 3+.
@@ -19,8 +21,8 @@ import (
// that identifies a URL to use for the callback operation. // that identifies a URL to use for the callback operation.
// - https://spec.openapis.org/oas/v3.1.0#callback-object // - https://spec.openapis.org/oas/v3.1.0#callback-object
type Callback struct { type Callback struct {
Expression map[string]*PathItem `json:"-" yaml:"-"` Expression orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Callback low *low.Callback
} }
@@ -28,9 +30,9 @@ type Callback struct {
func NewCallback(lowCallback *low.Callback) *Callback { func NewCallback(lowCallback *low.Callback) *Callback {
n := new(Callback) n := new(Callback)
n.low = lowCallback n.low = lowCallback
n.Expression = make(map[string]*PathItem) n.Expression = orderedmap.New[string, *PathItem]()
for i := range lowCallback.Expression.Value { for pair := orderedmap.First(lowCallback.Expression.Value); pair != nil; pair = pair.Next() {
n.Expression[i.Value] = NewPathItem(lowCallback.Expression.Value[i].Value) n.Expression.Set(pair.Key().Value, NewPathItem(pair.Value().Value))
} }
n.Extensions = make(map[string]any) n.Extensions = make(map[string]any)
for k, v := range lowCallback.Extensions { for k, v := range lowCallback.Extensions {
@@ -66,16 +68,16 @@ func (c *Callback) MarshalYAML() (interface{}, error) {
} }
var mapped []*cbItem var mapped []*cbItem
for k, ex := range c.Expression { for pair := orderedmap.First(c.Expression); pair != nil; pair = pair.Next() {
ln := 999 // default to a high value to weight new content to the bottom. ln := 999 // default to a high value to weight new content to the bottom.
if c.low != nil { if c.low != nil {
for lKey := range c.low.Expression.Value { for lPair := orderedmap.First(c.low.Expression.Value); lPair != nil; lPair = lPair.Next() {
if lKey.Value == k { if lPair.Key().Value == pair.Key() {
ln = lKey.KeyNode.Line ln = lPair.Key().KeyNode.Line
} }
} }
} }
mapped = append(mapped, &cbItem{ex, k, ln, nil}) mapped = append(mapped, &cbItem{pair.Value(), pair.Key(), ln, nil})
} }
// extract extensions // extract extensions

View File

@@ -4,19 +4,21 @@
package v3 package v3
import ( import (
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strings"
"testing"
) )
func TestCallback_MarshalYAML(t *testing.T) { func TestCallback_MarshalYAML(t *testing.T) {
cb := &Callback{ cb := &Callback{
Expression: map[string]*PathItem{ Expression: orderedmap.ToOrderedMap(map[string]*PathItem{
"https://pb33f.io": { "https://pb33f.io": {
Get: &Operation{ Get: &Operation{
OperationId: "oneTwoThree", OperationId: "oneTwoThree",
@@ -27,7 +29,7 @@ func TestCallback_MarshalYAML(t *testing.T) {
OperationId: "openaypeeeye", OperationId: "openaypeeeye",
}, },
}, },
}, }),
Extensions: map[string]any{ Extensions: map[string]any{
"x-burgers": "why not?", "x-burgers": "why not?",
}, },
@@ -39,7 +41,7 @@ func TestCallback_MarshalYAML(t *testing.T) {
assert.Len(t, rend, 152) assert.Len(t, rend, 152)
// mutate // mutate
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam" cb.Expression.GetOrZero("https://pb33f.io").Get.OperationId = "blim-blam"
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"} cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
rend, _ = cb.Render() rend, _ = cb.Render()

View File

@@ -6,12 +6,12 @@ package v3
import ( import (
"sync" "sync"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
highbase "github.com/pb33f/libopenapi/datamodel/high/base" highbase "github.com/pb33f/libopenapi/datamodel/high/base"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -21,16 +21,16 @@ import (
// will have no effect on the API unless they are explicitly referenced from properties outside the components object. // will have no effect on the API unless they are explicitly referenced from properties outside the components object.
// - https://spec.openapis.org/oas/v3.1.0#components-object // - https://spec.openapis.org/oas/v3.1.0#components-object
type Components struct { type Components struct {
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"` Schemas orderedmap.Map[string, *highbase.SchemaProxy] `json:"schemas,omitempty" yaml:"schemas,omitempty"`
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"` Responses orderedmap.Map[string, *Response] `json:"responses,omitempty" yaml:"responses,omitempty"`
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` Parameters orderedmap.Map[string, *Parameter] `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"` Examples orderedmap.Map[string, *highbase.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"` RequestBodies orderedmap.Map[string, *RequestBody] `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` Headers orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"`
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"` SecuritySchemes orderedmap.Map[string, *SecurityScheme] `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"` Links orderedmap.Map[string, *Link] `json:"links,omitempty" yaml:"links,omitempty"`
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` Callbacks orderedmap.Map[string, *Callback] `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Components low *low.Components
} }
@@ -43,15 +43,15 @@ func NewComponents(comp *low.Components) *Components {
if len(comp.Extensions) > 0 { if len(comp.Extensions) > 0 {
c.Extensions = high.ExtractExtensions(comp.Extensions) c.Extensions = high.ExtractExtensions(comp.Extensions)
} }
cbMap := make(map[string]*Callback) cbMap := orderedmap.New[string, *Callback]()
linkMap := make(map[string]*Link) linkMap := orderedmap.New[string, *Link]()
responseMap := make(map[string]*Response) responseMap := orderedmap.New[string, *Response]()
parameterMap := make(map[string]*Parameter) parameterMap := orderedmap.New[string, *Parameter]()
exampleMap := make(map[string]*highbase.Example) exampleMap := orderedmap.New[string, *highbase.Example]()
requestBodyMap := make(map[string]*RequestBody) requestBodyMap := orderedmap.New[string, *RequestBody]()
headerMap := make(map[string]*Header) headerMap := orderedmap.New[string, *Header]()
securitySchemeMap := make(map[string]*SecurityScheme) securitySchemeMap := orderedmap.New[string, *SecurityScheme]()
schemas := make(map[string]*highbase.SchemaProxy) schemas := orderedmap.New[string, *highbase.SchemaProxy]()
// build all components asynchronously. // build all components asynchronously.
var wg sync.WaitGroup var wg sync.WaitGroup
@@ -114,32 +114,33 @@ type componentResult[T any] struct {
} }
// buildComponent builds component structs from low level structs. // buildComponent builds component structs from low level structs.
func buildComponent[IN any, OUT any](inMap map[lowmodel.KeyReference[string]]lowmodel.ValueReference[IN], outMap map[string]OUT, translateItem func(IN) OUT) { func buildComponent[IN any, OUT any](inMap orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]], outMap orderedmap.Map[string, OUT], translateItem func(IN) OUT) {
translateFunc := func(key lowmodel.KeyReference[string], value lowmodel.ValueReference[IN]) (componentResult[OUT], error) { translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]]) (componentResult[OUT], error) {
return componentResult[OUT]{key: key.Value, res: translateItem(value.Value)}, nil return componentResult[OUT]{key: pair.Key().Value, res: translateItem(pair.Value().Value)}, nil
} }
resultFunc := func(value componentResult[OUT]) error { resultFunc := func(value componentResult[OUT]) error {
outMap[value.key] = value.res outMap.Set(value.key, value.res)
return nil return nil
} }
_ = datamodel.TranslateMapParallel(inMap, translateFunc, resultFunc) _ = orderedmap.TranslateMapParallel(inMap, translateFunc, resultFunc)
} }
// buildSchema builds a schema from low level structs. // buildSchema builds a schema from low level structs.
func buildSchema(inMap map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*base.SchemaProxy], outMap map[string]*highbase.SchemaProxy) { func buildSchema(inMap orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]], outMap orderedmap.Map[string, *highbase.SchemaProxy]) {
translateFunc := func(key lowmodel.KeyReference[string], value lowmodel.ValueReference[*base.SchemaProxy]) (componentResult[*highbase.SchemaProxy], error) { translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]]) (componentResult[*highbase.SchemaProxy], error) {
value := pair.Value()
var sch *highbase.SchemaProxy var sch *highbase.SchemaProxy
sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
Value: value.Value, Value: value.Value,
ValueNode: value.ValueNode, ValueNode: value.ValueNode,
}) })
return componentResult[*highbase.SchemaProxy]{res: sch, key: key.Value}, nil return componentResult[*highbase.SchemaProxy]{res: sch, key: pair.Key().Value}, nil
} }
resultFunc := func(value componentResult[*highbase.SchemaProxy]) error { resultFunc := func(value componentResult[*highbase.SchemaProxy]) error {
outMap[value.key] = value.res outMap.Set(value.key, value.res)
return nil return nil
} }
_ = datamodel.TranslateMapParallel(inMap, translateFunc, resultFunc) _ = orderedmap.TranslateMapParallel(inMap, translateFunc, resultFunc)
} }
// GoLow returns the low-level Components instance used to create the high-level one. // GoLow returns the low-level Components instance used to create the high-level one.

View File

@@ -4,38 +4,40 @@
package v3 package v3
import ( import (
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strings"
"testing"
) )
func TestComponents_MarshalYAML(t *testing.T) { func TestComponents_MarshalYAML(t *testing.T) {
comp := &Components{ comp := &Components{
Responses: map[string]*Response{ Responses: orderedmap.ToOrderedMap(map[string]*Response{
"200": { "200": {
Description: "OK", Description: "OK",
}, },
}, }),
Parameters: map[string]*Parameter{ Parameters: orderedmap.ToOrderedMap(map[string]*Parameter{
"id": { "id": {
Name: "id", Name: "id",
In: "path", In: "path",
}, },
}, }),
RequestBodies: map[string]*RequestBody{ RequestBodies: orderedmap.ToOrderedMap(map[string]*RequestBody{
"body": { "body": {
Content: map[string]*MediaType{ Content: orderedmap.ToOrderedMap(map[string]*MediaType{
"application/json": { "application/json": {
Example: "why?", Example: "why?",
}, },
}, }),
}, },
}, }),
} }
dat, _ := comp.Render() dat, _ := comp.Render()

View File

@@ -11,10 +11,12 @@ package v3
import ( import (
"bytes" "bytes"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -81,7 +83,7 @@ type Document struct {
// for example by an out-of-band registration. The key name is a unique string to refer to each webhook, // for example by an out-of-band registration. The key name is a unique string to refer to each webhook,
// while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider // while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider
// and the expected responses. An example is available. // and the expected responses. An example is available.
Webhooks map[string]*PathItem `json:"webhooks,omitempty" yaml:"webhooks,omitempty"` Webhooks orderedmap.Map[string, *PathItem] `json:"webhooks,omitempty" yaml:"webhooks,omitempty"`
// Index is a reference to the *index.SpecIndex that was created for the document and used // Index is a reference to the *index.SpecIndex that was created for the document and used
// as a guide when building out the Document. Ideal if further processing is required on the model and // as a guide when building out the Document. Ideal if further processing is required on the model and
@@ -129,9 +131,9 @@ func NewDocument(document *low.Document) *Document {
d.JsonSchemaDialect = document.JsonSchemaDialect.Value d.JsonSchemaDialect = document.JsonSchemaDialect.Value
} }
if !document.Webhooks.IsEmpty() { if !document.Webhooks.IsEmpty() {
hooks := make(map[string]*PathItem) hooks := orderedmap.New[string, *PathItem]()
for h := range document.Webhooks.Value { for pair := orderedmap.First(document.Webhooks.Value); pair != nil; pair = pair.Next() {
hooks[h.Value] = NewPathItem(document.Webhooks.Value[h].Value) hooks.Set(pair.Key().Value, NewPathItem(pair.Value().Value))
} }
d.Webhooks = hooks d.Webhooks = hooks
} }

View File

@@ -57,7 +57,7 @@ func TestNewDocument_Security(t *testing.T) {
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Security, 1) assert.Len(t, h.Security, 1)
assert.Len(t, h.Security[0].Requirements, 1) assert.Len(t, h.Security[0].Requirements, 1)
assert.Len(t, h.Security[0].Requirements["OAuthScheme"], 2) assert.Len(t, h.Security[0].Requirements.GetOrZero("OAuthScheme"), 2)
} }
func TestNewDocument_Info(t *testing.T) { func TestNewDocument_Info(t *testing.T) {
@@ -98,14 +98,14 @@ func TestNewDocument_Servers(t *testing.T) {
assert.Equal(t, "{scheme}://api.pb33f.io", h.Servers[0].URL) assert.Equal(t, "{scheme}://api.pb33f.io", h.Servers[0].URL)
assert.Equal(t, "this is our main API server, for all fun API things.", h.Servers[0].Description) assert.Equal(t, "this is our main API server, for all fun API things.", h.Servers[0].Description)
assert.Len(t, h.Servers[0].Variables, 1) assert.Len(t, h.Servers[0].Variables, 1)
assert.Equal(t, "https", h.Servers[0].Variables["scheme"].Default) assert.Equal(t, "https", h.Servers[0].Variables.GetOrZero("scheme").Default)
assert.Len(t, h.Servers[0].Variables["scheme"].Enum, 2) assert.Len(t, h.Servers[0].Variables.GetOrZero("scheme").Enum, 2)
assert.Equal(t, "https://{domain}.{host}.com", h.Servers[1].URL) assert.Equal(t, "https://{domain}.{host}.com", h.Servers[1].URL)
assert.Equal(t, "this is our second API server, for all fun API things.", h.Servers[1].Description) assert.Equal(t, "this is our second API server, for all fun API things.", h.Servers[1].Description)
assert.Len(t, h.Servers[1].Variables, 2) assert.Len(t, h.Servers[1].Variables, 2)
assert.Equal(t, "api", h.Servers[1].Variables["domain"].Default) assert.Equal(t, "api", h.Servers[1].Variables.GetOrZero("domain").Default)
assert.Equal(t, "pb33f.io", h.Servers[1].Variables["host"].Default) assert.Equal(t, "pb33f.io", h.Servers[1].Variables.GetOrZero("host").Default)
wentLow := h.GoLow() wentLow := h.GoLow()
assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.KeyNode.Line) assert.Equal(t, 45, wentLow.Servers.Value[0].Value.Description.KeyNode.Line)
@@ -117,7 +117,7 @@ func TestNewDocument_Servers(t *testing.T) {
assert.Equal(t, 45, wentLower.Description.ValueNode.Line) assert.Equal(t, 45, wentLower.Description.ValueNode.Line)
assert.Equal(t, 18, wentLower.Description.ValueNode.Column) assert.Equal(t, 18, wentLower.Description.ValueNode.Column)
wentLowest := h.Servers[0].Variables["scheme"].GoLow() wentLowest := h.Servers[0].Variables.GetOrZero("scheme").GoLow()
assert.Equal(t, 50, wentLowest.Description.ValueNode.Line) assert.Equal(t, 50, wentLowest.Description.ValueNode.Line)
assert.Equal(t, 22, wentLowest.Description.ValueNode.Column) assert.Equal(t, 22, wentLowest.Description.ValueNode.Column)
} }
@@ -147,18 +147,18 @@ func TestNewDocument_Tags(t *testing.T) {
func TestNewDocument_Webhooks(t *testing.T) { func TestNewDocument_Webhooks(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Webhooks, 1) assert.Equal(t, 1, orderedmap.Len(h.Webhooks))
assert.Equal(t, "Information about a new burger", h.Webhooks["someHook"].Post.RequestBody.Description) assert.Equal(t, "Information about a new burger", h.Webhooks.GetOrZero("someHook").Post.RequestBody.Description)
} }
func TestNewDocument_Components_Links(t *testing.T) { func TestNewDocument_Components_Links(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Links, 2) assert.Equal(t, 2, orderedmap.Len(h.Components.Links))
assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId) assert.Equal(t, "locateBurger", h.Components.Links.GetOrZero("LocateBurger").OperationId)
assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"]) assert.Equal(t, "$response.body#/id", h.Components.Links.GetOrZero("LocateBurger").Parameters.GetOrZero("burgerId"))
wentLow := h.Components.Links["LocateBurger"].GoLow() wentLow := h.Components.Links.GetOrZero("LocateBurger").GoLow()
assert.Equal(t, 310, wentLow.OperationId.ValueNode.Line) assert.Equal(t, 310, wentLow.OperationId.ValueNode.Line)
assert.Equal(t, 20, wentLow.OperationId.ValueNode.Column) assert.Equal(t, 20, wentLow.OperationId.ValueNode.Column)
} }
@@ -166,29 +166,29 @@ func TestNewDocument_Components_Links(t *testing.T) {
func TestNewDocument_Components_Callbacks(t *testing.T) { func TestNewDocument_Components_Callbacks(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Callbacks, 1) assert.Equal(t, 1, orderedmap.Len(h.Components.Callbacks))
assert.Equal( assert.Equal(
t, t,
"Callback payload", "Callback payload",
h.Components.Callbacks["BurgerCallback"].Expression["{$request.query.queryUrl}"].Post.RequestBody.Description, h.Components.Callbacks.GetOrZero("BurgerCallback").Expression.GetOrZero("{$request.query.queryUrl}").Post.RequestBody.Description,
) )
assert.Equal( assert.Equal(
t, t,
298, 298,
h.Components.Callbacks["BurgerCallback"].GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Line, h.Components.Callbacks.GetOrZero("BurgerCallback").GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Line,
) )
assert.Equal( assert.Equal(
t, t,
9, 9,
h.Components.Callbacks["BurgerCallback"].GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Column, h.Components.Callbacks.GetOrZero("BurgerCallback").GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Column,
) )
assert.Equal(t, "please", h.Components.Callbacks["BurgerCallback"].Extensions["x-break-everything"]) assert.Equal(t, "please", h.Components.Callbacks.GetOrZero("BurgerCallback").Extensions["x-break-everything"])
for k := range h.Components.GoLow().Callbacks.Value { for pair := orderedmap.First(h.Components.GoLow().Callbacks.Value); pair != nil; pair = pair.Next() {
if k.Value == "BurgerCallback" { if pair.Key().Value == "BurgerCallback" {
assert.Equal(t, 295, k.KeyNode.Line) assert.Equal(t, 295, pair.Key().KeyNode.Line)
assert.Equal(t, 5, k.KeyNode.Column) assert.Equal(t, 5, pair.Key().KeyNode.Column)
} }
} }
} }
@@ -196,30 +196,30 @@ func TestNewDocument_Components_Callbacks(t *testing.T) {
func TestNewDocument_Components_Schemas(t *testing.T) { func TestNewDocument_Components_Schemas(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Schemas, 6) assert.Equal(t, 6, orderedmap.Len(h.Components.Schemas))
goLow := h.Components.GoLow() goLow := h.Components.GoLow()
a := h.Components.Schemas["Error"] a := h.Components.Schemas.GetOrZero("Error")
abcd := a.Schema().Properties["message"].Schema().Example abcd := a.Schema().Properties.GetOrZero("message").Schema().Example
assert.Equal(t, "No such burger as 'Big-Whopper'", abcd) assert.Equal(t, "No such burger as 'Big-Whopper'", abcd)
assert.Equal(t, 433, goLow.Schemas.KeyNode.Line) assert.Equal(t, 433, goLow.Schemas.KeyNode.Line)
assert.Equal(t, 3, goLow.Schemas.KeyNode.Column) assert.Equal(t, 3, goLow.Schemas.KeyNode.Column)
assert.Equal(t, 436, a.Schema().GoLow().Description.KeyNode.Line) assert.Equal(t, 436, a.Schema().GoLow().Description.KeyNode.Line)
b := h.Components.Schemas["Burger"] b := h.Components.Schemas.GetOrZero("Burger")
assert.Len(t, b.Schema().Required, 2) assert.Len(t, b.Schema().Required, 2)
assert.Equal(t, "golden slices of happy fun joy", b.Schema().Properties["fries"].Schema().Description) assert.Equal(t, "golden slices of happy fun joy", b.Schema().Properties.GetOrZero("fries").Schema().Description)
assert.Equal(t, int64(2), b.Schema().Properties["numPatties"].Schema().Example) assert.Equal(t, int64(2), b.Schema().Properties.GetOrZero("numPatties").Schema().Example)
assert.Equal(t, 448, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Line) assert.Equal(t, 448, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Line)
assert.Equal(t, 7, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Column) assert.Equal(t, 7, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Column)
assert.Equal(t, 450, b.Schema().GoLow().FindProperty("name").ValueNode.Line) assert.Equal(t, 450, b.Schema().GoLow().FindProperty("name").ValueNode.Line)
f := h.Components.Schemas["Fries"] f := h.Components.Schemas.GetOrZero("Fries")
assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items.A.Schema().Example) assert.Equal(t, "salt", f.Schema().Properties.GetOrZero("seasoning").Schema().Items.A.Schema().Example)
assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 1) assert.Len(t, f.Schema().Properties.GetOrZero("favoriteDrink").Schema().Properties.GetOrZero("drinkType").Schema().Enum, 1)
d := h.Components.Schemas["Drink"] d := h.Components.Schemas.GetOrZero("Drink")
assert.Len(t, d.Schema().Required, 2) assert.Len(t, d.Schema().Required, 2)
assert.True(t, d.Schema().AdditionalProperties.(bool)) assert.True(t, d.Schema().AdditionalProperties.(bool))
assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName) assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName)
@@ -227,7 +227,7 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
assert.Equal(t, 516, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line) assert.Equal(t, 516, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line)
assert.Equal(t, 23, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Column) assert.Equal(t, 23, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Column)
pl := h.Components.Schemas["SomePayload"] pl := h.Components.Schemas.GetOrZero("SomePayload")
assert.Equal(t, "is html programming? yes.", pl.Schema().XML.Name) assert.Equal(t, "is html programming? yes.", pl.Schema().XML.Name)
assert.Equal(t, 523, pl.Schema().XML.GoLow().Name.ValueNode.Line) assert.Equal(t, 523, pl.Schema().XML.GoLow().Name.ValueNode.Line)
@@ -238,62 +238,62 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
func TestNewDocument_Components_Headers(t *testing.T) { func TestNewDocument_Components_Headers(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Headers, 1) assert.Equal(t, 1, orderedmap.Len(h.Components.Headers))
assert.Equal(t, "this is a header example for UseOil", h.Components.Headers["UseOil"].Description) assert.Equal(t, "this is a header example for UseOil", h.Components.Headers.GetOrZero("UseOil").Description)
assert.Equal(t, 323, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Line) assert.Equal(t, 323, h.Components.Headers.GetOrZero("UseOil").GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Column) assert.Equal(t, 20, h.Components.Headers.GetOrZero("UseOil").GoLow().Description.ValueNode.Column)
} }
func TestNewDocument_Components_RequestBodies(t *testing.T) { func TestNewDocument_Components_RequestBodies(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.RequestBodies, 1) assert.Equal(t, 1, orderedmap.Len(h.Components.RequestBodies))
assert.Equal(t, "Give us the new burger!", h.Components.RequestBodies["BurgerRequest"].Description) assert.Equal(t, "Give us the new burger!", h.Components.RequestBodies.GetOrZero("BurgerRequest").Description)
assert.Equal(t, 328, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Line) assert.Equal(t, 328, h.Components.RequestBodies.GetOrZero("BurgerRequest").GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Column) assert.Equal(t, 20, h.Components.RequestBodies.GetOrZero("BurgerRequest").GoLow().Description.ValueNode.Column)
assert.Len(t, h.Components.RequestBodies["BurgerRequest"].Content["application/json"].Examples, 2) assert.Equal(t, 2, orderedmap.Len(h.Components.RequestBodies.GetOrZero("BurgerRequest").Content.GetOrZero("application/json").Examples))
} }
func TestNewDocument_Components_Examples(t *testing.T) { func TestNewDocument_Components_Examples(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Examples, 1) assert.Equal(t, 1, orderedmap.Len(h.Components.Examples))
assert.Equal(t, "A juicy two hander sammich", h.Components.Examples["QuarterPounder"].Summary) assert.Equal(t, "A juicy two hander sammich", h.Components.Examples.GetOrZero("QuarterPounder").Summary)
assert.Equal(t, 346, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Line) assert.Equal(t, 346, h.Components.Examples.GetOrZero("QuarterPounder").GoLow().Summary.ValueNode.Line)
assert.Equal(t, 16, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Column) assert.Equal(t, 16, h.Components.Examples.GetOrZero("QuarterPounder").GoLow().Summary.ValueNode.Column)
} }
func TestNewDocument_Components_Responses(t *testing.T) { func TestNewDocument_Components_Responses(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Responses, 1) assert.Equal(t, 1, orderedmap.Len(h.Components.Responses))
assert.Equal(t, "all the dressings for a burger.", h.Components.Responses["DressingResponse"].Description) assert.Equal(t, "all the dressings for a burger.", h.Components.Responses.GetOrZero("DressingResponse").Description)
assert.Equal(t, "array", h.Components.Responses["DressingResponse"].Content["application/json"].Schema.Schema().Type[0]) assert.Equal(t, "array", h.Components.Responses.GetOrZero("DressingResponse").Content.GetOrZero("application/json").Schema.Schema().Type[0])
assert.Equal(t, 352, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Line) assert.Equal(t, 352, h.Components.Responses.GetOrZero("DressingResponse").GoLow().Description.KeyNode.Line)
assert.Equal(t, 7, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Column) assert.Equal(t, 7, h.Components.Responses.GetOrZero("DressingResponse").GoLow().Description.KeyNode.Column)
} }
func TestNewDocument_Components_SecuritySchemes(t *testing.T) { func TestNewDocument_Components_SecuritySchemes(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.SecuritySchemes, 3) assert.Equal(t, 3, orderedmap.Len(h.Components.SecuritySchemes))
api := h.Components.SecuritySchemes["APIKeyScheme"] api := h.Components.SecuritySchemes.GetOrZero("APIKeyScheme")
assert.Equal(t, "an apiKey security scheme", api.Description) assert.Equal(t, "an apiKey security scheme", api.Description)
assert.Equal(t, 364, api.GoLow().Description.ValueNode.Line) assert.Equal(t, 364, api.GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, api.GoLow().Description.ValueNode.Column) assert.Equal(t, 20, api.GoLow().Description.ValueNode.Column)
jwt := h.Components.SecuritySchemes["JWTScheme"] jwt := h.Components.SecuritySchemes.GetOrZero("JWTScheme")
assert.Equal(t, "an JWT security scheme", jwt.Description) assert.Equal(t, "an JWT security scheme", jwt.Description)
assert.Equal(t, 369, jwt.GoLow().Description.ValueNode.Line) assert.Equal(t, 369, jwt.GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, jwt.GoLow().Description.ValueNode.Column) assert.Equal(t, 20, jwt.GoLow().Description.ValueNode.Column)
oAuth := h.Components.SecuritySchemes["OAuthScheme"] oAuth := h.Components.SecuritySchemes.GetOrZero("OAuthScheme")
assert.Equal(t, "an oAuth security scheme", oAuth.Description) assert.Equal(t, "an oAuth security scheme", oAuth.Description)
assert.Equal(t, 375, oAuth.GoLow().Description.ValueNode.Line) assert.Equal(t, 375, oAuth.GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, oAuth.GoLow().Description.ValueNode.Column) assert.Equal(t, 20, oAuth.GoLow().Description.ValueNode.Column)
assert.Len(t, oAuth.Flows.Implicit.Scopes, 2) assert.Equal(t, 2, orderedmap.Len(oAuth.Flows.Implicit.Scopes))
assert.Equal(t, "read all burgers", oAuth.Flows.Implicit.Scopes["read:burgers"]) assert.Equal(t, "read all burgers", oAuth.Flows.Implicit.Scopes.GetOrZero("read:burgers"))
assert.Equal(t, "https://pb33f.io/oauth", oAuth.Flows.AuthorizationCode.AuthorizationUrl) assert.Equal(t, "https://pb33f.io/oauth", oAuth.Flows.AuthorizationCode.AuthorizationUrl)
// check the lowness is low. // check the lowness is low.
@@ -306,26 +306,26 @@ func TestNewDocument_Components_SecuritySchemes(t *testing.T) {
func TestNewDocument_Components_Parameters(t *testing.T) { func TestNewDocument_Components_Parameters(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Components.Parameters, 2) assert.Equal(t, 2, orderedmap.Len(h.Components.Parameters))
bh := h.Components.Parameters["BurgerHeader"] bh := h.Components.Parameters.GetOrZero("BurgerHeader")
assert.Equal(t, "burgerHeader", bh.Name) assert.Equal(t, "burgerHeader", bh.Name)
assert.Equal(t, 392, bh.GoLow().Name.KeyNode.Line) assert.Equal(t, 392, bh.GoLow().Name.KeyNode.Line)
assert.Len(t, bh.Schema.Schema().Properties, 2) assert.Equal(t, 2, orderedmap.Len(bh.Schema.Schema().Properties))
assert.Equal(t, "big-mac", bh.Example) assert.Equal(t, "big-mac", bh.Example)
assert.True(t, bh.Required) assert.True(t, bh.Required)
assert.Equal( assert.Equal(
t, t,
"this is a header", "this is a header",
bh.Content["application/json"].Encoding["burgerTheme"].Headers["someHeader"].Description, bh.Content.GetOrZero("application/json").Encoding.GetOrZero("burgerTheme").Headers.GetOrZero("someHeader").Description,
) )
assert.Len(t, bh.Content["application/json"].Schema.Schema().Properties, 2) assert.Equal(t, 2, orderedmap.Len(bh.Content.GetOrZero("application/json").Schema.Schema().Properties))
assert.Equal(t, 409, bh.Content["application/json"].Encoding["burgerTheme"].GoLow().ContentType.ValueNode.Line) assert.Equal(t, 409, bh.Content.GetOrZero("application/json").Encoding.GetOrZero("burgerTheme").GoLow().ContentType.ValueNode.Line)
} }
func TestNewDocument_Paths(t *testing.T) { func TestNewDocument_Paths(t *testing.T) {
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
assert.Len(t, h.Paths.PathItems, 5) assert.Equal(t, 5, orderedmap.Len(h.Paths.PathItems))
testBurgerShop(t, h, true) testBurgerShop(t, h, true)
} }
@@ -346,36 +346,36 @@ func testBurgerShop(t *testing.T, h *Document, checkLines bool) {
assert.Len(t, burgersOp.Post.Tags, 1) assert.Len(t, burgersOp.Post.Tags, 1)
assert.Equal(t, "A new burger for our menu, yummy yum yum.", burgersOp.Post.Description) assert.Equal(t, "A new burger for our menu, yummy yum yum.", burgersOp.Post.Description)
assert.Equal(t, "Give us the new burger!", burgersOp.Post.RequestBody.Description) assert.Equal(t, "Give us the new burger!", burgersOp.Post.RequestBody.Description)
assert.Len(t, burgersOp.Post.Responses.Codes, 3) assert.Equal(t, 3, orderedmap.Len(burgersOp.Post.Responses.Codes))
if checkLines { if checkLines {
assert.Equal(t, 64, burgersOp.GoLow().Post.KeyNode.Line) assert.Equal(t, 64, burgersOp.GoLow().Post.KeyNode.Line)
assert.Equal(t, 63, h.Paths.GoLow().FindPath("/burgers").ValueNode.Line) assert.Equal(t, 63, h.Paths.GoLow().FindPath("/burgers").ValueNode.Line)
} }
okResp := burgersOp.Post.Responses.FindResponseByCode(200) okResp := burgersOp.Post.Responses.FindResponseByCode(200)
assert.Len(t, okResp.Headers, 1) assert.Equal(t, 1, orderedmap.Len(okResp.Headers))
assert.Equal(t, "A tasty burger for you to eat.", okResp.Description) assert.Equal(t, "A tasty burger for you to eat.", okResp.Description)
assert.Len(t, okResp.Content["application/json"].Examples, 2) assert.Equal(t, 2, orderedmap.Len(okResp.Content.GetOrZero("application/json").Examples))
assert.Equal( assert.Equal(
t, t,
"a cripsy fish sammich filled with ocean goodness.", "a cripsy fish sammich filled with ocean goodness.",
okResp.Content["application/json"].Examples["filetOFish"].Summary, okResp.Content.GetOrZero("application/json").Examples.GetOrZero("filetOFish").Summary,
) )
assert.Len(t, okResp.Links, 2) assert.Equal(t, 2, orderedmap.Len(okResp.Links))
assert.Equal(t, "locateBurger", okResp.Links["LocateBurger"].OperationId) assert.Equal(t, "locateBurger", okResp.Links.GetOrZero("LocateBurger").OperationId)
assert.Len(t, burgersOp.Post.Security[0].Requirements, 1) assert.Len(t, burgersOp.Post.Security[0].Requirements, 1)
assert.Len(t, burgersOp.Post.Security[0].Requirements["OAuthScheme"], 2) assert.Len(t, burgersOp.Post.Security[0].Requirements.GetOrZero("OAuthScheme"), 2)
assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements["OAuthScheme"][0]) assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements.GetOrZero("OAuthScheme")[0])
assert.Len(t, burgersOp.Post.Servers, 1) assert.Len(t, burgersOp.Post.Servers, 1)
assert.Equal(t, "https://pb33f.io", burgersOp.Post.Servers[0].URL) assert.Equal(t, "https://pb33f.io", burgersOp.Post.Servers[0].URL)
if checkLines { if checkLines {
assert.Equal(t, 69, burgersOp.Post.GoLow().Description.ValueNode.Line) assert.Equal(t, 69, burgersOp.Post.GoLow().Description.ValueNode.Line)
assert.Equal(t, 74, burgersOp.Post.Responses.GoLow().FindResponseByCode("200").ValueNode.Line) assert.Equal(t, 74, burgersOp.Post.Responses.GoLow().FindResponseByCode("200").ValueNode.Line)
assert.Equal(t, 80, okResp.Content["application/json"].GoLow().Schema.KeyNode.Line) assert.Equal(t, 80, okResp.Content.GetOrZero("application/json").GoLow().Schema.KeyNode.Line)
assert.Equal(t, 15, okResp.Content["application/json"].GoLow().Schema.KeyNode.Column) assert.Equal(t, 15, okResp.Content.GetOrZero("application/json").GoLow().Schema.KeyNode.Column)
assert.Equal(t, 77, okResp.GoLow().Description.KeyNode.Line) assert.Equal(t, 77, okResp.GoLow().Description.KeyNode.Line)
assert.Equal(t, 310, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line) assert.Equal(t, 310, okResp.Links.GetOrZero("LocateBurger").GoLow().OperationId.ValueNode.Line)
assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line) assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line)
} }
@@ -534,7 +534,7 @@ func TestDocument_MarshalJSON(t *testing.T) {
newDoc := NewDocument(lowDoc) newDoc := NewDocument(lowDoc)
assert.Equal(t, orderedmap.Len(newDoc.Paths.PathItems), orderedmap.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)) assert.Equal(t, orderedmap.Len(newDoc.Components.Schemas), orderedmap.Len(highDoc.Components.Schemas))
} }
func TestDocument_MarshalYAMLInline(t *testing.T) { func TestDocument_MarshalYAMLInline(t *testing.T) {
@@ -711,8 +711,8 @@ components:
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
// mutate the schema // mutate the schema
g := h.Components.Schemas["BurgerHeader"].Schema() g := h.Components.Schemas.GetOrZero("BurgerHeader").Schema()
ds := g.Properties["burgerTheme"].Schema() ds := g.Properties.GetOrZero("burgerTheme").Schema()
ds.Description = "changed" ds.Description = "changed"
// render the document to YAML and it should be identical. // render the document to YAML and it should be identical.

View File

@@ -7,17 +7,18 @@ import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Encoding represents an OpenAPI 3+ Encoding object // Encoding represents an OpenAPI 3+ Encoding object
// - https://spec.openapis.org/oas/v3.1.0#encoding-object // - https://spec.openapis.org/oas/v3.1.0#encoding-object
type Encoding struct { type Encoding struct {
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"` ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` Headers orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"` Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
low *low.Encoding low *low.Encoding
} }
@@ -55,10 +56,10 @@ func (e *Encoding) MarshalYAML() (interface{}, error) {
} }
// ExtractEncoding converts hard to navigate low-level plumbing Encoding definitions, into a high-level simple map // ExtractEncoding converts hard to navigate low-level plumbing Encoding definitions, into a high-level simple map
func ExtractEncoding(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Encoding]) map[string]*Encoding { func ExtractEncoding(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Encoding]]) orderedmap.Map[string, *Encoding] {
extracted := make(map[string]*Encoding) extracted := orderedmap.New[string, *Encoding]()
for k, v := range elements { for pair := orderedmap.First(elements); pair != nil; pair = pair.Next() {
extracted[k.Value] = NewEncoding(v.Value) extracted.Set(pair.Key().Value, NewEncoding(pair.Value().Value))
} }
return extracted return extracted
} }

View File

@@ -4,9 +4,11 @@
package v3 package v3
import ( import (
"github.com/stretchr/testify/assert"
"strings" "strings"
"testing" "testing"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
) )
func TestEncoding_MarshalYAML(t *testing.T) { func TestEncoding_MarshalYAML(t *testing.T) {
@@ -14,9 +16,11 @@ func TestEncoding_MarshalYAML(t *testing.T) {
explode := true explode := true
encoding := &Encoding{ encoding := &Encoding{
ContentType: "application/json", ContentType: "application/json",
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}}, Headers: orderedmap.ToOrderedMap(map[string]*Header{
Style: "simple", "x-pizza-time": {Description: "oh yes please"},
Explode: &explode, }),
Style: "simple",
Explode: &explode,
} }
rend, _ := encoding.Render() rend, _ := encoding.Render()

View File

@@ -9,24 +9,25 @@ import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one. // Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#header-object // - https://spec.openapis.org/oas/v3.1.0#header-object
type Header struct { type Header struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"` Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode bool `json:"explode,omitempty" yaml:"explode,omitempty"` Explode bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"` Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"` Examples orderedmap.Map[string, *highbase.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"` Content orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Header low *low.Header
} }
@@ -65,10 +66,10 @@ func (h *Header) GoLowUntyped() any {
} }
// ExtractHeaders will extract a hard to navigate low-level Header map, into simple high-level one. // ExtractHeaders will extract a hard to navigate low-level Header map, into simple high-level one.
func ExtractHeaders(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Header]) map[string]*Header { func ExtractHeaders(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Header]]) orderedmap.Map[string, *Header] {
extracted := make(map[string]*Header) extracted := orderedmap.New[string, *Header]()
for k, v := range elements { for pair := orderedmap.First(elements); pair != nil; pair = pair.Next() {
extracted[k.Value] = NewHeader(v.Value) extracted.Set(pair.Key().Value, NewHeader(pair.Value().Value))
} }
return extracted return extracted
} }

View File

@@ -4,10 +4,12 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/stretchr/testify/assert"
"strings" "strings"
"testing" "testing"
"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
) )
func TestHeader_MarshalYAML(t *testing.T) { func TestHeader_MarshalYAML(t *testing.T) {
@@ -21,8 +23,10 @@ func TestHeader_MarshalYAML(t *testing.T) {
Explode: true, Explode: true,
AllowReserved: true, AllowReserved: true,
Example: "example", Example: "example",
Examples: map[string]*base.Example{"example": {Value: "example"}}, Examples: orderedmap.ToOrderedMap(map[string]*base.Example{
Extensions: map[string]interface{}{"x-burgers": "why not?"}, "example": {Value: "example"},
}),
Extensions: map[string]interface{}{"x-burgers": "why not?"},
} }
rend, _ := header.Render() rend, _ := header.Render()

View File

@@ -6,6 +6,7 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -22,13 +23,13 @@ import (
// in an operation and using them as parameters while invoking the linked operation. // in an operation and using them as parameters while invoking the linked operation.
// - https://spec.openapis.org/oas/v3.1.0#link-object // - https://spec.openapis.org/oas/v3.1.0#link-object
type Link struct { type Link struct {
OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"` OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"` OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
Parameters map[string]string `json:"parameters,omitempty" yaml:"parameters,omitempty"` Parameters orderedmap.Map[string, string] `json:"parameters,omitempty" yaml:"parameters,omitempty"`
RequestBody string `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` RequestBody string `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Server *Server `json:"server,omitempty" yaml:"server,omitempty"` Server *Server `json:"server,omitempty" yaml:"server,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Link low *low.Link
} }
@@ -38,9 +39,9 @@ func NewLink(link *low.Link) *Link {
l.low = link l.low = link
l.OperationRef = link.OperationRef.Value l.OperationRef = link.OperationRef.Value
l.OperationId = link.OperationId.Value l.OperationId = link.OperationId.Value
params := make(map[string]string) params := orderedmap.New[string, string]()
for k, v := range link.Parameters.Value { for pair := orderedmap.First(link.Parameters.Value); pair != nil; pair = pair.Next() {
params[k.Value] = v.Value params.Set(pair.Key().Value, pair.Value().Value)
} }
l.Parameters = params l.Parameters = params
l.RequestBody = link.RequestBody.Value l.RequestBody = link.RequestBody.Value

View File

@@ -4,18 +4,20 @@
package v3 package v3
import ( import (
"github.com/stretchr/testify/assert"
"strings" "strings"
"testing" "testing"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
) )
func TestLink_MarshalYAML(t *testing.T) { func TestLink_MarshalYAML(t *testing.T) {
link := Link{ link := Link{
OperationRef: "somewhere", OperationRef: "somewhere",
OperationId: "somewhereOutThere", OperationId: "somewhereOutThere",
Parameters: map[string]string{ Parameters: orderedmap.ToOrderedMap(map[string]string{
"over": "theRainbow", "over": "theRainbow",
}, }),
RequestBody: "hello?", RequestBody: "hello?",
Description: "are you there?", Description: "are you there?",
Server: &Server{ Server: &Server{

View File

@@ -4,12 +4,11 @@
package v3 package v3
import ( import (
"sync"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -18,11 +17,11 @@ import (
// Each Media Type Object provides schema and examples for the media type identified by its key. // Each Media Type Object provides schema and examples for the media type identified by its key.
// - https://spec.openapis.org/oas/v3.1.0#media-type-object // - https://spec.openapis.org/oas/v3.1.0#media-type-object
type MediaType struct { type MediaType struct {
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"` Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"` Examples orderedmap.Map[string, *base.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"` Encoding orderedmap.Map[string, *Encoding] `json:"encoding,omitempty" yaml:"encoding,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.MediaType low *low.MediaType
} }
@@ -74,29 +73,18 @@ func (m *MediaType) MarshalYAMLInline() (interface{}, error) {
// ExtractContent takes in a complex and hard to navigate low-level content map, and converts it in to a much simpler // ExtractContent takes in a complex and hard to navigate low-level content map, and converts it in to a much simpler
// and easier to navigate high-level one. // and easier to navigate high-level one.
func ExtractContent(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.MediaType]) map[string]*MediaType { func ExtractContent(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) orderedmap.Map[string, *MediaType] {
// extract everything async extracted := orderedmap.New[string, *MediaType]()
doneChan := make(chan bool) translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) (asyncResult[*MediaType], error) {
return asyncResult[*MediaType]{
var extLock sync.RWMutex key: pair.Key().Value,
extractContentItem := func(k lowmodel.KeyReference[string], result: NewMediaType(pair.Value().Value),
v lowmodel.ValueReference[*low.MediaType], c chan bool, e map[string]*MediaType) { }, nil
extLock.Lock()
e[k.Value] = NewMediaType(v.Value)
extLock.Unlock()
c <- true
} }
extracted := make(map[string]*MediaType) resultFunc := func(value asyncResult[*MediaType]) error {
for k, v := range elements { extracted.Set(value.key, value.result)
go extractContentItem(k, v, doneChan, extracted) return nil
}
n := 0
for n < len(elements) {
select {
case <-doneChan:
n++
}
} }
_ = orderedmap.TranslateMapParallel(elements, translateFunc, resultFunc)
return extracted return extracted
} }

View File

@@ -28,7 +28,7 @@ func TestMediaType_MarshalYAMLInline(t *testing.T) {
// create a new document and extract a media type object from it. // create a new document and extract a media type object from it.
d := NewDocument(lowDoc) d := NewDocument(lowDoc)
mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content["application/json"] mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content.GetOrZero("application/json")
// render out the media type // render out the media type
yml, _ := mt.Render() 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. // create a new document and extract a media type object from it.
d := NewDocument(lowDoc) d := NewDocument(lowDoc)
mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content["application/json"] mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content.GetOrZero("application/json")
// render out the media type // render out the media type
yml, _ := mt.Render() yml, _ := mt.Render()

View File

@@ -6,17 +6,18 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one. // OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object // - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
type OAuthFlow struct { type OAuthFlow struct {
AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"` AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"` TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
RefreshUrl string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"` RefreshUrl string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"`
Scopes map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"` Scopes orderedmap.Map[string, string] `json:"scopes,omitempty" yaml:"scopes,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.OAuthFlow low *low.OAuthFlow
} }
@@ -27,9 +28,9 @@ func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
o.TokenUrl = flow.TokenUrl.Value o.TokenUrl = flow.TokenUrl.Value
o.AuthorizationUrl = flow.AuthorizationUrl.Value o.AuthorizationUrl = flow.AuthorizationUrl.Value
o.RefreshUrl = flow.RefreshUrl.Value o.RefreshUrl = flow.RefreshUrl.Value
scopes := make(map[string]string) scopes := orderedmap.New[string, string]()
for k, v := range flow.Scopes.Value { for pair := orderedmap.First(flow.Scopes.Value); pair != nil; pair = pair.Next() {
scopes[k.Value] = v.Value scopes.Set(pair.Key().Value, pair.Value().Value)
} }
o.Scopes = scopes o.Scopes = scopes
o.Extensions = high.ExtractExtensions(flow.Extensions) o.Extensions = high.ExtractExtensions(flow.Extensions)

View File

@@ -4,9 +4,11 @@
package v3 package v3
import ( import (
"github.com/stretchr/testify/assert"
"strings" "strings"
"testing" "testing"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
) )
func TestOAuthFlow_MarshalYAML(t *testing.T) { func TestOAuthFlow_MarshalYAML(t *testing.T) {
@@ -15,7 +17,10 @@ func TestOAuthFlow_MarshalYAML(t *testing.T) {
AuthorizationUrl: "https://pb33f.io", AuthorizationUrl: "https://pb33f.io",
TokenUrl: "https://pb33f.io/token", TokenUrl: "https://pb33f.io/token",
RefreshUrl: "https://pb33f.io/refresh", RefreshUrl: "https://pb33f.io/refresh",
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"}, Scopes: orderedmap.ToOrderedMap(map[string]string{
"chicken": "nuggets",
"beefy": "soup",
}),
} }
rend, _ := oflow.Render() rend, _ := oflow.Render()

View File

@@ -4,13 +4,14 @@
package v3 package v3
import ( import (
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strings"
"testing"
) )
func TestNewOAuthFlows(t *testing.T) { func TestNewOAuthFlows(t *testing.T) {
@@ -81,7 +82,7 @@ clientCredentials:
CHIP:CHOP: microwave a sock` CHIP:CHOP: microwave a sock`
// now modify it and render it back out, and it should be identical! // now modify it and render it back out, and it should be identical!
r.ClientCredentials.Scopes["CHIP:CHOP"] = "microwave a sock" r.ClientCredentials.Scopes.Set("CHIP:CHOP", "microwave a sock")
rBytes, _ = r.Render() rBytes, _ = r.Render()
assert.Equal(t, modified, strings.TrimSpace(string(rBytes))) assert.Equal(t, modified, strings.TrimSpace(string(rBytes)))

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -16,19 +17,19 @@ import (
// happens here. The entire being for existence of this library and the specification, is this Operation. // happens here. The entire being for existence of this library and the specification, is this Operation.
// - https://spec.openapis.org/oas/v3.1.0#operation-object // - https://spec.openapis.org/oas/v3.1.0#operation-object
type Operation struct { type Operation struct {
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"` Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"` OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"` Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
RequestBody *RequestBody `json:"requestBody,omitempty" yaml:"requestBody,omitempty"` RequestBody *RequestBody `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
Responses *Responses `json:"responses,omitempty" yaml:"responses,omitempty"` Responses *Responses `json:"responses,omitempty" yaml:"responses,omitempty"`
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"` Callbacks orderedmap.Map[string, *Callback] `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"` Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"`
Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"` Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Operation low *low.Operation
} }
@@ -81,9 +82,9 @@ func NewOperation(operation *low.Operation) *Operation {
o.Servers = servers o.Servers = servers
o.Extensions = high.ExtractExtensions(operation.Extensions) o.Extensions = high.ExtractExtensions(operation.Extensions)
if !operation.Callbacks.IsEmpty() { if !operation.Callbacks.IsEmpty() {
cbs := make(map[string]*Callback) cbs := orderedmap.New[string, *Callback]()
for k, v := range operation.Callbacks.Value { for pair := orderedmap.First(operation.Callbacks.Value); pair != nil; pair = pair.Next() {
cbs[k.Value] = NewCallback(v.Value) cbs.Set(pair.Key().Value, NewCallback(pair.Value().Value))
} }
o.Callbacks = cbs o.Callbacks = cbs
} }

View File

@@ -50,7 +50,7 @@ callbacks:
assert.Equal(t, "https://pb33f.io", r.ExternalDocs.URL) assert.Equal(t, "https://pb33f.io", r.ExternalDocs.URL)
assert.Equal(t, 1, r.GoLow().ExternalDocs.KeyNode.Line) assert.Equal(t, 1, r.GoLow().ExternalDocs.KeyNode.Line)
assert.Contains(t, r.Callbacks, "testCallback") assert.Contains(t, r.Callbacks, "testCallback")
assert.Contains(t, r.Callbacks["testCallback"].Expression, "{$request.body#/callbackUrl}") assert.Contains(t, r.Callbacks.GetOrZero("testCallback").Expression, "{$request.body#/callbackUrl}")
assert.Equal(t, 3, r.GoLow().Callbacks.KeyNode.Line) assert.Equal(t, 3, r.GoLow().Callbacks.KeyNode.Line)
} }

View File

@@ -38,6 +38,6 @@ func Example_createHighLevelOpenAPIDocument() {
// Print out some details // Print out some details
fmt.Printf("Petstore contains %d paths and %d component schemas", fmt.Printf("Petstore contains %d paths and %d component schemas",
orderedmap.Len(doc.Paths.PathItems), len(doc.Components.Schemas)) orderedmap.Len(doc.Paths.PathItems), orderedmap.Len(doc.Components.Schemas))
// Output: Petstore contains 13 paths and 8 component schemas // Output: Petstore contains 13 paths and 8 component schemas
} }

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -15,20 +16,20 @@ import (
// A unique parameter is defined by a combination of a name and location. // A unique parameter is defined by a combination of a name and location.
// - https://spec.openapis.org/oas/v3.1.0#parameter-object // - https://spec.openapis.org/oas/v3.1.0#parameter-object
type Parameter struct { type Parameter struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"` Name string `json:"name,omitempty" yaml:"name,omitempty"`
In string `json:"in,omitempty" yaml:"in,omitempty"` In string `json:"in,omitempty" yaml:"in,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"` Required bool `json:"required,omitempty" yaml:"required,omitempty"`
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"` AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
Style string `json:"style,omitempty" yaml:"style,omitempty"` Style string `json:"style,omitempty" yaml:"style,omitempty"`
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"` Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"` AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"` Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"` Examples orderedmap.Map[string, *base.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"` Content orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Parameter low *low.Parameter
} }

View File

@@ -8,6 +8,7 @@ import (
"testing" "testing"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -23,8 +24,10 @@ func TestParameter_MarshalYAML(t *testing.T) {
Explode: &explode, Explode: &explode,
AllowReserved: true, AllowReserved: true,
Example: "example", Example: "example",
Examples: map[string]*base.Example{"example": {Value: "example"}}, Examples: orderedmap.ToOrderedMap(map[string]*base.Example{
Extensions: map[string]interface{}{"x-burgers": "why not?"}, "example": {Value: "example"},
}),
Extensions: map[string]interface{}{"x-burgers": "why not?"},
} }
rend, _ := param.Render() rend, _ := param.Render()
@@ -57,8 +60,10 @@ func TestParameter_MarshalYAMLInline(t *testing.T) {
Explode: &explode, Explode: &explode,
AllowReserved: true, AllowReserved: true,
Example: "example", Example: "example",
Examples: map[string]*base.Example{"example": {Value: "example"}}, Examples: orderedmap.ToOrderedMap(map[string]*base.Example{
Extensions: map[string]interface{}{"x-burgers": "why not?"}, "example": {Value: "example"},
}),
Extensions: map[string]interface{}{"x-burgers": "why not?"},
} }
rend, _ := param.RenderInline() rend, _ := param.RenderInline()

View File

@@ -84,7 +84,7 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
} }
var mapped []*pathItem var mapped []*pathItem
action := func(pair orderedmap.Pair[string, *PathItem]) error { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
k := pair.Key() k := pair.Key()
pi := pair.Value() pi := pair.Value()
ln := 9999 // default to a high value to weight new content to the bottom. ln := 9999 // default to a high value to weight new content to the bottom.
@@ -95,9 +95,7 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
} }
} }
mapped = append(mapped, &pathItem{pi, k, ln, nil}) mapped = append(mapped, &pathItem{pi, k, ln, nil})
return nil
} }
_ = orderedmap.For[string, *PathItem](p.PathItems, action)
nb := high.NewNodeBuilder(p, p.low) nb := high.NewNodeBuilder(p, p.low)
extNode := nb.Render() extNode := nb.Render()
@@ -142,7 +140,7 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
} }
var mapped []*pathItem var mapped []*pathItem
action := func(pair orderedmap.Pair[string, *PathItem]) error { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
k := pair.Key() k := pair.Key()
pi := pair.Value() pi := pair.Value()
ln := 9999 // default to a high value to weight new content to the bottom. ln := 9999 // default to a high value to weight new content to the bottom.
@@ -153,9 +151,7 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
} }
} }
mapped = append(mapped, &pathItem{pi, k, ln, nil}) mapped = append(mapped, &pathItem{pi, k, ln, nil})
return nil
} }
_ = orderedmap.For[string, *PathItem](p.PathItems, action)
nb := high.NewNodeBuilder(p, p.low) nb := high.NewNodeBuilder(p, p.low)
nb.Resolve = true nb.Resolve = true

View File

@@ -6,16 +6,17 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// RequestBody represents a high-level OpenAPI 3+ RequestBody object, backed by a low-level one. // RequestBody represents a high-level OpenAPI 3+ RequestBody object, backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#request-body-object // - https://spec.openapis.org/oas/v3.1.0#request-body-object
type RequestBody struct { type RequestBody struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"` Content orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
Required *bool `json:"required,omitempty" yaml:"required,renderZero,omitempty"` Required *bool `json:"required,omitempty" yaml:"required,renderZero,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.RequestBody low *low.RequestBody
} }

View File

@@ -6,6 +6,7 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -15,11 +16,11 @@ import (
// operations based on the response. // operations based on the response.
// - https://spec.openapis.org/oas/v3.1.0#response-object // - https://spec.openapis.org/oas/v3.1.0#response-object
type Response struct { type Response struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"` Headers orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"` Content orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"` Links orderedmap.Map[string, *Link] `json:"links,omitempty" yaml:"links,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Response low *low.Response
} }
@@ -36,9 +37,9 @@ func NewResponse(response *low.Response) *Response {
r.Content = ExtractContent(response.Content.Value) r.Content = ExtractContent(response.Content.Value)
} }
if !response.Links.IsEmpty() { if !response.Links.IsEmpty() {
responseLinks := make(map[string]*Link) responseLinks := orderedmap.New[string, *Link]()
for k, v := range response.Links.Value { for pair := orderedmap.First(response.Links.Value); pair != nil; pair = pair.Next() {
responseLinks[k.Value] = NewLink(v.Value) responseLinks.Set(pair.Key().Value, NewLink(pair.Value().Value))
} }
r.Links = responseLinks r.Links = responseLinks
} }

View File

@@ -7,10 +7,10 @@ import (
"fmt" "fmt"
"sort" "sort"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowbase "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -30,9 +30,9 @@ import (
// be the response for a successful operation call. // be the response for a successful operation call.
// - https://spec.openapis.org/oas/v3.1.0#responses-object // - https://spec.openapis.org/oas/v3.1.0#responses-object
type Responses struct { type Responses struct {
Codes map[string]*Response `json:"-" yaml:"-"` Codes orderedmap.Map[string, *Response] `json:"-" yaml:"-"`
Default *Response `json:"default,omitempty" yaml:"default,omitempty"` Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Responses low *low.Responses
} }
@@ -45,28 +45,31 @@ func NewResponses(responses *low.Responses) *Responses {
if !responses.Default.IsEmpty() { if !responses.Default.IsEmpty() {
r.Default = NewResponse(responses.Default.Value) r.Default = NewResponse(responses.Default.Value)
} }
codes := make(map[string]*Response) codes := orderedmap.New[string, *Response]()
type respRes struct { type respRes struct {
code string code string
resp *Response resp *Response
} }
translateFunc := func(key lowbase.KeyReference[string], value lowbase.ValueReference[*low.Response]) (respRes, error) { translateFunc := func(pair orderedmap.Pair[lowbase.KeyReference[string], lowbase.ValueReference[*low.Response]]) (asyncResult[*Response], error) {
return respRes{code: key.Value, resp: NewResponse(value.Value)}, nil return asyncResult[*Response]{
key: pair.Key().Value,
result: NewResponse(pair.Value().Value),
}, nil
} }
resultFunc := func(value respRes) error { resultFunc := func(value asyncResult[*Response]) error {
codes[value.code] = value.resp codes.Set(value.key, value.result)
return nil return nil
} }
_ = datamodel.TranslateMapParallel[lowbase.KeyReference[string], lowbase.ValueReference[*low.Response], respRes](responses.Codes, translateFunc, resultFunc) _ = orderedmap.TranslateMapParallel[lowbase.KeyReference[string], lowbase.ValueReference[*low.Response], asyncResult[*Response]](responses.Codes, translateFunc, resultFunc)
r.Codes = codes r.Codes = codes
return r return r
} }
// FindResponseByCode is a shortcut for looking up code by an integer vs. a string // FindResponseByCode is a shortcut for looking up code by an integer vs. a string
func (r *Responses) FindResponseByCode(code int) *Response { func (r *Responses) FindResponseByCode(code int) *Response {
return r.Codes[fmt.Sprintf("%d", code)] return r.Codes.GetOrZero(fmt.Sprintf("%d", code))
} }
// GoLow returns the low-level Response object used to create the high-level one. // GoLow returns the low-level Response object used to create the high-level one.
@@ -101,16 +104,16 @@ func (r *Responses) MarshalYAML() (interface{}, error) {
} }
var mapped []*responseItem var mapped []*responseItem
for k, re := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
ln := 9999 // default to a high value to weight new content to the bottom. ln := 9999 // default to a high value to weight new content to the bottom.
if r.low != nil { if r.low != nil {
for lKey := range r.low.Codes { for lPair := orderedmap.First(r.low.Codes); lPair != nil; lPair = lPair.Next() {
if lKey.Value == k { if lPair.Key().Value == pair.Key() {
ln = lKey.KeyNode.Line ln = lPair.Key().KeyNode.Line
} }
} }
} }
mapped = append(mapped, &responseItem{re, k, ln, nil}) mapped = append(mapped, &responseItem{pair.Value(), pair.Key(), ln, nil})
} }
// extract extensions // extract extensions
@@ -157,16 +160,16 @@ func (r *Responses) MarshalYAMLInline() (interface{}, error) {
} }
var mapped []*responseItem var mapped []*responseItem
for k, re := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
ln := 9999 // default to a high value to weight new content to the bottom. ln := 9999 // default to a high value to weight new content to the bottom.
if r.low != nil { if r.low != nil {
for lKey := range r.low.Codes { for lPair := orderedmap.First(r.low.Codes); lPair != nil; lPair = lPair.Next() {
if lKey.Value == k { if lPair.Key().Value == pair.Key() {
ln = lKey.KeyNode.Line ln = lPair.Key().KeyNode.Line
} }
} }
} }
mapped = append(mapped, &responseItem{re, k, ln, nil}) mapped = append(mapped, &responseItem{pair.Value(), pair.Key(), ln, nil})
} }
// extract extensions // extract extensions

View File

@@ -6,16 +6,17 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3" low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one. // Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one.
// - https://spec.openapis.org/oas/v3.1.0#server-object // - https://spec.openapis.org/oas/v3.1.0#server-object
type Server struct { type Server struct {
URL string `json:"url,omitempty" yaml:"url,omitempty"` URL string `json:"url,omitempty" yaml:"url,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Variables map[string]*ServerVariable `json:"variables,omitempty" yaml:"variables,omitempty"` Variables orderedmap.Map[string, *ServerVariable] `json:"variables,omitempty" yaml:"variables,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *low.Server low *low.Server
} }
@@ -25,9 +26,9 @@ func NewServer(server *low.Server) *Server {
s.low = server s.low = server
s.Description = server.Description.Value s.Description = server.Description.Value
s.URL = server.URL.Value s.URL = server.URL.Value
vars := make(map[string]*ServerVariable) vars := orderedmap.New[string, *ServerVariable]()
for k, val := range server.Variables.Value { for pair := orderedmap.First(server.Variables.Value); pair != nil; pair = pair.Next() {
vars[k.Value] = NewServerVariable(val.Value) vars.Set(pair.Key().Value, NewServerVariable(pair.Value().Value))
} }
s.Variables = vars s.Variables = vars
s.Extensions = high.ExtractExtensions(server.Extensions) s.Extensions = high.ExtractExtensions(server.Extensions)

View File

@@ -4,9 +4,11 @@
package v3 package v3
import ( import (
"github.com/stretchr/testify/assert"
"strings" "strings"
"testing" "testing"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
) )
func TestServer_MarshalYAML(t *testing.T) { func TestServer_MarshalYAML(t *testing.T) {
@@ -23,11 +25,11 @@ description: the b33f`
assert.Equal(t, desired, strings.TrimSpace(string(rend))) assert.Equal(t, desired, strings.TrimSpace(string(rend)))
// mutate // mutate
server.Variables = map[string]*ServerVariable{ server.Variables = orderedmap.ToOrderedMap(map[string]*ServerVariable{
"rainbow": { "rainbow": {
Enum: []string{"one", "two", "three"}, Enum: []string{"one", "two", "three"},
}, },
} })
desired = `url: https://pb33f.io desired = `url: https://pb33f.io
description: the b33f description: the b33f

View File

@@ -10,6 +10,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -96,8 +97,8 @@ type Schema struct {
If low.NodeReference[*SchemaProxy] If low.NodeReference[*SchemaProxy]
Else low.NodeReference[*SchemaProxy] Else low.NodeReference[*SchemaProxy]
Then low.NodeReference[*SchemaProxy] Then low.NodeReference[*SchemaProxy]
DependentSchemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] DependentSchemas low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] PatternProperties low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
PropertyNames low.NodeReference[*SchemaProxy] PropertyNames low.NodeReference[*SchemaProxy]
UnevaluatedItems low.NodeReference[*SchemaProxy] UnevaluatedItems low.NodeReference[*SchemaProxy]
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]] UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]
@@ -120,7 +121,7 @@ type Schema struct {
Required low.NodeReference[[]low.ValueReference[string]] Required low.NodeReference[[]low.ValueReference[string]]
Enum low.NodeReference[[]low.ValueReference[any]] Enum low.NodeReference[[]low.ValueReference[any]]
Not low.NodeReference[*SchemaProxy] Not low.NodeReference[*SchemaProxy]
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] Properties low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
AdditionalProperties low.NodeReference[any] AdditionalProperties low.NodeReference[any]
Description low.NodeReference[string] Description low.NodeReference[string]
ContentEncoding low.NodeReference[string] ContentEncoding low.NodeReference[string]
@@ -305,10 +306,10 @@ func (s *Schema) Hash() [32]byte {
for i := range s.Enum.Value { for i := range s.Enum.Value {
d = append(d, fmt.Sprint(s.Enum.Value[i].Value)) d = append(d, fmt.Sprint(s.Enum.Value[i].Value))
} }
propKeys := make([]string, len(s.Properties.Value)) propKeys := make([]string, orderedmap.Len(s.Properties.Value))
z := 0 z := 0
for i := range s.Properties.Value { for pair := orderedmap.First(s.Properties.Value); pair != nil; pair = pair.Next() {
propKeys[z] = i.Value propKeys[z] = pair.Key().Value
z++ z++
} }
sort.Strings(propKeys) sort.Strings(propKeys)
@@ -414,10 +415,10 @@ func (s *Schema) Hash() [32]byte {
d = append(d, fmt.Sprint(s.Anchor.Value)) d = append(d, fmt.Sprint(s.Anchor.Value))
} }
depSchemasKeys := make([]string, len(s.DependentSchemas.Value)) depSchemasKeys := make([]string, orderedmap.Len(s.DependentSchemas.Value))
z = 0 z = 0
for i := range s.DependentSchemas.Value { for pair := orderedmap.First(s.DependentSchemas.Value); pair != nil; pair = pair.Next() {
depSchemasKeys[z] = i.Value depSchemasKeys[z] = pair.Key().Value
z++ z++
} }
sort.Strings(depSchemasKeys) sort.Strings(depSchemasKeys)
@@ -425,10 +426,10 @@ func (s *Schema) Hash() [32]byte {
d = append(d, low.GenerateHashString(s.FindDependentSchema(depSchemasKeys[k]).Value)) d = append(d, low.GenerateHashString(s.FindDependentSchema(depSchemasKeys[k]).Value))
} }
patternPropsKeys := make([]string, len(s.PatternProperties.Value)) patternPropsKeys := make([]string, orderedmap.Len(s.PatternProperties.Value))
z = 0 z = 0
for i := range s.PatternProperties.Value { for pair := orderedmap.First(s.PatternProperties.Value); pair != nil; pair = pair.Next() {
patternPropsKeys[z] = i.Value patternPropsKeys[z] = pair.Key().Value
z++ z++
} }
sort.Strings(patternPropsKeys) sort.Strings(patternPropsKeys)
@@ -490,19 +491,19 @@ func (s *Schema) Hash() [32]byte {
// FindProperty will return a ValueReference pointer containing a SchemaProxy pointer // FindProperty will return a ValueReference pointer containing a SchemaProxy pointer
// from a property key name. if found // from a property key name. if found
func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] { func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] {
return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value) return low.FindItemInOrderedMap[*SchemaProxy](name, s.Properties.Value)
} }
// FindDependentSchema will return a ValueReference pointer containing a SchemaProxy pointer // FindDependentSchema will return a ValueReference pointer containing a SchemaProxy pointer
// from a dependent schema key name. if found (3.1+ only) // from a dependent schema key name. if found (3.1+ only)
func (s *Schema) FindDependentSchema(name string) *low.ValueReference[*SchemaProxy] { func (s *Schema) FindDependentSchema(name string) *low.ValueReference[*SchemaProxy] {
return low.FindItemInMap[*SchemaProxy](name, s.DependentSchemas.Value) return low.FindItemInOrderedMap[*SchemaProxy](name, s.DependentSchemas.Value)
} }
// FindPatternProperty will return a ValueReference pointer containing a SchemaProxy pointer // FindPatternProperty will return a ValueReference pointer containing a SchemaProxy pointer
// from a pattern property key name. if found (3.1+ only) // from a pattern property key name. if found (3.1+ only)
func (s *Schema) FindPatternProperty(name string) *low.ValueReference[*SchemaProxy] { func (s *Schema) FindPatternProperty(name string) *low.ValueReference[*SchemaProxy] {
return low.FindItemInMap[*SchemaProxy](name, s.PatternProperties.Value) return low.FindItemInOrderedMap[*SchemaProxy](name, s.PatternProperties.Value)
} }
// GetExtensions returns all extensions for Schema // GetExtensions returns all extensions for Schema
@@ -1067,7 +1068,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
return nil return nil
} }
func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]], error) { func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]], error) {
// for property, build in a new thread! // for property, build in a new thread!
bChan := make(chan schemaProxyBuildResult) bChan := make(chan schemaProxyBuildResult)
@@ -1088,7 +1089,7 @@ func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low
_, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content) _, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content)
if propsNode != nil { if propsNode != nil {
propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]) propertyMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*SchemaProxy]]()
var currentProp *yaml.Node var currentProp *yaml.Node
totalProps := 0 totalProps := 0
for i, prop := range propsNode.Content { for i, prop := range propsNode.Content {
@@ -1119,10 +1120,10 @@ func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low
select { select {
case res := <-bChan: case res := <-bChan:
completedProps++ completedProps++
propertyMap[res.k] = res.v propertyMap.Set(res.k, res.v)
} }
} }
return &low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]{ return &low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]{
Value: propertyMap, Value: propertyMap,
KeyNode: propLabel, KeyNode: propLabel,
ValueNode: propsNode, ValueNode: propsNode,

View File

@@ -6,12 +6,14 @@ package base
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// SecurityRequirement is a low-level representation of a Swagger / OpenAPI 3 SecurityRequirement object. // SecurityRequirement is a low-level representation of a Swagger / OpenAPI 3 SecurityRequirement object.
@@ -23,7 +25,7 @@ import (
// - https://swagger.io/specification/v2/#securityDefinitionsObject // - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/#security-requirement-object // - https://swagger.io/specification/#security-requirement-object
type SecurityRequirement struct { type SecurityRequirement struct {
Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]] Requirements low.ValueReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]]
*low.Reference *low.Reference
} }
@@ -33,7 +35,7 @@ func (s *SecurityRequirement) Build(_, root *yaml.Node, _ *index.SpecIndex) erro
utils.CheckForMergeNodes(root) utils.CheckForMergeNodes(root)
s.Reference = new(low.Reference) s.Reference = new(low.Reference)
var labelNode *yaml.Node var labelNode *yaml.Node
valueMap := make(map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]) valueMap := orderedmap.New[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]()
var arr []low.ValueReference[string] var arr []low.ValueReference[string]
for i := range root.Content { for i := range root.Content {
if i%2 == 0 { if i%2 == 0 {
@@ -47,15 +49,18 @@ func (s *SecurityRequirement) Build(_, root *yaml.Node, _ *index.SpecIndex) erro
ValueNode: root.Content[i].Content[j], ValueNode: root.Content[i].Content[j],
}) })
} }
valueMap[low.KeyReference[string]{ valueMap.Set(
Value: labelNode.Value, low.KeyReference[string]{
KeyNode: labelNode, Value: labelNode.Value,
}] = low.ValueReference[[]low.ValueReference[string]]{ KeyNode: labelNode,
Value: arr, },
ValueNode: root.Content[i], low.ValueReference[[]low.ValueReference[string]]{
} Value: arr,
ValueNode: root.Content[i],
},
)
} }
s.Requirements = low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]{ s.Requirements = low.ValueReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]]{
Value: valueMap, Value: valueMap,
ValueNode: root, ValueNode: root,
} }
@@ -64,9 +69,9 @@ func (s *SecurityRequirement) Build(_, root *yaml.Node, _ *index.SpecIndex) erro
// FindRequirement will attempt to locate a security requirement string from a supplied name. // FindRequirement will attempt to locate a security requirement string from a supplied name.
func (s *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] { func (s *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] {
for k := range s.Requirements.Value { for pair := orderedmap.First(s.Requirements.Value); pair != nil; pair = pair.Next() {
if k.Value == name { if pair.Key().Value == name {
return s.Requirements.Value[k].Value return pair.Value().Value
} }
} }
return nil return nil
@@ -74,10 +79,10 @@ func (s *SecurityRequirement) FindRequirement(name string) []low.ValueReference[
// GetKeys returns a string slice of all the keys used in the requirement. // GetKeys returns a string slice of all the keys used in the requirement.
func (s *SecurityRequirement) GetKeys() []string { func (s *SecurityRequirement) GetKeys() []string {
keys := make([]string, len(s.Requirements.Value)) keys := make([]string, orderedmap.Len(s.Requirements.Value))
z := 0 z := 0
for k := range s.Requirements.Value { for pair := orderedmap.First(s.Requirements.Value); pair != nil; pair = pair.Next() {
keys[z] = k.Value keys[z] = pair.Key().Value
} }
return keys return keys
} }
@@ -85,18 +90,17 @@ func (s *SecurityRequirement) GetKeys() []string {
// Hash will return a consistent SHA256 Hash of the SecurityRequirement object // Hash will return a consistent SHA256 Hash of the SecurityRequirement object
func (s *SecurityRequirement) Hash() [32]byte { func (s *SecurityRequirement) Hash() [32]byte {
var f []string var f []string
values := make(map[string][]string, len(s.Requirements.Value)) values := make(map[string][]string, orderedmap.Len(s.Requirements.Value))
var valKeys []string var valKeys []string
for k := range s.Requirements.Value { for pair := orderedmap.First(s.Requirements.Value); pair != nil; pair = pair.Next() {
var vals []string var vals []string
for y := range s.Requirements.Value[k].Value { for y := range pair.Value().Value {
vals = append(vals, s.Requirements.Value[k].Value[y].Value) vals = append(vals, pair.Value().Value[y].Value)
// lol, I know. -------^^^^^ <- this is the actual value.
} }
sort.Strings(vals) sort.Strings(vals)
valKeys = append(valKeys, k.Value) valKeys = append(valKeys, pair.Key().Value)
if len(vals) > 0 { if len(vals) > 0 {
values[k.Value] = vals values[pair.Key().Value] = vals
} }
} }
sort.Strings(valKeys) sort.Strings(valKeys)

View File

@@ -6,13 +6,15 @@ package low
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
"reflect" "reflect"
"strconv" "strconv"
"strings" "strings"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
) )
// FindItemInMap accepts a string key and a collection of KeyReference[string] and ValueReference[T]. Every // FindItemInMap accepts a string key and a collection of KeyReference[string] and ValueReference[T]. Every
@@ -29,6 +31,22 @@ func FindItemInMap[T any](item string, collection map[KeyReference[string]]Value
return nil return nil
} }
// FindItemInOrderedMap accepts a string key and a collection of KeyReference[string] and ValueReference[T].
// Every KeyReference will have its value checked against the string key and if there is a match, it will be
// returned.
func FindItemInOrderedMap[T any](item string, collection orderedmap.Map[KeyReference[string], ValueReference[T]]) *ValueReference[T] {
for pair := orderedmap.First(collection); pair != nil; pair = pair.Next() {
n := pair.Key()
if n.Value == item {
return pair.ValuePtr()
}
if strings.EqualFold(item, n.Value) {
return pair.ValuePtr()
}
}
return nil
}
// helper function to generate a list of all the things an index should be searched for. // helper function to generate a list of all the things an index should be searched for.
func generateIndexCollection(idx *index.SpecIndex) []func() map[string]*index.Reference { func generateIndexCollection(idx *index.SpecIndex) []func() map[string]*index.Reference {
return []func() map[string]*index.Reference{ return []func() map[string]*index.Reference{
@@ -366,8 +384,8 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
root *yaml.Node, root *yaml.Node,
idx *index.SpecIndex, idx *index.SpecIndex,
includeExtensions bool, includeExtensions bool,
) (map[KeyReference[string]]ValueReference[PT], error) { ) (orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
valueMap := make(map[KeyReference[string]]ValueReference[PT]) valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]()
var circError error var circError error
if utils.IsNodeMap(root) { if utils.IsNodeMap(root) {
var currentKey *yaml.Node var currentKey *yaml.Node
@@ -431,15 +449,18 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
SetReference(n, referenceValue) SetReference(n, referenceValue)
} }
if currentKey != nil { if currentKey != nil {
valueMap[KeyReference[string]{ valueMap.Set(
Value: currentKey.Value, KeyReference[string]{
KeyNode: currentKey, Value: currentKey.Value,
}] = ValueReference[PT]{ KeyNode: currentKey,
Value: n, },
ValueNode: node, ValueReference[PT]{
//IsReference: isReference, Value: n,
Reference: referenceValue, ValueNode: node,
} //IsReference: isReference,
Reference: referenceValue,
},
)
} }
} }
} }
@@ -458,7 +479,7 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
func ExtractMapNoLookup[PT Buildable[N], N any]( func ExtractMapNoLookup[PT Buildable[N], N any](
root *yaml.Node, root *yaml.Node,
idx *index.SpecIndex, idx *index.SpecIndex,
) (map[KeyReference[string]]ValueReference[PT], error) { ) (orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
return ExtractMapNoLookupExtensions[PT, N](root, idx, false) return ExtractMapNoLookupExtensions[PT, N](root, idx, false)
} }
@@ -478,7 +499,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
root *yaml.Node, root *yaml.Node,
idx *index.SpecIndex, idx *index.SpecIndex,
extensions bool, extensions bool,
) (map[KeyReference[string]]ValueReference[PT], *yaml.Node, *yaml.Node, error) { ) (orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) {
//var isReference bool //var isReference bool
var referenceValue string var referenceValue string
var labelNode, valueNode *yaml.Node var labelNode, valueNode *yaml.Node
@@ -522,8 +543,9 @@ func ExtractMapExtensions[PT Buildable[N], N any](
} }
if valueNode != nil { if valueNode != nil {
var currentLabelNode *yaml.Node var currentLabelNode *yaml.Node
valueMap := make(map[KeyReference[string]]ValueReference[PT]) valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]()
// TODO: Convert to datamodel.TranslatePipeline.
bChan := make(chan mappingResult[PT]) bChan := make(chan mappingResult[PT])
eChan := make(chan error) eChan := make(chan error)
@@ -598,7 +620,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
return valueMap, labelNode, valueNode, err return valueMap, labelNode, valueNode, err
case res := <-bChan: case res := <-bChan:
completedKeys++ completedKeys++
valueMap[res.k] = res.v valueMap.Set(res.k, res.v)
} }
} }
if circError != nil && !idx.AllowCircularReferenceResolving() { if circError != nil && !idx.AllowCircularReferenceResolving() {
@@ -618,7 +640,7 @@ func ExtractMap[PT Buildable[N], N any](
label string, label string,
root *yaml.Node, root *yaml.Node,
idx *index.SpecIndex, idx *index.SpecIndex,
) (map[KeyReference[string]]ValueReference[PT], *yaml.Node, *yaml.Node, error) { ) (orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) {
return ExtractMapExtensions[PT, N](label, root, idx, false) return ExtractMapExtensions[PT, N](label, root, idx, false)
} }

View File

@@ -11,6 +11,7 @@ import (
"testing" "testing"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/resolver" "github.com/pb33f/libopenapi/resolver"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -795,7 +796,7 @@ func TestExtractArray_Ref_Nested(t *testing.T) {
errs := resolve.CheckForCircularReferences() errs := resolve.CheckForCircularReferences()
assert.Len(t, errs, 1) assert.Len(t, errs, 1)
yml = `limes: yml = `limes:
$ref: '#/components/schemas/let-us-eat-cake'` $ref: '#/components/schemas/let-us-eat-cake'`
var cNode yaml.Node var cNode yaml.Node
@@ -824,7 +825,7 @@ func TestExtractArray_Ref_Nested_Circular(t *testing.T) {
errs := resolve.CheckForCircularReferences() errs := resolve.CheckForCircularReferences()
assert.Len(t, errs, 1) assert.Len(t, errs, 1)
yml = `limes: yml = `limes:
- $ref: '#/components/schemas/things'` - $ref: '#/components/schemas/things'`
var cNode yaml.Node var cNode yaml.Node
@@ -851,7 +852,7 @@ func TestExtractArray_Ref_Nested_BadRef(t *testing.T) {
assert.NoError(t, mErr) assert.NoError(t, mErr)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
yml = `limes: yml = `limes:
- $ref: '#/components/schemas/thangs'` - $ref: '#/components/schemas/thangs'`
var cNode yaml.Node var cNode yaml.Node
@@ -880,7 +881,7 @@ func TestExtractArray_Ref_Nested_CircularFlat(t *testing.T) {
errs := resolve.CheckForCircularReferences() errs := resolve.CheckForCircularReferences()
assert.Len(t, errs, 1) assert.Len(t, errs, 1)
yml = `limes: yml = `limes:
$ref: '#/components/schemas/things'` $ref: '#/components/schemas/things'`
var cNode yaml.Node var cNode yaml.Node
@@ -902,7 +903,7 @@ func TestExtractArray_BadBuild(t *testing.T) {
assert.NoError(t, mErr) assert.NoError(t, mErr)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig()) idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateClosedAPIIndexConfig())
yml = `limes: yml = `limes:
- dontWork: 1` - dontWork: 1`
var cNode yaml.Node var cNode yaml.Node
@@ -994,12 +995,12 @@ one:
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, things, 2) assert.Len(t, things, 2)
for k, v := range things { for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
if k.Value == "x-hey" { if pair.Key().Value == "x-hey" {
continue continue
} }
assert.Equal(t, "one", k.Value) assert.Equal(t, "one", pair.Key().Value)
assert.Len(t, v.ValueNode.Content, 2) assert.Len(t, pair.Value().ValueNode.Content, 2)
} }
} }
@@ -1050,8 +1051,8 @@ one:
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, things, 1) assert.Len(t, things, 1)
for k := range things { for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
assert.Equal(t, "one", k.Value) assert.Equal(t, "one", pair.Key().Value)
} }
} }
@@ -1283,8 +1284,8 @@ one:
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, things, 1) assert.Len(t, things, 1)
for k := range things { for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
assert.Equal(t, 99, things[k].Value.AlmostWork.Value) assert.Equal(t, 99, pair.Value().Value.AlmostWork.Value)
} }
} }
@@ -1313,8 +1314,8 @@ func TestExtractMapFlat_DoubleRef(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
assert.Len(t, things, 1) assert.Len(t, things, 1)
for k := range things { for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
assert.Equal(t, 99, things[k].Value.AlmostWork.Value) assert.Equal(t, 99, pair.Value().Value.AlmostWork.Value)
} }
} }

View File

@@ -5,13 +5,15 @@ package v2
import ( import (
"crypto/sha256" "crypto/sha256"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings"
) )
// ParameterDefinitions is a low-level representation of a Swagger / OpenAPI 2 Parameters Definitions object. // ParameterDefinitions is a low-level representation of a Swagger / OpenAPI 2 Parameters Definitions object.
@@ -20,7 +22,7 @@ import (
// referenced to the ones defined here. It does not define global operation parameters // referenced to the ones defined here. It does not define global operation parameters
// - https://swagger.io/specification/v2/#parametersDefinitionsObject // - https://swagger.io/specification/v2/#parametersDefinitionsObject
type ParameterDefinitions struct { type ParameterDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter] Definitions orderedmap.Map[low.KeyReference[string], low.ValueReference[*Parameter]]
} }
// ResponsesDefinitions is a low-level representation of a Swagger / OpenAPI 2 Responses Definitions object. // ResponsesDefinitions is a low-level representation of a Swagger / OpenAPI 2 Responses Definitions object.
@@ -29,7 +31,7 @@ type ParameterDefinitions struct {
// referenced to the ones defined here. It does not define global operation responses // referenced to the ones defined here. It does not define global operation responses
// - https://swagger.io/specification/v2/#responsesDefinitionsObject // - https://swagger.io/specification/v2/#responsesDefinitionsObject
type ResponsesDefinitions struct { type ResponsesDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*Response] Definitions orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]
} }
// SecurityDefinitions is a low-level representation of a Swagger / OpenAPI 2 Security Definitions object. // SecurityDefinitions is a low-level representation of a Swagger / OpenAPI 2 Security Definitions object.
@@ -38,7 +40,7 @@ type ResponsesDefinitions struct {
// schemes on the operations and only serves to provide the relevant details for each scheme // schemes on the operations and only serves to provide the relevant details for each scheme
// - https://swagger.io/specification/v2/#securityDefinitionsObject // - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityDefinitions struct { type SecurityDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme] Definitions orderedmap.Map[low.KeyReference[string], low.ValueReference[*SecurityScheme]]
} }
// Definitions is a low-level representation of a Swagger / OpenAPI 2 Definitions object // Definitions is a low-level representation of a Swagger / OpenAPI 2 Definitions object
@@ -47,33 +49,34 @@ type SecurityDefinitions struct {
// arrays or models. // arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject // - https://swagger.io/specification/v2/#definitionsObject
type Definitions struct { type Definitions struct {
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] Schemas orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]
} }
// FindSchema will attempt to locate a base.SchemaProxy instance using a name. // FindSchema will attempt to locate a base.SchemaProxy instance using a name.
func (d *Definitions) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] { func (d *Definitions) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] {
return low.FindItemInMap[*base.SchemaProxy](schema, d.Schemas) return low.FindItemInOrderedMap[*base.SchemaProxy](schema, d.Schemas)
} }
// FindParameter will attempt to locate a Parameter instance using a name. // FindParameter will attempt to locate a Parameter instance using a name.
func (pd *ParameterDefinitions) FindParameter(parameter string) *low.ValueReference[*Parameter] { func (pd *ParameterDefinitions) FindParameter(parameter string) *low.ValueReference[*Parameter] {
return low.FindItemInMap[*Parameter](parameter, pd.Definitions) return low.FindItemInOrderedMap[*Parameter](parameter, pd.Definitions)
} }
// FindResponse will attempt to locate a Response instance using a name. // FindResponse will attempt to locate a Response instance using a name.
func (r *ResponsesDefinitions) FindResponse(response string) *low.ValueReference[*Response] { func (r *ResponsesDefinitions) FindResponse(response string) *low.ValueReference[*Response] {
return low.FindItemInMap[*Response](response, r.Definitions) return low.FindItemInOrderedMap[*Response](response, r.Definitions)
} }
// FindSecurityDefinition will attempt to locate a SecurityScheme using a name. // FindSecurityDefinition will attempt to locate a SecurityScheme using a name.
func (s *SecurityDefinitions) FindSecurityDefinition(securityDef string) *low.ValueReference[*SecurityScheme] { func (s *SecurityDefinitions) FindSecurityDefinition(securityDef string) *low.ValueReference[*SecurityScheme] {
return low.FindItemInMap[*SecurityScheme](securityDef, s.Definitions) return low.FindItemInOrderedMap[*SecurityScheme](securityDef, s.Definitions)
} }
// Build will extract all definitions into SchemaProxy instances. // Build will extract all definitions into SchemaProxy instances.
func (d *Definitions) Build(_, root *yaml.Node, idx *index.SpecIndex) error { func (d *Definitions) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
root = utils.NodeAlias(root) root = utils.NodeAlias(root)
utils.CheckForMergeNodes(root) utils.CheckForMergeNodes(root)
// TODO: Refactor with orderedmap.TranslatePipeline.
errorChan := make(chan error) errorChan := make(chan error)
resultChan := make(chan definitionResult[*base.SchemaProxy]) resultChan := make(chan definitionResult[*base.SchemaProxy])
var defLabel *yaml.Node var defLabel *yaml.Node
@@ -99,17 +102,18 @@ func (d *Definitions) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
} }
completedDefs := 0 completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]) results := orderedmap.New[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]()
for completedDefs < totalDefinitions { for completedDefs < totalDefinitions {
select { select {
case err := <-errorChan: case err := <-errorChan:
return err return err
case sch := <-resultChan: case sch := <-resultChan:
completedDefs++ completedDefs++
results[low.KeyReference[string]{ key := low.KeyReference[string]{
Value: sch.k.Value, Value: sch.k.Value,
KeyNode: sch.k, KeyNode: sch.k,
}] = sch.v }
results.Set(key, sch.v)
} }
} }
d.Schemas = results d.Schemas = results
@@ -119,10 +123,10 @@ func (d *Definitions) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
// Hash will return a consistent SHA256 Hash of the Definitions object // Hash will return a consistent SHA256 Hash of the Definitions object
func (d *Definitions) Hash() [32]byte { func (d *Definitions) Hash() [32]byte {
var f []string var f []string
keys := make([]string, len(d.Schemas)) keys := make([]string, orderedmap.Len(d.Schemas))
z := 0 z := 0
for k := range d.Schemas { for pair := orderedmap.First(d.Schemas); pair != nil; pair = pair.Next() {
keys[z] = k.Value keys[z] = pair.Key().Value
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)
@@ -158,17 +162,18 @@ func (pd *ParameterDefinitions) Build(_, root *yaml.Node, idx *index.SpecIndex)
} }
completedDefs := 0 completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*Parameter]) results := orderedmap.New[low.KeyReference[string], low.ValueReference[*Parameter]]()
for completedDefs < totalDefinitions { for completedDefs < totalDefinitions {
select { select {
case err := <-errorChan: case err := <-errorChan:
return err return err
case sch := <-resultChan: case sch := <-resultChan:
completedDefs++ completedDefs++
results[low.KeyReference[string]{ key := low.KeyReference[string]{
Value: sch.k.Value, Value: sch.k.Value,
KeyNode: sch.k, KeyNode: sch.k,
}] = sch.v }
results.Set(key, sch.v)
} }
} }
pd.Definitions = results pd.Definitions = results
@@ -207,17 +212,18 @@ func (r *ResponsesDefinitions) Build(_, root *yaml.Node, idx *index.SpecIndex) e
} }
completedDefs := 0 completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*Response]) results := orderedmap.New[low.KeyReference[string], low.ValueReference[*Response]]()
for completedDefs < totalDefinitions { for completedDefs < totalDefinitions {
select { select {
case err := <-errorChan: case err := <-errorChan:
return err return err
case sch := <-resultChan: case sch := <-resultChan:
completedDefs++ completedDefs++
results[low.KeyReference[string]{ key := low.KeyReference[string]{
Value: sch.k.Value, Value: sch.k.Value,
KeyNode: sch.k, KeyNode: sch.k,
}] = sch.v }
results.Set(key, sch.v)
} }
} }
r.Definitions = results r.Definitions = results
@@ -253,17 +259,18 @@ func (s *SecurityDefinitions) Build(_, root *yaml.Node, idx *index.SpecIndex) er
} }
completedDefs := 0 completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]) results := orderedmap.New[low.KeyReference[string], low.ValueReference[*SecurityScheme]]()
for completedDefs < totalDefinitions { for completedDefs < totalDefinitions {
select { select {
case err := <-errorChan: case err := <-errorChan:
return err return err
case sch := <-resultChan: case sch := <-resultChan:
completedDefs++ completedDefs++
results[low.KeyReference[string]{ key := low.KeyReference[string]{
Value: sch.k.Value, Value: sch.k.Value,
KeyNode: sch.k, KeyNode: sch.k,
}] = sch.v }
results.Set(key, sch.v)
} }
} }
s.Definitions = results s.Definitions = results

View File

@@ -6,24 +6,26 @@ package v2
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Examples represents a low-level Swagger / OpenAPI 2 Example object. // Examples represents a low-level Swagger / OpenAPI 2 Example object.
// Allows sharing examples for operation responses // Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject // - https://swagger.io/specification/v2/#exampleObject
type Examples struct { type Examples struct {
Values map[low.KeyReference[string]]low.ValueReference[any] Values orderedmap.Map[low.KeyReference[string], low.ValueReference[any]]
} }
// FindExample attempts to locate an example value, using a key label. // FindExample attempts to locate an example value, using a key label.
func (e *Examples) FindExample(name string) *low.ValueReference[any] { func (e *Examples) FindExample(name string) *low.ValueReference[any] {
return low.FindItemInMap[any](name, e.Values) return low.FindItemInOrderedMap[any](name, e.Values)
} }
// Build will extract all examples and will attempt to unmarshal content into a map or slice based on type. // Build will extract all examples and will attempt to unmarshal content into a map or slice based on type.
@@ -32,7 +34,7 @@ func (e *Examples) Build(_, root *yaml.Node, _ *index.SpecIndex) error {
utils.CheckForMergeNodes(root) utils.CheckForMergeNodes(root)
var keyNode, currNode *yaml.Node var keyNode, currNode *yaml.Node
var err error var err error
e.Values = make(map[low.KeyReference[string]]low.ValueReference[any]) e.Values = orderedmap.New[low.KeyReference[string], low.ValueReference[any]]()
for i := range root.Content { for i := range root.Content {
if i%2 == 0 { if i%2 == 0 {
keyNode = root.Content[i] keyNode = root.Content[i]
@@ -48,32 +50,40 @@ func (e *Examples) Build(_, root *yaml.Node, _ *index.SpecIndex) error {
// lets just default to interface // lets just default to interface
var j interface{} var j interface{}
_ = currNode.Decode(&j) _ = currNode.Decode(&j)
e.Values[low.KeyReference[string]{ e.Values.Set(
Value: keyNode.Value, low.KeyReference[string]{
KeyNode: keyNode, Value: keyNode.Value,
}] = low.ValueReference[any]{ KeyNode: keyNode,
Value: j, },
ValueNode: currNode, low.ValueReference[any]{
} Value: j,
ValueNode: currNode,
},
)
continue continue
} }
e.Values[low.KeyReference[string]{ e.Values.Set(
Value: keyNode.Value, low.KeyReference[string]{
KeyNode: keyNode, Value: keyNode.Value,
}] = low.ValueReference[any]{ KeyNode: keyNode,
Value: k, },
ValueNode: currNode, low.ValueReference[any]{
} Value: k,
ValueNode: currNode,
},
)
continue continue
} }
e.Values[low.KeyReference[string]{ e.Values.Set(
Value: keyNode.Value, low.KeyReference[string]{
KeyNode: keyNode, Value: keyNode.Value,
}] = low.ValueReference[any]{ KeyNode: keyNode,
Value: n, },
ValueNode: currNode, low.ValueReference[any]{
} Value: n,
ValueNode: currNode,
},
)
} }
return nil return nil
} }
@@ -81,10 +91,10 @@ func (e *Examples) Build(_, root *yaml.Node, _ *index.SpecIndex) error {
// Hash will return a consistent SHA256 Hash of the Examples object // Hash will return a consistent SHA256 Hash of the Examples object
func (e *Examples) Hash() [32]byte { func (e *Examples) Hash() [32]byte {
var f []string var f []string
keys := make([]string, len(e.Values)) keys := make([]string, orderedmap.Len(e.Values))
z := 0 z := 0
for k := range e.Values { for pair := orderedmap.First(e.Values); pair != nil; pair = pair.Next() {
keys[z] = k.Value keys[z] = pair.Key().Value
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,7 +6,6 @@ package v2
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"io"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -32,28 +31,24 @@ func (p *Paths) GetExtensions() map[low.KeyReference[string]]low.ValueReference[
// FindPath attempts to locate a PathItem instance, given a path key. // FindPath attempts to locate a PathItem instance, given a path key.
func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) { func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) {
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
if pair.Key().Value == path { if pair.Key().Value == path {
result = pair.ValuePtr() result = pair.ValuePtr()
return io.EOF break
} }
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
return result return result
} }
// FindPathAndKey attempts to locate a PathItem instance, given a path key. // FindPathAndKey attempts to locate a PathItem instance, given a path key.
func (p *Paths) FindPathAndKey(path string) (key *low.KeyReference[string], value *low.ValueReference[*PathItem]) { 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 { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
if pair.Key().Value == path { if pair.Key().Value == path {
key = pair.KeyPtr() key = pair.KeyPtr()
value = pair.ValuePtr() value = pair.ValuePtr()
return io.EOF break
} }
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
return key, value return key, value
} }
@@ -167,14 +162,12 @@ func (p *Paths) Hash() [32]byte {
keys := make(map[string]low.ValueReference[*PathItem]) keys := make(map[string]low.ValueReference[*PathItem])
z := 0 z := 0
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
k := pair.Key().Value k := pair.Key().Value
keys[k] = pair.Value() keys[k] = pair.Value()
l[z] = k l[z] = k
z++ z++
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
sort.Strings(l) sort.Strings(l)
for k := range l { for k := range l {

View File

@@ -6,13 +6,15 @@ package v2
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings"
) )
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one. // Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
@@ -22,7 +24,7 @@ import (
type Response struct { type Response struct {
Description low.NodeReference[string] Description low.NodeReference[string]
Schema low.NodeReference[*base.SchemaProxy] Schema low.NodeReference[*base.SchemaProxy]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] Headers low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]
Examples low.NodeReference[*Examples] Examples low.NodeReference[*Examples]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
@@ -39,7 +41,7 @@ func (r *Response) GetExtensions() map[low.KeyReference[string]]low.ValueReferen
// FindHeader will attempt to locate a Header value, given a key // FindHeader will attempt to locate a Header value, given a key
func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] { func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
return low.FindItemInMap[*Header](hType, r.Headers.Value) return low.FindItemInOrderedMap[*Header](hType, r.Headers.Value)
} }
// Build will extract schema, extensions, examples and headers from node // Build will extract schema, extensions, examples and headers from node
@@ -68,7 +70,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return err return err
} }
if headers != nil { if headers != nil {
r.Headers = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]{ r.Headers = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]{
Value: headers, Value: headers,
KeyNode: lN, KeyNode: lN,
ValueNode: kN, ValueNode: kN,
@@ -87,8 +89,8 @@ func (r *Response) Hash() [32]byte {
f = append(f, low.GenerateHashString(r.Schema.Value)) f = append(f, low.GenerateHashString(r.Schema.Value))
} }
if !r.Examples.IsEmpty() { if !r.Examples.IsEmpty() {
for k := range r.Examples.Value.Values { for pair := orderedmap.First(r.Examples.Value.Values); pair != nil; pair = pair.Next() {
f = append(f, low.GenerateHashString(r.Examples.Value.Values[k].Value)) f = append(f, low.GenerateHashString(pair.Value().Value))
} }
} }
keys := make([]string, len(r.Extensions)) keys := make([]string, len(r.Extensions))

View File

@@ -6,17 +6,19 @@ package v2
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Responses is a low-level representation of a Swagger / OpenAPI 2 Responses object. // Responses is a low-level representation of a Swagger / OpenAPI 2 Responses object.
type Responses struct { type Responses struct {
Codes map[low.KeyReference[string]]low.ValueReference[*Response] Codes orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]
Default low.NodeReference[*Response] Default low.NodeReference[*Response]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
@@ -55,12 +57,12 @@ func (r *Responses) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
} }
func (r *Responses) getDefault() *low.NodeReference[*Response] { func (r *Responses) getDefault() *low.NodeReference[*Response] {
for n, o := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
if strings.ToLower(n.Value) == DefaultLabel { if strings.ToLower(pair.Key().Value) == DefaultLabel {
return &low.NodeReference[*Response]{ return &low.NodeReference[*Response]{
ValueNode: o.ValueNode, ValueNode: pair.Value().ValueNode,
KeyNode: n.KeyNode, KeyNode: pair.Key().KeyNode,
Value: o.Value, Value: pair.Value().Value,
} }
} }
} }
@@ -71,34 +73,34 @@ func (r *Responses) getDefault() *low.NodeReference[*Response] {
func (r *Responses) deleteCode(code string) { func (r *Responses) deleteCode(code string) {
var key *low.KeyReference[string] var key *low.KeyReference[string]
if r.Codes != nil { if r.Codes != nil {
for k := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
if k.Value == code { if pair.Key().Value == code {
key = &k key = pair.KeyPtr()
break break
} }
} }
} }
// should never be nil, but, you never know... science and all that! // should never be nil, but, you never know... science and all that!
if key != nil { if key != nil {
delete(r.Codes, *key) r.Codes.Delete(*key)
} }
} }
// FindResponseByCode will attempt to locate a Response instance using an HTTP response code string. // FindResponseByCode will attempt to locate a Response instance using an HTTP response code string.
func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] { func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] {
return low.FindItemInMap[*Response](code, r.Codes) return low.FindItemInOrderedMap[*Response](code, r.Codes)
} }
// Hash will return a consistent SHA256 Hash of the Examples object // Hash will return a consistent SHA256 Hash of the Examples object
func (r *Responses) Hash() [32]byte { func (r *Responses) Hash() [32]byte {
var f []string var f []string
var keys []string var keys []string
keys = make([]string, len(r.Codes)) keys = make([]string, orderedmap.Len(r.Codes))
cmap := make(map[string]*Response, len(keys)) cmap := make(map[string]*Response, len(keys))
z := 0 z := 0
for k := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
keys[z] = k.Value keys[z] = pair.Key().Value
cmap[k.Value] = r.Codes[k].Value cmap[pair.Key().Value] = pair.Value().Value
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,12 +6,14 @@ package v2
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object. // Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object.
@@ -19,7 +21,7 @@ import (
// Scopes lists the available scopes for an OAuth2 security scheme. // Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject // - https://swagger.io/specification/v2/#scopesObject
type Scopes struct { type Scopes struct {
Values map[low.KeyReference[string]]low.ValueReference[string] Values orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
@@ -30,7 +32,7 @@ func (s *Scopes) GetExtensions() map[low.KeyReference[string]]low.ValueReference
// FindScope will attempt to locate a scope string using a key. // FindScope will attempt to locate a scope string using a key.
func (s *Scopes) FindScope(scope string) *low.ValueReference[string] { func (s *Scopes) FindScope(scope string) *low.ValueReference[string] {
return low.FindItemInMap[string](scope, s.Values) return low.FindItemInOrderedMap[string](scope, s.Values)
} }
// Build will extract scope values and extensions from node. // Build will extract scope values and extensions from node.
@@ -38,20 +40,23 @@ func (s *Scopes) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
root = utils.NodeAlias(root) root = utils.NodeAlias(root)
utils.CheckForMergeNodes(root) utils.CheckForMergeNodes(root)
s.Extensions = low.ExtractExtensions(root) s.Extensions = low.ExtractExtensions(root)
valueMap := make(map[low.KeyReference[string]]low.ValueReference[string]) valueMap := orderedmap.New[low.KeyReference[string], low.ValueReference[string]]()
if utils.IsNodeMap(root) { if utils.IsNodeMap(root) {
for k := range root.Content { for k := range root.Content {
if k%2 == 0 { if k%2 == 0 {
if strings.Contains(root.Content[k].Value, "x-") { if strings.Contains(root.Content[k].Value, "x-") {
continue continue
} }
valueMap[low.KeyReference[string]{ valueMap.Set(
Value: root.Content[k].Value, low.KeyReference[string]{
KeyNode: root.Content[k], Value: root.Content[k].Value,
}] = low.ValueReference[string]{ KeyNode: root.Content[k],
Value: root.Content[k+1].Value, },
ValueNode: root.Content[k+1], low.ValueReference[string]{
} Value: root.Content[k+1].Value,
ValueNode: root.Content[k+1],
},
)
} }
} }
s.Values = valueMap s.Values = valueMap
@@ -62,12 +67,12 @@ func (s *Scopes) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
// Hash will return a consistent SHA256 Hash of the Scopes object // Hash will return a consistent SHA256 Hash of the Scopes object
func (s *Scopes) Hash() [32]byte { func (s *Scopes) Hash() [32]byte {
var f []string var f []string
vals := make(map[string]low.ValueReference[string], len(s.Values)) vals := make(map[string]low.ValueReference[string], orderedmap.Len(s.Values))
keys := make([]string, len(s.Values)) keys := make([]string, orderedmap.Len(s.Values))
z := 0 z := 0
for k := range s.Values { for pair := orderedmap.First(s.Values); pair != nil; pair = pair.Next() {
keys[z] = k.Value keys[z] = pair.Key().Value
vals[k.Value] = s.Values[k] vals[pair.Key().Value] = pair.Value()
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,10 +6,12 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/utils"
"sort" "sort"
"strings" "strings"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
@@ -23,7 +25,7 @@ import (
// that identifies a URL to use for the callback operation. // that identifies a URL to use for the callback operation.
// - https://spec.openapis.org/oas/v3.1.0#callback-object // - https://spec.openapis.org/oas/v3.1.0#callback-object
type Callback struct { type Callback struct {
Expression low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]] Expression low.ValueReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
@@ -35,7 +37,7 @@ func (cb *Callback) GetExtensions() map[low.KeyReference[string]]low.ValueRefere
// FindExpression will locate a string expression and return a ValueReference containing the located PathItem // FindExpression will locate a string expression and return a ValueReference containing the located PathItem
func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] { func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] {
return low.FindItemInMap[*PathItem](exp, cb.Expression.Value) return low.FindItemInOrderedMap[*PathItem](exp, cb.Expression.Value)
} }
// Build will extract extensions, expressions and PathItem objects for Callback // Build will extract extensions, expressions and PathItem objects for Callback
@@ -47,7 +49,7 @@ func (cb *Callback) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
// handle callback // handle callback
var currentCB *yaml.Node var currentCB *yaml.Node
callbacks := make(map[low.KeyReference[string]]low.ValueReference[*PathItem]) callbacks := orderedmap.New[low.KeyReference[string], low.ValueReference[*PathItem]]()
for i, callbackNode := range root.Content { for i, callbackNode := range root.Content {
if i%2 == 0 { if i%2 == 0 {
@@ -61,17 +63,20 @@ func (cb *Callback) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if eErr != nil { if eErr != nil {
return eErr return eErr
} }
callbacks[low.KeyReference[string]{ callbacks.Set(
Value: currentCB.Value, low.KeyReference[string]{
KeyNode: currentCB, Value: currentCB.Value,
}] = low.ValueReference[*PathItem]{ KeyNode: currentCB,
Value: callback, },
ValueNode: callbackNode, low.ValueReference[*PathItem]{
Reference: rv, Value: callback,
} ValueNode: callbackNode,
Reference: rv,
},
)
} }
if len(callbacks) > 0 { if orderedmap.Len(callbacks) > 0 {
cb.Expression = low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]{ cb.Expression = low.ValueReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]]{
Value: callbacks, Value: callbacks,
ValueNode: root, ValueNode: root,
} }
@@ -83,10 +88,10 @@ func (cb *Callback) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
func (cb *Callback) Hash() [32]byte { func (cb *Callback) Hash() [32]byte {
var f []string var f []string
var keys []string var keys []string
keys = make([]string, len(cb.Expression.Value)) keys = make([]string, orderedmap.Len(cb.Expression.Value))
z := 0 z := 0
for k := range cb.Expression.Value { for pair := orderedmap.First(cb.Expression.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(cb.Expression.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -14,6 +14,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -24,15 +25,15 @@ import (
// will have no effect on the API unless they are explicitly referenced from properties outside the components object. // will have no effect on the API unless they are explicitly referenced from properties outside the components object.
// - https://spec.openapis.org/oas/v3.1.0#components-object // - https://spec.openapis.org/oas/v3.1.0#components-object
type Components struct { type Components struct {
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]] Schemas low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]]
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]] Responses low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]]
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]] Parameters low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Parameter]]]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]] Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
RequestBodies low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*RequestBody]] RequestBodies low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*RequestBody]]]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] Headers low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]
SecuritySchemes low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]] SecuritySchemes low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SecurityScheme]]]
Links low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]] Links low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]]
Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]] Callbacks low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
@@ -75,16 +76,16 @@ func (co *Components) Hash() [32]byte {
return sha256.Sum256([]byte(strings.Join(f, "|"))) return sha256.Sum256([]byte(strings.Join(f, "|")))
} }
func generateHashForObjectMap[T any](collection map[low.KeyReference[string]]low.ValueReference[T], hash *[]string) { func generateHashForObjectMap[T any](collection orderedmap.Map[low.KeyReference[string], low.ValueReference[T]], hash *[]string) {
if collection == nil { if collection == nil {
return return
} }
l := make([]string, len(collection)) l := make([]string, orderedmap.Len(collection))
keys := make(map[string]low.ValueReference[T]) keys := make(map[string]low.ValueReference[T])
z := 0 z := 0
for k := range collection { for pair := orderedmap.First(collection); pair != nil; pair = pair.Next() {
keys[k.Value] = collection[k] keys[pair.Key().Value] = pair.Value()
l[z] = k.Value l[z] = pair.Key().Value
z++ z++
} }
sort.Strings(l) sort.Strings(l)
@@ -100,43 +101,43 @@ func (co *Components) FindExtension(ext string) *low.ValueReference[any] {
// FindSchema attempts to locate a SchemaProxy from 'schemas' with a specific name // FindSchema attempts to locate a SchemaProxy from 'schemas' with a specific name
func (co *Components) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] { func (co *Components) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] {
return low.FindItemInMap[*base.SchemaProxy](schema, co.Schemas.Value) return low.FindItemInOrderedMap[*base.SchemaProxy](schema, co.Schemas.Value)
} }
// FindResponse attempts to locate a Response from 'responses' with a specific name // FindResponse attempts to locate a Response from 'responses' with a specific name
func (co *Components) FindResponse(response string) *low.ValueReference[*Response] { func (co *Components) FindResponse(response string) *low.ValueReference[*Response] {
return low.FindItemInMap[*Response](response, co.Responses.Value) return low.FindItemInOrderedMap[*Response](response, co.Responses.Value)
} }
// FindParameter attempts to locate a Parameter from 'parameters' with a specific name // FindParameter attempts to locate a Parameter from 'parameters' with a specific name
func (co *Components) FindParameter(response string) *low.ValueReference[*Parameter] { func (co *Components) FindParameter(response string) *low.ValueReference[*Parameter] {
return low.FindItemInMap[*Parameter](response, co.Parameters.Value) return low.FindItemInOrderedMap[*Parameter](response, co.Parameters.Value)
} }
// FindSecurityScheme attempts to locate a SecurityScheme from 'securitySchemes' with a specific name // FindSecurityScheme attempts to locate a SecurityScheme from 'securitySchemes' with a specific name
func (co *Components) FindSecurityScheme(sScheme string) *low.ValueReference[*SecurityScheme] { func (co *Components) FindSecurityScheme(sScheme string) *low.ValueReference[*SecurityScheme] {
return low.FindItemInMap[*SecurityScheme](sScheme, co.SecuritySchemes.Value) return low.FindItemInOrderedMap[*SecurityScheme](sScheme, co.SecuritySchemes.Value)
} }
// FindExample attempts tp // FindExample attempts tp
func (co *Components) FindExample(example string) *low.ValueReference[*base.Example] { func (co *Components) FindExample(example string) *low.ValueReference[*base.Example] {
return low.FindItemInMap[*base.Example](example, co.Examples.Value) return low.FindItemInOrderedMap[*base.Example](example, co.Examples.Value)
} }
func (co *Components) FindRequestBody(requestBody string) *low.ValueReference[*RequestBody] { func (co *Components) FindRequestBody(requestBody string) *low.ValueReference[*RequestBody] {
return low.FindItemInMap[*RequestBody](requestBody, co.RequestBodies.Value) return low.FindItemInOrderedMap[*RequestBody](requestBody, co.RequestBodies.Value)
} }
func (co *Components) FindHeader(header string) *low.ValueReference[*Header] { func (co *Components) FindHeader(header string) *low.ValueReference[*Header] {
return low.FindItemInMap[*Header](header, co.Headers.Value) return low.FindItemInOrderedMap[*Header](header, co.Headers.Value)
} }
func (co *Components) FindLink(link string) *low.ValueReference[*Link] { func (co *Components) FindLink(link string) *low.ValueReference[*Link] {
return low.FindItemInMap[*Link](link, co.Links.Value) return low.FindItemInOrderedMap[*Link](link, co.Links.Value)
} }
func (co *Components) FindCallback(callback string) *low.ValueReference[*Callback] { func (co *Components) FindCallback(callback string) *low.ValueReference[*Callback] {
return low.FindItemInMap[*Callback](callback, co.Callbacks.Value) return low.FindItemInOrderedMap[*Callback](callback, co.Callbacks.Value)
} }
// Build converts root YAML node containing components to low level model. // Build converts root YAML node containing components to low level model.
@@ -222,13 +223,13 @@ func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
// extractComponentValues converts all the YAML nodes of a component type to // extractComponentValues converts all the YAML nodes of a component type to
// low level model. // low level model.
// Process each node in parallel. // Process each node in parallel.
func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]], error) { func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.Node, idx *index.SpecIndex) (low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]], error) {
var emptyResult low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]] var emptyResult low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]]
_, nodeLabel, nodeValue := utils.FindKeyNodeFullTop(label, root.Content) _, nodeLabel, nodeValue := utils.FindKeyNodeFullTop(label, root.Content)
if nodeValue == nil { if nodeValue == nil {
return emptyResult, nil return emptyResult, nil
} }
componentValues := make(map[low.KeyReference[string]]low.ValueReference[T]) componentValues := orderedmap.New[low.KeyReference[string], low.ValueReference[T]]()
if utils.IsNodeArray(nodeValue) { if utils.IsNodeArray(nodeValue) {
return emptyResult, fmt.Errorf("node is array, cannot be used in components: line %d, column %d", nodeValue.Line, nodeValue.Column) return emptyResult, fmt.Errorf("node is array, cannot be used in components: line %d, column %d", nodeValue.Line, nodeValue.Column)
} }
@@ -271,7 +272,7 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
// Collect output. // Collect output.
go func() { go func() {
for result := range out { for result := range out {
componentValues[result.key] = result.value componentValues.Set(result.key, result.value)
} }
close(done) close(done)
wg.Done() wg.Done()
@@ -317,7 +318,7 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
return emptyResult, err return emptyResult, err
} }
results := low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]]{ results := low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]]{
KeyNode: nodeLabel, KeyNode: nodeLabel,
ValueNode: nodeValue, ValueNode: nodeValue,
Value: componentValues, Value: componentValues,

View File

@@ -9,6 +9,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/resolver" "github.com/pb33f/libopenapi/resolver"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
) )
@@ -247,7 +248,7 @@ func extractWebhooks(info *datamodel.SpecInfo, doc *Document, idx *index.SpecInd
return eErr return eErr
} }
if hooks != nil { if hooks != nil {
doc.Webhooks = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]{ doc.Webhooks = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]]{
Value: hooks, Value: hooks,
KeyNode: hooksL, KeyNode: hooksL,
ValueNode: hooksN, ValueNode: hooksN,

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -225,10 +226,10 @@ func TestCreateDocument_Info(t *testing.T) {
func TestCreateDocument_WebHooks(t *testing.T) { func TestCreateDocument_WebHooks(t *testing.T) {
initTest() initTest()
assert.Len(t, doc.Webhooks.Value, 1) assert.Len(t, doc.Webhooks.Value, 1)
for i := range doc.Webhooks.Value { for pair := orderedmap.First(doc.Webhooks.Value); pair != nil; pair = pair.Next() {
// a nice deep model should be available for us. // a nice deep model should be available for us.
assert.Equal(t, "Information about a new burger", assert.Equal(t, "Information about a new burger",
doc.Webhooks.Value[i].Value.Post.Value.RequestBody.Value.Description.Value) pair.Value().Value.Post.Value.RequestBody.Value.Description.Value)
} }
} }

View File

@@ -12,6 +12,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
) )
type Document struct { type Document struct {
@@ -37,7 +38,7 @@ type Document struct {
// for example by an out-of-band registration. The key name is a unique string to refer to each webhook, // for example by an out-of-band registration. The key name is a unique string to refer to each webhook,
// while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider // while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider
// and the expected responses. An example is available. // and the expected responses. An example is available.
Webhooks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]] // 3.1 Webhooks low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*PathItem]]] // 3.1
// Servers is a slice of Server instances which provide connectivity information to a target server. If the servers // Servers is a slice of Server instances which provide connectivity information to a target server. If the servers
// property is not provided, or is an empty array, the default value would be a Server Object with an url value of /. // property is not provided, or is an empty array, the default value would be a Server Object with an url value of /.
@@ -87,9 +88,10 @@ type Document struct {
// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name. // FindSecurityRequirement will attempt to locate a security requirement string from a supplied name.
func (d *Document) FindSecurityRequirement(name string) []low.ValueReference[string] { func (d *Document) FindSecurityRequirement(name string) []low.ValueReference[string] {
for k := range d.Security.Value { for k := range d.Security.Value {
for i := range d.Security.Value[k].Value.Requirements.Value { requirements := d.Security.Value[k].Value.Requirements
if i.Value == name { for pair := orderedmap.First(requirements.Value); pair != nil; pair = pair.Next() {
return d.Security.Value[k].Value.Requirements.Value[i].Value if pair.Key().Value == name {
return pair.Value().Value
} }
} }
} }

View File

@@ -6,18 +6,20 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"strings"
) )
// Encoding represents a low-level OpenAPI 3+ Encoding object // Encoding represents a low-level OpenAPI 3+ Encoding object
// - https://spec.openapis.org/oas/v3.1.0#encoding-object // - https://spec.openapis.org/oas/v3.1.0#encoding-object
type Encoding struct { type Encoding struct {
ContentType low.NodeReference[string] ContentType low.NodeReference[string]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] Headers low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]
Style low.NodeReference[string] Style low.NodeReference[string]
Explode low.NodeReference[bool] Explode low.NodeReference[bool]
AllowReserved low.NodeReference[bool] AllowReserved low.NodeReference[bool]
@@ -26,7 +28,7 @@ type Encoding struct {
// FindHeader attempts to locate a Header with the supplied name // FindHeader attempts to locate a Header with the supplied name
func (en *Encoding) FindHeader(hType string) *low.ValueReference[*Header] { func (en *Encoding) FindHeader(hType string) *low.ValueReference[*Header] {
return low.FindItemInMap[*Header](hType, en.Headers.Value) return low.FindItemInOrderedMap[*Header](hType, en.Headers.Value)
} }
// Hash will return a consistent SHA256 Hash of the Encoding object // Hash will return a consistent SHA256 Hash of the Encoding object
@@ -35,18 +37,19 @@ func (en *Encoding) Hash() [32]byte {
if en.ContentType.Value != "" { if en.ContentType.Value != "" {
f = append(f, en.ContentType.Value) f = append(f, en.ContentType.Value)
} }
if len(en.Headers.Value) > 0 { if orderedmap.Len(en.Headers.Value) > 0 {
l := make([]string, len(en.Headers.Value)) l := make([]string, orderedmap.Len(en.Headers.Value))
keys := make(map[string]low.ValueReference[*Header]) keys := make(map[string]low.ValueReference[*Header])
z := 0 z := 0
for k := range en.Headers.Value { for pair := orderedmap.First(en.Headers.Value); pair != nil; pair = pair.Next() {
keys[k.Value] = en.Headers.Value[k] keys[pair.Key().Value] = pair.Value()
l[z] = k.Value l[z] = pair.Key().Value
z++ z++
} }
for k := range en.Headers.Value { // FIXME: Redundant iteration?
f = append(f, fmt.Sprintf("%s-%x", k.Value, en.Headers.Value[k].Value.Hash())) for pair := orderedmap.First(en.Headers.Value); pair != nil; pair = pair.Next() {
f = append(f, fmt.Sprintf("%s-%x", pair.Key().Value, pair.Value().Value.Hash()))
} }
} }
if en.Style.Value != "" { if en.Style.Value != "" {
@@ -67,7 +70,7 @@ func (en *Encoding) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return err return err
} }
if headers != nil { if headers != nil {
en.Headers = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]{ en.Headers = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]{
Value: headers, Value: headers,
KeyNode: hL, KeyNode: hL,
ValueNode: hN, ValueNode: hN,

View File

@@ -6,13 +6,15 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings"
) )
// Header represents a low-level OpenAPI 3+ Header object. // Header represents a low-level OpenAPI 3+ Header object.
@@ -27,8 +29,8 @@ type Header struct {
AllowReserved low.NodeReference[bool] AllowReserved low.NodeReference[bool]
Schema low.NodeReference[*base.SchemaProxy] Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any] Example low.NodeReference[any]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]] Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]] Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
@@ -40,12 +42,12 @@ func (h *Header) FindExtension(ext string) *low.ValueReference[any] {
// FindExample will attempt to locate an Example with a specified name // FindExample will attempt to locate an Example with a specified name
func (h *Header) FindExample(eType string) *low.ValueReference[*base.Example] { func (h *Header) FindExample(eType string) *low.ValueReference[*base.Example] {
return low.FindItemInMap[*base.Example](eType, h.Examples.Value) return low.FindItemInOrderedMap[*base.Example](eType, h.Examples.Value)
} }
// FindContent will attempt to locate a MediaType definition, with a specified name // FindContent will attempt to locate a MediaType definition, with a specified name
func (h *Header) FindContent(ext string) *low.ValueReference[*MediaType] { func (h *Header) FindContent(ext string) *low.ValueReference[*MediaType] {
return low.FindItemInMap[*MediaType](ext, h.Content.Value) return low.FindItemInOrderedMap[*MediaType](ext, h.Content.Value)
} }
// GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface. // GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface.
@@ -73,14 +75,14 @@ func (h *Header) Hash() [32]byte {
if h.Example.Value != nil { if h.Example.Value != nil {
f = append(f, fmt.Sprint(h.Example.Value)) f = append(f, fmt.Sprint(h.Example.Value))
} }
if len(h.Examples.Value) > 0 { if orderedmap.Len(h.Examples.Value) > 0 {
for k := range h.Examples.Value { for pair := orderedmap.First(h.Examples.Value); pair != nil; pair = pair.Next() {
f = append(f, fmt.Sprintf("%s-%x", k.Value, h.Examples.Value[k].Value.Hash())) f = append(f, fmt.Sprintf("%s-%x", pair.Key().Value, pair.Value().Value.Hash()))
} }
} }
if len(h.Content.Value) > 0 { if orderedmap.Len(h.Content.Value) > 0 {
for k := range h.Content.Value { for pair := orderedmap.First(h.Content.Value); pair != nil; pair = pair.Next() {
f = append(f, fmt.Sprintf("%s-%x", k.Value, h.Content.Value[k].Value.Hash())) f = append(f, fmt.Sprintf("%s-%x", pair.Key().Value, pair.Value().Value.Hash()))
} }
} }
keys := make([]string, len(h.Extensions)) keys := make([]string, len(h.Extensions))
@@ -113,7 +115,7 @@ func (h *Header) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return eErr return eErr
} }
if exps != nil { if exps != nil {
h.Examples = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]{ h.Examples = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{
Value: exps, Value: exps,
KeyNode: expsL, KeyNode: expsL,
ValueNode: expsN, ValueNode: expsN,
@@ -134,7 +136,7 @@ func (h *Header) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if cErr != nil { if cErr != nil {
return cErr return cErr
} }
h.Content = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]{ h.Content = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{
Value: con, Value: con,
KeyNode: cL, KeyNode: cL,
ValueNode: cN, ValueNode: cN,

View File

@@ -6,12 +6,14 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Link represents a low-level OpenAPI 3+ Link object. // Link represents a low-level OpenAPI 3+ Link object.
@@ -29,7 +31,7 @@ import (
type Link struct { type Link struct {
OperationRef low.NodeReference[string] OperationRef low.NodeReference[string]
OperationId low.NodeReference[string] OperationId low.NodeReference[string]
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]] Parameters low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]
RequestBody low.NodeReference[string] RequestBody low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
Server low.NodeReference[*Server] Server low.NodeReference[*Server]
@@ -44,7 +46,7 @@ func (l *Link) GetExtensions() map[low.KeyReference[string]]low.ValueReference[a
// FindParameter will attempt to locate a parameter string value, using a parameter name input. // FindParameter will attempt to locate a parameter string value, using a parameter name input.
func (l *Link) FindParameter(pName string) *low.ValueReference[string] { func (l *Link) FindParameter(pName string) *low.ValueReference[string] {
return low.FindItemInMap[string](pName, l.Parameters.Value) return low.FindItemInOrderedMap[string](pName, l.Parameters.Value)
} }
// FindExtension will attempt to locate an extension with a specific key // FindExtension will attempt to locate an extension with a specific key
@@ -87,10 +89,10 @@ func (l *Link) Hash() [32]byte {
} }
// todo: needs ordering. // todo: needs ordering.
keys := make([]string, len(l.Parameters.Value)) keys := make([]string, orderedmap.Len(l.Parameters.Value))
z := 0 z := 0
for k := range l.Parameters.Value { for pair := orderedmap.First(l.Parameters.Value); pair != nil; pair = pair.Next() {
keys[z] = l.Parameters.Value[k].Value keys[z] = pair.Value().Value
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,13 +6,15 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings"
) )
// MediaType represents a low-level OpenAPI MediaType object. // MediaType represents a low-level OpenAPI MediaType object.
@@ -22,8 +24,8 @@ import (
type MediaType struct { type MediaType struct {
Schema low.NodeReference[*base.SchemaProxy] Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any] Example low.NodeReference[any]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]] Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
Encoding low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]] Encoding low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
@@ -40,16 +42,16 @@ func (mt *MediaType) FindExtension(ext string) *low.ValueReference[any] {
// FindPropertyEncoding will attempt to locate an Encoding value with a specific name. // FindPropertyEncoding will attempt to locate an Encoding value with a specific name.
func (mt *MediaType) FindPropertyEncoding(eType string) *low.ValueReference[*Encoding] { func (mt *MediaType) FindPropertyEncoding(eType string) *low.ValueReference[*Encoding] {
return low.FindItemInMap[*Encoding](eType, mt.Encoding.Value) return low.FindItemInOrderedMap[*Encoding](eType, mt.Encoding.Value)
} }
// FindExample will attempt to locate an Example with a specific name. // FindExample will attempt to locate an Example with a specific name.
func (mt *MediaType) FindExample(eType string) *low.ValueReference[*base.Example] { func (mt *MediaType) FindExample(eType string) *low.ValueReference[*base.Example] {
return low.FindItemInMap[*base.Example](eType, mt.Examples.Value) return low.FindItemInOrderedMap[*base.Example](eType, mt.Examples.Value)
} }
// GetAllExamples will extract all examples from the MediaType instance. // GetAllExamples will extract all examples from the MediaType instance.
func (mt *MediaType) GetAllExamples() map[low.KeyReference[string]]low.ValueReference[*base.Example] { func (mt *MediaType) GetAllExamples() orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]] {
return mt.Examples.Value return mt.Examples.Value
} }
@@ -97,7 +99,7 @@ func (mt *MediaType) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return eErr return eErr
} }
if exps != nil { if exps != nil {
mt.Examples = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]{ mt.Examples = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{
Value: exps, Value: exps,
KeyNode: expsL, KeyNode: expsL,
ValueNode: expsN, ValueNode: expsN,
@@ -110,7 +112,7 @@ func (mt *MediaType) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return encErr return encErr
} }
if encs != nil { if encs != nil {
mt.Encoding = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]{ mt.Encoding = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]]{
Value: encs, Value: encs,
KeyNode: encsL, KeyNode: encsL,
ValueNode: encsN, ValueNode: encsN,
@@ -128,18 +130,18 @@ func (mt *MediaType) Hash() [32]byte {
if mt.Example.Value != nil { if mt.Example.Value != nil {
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(mt.Example.Value))))) f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(mt.Example.Value)))))
} }
keys := make([]string, len(mt.Examples.Value)) keys := make([]string, orderedmap.Len(mt.Examples.Value))
z := 0 z := 0
for k := range mt.Examples.Value { for pair := orderedmap.First(mt.Examples.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(mt.Examples.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)
f = append(f, keys...) f = append(f, keys...)
keys = make([]string, len(mt.Encoding.Value)) keys = make([]string, orderedmap.Len(mt.Encoding.Value))
z = 0 z = 0
for k := range mt.Encoding.Value { for pair := orderedmap.First(mt.Encoding.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(mt.Encoding.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
} }
sort.Strings(keys) sort.Strings(keys)
f = append(f, keys...) f = append(f, keys...)

View File

@@ -6,12 +6,14 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object. // OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object.
@@ -95,7 +97,7 @@ type OAuthFlow struct {
AuthorizationUrl low.NodeReference[string] AuthorizationUrl low.NodeReference[string]
TokenUrl low.NodeReference[string] TokenUrl low.NodeReference[string]
RefreshUrl low.NodeReference[string] RefreshUrl low.NodeReference[string]
Scopes low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]] Scopes low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
@@ -107,7 +109,7 @@ func (o *OAuthFlow) GetExtensions() map[low.KeyReference[string]]low.ValueRefere
// FindScope attempts to locate a scope using a specified name. // FindScope attempts to locate a scope using a specified name.
func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] { func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] {
return low.FindItemInMap[string](scope, o.Scopes.Value) return low.FindItemInOrderedMap[string](scope, o.Scopes.Value)
} }
// FindExtension attempts to locate an extension with a specified key // FindExtension attempts to locate an extension with a specified key
@@ -134,10 +136,10 @@ func (o *OAuthFlow) Hash() [32]byte {
if !o.RefreshUrl.IsEmpty() { if !o.RefreshUrl.IsEmpty() {
f = append(f, o.RefreshUrl.Value) f = append(f, o.RefreshUrl.Value)
} }
keys := make([]string, len(o.Scopes.Value)) keys := make([]string, orderedmap.Len(o.Scopes.Value))
z := 0 z := 0
for k := range o.Scopes.Value { for pair := orderedmap.First(o.Scopes.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", k.Value, sha256.Sum256([]byte(fmt.Sprint(o.Scopes.Value[k].Value)))) keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, sha256.Sum256([]byte(fmt.Sprint(pair.Value().Value))))
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,13 +6,15 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings"
) )
// Operation is a low-level representation of an OpenAPI 3+ Operation object. // Operation is a low-level representation of an OpenAPI 3+ Operation object.
@@ -29,7 +31,7 @@ type Operation struct {
Parameters low.NodeReference[[]low.ValueReference[*Parameter]] Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
RequestBody low.NodeReference[*RequestBody] RequestBody low.NodeReference[*RequestBody]
Responses low.NodeReference[*Responses] Responses low.NodeReference[*Responses]
Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]] Callbacks low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]]
Deprecated low.NodeReference[bool] Deprecated low.NodeReference[bool]
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]] Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
Servers low.NodeReference[[]low.ValueReference[*Server]] Servers low.NodeReference[[]low.ValueReference[*Server]]
@@ -39,15 +41,16 @@ type Operation struct {
// FindCallback will attempt to locate a Callback instance by the supplied name. // FindCallback will attempt to locate a Callback instance by the supplied name.
func (o *Operation) FindCallback(callback string) *low.ValueReference[*Callback] { func (o *Operation) FindCallback(callback string) *low.ValueReference[*Callback] {
return low.FindItemInMap[*Callback](callback, o.Callbacks.Value) return low.FindItemInOrderedMap[*Callback](callback, o.Callbacks.Value)
} }
// FindSecurityRequirement will attempt to locate a security requirement string from a supplied name. // FindSecurityRequirement will attempt to locate a security requirement string from a supplied name.
func (o *Operation) FindSecurityRequirement(name string) []low.ValueReference[string] { func (o *Operation) FindSecurityRequirement(name string) []low.ValueReference[string] {
for k := range o.Security.Value { for k := range o.Security.Value {
for i := range o.Security.Value[k].Value.Requirements.Value { requirements := o.Security.Value[k].Value.Requirements
if i.Value == name { for pair := orderedmap.First(requirements.Value); pair != nil; pair = pair.Next() {
return o.Security.Value[k].Value.Requirements.Value[i].Value if pair.Key().Value == name {
return pair.Value().Value
} }
} }
} }
@@ -101,7 +104,7 @@ func (o *Operation) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return cbErr return cbErr
} }
if callbacks != nil { if callbacks != nil {
o.Callbacks = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]]{ o.Callbacks = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]]{
Value: callbacks, Value: callbacks,
KeyNode: cbL, KeyNode: cbL,
ValueNode: cbN, ValueNode: cbN,
@@ -202,10 +205,10 @@ func (o *Operation) Hash() [32]byte {
sort.Strings(keys) sort.Strings(keys)
f = append(f, keys...) f = append(f, keys...)
keys = make([]string, len(o.Callbacks.Value)) keys = make([]string, orderedmap.Len(o.Callbacks.Value))
z := 0 z := 0
for k := range o.Callbacks.Value { for pair := orderedmap.First(o.Callbacks.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(o.Callbacks.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)
@@ -278,6 +281,6 @@ func (o *Operation) GetServers() low.NodeReference[any] {
Value: o.Servers.Value, Value: o.Servers.Value,
} }
} }
func (o *Operation) GetCallbacks() low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]] { func (o *Operation) GetCallbacks() low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]] {
return o.Callbacks return o.Callbacks
} }

View File

@@ -6,13 +6,15 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sort"
"strings"
) )
// Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one. // Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one.
@@ -31,20 +33,20 @@ type Parameter struct {
AllowReserved low.NodeReference[bool] AllowReserved low.NodeReference[bool]
Schema low.NodeReference[*base.SchemaProxy] Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any] Example low.NodeReference[any]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]] Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]] Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
// FindContent will attempt to locate a MediaType instance using the specified name. // FindContent will attempt to locate a MediaType instance using the specified name.
func (p *Parameter) FindContent(cType string) *low.ValueReference[*MediaType] { func (p *Parameter) FindContent(cType string) *low.ValueReference[*MediaType] {
return low.FindItemInMap[*MediaType](cType, p.Content.Value) return low.FindItemInOrderedMap[*MediaType](cType, p.Content.Value)
} }
// FindExample will attempt to locate a base.Example instance using the specified name. // FindExample will attempt to locate a base.Example instance using the specified name.
func (p *Parameter) FindExample(eType string) *low.ValueReference[*base.Example] { func (p *Parameter) FindExample(eType string) *low.ValueReference[*base.Example] {
return low.FindItemInMap[*base.Example](eType, p.Examples.Value) return low.FindItemInOrderedMap[*base.Example](eType, p.Examples.Value)
} }
// FindExtension attempts to locate an extension using the specified name. // FindExtension attempts to locate an extension using the specified name.
@@ -85,7 +87,7 @@ func (p *Parameter) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return eErr return eErr
} }
if exps != nil { if exps != nil {
p.Examples = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]{ p.Examples = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]{
Value: exps, Value: exps,
KeyNode: expsL, KeyNode: expsL,
ValueNode: expsN, ValueNode: expsN,
@@ -97,7 +99,7 @@ func (p *Parameter) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if cErr != nil { if cErr != nil {
return cErr return cErr
} }
p.Content = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]{ p.Content = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{
Value: con, Value: con,
KeyNode: cL, KeyNode: cL,
ValueNode: cN, ValueNode: cN,
@@ -133,18 +135,18 @@ func (p *Parameter) Hash() [32]byte {
} }
var keys []string var keys []string
keys = make([]string, len(p.Examples.Value)) keys = make([]string, orderedmap.Len(p.Examples.Value))
z := 0 z := 0
for k := range p.Examples.Value { for pair := orderedmap.First(p.Examples.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(p.Examples.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)
f = append(f, keys...) f = append(f, keys...)
keys = make([]string, len(p.Content.Value)) keys = make([]string, orderedmap.Len(p.Content.Value))
z = 0 z = 0
for k := range p.Content.Value { for pair := orderedmap.First(p.Content.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(p.Content.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,7 +6,6 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"io"
"sort" "sort"
"strings" "strings"
"sync" "sync"
@@ -33,28 +32,24 @@ type Paths struct {
// FindPath will attempt to locate a PathItem using the provided path string. // FindPath will attempt to locate a PathItem using the provided path string.
func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) { func (p *Paths) FindPath(path string) (result *low.ValueReference[*PathItem]) {
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
if pair.Key().Value == path { if pair.Key().Value == path {
result = pair.ValuePtr() result = pair.ValuePtr()
return io.EOF break
} }
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
return result return result
} }
// FindPathAndKey attempts to locate a PathItem instance, given a path key. // FindPathAndKey attempts to locate a PathItem instance, given a path key.
func (p *Paths) FindPathAndKey(path string) (key *low.KeyReference[string], value *low.ValueReference[*PathItem]) { 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 { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
if pair.Key().Value == path { if pair.Key().Value == path {
key = pair.KeyPtr() key = pair.KeyPtr()
value = pair.ValuePtr() value = pair.ValuePtr()
return io.EOF break
} }
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
return key, value return key, value
} }
@@ -198,14 +193,12 @@ func (p *Paths) Hash() [32]byte {
keys := make(map[string]low.ValueReference[*PathItem]) keys := make(map[string]low.ValueReference[*PathItem])
z := 0 z := 0
action := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*PathItem]]) error { for pair := orderedmap.First(p.PathItems); pair != nil; pair = pair.Next() {
k := pair.Key().Value k := pair.Key().Value
keys[k] = pair.Value() keys[k] = pair.Value()
l[z] = k l[z] = k
z++ z++
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*PathItem]](p.PathItems, action)
sort.Strings(l) sort.Strings(l)
for k := range l { for k := range l {

View File

@@ -6,19 +6,21 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// RequestBody represents a low-level OpenAPI 3+ RequestBody object. // RequestBody represents a low-level OpenAPI 3+ RequestBody object.
// - https://spec.openapis.org/oas/v3.1.0#request-body-object // - https://spec.openapis.org/oas/v3.1.0#request-body-object
type RequestBody struct { type RequestBody struct {
Description low.NodeReference[string] Description low.NodeReference[string]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]] Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
Required low.NodeReference[bool] Required low.NodeReference[bool]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
@@ -36,7 +38,7 @@ func (rb *RequestBody) GetExtensions() map[low.KeyReference[string]]low.ValueRef
// FindContent attempts to find content/MediaType defined using a specified name. // FindContent attempts to find content/MediaType defined using a specified name.
func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType] { func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType] {
return low.FindItemInMap[*MediaType](cType, rb.Content.Value) return low.FindItemInOrderedMap[*MediaType](cType, rb.Content.Value)
} }
// Build will extract extensions and MediaType objects from the node. // Build will extract extensions and MediaType objects from the node.
@@ -52,7 +54,7 @@ func (rb *RequestBody) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return cErr return cErr
} }
if con != nil { if con != nil {
rb.Content = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]{ rb.Content = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{
Value: con, Value: con,
KeyNode: cL, KeyNode: cL,
ValueNode: cN, ValueNode: cN,
@@ -70,8 +72,8 @@ func (rb *RequestBody) Hash() [32]byte {
if !rb.Required.IsEmpty() { if !rb.Required.IsEmpty() {
f = append(f, fmt.Sprint(rb.Required.Value)) f = append(f, fmt.Sprint(rb.Required.Value))
} }
for k := range rb.Content.Value { for pair := orderedmap.First(rb.Content.Value); pair != nil; pair = pair.Next() {
f = append(f, low.GenerateHashString(rb.Content.Value[k].Value)) f = append(f, low.GenerateHashString(pair.Value().Value))
} }
keys := make([]string, len(rb.Extensions)) keys := make([]string, len(rb.Extensions))

View File

@@ -6,12 +6,14 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Response represents a high-level OpenAPI 3+ Response object that is backed by a low-level one. // Response represents a high-level OpenAPI 3+ Response object that is backed by a low-level one.
@@ -21,10 +23,10 @@ import (
// - https://spec.openapis.org/oas/v3.1.0#response-object // - https://spec.openapis.org/oas/v3.1.0#response-object
type Response struct { type Response struct {
Description low.NodeReference[string] Description low.NodeReference[string]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] Headers low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]] Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
Links low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]] Links low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]]
*low.Reference *low.Reference
} }
@@ -40,17 +42,17 @@ func (r *Response) GetExtensions() map[low.KeyReference[string]]low.ValueReferen
// FindContent will attempt to locate a MediaType instance using the supplied key. // FindContent will attempt to locate a MediaType instance using the supplied key.
func (r *Response) FindContent(cType string) *low.ValueReference[*MediaType] { func (r *Response) FindContent(cType string) *low.ValueReference[*MediaType] {
return low.FindItemInMap[*MediaType](cType, r.Content.Value) return low.FindItemInOrderedMap[*MediaType](cType, r.Content.Value)
} }
// FindHeader will attempt to locate a Header instance using the supplied key. // FindHeader will attempt to locate a Header instance using the supplied key.
func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] { func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
return low.FindItemInMap[*Header](hType, r.Headers.Value) return low.FindItemInOrderedMap[*Header](hType, r.Headers.Value)
} }
// FindLink will attempt to locate a Link instance using the supplied key. // FindLink will attempt to locate a Link instance using the supplied key.
func (r *Response) FindLink(hType string) *low.ValueReference[*Link] { func (r *Response) FindLink(hType string) *low.ValueReference[*Link] {
return low.FindItemInMap[*Link](hType, r.Links.Value) return low.FindItemInOrderedMap[*Link](hType, r.Links.Value)
} }
// Build will extract headers, extensions, content and links from node. // Build will extract headers, extensions, content and links from node.
@@ -66,7 +68,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return err return err
} }
if headers != nil { if headers != nil {
r.Headers = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]{ r.Headers = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]{
Value: headers, Value: headers,
KeyNode: lN, KeyNode: lN,
ValueNode: kN, ValueNode: kN,
@@ -78,7 +80,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return cErr return cErr
} }
if con != nil { if con != nil {
r.Content = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]{ r.Content = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]{
Value: con, Value: con,
KeyNode: clN, KeyNode: clN,
ValueNode: cN, ValueNode: cN,
@@ -91,7 +93,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return lErr return lErr
} }
if links != nil { if links != nil {
r.Links = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]]{ r.Links = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]]{
Value: links, Value: links,
KeyNode: linkLabel, KeyNode: linkLabel,
ValueNode: linkValue, ValueNode: linkValue,
@@ -106,26 +108,26 @@ func (r *Response) Hash() [32]byte {
if r.Description.Value != "" { if r.Description.Value != "" {
f = append(f, r.Description.Value) f = append(f, r.Description.Value)
} }
keys := make([]string, len(r.Headers.Value)) keys := make([]string, orderedmap.Len(r.Headers.Value))
z := 0 z := 0
for k := range r.Headers.Value { for pair := orderedmap.First(r.Headers.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(r.Headers.Value[k].Value)) keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value))
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)
f = append(f, keys...) f = append(f, keys...)
keys = make([]string, len(r.Content.Value)) keys = make([]string, orderedmap.Len(r.Content.Value))
z = 0 z = 0
for k := range r.Content.Value { for pair := orderedmap.First(r.Content.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(r.Content.Value[k].Value)) keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value))
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)
f = append(f, keys...) f = append(f, keys...)
keys = make([]string, len(r.Links.Value)) keys = make([]string, orderedmap.Len(r.Links.Value))
z = 0 z = 0
for k := range r.Links.Value { for pair := orderedmap.First(r.Links.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(r.Links.Value[k].Value)) keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value))
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -6,12 +6,14 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Responses represents a low-level OpenAPI 3+ Responses object. // Responses represents a low-level OpenAPI 3+ Responses object.
@@ -33,7 +35,7 @@ import (
// the duplication. Perhaps in the future we could use generics here, but for now to keep things // the duplication. Perhaps in the future we could use generics here, but for now to keep things
// simple, they are broken out into individual versions. // simple, they are broken out into individual versions.
type Responses struct { type Responses struct {
Codes map[low.KeyReference[string]]low.ValueReference[*Response] Codes orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]
Default low.NodeReference[*Response] Default low.NodeReference[*Response]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
@@ -75,12 +77,12 @@ func (r *Responses) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
} }
func (r *Responses) getDefault() *low.NodeReference[*Response] { func (r *Responses) getDefault() *low.NodeReference[*Response] {
for n, o := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
if strings.ToLower(n.Value) == DefaultLabel { if strings.ToLower(pair.Key().Value) == DefaultLabel {
return &low.NodeReference[*Response]{ return &low.NodeReference[*Response]{
ValueNode: o.ValueNode, ValueNode: pair.Value().ValueNode,
KeyNode: n.KeyNode, KeyNode: pair.Key().KeyNode,
Value: o.Value, Value: pair.Value().Value,
} }
} }
} }
@@ -90,35 +92,33 @@ func (r *Responses) getDefault() *low.NodeReference[*Response] {
// used to remove default from codes extracted by Build() // used to remove default from codes extracted by Build()
func (r *Responses) deleteCode(code string) { func (r *Responses) deleteCode(code string) {
var key *low.KeyReference[string] var key *low.KeyReference[string]
if r.Codes != nil { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
for k := range r.Codes { if pair.Key().Value == code {
if k.Value == code { key = pair.KeyPtr()
key = &k break
break
}
} }
} }
// should never be nil, but, you never know... science and all that! // should never be nil, but, you never know... science and all that!
if key != nil { if key != nil {
delete(r.Codes, *key) r.Codes.Delete(*key)
} }
} }
// FindResponseByCode will attempt to locate a Response using an HTTP response code. // FindResponseByCode will attempt to locate a Response using an HTTP response code.
func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] { func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] {
return low.FindItemInMap[*Response](code, r.Codes) return low.FindItemInOrderedMap[*Response](code, r.Codes)
} }
// Hash will return a consistent SHA256 Hash of the Examples object // Hash will return a consistent SHA256 Hash of the Examples object
func (r *Responses) Hash() [32]byte { func (r *Responses) Hash() [32]byte {
var f []string var f []string
var keys []string var keys []string
keys = make([]string, len(r.Codes)) keys = make([]string, orderedmap.Len(r.Codes))
cMap := make(map[string]*Response, len(keys)) cMap := make(map[string]*Response, len(keys))
z := 0 z := 0
for k := range r.Codes { for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
keys[z] = k.Value keys[z] = pair.Key().Value
cMap[k.Value] = r.Codes[k].Value cMap[pair.Key().Value] = pair.Value().Value
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -5,12 +5,14 @@ package v3
import ( import (
"crypto/sha256" "crypto/sha256"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort" "sort"
"strings" "strings"
"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"
) )
// Server represents a low-level OpenAPI 3+ Server object. // Server represents a low-level OpenAPI 3+ Server object.
@@ -18,7 +20,7 @@ import (
type Server struct { type Server struct {
URL low.NodeReference[string] URL low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
Variables low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*ServerVariable]] Variables low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*ServerVariable]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference *low.Reference
} }
@@ -30,7 +32,7 @@ func (s *Server) GetExtensions() map[low.KeyReference[string]]low.ValueReference
// FindVariable attempts to locate a ServerVariable instance using the supplied key. // FindVariable attempts to locate a ServerVariable instance using the supplied key.
func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVariable] { func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVariable] {
return low.FindItemInMap[*ServerVariable](serverVar, s.Variables.Value) return low.FindItemInOrderedMap[*ServerVariable](serverVar, s.Variables.Value)
} }
// Build will extract server variables from the supplied node. // Build will extract server variables from the supplied node.
@@ -43,7 +45,7 @@ func (s *Server) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if vars == nil { if vars == nil {
return nil return nil
} }
variablesMap := make(map[low.KeyReference[string]]low.ValueReference[*ServerVariable]) variablesMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*ServerVariable]]()
if utils.IsNodeMap(vars) { if utils.IsNodeMap(vars) {
var currentNode string var currentNode string
var keyNode *yaml.Node var keyNode *yaml.Node
@@ -56,15 +58,18 @@ func (s *Server) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
variable := ServerVariable{} variable := ServerVariable{}
variable.Reference = new(low.Reference) variable.Reference = new(low.Reference)
_ = low.BuildModel(varNode, &variable) _ = low.BuildModel(varNode, &variable)
variablesMap[low.KeyReference[string]{ variablesMap.Set(
Value: currentNode, low.KeyReference[string]{
KeyNode: keyNode, Value: currentNode,
}] = low.ValueReference[*ServerVariable]{ KeyNode: keyNode,
ValueNode: varNode, },
Value: &variable, low.ValueReference[*ServerVariable]{
} ValueNode: varNode,
Value: &variable,
},
)
} }
s.Variables = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*ServerVariable]]{ s.Variables = low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*ServerVariable]]]{
KeyNode: kn, KeyNode: kn,
ValueNode: vars, ValueNode: vars,
Value: variablesMap, Value: variablesMap,
@@ -76,10 +81,10 @@ func (s *Server) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
// Hash will return a consistent SHA256 Hash of the Server object // Hash will return a consistent SHA256 Hash of the Server object
func (s *Server) Hash() [32]byte { func (s *Server) Hash() [32]byte {
var f []string var f []string
keys := make([]string, len(s.Variables.Value)) keys := make([]string, orderedmap.Len(s.Variables.Value))
z := 0 z := 0
for k := range s.Variables.Value { for pair := orderedmap.First(s.Variables.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(s.Variables.Value[k].Value) keys[z] = low.GenerateHashString(pair.Value().Value)
z++ z++
} }
sort.Strings(keys) sort.Strings(keys)

View File

@@ -50,7 +50,7 @@ func ExampleNewDocument_fromOpenAPI3Document() {
// get a count of the number of paths and schemas. // get a count of the number of paths and schemas.
paths := orderedmap.Len(v3Model.Model.Paths.PathItems) paths := orderedmap.Len(v3Model.Model.Paths.PathItems)
schemas := len(v3Model.Model.Components.Schemas) schemas := orderedmap.Len(v3Model.Model.Components.Schemas)
// print the number of paths and schemas in the document // print the number of paths and schemas in the document
fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas)
@@ -156,7 +156,7 @@ func ExampleNewDocument_fromSwaggerDocument() {
// get a count of the number of paths and schemas. // get a count of the number of paths and schemas.
paths := orderedmap.Len(v2Model.Model.Paths.PathItems) paths := orderedmap.Len(v2Model.Model.Paths.PathItems)
schemas := len(v2Model.Model.Definitions.Definitions) schemas := orderedmap.Len(v2Model.Model.Definitions.Definitions)
// print the number of paths and schemas in the document // print the number of paths and schemas in the document
fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas) fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas)
@@ -187,7 +187,7 @@ func ExampleNewDocument_fromUnknownVersion() {
} }
if len(errors) <= 0 { if len(errors) <= 0 {
paths = orderedmap.Len(v3Model.Model.Paths.PathItems) paths = orderedmap.Len(v3Model.Model.Paths.PathItems)
schemas = len(v3Model.Model.Components.Schemas) schemas = orderedmap.Len(v3Model.Model.Components.Schemas)
} }
} }
if document.GetSpecInfo().SpecType == utils.OpenApi2 { if document.GetSpecInfo().SpecType == utils.OpenApi2 {
@@ -197,7 +197,7 @@ func ExampleNewDocument_fromUnknownVersion() {
} }
if len(errors) <= 0 { if len(errors) <= 0 {
paths = orderedmap.Len(v2Model.Model.Paths.PathItems) paths = orderedmap.Len(v2Model.Model.Paths.PathItems)
schemas = len(v2Model.Model.Definitions.Definitions) schemas = orderedmap.Len(v2Model.Model.Definitions.Definitions)
} }
} }
@@ -562,8 +562,8 @@ components:
} }
// get a reference to SchemaOne and ParameterOne // get a reference to SchemaOne and ParameterOne
schemaOne := docModel.Model.Components.Schemas["SchemaOne"].Schema() schemaOne := docModel.Model.Components.Schemas.GetOrZero("SchemaOne").Schema()
parameterOne := docModel.Model.Components.Parameters["ParameterOne"] parameterOne := docModel.Model.Components.Parameters.GetOrZero("ParameterOne")
// unpack schemaOne extensions into complex `cakes` type // unpack schemaOne extensions into complex `cakes` type
schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *low.Schema](schemaOne) schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *low.Schema](schemaOne)

View File

@@ -8,11 +8,6 @@ import (
"strings" "strings"
"testing" "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"
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high/base" "github.com/pb33f/libopenapi/datamodel/high/base"
v3high "github.com/pb33f/libopenapi/datamodel/high/v3" v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
@@ -254,17 +249,18 @@ func TestDocument_RenderAndReload(t *testing.T) {
// mutate the model // mutate the model
h := m.Model h := m.Model
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId = "findACakeInABakery" 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/findByStatus").Get.Responses.Codes.GetOrZero("400").Description = "a nice bucket of mice"
h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags = h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags =
append(h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, "gurgle", "giggle") append(h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, "gurgle", "giggle")
h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security = append(h.Paths.PathItems.GetOrZero("/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{ &base.SecurityRequirement{Requirements: orderedmap.ToOrderedMap(map[string][]string{
"pizza-and-cake": {"read:abook", "write:asong"}, "pizza-and-cake": {"read:abook", "write:asong"},
}}) })},
)
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example = "I am a teapot, filled with love." h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example = "I am a teapot, filled with love."
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl = "https://pb33f.io" h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
bytes, _, newDocModel, e := doc.RenderAndReload() bytes, _, newDocModel, e := doc.RenderAndReload()
assert.Nil(t, e) assert.Nil(t, e)
@@ -273,17 +269,17 @@ func TestDocument_RenderAndReload(t *testing.T) {
h = newDocModel.Model h = newDocModel.Model
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId) assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId)
assert.Equal(t, "a nice bucket of mice", assert.Equal(t, "a nice bucket of mice",
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes["400"].Description) h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes.GetOrZero("400").Description)
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3) assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3) assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
yu := h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security 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, "read:abook", yu[len(yu)-1].Requirements.GetOrZero("pizza-and-cake")[0])
assert.Equal(t, "I am a teapot, filled with love.", assert.Equal(t, "I am a teapot, filled with love.",
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example) h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example)
assert.Equal(t, "https://pb33f.io", assert.Equal(t, "https://pb33f.io",
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl) h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl)
} }
func TestDocument_Render(t *testing.T) { func TestDocument_Render(t *testing.T) {
@@ -304,18 +300,20 @@ func TestDocument_Render(t *testing.T) {
// mutate the model // mutate the model
h := m.Model h := m.Model
h.Paths.PathItems["/pet/findByStatus"].Get.OperationId = "findACakeInABakery" h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId = "findACakeInABakery"
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description = "a nice bucket of mice" h.Paths.PathItems.GetOrZero("/pet/findByStatus").
h.Paths.PathItems["/pet/findByTags"].Get.Tags = Get.Responses.Codes.GetOrZero("400").Description = "a nice bucket of mice"
append(h.Paths.PathItems["/pet/findByTags"].Get.Tags, "gurgle", "giggle") 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{ &base.SecurityRequirement{Requirements: orderedmap.ToOrderedMap(map[string][]string{
"pizza-and-cake": {"read:abook", "write:asong"}, "pizza-and-cake": {"read:abook", "write:asong"},
}}) })},
)
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example = "I am a teapot, filled with love." h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example = "I am a teapot, filled with love."
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl = "https://pb33f.io" h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
bytes, e := doc.Render() bytes, e := doc.Render()
assert.NoError(t, e) assert.NoError(t, e)
@@ -329,19 +327,19 @@ func TestDocument_Render(t *testing.T) {
assert.Len(t, docErrs, 0) assert.Len(t, docErrs, 0)
h = newDocModel.Model 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", assert.Equal(t, "a nice bucket of mice",
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description) h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes.GetOrZero("400").Description)
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3) assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3) assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
yu := h.Paths.PathItems["/pet/{petId}"].Delete.Security 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, "read:abook", yu[len(yu)-1].Requirements.GetOrZero("pizza-and-cake")[0])
assert.Equal(t, "I am a teapot, filled with love.", assert.Equal(t, "I am a teapot, filled with love.",
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example) h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example)
assert.Equal(t, "https://pb33f.io", assert.Equal(t, "https://pb33f.io",
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl) h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl)
} }
func TestDocument_RenderWithLargeIndention(t *testing.T) { func TestDocument_RenderWithLargeIndention(t *testing.T) {
@@ -354,7 +352,6 @@ func TestDocument_RenderWithLargeIndention(t *testing.T) {
doc.BuildV3Model() doc.BuildV3Model()
bytes, _ := doc.Render() bytes, _ := doc.Render()
assert.Equal(t, json, string(bytes)) assert.Equal(t, json, string(bytes))
} }
func TestDocument_Render_ChangeCheck_Burgershop(t *testing.T) { func TestDocument_Render_ChangeCheck_Burgershop(t *testing.T) {
@@ -566,16 +563,16 @@ func TestSchemaRefIsFollowed(t *testing.T) {
// get a count of the number of paths and schemas. // get a count of the number of paths and schemas.
schemas := v3Model.Model.Components.Schemas schemas := v3Model.Model.Components.Schemas
assert.Equal(t, 4, len(schemas)) assert.Equal(t, 4, orderedmap.Len(schemas))
fp := schemas["FP"] fp := schemas.GetOrZero("FP")
fbsref := schemas["FBSRef"] fbsref := schemas.GetOrZero("FBSRef")
assert.Equal(t, fp.Schema().Pattern, fbsref.Schema().Pattern) assert.Equal(t, fp.Schema().Pattern, fbsref.Schema().Pattern)
assert.Equal(t, fp.Schema().Example, fbsref.Schema().Example) assert.Equal(t, fp.Schema().Example, fbsref.Schema().Example)
byte := schemas["Byte"] byte := schemas.GetOrZero("Byte")
uint64 := schemas["UInt64"] uint64 := schemas.GetOrZero("UInt64")
assert.Equal(t, uint64.Schema().Format, byte.Schema().Format) assert.Equal(t, uint64.Schema().Format, byte.Schema().Format)
assert.Equal(t, uint64.Schema().Type, byte.Schema().Type) assert.Equal(t, uint64.Schema().Type, byte.Schema().Type)
@@ -674,7 +671,8 @@ components:
example: example:
"4578152156": Not Started "4578152156": Not Started
"5678904321": On Hold "5678904321": On Hold
type: object` type: object
`
doc, err := NewDocument([]byte(d)) doc, err := NewDocument([]byte(d))
if err != nil { if err != nil {
@@ -689,7 +687,7 @@ components:
// render the document. // render the document.
rend, _ := result.Model.Render() rend, _ := result.Model.Render()
assert.Len(t, rend, 644) assert.Equal(t, d, string(rend))
} }
func TestDocument_OperationsAsRefs(t *testing.T) { func TestDocument_OperationsAsRefs(t *testing.T) {
@@ -897,12 +895,11 @@ func TestDocument_Render_PreserveOrder(t *testing.T) {
require.Equal(t, pathCount, orderedmap.Len(pathItems)) require.Equal(t, pathCount, orderedmap.Len(pathItems))
var i int var i int
_ = orderedmap.For(model.Model.Paths.PathItems, func(pair orderedmap.Pair[string, *v3high.PathItem]) error { for pair := orderedmap.First(model.Model.Paths.PathItems); pair != nil; pair = pair.Next() {
pathName := fmt.Sprintf("/foobar/%d", i) pathName := fmt.Sprintf("/foobar/%d", i)
assert.Equal(t, pathName, pair.Key()) assert.Equal(t, pathName, pair.Key())
i++ i++
return nil }
})
assert.Equal(t, pathCount, i) assert.Equal(t, pathCount, i)
} }

View File

@@ -72,6 +72,11 @@ func (o *wrapOrderedMap[K, V]) First() Pair[K, V] {
} }
} }
// IsZero is required to support `omitempty` tag for YAML/JSON marshaling.
func (o *wrapOrderedMap[K, V]) IsZero() bool {
return o.Len() == 0
}
func (p *wrapPair[K, V]) Next() Pair[K, V] { func (p *wrapPair[K, V]) Next() Pair[K, V] {
next := p.Pair.Next() next := p.Pair.Next()
if next == nil { if next == nil {
@@ -140,23 +145,13 @@ func ToOrderedMap[K comparable, V any](m map[K]V) Map[K, V] {
return om return om
} }
// For iterates a `Map` and calls action() on each map pair. // First returns map's first pair for iteration.
// action() may return `io.EOF` to break iteration.
// Safely handles nil pointer. // Safely handles nil pointer.
func For[K comparable, V any](m Map[K, V], action ActionFunc[K, V]) error { func First[K comparable, V any](m Map[K, V]) Pair[K, V] {
ctx, cancel := context.WithCancel(context.Background()) if m == nil {
defer cancel() return nil
c := Iterate(ctx, m)
for pair := range c {
err := action(pair)
if err == io.EOF {
return nil
}
if err != nil {
return err
}
} }
return nil return m.First()
} }
// TranslateMapParallel iterates a `Map` in parallel and calls translate() // TranslateMapParallel iterates a `Map` in parallel and calls translate()

View File

@@ -159,68 +159,6 @@ func TestMap(t *testing.T) {
}) })
}) })
t.Run("For()", func(t *testing.T) {
const mapSize = 10
t.Run("Nil pointer", func(t *testing.T) {
var m orderedmap.Map[string, int]
err := orderedmap.For(m, func(_ orderedmap.Pair[string, int]) error {
return errors.New("Expected no data")
})
require.NoError(t, err)
})
t.Run("Empty", func(t *testing.T) {
m := orderedmap.New[string, int]()
err := orderedmap.For(m, func(_ orderedmap.Pair[string, int]) error {
return errors.New("Expected no data")
})
require.NoError(t, err)
})
t.Run("Full iteration", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
var i int
err := orderedmap.For(m, func(pair orderedmap.Pair[string, int]) error {
assert.Equal(t, fmt.Sprintf("key%d", i), pair.Key())
assert.Equal(t, fmt.Sprintf("key%d", i), *pair.KeyPtr())
assert.Equal(t, i+1000, pair.Value())
assert.Equal(t, i+1000, *pair.ValuePtr())
i++
require.LessOrEqual(t, i, mapSize)
return nil
})
require.NoError(t, err)
assert.Equal(t, mapSize, i)
})
t.Run("Partial iteration", func(t *testing.T) {
m := orderedmap.New[string, int]()
for i := 0; i < mapSize; i++ {
m.Set(fmt.Sprintf("key%d", i), i+1000)
}
var i int
err := orderedmap.For(m, func(pair orderedmap.Pair[string, int]) error {
assert.Equal(t, fmt.Sprintf("key%d", i), pair.Key())
assert.Equal(t, fmt.Sprintf("key%d", i), *pair.KeyPtr())
assert.Equal(t, i+1000, pair.Value())
assert.Equal(t, i+1000, *pair.ValuePtr())
i++
if i >= mapSize/2 {
return io.EOF
}
return nil
})
require.NoError(t, err)
assert.Equal(t, mapSize/2, i)
})
})
t.Run("TranslateMapParallel()", func(t *testing.T) { t.Run("TranslateMapParallel()", func(t *testing.T) {
const mapSize = 1000 const mapSize = 1000

View File

@@ -5,8 +5,9 @@ package renderer
import ( import (
"fmt" "fmt"
"github.com/pb33f/libopenapi"
"os" "os"
"github.com/pb33f/libopenapi"
) )
func ExampleMockGenerator_generateBurgerMock_yaml() { func ExampleMockGenerator_generateBurgerMock_yaml() {
@@ -21,7 +22,7 @@ func ExampleMockGenerator_generateBurgerMock_yaml() {
v3Model, _ := document.BuildV3Model() v3Model, _ := document.BuildV3Model()
// create a mock of the Burger model // create a mock of the Burger model
burgerModel := v3Model.Model.Components.Schemas["Burger"] burgerModel := v3Model.Model.Components.Schemas.GetOrZero("Burger")
burger := burgerModel.Schema() burger := burgerModel.Schema()
mock, err := mg.GenerateMock(burger, "") mock, err := mg.GenerateMock(burger, "")
@@ -45,7 +46,7 @@ func ExampleMockGenerator_generateFriesMock_json() {
v3Model, _ := document.BuildV3Model() v3Model, _ := document.BuildV3Model()
// create a mock of the Fries model // create a mock of the Fries model
friesModel := v3Model.Model.Components.Schemas["Fries"] friesModel := v3Model.Model.Components.Schemas.GetOrZero("Fries")
fries := friesModel.Schema() fries := friesModel.Schema()
mock, err := mg.GenerateMock(fries, "") mock, err := mg.GenerateMock(fries, "")
@@ -68,7 +69,8 @@ func ExampleMockGenerator_generateRequestMock_json() {
v3Model, _ := document.BuildV3Model() v3Model, _ := document.BuildV3Model()
// create a mock of the burger request model, extracted from the operation directly. // create a mock of the burger request model, extracted from the operation directly.
burgerRequestModel := v3Model.Model.Paths.PathItems["/burgers"].Post.RequestBody.Content["application/json"] burgerRequestModel := v3Model.Model.Paths.PathItems.GetOrZero("/burgers").
Post.RequestBody.Content.GetOrZero("application/json")
// use the 'cakeBurger' example to generate a mock // use the 'cakeBurger' example to generate a mock
mock, err := mg.GenerateMock(burgerRequestModel, "cakeBurger") mock, err := mg.GenerateMock(burgerRequestModel, "cakeBurger")
@@ -92,7 +94,8 @@ func ExampleMockGenerator_generateResponseMock_json() {
v3Model, _ := document.BuildV3Model() v3Model, _ := document.BuildV3Model()
// create a mock of the burger response model, extracted from the operation directly. // create a mock of the burger response model, extracted from the operation directly.
burgerResponseModel := v3Model.Model.Paths.PathItems["/burgers"].Post.Responses.Codes["200"].Content["application/json"] burgerResponseModel := v3Model.Model.Paths.PathItems.GetOrZero("/burgers").
Post.Responses.Codes.GetOrZero("200").Content.GetOrZero("application/json")
// use the 'filetOFish' example to generate a mock // use the 'filetOFish' example to generate a mock
mock, err := mg.GenerateMock(burgerResponseModel, "filetOFish") mock, err := mg.GenerateMock(burgerResponseModel, "filetOFish")
@@ -116,7 +119,7 @@ func ExampleMockGenerator_generatePolymorphicMock_json() {
v3Model, _ := document.BuildV3Model() v3Model, _ := document.BuildV3Model()
// create a mock of the SomePayload component, which uses polymorphism (incorrectly) // create a mock of the SomePayload component, which uses polymorphism (incorrectly)
payloadModel := v3Model.Model.Components.Schemas["SomePayload"] payloadModel := v3Model.Model.Components.Schemas.GetOrZero("SomePayload")
payload := payloadModel.Schema() payload := payloadModel.Schema()
mock, err := mg.GenerateMock(payload, "") mock, err := mg.GenerateMock(payload, "")

View File

@@ -7,14 +7,16 @@ import (
cryptoRand "crypto/rand" cryptoRand "crypto/rand"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"github.com/lucasjones/reggen"
"github.com/pb33f/libopenapi/datamodel/high/base"
"golang.org/x/exp/slices"
"io" "io"
"math/rand" "math/rand"
"os" "os"
"strings" "strings"
"time" "time"
"github.com/lucasjones/reggen"
"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/orderedmap"
"golang.org/x/exp/slices"
) )
const rootType = "rootType" const rootType = "rootType"
@@ -217,16 +219,17 @@ func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, struct
if properties != nil { if properties != nil {
// check if this schema has required properties, if so, then only render required props, if not // check if this schema has required properties, if so, then only render required props, if not
// render everything in the schema. // render everything in the schema.
checkProps := make(map[string]*base.SchemaProxy) checkProps := orderedmap.New[string, *base.SchemaProxy]()
if len(schema.Required) > 0 { if len(schema.Required) > 0 {
for _, requiredProp := range schema.Required { for _, requiredProp := range schema.Required {
checkProps[requiredProp] = properties[requiredProp] checkProps.Set(requiredProp, properties.GetOrZero(requiredProp))
} }
} else { } else {
checkProps = properties checkProps = properties
} }
for propName, propValue := range checkProps { for pair := orderedmap.First(checkProps); pair != nil; pair = pair.Next() {
// render property // render property
propName, propValue := pair.Key(), pair.Value()
propertySchema := propValue.Schema() propertySchema := propValue.Schema()
wr.DiveIntoSchema(propertySchema, propName, propertyMap, depth+1) wr.DiveIntoSchema(propertySchema, propName, propertyMap, depth+1)
} }
@@ -249,8 +252,9 @@ func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, struct
dependentSchemas := schema.DependentSchemas dependentSchemas := schema.DependentSchemas
if dependentSchemas != nil { if dependentSchemas != nil {
dependentSchemasMap := make(map[string]any) dependentSchemasMap := make(map[string]any)
for k, dependentSchema := range dependentSchemas { for pair := orderedmap.First(dependentSchemas); pair != nil; pair = pair.Next() {
// only map if the property exists // only map if the property exists
k, dependentSchema := pair.Key(), pair.Value()
if propertyMap[k] != nil { if propertyMap[k] != nil {
dependentSchemaCompiled := dependentSchema.Schema() dependentSchemaCompiled := dependentSchema.Schema()
wr.DiveIntoSchema(dependentSchemaCompiled, k, dependentSchemasMap, depth+1) wr.DiveIntoSchema(dependentSchemaCompiled, k, dependentSchemasMap, depth+1)

View File

@@ -8,17 +8,18 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"net/url" "net/url"
"os" "os"
"regexp" "regexp"
"strings" "strings"
"testing" "testing"
"time" "time"
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
) )
func TestRenderSchema(t *testing.T) { func TestRenderSchema(t *testing.T) {
@@ -1144,7 +1145,7 @@ properties:
loopMe = func(parent *highbase.SchemaProxy, level int) { loopMe = func(parent *highbase.SchemaProxy, level int) {
schemaProxy := buildSchema() schemaProxy := buildSchema()
if parent != nil { if parent != nil {
parent.Schema().Properties["child"] = schemaProxy parent.Schema().Properties.Set("child", schemaProxy)
} }
if level < 110 { if level < 110 {
loopMe(schemaProxy, level+1) loopMe(schemaProxy, level+1)

View File

@@ -5,7 +5,8 @@ package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
) )
// CallbackChanges represents all changes made between two Callback OpenAPI objects. // CallbackChanges represents all changes made between two Callback OpenAPI objects.
@@ -65,14 +66,14 @@ func CompareCallback(l, r *v3.Callback) *CallbackChanges {
lValues := make(map[string]low.ValueReference[*v3.PathItem]) lValues := make(map[string]low.ValueReference[*v3.PathItem])
rValues := make(map[string]low.ValueReference[*v3.PathItem]) rValues := make(map[string]low.ValueReference[*v3.PathItem])
for k := range l.Expression.Value { for pair := orderedmap.First(l.Expression.Value); pair != nil; pair = pair.Next() {
lHashes[k.Value] = low.GenerateHashString(l.Expression.Value[k].Value) lHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
lValues[k.Value] = l.Expression.Value[k] lValues[pair.Key().Value] = pair.Value()
} }
for k := range r.Expression.Value { for pair := orderedmap.First(r.Expression.Value); pair != nil; pair = pair.Next() {
rHashes[k.Value] = low.GenerateHashString(r.Expression.Value[k].Value) rHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
rValues[k.Value] = r.Expression.Value[k] rValues[pair.Key().Value] = pair.Value()
} }
expChanges := make(map[string]*PathItemChanges) expChanges := make(map[string]*PathItemChanges)

View File

@@ -5,11 +5,13 @@ package model
import ( import (
"fmt" "fmt"
"github.com/pb33f/libopenapi/utils"
"reflect" "reflect"
"strings" "strings"
"sync" "sync"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -235,12 +237,12 @@ func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Chan
// CheckMapForChanges checks a left and right low level map for any additions, subtractions or modifications to // CheckMapForChanges checks a left and right low level map for any additions, subtractions or modifications to
// values. The compareFunc argument should reference the correct comparison function for the generic type. // values. The compareFunc argument should reference the correct comparison function for the generic type.
func CheckMapForChanges[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], func CheckMapForChanges[T any, R any](expLeft, expRight orderedmap.Map[low.KeyReference[string], low.ValueReference[T]],
changes *[]*Change, label string, compareFunc func(l, r T) R) map[string]R { changes *[]*Change, label string, compareFunc func(l, r T) R) map[string]R {
return CheckMapForChangesWithComp(expLeft, expRight, changes, label, compareFunc, true) return CheckMapForChangesWithComp(expLeft, expRight, changes, label, compareFunc, true)
} }
func CheckMapForAdditionRemoval[T any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], func CheckMapForAdditionRemoval[T any](expLeft, expRight orderedmap.Map[low.KeyReference[string], low.ValueReference[T]],
changes *[]*Change, label string) any { changes *[]*Change, label string) any {
// do nothing // do nothing
doNothing := func(l, r T) any { doNothing := func(l, r T) any {
@@ -263,7 +265,7 @@ func CheckMapForAdditionRemoval[T any](expLeft, expRight map[low.KeyReference[st
// CheckMapForChangesWithComp checks a left and right low level map for any additions, subtractions or modifications to // CheckMapForChangesWithComp checks a left and right low level map for any additions, subtractions or modifications to
// values. The compareFunc argument should reference the correct comparison function for the generic type. The compare // values. The compareFunc argument should reference the correct comparison function for the generic type. The compare
// bit determines if the comparison should be run or not. // bit determines if the comparison should be run or not.
func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], func CheckMapForChangesWithComp[T any, R any](expLeft, expRight orderedmap.Map[low.KeyReference[string], low.ValueReference[T]],
changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool) map[string]R { changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool) map[string]R {
// stop concurrent threads screwing up changes. // stop concurrent threads screwing up changes.
@@ -274,14 +276,16 @@ func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyRefer
lValues := make(map[string]low.ValueReference[T]) lValues := make(map[string]low.ValueReference[T])
rValues := make(map[string]low.ValueReference[T]) rValues := make(map[string]low.ValueReference[T])
for k := range expLeft { for pair := orderedmap.First(expLeft); pair != nil; pair = pair.Next() {
lHashes[k.Value] = low.GenerateHashString(expLeft[k].Value) k := pair.Key()
lValues[k.Value] = expLeft[k] lHashes[k.Value] = low.GenerateHashString(pair.Value().Value)
lValues[k.Value] = pair.Value()
} }
for k := range expRight { for pair := orderedmap.First(expRight); pair != nil; pair = pair.Next() {
rHashes[k.Value] = low.GenerateHashString(expRight[k].Value) k := pair.Key()
rValues[k.Value] = expRight[k] rHashes[k.Value] = low.GenerateHashString(pair.Value().Value)
rValues[k.Value] = pair.Value()
} }
expChanges := make(map[string]R) expChanges := make(map[string]R)

View File

@@ -10,6 +10,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
) )
// ComponentsChanges represents changes made to both OpenAPI and Swagger documents. This model is based on OpenAPI 3 // ComponentsChanges represents changes made to both OpenAPI and Swagger documents. This model is based on OpenAPI 3
@@ -55,7 +56,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.ParameterDefinitions) lDef := l.(*v2.ParameterDefinitions)
rDef := r.(*v2.ParameterDefinitions) rDef := r.(*v2.ParameterDefinitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Parameter] var a, b orderedmap.Map[low.KeyReference[string], low.ValueReference[*v2.Parameter]]
if lDef != nil { if lDef != nil {
a = lDef.Definitions a = lDef.Definitions
} }
@@ -70,7 +71,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.ResponsesDefinitions) lDef := l.(*v2.ResponsesDefinitions)
rDef := r.(*v2.ResponsesDefinitions) rDef := r.(*v2.ResponsesDefinitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*v2.Response] var a, b orderedmap.Map[low.KeyReference[string], low.ValueReference[*v2.Response]]
if lDef != nil { if lDef != nil {
a = lDef.Definitions a = lDef.Definitions
} }
@@ -85,7 +86,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.Definitions) lDef := l.(*v2.Definitions)
rDef := r.(*v2.Definitions) rDef := r.(*v2.Definitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] var a, b orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]
if lDef != nil { if lDef != nil {
a = lDef.Schemas a = lDef.Schemas
} }
@@ -100,7 +101,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) { reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*v2.SecurityDefinitions) lDef := l.(*v2.SecurityDefinitions)
rDef := r.(*v2.SecurityDefinitions) rDef := r.(*v2.SecurityDefinitions)
var a, b map[low.KeyReference[string]]low.ValueReference[*v2.SecurityScheme] var a, b orderedmap.Map[low.KeyReference[string], low.ValueReference[*v2.SecurityScheme]]
if lDef != nil { if lDef != nil {
a = lDef.Definitions a = lDef.Definitions
} }
@@ -217,7 +218,7 @@ type componentComparison struct {
} }
// run a generic comparison in a thread which in turn splits checks into further threads. // run a generic comparison in a thread which in turn splits checks into further threads.
func runComparison[T any, R any](l, r map[low.KeyReference[string]]low.ValueReference[T], func runComparison[T any, R any](l, r orderedmap.Map[low.KeyReference[string], low.ValueReference[T]],
changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison) { changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison) {
// for schemas // for schemas

View File

@@ -6,6 +6,7 @@ package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
) )
// ExamplesChanges represents changes made between Swagger Examples objects (Not OpenAPI 3). // ExamplesChanges represents changes made between Swagger Examples objects (Not OpenAPI 3).
@@ -37,14 +38,14 @@ func CompareExamplesV2(l, r *v2.Examples) *ExamplesChanges {
lValues := make(map[string]low.ValueReference[any]) lValues := make(map[string]low.ValueReference[any])
rValues := make(map[string]low.ValueReference[any]) rValues := make(map[string]low.ValueReference[any])
for k := range l.Values { for pair := orderedmap.First(l.Values); pair != nil; pair = pair.Next() {
lHashes[k.Value] = low.GenerateHashString(l.Values[k].Value) lHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
lValues[k.Value] = l.Values[k] lValues[pair.Key().Value] = pair.Value()
} }
for k := range r.Values { for pair := orderedmap.First(r.Values); pair != nil; pair = pair.Next() {
rHashes[k.Value] = low.GenerateHashString(r.Values[k].Value) rHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
rValues[k.Value] = r.Values[k] rValues[pair.Key().Value] = pair.Value()
} }
var changes []*Change var changes []*Change

View File

@@ -6,6 +6,7 @@ package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
) )
// LinkChanges represent changes made between two OpenAPI Link Objects. // LinkChanges represent changes made between two OpenAPI Link Objects.
@@ -127,11 +128,11 @@ func CompareLinks(l, r *v3.Link) *LinkChanges {
// parameters // parameters
lValues := make(map[string]low.ValueReference[string]) lValues := make(map[string]low.ValueReference[string])
rValues := make(map[string]low.ValueReference[string]) rValues := make(map[string]low.ValueReference[string])
for i := range l.Parameters.Value { for pair := orderedmap.First(l.Parameters.Value); pair != nil; pair = pair.Next() {
lValues[i.Value] = l.Parameters.Value[i] lValues[pair.Key().Value] = pair.Value()
} }
for i := range r.Parameters.Value { for pair := orderedmap.First(r.Parameters.Value); pair != nil; pair = pair.Next() {
rValues[i.Value] = r.Parameters.Value[i] rValues[pair.Key().Value] = pair.Value()
} }
for k := range lValues { for k := range lValues {
if _, ok := rValues[k]; !ok { if _, ok := rValues[k]; !ok {

View File

@@ -5,7 +5,8 @@ package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
) )
// OAuthFlowsChanges represents changes found between two OpenAPI OAuthFlows objects. // OAuthFlowsChanges represents changes found between two OpenAPI OAuthFlows objects.
@@ -228,26 +229,26 @@ func CompareOAuthFlow(l, r *v3.OAuthFlow) *OAuthFlowChanges {
CheckProperties(props) CheckProperties(props)
for v := range l.Scopes.Value { for pair := orderedmap.First(l.Scopes.Value); pair != nil; pair = pair.Next() {
if r != nil && r.FindScope(v.Value) == nil { if r != nil && r.FindScope(pair.Key().Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes, CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Scopes.Value[v].ValueNode, nil, true, pair.Value().ValueNode, nil, true,
v.Value, nil) pair.Key().Value, nil)
continue continue
} }
if r != nil && r.FindScope(v.Value) != nil { if r != nil && r.FindScope(pair.Key().Value) != nil {
if l.Scopes.Value[v].Value != r.FindScope(v.Value).Value { if pair.Value().Value != r.FindScope(pair.Key().Value).Value {
CreateChange(&changes, Modified, v3.Scopes, CreateChange(&changes, Modified, v3.Scopes,
l.Scopes.Value[v].ValueNode, r.FindScope(v.Value).ValueNode, true, pair.Value().ValueNode, r.FindScope(pair.Key().Value).ValueNode, true,
l.Scopes.Value[v].Value, r.FindScope(v.Value).Value) pair.Value().Value, r.FindScope(pair.Key().Value).Value)
} }
} }
} }
for v := range r.Scopes.Value { for pair := orderedmap.First(r.Scopes.Value); pair != nil; pair = pair.Next() {
if l != nil && l.FindScope(v.Value) == nil { if l != nil && l.FindScope(pair.Key().Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes, CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Scopes.Value[v].ValueNode, false, nil, pair.Value().ValueNode, false,
nil, v.Value) nil, pair.Key().Value)
} }
} }
oa := new(OAuthFlowChanges) oa := new(OAuthFlowChanges)

View File

@@ -77,16 +77,12 @@ func ComparePaths(l, r any) *PathsChanges {
lKeys := make(map[string]low.ValueReference[*v2.PathItem]) lKeys := make(map[string]low.ValueReference[*v2.PathItem])
rKeys := make(map[string]low.ValueReference[*v2.PathItem]) rKeys := make(map[string]low.ValueReference[*v2.PathItem])
laction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v2.PathItem]]) error { for pair := orderedmap.First(lPath.PathItems); pair != nil; pair = pair.Next() {
lKeys[pair.Key().Value] = pair.Value() lKeys[pair.Key().Value] = pair.Value()
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*v2.PathItem]](lPath.PathItems, laction) for pair := orderedmap.First(rPath.PathItems); pair != nil; pair = pair.Next() {
raction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v2.PathItem]]) error {
rKeys[pair.Key().Value] = pair.Value() 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. // run every comparison in a thread.
var mLock sync.Mutex var mLock sync.Mutex
@@ -152,16 +148,12 @@ func ComparePaths(l, r any) *PathsChanges {
lKeys := make(map[string]low.ValueReference[*v3.PathItem]) lKeys := make(map[string]low.ValueReference[*v3.PathItem])
rKeys := make(map[string]low.ValueReference[*v3.PathItem]) rKeys := make(map[string]low.ValueReference[*v3.PathItem])
laction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v3.PathItem]]) error { for pair := orderedmap.First(lPath.PathItems); pair != nil; pair = pair.Next() {
lKeys[pair.Key().Value] = pair.Value() lKeys[pair.Key().Value] = pair.Value()
return nil
} }
_ = orderedmap.For[low.KeyReference[string], low.ValueReference[*v3.PathItem]](lPath.PathItems, laction) for pair := orderedmap.First(rPath.PathItems); pair != nil; pair = pair.Next() {
raction := func(pair orderedmap.Pair[low.KeyReference[string], low.ValueReference[*v3.PathItem]]) error {
rKeys[pair.Key().Value] = pair.Value() 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. // run every comparison in a thread.
var mLock sync.Mutex var mLock sync.Mutex

View File

@@ -5,14 +5,15 @@ package model
import ( import (
"fmt" "fmt"
"golang.org/x/exp/slices"
"sort" "sort"
"sync" "sync"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils" "github.com/pb33f/libopenapi/utils"
"golang.org/x/exp/slices"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -424,7 +425,7 @@ func checkSchemaXML(lSchema *base.Schema, rSchema *base.Schema, changes *[]*Chan
func checkMappedSchemaOfASchema( func checkMappedSchemaOfASchema(
lSchema, lSchema,
rSchema map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy], rSchema orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]],
changes *[]*Change, changes *[]*Change,
doneChan chan bool, doneChan chan bool,
) (map[string]*SchemaChanges, int) { ) (map[string]*SchemaChanges, int) {
@@ -437,15 +438,15 @@ func checkMappedSchemaOfASchema(
rEntities := make(map[string]*base.SchemaProxy) rEntities := make(map[string]*base.SchemaProxy)
rKeyNodes := make(map[string]*yaml.Node) rKeyNodes := make(map[string]*yaml.Node)
for w := range lSchema { for pair := orderedmap.First(lSchema); pair != nil; pair = pair.Next() {
lProps = append(lProps, w.Value) lProps = append(lProps, pair.Key().Value)
lEntities[w.Value] = lSchema[w].Value lEntities[pair.Key().Value] = pair.Value().Value
lKeyNodes[w.Value] = w.KeyNode lKeyNodes[pair.Key().Value] = pair.Key().KeyNode
} }
for w := range rSchema { for pair := orderedmap.First(rSchema); pair != nil; pair = pair.Next() {
rProps = append(rProps, w.Value) rProps = append(rProps, pair.Key().Value)
rEntities[w.Value] = rSchema[w].Value rEntities[pair.Key().Value] = pair.Value().Value
rKeyNodes[w.Value] = w.KeyNode rKeyNodes[pair.Key().Value] = pair.Key().KeyNode
} }
sort.Strings(lProps) sort.Strings(lProps)
sort.Strings(rProps) sort.Strings(rProps)

View File

@@ -5,8 +5,9 @@ package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/v2" v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
) )
// ScopesChanges represents changes between two Swagger Scopes Objects // ScopesChanges represents changes between two Swagger Scopes Objects
@@ -46,26 +47,26 @@ func CompareScopes(l, r *v2.Scopes) *ScopesChanges {
return nil return nil
} }
var changes []*Change var changes []*Change
for v := range l.Values { for pair := orderedmap.First(l.Values); pair != nil; pair = pair.Next() {
if r != nil && r.FindScope(v.Value) == nil { if r != nil && r.FindScope(pair.Key().Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes, CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Values[v].ValueNode, nil, true, pair.Value().ValueNode, nil, true,
v.Value, nil) pair.Key().Value, nil)
continue continue
} }
if r != nil && r.FindScope(v.Value) != nil { if r != nil && r.FindScope(pair.Key().Value) != nil {
if l.Values[v].Value != r.FindScope(v.Value).Value { if pair.Value().Value != r.FindScope(pair.Key().Value).Value {
CreateChange(&changes, Modified, v3.Scopes, CreateChange(&changes, Modified, v3.Scopes,
l.Values[v].ValueNode, r.FindScope(v.Value).ValueNode, true, pair.Value().ValueNode, r.FindScope(pair.Key().Value).ValueNode, true,
l.Values[v].Value, r.FindScope(v.Value).Value) pair.Value().Value, r.FindScope(pair.Key().Value).Value)
} }
} }
} }
for v := range r.Values { for pair := orderedmap.First(r.Values); pair != nil; pair = pair.Next() {
if l != nil && l.FindScope(v.Value) == nil { if l != nil && l.FindScope(pair.Key().Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes, CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Values[v].ValueNode, false, nil, pair.Value().ValueNode, false,
nil, v.Value) nil, pair.Key().Value)
} }
} }

View File

@@ -6,7 +6,8 @@ package model
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -56,22 +57,22 @@ func addedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) {
} }
// tricky to do this correctly, this is my solution. // tricky to do this correctly, this is my solution.
func checkSecurityRequirement(lSec, rSec map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]], func checkSecurityRequirement(lSec, rSec orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]],
changes *[]*Change) { changes *[]*Change) {
lKeys := make([]string, len(lSec)) lKeys := make([]string, orderedmap.Len(lSec))
rKeys := make([]string, len(rSec)) rKeys := make([]string, orderedmap.Len(rSec))
lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]]) rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
var n, z int var n, z int
for i := range lSec { for pair := orderedmap.First(lSec); pair != nil; pair = pair.Next() {
lKeys[n] = i.Value lKeys[n] = pair.Key().Value
lValues[i.Value] = lSec[i] lValues[pair.Key().Value] = pair.Value()
n++ n++
} }
for i := range rSec { for pair := orderedmap.First(rSec); pair != nil; pair = pair.Next() {
rKeys[z] = i.Value rKeys[z] = pair.Key().Value
rValues[i.Value] = rSec[i] rValues[pair.Key().Value] = pair.Value()
z++ z++
} }