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"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"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.
// no fidelity is lost, everything is still available via GoLow()
func ExtractExamples(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Example]) map[string]*Example {
extracted := make(map[string]*Example)
for k, v := range elements {
extracted[k.Value] = NewExample(v.Value)
func ExtractExamples(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Example]]) orderedmap.Map[string, *Example] {
extracted := orderedmap.New[string, *Example]()
for pair := orderedmap.First(elements); pair != nil; pair = pair.Next() {
extracted.Set(pair.Key().Value, NewExample(pair.Value().Value))
}
return extracted
}

View File

@@ -5,12 +5,14 @@ package base
import (
"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"
"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) {
@@ -61,14 +63,13 @@ func TestExtractExamples(t *testing.T) {
_ = lowExample.Build(nil, cNode.Content[0], nil)
examplesMap := make(map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*lowbase.Example])
examplesMap[lowmodel.KeyReference[string]{
Value: "green",
}] = lowmodel.ValueReference[*lowbase.Example]{
Value: &lowExample,
}
examplesMap := orderedmap.New[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowbase.Example]]()
examplesMap.Set(
lowmodel.KeyReference[string]{Value: "green"},
lowmodel.ValueReference[*lowbase.Example]{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"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -56,8 +57,8 @@ type Schema struct {
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
DependentSchemas orderedmap.Map[string, *SchemaProxy] `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
PatternProperties orderedmap.Map[string, *SchemaProxy] `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
@@ -73,7 +74,7 @@ type Schema struct {
// Compatible with all versions
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"`
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
@@ -356,14 +357,13 @@ func NewSchema(schema *base.Schema) *Schema {
// props async
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,
KeyNode: k.KeyNode,
ValueNode: v.ValueNode,
},
)
}))
switch sw {
case 0:
@@ -375,18 +375,18 @@ func NewSchema(schema *base.Schema) *Schema {
}
}
props := make(map[string]*SchemaProxy)
for k, v := range schema.Properties.Value {
buildProps(k, v, props, 0)
props := orderedmap.New[string, *SchemaProxy]()
for pair := orderedmap.First(schema.Properties.Value); pair != nil; pair = pair.Next() {
buildProps(pair.Key(), pair.Value(), props, 0)
}
dependents := make(map[string]*SchemaProxy)
for k, v := range schema.DependentSchemas.Value {
buildProps(k, v, dependents, 1)
dependents := orderedmap.New[string, *SchemaProxy]()
for pair := orderedmap.First(schema.DependentSchemas.Value); pair != nil; pair = pair.Next() {
buildProps(pair.Key(), pair.Value(), dependents, 1)
}
patternProps := make(map[string]*SchemaProxy)
for k, v := range schema.PatternProperties.Value {
buildProps(k, v, patternProps, 2)
patternProps := orderedmap.New[string, *SchemaProxy]()
for pair := orderedmap.First(schema.PatternProperties.Value); pair != nil; pair = pair.Next() {
buildProps(pair.Key(), pair.Value(), patternProps, 2)
}
var allOf []*SchemaProxy

View File

@@ -303,8 +303,8 @@ $anchor: anchor`
assert.Equal(t, "string", compiled.If.Schema().Type[0])
assert.Equal(t, "integer", compiled.Else.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.DependentSchemas["schemaOne"].Schema().Type[0])
assert.Equal(t, "string", compiled.PatternProperties.GetOrZero("patternOne").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, "boolean", compiled.UnevaluatedItems.Schema().Type[0])
assert.Equal(t, "integer", compiled.UnevaluatedProperties.A.Schema().Type[0])
@@ -500,9 +500,9 @@ required: [cake, fish]`
assert.Nil(t, schemaProxy.GetBuildError())
assert.True(t, compiled.ExclusiveMaximum.A)
assert.Equal(t, float64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum.B)
assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B)
assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2)
assert.Equal(t, float64(123), compiled.Properties.GetOrZero("somethingB").Schema().ExclusiveMinimum.B)
assert.Equal(t, float64(334), compiled.Properties.GetOrZero("somethingB").Schema().ExclusiveMaximum.B)
assert.Len(t, compiled.Properties.GetOrZero("somethingB").Schema().Properties.GetOrZero("somethingBProp").Schema().Type, 2)
assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description)
@@ -734,7 +734,7 @@ properties:
highSchema := NewSchema(&lowSchema)
// 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
}
@@ -765,7 +765,7 @@ properties:
})
// 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
}

View File

@@ -4,11 +4,13 @@
package base
import (
"sort"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
)
// 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
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityRequirement struct {
Requirements map[string][]string `json:"-" yaml:"-"`
Requirements orderedmap.Map[string, []string] `json:"-" yaml:"-"`
low *base.SecurityRequirement
}
@@ -27,14 +29,14 @@ type SecurityRequirement struct {
func NewSecurityRequirement(req *base.SecurityRequirement) *SecurityRequirement {
r := new(SecurityRequirement)
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.
for reqK := range req.Requirements.Value {
for pair := orderedmap.First(req.Requirements.Value); pair != nil; pair = pair.Next() {
var vals []string
for valK := range req.Requirements.Value[reqK].Value {
vals = append(vals, req.Requirements.Value[reqK].Value[valK].Value)
for valK := range pair.Value().Value {
vals = append(vals, pair.Value().Value[valK].Value)
}
values[reqK.Value] = vals
values.Set(pair.Key().Value, vals)
}
r.Requirements = values
return r
@@ -67,24 +69,23 @@ func (s *SecurityRequirement) MarshalYAML() (interface{}, error) {
}
m := utils.CreateEmptyMapNode()
keys := make([]*req, len(s.Requirements))
keys := make([]*req, orderedmap.Len(s.Requirements))
i := 0
for k := range s.Requirements {
keys[i] = &req{key: k, val: s.Requirements[k]}
for pair := orderedmap.First(s.Requirements); pair != nil; pair = pair.Next() {
keys[i] = &req{key: pair.Key(), val: pair.Value()}
i++
}
i = 0
if s.low != nil {
for o := range keys {
kv := keys[o].key
for k := range s.low.Requirements.Value {
if k.Value == kv {
gh := s.low.Requirements.Value[k]
keys[o].line = k.KeyNode.Line
keys[o].lowKey = &k
keys[o].lowVal = &gh
for pair := orderedmap.First(s.low.Requirements.Value); pair != nil; pair = pair.Next() {
if pair.Key().Value == kv {
keys[o].line = pair.Key().KeyNode.Line
keys[o].lowKey = pair.KeyPtr()
keys[o].lowVal = pair.ValuePtr()
}
i++
}

View File

@@ -4,12 +4,14 @@
package base
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"
"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) {
@@ -32,11 +34,11 @@ cake:
highExt := NewSecurityRequirement(&lowExt)
assert.Len(t, highExt.Requirements["pizza"], 2)
assert.Len(t, highExt.Requirements["cake"], 2)
assert.Len(t, highExt.Requirements.GetOrZero("pizza"), 2)
assert.Len(t, highExt.Requirements.GetOrZero("cake"), 2)
wentLow := highExt.GoLow()
assert.Len(t, wentLow.Requirements.Value, 2)
assert.Equal(t, 2, orderedmap.Len(wentLow.Requirements.Value))
assert.NotNil(t, highExt.GoLowUntyped())
// render the high-level object as YAML

View File

@@ -5,14 +5,15 @@ package high
import (
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"reflect"
"sort"
"strconv"
"strings"
"unicode"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
// 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
}
// 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 {
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 {
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"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
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.
@@ -16,7 +17,7 @@ import (
// arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject
type Definitions struct {
Definitions map[string]*highbase.SchemaProxy
Definitions orderedmap.Map[string, *highbase.SchemaProxy]
low *low.Definitions
}
@@ -24,12 +25,20 @@ type Definitions struct {
func NewDefinitions(definitions *low.Definitions) *Definitions {
rd := new(Definitions)
rd.low = definitions
defs := make(map[string]*highbase.SchemaProxy)
for k := range definitions.Schemas {
defs[k.Value] = highbase.NewSchemaProxy(&lowmodel.NodeReference[*lowbase.SchemaProxy]{
Value: definitions.Schemas[k].Value,
})
defs := orderedmap.New[string, *highbase.SchemaProxy]()
translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*lowbase.SchemaProxy]]) (asyncResult[*highbase.SchemaProxy], error) {
return asyncResult[*highbase.SchemaProxy]{
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
return rd
}

View File

@@ -3,13 +3,16 @@
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.
// Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject
type Example struct {
Values map[string]any
Values orderedmap.Map[string, any]
low *low.Examples
}
@@ -17,10 +20,10 @@ type Example struct {
func NewExample(examples *low.Examples) *Example {
e := new(Example)
e.low = examples
if len(examples.Values) > 0 {
values := make(map[string]any)
for k := range examples.Values {
values[k.Value] = examples.Values[k].Value
if orderedmap.Len(examples.Values) > 0 {
values := orderedmap.New[string, any]()
for pair := orderedmap.First(examples.Values); pair != nil; pair = pair.Next() {
values.Set(pair.Key().Value, pair.Value().Value)
}
e.Values = values
}

View File

@@ -3,7 +3,11 @@
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
// 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
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
type ParameterDefinitions struct {
Definitions map[string]*Parameter
Definitions orderedmap.Map[string, *Parameter]
low *low.ParameterDefinitions
}
@@ -21,26 +25,18 @@ type ParameterDefinitions struct {
func NewParametersDefinitions(parametersDefinitions *low.ParameterDefinitions) *ParameterDefinitions {
pd := new(ParameterDefinitions)
pd.low = parametersDefinitions
params := make(map[string]*Parameter)
var buildParam = func(name string, param *low.Parameter, rChan chan<- asyncResult[*Parameter]) {
rChan <- asyncResult[*Parameter]{
key: name,
result: NewParameter(param),
}
}
resChan := make(chan asyncResult[*Parameter])
for k := range parametersDefinitions.Definitions {
go buildParam(k.Value, parametersDefinitions.Definitions[k].Value, resChan)
}
totalParams := len(parametersDefinitions.Definitions)
completedParams := 0
for completedParams < totalParams {
select {
case r := <-resChan:
completedParams++
params[r.key] = r.result
params := orderedmap.New[string, *Parameter]()
translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Parameter]]) (asyncResult[*Parameter], error) {
return asyncResult[*Parameter]{
key: pair.Key().Value,
result: NewParameter(pair.Value().Value),
}, nil
}
resultFunc := func(value asyncResult[*Parameter]) error {
params.Set(value.key, value.result)
return nil
}
_ = orderedmap.TranslateMapParallel(parametersDefinitions.Definitions, translateFunc, resultFunc)
pd.Definitions = params
return pd
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base"
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.
@@ -15,7 +16,7 @@ import (
type Response struct {
Description string
Schema *base.SchemaProxy
Headers map[string]*Header
Headers orderedmap.Map[string, *Header]
Examples *Example
Extensions map[string]any
low *low.Response
@@ -33,9 +34,9 @@ func NewResponse(response *low.Response) *Response {
r.Schema = base.NewSchemaProxy(&response.Schema)
}
if !response.Headers.IsEmpty() {
headers := make(map[string]*Header)
for k := range response.Headers.Value {
headers[k.Value] = NewHeader(response.Headers.Value[k].Value)
headers := orderedmap.New[string, *Header]()
for pair := orderedmap.First(response.Headers.Value); pair != nil; pair = pair.Next() {
headers.Set(pair.Key().Value, NewHeader(pair.Value().Value))
}
r.Headers = headers
}

View File

@@ -5,12 +5,14 @@ package v2
import (
"github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
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.
type Responses struct {
Codes map[string]*Response
Codes orderedmap.Map[string, *Response]
Default *Response
Extensions map[string]any
low *low.Responses
@@ -22,36 +24,26 @@ func NewResponses(responses *low.Responses) *Responses {
r.low = responses
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() {
r.Default = NewResponse(responses.Default.Value)
}
// run everything async. lots of responses with lots of data are possible.
if len(responses.Codes) > 0 {
resultChan := make(chan asyncResult[*Response])
for k := range responses.Codes {
go buildPath(k.Value, responses.Codes[k].Value, resultChan)
}
resp := make(map[string]*Response)
totalResponses := len(responses.Codes)
completedResponses := 0
for completedResponses < totalResponses {
select {
case res := <-resultChan:
completedResponses++
resp[res.key] = res.result
if orderedmap.Len(responses.Codes) > 0 {
resp := 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 {
resp.Set(value.key, value.result)
return nil
}
_ = orderedmap.TranslateMapParallel(responses.Codes, translateFunc, resultFunc)
r.Codes = resp
}
return r
}

View File

@@ -3,7 +3,11 @@
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.
// 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
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
type ResponsesDefinitions struct {
Definitions map[string]*Response
Definitions orderedmap.Map[string, *Response]
low *low.ResponsesDefinitions
}
@@ -20,28 +24,19 @@ type ResponsesDefinitions struct {
func NewResponsesDefinitions(responsesDefinitions *low.ResponsesDefinitions) *ResponsesDefinitions {
rd := new(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.
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
}
}
_ = orderedmap.TranslateMapParallel(responsesDefinitions.Definitions, translateFunc, resultFunc)
rd.Definitions = responses
return rd
}

View File

@@ -5,6 +5,7 @@ package v2
import (
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.
@@ -12,7 +13,7 @@ import (
// Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject
type Scopes struct {
Values map[string]string
Values orderedmap.Map[string, string]
low *low.Scopes
}
@@ -20,9 +21,9 @@ type Scopes struct {
func NewScopes(scopes *low.Scopes) *Scopes {
s := new(Scopes)
s.low = scopes
scopeValues := make(map[string]string)
for k := range scopes.Values {
scopeValues[k.Value] = scopes.Values[k].Value
scopeValues := orderedmap.New[string, string]()
for pair := orderedmap.First(scopes.Values); pair != nil; pair = pair.Next() {
scopeValues.Set(pair.Key().Value, pair.Value().Value)
}
s.Values = scopeValues
return s

View File

@@ -3,7 +3,11 @@
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
// 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
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityDefinitions struct {
Definitions map[string]*SecurityScheme
Definitions orderedmap.Map[string, *SecurityScheme]
low *low.SecurityDefinitions
}
@@ -20,10 +24,23 @@ type SecurityDefinitions struct {
func NewSecurityDefinitions(definitions *low.SecurityDefinitions) *SecurityDefinitions {
sd := new(SecurityDefinitions)
sd.low = definitions
schemes := make(map[string]*SecurityScheme)
for k := range definitions.Definitions {
schemes[k.Value] = NewSecurityScheme(definitions.Definitions[k].Value)
schemes := orderedmap.New[string, *SecurityScheme]()
translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.SecurityScheme]]) (asyncResult[*SecurityScheme], error) {
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
return sd
}

View File

@@ -173,9 +173,3 @@ func NewSwaggerDocument(document *low.Swagger) *Swagger {
func (s *Swagger) GoLow() *low.Swagger {
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 (
"github.com/pb33f/libopenapi/datamodel"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
"io/ioutil"
@@ -80,17 +81,17 @@ func TestNewSwaggerDocument_Parameters(t *testing.T) {
initTest()
highDoc := NewSwaggerDocument(doc)
params := highDoc.Parameters
assert.Len(t, params.Definitions, 1)
assert.Equal(t, "query", params.Definitions["simpleParam"].In)
assert.Equal(t, "simple", params.Definitions["simpleParam"].Name)
assert.Equal(t, "string", params.Definitions["simpleParam"].Type)
assert.Equal(t, "nuggets", params.Definitions["simpleParam"].Extensions["x-chicken"])
assert.Equal(t, 1, orderedmap.Len(params.Definitions))
assert.Equal(t, "query", params.Definitions.GetOrZero("simpleParam").In)
assert.Equal(t, "simple", params.Definitions.GetOrZero("simpleParam").Name)
assert.Equal(t, "string", params.Definitions.GetOrZero("simpleParam").Type)
assert.Equal(t, "nuggets", params.Definitions.GetOrZero("simpleParam").Extensions["x-chicken"])
wentLow := params.GoLow()
assert.Equal(t, 20, wentLow.FindParameter("simpleParam").ValueNode.Line)
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, 11, wentLower.Name.ValueNode.Column)
@@ -100,7 +101,7 @@ func TestNewSwaggerDocument_Security(t *testing.T) {
initTest()
highDoc := NewSwaggerDocument(doc)
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()
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) {
initTest()
highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.SecurityDefinitions.Definitions, 3)
assert.Equal(t, "oauth2", highDoc.SecurityDefinitions.Definitions["petstore_auth"].Type)
assert.Equal(t, 3, orderedmap.Len(highDoc.SecurityDefinitions.Definitions))
assert.Equal(t, "oauth2", highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Type)
assert.Equal(t, "https://petstore.swagger.io/oauth/authorize",
highDoc.SecurityDefinitions.Definitions["petstore_auth"].AuthorizationUrl)
assert.Equal(t, "implicit", highDoc.SecurityDefinitions.Definitions["petstore_auth"].Flow)
assert.Len(t, highDoc.SecurityDefinitions.Definitions["petstore_auth"].Scopes.Values, 2)
highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").AuthorizationUrl)
assert.Equal(t, "implicit", highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Flow)
assert.Equal(t, 2, orderedmap.Len(highDoc.SecurityDefinitions.Definitions.GetOrZero("petstore_auth").Scopes.Values))
goLow := highDoc.SecurityDefinitions.GoLow()
assert.Equal(t, 661, goLow.FindSecurityDefinition("petstore_auth").ValueNode.Line)
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, 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, 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) {
initTest()
highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.Responses.Definitions, 2)
assert.Equal(t, 2, orderedmap.Len(highDoc.Responses.Definitions))
defs := highDoc.Responses.Definitions
assert.Equal(t, "morning", defs["200"].Extensions["x-coffee"])
assert.Equal(t, "OK", defs["200"].Description)
assert.Equal(t, "morning", defs.GetOrZero("200").Extensions["x-coffee"])
assert.Equal(t, "OK", defs.GetOrZero("200").Description)
assert.Equal(t, "a generic API response object",
defs["200"].Schema.Schema().Description)
assert.Len(t, defs["200"].Examples.Values, 3)
defs.GetOrZero("200").Schema.Schema().Description)
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.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.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)
expWentLow := defs["200"].Examples.GoLow()
expWentLow := defs.GetOrZero("200").Examples.GoLow()
assert.Equal(t, 702, expWentLow.FindExample("application/json").ValueNode.Line)
assert.Equal(t, 9, expWentLow.FindExample("application/json").ValueNode.Column)
wentLow := highDoc.Responses.GoLow()
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)
x := y.Items
@@ -192,7 +193,7 @@ func TestNewSwaggerDocument_Definitions(t *testing.T) {
initTest()
highDoc := NewSwaggerDocument(doc)
assert.Len(t, highDoc.Definitions.Definitions, 6)
assert.Equal(t, 6, orderedmap.Len(highDoc.Definitions.Definitions))
wentLow := highDoc.Definitions.GoLow()
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) {
initTest()
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")
assert.Equal(t, "man", upload.Extensions["x-potato"])
@@ -264,9 +265,9 @@ func TestNewSwaggerDocument_Responses(t *testing.T) {
highDoc := NewSwaggerDocument(doc)
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, "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
import (
"sort"
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
)
// Callback represents a high-level Callback object for OpenAPI 3+.
@@ -19,7 +21,7 @@ import (
// that identifies a URL to use for the callback operation.
// - https://spec.openapis.org/oas/v3.1.0#callback-object
type Callback struct {
Expression map[string]*PathItem `json:"-" yaml:"-"`
Expression orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.Callback
}
@@ -28,9 +30,9 @@ type Callback struct {
func NewCallback(lowCallback *low.Callback) *Callback {
n := new(Callback)
n.low = lowCallback
n.Expression = make(map[string]*PathItem)
for i := range lowCallback.Expression.Value {
n.Expression[i.Value] = NewPathItem(lowCallback.Expression.Value[i].Value)
n.Expression = orderedmap.New[string, *PathItem]()
for pair := orderedmap.First(lowCallback.Expression.Value); pair != nil; pair = pair.Next() {
n.Expression.Set(pair.Key().Value, NewPathItem(pair.Value().Value))
}
n.Extensions = make(map[string]any)
for k, v := range lowCallback.Extensions {
@@ -66,16 +68,16 @@ func (c *Callback) MarshalYAML() (interface{}, error) {
}
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.
if c.low != nil {
for lKey := range c.low.Expression.Value {
if lKey.Value == k {
ln = lKey.KeyNode.Line
for lPair := orderedmap.First(c.low.Expression.Value); lPair != nil; lPair = lPair.Next() {
if lPair.Key().Value == pair.Key() {
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

View File

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

View File

@@ -6,12 +6,12 @@ package v3
import (
"sync"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high"
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -21,15 +21,15 @@ import (
// 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
type Components struct {
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Schemas orderedmap.Map[string, *highbase.SchemaProxy] `json:"schemas,omitempty" yaml:"schemas,omitempty"`
Responses orderedmap.Map[string, *Response] `json:"responses,omitempty" yaml:"responses,omitempty"`
Parameters orderedmap.Map[string, *Parameter] `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Examples orderedmap.Map[string, *highbase.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
RequestBodies orderedmap.Map[string, *RequestBody] `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
Headers orderedmap.Map[string, *Header] `json:"headers,omitempty" yaml:"headers,omitempty"`
SecuritySchemes orderedmap.Map[string, *SecurityScheme] `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
Links orderedmap.Map[string, *Link] `json:"links,omitempty" yaml:"links,omitempty"`
Callbacks orderedmap.Map[string, *Callback] `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.Components
}
@@ -43,15 +43,15 @@ func NewComponents(comp *low.Components) *Components {
if len(comp.Extensions) > 0 {
c.Extensions = high.ExtractExtensions(comp.Extensions)
}
cbMap := make(map[string]*Callback)
linkMap := make(map[string]*Link)
responseMap := make(map[string]*Response)
parameterMap := make(map[string]*Parameter)
exampleMap := make(map[string]*highbase.Example)
requestBodyMap := make(map[string]*RequestBody)
headerMap := make(map[string]*Header)
securitySchemeMap := make(map[string]*SecurityScheme)
schemas := make(map[string]*highbase.SchemaProxy)
cbMap := orderedmap.New[string, *Callback]()
linkMap := orderedmap.New[string, *Link]()
responseMap := orderedmap.New[string, *Response]()
parameterMap := orderedmap.New[string, *Parameter]()
exampleMap := orderedmap.New[string, *highbase.Example]()
requestBodyMap := orderedmap.New[string, *RequestBody]()
headerMap := orderedmap.New[string, *Header]()
securitySchemeMap := orderedmap.New[string, *SecurityScheme]()
schemas := orderedmap.New[string, *highbase.SchemaProxy]()
// build all components asynchronously.
var wg sync.WaitGroup
@@ -114,32 +114,33 @@ type componentResult[T any] struct {
}
// 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) {
translateFunc := func(key lowmodel.KeyReference[string], value lowmodel.ValueReference[IN]) (componentResult[OUT], error) {
return componentResult[OUT]{key: key.Value, res: translateItem(value.Value)}, nil
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(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]]) (componentResult[OUT], error) {
return componentResult[OUT]{key: pair.Key().Value, res: translateItem(pair.Value().Value)}, nil
}
resultFunc := func(value componentResult[OUT]) error {
outMap[value.key] = value.res
outMap.Set(value.key, value.res)
return nil
}
_ = datamodel.TranslateMapParallel(inMap, translateFunc, resultFunc)
_ = orderedmap.TranslateMapParallel(inMap, translateFunc, resultFunc)
}
// buildSchema builds a schema from low level structs.
func buildSchema(inMap map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*base.SchemaProxy], outMap map[string]*highbase.SchemaProxy) {
translateFunc := func(key lowmodel.KeyReference[string], value lowmodel.ValueReference[*base.SchemaProxy]) (componentResult[*highbase.SchemaProxy], error) {
func buildSchema(inMap orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]], outMap orderedmap.Map[string, *highbase.SchemaProxy]) {
translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]]) (componentResult[*highbase.SchemaProxy], error) {
value := pair.Value()
var sch *highbase.SchemaProxy
sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
Value: value.Value,
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 {
outMap[value.key] = value.res
outMap.Set(value.key, value.res)
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.

View File

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

View File

@@ -11,10 +11,12 @@ package v3
import (
"bytes"
"github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"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,
// 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.
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
// 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
}
if !document.Webhooks.IsEmpty() {
hooks := make(map[string]*PathItem)
for h := range document.Webhooks.Value {
hooks[h.Value] = NewPathItem(document.Webhooks.Value[h].Value)
hooks := orderedmap.New[string, *PathItem]()
for pair := orderedmap.First(document.Webhooks.Value); pair != nil; pair = pair.Next() {
hooks.Set(pair.Key().Value, NewPathItem(pair.Value().Value))
}
d.Webhooks = hooks
}

View File

@@ -57,7 +57,7 @@ func TestNewDocument_Security(t *testing.T) {
h := NewDocument(lowDoc)
assert.Len(t, h.Security, 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) {
@@ -98,14 +98,14 @@ func TestNewDocument_Servers(t *testing.T) {
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.Len(t, h.Servers[0].Variables, 1)
assert.Equal(t, "https", h.Servers[0].Variables["scheme"].Default)
assert.Len(t, h.Servers[0].Variables["scheme"].Enum, 2)
assert.Equal(t, "https", h.Servers[0].Variables.GetOrZero("scheme").Default)
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, "this is our second API server, for all fun API things.", h.Servers[1].Description)
assert.Len(t, h.Servers[1].Variables, 2)
assert.Equal(t, "api", h.Servers[1].Variables["domain"].Default)
assert.Equal(t, "pb33f.io", h.Servers[1].Variables["host"].Default)
assert.Equal(t, "api", h.Servers[1].Variables.GetOrZero("domain").Default)
assert.Equal(t, "pb33f.io", h.Servers[1].Variables.GetOrZero("host").Default)
wentLow := h.GoLow()
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, 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, 22, wentLowest.Description.ValueNode.Column)
}
@@ -147,18 +147,18 @@ func TestNewDocument_Tags(t *testing.T) {
func TestNewDocument_Webhooks(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Webhooks, 1)
assert.Equal(t, "Information about a new burger", h.Webhooks["someHook"].Post.RequestBody.Description)
assert.Equal(t, 1, orderedmap.Len(h.Webhooks))
assert.Equal(t, "Information about a new burger", h.Webhooks.GetOrZero("someHook").Post.RequestBody.Description)
}
func TestNewDocument_Components_Links(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Links, 2)
assert.Equal(t, "locateBurger", h.Components.Links["LocateBurger"].OperationId)
assert.Equal(t, "$response.body#/id", h.Components.Links["LocateBurger"].Parameters["burgerId"])
assert.Equal(t, 2, orderedmap.Len(h.Components.Links))
assert.Equal(t, "locateBurger", h.Components.Links.GetOrZero("LocateBurger").OperationId)
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, 20, wentLow.OperationId.ValueNode.Column)
}
@@ -166,29 +166,29 @@ func TestNewDocument_Components_Links(t *testing.T) {
func TestNewDocument_Components_Callbacks(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Callbacks, 1)
assert.Equal(t, 1, orderedmap.Len(h.Components.Callbacks))
assert.Equal(
t,
"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(
t,
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(
t,
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 {
if k.Value == "BurgerCallback" {
assert.Equal(t, 295, k.KeyNode.Line)
assert.Equal(t, 5, k.KeyNode.Column)
for pair := orderedmap.First(h.Components.GoLow().Callbacks.Value); pair != nil; pair = pair.Next() {
if pair.Key().Value == "BurgerCallback" {
assert.Equal(t, 295, pair.Key().KeyNode.Line)
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) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Schemas, 6)
assert.Equal(t, 6, orderedmap.Len(h.Components.Schemas))
goLow := h.Components.GoLow()
a := h.Components.Schemas["Error"]
abcd := a.Schema().Properties["message"].Schema().Example
a := h.Components.Schemas.GetOrZero("Error")
abcd := a.Schema().Properties.GetOrZero("message").Schema().Example
assert.Equal(t, "No such burger as 'Big-Whopper'", abcd)
assert.Equal(t, 433, goLow.Schemas.KeyNode.Line)
assert.Equal(t, 3, goLow.Schemas.KeyNode.Column)
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.Equal(t, "golden slices of happy fun joy", b.Schema().Properties["fries"].Schema().Description)
assert.Equal(t, int64(2), b.Schema().Properties["numPatties"].Schema().Example)
assert.Equal(t, "golden slices of happy fun joy", b.Schema().Properties.GetOrZero("fries").Schema().Description)
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, 7, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Column)
assert.Equal(t, 450, b.Schema().GoLow().FindProperty("name").ValueNode.Line)
f := h.Components.Schemas["Fries"]
assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items.A.Schema().Example)
assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 1)
f := h.Components.Schemas.GetOrZero("Fries")
assert.Equal(t, "salt", f.Schema().Properties.GetOrZero("seasoning").Schema().Items.A.Schema().Example)
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.True(t, d.Schema().AdditionalProperties.(bool))
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, 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, 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) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Headers, 1)
assert.Equal(t, "this is a header example for UseOil", h.Components.Headers["UseOil"].Description)
assert.Equal(t, 323, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, h.Components.Headers["UseOil"].GoLow().Description.ValueNode.Column)
assert.Equal(t, 1, orderedmap.Len(h.Components.Headers))
assert.Equal(t, "this is a header example for UseOil", h.Components.Headers.GetOrZero("UseOil").Description)
assert.Equal(t, 323, h.Components.Headers.GetOrZero("UseOil").GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, h.Components.Headers.GetOrZero("UseOil").GoLow().Description.ValueNode.Column)
}
func TestNewDocument_Components_RequestBodies(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.RequestBodies, 1)
assert.Equal(t, "Give us the new burger!", h.Components.RequestBodies["BurgerRequest"].Description)
assert.Equal(t, 328, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, h.Components.RequestBodies["BurgerRequest"].GoLow().Description.ValueNode.Column)
assert.Len(t, h.Components.RequestBodies["BurgerRequest"].Content["application/json"].Examples, 2)
assert.Equal(t, 1, orderedmap.Len(h.Components.RequestBodies))
assert.Equal(t, "Give us the new burger!", h.Components.RequestBodies.GetOrZero("BurgerRequest").Description)
assert.Equal(t, 328, h.Components.RequestBodies.GetOrZero("BurgerRequest").GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, h.Components.RequestBodies.GetOrZero("BurgerRequest").GoLow().Description.ValueNode.Column)
assert.Equal(t, 2, orderedmap.Len(h.Components.RequestBodies.GetOrZero("BurgerRequest").Content.GetOrZero("application/json").Examples))
}
func TestNewDocument_Components_Examples(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Examples, 1)
assert.Equal(t, "A juicy two hander sammich", h.Components.Examples["QuarterPounder"].Summary)
assert.Equal(t, 346, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Line)
assert.Equal(t, 16, h.Components.Examples["QuarterPounder"].GoLow().Summary.ValueNode.Column)
assert.Equal(t, 1, orderedmap.Len(h.Components.Examples))
assert.Equal(t, "A juicy two hander sammich", h.Components.Examples.GetOrZero("QuarterPounder").Summary)
assert.Equal(t, 346, h.Components.Examples.GetOrZero("QuarterPounder").GoLow().Summary.ValueNode.Line)
assert.Equal(t, 16, h.Components.Examples.GetOrZero("QuarterPounder").GoLow().Summary.ValueNode.Column)
}
func TestNewDocument_Components_Responses(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Responses, 1)
assert.Equal(t, "all the dressings for a burger.", h.Components.Responses["DressingResponse"].Description)
assert.Equal(t, "array", h.Components.Responses["DressingResponse"].Content["application/json"].Schema.Schema().Type[0])
assert.Equal(t, 352, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Line)
assert.Equal(t, 7, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Column)
assert.Equal(t, 1, orderedmap.Len(h.Components.Responses))
assert.Equal(t, "all the dressings for a burger.", h.Components.Responses.GetOrZero("DressingResponse").Description)
assert.Equal(t, "array", h.Components.Responses.GetOrZero("DressingResponse").Content.GetOrZero("application/json").Schema.Schema().Type[0])
assert.Equal(t, 352, h.Components.Responses.GetOrZero("DressingResponse").GoLow().Description.KeyNode.Line)
assert.Equal(t, 7, h.Components.Responses.GetOrZero("DressingResponse").GoLow().Description.KeyNode.Column)
}
func TestNewDocument_Components_SecuritySchemes(t *testing.T) {
initTest()
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, 364, api.GoLow().Description.ValueNode.Line)
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, 369, jwt.GoLow().Description.ValueNode.Line)
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, 375, oAuth.GoLow().Description.ValueNode.Line)
assert.Equal(t, 20, oAuth.GoLow().Description.ValueNode.Column)
assert.Len(t, oAuth.Flows.Implicit.Scopes, 2)
assert.Equal(t, "read all burgers", oAuth.Flows.Implicit.Scopes["read:burgers"])
assert.Equal(t, 2, orderedmap.Len(oAuth.Flows.Implicit.Scopes))
assert.Equal(t, "read all burgers", oAuth.Flows.Implicit.Scopes.GetOrZero("read:burgers"))
assert.Equal(t, "https://pb33f.io/oauth", oAuth.Flows.AuthorizationCode.AuthorizationUrl)
// check the lowness is low.
@@ -306,26 +306,26 @@ func TestNewDocument_Components_SecuritySchemes(t *testing.T) {
func TestNewDocument_Components_Parameters(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Components.Parameters, 2)
bh := h.Components.Parameters["BurgerHeader"]
assert.Equal(t, 2, orderedmap.Len(h.Components.Parameters))
bh := h.Components.Parameters.GetOrZero("BurgerHeader")
assert.Equal(t, "burgerHeader", bh.Name)
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.True(t, bh.Required)
assert.Equal(
t,
"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, 409, bh.Content["application/json"].Encoding["burgerTheme"].GoLow().ContentType.ValueNode.Line)
assert.Equal(t, 2, orderedmap.Len(bh.Content.GetOrZero("application/json").Schema.Schema().Properties))
assert.Equal(t, 409, bh.Content.GetOrZero("application/json").Encoding.GetOrZero("burgerTheme").GoLow().ContentType.ValueNode.Line)
}
func TestNewDocument_Paths(t *testing.T) {
initTest()
h := NewDocument(lowDoc)
assert.Len(t, h.Paths.PathItems, 5)
assert.Equal(t, 5, orderedmap.Len(h.Paths.PathItems))
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.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.Len(t, burgersOp.Post.Responses.Codes, 3)
assert.Equal(t, 3, orderedmap.Len(burgersOp.Post.Responses.Codes))
if checkLines {
assert.Equal(t, 64, burgersOp.GoLow().Post.KeyNode.Line)
assert.Equal(t, 63, h.Paths.GoLow().FindPath("/burgers").ValueNode.Line)
}
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.Len(t, okResp.Content["application/json"].Examples, 2)
assert.Equal(t, 2, orderedmap.Len(okResp.Content.GetOrZero("application/json").Examples))
assert.Equal(
t,
"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, "locateBurger", okResp.Links["LocateBurger"].OperationId)
assert.Equal(t, 2, orderedmap.Len(okResp.Links))
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["OAuthScheme"], 2)
assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements["OAuthScheme"][0])
assert.Len(t, burgersOp.Post.Security[0].Requirements.GetOrZero("OAuthScheme"), 2)
assert.Equal(t, "read:burgers", burgersOp.Post.Security[0].Requirements.GetOrZero("OAuthScheme")[0])
assert.Len(t, burgersOp.Post.Servers, 1)
assert.Equal(t, "https://pb33f.io", burgersOp.Post.Servers[0].URL)
if checkLines {
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, 80, okResp.Content["application/json"].GoLow().Schema.KeyNode.Line)
assert.Equal(t, 15, okResp.Content["application/json"].GoLow().Schema.KeyNode.Column)
assert.Equal(t, 80, okResp.Content.GetOrZero("application/json").GoLow().Schema.KeyNode.Line)
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, 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)
}
@@ -534,7 +534,7 @@ func TestDocument_MarshalJSON(t *testing.T) {
newDoc := NewDocument(lowDoc)
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) {
@@ -711,8 +711,8 @@ components:
h := NewDocument(lowDoc)
// mutate the schema
g := h.Components.Schemas["BurgerHeader"].Schema()
ds := g.Properties["burgerTheme"].Schema()
g := h.Components.Schemas.GetOrZero("BurgerHeader").Schema()
ds := g.Properties.GetOrZero("burgerTheme").Schema()
ds.Description = "changed"
// render the document to YAML and it should be identical.

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -14,7 +15,7 @@ import (
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
type Encoding struct {
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"`
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
@@ -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
func ExtractEncoding(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Encoding]) map[string]*Encoding {
extracted := make(map[string]*Encoding)
for k, v := range elements {
extracted[k.Value] = NewEncoding(v.Value)
func ExtractEncoding(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Encoding]]) orderedmap.Map[string, *Encoding] {
extracted := orderedmap.New[string, *Encoding]()
for pair := orderedmap.First(elements); pair != nil; pair = pair.Next() {
extracted.Set(pair.Key().Value, NewEncoding(pair.Value().Value))
}
return extracted
}

View File

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

View File

@@ -9,6 +9,7 @@ import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -24,8 +25,8 @@ type Header struct {
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"`
Examples orderedmap.Map[string, *highbase.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
Content orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
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.
func ExtractHeaders(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Header]) map[string]*Header {
extracted := make(map[string]*Header)
for k, v := range elements {
extracted[k.Value] = NewHeader(v.Value)
func ExtractHeaders(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Header]]) orderedmap.Map[string, *Header] {
extracted := orderedmap.New[string, *Header]()
for pair := orderedmap.First(elements); pair != nil; pair = pair.Next() {
extracted.Set(pair.Key().Value, NewHeader(pair.Value().Value))
}
return extracted
}

View File

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

View File

@@ -6,6 +6,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -24,7 +25,7 @@ import (
type Link struct {
OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,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"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Server *Server `json:"server,omitempty" yaml:"server,omitempty"`
@@ -38,9 +39,9 @@ func NewLink(link *low.Link) *Link {
l.low = link
l.OperationRef = link.OperationRef.Value
l.OperationId = link.OperationId.Value
params := make(map[string]string)
for k, v := range link.Parameters.Value {
params[k.Value] = v.Value
params := orderedmap.New[string, string]()
for pair := orderedmap.First(link.Parameters.Value); pair != nil; pair = pair.Next() {
params.Set(pair.Key().Value, pair.Value().Value)
}
l.Parameters = params
l.RequestBody = link.RequestBody.Value

View File

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

View File

@@ -4,12 +4,11 @@
package v3
import (
"sync"
"github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -20,8 +19,8 @@ import (
type MediaType struct {
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"`
Examples orderedmap.Map[string, *base.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
Encoding orderedmap.Map[string, *Encoding] `json:"encoding,omitempty" yaml:"encoding,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
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
// and easier to navigate high-level one.
func ExtractContent(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.MediaType]) map[string]*MediaType {
// extract everything async
doneChan := make(chan bool)
var extLock sync.RWMutex
extractContentItem := func(k lowmodel.KeyReference[string],
v lowmodel.ValueReference[*low.MediaType], c chan bool, e map[string]*MediaType) {
extLock.Lock()
e[k.Value] = NewMediaType(v.Value)
extLock.Unlock()
c <- true
}
extracted := make(map[string]*MediaType)
for k, v := range elements {
go extractContentItem(k, v, doneChan, extracted)
}
n := 0
for n < len(elements) {
select {
case <-doneChan:
n++
func ExtractContent(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) orderedmap.Map[string, *MediaType] {
extracted := orderedmap.New[string, *MediaType]()
translateFunc := func(pair orderedmap.Pair[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) (asyncResult[*MediaType], error) {
return asyncResult[*MediaType]{
key: pair.Key().Value,
result: NewMediaType(pair.Value().Value),
}, nil
}
resultFunc := func(value asyncResult[*MediaType]) error {
extracted.Set(value.key, value.result)
return nil
}
_ = orderedmap.TranslateMapParallel(elements, translateFunc, resultFunc)
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.
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
yml, _ := mt.Render()
@@ -118,7 +118,7 @@ func TestMediaType_MarshalYAML(t *testing.T) {
// create a new document and extract a media type object from it.
d := NewDocument(lowDoc)
mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content["application/json"]
mt := d.Paths.PathItems.GetOrZero("/pet").Put.RequestBody.Content.GetOrZero("application/json")
// render out the media type
yml, _ := mt.Render()

View File

@@ -6,6 +6,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -15,7 +16,7 @@ type OAuthFlow struct {
AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,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:"-"`
low *low.OAuthFlow
}
@@ -27,9 +28,9 @@ func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
o.TokenUrl = flow.TokenUrl.Value
o.AuthorizationUrl = flow.AuthorizationUrl.Value
o.RefreshUrl = flow.RefreshUrl.Value
scopes := make(map[string]string)
for k, v := range flow.Scopes.Value {
scopes[k.Value] = v.Value
scopes := orderedmap.New[string, string]()
for pair := orderedmap.First(flow.Scopes.Value); pair != nil; pair = pair.Next() {
scopes.Set(pair.Key().Value, pair.Value().Value)
}
o.Scopes = scopes
o.Extensions = high.ExtractExtensions(flow.Extensions)

View File

@@ -4,9 +4,11 @@
package v3
import (
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
)
func TestOAuthFlow_MarshalYAML(t *testing.T) {
@@ -15,7 +17,10 @@ func TestOAuthFlow_MarshalYAML(t *testing.T) {
AuthorizationUrl: "https://pb33f.io",
TokenUrl: "https://pb33f.io/token",
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()

View File

@@ -4,13 +4,14 @@
package v3
import (
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestNewOAuthFlows(t *testing.T) {
@@ -81,7 +82,7 @@ clientCredentials:
CHIP:CHOP: microwave a sock`
// 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()
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/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -24,7 +25,7 @@ type Operation struct {
Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
RequestBody *RequestBody `json:"requestBody,omitempty" yaml:"requestBody,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"`
Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"`
Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"`
@@ -81,9 +82,9 @@ func NewOperation(operation *low.Operation) *Operation {
o.Servers = servers
o.Extensions = high.ExtractExtensions(operation.Extensions)
if !operation.Callbacks.IsEmpty() {
cbs := make(map[string]*Callback)
for k, v := range operation.Callbacks.Value {
cbs[k.Value] = NewCallback(v.Value)
cbs := orderedmap.New[string, *Callback]()
for pair := orderedmap.First(operation.Callbacks.Value); pair != nil; pair = pair.Next() {
cbs.Set(pair.Key().Value, NewCallback(pair.Value().Value))
}
o.Callbacks = cbs
}

View File

@@ -50,7 +50,7 @@ callbacks:
assert.Equal(t, "https://pb33f.io", r.ExternalDocs.URL)
assert.Equal(t, 1, r.GoLow().ExternalDocs.KeyNode.Line)
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)
}

View File

@@ -38,6 +38,6 @@ func Example_createHighLevelOpenAPIDocument() {
// Print out some details
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
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/high"
"github.com/pb33f/libopenapi/datamodel/high/base"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -26,8 +27,8 @@ type Parameter struct {
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"`
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"`
Examples orderedmap.Map[string, *base.Example] `json:"examples,omitempty" yaml:"examples,omitempty"`
Content orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.Parameter
}

View File

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

View File

@@ -84,7 +84,7 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
}
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()
pi := pair.Value()
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})
return nil
}
_ = orderedmap.For[string, *PathItem](p.PathItems, action)
nb := high.NewNodeBuilder(p, p.low)
extNode := nb.Render()
@@ -142,7 +140,7 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
}
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()
pi := pair.Value()
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})
return nil
}
_ = orderedmap.For[string, *PathItem](p.PathItems, action)
nb := high.NewNodeBuilder(p, p.low)
nb.Resolve = true

View File

@@ -6,6 +6,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -13,7 +14,7 @@ import (
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
type RequestBody struct {
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"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.RequestBody

View File

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

View File

@@ -7,10 +7,10 @@ import (
"fmt"
"sort"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high"
lowbase "github.com/pb33f/libopenapi/datamodel/low"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
@@ -30,7 +30,7 @@ import (
// be the response for a successful operation call.
// - https://spec.openapis.org/oas/v3.1.0#responses-object
type Responses struct {
Codes map[string]*Response `json:"-" yaml:"-"`
Codes orderedmap.Map[string, *Response] `json:"-" yaml:"-"`
Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.Responses
@@ -45,28 +45,31 @@ func NewResponses(responses *low.Responses) *Responses {
if !responses.Default.IsEmpty() {
r.Default = NewResponse(responses.Default.Value)
}
codes := make(map[string]*Response)
codes := orderedmap.New[string, *Response]()
type respRes struct {
code string
resp *Response
}
translateFunc := func(key lowbase.KeyReference[string], value lowbase.ValueReference[*low.Response]) (respRes, error) {
return respRes{code: key.Value, resp: NewResponse(value.Value)}, nil
translateFunc := func(pair orderedmap.Pair[lowbase.KeyReference[string], lowbase.ValueReference[*low.Response]]) (asyncResult[*Response], error) {
return asyncResult[*Response]{
key: pair.Key().Value,
result: NewResponse(pair.Value().Value),
}, nil
}
resultFunc := func(value respRes) error {
codes[value.code] = value.resp
resultFunc := func(value asyncResult[*Response]) error {
codes.Set(value.key, value.result)
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
return r
}
// FindResponseByCode is a shortcut for looking up code by an integer vs. a string
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.
@@ -101,16 +104,16 @@ func (r *Responses) MarshalYAML() (interface{}, error) {
}
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.
if r.low != nil {
for lKey := range r.low.Codes {
if lKey.Value == k {
ln = lKey.KeyNode.Line
for lPair := orderedmap.First(r.low.Codes); lPair != nil; lPair = lPair.Next() {
if lPair.Key().Value == pair.Key() {
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
@@ -157,16 +160,16 @@ func (r *Responses) MarshalYAMLInline() (interface{}, error) {
}
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.
if r.low != nil {
for lKey := range r.low.Codes {
if lKey.Value == k {
ln = lKey.KeyNode.Line
for lPair := orderedmap.First(r.low.Codes); lPair != nil; lPair = lPair.Next() {
if lPair.Key().Value == pair.Key() {
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

View File

@@ -6,6 +6,7 @@ package v3
import (
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/orderedmap"
"gopkg.in/yaml.v3"
)
@@ -14,7 +15,7 @@ import (
type Server struct {
URL string `json:"url,omitempty" yaml:"url,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:"-"`
low *low.Server
}
@@ -25,9 +26,9 @@ func NewServer(server *low.Server) *Server {
s.low = server
s.Description = server.Description.Value
s.URL = server.URL.Value
vars := make(map[string]*ServerVariable)
for k, val := range server.Variables.Value {
vars[k.Value] = NewServerVariable(val.Value)
vars := orderedmap.New[string, *ServerVariable]()
for pair := orderedmap.First(server.Variables.Value); pair != nil; pair = pair.Next() {
vars.Set(pair.Key().Value, NewServerVariable(pair.Value().Value))
}
s.Variables = vars
s.Extensions = high.ExtractExtensions(server.Extensions)

View File

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

View File

@@ -10,6 +10,7 @@ import (
"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"
)
@@ -96,8 +97,8 @@ type Schema struct {
If low.NodeReference[*SchemaProxy]
Else low.NodeReference[*SchemaProxy]
Then low.NodeReference[*SchemaProxy]
DependentSchemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
DependentSchemas low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
PatternProperties low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
PropertyNames low.NodeReference[*SchemaProxy]
UnevaluatedItems low.NodeReference[*SchemaProxy]
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]
@@ -120,7 +121,7 @@ type Schema struct {
Required low.NodeReference[[]low.ValueReference[string]]
Enum low.NodeReference[[]low.ValueReference[any]]
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]
Description low.NodeReference[string]
ContentEncoding low.NodeReference[string]
@@ -305,10 +306,10 @@ func (s *Schema) Hash() [32]byte {
for i := range s.Enum.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
for i := range s.Properties.Value {
propKeys[z] = i.Value
for pair := orderedmap.First(s.Properties.Value); pair != nil; pair = pair.Next() {
propKeys[z] = pair.Key().Value
z++
}
sort.Strings(propKeys)
@@ -414,10 +415,10 @@ func (s *Schema) Hash() [32]byte {
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
for i := range s.DependentSchemas.Value {
depSchemasKeys[z] = i.Value
for pair := orderedmap.First(s.DependentSchemas.Value); pair != nil; pair = pair.Next() {
depSchemasKeys[z] = pair.Key().Value
z++
}
sort.Strings(depSchemasKeys)
@@ -425,10 +426,10 @@ func (s *Schema) Hash() [32]byte {
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
for i := range s.PatternProperties.Value {
patternPropsKeys[z] = i.Value
for pair := orderedmap.First(s.PatternProperties.Value); pair != nil; pair = pair.Next() {
patternPropsKeys[z] = pair.Key().Value
z++
}
sort.Strings(patternPropsKeys)
@@ -490,19 +491,19 @@ func (s *Schema) Hash() [32]byte {
// FindProperty will return a ValueReference pointer containing a SchemaProxy pointer
// from a property key name. if found
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
// from a dependent schema key name. if found (3.1+ only)
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
// from a pattern property key name. if found (3.1+ only)
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
@@ -1067,7 +1068,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
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!
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)
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
totalProps := 0
for i, prop := range propsNode.Content {
@@ -1119,10 +1120,10 @@ func buildPropertyMap(root *yaml.Node, idx *index.SpecIndex, label string) (*low
select {
case res := <-bChan:
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,
KeyNode: propLabel,
ValueNode: propsNode,

View File

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

View File

@@ -6,13 +6,15 @@ package low
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
"gopkg.in/yaml.v3"
"reflect"
"strconv"
"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
@@ -29,6 +31,22 @@ func FindItemInMap[T any](item string, collection map[KeyReference[string]]Value
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.
func generateIndexCollection(idx *index.SpecIndex) []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,
idx *index.SpecIndex,
includeExtensions bool,
) (map[KeyReference[string]]ValueReference[PT], error) {
valueMap := make(map[KeyReference[string]]ValueReference[PT])
) (orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]()
var circError error
if utils.IsNodeMap(root) {
var currentKey *yaml.Node
@@ -431,15 +449,18 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
SetReference(n, referenceValue)
}
if currentKey != nil {
valueMap[KeyReference[string]{
valueMap.Set(
KeyReference[string]{
Value: currentKey.Value,
KeyNode: currentKey,
}] = ValueReference[PT]{
},
ValueReference[PT]{
Value: n,
ValueNode: node,
//IsReference: isReference,
Reference: referenceValue,
}
},
)
}
}
}
@@ -458,7 +479,7 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
func ExtractMapNoLookup[PT Buildable[N], N any](
root *yaml.Node,
idx *index.SpecIndex,
) (map[KeyReference[string]]ValueReference[PT], error) {
) (orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
return ExtractMapNoLookupExtensions[PT, N](root, idx, false)
}
@@ -478,7 +499,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
root *yaml.Node,
idx *index.SpecIndex,
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 referenceValue string
var labelNode, valueNode *yaml.Node
@@ -522,8 +543,9 @@ func ExtractMapExtensions[PT Buildable[N], N any](
}
if valueNode != nil {
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])
eChan := make(chan error)
@@ -598,7 +620,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
return valueMap, labelNode, valueNode, err
case res := <-bChan:
completedKeys++
valueMap[res.k] = res.v
valueMap.Set(res.k, res.v)
}
}
if circError != nil && !idx.AllowCircularReferenceResolving() {
@@ -618,7 +640,7 @@ func ExtractMap[PT Buildable[N], N any](
label string,
root *yaml.Node,
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)
}

View File

@@ -11,6 +11,7 @@ import (
"testing"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/resolver"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
@@ -994,12 +995,12 @@ one:
assert.NoError(t, err)
assert.Len(t, things, 2)
for k, v := range things {
if k.Value == "x-hey" {
for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
if pair.Key().Value == "x-hey" {
continue
}
assert.Equal(t, "one", k.Value)
assert.Len(t, v.ValueNode.Content, 2)
assert.Equal(t, "one", pair.Key().Value)
assert.Len(t, pair.Value().ValueNode.Content, 2)
}
}
@@ -1050,8 +1051,8 @@ one:
assert.NoError(t, err)
assert.Len(t, things, 1)
for k := range things {
assert.Equal(t, "one", k.Value)
for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
assert.Equal(t, "one", pair.Key().Value)
}
}
@@ -1283,8 +1284,8 @@ one:
assert.NoError(t, err)
assert.Len(t, things, 1)
for k := range things {
assert.Equal(t, 99, things[k].Value.AlmostWork.Value)
for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
assert.Equal(t, 99, pair.Value().Value.AlmostWork.Value)
}
}
@@ -1313,8 +1314,8 @@ func TestExtractMapFlat_DoubleRef(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, things, 1)
for k := range things {
assert.Equal(t, 99, things[k].Value.AlmostWork.Value)
for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
assert.Equal(t, 99, pair.Value().Value.AlmostWork.Value)
}
}

View File

@@ -5,13 +5,15 @@ package v2
import (
"crypto/sha256"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"strings"
)
// 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
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
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.
@@ -29,7 +31,7 @@ type ParameterDefinitions struct {
// referenced to the ones defined here. It does not define global operation responses
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
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.
@@ -38,7 +40,7 @@ type ResponsesDefinitions struct {
// schemes on the operations and only serves to provide the relevant details for each scheme
// - https://swagger.io/specification/v2/#securityDefinitionsObject
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
@@ -47,33 +49,34 @@ type SecurityDefinitions struct {
// arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject
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.
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.
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.
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.
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.
func (d *Definitions) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
root = utils.NodeAlias(root)
utils.CheckForMergeNodes(root)
// TODO: Refactor with orderedmap.TranslatePipeline.
errorChan := make(chan error)
resultChan := make(chan definitionResult[*base.SchemaProxy])
var defLabel *yaml.Node
@@ -99,17 +102,18 @@ func (d *Definitions) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
}
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 {
select {
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
key := low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v
}
results.Set(key, sch.v)
}
}
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
func (d *Definitions) Hash() [32]byte {
var f []string
keys := make([]string, len(d.Schemas))
keys := make([]string, orderedmap.Len(d.Schemas))
z := 0
for k := range d.Schemas {
keys[z] = k.Value
for pair := orderedmap.First(d.Schemas); pair != nil; pair = pair.Next() {
keys[z] = pair.Key().Value
z++
}
sort.Strings(keys)
@@ -158,17 +162,18 @@ func (pd *ParameterDefinitions) Build(_, root *yaml.Node, idx *index.SpecIndex)
}
completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*Parameter])
results := orderedmap.New[low.KeyReference[string], low.ValueReference[*Parameter]]()
for completedDefs < totalDefinitions {
select {
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
key := low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v
}
results.Set(key, sch.v)
}
}
pd.Definitions = results
@@ -207,17 +212,18 @@ func (r *ResponsesDefinitions) Build(_, root *yaml.Node, idx *index.SpecIndex) e
}
completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*Response])
results := orderedmap.New[low.KeyReference[string], low.ValueReference[*Response]]()
for completedDefs < totalDefinitions {
select {
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
key := low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v
}
results.Set(key, sch.v)
}
}
r.Definitions = results
@@ -253,17 +259,18 @@ func (s *SecurityDefinitions) Build(_, root *yaml.Node, idx *index.SpecIndex) er
}
completedDefs := 0
results := make(map[low.KeyReference[string]]low.ValueReference[*SecurityScheme])
results := orderedmap.New[low.KeyReference[string], low.ValueReference[*SecurityScheme]]()
for completedDefs < totalDefinitions {
select {
case err := <-errorChan:
return err
case sch := <-resultChan:
completedDefs++
results[low.KeyReference[string]{
key := low.KeyReference[string]{
Value: sch.k.Value,
KeyNode: sch.k,
}] = sch.v
}
results.Set(key, sch.v)
}
}
s.Definitions = results

View File

@@ -6,24 +6,26 @@ package v2
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
// Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject
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.
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.
@@ -32,7 +34,7 @@ func (e *Examples) Build(_, root *yaml.Node, _ *index.SpecIndex) error {
utils.CheckForMergeNodes(root)
var keyNode, currNode *yaml.Node
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 {
if i%2 == 0 {
keyNode = root.Content[i]
@@ -48,32 +50,40 @@ func (e *Examples) Build(_, root *yaml.Node, _ *index.SpecIndex) error {
// lets just default to interface
var j interface{}
_ = currNode.Decode(&j)
e.Values[low.KeyReference[string]{
e.Values.Set(
low.KeyReference[string]{
Value: keyNode.Value,
KeyNode: keyNode,
}] = low.ValueReference[any]{
},
low.ValueReference[any]{
Value: j,
ValueNode: currNode,
}
},
)
continue
}
e.Values[low.KeyReference[string]{
e.Values.Set(
low.KeyReference[string]{
Value: keyNode.Value,
KeyNode: keyNode,
}] = low.ValueReference[any]{
},
low.ValueReference[any]{
Value: k,
ValueNode: currNode,
}
},
)
continue
}
e.Values[low.KeyReference[string]{
e.Values.Set(
low.KeyReference[string]{
Value: keyNode.Value,
KeyNode: keyNode,
}] = low.ValueReference[any]{
},
low.ValueReference[any]{
Value: n,
ValueNode: currNode,
}
},
)
}
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
func (e *Examples) Hash() [32]byte {
var f []string
keys := make([]string, len(e.Values))
keys := make([]string, orderedmap.Len(e.Values))
z := 0
for k := range e.Values {
keys[z] = k.Value
for pair := orderedmap.First(e.Values); pair != nil; pair = pair.Next() {
keys[z] = pair.Key().Value
z++
}
sort.Strings(keys)

View File

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

View File

@@ -6,13 +6,15 @@ package v2
import (
"crypto/sha256"
"fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"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.
@@ -22,7 +24,7 @@ import (
type Response struct {
Description low.NodeReference[string]
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]
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
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
@@ -68,7 +70,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return err
}
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,
KeyNode: lN,
ValueNode: kN,
@@ -87,8 +89,8 @@ func (r *Response) Hash() [32]byte {
f = append(f, low.GenerateHashString(r.Schema.Value))
}
if !r.Examples.IsEmpty() {
for k := range r.Examples.Value.Values {
f = append(f, low.GenerateHashString(r.Examples.Value.Values[k].Value))
for pair := orderedmap.First(r.Examples.Value.Values); pair != nil; pair = pair.Next() {
f = append(f, low.GenerateHashString(pair.Value().Value))
}
}
keys := make([]string, len(r.Extensions))

View File

@@ -6,17 +6,19 @@ package v2
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
type Responses struct {
Codes map[low.KeyReference[string]]low.ValueReference[*Response]
Codes orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]
Default low.NodeReference[*Response]
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] {
for n, o := range r.Codes {
if strings.ToLower(n.Value) == DefaultLabel {
for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
if strings.ToLower(pair.Key().Value) == DefaultLabel {
return &low.NodeReference[*Response]{
ValueNode: o.ValueNode,
KeyNode: n.KeyNode,
Value: o.Value,
ValueNode: pair.Value().ValueNode,
KeyNode: pair.Key().KeyNode,
Value: pair.Value().Value,
}
}
}
@@ -71,34 +73,34 @@ func (r *Responses) getDefault() *low.NodeReference[*Response] {
func (r *Responses) deleteCode(code string) {
var key *low.KeyReference[string]
if r.Codes != nil {
for k := range r.Codes {
if k.Value == code {
key = &k
for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
if pair.Key().Value == code {
key = pair.KeyPtr()
break
}
}
}
// should never be nil, but, you never know... science and all that!
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.
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
func (r *Responses) Hash() [32]byte {
var f []string
var keys []string
keys = make([]string, len(r.Codes))
keys = make([]string, orderedmap.Len(r.Codes))
cmap := make(map[string]*Response, len(keys))
z := 0
for k := range r.Codes {
keys[z] = k.Value
cmap[k.Value] = r.Codes[k].Value
for pair := orderedmap.First(r.Codes); pair != nil; pair = pair.Next() {
keys[z] = pair.Key().Value
cmap[pair.Key().Value] = pair.Value().Value
z++
}
sort.Strings(keys)

View File

@@ -6,12 +6,14 @@ package v2
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
@@ -19,7 +21,7 @@ import (
// Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject
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]
}
@@ -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.
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.
@@ -38,20 +40,23 @@ func (s *Scopes) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
root = utils.NodeAlias(root)
utils.CheckForMergeNodes(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) {
for k := range root.Content {
if k%2 == 0 {
if strings.Contains(root.Content[k].Value, "x-") {
continue
}
valueMap[low.KeyReference[string]{
valueMap.Set(
low.KeyReference[string]{
Value: root.Content[k].Value,
KeyNode: root.Content[k],
}] = low.ValueReference[string]{
},
low.ValueReference[string]{
Value: root.Content[k+1].Value,
ValueNode: root.Content[k+1],
}
},
)
}
}
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
func (s *Scopes) Hash() [32]byte {
var f []string
vals := make(map[string]low.ValueReference[string], len(s.Values))
keys := make([]string, len(s.Values))
vals := make(map[string]low.ValueReference[string], orderedmap.Len(s.Values))
keys := make([]string, orderedmap.Len(s.Values))
z := 0
for k := range s.Values {
keys[z] = k.Value
vals[k.Value] = s.Values[k]
for pair := orderedmap.First(s.Values); pair != nil; pair = pair.Next() {
keys[z] = pair.Key().Value
vals[pair.Key().Value] = pair.Value()
z++
}
sort.Strings(keys)

View File

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

View File

@@ -14,6 +14,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"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.
// - https://spec.openapis.org/oas/v3.1.0#components-object
type Components struct {
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]
RequestBodies low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*RequestBody]]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
SecuritySchemes low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]]
Links low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]]
Callbacks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]]
Schemas low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]]
Responses low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Response]]]
Parameters low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Parameter]]]
Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
RequestBodies low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*RequestBody]]]
Headers low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]
SecuritySchemes low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SecurityScheme]]]
Links low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Link]]]
Callbacks low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Callback]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference
}
@@ -75,16 +76,16 @@ func (co *Components) Hash() [32]byte {
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 {
return
}
l := make([]string, len(collection))
l := make([]string, orderedmap.Len(collection))
keys := make(map[string]low.ValueReference[T])
z := 0
for k := range collection {
keys[k.Value] = collection[k]
l[z] = k.Value
for pair := orderedmap.First(collection); pair != nil; pair = pair.Next() {
keys[pair.Key().Value] = pair.Value()
l[z] = pair.Key().Value
z++
}
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
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
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
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
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
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] {
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] {
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] {
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] {
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.
@@ -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
// low level model.
// 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) {
var emptyResult low.NodeReference[map[low.KeyReference[string]]low.ValueReference[T]]
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[orderedmap.Map[low.KeyReference[string], low.ValueReference[T]]]
_, nodeLabel, nodeValue := utils.FindKeyNodeFullTop(label, root.Content)
if nodeValue == 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) {
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.
go func() {
for result := range out {
componentValues[result.key] = result.value
componentValues.Set(result.key, result.value)
}
close(done)
wg.Done()
@@ -317,7 +318,7 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
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,
ValueNode: nodeValue,
Value: componentValues,

View File

@@ -9,6 +9,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/resolver"
"github.com/pb33f/libopenapi/utils"
)
@@ -247,7 +248,7 @@ func extractWebhooks(info *datamodel.SpecInfo, doc *Document, idx *index.SpecInd
return eErr
}
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,
KeyNode: hooksL,
ValueNode: hooksN,

View File

@@ -7,6 +7,7 @@ import (
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/stretchr/testify/assert"
)
@@ -225,10 +226,10 @@ func TestCreateDocument_Info(t *testing.T) {
func TestCreateDocument_WebHooks(t *testing.T) {
initTest()
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.
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/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
)
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,
// 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.
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
// 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.
func (d *Document) FindSecurityRequirement(name string) []low.ValueReference[string] {
for k := range d.Security.Value {
for i := range d.Security.Value[k].Value.Requirements.Value {
if i.Value == name {
return d.Security.Value[k].Value.Requirements.Value[i].Value
requirements := d.Security.Value[k].Value.Requirements
for pair := orderedmap.First(requirements.Value); pair != nil; pair = pair.Next() {
if pair.Key().Value == name {
return pair.Value().Value
}
}
}

View File

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

View File

@@ -6,13 +6,15 @@ package v3
import (
"crypto/sha256"
"fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"strings"
)
// Header represents a low-level OpenAPI 3+ Header object.
@@ -27,8 +29,8 @@ type Header struct {
AllowReserved low.NodeReference[bool]
Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
*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
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
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.
@@ -73,14 +75,14 @@ func (h *Header) Hash() [32]byte {
if h.Example.Value != nil {
f = append(f, fmt.Sprint(h.Example.Value))
}
if len(h.Examples.Value) > 0 {
for k := range h.Examples.Value {
f = append(f, fmt.Sprintf("%s-%x", k.Value, h.Examples.Value[k].Value.Hash()))
if orderedmap.Len(h.Examples.Value) > 0 {
for pair := orderedmap.First(h.Examples.Value); pair != nil; pair = pair.Next() {
f = append(f, fmt.Sprintf("%s-%x", pair.Key().Value, pair.Value().Value.Hash()))
}
}
if len(h.Content.Value) > 0 {
for k := range h.Content.Value {
f = append(f, fmt.Sprintf("%s-%x", k.Value, h.Content.Value[k].Value.Hash()))
if orderedmap.Len(h.Content.Value) > 0 {
for pair := orderedmap.First(h.Content.Value); pair != nil; pair = pair.Next() {
f = append(f, fmt.Sprintf("%s-%x", pair.Key().Value, pair.Value().Value.Hash()))
}
}
keys := make([]string, len(h.Extensions))
@@ -113,7 +115,7 @@ func (h *Header) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return eErr
}
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,
KeyNode: expsL,
ValueNode: expsN,
@@ -134,7 +136,7 @@ func (h *Header) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if cErr != nil {
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,
KeyNode: cL,
ValueNode: cN,

View File

@@ -6,12 +6,14 @@ package v3
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
@@ -29,7 +31,7 @@ import (
type Link struct {
OperationRef 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]
Description low.NodeReference[string]
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.
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
@@ -87,10 +89,10 @@ func (l *Link) Hash() [32]byte {
}
// todo: needs ordering.
keys := make([]string, len(l.Parameters.Value))
keys := make([]string, orderedmap.Len(l.Parameters.Value))
z := 0
for k := range l.Parameters.Value {
keys[z] = l.Parameters.Value[k].Value
for pair := orderedmap.First(l.Parameters.Value); pair != nil; pair = pair.Next() {
keys[z] = pair.Value().Value
z++
}
sort.Strings(keys)

View File

@@ -6,13 +6,15 @@ package v3
import (
"crypto/sha256"
"fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"strings"
)
// MediaType represents a low-level OpenAPI MediaType object.
@@ -22,8 +24,8 @@ import (
type MediaType struct {
Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]
Encoding low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]
Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
Encoding low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Encoding]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
*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.
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.
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.
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
}
@@ -97,7 +99,7 @@ func (mt *MediaType) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return eErr
}
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,
KeyNode: expsL,
ValueNode: expsN,
@@ -110,7 +112,7 @@ func (mt *MediaType) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return encErr
}
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,
KeyNode: encsL,
ValueNode: encsN,
@@ -128,18 +130,18 @@ func (mt *MediaType) Hash() [32]byte {
if mt.Example.Value != nil {
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
for k := range mt.Examples.Value {
keys[z] = low.GenerateHashString(mt.Examples.Value[k].Value)
for pair := orderedmap.First(mt.Examples.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(pair.Value().Value)
z++
}
sort.Strings(keys)
f = append(f, keys...)
keys = make([]string, len(mt.Encoding.Value))
keys = make([]string, orderedmap.Len(mt.Encoding.Value))
z = 0
for k := range mt.Encoding.Value {
keys[z] = low.GenerateHashString(mt.Encoding.Value[k].Value)
for pair := orderedmap.First(mt.Encoding.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(pair.Value().Value)
}
sort.Strings(keys)
f = append(f, keys...)

View File

@@ -6,12 +6,14 @@ package v3
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
@@ -95,7 +97,7 @@ type OAuthFlow struct {
AuthorizationUrl low.NodeReference[string]
TokenUrl 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]
*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.
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
@@ -134,10 +136,10 @@ func (o *OAuthFlow) Hash() [32]byte {
if !o.RefreshUrl.IsEmpty() {
f = append(f, o.RefreshUrl.Value)
}
keys := make([]string, len(o.Scopes.Value))
keys := make([]string, orderedmap.Len(o.Scopes.Value))
z := 0
for k := range o.Scopes.Value {
keys[z] = fmt.Sprintf("%s-%s", k.Value, sha256.Sum256([]byte(fmt.Sprint(o.Scopes.Value[k].Value))))
for pair := orderedmap.First(o.Scopes.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, sha256.Sum256([]byte(fmt.Sprint(pair.Value().Value))))
z++
}
sort.Strings(keys)

View File

@@ -6,13 +6,15 @@ package v3
import (
"crypto/sha256"
"fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"strings"
)
// 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]]
RequestBody low.NodeReference[*RequestBody]
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]
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
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.
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.
func (o *Operation) FindSecurityRequirement(name string) []low.ValueReference[string] {
for k := range o.Security.Value {
for i := range o.Security.Value[k].Value.Requirements.Value {
if i.Value == name {
return o.Security.Value[k].Value.Requirements.Value[i].Value
requirements := o.Security.Value[k].Value.Requirements
for pair := orderedmap.First(requirements.Value); pair != nil; pair = pair.Next() {
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
}
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,
KeyNode: cbL,
ValueNode: cbN,
@@ -202,10 +205,10 @@ func (o *Operation) Hash() [32]byte {
sort.Strings(keys)
f = append(f, keys...)
keys = make([]string, len(o.Callbacks.Value))
keys = make([]string, orderedmap.Len(o.Callbacks.Value))
z := 0
for k := range o.Callbacks.Value {
keys[z] = low.GenerateHashString(o.Callbacks.Value[k].Value)
for pair := orderedmap.First(o.Callbacks.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(pair.Value().Value)
z++
}
sort.Strings(keys)
@@ -278,6 +281,6 @@ func (o *Operation) GetServers() low.NodeReference[any] {
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
}

View File

@@ -6,13 +6,15 @@ package v3
import (
"crypto/sha256"
"fmt"
"sort"
"strings"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"strings"
)
// 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]
Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any]
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.Example]]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
Examples low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*base.Example]]]
Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
*low.Reference
}
// FindContent will attempt to locate a MediaType instance using the specified name.
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.
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.
@@ -85,7 +87,7 @@ func (p *Parameter) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return eErr
}
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,
KeyNode: expsL,
ValueNode: expsN,
@@ -97,7 +99,7 @@ func (p *Parameter) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if cErr != nil {
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,
KeyNode: cL,
ValueNode: cN,
@@ -133,18 +135,18 @@ func (p *Parameter) Hash() [32]byte {
}
var keys []string
keys = make([]string, len(p.Examples.Value))
keys = make([]string, orderedmap.Len(p.Examples.Value))
z := 0
for k := range p.Examples.Value {
keys[z] = low.GenerateHashString(p.Examples.Value[k].Value)
for pair := orderedmap.First(p.Examples.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(pair.Value().Value)
z++
}
sort.Strings(keys)
f = append(f, keys...)
keys = make([]string, len(p.Content.Value))
keys = make([]string, orderedmap.Len(p.Content.Value))
z = 0
for k := range p.Content.Value {
keys[z] = low.GenerateHashString(p.Content.Value[k].Value)
for pair := orderedmap.First(p.Content.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(pair.Value().Value)
z++
}
sort.Strings(keys)

View File

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

View File

@@ -6,19 +6,21 @@ package v3
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
type RequestBody struct {
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]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
*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.
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.
@@ -52,7 +54,7 @@ func (rb *RequestBody) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return cErr
}
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,
KeyNode: cL,
ValueNode: cN,
@@ -70,8 +72,8 @@ func (rb *RequestBody) Hash() [32]byte {
if !rb.Required.IsEmpty() {
f = append(f, fmt.Sprint(rb.Required.Value))
}
for k := range rb.Content.Value {
f = append(f, low.GenerateHashString(rb.Content.Value[k].Value))
for pair := orderedmap.First(rb.Content.Value); pair != nil; pair = pair.Next() {
f = append(f, low.GenerateHashString(pair.Value().Value))
}
keys := make([]string, len(rb.Extensions))

View File

@@ -6,12 +6,14 @@ package v3
import (
"crypto/sha256"
"fmt"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
@@ -21,10 +23,10 @@ import (
// - https://spec.openapis.org/oas/v3.1.0#response-object
type Response struct {
Description low.NodeReference[string]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
Headers low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*Header]]]
Content low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*MediaType]]]
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
}
@@ -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.
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.
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.
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.
@@ -66,7 +68,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return err
}
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,
KeyNode: lN,
ValueNode: kN,
@@ -78,7 +80,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return cErr
}
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,
KeyNode: clN,
ValueNode: cN,
@@ -91,7 +93,7 @@ func (r *Response) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
return lErr
}
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,
KeyNode: linkLabel,
ValueNode: linkValue,
@@ -106,26 +108,26 @@ func (r *Response) Hash() [32]byte {
if 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
for k := range r.Headers.Value {
keys[z] = fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(r.Headers.Value[k].Value))
for pair := orderedmap.First(r.Headers.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value))
z++
}
sort.Strings(keys)
f = append(f, keys...)
keys = make([]string, len(r.Content.Value))
keys = make([]string, orderedmap.Len(r.Content.Value))
z = 0
for k := range r.Content.Value {
keys[z] = fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(r.Content.Value[k].Value))
for pair := orderedmap.First(r.Content.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value))
z++
}
sort.Strings(keys)
f = append(f, keys...)
keys = make([]string, len(r.Links.Value))
keys = make([]string, orderedmap.Len(r.Links.Value))
z = 0
for k := range r.Links.Value {
keys[z] = fmt.Sprintf("%s-%s", k.Value, low.GenerateHashString(r.Links.Value[k].Value))
for pair := orderedmap.First(r.Links.Value); pair != nil; pair = pair.Next() {
keys[z] = fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value))
z++
}
sort.Strings(keys)

View File

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

View File

@@ -5,12 +5,14 @@ package v3
import (
"crypto/sha256"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"sort"
"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.
@@ -18,7 +20,7 @@ import (
type Server struct {
URL 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]
*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.
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.
@@ -43,7 +45,7 @@ func (s *Server) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
if vars == 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) {
var currentNode string
var keyNode *yaml.Node
@@ -56,15 +58,18 @@ func (s *Server) Build(_, root *yaml.Node, idx *index.SpecIndex) error {
variable := ServerVariable{}
variable.Reference = new(low.Reference)
_ = low.BuildModel(varNode, &variable)
variablesMap[low.KeyReference[string]{
variablesMap.Set(
low.KeyReference[string]{
Value: currentNode,
KeyNode: keyNode,
}] = low.ValueReference[*ServerVariable]{
},
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,
ValueNode: vars,
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
func (s *Server) Hash() [32]byte {
var f []string
keys := make([]string, len(s.Variables.Value))
keys := make([]string, orderedmap.Len(s.Variables.Value))
z := 0
for k := range s.Variables.Value {
keys[z] = low.GenerateHashString(s.Variables.Value[k].Value)
for pair := orderedmap.First(s.Variables.Value); pair != nil; pair = pair.Next() {
keys[z] = low.GenerateHashString(pair.Value().Value)
z++
}
sort.Strings(keys)

View File

@@ -50,7 +50,7 @@ func ExampleNewDocument_fromOpenAPI3Document() {
// get a count of the number of paths and schemas.
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
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.
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
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 {
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 {
@@ -197,7 +197,7 @@ func ExampleNewDocument_fromUnknownVersion() {
}
if len(errors) <= 0 {
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
schemaOne := docModel.Model.Components.Schemas["SchemaOne"].Schema()
parameterOne := docModel.Model.Components.Parameters["ParameterOne"]
schemaOne := docModel.Model.Components.Schemas.GetOrZero("SchemaOne").Schema()
parameterOne := docModel.Model.Components.Parameters.GetOrZero("ParameterOne")
// unpack schemaOne extensions into complex `cakes` type
schemaOneExtensions, schemaUnpackErrors := high.UnpackExtensions[cakes, *low.Schema](schemaOne)

View File

@@ -8,11 +8,6 @@ import (
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/pb33f/libopenapi/what-changed/model"
"github.com/stretchr/testify/assert"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/high/base"
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
@@ -254,17 +249,18 @@ func TestDocument_RenderAndReload(t *testing.T) {
// mutate the model
h := m.Model
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 =
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,
&base.SecurityRequirement{Requirements: map[string][]string{
&base.SecurityRequirement{Requirements: orderedmap.ToOrderedMap(map[string][]string{
"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.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example = "I am a teapot, filled with love."
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
bytes, _, newDocModel, e := doc.RenderAndReload()
assert.Nil(t, e)
@@ -273,17 +269,17 @@ func TestDocument_RenderAndReload(t *testing.T) {
h = newDocModel.Model
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId)
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)
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.",
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",
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl)
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl)
}
func TestDocument_Render(t *testing.T) {
@@ -304,18 +300,20 @@ func TestDocument_Render(t *testing.T) {
// mutate the model
h := m.Model
h.Paths.PathItems["/pet/findByStatus"].Get.OperationId = "findACakeInABakery"
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description = "a nice bucket of mice"
h.Paths.PathItems["/pet/findByTags"].Get.Tags =
append(h.Paths.PathItems["/pet/findByTags"].Get.Tags, "gurgle", "giggle")
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId = "findACakeInABakery"
h.Paths.PathItems.GetOrZero("/pet/findByStatus").
Get.Responses.Codes.GetOrZero("400").Description = "a nice bucket of mice"
h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags =
append(h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, "gurgle", "giggle")
h.Paths.PathItems["/pet/{petId}"].Delete.Security = append(h.Paths.PathItems["/pet/{petId}"].Delete.Security,
&base.SecurityRequirement{Requirements: map[string][]string{
h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security = append(h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security,
&base.SecurityRequirement{Requirements: orderedmap.ToOrderedMap(map[string][]string{
"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.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example = "I am a teapot, filled with love."
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
bytes, e := doc.Render()
assert.NoError(t, e)
@@ -329,19 +327,19 @@ func TestDocument_Render(t *testing.T) {
assert.Len(t, docErrs, 0)
h = newDocModel.Model
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems["/pet/findByStatus"].Get.OperationId)
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId)
assert.Equal(t, "a nice bucket of mice",
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description)
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3)
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes.GetOrZero("400").Description)
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3)
yu := h.Paths.PathItems["/pet/{petId}"].Delete.Security
assert.Equal(t, "read:abook", yu[len(yu)-1].Requirements["pizza-and-cake"][0])
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
yu := h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security
assert.Equal(t, "read:abook", yu[len(yu)-1].Requirements.GetOrZero("pizza-and-cake")[0])
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",
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl)
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl)
}
func TestDocument_RenderWithLargeIndention(t *testing.T) {
@@ -354,7 +352,6 @@ func TestDocument_RenderWithLargeIndention(t *testing.T) {
doc.BuildV3Model()
bytes, _ := doc.Render()
assert.Equal(t, json, string(bytes))
}
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.
schemas := v3Model.Model.Components.Schemas
assert.Equal(t, 4, len(schemas))
assert.Equal(t, 4, orderedmap.Len(schemas))
fp := schemas["FP"]
fbsref := schemas["FBSRef"]
fp := schemas.GetOrZero("FP")
fbsref := schemas.GetOrZero("FBSRef")
assert.Equal(t, fp.Schema().Pattern, fbsref.Schema().Pattern)
assert.Equal(t, fp.Schema().Example, fbsref.Schema().Example)
byte := schemas["Byte"]
uint64 := schemas["UInt64"]
byte := schemas.GetOrZero("Byte")
uint64 := schemas.GetOrZero("UInt64")
assert.Equal(t, uint64.Schema().Format, byte.Schema().Format)
assert.Equal(t, uint64.Schema().Type, byte.Schema().Type)
@@ -674,7 +671,8 @@ components:
example:
"4578152156": Not Started
"5678904321": On Hold
type: object`
type: object
`
doc, err := NewDocument([]byte(d))
if err != nil {
@@ -689,7 +687,7 @@ components:
// render the document.
rend, _ := result.Model.Render()
assert.Len(t, rend, 644)
assert.Equal(t, d, string(rend))
}
func TestDocument_OperationsAsRefs(t *testing.T) {
@@ -897,12 +895,11 @@ func TestDocument_Render_PreserveOrder(t *testing.T) {
require.Equal(t, pathCount, orderedmap.Len(pathItems))
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)
assert.Equal(t, pathName, pair.Key())
i++
return nil
})
}
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] {
next := p.Pair.Next()
if next == nil {
@@ -140,23 +145,13 @@ func ToOrderedMap[K comparable, V any](m map[K]V) Map[K, V] {
return om
}
// For iterates a `Map` and calls action() on each map pair.
// action() may return `io.EOF` to break iteration.
// First returns map's first pair for iteration.
// Safely handles nil pointer.
func For[K comparable, V any](m Map[K, V], action ActionFunc[K, V]) error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
c := Iterate(ctx, m)
for pair := range c {
err := action(pair)
if err == io.EOF {
func First[K comparable, V any](m Map[K, V]) Pair[K, V] {
if m == nil {
return nil
}
if err != nil {
return err
}
}
return nil
return m.First()
}
// 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) {
const mapSize = 1000

View File

@@ -5,8 +5,9 @@ package renderer
import (
"fmt"
"github.com/pb33f/libopenapi"
"os"
"github.com/pb33f/libopenapi"
)
func ExampleMockGenerator_generateBurgerMock_yaml() {
@@ -21,7 +22,7 @@ func ExampleMockGenerator_generateBurgerMock_yaml() {
v3Model, _ := document.BuildV3Model()
// create a mock of the Burger model
burgerModel := v3Model.Model.Components.Schemas["Burger"]
burgerModel := v3Model.Model.Components.Schemas.GetOrZero("Burger")
burger := burgerModel.Schema()
mock, err := mg.GenerateMock(burger, "")
@@ -45,7 +46,7 @@ func ExampleMockGenerator_generateFriesMock_json() {
v3Model, _ := document.BuildV3Model()
// create a mock of the Fries model
friesModel := v3Model.Model.Components.Schemas["Fries"]
friesModel := v3Model.Model.Components.Schemas.GetOrZero("Fries")
fries := friesModel.Schema()
mock, err := mg.GenerateMock(fries, "")
@@ -68,7 +69,8 @@ func ExampleMockGenerator_generateRequestMock_json() {
v3Model, _ := document.BuildV3Model()
// 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
mock, err := mg.GenerateMock(burgerRequestModel, "cakeBurger")
@@ -92,7 +94,8 @@ func ExampleMockGenerator_generateResponseMock_json() {
v3Model, _ := document.BuildV3Model()
// 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
mock, err := mg.GenerateMock(burgerResponseModel, "filetOFish")
@@ -116,7 +119,7 @@ func ExampleMockGenerator_generatePolymorphicMock_json() {
v3Model, _ := document.BuildV3Model()
// 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()
mock, err := mg.GenerateMock(payload, "")

View File

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

View File

@@ -8,17 +8,18 @@ import (
"encoding/json"
"errors"
"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"
"os"
"regexp"
"strings"
"testing"
"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) {
@@ -1144,7 +1145,7 @@ properties:
loopMe = func(parent *highbase.SchemaProxy, level int) {
schemaProxy := buildSchema()
if parent != nil {
parent.Schema().Properties["child"] = schemaProxy
parent.Schema().Properties.Set("child", schemaProxy)
}
if level < 110 {
loopMe(schemaProxy, level+1)

View File

@@ -5,7 +5,8 @@ package model
import (
"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.
@@ -65,14 +66,14 @@ func CompareCallback(l, r *v3.Callback) *CallbackChanges {
lValues := make(map[string]low.ValueReference[*v3.PathItem])
rValues := make(map[string]low.ValueReference[*v3.PathItem])
for k := range l.Expression.Value {
lHashes[k.Value] = low.GenerateHashString(l.Expression.Value[k].Value)
lValues[k.Value] = l.Expression.Value[k]
for pair := orderedmap.First(l.Expression.Value); pair != nil; pair = pair.Next() {
lHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
lValues[pair.Key().Value] = pair.Value()
}
for k := range r.Expression.Value {
rHashes[k.Value] = low.GenerateHashString(r.Expression.Value[k].Value)
rValues[k.Value] = r.Expression.Value[k]
for pair := orderedmap.First(r.Expression.Value); pair != nil; pair = pair.Next() {
rHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
rValues[pair.Key().Value] = pair.Value()
}
expChanges := make(map[string]*PathItemChanges)

View File

@@ -5,11 +5,13 @@ package model
import (
"fmt"
"github.com/pb33f/libopenapi/utils"
"reflect"
"strings"
"sync"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"github.com/pb33f/libopenapi/datamodel/low"
"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
// 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 {
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 {
// do nothing
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
// 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.
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 {
// 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])
rValues := make(map[string]low.ValueReference[T])
for k := range expLeft {
lHashes[k.Value] = low.GenerateHashString(expLeft[k].Value)
lValues[k.Value] = expLeft[k]
for pair := orderedmap.First(expLeft); pair != nil; pair = pair.Next() {
k := pair.Key()
lHashes[k.Value] = low.GenerateHashString(pair.Value().Value)
lValues[k.Value] = pair.Value()
}
for k := range expRight {
rHashes[k.Value] = low.GenerateHashString(expRight[k].Value)
rValues[k.Value] = expRight[k]
for pair := orderedmap.First(expRight); pair != nil; pair = pair.Next() {
k := pair.Key()
rHashes[k.Value] = low.GenerateHashString(pair.Value().Value)
rValues[k.Value] = pair.Value()
}
expChanges := make(map[string]R)

View File

@@ -10,6 +10,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low/base"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
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
@@ -55,7 +56,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.ParameterDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*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 {
a = lDef.Definitions
}
@@ -70,7 +71,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.ResponsesDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*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 {
a = lDef.Definitions
}
@@ -85,7 +86,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.Definitions{}) == reflect.TypeOf(r) {
lDef := l.(*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 {
a = lDef.Schemas
}
@@ -100,7 +101,7 @@ func CompareComponents(l, r any) *ComponentsChanges {
reflect.TypeOf(&v2.SecurityDefinitions{}) == reflect.TypeOf(r) {
lDef := l.(*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 {
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.
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) {
// for schemas

View File

@@ -6,6 +6,7 @@ package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/orderedmap"
)
// 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])
rValues := make(map[string]low.ValueReference[any])
for k := range l.Values {
lHashes[k.Value] = low.GenerateHashString(l.Values[k].Value)
lValues[k.Value] = l.Values[k]
for pair := orderedmap.First(l.Values); pair != nil; pair = pair.Next() {
lHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
lValues[pair.Key().Value] = pair.Value()
}
for k := range r.Values {
rHashes[k.Value] = low.GenerateHashString(r.Values[k].Value)
rValues[k.Value] = r.Values[k]
for pair := orderedmap.First(r.Values); pair != nil; pair = pair.Next() {
rHashes[pair.Key().Value] = low.GenerateHashString(pair.Value().Value)
rValues[pair.Key().Value] = pair.Value()
}
var changes []*Change

View File

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

View File

@@ -5,7 +5,8 @@ package model
import (
"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.
@@ -228,26 +229,26 @@ func CompareOAuthFlow(l, r *v3.OAuthFlow) *OAuthFlowChanges {
CheckProperties(props)
for v := range l.Scopes.Value {
if r != nil && r.FindScope(v.Value) == nil {
for pair := orderedmap.First(l.Scopes.Value); pair != nil; pair = pair.Next() {
if r != nil && r.FindScope(pair.Key().Value) == nil {
CreateChange(&changes, ObjectRemoved, v3.Scopes,
l.Scopes.Value[v].ValueNode, nil, true,
v.Value, nil)
pair.Value().ValueNode, nil, true,
pair.Key().Value, nil)
continue
}
if r != nil && r.FindScope(v.Value) != nil {
if l.Scopes.Value[v].Value != r.FindScope(v.Value).Value {
if r != nil && r.FindScope(pair.Key().Value) != nil {
if pair.Value().Value != r.FindScope(pair.Key().Value).Value {
CreateChange(&changes, Modified, v3.Scopes,
l.Scopes.Value[v].ValueNode, r.FindScope(v.Value).ValueNode, true,
l.Scopes.Value[v].Value, r.FindScope(v.Value).Value)
pair.Value().ValueNode, r.FindScope(pair.Key().Value).ValueNode, true,
pair.Value().Value, r.FindScope(pair.Key().Value).Value)
}
}
}
for v := range r.Scopes.Value {
if l != nil && l.FindScope(v.Value) == nil {
for pair := orderedmap.First(r.Scopes.Value); pair != nil; pair = pair.Next() {
if l != nil && l.FindScope(pair.Key().Value) == nil {
CreateChange(&changes, ObjectAdded, v3.Scopes,
nil, r.Scopes.Value[v].ValueNode, false,
nil, v.Value)
nil, pair.Value().ValueNode, false,
nil, pair.Key().Value)
}
}
oa := new(OAuthFlowChanges)

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,8 @@ package model
import (
"github.com/pb33f/libopenapi/datamodel/low"
"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"
)
@@ -56,22 +57,22 @@ func addedSecurityRequirement(vn *yaml.Node, name string, changes *[]*Change) {
}
// 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) {
lKeys := make([]string, len(lSec))
rKeys := make([]string, len(rSec))
lKeys := make([]string, orderedmap.Len(lSec))
rKeys := make([]string, orderedmap.Len(rSec))
lValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
rValues := make(map[string]low.ValueReference[[]low.ValueReference[string]])
var n, z int
for i := range lSec {
lKeys[n] = i.Value
lValues[i.Value] = lSec[i]
for pair := orderedmap.First(lSec); pair != nil; pair = pair.Next() {
lKeys[n] = pair.Key().Value
lValues[pair.Key().Value] = pair.Value()
n++
}
for i := range rSec {
rKeys[z] = i.Value
rValues[i.Value] = rSec[i]
for pair := orderedmap.First(rSec); pair != nil; pair = pair.Next() {
rKeys[z] = pair.Key().Value
rValues[pair.Key().Value] = pair.Value()
z++
}