mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
fix: continued moving everything to orderedmaps plus cleaned up most the tests
This commit is contained in:
@@ -20,8 +20,8 @@ import (
|
||||
//
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||
type Discriminator struct {
|
||||
PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"`
|
||||
Mapping orderedmap.Map[string, string] `json:"mapping,omitempty" yaml:"mapping,omitempty"`
|
||||
PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"`
|
||||
Mapping *orderedmap.Map[string, string] `json:"mapping,omitempty" yaml:"mapping,omitempty"`
|
||||
low *low.Discriminator
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ func TestDynamicValue_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
// convert node into yaml
|
||||
bits, _ := yaml.Marshal(rend)
|
||||
assert.Equal(t, "properties:\n rice:\n $ref: '#/components/schemas/rice'", strings.TrimSpace(string(bits)))
|
||||
assert.Equal(t, "properties:\n rice:\n type: array\n items:\n type: string", strings.TrimSpace(string(bits)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_MarshalYAMLInline_Error(t *testing.T) {
|
||||
|
||||
@@ -15,11 +15,11 @@ import (
|
||||
//
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
||||
type Example struct {
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Value any `json:"value,omitempty" yaml:"value,omitempty"`
|
||||
ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Value *yaml.Node `json:"value,omitempty" yaml:"value,omitempty"`
|
||||
ExternalValue string `json:"externalValue,omitempty" yaml:"externalValue,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Example
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ 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 orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Example]]) orderedmap.Map[string, *Example] {
|
||||
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))
|
||||
|
||||
@@ -17,7 +17,6 @@ import (
|
||||
)
|
||||
|
||||
func TestNewExample(t *testing.T) {
|
||||
|
||||
var cNode yaml.Node
|
||||
|
||||
yml := `summary: an example
|
||||
@@ -37,18 +36,23 @@ x-hack: code`
|
||||
// build high
|
||||
highExample := NewExample(&lowExample)
|
||||
|
||||
var xHack string
|
||||
_ = highExample.Extensions.GetOrZero("x-hack").Decode(&xHack)
|
||||
|
||||
var example string
|
||||
_ = highExample.Value.Decode(&example)
|
||||
|
||||
assert.Equal(t, "an example", highExample.Summary)
|
||||
assert.Equal(t, "something more", highExample.Description)
|
||||
assert.Equal(t, "https://pb33f.io", highExample.ExternalValue)
|
||||
assert.Equal(t, "code", highExample.Extensions["x-hack"])
|
||||
assert.Equal(t, "a thing", highExample.Value)
|
||||
assert.Equal(t, "code", xHack)
|
||||
assert.Equal(t, "a thing", example)
|
||||
assert.Equal(t, 4, highExample.GoLow().ExternalValue.ValueNode.Line)
|
||||
assert.NotNil(t, highExample.GoLowUntyped())
|
||||
|
||||
// render the example as YAML
|
||||
rendered, _ := highExample.Render()
|
||||
assert.Equal(t, strings.TrimSpace(string(rendered)), yml)
|
||||
|
||||
assert.Equal(t, yml, strings.TrimSpace(string(rendered)))
|
||||
}
|
||||
|
||||
func TestExtractExamples(t *testing.T) {
|
||||
@@ -71,11 +75,9 @@ func TestExtractExamples(t *testing.T) {
|
||||
)
|
||||
|
||||
assert.Equal(t, "herbs", ExtractExamples(examplesMap).GetOrZero("green").Summary)
|
||||
|
||||
}
|
||||
|
||||
func ExampleNewExample() {
|
||||
|
||||
// create some example yaml (or can be JSON, it does not matter)
|
||||
yml := `summary: something interesting
|
||||
description: something more interesting with detail
|
||||
@@ -98,5 +100,4 @@ x-hack: code`
|
||||
|
||||
fmt.Print(highExample.ExternalValue)
|
||||
// Output: https://pb33f.io
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package base
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -16,9 +17,9 @@ import (
|
||||
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
||||
type ExternalDoc struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.ExternalDoc
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ func (e *ExternalDoc) GoLowUntyped() any {
|
||||
return e.low
|
||||
}
|
||||
|
||||
func (e *ExternalDoc) GetExtensions() map[string]any {
|
||||
func (e *ExternalDoc) GetExtensions() *orderedmap.Map[string, *yaml.Node] {
|
||||
return e.Extensions
|
||||
}
|
||||
|
||||
|
||||
@@ -5,17 +5,17 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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 TestNewExternalDoc(t *testing.T) {
|
||||
|
||||
var cNode yaml.Node
|
||||
|
||||
yml := `description: hack code
|
||||
@@ -31,22 +31,23 @@ x-hack: code`
|
||||
|
||||
highExt := NewExternalDoc(&lowExt)
|
||||
|
||||
var xHack string
|
||||
_ = highExt.Extensions.GetOrZero("x-hack").Decode(&xHack)
|
||||
|
||||
assert.Equal(t, "hack code", highExt.Description)
|
||||
assert.Equal(t, "https://pb33f.io", highExt.URL)
|
||||
assert.Equal(t, "code", highExt.Extensions["x-hack"])
|
||||
assert.Equal(t, "code", xHack)
|
||||
|
||||
wentLow := highExt.GoLow()
|
||||
assert.Equal(t, 2, wentLow.URL.ValueNode.Line)
|
||||
assert.Len(t, highExt.GetExtensions(), 1)
|
||||
assert.Equal(t, 1, orderedmap.Len(highExt.GetExtensions()))
|
||||
|
||||
// render the high-level object as YAML
|
||||
rendered, _ := highExt.Render()
|
||||
assert.Equal(t, strings.TrimSpace(string(rendered)), yml)
|
||||
|
||||
}
|
||||
|
||||
func ExampleNewExternalDoc() {
|
||||
|
||||
func TestExampleNewExternalDoc(t *testing.T) {
|
||||
// create a new external documentation spec reference
|
||||
// this can be YAML or JSON.
|
||||
yml := `description: hack code docs
|
||||
@@ -67,7 +68,8 @@ x-hack: code`
|
||||
// create new high-level ExternalDoc
|
||||
highExt := NewExternalDoc(&lowExt)
|
||||
|
||||
// print out a extension
|
||||
fmt.Print(highExt.Extensions["x-hack"])
|
||||
// Output: code
|
||||
var xHack string
|
||||
_ = highExt.Extensions.GetOrZero("x-hack").Decode(&xHack)
|
||||
|
||||
assert.Equal(t, "code", xHack)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package base
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -17,14 +18,14 @@ import (
|
||||
// v2 - https://swagger.io/specification/v2/#infoObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#info-object
|
||||
type Info struct {
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
|
||||
Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"`
|
||||
License *License `json:"license,omitempty" yaml:"license,omitempty"`
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
Extensions map[string]any
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
|
||||
Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"`
|
||||
License *License `json:"license,omitempty" yaml:"license,omitempty"`
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Info
|
||||
}
|
||||
|
||||
@@ -53,7 +54,7 @@ func NewInfo(info *low.Info) *Info {
|
||||
if !info.Version.IsEmpty() {
|
||||
i.Version = info.Version.Value
|
||||
}
|
||||
if len(info.Extensions) > 0 {
|
||||
if orderedmap.Len(info.Extensions) > 0 {
|
||||
i.Extensions = high.ExtractExtensions(info.Extensions)
|
||||
}
|
||||
return i
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -37,6 +39,9 @@ x-cli-name: chicken cli`
|
||||
|
||||
highInfo := NewInfo(&lowInfo)
|
||||
|
||||
var xCliName string
|
||||
_ = highInfo.Extensions.GetOrZero("x-cli-name").Decode(&xCliName)
|
||||
|
||||
assert.Equal(t, "chicken", highInfo.Title)
|
||||
assert.Equal(t, "a chicken nugget", highInfo.Summary)
|
||||
assert.Equal(t, "nugget", highInfo.Description)
|
||||
@@ -45,7 +50,7 @@ x-cli-name: chicken cli`
|
||||
assert.Equal(t, "pb33f", highInfo.License.Name)
|
||||
assert.Equal(t, "https://pb33f.io", highInfo.License.URL)
|
||||
assert.Equal(t, "99.99", highInfo.Version)
|
||||
assert.Equal(t, "chicken cli", highInfo.Extensions["x-cli-name"])
|
||||
assert.Equal(t, "chicken cli", xCliName)
|
||||
|
||||
wentLow := highInfo.GoLow()
|
||||
assert.Equal(t, 10, wentLow.Version.ValueNode.Line)
|
||||
@@ -109,13 +114,12 @@ url: https://opensource.org/licenses/MIT`
|
||||
}
|
||||
|
||||
func TestInfo_Render(t *testing.T) {
|
||||
|
||||
ext := make(map[string]any)
|
||||
ext["x-pizza"] = "pepperoni"
|
||||
ext["x-cake"] = &License{
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-pizza", utils.CreateStringNode("pepperoni"))
|
||||
ext.Set("x-cake", utils.CreateYamlNode(&License{
|
||||
Name: "someone",
|
||||
URL: "nowhere",
|
||||
}
|
||||
}))
|
||||
highI := &Info{
|
||||
Title: "hey",
|
||||
Description: "there you",
|
||||
@@ -146,6 +150,9 @@ func TestInfo_Render(t *testing.T) {
|
||||
// build high
|
||||
highInfo := NewInfo(&lowInfo)
|
||||
|
||||
var xPizza string
|
||||
_ = highInfo.Extensions.GetOrZero("x-pizza").Decode(&xPizza)
|
||||
|
||||
assert.Equal(t, "hey", highInfo.Title)
|
||||
assert.Equal(t, "there you", highInfo.Description)
|
||||
assert.Equal(t, "have you got any money", highInfo.TermsOfService)
|
||||
@@ -154,12 +161,11 @@ func TestInfo_Render(t *testing.T) {
|
||||
assert.Equal(t, "MIT", highInfo.License.Name)
|
||||
assert.Equal(t, "https://opensource.org/licenses/MIT", highInfo.License.URL)
|
||||
assert.Equal(t, "1.2.3", highInfo.Version)
|
||||
assert.Equal(t, "pepperoni", highInfo.Extensions["x-pizza"])
|
||||
assert.Equal(t, "pepperoni", xPizza)
|
||||
assert.NotNil(t, highInfo.GoLowUntyped())
|
||||
}
|
||||
|
||||
func TestInfo_RenderOrder(t *testing.T) {
|
||||
|
||||
yml := `title: hey
|
||||
description: there you
|
||||
termsOfService: have you got any money
|
||||
@@ -187,6 +193,9 @@ x-cake:
|
||||
// build high
|
||||
highInfo := NewInfo(&lowInfo)
|
||||
|
||||
var xPizza string
|
||||
_ = highInfo.Extensions.GetOrZero("x-pizza").Decode(&xPizza)
|
||||
|
||||
assert.Equal(t, "hey", highInfo.Title)
|
||||
assert.Equal(t, "there you", highInfo.Description)
|
||||
assert.Equal(t, "have you got any money", highInfo.TermsOfService)
|
||||
@@ -195,7 +204,7 @@ x-cake:
|
||||
assert.Equal(t, "MIT", highInfo.License.Name)
|
||||
assert.Equal(t, "https://opensource.org/licenses/MIT", highInfo.License.URL)
|
||||
assert.Equal(t, "1.2.3", highInfo.Version)
|
||||
assert.Equal(t, "pepperoni", highInfo.Extensions["x-pizza"])
|
||||
assert.Equal(t, "pepperoni", xPizza)
|
||||
|
||||
// marshal high back to yaml, should be the same as the original, in same order.
|
||||
bytes, _ := highInfo.Render()
|
||||
|
||||
@@ -45,22 +45,22 @@ type Schema struct {
|
||||
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
|
||||
|
||||
// in 3.1 examples can be an array (which is recommended)
|
||||
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
Examples []*yaml.Node `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
|
||||
// in 3.1 prefixItems provides tuple validation support.
|
||||
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
|
||||
|
||||
// 3.1 Specific properties
|
||||
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
|
||||
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
|
||||
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
|
||||
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 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"`
|
||||
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
|
||||
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
|
||||
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
|
||||
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 *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"`
|
||||
|
||||
// in 3.1 UnevaluatedProperties can be a Schema or a boolean
|
||||
// https://github.com/pb33f/libopenapi/issues/118
|
||||
@@ -73,35 +73,35 @@ type Schema struct {
|
||||
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
|
||||
|
||||
// Compatible with all versions
|
||||
Not *SchemaProxy `json:"not,omitempty" yaml:"not,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,renderZero,omitempty" yaml:"maximum,renderZero,omitempty"`
|
||||
Minimum *float64 `json:"minimum,renderZero,omitempty," yaml:"minimum,renderZero,omitempty"`
|
||||
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
|
||||
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
|
||||
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
|
||||
UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
|
||||
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
|
||||
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
|
||||
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||
AdditionalProperties *DynamicValue[*SchemaProxy, bool] `json:"additionalProperties,renderZero,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
|
||||
Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"`
|
||||
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
|
||||
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
|
||||
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
|
||||
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
|
||||
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Not *SchemaProxy `json:"not,omitempty" yaml:"not,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,renderZero,omitempty" yaml:"maximum,renderZero,omitempty"`
|
||||
Minimum *float64 `json:"minimum,renderZero,omitempty," yaml:"minimum,renderZero,omitempty"`
|
||||
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
|
||||
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
|
||||
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
||||
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
|
||||
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
|
||||
UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
|
||||
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
|
||||
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
|
||||
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Enum []*yaml.Node `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||
AdditionalProperties *DynamicValue[*SchemaProxy, bool] `json:"additionalProperties,renderZero,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Default *yaml.Node `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
|
||||
Const *yaml.Node `json:"const,omitempty" yaml:"const,renderZero,omitempty"`
|
||||
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
|
||||
ReadOnly *bool `json:"readOnly,renderZero,omitempty" yaml:"readOnly,renderZero,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
|
||||
WriteOnly *bool `json:"writeOnly,renderZero,omitempty" yaml:"writeOnly,renderZero,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
|
||||
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
|
||||
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
Example *yaml.Node `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *base.Schema
|
||||
|
||||
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
||||
@@ -266,17 +266,17 @@ func NewSchema(schema *base.Schema) *Schema {
|
||||
s.Nullable = &schema.Nullable.Value
|
||||
}
|
||||
if !schema.ReadOnly.IsEmpty() {
|
||||
s.ReadOnly = schema.ReadOnly.Value
|
||||
s.ReadOnly = &schema.ReadOnly.Value
|
||||
}
|
||||
if !schema.WriteOnly.IsEmpty() {
|
||||
s.WriteOnly = schema.WriteOnly.Value
|
||||
s.WriteOnly = &schema.WriteOnly.Value
|
||||
}
|
||||
if !schema.Deprecated.IsEmpty() {
|
||||
s.Deprecated = &schema.Deprecated.Value
|
||||
}
|
||||
s.Example = schema.Example.Value
|
||||
if len(schema.Examples.Value) > 0 {
|
||||
examples := make([]any, len(schema.Examples.Value))
|
||||
examples := make([]*yaml.Node, len(schema.Examples.Value))
|
||||
for i := 0; i < len(schema.Examples.Value); i++ {
|
||||
examples[i] = schema.Examples.Value[i].Value
|
||||
}
|
||||
@@ -298,11 +298,11 @@ func NewSchema(schema *base.Schema) *Schema {
|
||||
}
|
||||
s.Required = req
|
||||
|
||||
var enum []any
|
||||
if !schema.Anchor.IsEmpty() {
|
||||
s.Anchor = schema.Anchor.Value
|
||||
}
|
||||
|
||||
var enum []*yaml.Node
|
||||
for i := range schema.Enum.Value {
|
||||
enum = append(enum, schema.Enum.Value[i].Value)
|
||||
}
|
||||
@@ -322,11 +322,13 @@ func NewSchema(schema *base.Schema) *Schema {
|
||||
|
||||
// for every item, build schema async
|
||||
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
|
||||
p := NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: sch.ValueNode,
|
||||
n := &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: sch.GetValueNode(),
|
||||
Value: sch.Value,
|
||||
Reference: sch.GetReference(),
|
||||
})
|
||||
}
|
||||
n.SetReference(sch.GetReference(), sch.GetValueNode())
|
||||
|
||||
p := NewSchemaProxy(n)
|
||||
|
||||
bChan <- buildResult{idx: idx, s: p}
|
||||
}
|
||||
@@ -353,7 +355,7 @@ func NewSchema(schema *base.Schema) *Schema {
|
||||
|
||||
// props async
|
||||
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy],
|
||||
props orderedmap.Map[string, *SchemaProxy], sw int,
|
||||
props *orderedmap.Map[string, *SchemaProxy], sw int,
|
||||
) {
|
||||
props.Set(k.Value, NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
Value: v.Value,
|
||||
|
||||
@@ -4,13 +4,14 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An
|
||||
@@ -98,11 +99,15 @@ func (sp *SchemaProxy) Schema() *Schema {
|
||||
|
||||
// IsReference returns true if the SchemaProxy is a reference to another Schema.
|
||||
func (sp *SchemaProxy) IsReference() bool {
|
||||
if sp == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if sp.refStr != "" {
|
||||
return true
|
||||
}
|
||||
if sp.schema != nil {
|
||||
return sp.schema.Value.IsSchemaReference()
|
||||
return sp.schema.Value.IsReference()
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -112,7 +117,14 @@ func (sp *SchemaProxy) GetReference() string {
|
||||
if sp.refStr != "" {
|
||||
return sp.refStr
|
||||
}
|
||||
return sp.schema.Value.GetSchemaReference()
|
||||
return sp.schema.GetValue().GetReference()
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) GetReferenceNode() *yaml.Node {
|
||||
if sp.refStr != "" {
|
||||
return nil
|
||||
}
|
||||
return sp.schema.GetValue().GetReferenceNode()
|
||||
}
|
||||
|
||||
// GetReferenceOrigin returns a pointer to the index.NodeOrigin of the $ref if this SchemaProxy is a reference to another Schema.
|
||||
@@ -171,12 +183,13 @@ func (sp *SchemaProxy) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
} else {
|
||||
refNode := sp.GetReferenceNode()
|
||||
if refNode != nil {
|
||||
return refNode, nil
|
||||
}
|
||||
|
||||
// do not build out a reference, just marshal the reference.
|
||||
mp := utils.CreateEmptyMapNode()
|
||||
mp.Content = append(mp.Content,
|
||||
utils.CreateStringNode("$ref"),
|
||||
utils.CreateStringNode(sp.GetReference()))
|
||||
return mp, nil
|
||||
return utils.CreateRefNode(sp.GetReference()), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -306,8 +306,8 @@ $anchor: anchor`
|
||||
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])
|
||||
assert.True(t, compiled.ReadOnly)
|
||||
assert.True(t, compiled.WriteOnly)
|
||||
assert.True(t, *compiled.ReadOnly)
|
||||
assert.True(t, *compiled.WriteOnly)
|
||||
assert.True(t, *compiled.Deprecated)
|
||||
assert.True(t, *compiled.Nullable)
|
||||
assert.Equal(t, "anchor", compiled.Anchor)
|
||||
@@ -548,7 +548,7 @@ func TestSchemaProxy_GoLow(t *testing.T) {
|
||||
|
||||
sp := NewSchemaProxy(&lowRef)
|
||||
assert.Equal(t, lowProxy, sp.GoLow())
|
||||
assert.Equal(t, ref, sp.GoLow().GetSchemaReference())
|
||||
assert.Equal(t, ref, sp.GoLow().GetReference())
|
||||
assert.Equal(t, ref, sp.GoLow().GetReference())
|
||||
|
||||
spNil := NewSchemaProxy(nil)
|
||||
@@ -703,7 +703,14 @@ examples:
|
||||
`
|
||||
highSchema := getHighSchema(t, yml)
|
||||
|
||||
assert.Equal(t, []any{int64(5), int64(10)}, highSchema.Examples)
|
||||
examples := []any{}
|
||||
for _, ex := range highSchema.Examples {
|
||||
var v int64
|
||||
assert.NoError(t, ex.Decode(&v))
|
||||
examples = append(examples, v)
|
||||
}
|
||||
|
||||
assert.Equal(t, []any{int64(5), int64(10)}, examples)
|
||||
}
|
||||
|
||||
func ExampleNewSchema() {
|
||||
@@ -1123,7 +1130,7 @@ components:
|
||||
|
||||
// now render it out, it should be identical.
|
||||
schemaBytes, _ := compiled.RenderInline()
|
||||
assert.Len(t, schemaBytes, 585)
|
||||
assert.Equal(t, "properties:\n bigBank:\n type: object\n properties:\n failure_balance_transaction:\n allOf:\n - type: object\n properties:\n name:\n type: string\n price:\n type: number\n anyOf:\n - description: A balance transaction\n anyOf:\n - maxLength: 5000\n type: string\n - description: A balance transaction\n", string(schemaBytes))
|
||||
}
|
||||
|
||||
func TestUnevaluatedPropertiesBoolean_True(t *testing.T) {
|
||||
|
||||
@@ -21,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 orderedmap.Map[string, []string] `json:"-" yaml:"-"`
|
||||
Requirements *orderedmap.Map[string, []string] `json:"-" yaml:"-"`
|
||||
low *base.SecurityRequirement
|
||||
}
|
||||
|
||||
@@ -59,7 +59,6 @@ func (s *SecurityRequirement) Render() ([]byte, error) {
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the SecurityRequirement object.
|
||||
func (s *SecurityRequirement) MarshalYAML() (interface{}, error) {
|
||||
|
||||
type req struct {
|
||||
line int
|
||||
key string
|
||||
|
||||
@@ -6,6 +6,7 @@ package base
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -19,7 +20,7 @@ type Tag struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Tag
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
)
|
||||
|
||||
func TestNewTag(t *testing.T) {
|
||||
|
||||
var cNode yaml.Node
|
||||
|
||||
yml := `name: chicken
|
||||
@@ -33,10 +32,13 @@ x-hack: code`
|
||||
|
||||
highTag := NewTag(&lowTag)
|
||||
|
||||
var xHack string
|
||||
_ = highTag.Extensions.GetOrZero("x-hack").Decode(&xHack)
|
||||
|
||||
assert.Equal(t, "chicken", highTag.Name)
|
||||
assert.Equal(t, "nuggets", highTag.Description)
|
||||
assert.Equal(t, "https://pb33f.io", highTag.ExternalDocs.URL)
|
||||
assert.Equal(t, "code", highTag.Extensions["x-hack"])
|
||||
assert.Equal(t, "code", xHack)
|
||||
|
||||
wentLow := highTag.GoLow()
|
||||
assert.Equal(t, 5, wentLow.FindExtension("x-hack").ValueNode.Line)
|
||||
@@ -45,11 +47,9 @@ x-hack: code`
|
||||
// render the tag as YAML
|
||||
highTagBytes, _ := highTag.Render()
|
||||
assert.Equal(t, strings.TrimSpace(string(highTagBytes)), yml)
|
||||
|
||||
}
|
||||
|
||||
func TestTag_RenderInline(t *testing.T) {
|
||||
|
||||
tag := &Tag{
|
||||
Name: "cake",
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package base
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -25,7 +26,7 @@ type XML struct {
|
||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||
Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
|
||||
Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.XML
|
||||
}
|
||||
|
||||
|
||||
@@ -11,27 +11,18 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high/nodes"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// NodeEntry represents a single node used by NodeBuilder.
|
||||
type NodeEntry struct {
|
||||
Tag string
|
||||
Key string
|
||||
Value any
|
||||
StringValue string
|
||||
Line int
|
||||
Style yaml.Style
|
||||
RenderZero bool
|
||||
}
|
||||
|
||||
// NodeBuilder is a structure used by libopenapi high-level objects, to render themselves back to YAML.
|
||||
// this allows high-level objects to be 'mutable' because all changes will be rendered out.
|
||||
type NodeBuilder struct {
|
||||
Version float32
|
||||
Nodes []*NodeEntry
|
||||
Nodes []*nodes.NodeEntry
|
||||
High any
|
||||
Low any
|
||||
Resolve bool // If set to true, all references will be rendered inline
|
||||
@@ -62,7 +53,6 @@ func NewNodeBuilder(high any, low any) *NodeBuilder {
|
||||
}
|
||||
|
||||
func (n *NodeBuilder) add(key string, i int) {
|
||||
|
||||
// only operate on exported fields.
|
||||
if unicode.IsLower(rune(key[0])) {
|
||||
return
|
||||
@@ -71,38 +61,38 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
// if the key is 'Extensions' then we need to extract the keys from the map
|
||||
// and add them to the node builder.
|
||||
if key == "Extensions" {
|
||||
extensions := reflect.ValueOf(n.High).Elem().FieldByName(key)
|
||||
for b, e := range extensions.MapKeys() {
|
||||
v := extensions.MapIndex(e)
|
||||
ev := reflect.ValueOf(n.High).Elem().FieldByName(key).Interface()
|
||||
var extensions *orderedmap.Map[string, *yaml.Node]
|
||||
if ev != nil {
|
||||
extensions = ev.(*orderedmap.Map[string, *yaml.Node])
|
||||
}
|
||||
|
||||
extKey := e.String()
|
||||
extValue := v.Interface()
|
||||
nodeEntry := &NodeEntry{Tag: extKey, Key: extKey, Value: extValue, Line: 9999 + b}
|
||||
var lowExtensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() {
|
||||
if j, ok := n.Low.(low.HasExtensionsUntyped); ok {
|
||||
lowExtensions = j.GetExtensions()
|
||||
}
|
||||
}
|
||||
|
||||
if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() {
|
||||
fieldValue := reflect.ValueOf(n.Low).Elem().FieldByName("Extensions")
|
||||
f := fieldValue.Interface()
|
||||
value := reflect.ValueOf(f)
|
||||
switch value.Kind() {
|
||||
case reflect.Map:
|
||||
if j, ok := n.Low.(low.HasExtensionsUntyped); ok {
|
||||
originalExtensions := j.GetExtensions()
|
||||
u := 0
|
||||
for k := range originalExtensions {
|
||||
if k.Value == extKey {
|
||||
if originalExtensions[k].ValueNode.Line != 0 {
|
||||
nodeEntry.Style = originalExtensions[k].ValueNode.Style
|
||||
nodeEntry.Line = originalExtensions[k].ValueNode.Line + u
|
||||
} else {
|
||||
nodeEntry.Line = 999999 + b + u
|
||||
}
|
||||
}
|
||||
u++
|
||||
}
|
||||
}
|
||||
j := 0
|
||||
if lowExtensions != nil {
|
||||
// If we have low extensions get the original lowest line number so we end up in the same place
|
||||
for pair := orderedmap.First(lowExtensions); pair != nil; pair = pair.Next() {
|
||||
if j == 0 || pair.Key().KeyNode.Line < j {
|
||||
j = pair.Key().KeyNode.Line
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for pair := orderedmap.First(extensions); pair != nil; pair = pair.Next() {
|
||||
nodeEntry := &nodes.NodeEntry{Tag: pair.Key(), Key: pair.Key(), Value: pair.Value(), Line: j}
|
||||
|
||||
if lowExtensions != nil {
|
||||
lowItem := low.FindItemInOrderedMap(pair.Key(), lowExtensions)
|
||||
nodeEntry.LowValue = lowItem
|
||||
}
|
||||
n.Nodes = append(n.Nodes, nodeEntry)
|
||||
j++
|
||||
}
|
||||
// done, extensions are handled separately.
|
||||
return
|
||||
@@ -119,11 +109,11 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
|
||||
var renderZeroFlag, omitemptyFlag bool
|
||||
tagParts := strings.Split(tag, ",")
|
||||
for i = 1; i < len(tagParts); i++ {
|
||||
if tagParts[i] == renderZero {
|
||||
for _, part := range tagParts {
|
||||
if part == renderZero {
|
||||
renderZeroFlag = true
|
||||
}
|
||||
if tagParts[i] == "omitempty" {
|
||||
if part == "omitempty" {
|
||||
omitemptyFlag = true
|
||||
}
|
||||
}
|
||||
@@ -133,7 +123,9 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
f := fieldValue.Interface()
|
||||
value := reflect.ValueOf(f)
|
||||
var isZero bool
|
||||
if zeroer, ok := f.(yaml.IsZeroer); ok && zeroer.IsZero() {
|
||||
if (value.Kind() == reflect.Interface || value.Kind() == reflect.Ptr) && value.IsNil() {
|
||||
isZero = true
|
||||
} else if zeroer, ok := f.(yaml.IsZeroer); ok && zeroer.IsZero() {
|
||||
isZero = true
|
||||
} else if f == nil || value.IsZero() {
|
||||
isZero = true
|
||||
@@ -146,7 +138,7 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
}
|
||||
|
||||
// create a new node entry
|
||||
nodeEntry := &NodeEntry{Tag: tagName, Key: key}
|
||||
nodeEntry := &nodes.NodeEntry{Tag: tagName, Key: key}
|
||||
nodeEntry.RenderZero = renderZeroFlag
|
||||
switch value.Kind() {
|
||||
case reflect.Float64, reflect.Float32:
|
||||
@@ -192,39 +184,24 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
fLow := lowFieldValue.Interface()
|
||||
value = reflect.ValueOf(fLow)
|
||||
|
||||
type lineStyle struct {
|
||||
line int
|
||||
style yaml.Style
|
||||
}
|
||||
|
||||
nodeEntry.LowValue = fLow
|
||||
switch value.Kind() {
|
||||
|
||||
case reflect.Slice:
|
||||
l := value.Len()
|
||||
lines := make([]lineStyle, l)
|
||||
lines := make([]int, l)
|
||||
for g := 0; g < l; g++ {
|
||||
qw := value.Index(g).Interface()
|
||||
if we, wok := qw.(low.HasKeyNode); wok {
|
||||
lines[g] = lineStyle{we.GetKeyNode().Line, we.GetKeyNode().Style}
|
||||
lines[g] = we.GetKeyNode().Line
|
||||
}
|
||||
}
|
||||
sort.Slice(lines, func(i, j int) bool {
|
||||
return lines[i].line < lines[j].line
|
||||
return lines[i] < lines[j]
|
||||
})
|
||||
nodeEntry.Line = lines[0].line // pick the lowest line number so this key is sorted in order.
|
||||
nodeEntry.Style = lines[0].style
|
||||
break
|
||||
nodeEntry.Line = lines[0]
|
||||
case reflect.Map:
|
||||
l := value.Len()
|
||||
line := make([]int, l)
|
||||
for q, ky := range value.MapKeys() {
|
||||
if we, wok := ky.Interface().(low.HasKeyNode); wok {
|
||||
line[q] = we.GetKeyNode().Line
|
||||
}
|
||||
}
|
||||
sort.Ints(line)
|
||||
nodeEntry.Line = line[0]
|
||||
|
||||
panic("only ordered maps are supported")
|
||||
case reflect.Struct:
|
||||
y := value.Interface()
|
||||
nodeEntry.Line = 9999 + i
|
||||
@@ -232,13 +209,11 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
if nb.IsReference() {
|
||||
if jk, kj := y.(low.HasKeyNode); kj {
|
||||
nodeEntry.Line = jk.GetKeyNode().Line
|
||||
nodeEntry.Style = jk.GetKeyNode().Style
|
||||
break
|
||||
}
|
||||
}
|
||||
if nb.GetValueNode() != nil {
|
||||
nodeEntry.Line = nb.GetValueNode().Line
|
||||
nodeEntry.Style = nb.GetValueNode().Style
|
||||
}
|
||||
}
|
||||
default:
|
||||
@@ -252,12 +227,13 @@ func (n *NodeBuilder) add(key string, i int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NodeBuilder) renderReference() []*yaml.Node {
|
||||
fg := n.Low.(low.IsReferenced)
|
||||
nodes := make([]*yaml.Node, 2)
|
||||
nodes[0] = utils.CreateStringNode("$ref")
|
||||
nodes[1] = utils.CreateStringNode(fg.GetReference())
|
||||
return nodes
|
||||
func (n *NodeBuilder) renderReference(fg low.IsReferenced) *yaml.Node {
|
||||
origNode := fg.GetReferenceNode()
|
||||
if origNode == nil {
|
||||
return utils.CreateRefNode(fg.GetReference())
|
||||
}
|
||||
|
||||
return origNode
|
||||
}
|
||||
|
||||
// Render will render the NodeBuilder back to a YAML node, iterating over every NodeEntry defined
|
||||
@@ -272,8 +248,7 @@ func (n *NodeBuilder) Render() *yaml.Node {
|
||||
g := reflect.ValueOf(fg)
|
||||
if !g.IsNil() {
|
||||
if fg.IsReference() && !n.Resolve {
|
||||
m.Content = append(m.Content, n.renderReference()...)
|
||||
return m
|
||||
return n.renderReference(n.Low.(low.IsReferenced))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -295,7 +270,7 @@ func (n *NodeBuilder) Render() *yaml.Node {
|
||||
// AddYAMLNode will add a new *yaml.Node to the parent node, using the tag, key and value provided.
|
||||
// If the value is nil, then the node will not be added. This method is recursive, so it will dig down
|
||||
// into any non-scalar types.
|
||||
func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Node {
|
||||
func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *nodes.NodeEntry) *yaml.Node {
|
||||
if entry.Value == nil {
|
||||
return parent
|
||||
}
|
||||
@@ -305,11 +280,11 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
var l *yaml.Node
|
||||
if entry.Tag != "" {
|
||||
l = utils.CreateStringNode(entry.Tag)
|
||||
l.Style = entry.KeyStyle
|
||||
}
|
||||
|
||||
value := entry.Value
|
||||
line := entry.Line
|
||||
key := entry.Key
|
||||
|
||||
var valueNode *yaml.Node
|
||||
switch t.Kind() {
|
||||
@@ -318,9 +293,15 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
val := value.(string)
|
||||
valueNode = utils.CreateStringNode(val)
|
||||
valueNode.Line = line
|
||||
valueNode.Style = entry.Style
|
||||
break
|
||||
|
||||
if entry.LowValue != nil {
|
||||
if vnut, ok := entry.LowValue.(low.HasValueNodeUntyped); ok {
|
||||
vn := vnut.GetValueNode()
|
||||
if vn != nil {
|
||||
valueNode.Style = vn.Style
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Bool:
|
||||
val := value.(bool)
|
||||
if !val {
|
||||
@@ -329,26 +310,18 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
valueNode = utils.CreateBoolNode("true")
|
||||
}
|
||||
valueNode.Line = line
|
||||
break
|
||||
|
||||
case reflect.Int:
|
||||
val := strconv.Itoa(value.(int))
|
||||
valueNode = utils.CreateIntNode(val)
|
||||
valueNode.Line = line
|
||||
break
|
||||
|
||||
case reflect.Int64:
|
||||
val := strconv.FormatInt(value.(int64), 10)
|
||||
valueNode = utils.CreateIntNode(val)
|
||||
valueNode.Line = line
|
||||
break
|
||||
|
||||
case reflect.Float32:
|
||||
val := strconv.FormatFloat(float64(value.(float32)), 'f', 2, 64)
|
||||
valueNode = utils.CreateFloatNode(val)
|
||||
valueNode.Line = line
|
||||
break
|
||||
|
||||
case reflect.Float64:
|
||||
precision := -1
|
||||
if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") {
|
||||
@@ -357,87 +330,8 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
val := strconv.FormatFloat(value.(float64), 'f', precision, 64)
|
||||
valueNode = utils.CreateFloatNode(val)
|
||||
valueNode.Line = line
|
||||
break
|
||||
|
||||
case reflect.Map:
|
||||
|
||||
// the keys will be rendered randomly, if we don't find out the original line
|
||||
// number of the tag.
|
||||
|
||||
var orderedCollection []*NodeEntry
|
||||
m := reflect.ValueOf(value)
|
||||
for g, k := range m.MapKeys() {
|
||||
var x string
|
||||
// extract key
|
||||
yu := k.Interface()
|
||||
if o, ok := yu.(low.HasKeyNode); ok {
|
||||
x = o.GetKeyNode().Value
|
||||
} else {
|
||||
x = k.String()
|
||||
}
|
||||
|
||||
// go low and pull out the line number.
|
||||
lowProps := reflect.ValueOf(n.Low)
|
||||
if n.Low != nil && !lowProps.IsZero() && !lowProps.IsNil() {
|
||||
gu := lowProps.Elem()
|
||||
gi := gu.FieldByName(key)
|
||||
jl := reflect.ValueOf(gi)
|
||||
if !jl.IsZero() && gi.Interface() != nil {
|
||||
gh := gi.Interface()
|
||||
// extract low level key line number
|
||||
if pr, ok := gh.(low.HasValueUnTyped); ok {
|
||||
fg := reflect.ValueOf(pr.GetValueUntyped())
|
||||
found := false
|
||||
found, orderedCollection = n.extractLowMapKeys(fg, x, found, orderedCollection, m, k)
|
||||
if found != true {
|
||||
// this is something new, add it.
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: x,
|
||||
Key: x,
|
||||
Line: 9999 + g,
|
||||
Value: m.MapIndex(k).Interface(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// this is a map, but it may be wrapped still.
|
||||
bj := reflect.ValueOf(gh)
|
||||
orderedCollection = n.extractLowMapKeysWrapped(bj, x, orderedCollection, g)
|
||||
}
|
||||
} else {
|
||||
// this is a map, without any low level details available (probably an extension map).
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: x,
|
||||
Key: x,
|
||||
Line: 9999 + g,
|
||||
Value: m.MapIndex(k).Interface(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
// this is a map, without any low level details available (probably an extension map).
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: x,
|
||||
Key: x,
|
||||
Line: 9999 + g,
|
||||
Value: m.MapIndex(k).Interface(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// sort the slice by line number to ensure everything is rendered in order.
|
||||
sort.Slice(orderedCollection, func(i, j int) bool {
|
||||
return orderedCollection[i].Line < orderedCollection[j].Line
|
||||
})
|
||||
|
||||
// create an empty map.
|
||||
p := utils.CreateEmptyMapNode()
|
||||
|
||||
// build out each map node in original order.
|
||||
for _, cv := range orderedCollection {
|
||||
n.AddYAMLNode(p, cv)
|
||||
}
|
||||
if len(p.Content) > 0 {
|
||||
valueNode = p
|
||||
}
|
||||
panic("only ordered maps are supported")
|
||||
|
||||
case reflect.Slice:
|
||||
|
||||
@@ -456,8 +350,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
if ut != nil && r.GetReference() != "" &&
|
||||
ut.(low.IsReferenced).IsReference() {
|
||||
if !n.Resolve {
|
||||
refNode := utils.CreateRefNode(glu.GoLowUntyped().(low.IsReferenced).GetReference())
|
||||
sl.Content = append(sl.Content, refNode)
|
||||
sl.Content = append(sl.Content, n.renderReference(glu.GoLowUntyped().(low.IsReferenced)))
|
||||
skip = true
|
||||
} else {
|
||||
skip = false
|
||||
@@ -472,13 +365,13 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
if er, ko := sqi.(Renderable); ko {
|
||||
var rend interface{}
|
||||
if !n.Resolve {
|
||||
rend, _ = er.(Renderable).MarshalYAML()
|
||||
rend, _ = er.MarshalYAML()
|
||||
} else {
|
||||
// try and render inline, if we can, otherwise treat as normal.
|
||||
if _, ko := er.(RenderableInline); ko {
|
||||
rend, _ = er.(RenderableInline).MarshalYAMLInline()
|
||||
} else {
|
||||
rend, _ = er.(Renderable).MarshalYAML()
|
||||
rend, _ = er.MarshalYAML()
|
||||
}
|
||||
}
|
||||
// check if this is a pointer or not.
|
||||
@@ -505,6 +398,17 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
if err != nil {
|
||||
return parent
|
||||
} else {
|
||||
if entry.LowValue != nil {
|
||||
if vnut, ok := entry.LowValue.(low.HasValueNodeUntyped); ok {
|
||||
vn := vnut.GetValueNode()
|
||||
if vn.Kind == yaml.SequenceNode {
|
||||
for i := range vn.Content {
|
||||
rawNode.Content[i].Style = vn.Content[i].Style
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
valueNode = &rawNode
|
||||
}
|
||||
|
||||
@@ -524,18 +428,29 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
return parent
|
||||
|
||||
case reflect.Ptr:
|
||||
if r, ok := value.(Renderable); ok {
|
||||
if m, ok := value.(orderedmap.MapToYamlNoder); ok {
|
||||
l := entry.LowValue
|
||||
|
||||
if l == nil {
|
||||
if gl, ok := value.(GoesLowUntyped); ok && gl.GoLowUntyped() != nil {
|
||||
l = gl.GoLowUntyped()
|
||||
}
|
||||
}
|
||||
|
||||
p := m.ToYamlNode(n, l)
|
||||
if len(p.Content) > 0 {
|
||||
valueNode = p
|
||||
}
|
||||
} else if r, ok := value.(Renderable); ok {
|
||||
if gl, lg := value.(GoesLowUntyped); lg {
|
||||
if gl.GoLowUntyped() != nil {
|
||||
ut := reflect.ValueOf(gl.GoLowUntyped())
|
||||
lut := gl.GoLowUntyped()
|
||||
if lut != nil {
|
||||
lr := lut.(low.IsReferenced)
|
||||
ut := reflect.ValueOf(lr)
|
||||
if !ut.IsNil() {
|
||||
if gl.GoLowUntyped().(low.IsReferenced).IsReference() {
|
||||
if lut.(low.IsReferenced).IsReference() {
|
||||
if !n.Resolve {
|
||||
// TODO: use renderReference here.
|
||||
rvn := utils.CreateEmptyMapNode()
|
||||
rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref"))
|
||||
rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference()))
|
||||
valueNode = rvn
|
||||
valueNode = n.renderReference(lut.(low.IsReferenced))
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -621,64 +536,6 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
||||
return parent
|
||||
}
|
||||
|
||||
func (n *NodeBuilder) extractLowMapKeysWrapped(iu reflect.Value, x string, orderedCollection []*NodeEntry, g int) []*NodeEntry {
|
||||
for _, ky := range iu.MapKeys() {
|
||||
ty := ky.Interface()
|
||||
if ere, eok := ty.(low.HasKeyNode); eok {
|
||||
er := ere.GetKeyNode().Value
|
||||
if er == x {
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: x,
|
||||
Key: x,
|
||||
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
|
||||
Value: iu.MapIndex(ky).Interface(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: x,
|
||||
Key: x,
|
||||
Line: 9999 + g,
|
||||
Value: iu.MapIndex(ky).Interface(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return orderedCollection
|
||||
}
|
||||
|
||||
func (n *NodeBuilder) extractLowMapKeys(fg reflect.Value, x string, found bool, orderedCollection []*NodeEntry, m reflect.Value, k reflect.Value) (bool, []*NodeEntry) {
|
||||
if fg.IsValid() && !fg.IsZero() {
|
||||
for j, ky := range fg.MapKeys() {
|
||||
hu := ky.Interface()
|
||||
if we, wok := hu.(low.HasKeyNode); wok {
|
||||
er := we.GetKeyNode().Value
|
||||
if er == x {
|
||||
found = true
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: x,
|
||||
Key: x,
|
||||
Line: we.GetKeyNode().Line,
|
||||
Value: m.MapIndex(k).Interface(),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
uu := ky.Interface()
|
||||
if uu == x {
|
||||
// this is a map, without any low level details available
|
||||
found = true
|
||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||
Tag: uu.(string),
|
||||
Key: uu.(string),
|
||||
Line: 9999 + j,
|
||||
Value: m.MapIndex(k).Interface(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return found, orderedCollection
|
||||
}
|
||||
|
||||
// Renderable is an interface that can be implemented by types that provide a custom MarshalYAML method.
|
||||
type Renderable interface {
|
||||
MarshalYAML() (interface{}, error)
|
||||
|
||||
@@ -4,169 +4,147 @@
|
||||
package high
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high/nodes"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type key struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
ref bool
|
||||
refStr string
|
||||
ln int
|
||||
nilval bool
|
||||
v any
|
||||
kn *yaml.Node
|
||||
low.IsReferenced `yaml:"-"`
|
||||
type valueReferenceStruct struct {
|
||||
ref bool
|
||||
refStr string
|
||||
Value string `yaml:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (k key) GetKeyNode() *yaml.Node {
|
||||
if k.kn != nil {
|
||||
return k.kn
|
||||
}
|
||||
kn := utils.CreateStringNode("meddy")
|
||||
kn.Line = k.ln
|
||||
return kn
|
||||
func (r valueReferenceStruct) IsReference() bool {
|
||||
return r.ref
|
||||
}
|
||||
|
||||
func (k key) GetValueUntyped() any {
|
||||
return k.v
|
||||
func (r valueReferenceStruct) GetReference() string {
|
||||
return r.refStr
|
||||
}
|
||||
|
||||
func (k key) GetValueNode() *yaml.Node {
|
||||
return k.GetValueNodeUntyped()
|
||||
func (r *valueReferenceStruct) SetReference(ref string, _ *yaml.Node) {
|
||||
r.refStr = ref
|
||||
}
|
||||
|
||||
func (k key) GetValueNodeUntyped() *yaml.Node {
|
||||
if k.nilval {
|
||||
return nil
|
||||
}
|
||||
kn := utils.CreateStringNode("maddy")
|
||||
kn.Line = k.ln
|
||||
return kn
|
||||
func (r *valueReferenceStruct) GetReferenceNode() *yaml.Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k key) IsReference() bool {
|
||||
return k.ref
|
||||
}
|
||||
|
||||
func (k key) GetReference() string {
|
||||
return k.refStr
|
||||
}
|
||||
|
||||
func (k key) SetReference(ref string) {
|
||||
k.refStr = ref
|
||||
}
|
||||
|
||||
func (k key) GoLowUntyped() any {
|
||||
return &k
|
||||
}
|
||||
|
||||
func (k key) MarshalYAML() (interface{}, error) {
|
||||
func (r valueReferenceStruct) MarshalYAML() (interface{}, error) {
|
||||
return utils.CreateStringNode("pizza"), nil
|
||||
}
|
||||
|
||||
func (k key) MarshalYAMLInline() (interface{}, error) {
|
||||
func (r valueReferenceStruct) MarshalYAMLInline() (interface{}, error) {
|
||||
return utils.CreateStringNode("pizza-inline!"), nil
|
||||
}
|
||||
|
||||
func (r valueReferenceStruct) GoLowUntyped() any {
|
||||
return &r
|
||||
}
|
||||
|
||||
type plug struct {
|
||||
Name []string `yaml:"name,omitempty"`
|
||||
}
|
||||
|
||||
type test1 struct {
|
||||
Thrig map[string]*plug `yaml:"thrig,omitempty"`
|
||||
Thing string `yaml:"thing,omitempty"`
|
||||
Thong int `yaml:"thong,omitempty"`
|
||||
Thrum int64 `yaml:"thrum,omitempty"`
|
||||
Thang float32 `yaml:"thang,omitempty"`
|
||||
Thung float64 `yaml:"thung,omitempty"`
|
||||
Thyme bool `yaml:"thyme,omitempty"`
|
||||
Thurm any `yaml:"thurm,omitempty"`
|
||||
Thugg *bool `yaml:"thugg,renderZero"`
|
||||
Thurr *int64 `yaml:"thurr,omitempty"`
|
||||
Thral *float64 `yaml:"thral,omitempty"`
|
||||
Throo *float64 `yaml:"throo,renderZero,omitempty"`
|
||||
Tharg []string `yaml:"tharg,omitempty"`
|
||||
Type []string `yaml:"type,omitempty"`
|
||||
Throg []*key `yaml:"throg,omitempty"`
|
||||
Thrat []interface{} `yaml:"thrat,omitempty"`
|
||||
Thrag []map[string][]string `yaml:"thrag,omitempty"`
|
||||
Thrug map[string]string `yaml:"thrug,omitempty"`
|
||||
Thoom []map[string]string `yaml:"thoom,omitempty"`
|
||||
Thomp map[key]string `yaml:"thomp,omitempty"`
|
||||
Thump key `yaml:"thump,omitempty"`
|
||||
Thane key `yaml:"thane,omitempty"`
|
||||
Thunk key `yaml:"thunk,omitempty"`
|
||||
Thrim *key `yaml:"thrim,omitempty"`
|
||||
Thril map[string]*key `yaml:"thril,omitempty"`
|
||||
Extensions map[string]any `yaml:"-"`
|
||||
ignoreMe string `yaml:"-"`
|
||||
IgnoreMe string `yaml:"-"`
|
||||
Thrig *orderedmap.Map[string, *plug] `yaml:"thrig,omitempty"`
|
||||
Thing string `yaml:"thing,omitempty"`
|
||||
Thong int `yaml:"thong,omitempty"`
|
||||
Thrum int64 `yaml:"thrum,omitempty"`
|
||||
Thang float32 `yaml:"thang,omitempty"`
|
||||
Thung float64 `yaml:"thung,omitempty"`
|
||||
Thyme bool `yaml:"thyme,omitempty"`
|
||||
Thurm any `yaml:"thurm,omitempty"`
|
||||
Thugg *bool `yaml:"thugg,renderZero"`
|
||||
Thurr *int64 `yaml:"thurr,omitempty"`
|
||||
Thral *float64 `yaml:"thral,omitempty"`
|
||||
Throo *float64 `yaml:"throo,renderZero,omitempty"`
|
||||
Tharg []string `yaml:"tharg,omitempty"`
|
||||
Type []string `yaml:"type,omitempty"`
|
||||
Throg []*valueReferenceStruct `yaml:"throg,omitempty"`
|
||||
Thrat []interface{} `yaml:"thrat,omitempty"`
|
||||
Thrag []*orderedmap.Map[string, []string] `yaml:"thrag,omitempty"`
|
||||
Thrug *orderedmap.Map[string, string] `yaml:"thrug,omitempty"`
|
||||
Thoom []*orderedmap.Map[string, string] `yaml:"thoom,omitempty"`
|
||||
Thomp *orderedmap.Map[low.KeyReference[string], string] `yaml:"thomp,omitempty"`
|
||||
Thump valueReferenceStruct `yaml:"thump,omitempty"`
|
||||
Thane valueReferenceStruct `yaml:"thane,omitempty"`
|
||||
Thunk valueReferenceStruct `yaml:"thunk,omitempty"`
|
||||
Thrim *valueReferenceStruct `yaml:"thrim,omitempty"`
|
||||
Thril *orderedmap.Map[string, *valueReferenceStruct] `yaml:"thril,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `yaml:"-"`
|
||||
ignoreMe string `yaml:"-"`
|
||||
IgnoreMe string `yaml:"-"`
|
||||
}
|
||||
|
||||
func (te *test1) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (te *test1) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
g := orderedmap.New[low.KeyReference[string], low.ValueReference[*yaml.Node]]()
|
||||
|
||||
g := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
|
||||
for i := range te.Extensions {
|
||||
|
||||
f := reflect.TypeOf(te.Extensions[i])
|
||||
switch f.Kind() {
|
||||
case reflect.String:
|
||||
vn := utils.CreateStringNode(te.Extensions[i].(string))
|
||||
vn.Line = 999999 // weighted to the bottom.
|
||||
g[low.KeyReference[string]{
|
||||
Value: i,
|
||||
KeyNode: vn,
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: vn,
|
||||
Value: te.Extensions[i].(string),
|
||||
}
|
||||
case reflect.Map:
|
||||
kn := utils.CreateStringNode(i)
|
||||
var vn yaml.Node
|
||||
_ = vn.Decode(te.Extensions[i])
|
||||
|
||||
kn.Line = 999999 // weighted to the bottom.
|
||||
g[low.KeyReference[string]{
|
||||
Value: i,
|
||||
KeyNode: kn,
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: &vn,
|
||||
Value: te.Extensions[i],
|
||||
}
|
||||
}
|
||||
i := 0
|
||||
for pair := orderedmap.First(te.Extensions); pair != nil; pair = pair.Next() {
|
||||
kn := utils.CreateStringNode(pair.Key())
|
||||
kn.Line = 999999 + i // weighted to the bottom.
|
||||
|
||||
g.Set(low.KeyReference[string]{
|
||||
Value: pair.Key(),
|
||||
KeyNode: kn,
|
||||
}, low.ValueReference[*yaml.Node]{
|
||||
ValueNode: pair.Value(),
|
||||
Value: pair.Value(),
|
||||
})
|
||||
i++
|
||||
}
|
||||
return g
|
||||
}
|
||||
|
||||
func (te *test1) MarshalYAML() (interface{}, error) {
|
||||
panic("MarshalYAML")
|
||||
nb := NewNodeBuilder(te, te)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
func (te *test1) GetKeyNode() *yaml.Node {
|
||||
panic("GetKeyNode")
|
||||
kn := utils.CreateStringNode("meddy")
|
||||
kn.Line = 20
|
||||
return kn
|
||||
}
|
||||
|
||||
func (te *test1) GoesLowUntyped() any {
|
||||
panic("GoesLowUntyped")
|
||||
return te
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder(t *testing.T) {
|
||||
|
||||
b := true
|
||||
c := int64(12345)
|
||||
d := 1234.1234
|
||||
|
||||
thoom1 := orderedmap.New[string, string]()
|
||||
thoom1.Set("maddy", "champion")
|
||||
|
||||
thoom2 := orderedmap.New[string, string]()
|
||||
thoom2.Set("ember", "naughty")
|
||||
|
||||
thomp := orderedmap.New[low.KeyReference[string], string]()
|
||||
thomp.Set(low.KeyReference[string]{
|
||||
Value: "meddy",
|
||||
KeyNode: utils.CreateStringNode("meddy"),
|
||||
}, "princess")
|
||||
|
||||
thrug := orderedmap.New[string, string]()
|
||||
thrug.Set("chicken", "nuggets")
|
||||
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-pizza", utils.CreateStringNode("time"))
|
||||
|
||||
t1 := test1{
|
||||
ignoreMe: "I should never be seen!",
|
||||
Thing: "ding",
|
||||
@@ -181,30 +159,18 @@ func TestNewNodeBuilder(t *testing.T) {
|
||||
Thral: &d,
|
||||
Tharg: []string{"chicken", "nuggets"},
|
||||
Type: []string{"chicken"},
|
||||
Thoom: []map[string]string{
|
||||
{
|
||||
"maddy": "champion",
|
||||
},
|
||||
{
|
||||
"ember": "naughty",
|
||||
},
|
||||
Thoom: []*orderedmap.Map[string, string]{
|
||||
thoom1,
|
||||
thoom2,
|
||||
},
|
||||
Thomp: map[key]string{
|
||||
{ln: 1}: "princess",
|
||||
},
|
||||
Thane: key{ // this is going to be ignored, needs to be a ValueReference
|
||||
ln: 2,
|
||||
ref: true,
|
||||
refStr: "ripples",
|
||||
},
|
||||
Thrug: map[string]string{
|
||||
"chicken": "nuggets",
|
||||
},
|
||||
Thump: key{Name: "I will be ignored", ln: 3},
|
||||
Thunk: key{ln: 4, nilval: true},
|
||||
Extensions: map[string]any{
|
||||
"x-pizza": "time",
|
||||
Thomp: thomp,
|
||||
Thane: valueReferenceStruct{ // this is going to be ignored, needs to be a ValueReference
|
||||
Value: "ripples",
|
||||
},
|
||||
Thrug: thrug,
|
||||
Thump: valueReferenceStruct{Value: "I will be ignored"},
|
||||
Thunk: valueReferenceStruct{},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, nil)
|
||||
@@ -235,11 +201,9 @@ thomp:
|
||||
x-pizza: time`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(data)))
|
||||
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_Type(t *testing.T) {
|
||||
|
||||
t1 := test1{
|
||||
Type: []string{"chicken", "soup"},
|
||||
}
|
||||
@@ -257,15 +221,12 @@ func TestNewNodeBuilder_Type(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_IsReferenced(t *testing.T) {
|
||||
|
||||
t1 := key{
|
||||
Name: "cotton",
|
||||
ref: true,
|
||||
refStr: "#/my/heart",
|
||||
ln: 2,
|
||||
t1 := &low.ValueReference[string]{
|
||||
Value: "cotton",
|
||||
}
|
||||
t1.SetReference("#/my/heart", nil)
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
node := nb.Render()
|
||||
|
||||
data, _ := yaml.Marshal(node)
|
||||
@@ -276,14 +237,14 @@ func TestNewNodeBuilder_IsReferenced(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_Extensions(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-pizza", utils.CreateStringNode("time"))
|
||||
ext.Set("x-money", utils.CreateStringNode("time"))
|
||||
|
||||
t1 := test1{
|
||||
Thing: "ding",
|
||||
Extensions: map[string]any{
|
||||
"x-pizza": "time",
|
||||
"x-money": "time",
|
||||
},
|
||||
Thong: 1,
|
||||
Thing: "ding",
|
||||
Extensions: ext,
|
||||
Thong: 1,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
@@ -294,14 +255,14 @@ func TestNewNodeBuilder_Extensions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_LowValueNode(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-pizza", utils.CreateStringNode("time"))
|
||||
ext.Set("x-money", utils.CreateStringNode("time"))
|
||||
|
||||
t1 := test1{
|
||||
Thing: "ding",
|
||||
Extensions: map[string]any{
|
||||
"x-pizza": "time",
|
||||
"x-money": "time",
|
||||
},
|
||||
Thong: 1,
|
||||
Thing: "ding",
|
||||
Extensions: ext,
|
||||
Thong: 1,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
@@ -313,12 +274,11 @@ func TestNewNodeBuilder_LowValueNode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_NoValue(t *testing.T) {
|
||||
|
||||
t1 := test1{
|
||||
Thing: "",
|
||||
}
|
||||
|
||||
nodeEnty := NodeEntry{}
|
||||
nodeEnty := nodes.NodeEntry{}
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
node := nb.AddYAMLNode(nil, &nodeEnty)
|
||||
assert.Nil(t, node)
|
||||
@@ -326,7 +286,7 @@ func TestNewNodeBuilder_NoValue(t *testing.T) {
|
||||
|
||||
func TestNewNodeBuilder_EmptyString(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nodeEnty := NodeEntry{}
|
||||
nodeEnty := nodes.NodeEntry{}
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
node := nb.AddYAMLNode(nil, &nodeEnty)
|
||||
assert.Nil(t, node)
|
||||
@@ -334,7 +294,7 @@ func TestNewNodeBuilder_EmptyString(t *testing.T) {
|
||||
|
||||
func TestNewNodeBuilder_EmptyStringRenderZero(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nodeEnty := NodeEntry{RenderZero: true, Value: ""}
|
||||
nodeEnty := nodes.NodeEntry{RenderZero: true, Value: ""}
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
m := utils.CreateEmptyMapNode()
|
||||
node := nb.AddYAMLNode(m, &nodeEnty)
|
||||
@@ -344,7 +304,7 @@ func TestNewNodeBuilder_EmptyStringRenderZero(t *testing.T) {
|
||||
func TestNewNodeBuilder_Bool(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
nodeEnty := NodeEntry{}
|
||||
nodeEnty := nodes.NodeEntry{}
|
||||
node := nb.AddYAMLNode(nil, &nodeEnty)
|
||||
assert.Nil(t, node)
|
||||
}
|
||||
@@ -364,7 +324,7 @@ func TestNewNodeBuilder_Int(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
p := utils.CreateEmptyMapNode()
|
||||
nodeEnty := NodeEntry{Tag: "p", Value: 12, Key: "p"}
|
||||
nodeEnty := nodes.NodeEntry{Tag: "p", Value: 12, Key: "p"}
|
||||
node := nb.AddYAMLNode(p, &nodeEnty)
|
||||
assert.NotNil(t, node)
|
||||
assert.Len(t, node.Content, 2)
|
||||
@@ -375,7 +335,7 @@ func TestNewNodeBuilder_Int64(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
p := utils.CreateEmptyMapNode()
|
||||
nodeEnty := NodeEntry{Tag: "p", Value: int64(234556), Key: "p"}
|
||||
nodeEnty := nodes.NodeEntry{Tag: "p", Value: int64(234556), Key: "p"}
|
||||
node := nb.AddYAMLNode(p, &nodeEnty)
|
||||
assert.NotNil(t, node)
|
||||
assert.Len(t, node.Content, 2)
|
||||
@@ -386,7 +346,7 @@ func TestNewNodeBuilder_Float32(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
p := utils.CreateEmptyMapNode()
|
||||
nodeEnty := NodeEntry{Tag: "p", Value: float32(1234.23), Key: "p"}
|
||||
nodeEnty := nodes.NodeEntry{Tag: "p", Value: float32(1234.23), Key: "p"}
|
||||
node := nb.AddYAMLNode(p, &nodeEnty)
|
||||
assert.NotNil(t, node)
|
||||
assert.Len(t, node.Content, 2)
|
||||
@@ -397,7 +357,7 @@ func TestNewNodeBuilder_Float64(t *testing.T) {
|
||||
t1 := new(test1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
p := utils.CreateEmptyMapNode()
|
||||
nodeEnty := NodeEntry{Tag: "p", Value: 1234.232323, Key: "p", StringValue: "1234.232323"}
|
||||
nodeEnty := nodes.NodeEntry{Tag: "p", Value: 1234.232323, Key: "p", StringValue: "1234.232323"}
|
||||
node := nb.AddYAMLNode(p, &nodeEnty)
|
||||
assert.NotNil(t, node)
|
||||
assert.Len(t, node.Content, 2)
|
||||
@@ -410,30 +370,33 @@ func TestNewNodeBuilder_EmptyNode(t *testing.T) {
|
||||
nb.Nodes = nil
|
||||
m := nb.Render()
|
||||
assert.Len(t, m.Content, 0)
|
||||
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MapKeyHasValue(t *testing.T) {
|
||||
thrug := orderedmap.New[string, string]()
|
||||
thrug.Set("dump", "trump")
|
||||
|
||||
t1 := test1{
|
||||
Thrug: map[string]string{
|
||||
"dump": "trump",
|
||||
},
|
||||
Thrug: thrug,
|
||||
}
|
||||
|
||||
type test1low struct {
|
||||
Thrug key `yaml:"thrug"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
Thrug *orderedmap.Map[*low.KeyReference[string], *low.ValueReference[string]] `yaml:"thrug"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
}
|
||||
|
||||
thrugLow := orderedmap.New[*low.KeyReference[string], *low.ValueReference[string]]()
|
||||
thrugLow.Set(&low.KeyReference[string]{
|
||||
Value: "dump",
|
||||
KeyNode: utils.CreateStringNode("dump"),
|
||||
}, &low.ValueReference[string]{
|
||||
Value: "trump",
|
||||
ValueNode: utils.CreateStringNode("trump"),
|
||||
})
|
||||
|
||||
t2 := test1low{
|
||||
Thrug: key{
|
||||
v: map[string]string{
|
||||
"dump": "trump",
|
||||
},
|
||||
ln: 2,
|
||||
},
|
||||
Thrug: thrugLow,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t2)
|
||||
@@ -448,31 +411,30 @@ func TestNewNodeBuilder_MapKeyHasValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) {
|
||||
thomp := orderedmap.New[low.KeyReference[string], string]()
|
||||
thomp.Set(low.KeyReference[string]{Value: "meddy", KeyNode: utils.CreateStringNode("meddy")}, "princess")
|
||||
|
||||
t1 := test1{
|
||||
Thomp: map[key]string{
|
||||
{v: "who"}: "princess",
|
||||
},
|
||||
Thomp: thomp,
|
||||
}
|
||||
|
||||
type test1low struct {
|
||||
Thomp key `yaml:"thomp"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
Thomp low.ValueReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]] `yaml:"thomp"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
}
|
||||
|
||||
valueMap := orderedmap.New[low.KeyReference[string], low.ValueReference[string]]()
|
||||
valueMap.Set(low.KeyReference[string]{
|
||||
Value: "ice",
|
||||
KeyNode: utils.CreateStringNode("ice"),
|
||||
}, low.ValueReference[string]{
|
||||
Value: "princess",
|
||||
})
|
||||
|
||||
t2 := test1low{
|
||||
Thomp: key{
|
||||
v: map[key]string{
|
||||
{
|
||||
v: key{
|
||||
v: "ice",
|
||||
kn: utils.CreateStringNode("limes"),
|
||||
},
|
||||
kn: utils.CreateStringNode("chimes"),
|
||||
ln: 6}: "princess",
|
||||
},
|
||||
ln: 2,
|
||||
Thomp: low.ValueReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]{
|
||||
Value: valueMap,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -488,23 +450,27 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch(t *testing.T) {
|
||||
thomp := orderedmap.New[low.KeyReference[string], string]()
|
||||
thomp.Set(low.KeyReference[string]{Value: "meddy", KeyNode: utils.CreateStringNode("meddy")}, "princess")
|
||||
|
||||
t1 := test1{
|
||||
Thomp: map[key]string{
|
||||
{v: "who"}: "princess",
|
||||
},
|
||||
Thomp: thomp,
|
||||
}
|
||||
|
||||
type test1low struct {
|
||||
Thomp low.NodeReference[map[key]string] `yaml:"thomp"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
Thomp low.NodeReference[*orderedmap.Map[low.KeyReference[string], string]] `yaml:"thomp"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
}
|
||||
|
||||
g := low.NodeReference[map[key]string]{
|
||||
Value: map[key]string{
|
||||
{v: "my", kn: utils.CreateStringNode("limes")}: "princess",
|
||||
},
|
||||
valMap := orderedmap.New[low.KeyReference[string], string]()
|
||||
valMap.Set(low.KeyReference[string]{
|
||||
Value: "meddy",
|
||||
KeyNode: utils.CreateStringNode("meddy"),
|
||||
}, "princess")
|
||||
|
||||
g := low.NodeReference[*orderedmap.Map[low.KeyReference[string], string]]{
|
||||
Value: valMap,
|
||||
}
|
||||
|
||||
t2 := test1low{
|
||||
@@ -522,94 +488,27 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch(t *testing.T) {
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatchKeyNode(t *testing.T) {
|
||||
|
||||
t1 := test1{
|
||||
Thomp: map[key]string{
|
||||
{v: "who"}: "princess",
|
||||
},
|
||||
}
|
||||
|
||||
type test1low struct {
|
||||
Thomp low.NodeReference[map[key]string] `yaml:"thomp"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
}
|
||||
|
||||
g := low.NodeReference[map[key]string]{
|
||||
Value: map[key]string{
|
||||
{v: "my", kn: utils.CreateStringNode("limes")}: "princess",
|
||||
},
|
||||
}
|
||||
|
||||
t2 := test1low{
|
||||
Thomp: g,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t2)
|
||||
node := nb.Render()
|
||||
|
||||
data, _ := yaml.Marshal(node)
|
||||
|
||||
desired := `thomp:
|
||||
meddy: princess`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch_NoWrap(t *testing.T) {
|
||||
|
||||
t1 := test1{
|
||||
Thomp: map[key]string{
|
||||
{v: "who"}: "princess",
|
||||
},
|
||||
}
|
||||
|
||||
type test1low struct {
|
||||
Thomp map[key]string `yaml:"thomp"`
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
}
|
||||
|
||||
t2 := test1low{
|
||||
Thomp: map[key]string{
|
||||
{v: "my", kn: utils.CreateStringNode("meddy")}: "princess",
|
||||
},
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t2)
|
||||
node := nb.Render()
|
||||
|
||||
data, _ := yaml.Marshal(node)
|
||||
|
||||
desired := `thomp:
|
||||
meddy: princess`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MissingLabel(t *testing.T) {
|
||||
|
||||
t1 := new(test1)
|
||||
nb := NewNodeBuilder(t1, t1)
|
||||
p := utils.CreateEmptyMapNode()
|
||||
nodeEnty := NodeEntry{Value: 1234.232323, Key: "p"}
|
||||
nodeEnty := nodes.NodeEntry{Value: 1234.232323, Key: "p"}
|
||||
node := nb.AddYAMLNode(p, &nodeEnty)
|
||||
assert.NotNil(t, node)
|
||||
assert.Len(t, node.Content, 0)
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_ExtensionMap(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
pizza := orderedmap.New[string, string]()
|
||||
pizza.Set("dump", "trump")
|
||||
ext.Set("x-pizza", utils.CreateYamlNode(pizza))
|
||||
ext.Set("x-money", utils.CreateStringNode("time"))
|
||||
|
||||
t1 := test1{
|
||||
Thing: "ding",
|
||||
Extensions: map[string]any{
|
||||
"x-pizza": map[string]string{
|
||||
"dump": "trump",
|
||||
},
|
||||
"x-money": "time",
|
||||
},
|
||||
Thong: 1,
|
||||
Thing: "ding",
|
||||
Extensions: ext,
|
||||
Thong: 1,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
@@ -621,20 +520,21 @@ func TestNewNodeBuilder_ExtensionMap(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
pizza := orderedmap.New[string, string]()
|
||||
pizza.Set("dump", "trump")
|
||||
ext.Set("x-pizza", utils.CreateYamlNode(pizza))
|
||||
cake := orderedmap.New[string, string]()
|
||||
cake.Set("maga", "nomore")
|
||||
ext.Set("x-cake", utils.CreateYamlNode(cake))
|
||||
|
||||
thril := orderedmap.New[string, *valueReferenceStruct]()
|
||||
thril.Set("princess", &valueReferenceStruct{Value: "who"})
|
||||
thril.Set("heavy", &valueReferenceStruct{Value: "who"})
|
||||
|
||||
t1 := test1{
|
||||
Extensions: map[string]any{
|
||||
"x-pizza": map[string]string{
|
||||
"dump": "trump",
|
||||
},
|
||||
"x-cake": map[string]string{
|
||||
"maga": "nomore",
|
||||
},
|
||||
},
|
||||
Thril: map[string]*key{
|
||||
"princess": {v: "who", Name: "beef", ln: 2},
|
||||
"heavy": {v: "who", Name: "industries", ln: 3},
|
||||
},
|
||||
Extensions: ext,
|
||||
Thril: thril,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, nil)
|
||||
@@ -642,13 +542,18 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) {
|
||||
|
||||
data, _ := yaml.Marshal(node)
|
||||
|
||||
assert.Len(t, data, 94)
|
||||
assert.Equal(t, `thril:
|
||||
princess: pizza
|
||||
heavy: pizza
|
||||
x-pizza:
|
||||
dump: trump
|
||||
x-cake:
|
||||
maga: nomore`, strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_SliceRef(t *testing.T) {
|
||||
|
||||
c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"}
|
||||
ty := []*key{&c}
|
||||
c := valueReferenceStruct{ref: true, refStr: "#/red/robin/yummmmm", Value: "milky"}
|
||||
ty := []*valueReferenceStruct{&c}
|
||||
t1 := test1{
|
||||
Throg: ty,
|
||||
}
|
||||
@@ -665,9 +570,8 @@ func TestNewNodeBuilder_SliceRef(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_SliceRef_Inline(t *testing.T) {
|
||||
|
||||
c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"}
|
||||
ty := []*key{&c}
|
||||
c := valueReferenceStruct{Value: "milky"}
|
||||
ty := []*valueReferenceStruct{&c}
|
||||
t1 := test1{
|
||||
Throg: ty,
|
||||
}
|
||||
@@ -684,22 +588,19 @@ func TestNewNodeBuilder_SliceRef_Inline(t *testing.T) {
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(data)))
|
||||
}
|
||||
|
||||
type testRender struct {
|
||||
}
|
||||
type testRender struct{}
|
||||
|
||||
func (t testRender) MarshalYAML() (interface{}, error) {
|
||||
return utils.CreateStringNode("testy!"), nil
|
||||
}
|
||||
|
||||
type testRenderRawNode struct {
|
||||
}
|
||||
type testRenderRawNode struct{}
|
||||
|
||||
func (t testRenderRawNode) MarshalYAML() (interface{}, error) {
|
||||
return yaml.Node{Kind: yaml.ScalarNode, Value: "zesty!"}, nil
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_SliceRef_Inline_NotCompatible(t *testing.T) {
|
||||
|
||||
ty := []interface{}{testRender{}}
|
||||
t1 := test1{
|
||||
Thrat: ty,
|
||||
@@ -718,7 +619,6 @@ func TestNewNodeBuilder_SliceRef_Inline_NotCompatible(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_SliceRef_Inline_NotCompatible_NotPointer(t *testing.T) {
|
||||
|
||||
ty := []interface{}{testRenderRawNode{}}
|
||||
t1 := test1{
|
||||
Thrat: ty,
|
||||
@@ -737,7 +637,6 @@ func TestNewNodeBuilder_SliceRef_Inline_NotCompatible_NotPointer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_PointerRef_Inline_NotCompatible_RawNode(t *testing.T) {
|
||||
|
||||
ty := testRenderRawNode{}
|
||||
t1 := test1{
|
||||
Thurm: &ty,
|
||||
@@ -755,8 +654,7 @@ func TestNewNodeBuilder_PointerRef_Inline_NotCompatible_RawNode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_PointerRef_Inline_NotCompatible(t *testing.T) {
|
||||
|
||||
ty := key{}
|
||||
ty := valueReferenceStruct{}
|
||||
t1 := test1{
|
||||
Thurm: &ty,
|
||||
}
|
||||
@@ -773,9 +671,8 @@ func TestNewNodeBuilder_PointerRef_Inline_NotCompatible(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_SliceNoRef(t *testing.T) {
|
||||
|
||||
c := key{ref: false, Name: "milky"}
|
||||
ty := []*key{&c}
|
||||
c := valueReferenceStruct{Value: "milky"}
|
||||
ty := []*valueReferenceStruct{&c}
|
||||
t1 := test1{
|
||||
Throg: ty,
|
||||
}
|
||||
@@ -792,7 +689,6 @@ func TestNewNodeBuilder_SliceNoRef(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestStructAny(t *testing.T) {
|
||||
|
||||
t1 := test1{
|
||||
Thurm: low.ValueReference[any]{
|
||||
ValueNode: utils.CreateStringNode("beer"),
|
||||
@@ -808,8 +704,8 @@ func TestNewNodeBuilder_TestStructAny(t *testing.T) {
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(data)))
|
||||
}
|
||||
func TestNewNodeBuilder_TestStructString(t *testing.T) {
|
||||
|
||||
func TestNewNodeBuilder_TestStructString(t *testing.T) {
|
||||
t1 := test1{
|
||||
Thurm: low.ValueReference[string]{
|
||||
ValueNode: utils.CreateStringNode("beer"),
|
||||
@@ -827,12 +723,11 @@ func TestNewNodeBuilder_TestStructString(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestStructPointer(t *testing.T) {
|
||||
|
||||
t1 := test1{
|
||||
Thrim: &key{
|
||||
Thrim: &valueReferenceStruct{
|
||||
ref: true,
|
||||
refStr: "#/cash/money",
|
||||
Name: "pizza",
|
||||
Value: "pizza",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -848,17 +743,17 @@ func TestNewNodeBuilder_TestStructPointer(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestStructRef(t *testing.T) {
|
||||
|
||||
fkn := utils.CreateStringNode("pizzaBurgers")
|
||||
fkn.Line = 22
|
||||
|
||||
thurm := low.NodeReference[string]{
|
||||
KeyNode: fkn,
|
||||
ValueNode: fkn,
|
||||
}
|
||||
thurm.SetReference("#/cash/money", nil)
|
||||
|
||||
t1 := test1{
|
||||
Thurm: low.NodeReference[string]{
|
||||
Reference: "#/cash/money",
|
||||
ReferenceNode: true,
|
||||
KeyNode: fkn,
|
||||
ValueNode: fkn,
|
||||
},
|
||||
Thurm: thurm,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
@@ -872,7 +767,6 @@ func TestNewNodeBuilder_TestStructRef(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestStructDefaultEncode(t *testing.T) {
|
||||
|
||||
f := 1
|
||||
t1 := test1{
|
||||
Thurm: &f,
|
||||
@@ -889,9 +783,10 @@ func TestNewNodeBuilder_TestStructDefaultEncode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestSliceMapSliceStruct(t *testing.T) {
|
||||
|
||||
a := []map[string][]string{
|
||||
{"pizza": {"beer", "wine"}},
|
||||
pizza := orderedmap.New[string, []string]()
|
||||
pizza.Set("pizza", []string{"beer", "wine"})
|
||||
a := []*orderedmap.Map[string, []string]{
|
||||
pizza,
|
||||
}
|
||||
|
||||
t1 := test1{
|
||||
@@ -912,7 +807,6 @@ func TestNewNodeBuilder_TestSliceMapSliceStruct(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestRenderZero(t *testing.T) {
|
||||
|
||||
f := false
|
||||
t1 := test1{
|
||||
Thugg: &f,
|
||||
@@ -929,7 +823,6 @@ func TestNewNodeBuilder_TestRenderZero(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestRenderZero_Float(t *testing.T) {
|
||||
|
||||
f := 0.0
|
||||
t1 := test1{
|
||||
Throo: &f,
|
||||
@@ -946,7 +839,6 @@ func TestNewNodeBuilder_TestRenderZero_Float(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestRenderZero_Float_NotZero(t *testing.T) {
|
||||
|
||||
f := 0.12
|
||||
t1 := test1{
|
||||
Throo: &f,
|
||||
@@ -963,11 +855,11 @@ func TestNewNodeBuilder_TestRenderZero_Float_NotZero(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_TestRenderServerVariableSimulation(t *testing.T) {
|
||||
thrig := orderedmap.New[string, *plug]()
|
||||
thrig.Set("pork", &plug{Name: []string{"gammon", "bacon"}})
|
||||
|
||||
t1 := test1{
|
||||
Thrig: map[string]*plug{
|
||||
"pork": {Name: []string{"gammon", "bacon"}},
|
||||
},
|
||||
Thrig: thrig,
|
||||
}
|
||||
|
||||
nb := NewNodeBuilder(&t1, &t1)
|
||||
@@ -985,22 +877,21 @@ func TestNewNodeBuilder_TestRenderServerVariableSimulation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNewNodeBuilder_ShouldHaveNotDoneTestsLikeThisOhWell(t *testing.T) {
|
||||
m := orderedmap.New[low.KeyReference[string], low.ValueReference[*valueReferenceStruct]]()
|
||||
|
||||
m := make(map[low.KeyReference[string]]low.ValueReference[*key])
|
||||
|
||||
m[low.KeyReference[string]{
|
||||
m.Set(low.KeyReference[string]{
|
||||
KeyNode: utils.CreateStringNode("pizza"),
|
||||
Value: "pizza",
|
||||
}] = low.ValueReference[*key]{
|
||||
}, low.ValueReference[*valueReferenceStruct]{
|
||||
ValueNode: utils.CreateStringNode("beer"),
|
||||
Value: &key{},
|
||||
}
|
||||
Value: &valueReferenceStruct{},
|
||||
})
|
||||
|
||||
d := make(map[string]*key)
|
||||
d["pizza"] = &key{}
|
||||
d := orderedmap.New[string, *valueReferenceStruct]()
|
||||
d.Set("pizza", &valueReferenceStruct{})
|
||||
|
||||
type t1low struct {
|
||||
Thril low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*key]]
|
||||
Thril low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*valueReferenceStruct]]]
|
||||
Thugg *bool `yaml:"thugg"`
|
||||
Throo *float32 `yaml:"throo"`
|
||||
}
|
||||
@@ -1010,7 +901,7 @@ func TestNewNodeBuilder_ShouldHaveNotDoneTestsLikeThisOhWell(t *testing.T) {
|
||||
}
|
||||
|
||||
t2 := t1low{
|
||||
Thril: low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*key]]{
|
||||
Thril: low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*valueReferenceStruct]]]{
|
||||
Value: m,
|
||||
ValueNode: utils.CreateStringNode("beer"),
|
||||
},
|
||||
|
||||
16
datamodel/high/nodes/nodeentry.go
Normal file
16
datamodel/high/nodes/nodeentry.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package nodes
|
||||
|
||||
import "gopkg.in/yaml.v3"
|
||||
|
||||
// NodeEntry represents a single node used by NodeBuilder.
|
||||
type NodeEntry struct {
|
||||
Tag string
|
||||
Key string
|
||||
Value any
|
||||
StringValue string
|
||||
Line int
|
||||
KeyStyle yaml.Style
|
||||
// ValueStyle yaml.Style
|
||||
RenderZero bool
|
||||
LowValue any
|
||||
}
|
||||
@@ -15,12 +15,13 @@ package high
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// GoesLow is used to represent any high-level model. All high level models meet this interface and can be used to
|
||||
// extract low-level models from any high-level model.
|
||||
type GoesLow[T any] interface {
|
||||
|
||||
// GoLow returns the low-level object that was used to create the high-level object. This allows consumers
|
||||
// to dive-down into the plumbing API at any point in the model.
|
||||
GoLow() T
|
||||
@@ -29,18 +30,17 @@ type GoesLow[T any] interface {
|
||||
// GoesLowUntyped is used to represent any high-level model. All high level models meet this interface and can be used to
|
||||
// extract low-level models from any high-level model.
|
||||
type GoesLowUntyped interface {
|
||||
|
||||
// GoLowUntyped returns the low-level object that was used to create the high-level object. This allows consumers
|
||||
// to dive-down into the plumbing API at any point in the model.
|
||||
GoLowUntyped() any
|
||||
}
|
||||
|
||||
// ExtractExtensions is a convenience method for converting low-level extension definitions, to a high level map[string]any
|
||||
// ExtractExtensions is a convenience method for converting low-level extension definitions, to a high level *orderedmap.Map[string, *yaml.Node]
|
||||
// definition that is easier to consume in applications.
|
||||
func ExtractExtensions(extensions map[low.KeyReference[string]]low.ValueReference[any]) map[string]any {
|
||||
extracted := make(map[string]any)
|
||||
for k, v := range extensions {
|
||||
extracted[k.Value] = v.Value
|
||||
func ExtractExtensions(extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]) *orderedmap.Map[string, *yaml.Node] {
|
||||
extracted := orderedmap.New[string, *yaml.Node]()
|
||||
for pair := orderedmap.First(extensions); pair != nil; pair = pair.Next() {
|
||||
extracted.Set(pair.Key().Value, pair.Value().Value)
|
||||
}
|
||||
return extracted
|
||||
}
|
||||
@@ -61,18 +61,18 @@ func ExtractExtensions(extensions map[low.KeyReference[string]]low.ValueReferenc
|
||||
//
|
||||
// schema := schemaProxy.Schema() // any high-level object that has
|
||||
// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema)
|
||||
func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string]*T, error) {
|
||||
m := make(map[string]*T)
|
||||
func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (*orderedmap.Map[string, *T], error) {
|
||||
m := orderedmap.New[string, *T]()
|
||||
ext := low.GoLow().GetExtensions()
|
||||
for i := range ext {
|
||||
key := i.Value
|
||||
for pair := orderedmap.First(ext); pair != nil; pair = pair.Next() {
|
||||
key := pair.Key().Value
|
||||
g := new(T)
|
||||
valueNode := ext[i].ValueNode
|
||||
valueNode := pair.Value().ValueNode
|
||||
err := valueNode.Decode(g)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[key] = g
|
||||
m.Set(key, g)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -4,21 +4,30 @@
|
||||
package high
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestExtractExtensions(t *testing.T) {
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
n := orderedmap.New[low.KeyReference[string], low.ValueReference[*yaml.Node]]()
|
||||
n.Set(low.KeyReference[string]{
|
||||
Value: "pb33f",
|
||||
}] = low.ValueReference[any]{
|
||||
Value: "new cowboy in town",
|
||||
}
|
||||
}, low.ValueReference[*yaml.Node]{
|
||||
Value: utils.CreateStringNode("new cowboy in town"),
|
||||
})
|
||||
ext := ExtractExtensions(n)
|
||||
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
||||
|
||||
var pb33f string
|
||||
err := ext.GetOrZero("pb33f").Decode(&pb33f)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "new cowboy in town", pb33f)
|
||||
}
|
||||
|
||||
type textExtension struct {
|
||||
@@ -35,15 +44,14 @@ func (p *parent) GoLow() *child {
|
||||
}
|
||||
|
||||
type child struct {
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (c *child) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return c.Extensions
|
||||
}
|
||||
|
||||
func TestUnpackExtensions(t *testing.T) {
|
||||
|
||||
var resultA, resultB yaml.Node
|
||||
|
||||
ymlA := `
|
||||
@@ -59,18 +67,18 @@ power: 2`
|
||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
n := orderedmap.New[low.KeyReference[string], low.ValueReference[*yaml.Node]]()
|
||||
n.Set(low.KeyReference[string]{
|
||||
Value: "x-rancher-a",
|
||||
}] = low.ValueReference[any]{
|
||||
}, low.ValueReference[*yaml.Node]{
|
||||
ValueNode: resultA.Content[0],
|
||||
}
|
||||
})
|
||||
|
||||
n[low.KeyReference[string]{
|
||||
n.Set(low.KeyReference[string]{
|
||||
Value: "x-rancher-b",
|
||||
}] = low.ValueReference[any]{
|
||||
}, low.ValueReference[*yaml.Node]{
|
||||
ValueNode: resultB.Content[0],
|
||||
}
|
||||
})
|
||||
|
||||
c := new(child)
|
||||
c.Extensions = n
|
||||
@@ -81,14 +89,13 @@ power: 2`
|
||||
res, err := UnpackExtensions[textExtension, *child](p)
|
||||
assert.NoError(t, err)
|
||||
assert.NotEmpty(t, res)
|
||||
assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy)
|
||||
assert.Equal(t, 100, res["x-rancher-a"].Power)
|
||||
assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy)
|
||||
assert.Equal(t, 2, res["x-rancher-b"].Power)
|
||||
assert.Equal(t, "buckaroo", res.GetOrZero("x-rancher-a").Cowboy)
|
||||
assert.Equal(t, 100, res.GetOrZero("x-rancher-a").Power)
|
||||
assert.Equal(t, "frogman", res.GetOrZero("x-rancher-b").Cowboy)
|
||||
assert.Equal(t, 2, res.GetOrZero("x-rancher-b").Power)
|
||||
}
|
||||
|
||||
func TestUnpackExtensions_Fail(t *testing.T) {
|
||||
|
||||
var resultA, resultB yaml.Node
|
||||
|
||||
ymlA := `
|
||||
@@ -105,18 +112,18 @@ power: hello`
|
||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
n := orderedmap.New[low.KeyReference[string], low.ValueReference[*yaml.Node]]()
|
||||
n.Set(low.KeyReference[string]{
|
||||
Value: "x-rancher-a",
|
||||
}] = low.ValueReference[any]{
|
||||
}, low.ValueReference[*yaml.Node]{
|
||||
ValueNode: resultA.Content[0],
|
||||
}
|
||||
})
|
||||
|
||||
n[low.KeyReference[string]{
|
||||
n.Set(low.KeyReference[string]{
|
||||
Value: "x-rancher-b",
|
||||
}] = low.ValueReference[any]{
|
||||
}, low.ValueReference[*yaml.Node]{
|
||||
ValueNode: resultB.Content[0],
|
||||
}
|
||||
})
|
||||
|
||||
c := new(child)
|
||||
c.Extensions = n
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
// arrays or models.
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
type Definitions struct {
|
||||
Definitions orderedmap.Map[string, *highbase.SchemaProxy]
|
||||
Definitions *orderedmap.Map[string, *highbase.SchemaProxy]
|
||||
low *low.Definitions
|
||||
}
|
||||
|
||||
@@ -28,7 +29,7 @@ func NewDefinitions(definitions *low.Definitions) *Definitions {
|
||||
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,
|
||||
key: pair.Key().Value,
|
||||
result: highbase.NewSchemaProxy(&lowmodel.NodeReference[*lowbase.SchemaProxy]{
|
||||
Value: pair.Value().Value,
|
||||
}),
|
||||
@@ -38,7 +39,7 @@ func NewDefinitions(definitions *low.Definitions) *Definitions {
|
||||
defs.Set(value.key, value.result)
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.TranslateMapParallel(definitions.Schemas, translateFunc, resultFunc)
|
||||
_ = datamodel.TranslateMapParallel(definitions.Schemas, translateFunc, resultFunc)
|
||||
rd.Definitions = defs
|
||||
return rd
|
||||
}
|
||||
|
||||
@@ -6,13 +6,14 @@ package v2
|
||||
import (
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 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 orderedmap.Map[string, any]
|
||||
Values *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Examples
|
||||
}
|
||||
|
||||
@@ -21,7 +22,7 @@ func NewExample(examples *low.Examples) *Example {
|
||||
e := new(Example)
|
||||
e.low = examples
|
||||
if orderedmap.Len(examples.Values) > 0 {
|
||||
values := orderedmap.New[string, any]()
|
||||
values := orderedmap.New[string, *yaml.Node]()
|
||||
for pair := orderedmap.First(examples.Values); pair != nil; pair = pair.Next() {
|
||||
values.Set(pair.Key().Value, pair.Value().Value)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package v2
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Header Represents a high-level Swagger / OpenAPI 2 Header object, backed by a low-level one.
|
||||
@@ -30,7 +32,7 @@ type Header struct {
|
||||
UniqueItems bool
|
||||
Enum []any
|
||||
MultipleOf int
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Header
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package v2
|
||||
|
||||
import (
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Items is a high-level representation of a Swagger / OpenAPI 2 Items object, backed by a low level one.
|
||||
@@ -16,7 +17,7 @@ type Items struct {
|
||||
Format string
|
||||
CollectionFormat string
|
||||
Items *Items
|
||||
Default any
|
||||
Default *yaml.Node
|
||||
Maximum int
|
||||
ExclusiveMaximum bool
|
||||
Minimum int
|
||||
@@ -27,7 +28,7 @@ type Items struct {
|
||||
MaxItems int
|
||||
MinItems int
|
||||
UniqueItems bool
|
||||
Enum []any
|
||||
Enum []*yaml.Node
|
||||
MultipleOf int
|
||||
low *low.Items
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func NewItems(items *low.Items) *Items {
|
||||
i.UniqueItems = items.UniqueItems.Value
|
||||
}
|
||||
if !items.Enum.IsEmpty() {
|
||||
var enums []any
|
||||
var enums []*yaml.Node
|
||||
for e := range items.Enum.Value {
|
||||
enums = append(enums, items.Enum.Value[e].Value)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ 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"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Operation represents a high-level Swagger / OpenAPI 2 Operation object, backed by a low-level one.
|
||||
@@ -25,7 +27,7 @@ type Operation struct {
|
||||
Schemes []string
|
||||
Deprecated bool
|
||||
Security []*base.SecurityRequirement
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Operation
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ 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"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Parameter represents a high-level Swagger / OpenAPI 2 Parameter object, backed by a low-level one.
|
||||
@@ -61,7 +63,7 @@ type Parameter struct {
|
||||
Schema *base.SchemaProxy
|
||||
Items *Items
|
||||
CollectionFormat string
|
||||
Default any
|
||||
Default *yaml.Node
|
||||
Maximum *int
|
||||
ExclusiveMaximum *bool
|
||||
Minimum *int
|
||||
@@ -72,9 +74,9 @@ type Parameter struct {
|
||||
MaxItems *int
|
||||
MinItems *int
|
||||
UniqueItems *bool
|
||||
Enum []any
|
||||
Enum []*yaml.Node
|
||||
MultipleOf *int
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Parameter
|
||||
}
|
||||
|
||||
@@ -147,7 +149,7 @@ func NewParameter(parameter *low.Parameter) *Parameter {
|
||||
p.UniqueItems = ¶meter.UniqueItems.Value
|
||||
}
|
||||
if !parameter.Enum.IsEmpty() {
|
||||
var enums []any
|
||||
var enums []*yaml.Node
|
||||
for e := range parameter.Enum.Value {
|
||||
enums = append(enums, parameter.Enum.Value[e].Value)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
@@ -16,7 +17,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 orderedmap.Map[string, *Parameter]
|
||||
Definitions *orderedmap.Map[string, *Parameter]
|
||||
low *low.ParameterDefinitions
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ func NewParametersDefinitions(parametersDefinitions *low.ParameterDefinitions) *
|
||||
params.Set(value.key, value.result)
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.TranslateMapParallel(parametersDefinitions.Definitions, translateFunc, resultFunc)
|
||||
_ = datamodel.TranslateMapParallel(parametersDefinitions.Definitions, translateFunc, resultFunc)
|
||||
pd.Definitions = params
|
||||
return pd
|
||||
}
|
||||
|
||||
@@ -4,10 +4,15 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowV2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// PathItem represents a high-level Swagger / OpenAPI 2 PathItem object backed by a low-level one.
|
||||
@@ -26,12 +31,12 @@ type PathItem struct {
|
||||
Head *Operation
|
||||
Patch *Operation
|
||||
Parameters []*Parameter
|
||||
Extensions map[string]any
|
||||
low *low.PathItem
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *lowV2.PathItem
|
||||
}
|
||||
|
||||
// NewPathItem will create a new high-level PathItem from a low-level one. All paths are built out asynchronously.
|
||||
func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
func NewPathItem(pathItem *lowV2.PathItem) *PathItem {
|
||||
p := new(PathItem)
|
||||
p.low = pathItem
|
||||
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
|
||||
@@ -42,7 +47,7 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
}
|
||||
p.Parameters = params
|
||||
}
|
||||
var buildOperation = func(method string, op *low.Operation) *Operation {
|
||||
buildOperation := func(method string, op *lowV2.Operation) *Operation {
|
||||
return NewOperation(op)
|
||||
}
|
||||
|
||||
@@ -50,49 +55,49 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
if !pathItem.Get.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Get = buildOperation(low.GetLabel, pathItem.Get.Value)
|
||||
p.Get = buildOperation(lowV2.GetLabel, pathItem.Get.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if !pathItem.Put.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Put = buildOperation(low.PutLabel, pathItem.Put.Value)
|
||||
p.Put = buildOperation(lowV2.PutLabel, pathItem.Put.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if !pathItem.Post.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Post = buildOperation(low.PostLabel, pathItem.Post.Value)
|
||||
p.Post = buildOperation(lowV2.PostLabel, pathItem.Post.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if !pathItem.Patch.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Patch = buildOperation(low.PatchLabel, pathItem.Patch.Value)
|
||||
p.Patch = buildOperation(lowV2.PatchLabel, pathItem.Patch.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if !pathItem.Delete.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Delete = buildOperation(low.DeleteLabel, pathItem.Delete.Value)
|
||||
p.Delete = buildOperation(lowV2.DeleteLabel, pathItem.Delete.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if !pathItem.Head.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Head = buildOperation(low.HeadLabel, pathItem.Head.Value)
|
||||
p.Head = buildOperation(lowV2.HeadLabel, pathItem.Head.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
if !pathItem.Options.IsEmpty() {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
p.Options = buildOperation(low.OptionsLabel, pathItem.Options.Value)
|
||||
p.Options = buildOperation(lowV2.OptionsLabel, pathItem.Options.Value)
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
@@ -101,32 +106,65 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
}
|
||||
|
||||
// GoLow returns the low-level PathItem used to create the high-level one.
|
||||
func (p *PathItem) GoLow() *low.PathItem {
|
||||
func (p *PathItem) GoLow() *lowV2.PathItem {
|
||||
return p.low
|
||||
}
|
||||
|
||||
func (p *PathItem) GetOperations() map[string]*Operation {
|
||||
o := make(map[string]*Operation)
|
||||
func (p *PathItem) GetOperations() *orderedmap.Map[string, *Operation] {
|
||||
o := orderedmap.New[string, *Operation]()
|
||||
|
||||
// TODO: this is a bit of a hack, but it works for now. We might just want to actually pull the data out of the document as a map and split it into the individual operations
|
||||
|
||||
type op struct {
|
||||
name string
|
||||
op *Operation
|
||||
line int
|
||||
}
|
||||
|
||||
getLine := func(field string, idx int) int {
|
||||
if p.GoLow() == nil {
|
||||
return idx
|
||||
}
|
||||
|
||||
l, ok := reflect.ValueOf(p.GoLow()).Elem().FieldByName(field).Interface().(low.NodeReference[*lowV2.Operation])
|
||||
if !ok || l.GetKeyNode() == nil {
|
||||
return idx
|
||||
}
|
||||
|
||||
return l.GetKeyNode().Line
|
||||
}
|
||||
|
||||
ops := []op{}
|
||||
|
||||
if p.Get != nil {
|
||||
o[low.GetLabel] = p.Get
|
||||
ops = append(ops, op{name: lowV2.GetLabel, op: p.Get, line: getLine("Get", -7)})
|
||||
}
|
||||
if p.Put != nil {
|
||||
o[low.PutLabel] = p.Put
|
||||
ops = append(ops, op{name: lowV2.PutLabel, op: p.Put, line: getLine("Put", -6)})
|
||||
}
|
||||
if p.Post != nil {
|
||||
o[low.PostLabel] = p.Post
|
||||
ops = append(ops, op{name: lowV2.PostLabel, op: p.Post, line: getLine("Post", -5)})
|
||||
}
|
||||
if p.Delete != nil {
|
||||
o[low.DeleteLabel] = p.Delete
|
||||
ops = append(ops, op{name: lowV2.DeleteLabel, op: p.Delete, line: getLine("Delete", -4)})
|
||||
}
|
||||
if p.Options != nil {
|
||||
o[low.OptionsLabel] = p.Options
|
||||
ops = append(ops, op{name: lowV2.OptionsLabel, op: p.Options, line: getLine("Options", -3)})
|
||||
}
|
||||
if p.Head != nil {
|
||||
o[low.HeadLabel] = p.Head
|
||||
ops = append(ops, op{name: lowV2.HeadLabel, op: p.Head, line: getLine("Head", -2)})
|
||||
}
|
||||
if p.Patch != nil {
|
||||
o[low.PatchLabel] = p.Patch
|
||||
ops = append(ops, op{name: lowV2.PatchLabel, op: p.Patch, line: getLine("Patch", -1)})
|
||||
}
|
||||
|
||||
slices.SortStableFunc(ops, func(a op, b op) int {
|
||||
return a.line - b.line
|
||||
})
|
||||
|
||||
for _, op := range ops {
|
||||
o.Set(op.name, op.op)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
@@ -5,16 +5,17 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPathItem_GetOperations(t *testing.T) {
|
||||
|
||||
yml := `get:
|
||||
description: get
|
||||
put:
|
||||
@@ -41,5 +42,5 @@ options:
|
||||
|
||||
r := NewPathItem(&n)
|
||||
|
||||
assert.Len(t, r.GetOperations(), 7)
|
||||
assert.Equal(t, 7, orderedmap.Len(r.GetOperations()))
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v2low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Paths represents a high-level Swagger / OpenAPI Paths object, backed by a low-level one.
|
||||
type Paths struct {
|
||||
PathItems orderedmap.Map[string, *PathItem]
|
||||
Extensions map[string]any
|
||||
PathItems *orderedmap.Map[string, *PathItem]
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *v2low.Paths
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
|
||||
@@ -16,9 +17,9 @@ import (
|
||||
type Response struct {
|
||||
Description string
|
||||
Schema *base.SchemaProxy
|
||||
Headers orderedmap.Map[string, *Header]
|
||||
Headers *orderedmap.Map[string, *Header]
|
||||
Examples *Example
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Response
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,19 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"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"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Responses is a high-level representation of a Swagger / OpenAPI 2 Responses object, backed by a low level one.
|
||||
type Responses struct {
|
||||
Codes orderedmap.Map[string, *Response]
|
||||
Codes *orderedmap.Map[string, *Response]
|
||||
Default *Response
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Responses
|
||||
}
|
||||
|
||||
@@ -32,7 +34,7 @@ func NewResponses(responses *low.Responses) *Responses {
|
||||
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,
|
||||
key: pair.Key().Value,
|
||||
result: NewResponse(pair.Value().Value),
|
||||
}, nil
|
||||
}
|
||||
@@ -40,7 +42,7 @@ func NewResponses(responses *low.Responses) *Responses {
|
||||
resp.Set(value.key, value.result)
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.TranslateMapParallel(responses.Codes, translateFunc, resultFunc)
|
||||
_ = datamodel.TranslateMapParallel(responses.Codes, translateFunc, resultFunc)
|
||||
r.Codes = resp
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
// referenced to the ones defined here. It does not define global operation responses
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
type ResponsesDefinitions struct {
|
||||
Definitions orderedmap.Map[string, *Response]
|
||||
Definitions *orderedmap.Map[string, *Response]
|
||||
low *low.ResponsesDefinitions
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ func NewResponsesDefinitions(responsesDefinitions *low.ResponsesDefinitions) *Re
|
||||
return nil
|
||||
}
|
||||
|
||||
_ = orderedmap.TranslateMapParallel(responsesDefinitions.Definitions, translateFunc, resultFunc)
|
||||
_ = datamodel.TranslateMapParallel(responsesDefinitions.Definitions, translateFunc, resultFunc)
|
||||
rd.Definitions = responses
|
||||
return rd
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
// Scopes lists the available scopes for an OAuth2 security scheme.
|
||||
// - https://swagger.io/specification/v2/#scopesObject
|
||||
type Scopes struct {
|
||||
Values orderedmap.Map[string, string]
|
||||
Values *orderedmap.Map[string, string]
|
||||
low *low.Scopes
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
// 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 orderedmap.Map[string, *SecurityScheme]
|
||||
Definitions *orderedmap.Map[string, *SecurityScheme]
|
||||
low *low.SecurityDefinitions
|
||||
}
|
||||
|
||||
@@ -35,12 +36,8 @@ func NewSecurityDefinitions(definitions *low.SecurityDefinitions) *SecurityDefin
|
||||
schemes.Set(value.key, value.result)
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.TranslateMapParallel(definitions.Definitions, translateFunc, resultFunc)
|
||||
_ = datamodel.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
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package v2
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// SecurityScheme is a high-level representation of a Swagger / OpenAPI 2 SecurityScheme object
|
||||
@@ -24,7 +26,7 @@ type SecurityScheme struct {
|
||||
AuthorizationUrl string
|
||||
TokenUrl string
|
||||
Scopes *Scopes
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.SecurityScheme
|
||||
}
|
||||
|
||||
|
||||
@@ -15,11 +15,12 @@ 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"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification.
|
||||
type Swagger struct {
|
||||
|
||||
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
|
||||
Swagger string
|
||||
|
||||
@@ -90,7 +91,7 @@ type Swagger struct {
|
||||
ExternalDocs *base.ExternalDoc
|
||||
|
||||
// Extensions contains all custom extensions defined for the top-level document.
|
||||
Extensions map[string]any
|
||||
Extensions *orderedmap.Map[string, *yaml.Node]
|
||||
low *low.Swagger
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,12 @@ package v2
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
var doc *v2.Swagger
|
||||
@@ -42,8 +41,12 @@ func BenchmarkNewDocument(b *testing.B) {
|
||||
func TestNewSwaggerDocument_Base(t *testing.T) {
|
||||
initTest()
|
||||
highDoc := NewSwaggerDocument(doc)
|
||||
|
||||
var xPet bool
|
||||
_ = highDoc.Extensions.GetOrZero("x-pet").Decode(&xPet)
|
||||
|
||||
assert.Equal(t, "2.0", highDoc.Swagger)
|
||||
assert.True(t, highDoc.Extensions["x-pet"].(bool))
|
||||
assert.True(t, xPet)
|
||||
assert.Equal(t, "petstore.swagger.io", highDoc.Host)
|
||||
assert.Equal(t, "/v2", highDoc.BasePath)
|
||||
assert.Len(t, highDoc.Schemes, 2)
|
||||
@@ -56,7 +59,6 @@ func TestNewSwaggerDocument_Base(t *testing.T) {
|
||||
wentLow := highDoc.GoLow()
|
||||
assert.Equal(t, 16, wentLow.Host.ValueNode.Line)
|
||||
assert.Equal(t, 7, wentLow.Host.ValueNode.Column)
|
||||
|
||||
}
|
||||
|
||||
func TestNewSwaggerDocument_Info(t *testing.T) {
|
||||
@@ -82,11 +84,15 @@ func TestNewSwaggerDocument_Parameters(t *testing.T) {
|
||||
initTest()
|
||||
highDoc := NewSwaggerDocument(doc)
|
||||
params := highDoc.Parameters
|
||||
|
||||
var xChicken string
|
||||
_ = params.Definitions.GetOrZero("simpleParam").Extensions.GetOrZero("x-chicken").Decode(&xChicken)
|
||||
|
||||
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"])
|
||||
assert.Equal(t, "nuggets", xChicken)
|
||||
|
||||
wentLow := params.GoLow()
|
||||
assert.Equal(t, 20, wentLow.FindParameter("simpleParam").ValueNode.Line)
|
||||
@@ -95,7 +101,6 @@ func TestNewSwaggerDocument_Parameters(t *testing.T) {
|
||||
wentLower := params.Definitions.GetOrZero("simpleParam").GoLow()
|
||||
assert.Equal(t, 21, wentLower.Name.ValueNode.Line)
|
||||
assert.Equal(t, 11, wentLower.Name.ValueNode.Column)
|
||||
|
||||
}
|
||||
|
||||
func TestNewSwaggerDocument_Security(t *testing.T) {
|
||||
@@ -107,7 +112,6 @@ func TestNewSwaggerDocument_Security(t *testing.T) {
|
||||
wentLow := highDoc.Security[0].GoLow()
|
||||
assert.Equal(t, 25, wentLow.Requirements.ValueNode.Line)
|
||||
assert.Equal(t, 5, wentLow.Requirements.ValueNode.Column)
|
||||
|
||||
}
|
||||
|
||||
func TestNewSwaggerDocument_Definitions_Security(t *testing.T) {
|
||||
@@ -140,22 +144,32 @@ func TestNewSwaggerDocument_Definitions_Responses(t *testing.T) {
|
||||
assert.Equal(t, 2, orderedmap.Len(highDoc.Responses.Definitions))
|
||||
|
||||
defs := highDoc.Responses.Definitions
|
||||
assert.Equal(t, "morning", defs.GetOrZero("200").Extensions["x-coffee"])
|
||||
|
||||
var xCoffee string
|
||||
_ = defs.GetOrZero("200").Extensions.GetOrZero("x-coffee").Decode(&xCoffee)
|
||||
|
||||
assert.Equal(t, "morning", xCoffee)
|
||||
assert.Equal(t, "OK", defs.GetOrZero("200").Description)
|
||||
assert.Equal(t, "a generic API response object",
|
||||
defs.GetOrZero("200").Schema.Schema().Description)
|
||||
assert.Equal(t, 3, orderedmap.Len(defs.GetOrZero("200").Examples.Values))
|
||||
|
||||
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"])
|
||||
var appJson map[string]interface{}
|
||||
_ = defs.GetOrZero("200").Examples.Values.GetOrZero("application/json").Decode(&appJson)
|
||||
|
||||
exp = defs.GetOrZero("200").Examples.Values.GetOrZero("text/xml")
|
||||
assert.Len(t, exp.([]interface{}), 3)
|
||||
assert.Equal(t, "two", exp.([]interface{})[1])
|
||||
assert.Len(t, appJson, 2)
|
||||
assert.Equal(t, "two", appJson["one"])
|
||||
|
||||
exp = defs.GetOrZero("200").Examples.Values.GetOrZero("text/plain")
|
||||
assert.Equal(t, "something else.", exp)
|
||||
var textXml []interface{}
|
||||
_ = defs.GetOrZero("200").Examples.Values.GetOrZero("text/xml").Decode(&textXml)
|
||||
|
||||
assert.Len(t, textXml, 3)
|
||||
assert.Equal(t, "two", textXml[1])
|
||||
|
||||
var textPlain string
|
||||
_ = defs.GetOrZero("200").Examples.Values.GetOrZero("text/plain").Decode(&textPlain)
|
||||
|
||||
assert.Equal(t, "something else.", textPlain)
|
||||
|
||||
expWentLow := defs.GetOrZero("200").Examples.GoLow()
|
||||
assert.Equal(t, 702, expWentLow.FindExample("application/json").ValueNode.Line)
|
||||
@@ -168,10 +182,13 @@ func TestNewSwaggerDocument_Definitions_Responses(t *testing.T) {
|
||||
assert.Len(t, y.Enum, 2)
|
||||
x := y.Items
|
||||
|
||||
var def string
|
||||
_ = x.Default.Decode(&def)
|
||||
|
||||
assert.Equal(t, "something", x.Format)
|
||||
assert.Equal(t, "array", x.Type)
|
||||
assert.Equal(t, "csv", x.CollectionFormat)
|
||||
assert.Equal(t, "cake", x.Default)
|
||||
assert.Equal(t, "cake", def)
|
||||
assert.Equal(t, 10, x.Maximum)
|
||||
assert.Equal(t, 1, x.Minimum)
|
||||
assert.True(t, x.ExclusiveMaximum)
|
||||
@@ -198,7 +215,6 @@ func TestNewSwaggerDocument_Definitions(t *testing.T) {
|
||||
|
||||
wentLow := highDoc.Definitions.GoLow()
|
||||
assert.Equal(t, 848, wentLow.FindSchema("User").ValueNode.Line)
|
||||
|
||||
}
|
||||
|
||||
func TestNewSwaggerDocument_Paths(t *testing.T) {
|
||||
@@ -207,7 +223,14 @@ func TestNewSwaggerDocument_Paths(t *testing.T) {
|
||||
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"])
|
||||
|
||||
var xPotato string
|
||||
_ = upload.Extensions.GetOrZero("x-potato").Decode(&xPotato)
|
||||
|
||||
var paramEnum0 string
|
||||
_ = upload.Post.Parameters[0].Enum[0].Decode(¶mEnum0)
|
||||
|
||||
assert.Equal(t, "man", xPotato)
|
||||
assert.Nil(t, upload.Get)
|
||||
assert.Nil(t, upload.Put)
|
||||
assert.Nil(t, upload.Patch)
|
||||
@@ -238,8 +261,11 @@ func TestNewSwaggerDocument_Paths(t *testing.T) {
|
||||
assert.Equal(t, 20, *upload.Post.Parameters[0].MaxItems)
|
||||
assert.True(t, *upload.Post.Parameters[0].UniqueItems)
|
||||
assert.Len(t, upload.Post.Parameters[0].Enum, 2)
|
||||
assert.Equal(t, "hello", upload.Post.Parameters[0].Enum[0])
|
||||
def := upload.Post.Parameters[0].Default.(map[string]interface{})
|
||||
assert.Equal(t, "hello", paramEnum0)
|
||||
|
||||
var def map[string]any
|
||||
_ = upload.Post.Parameters[0].Default.Decode(&def)
|
||||
|
||||
assert.Equal(t, "here", def["something"])
|
||||
|
||||
assert.Equal(t, "https://pb33f.io", upload.Post.ExternalDocs.URL)
|
||||
@@ -257,11 +283,9 @@ func TestNewSwaggerDocument_Paths(t *testing.T) {
|
||||
|
||||
wentLowest := upload.Post.GoLow()
|
||||
assert.Equal(t, 55, wentLowest.Tags.KeyNode.Line)
|
||||
|
||||
}
|
||||
|
||||
func TestNewSwaggerDocument_Responses(t *testing.T) {
|
||||
|
||||
initTest()
|
||||
highDoc := NewSwaggerDocument(doc)
|
||||
upload := highDoc.Paths.PathItems.GetOrZero("/pet/{petId}/uploadImage").Post
|
||||
@@ -278,5 +302,4 @@ func TestNewSwaggerDocument_Responses(t *testing.T) {
|
||||
wentLower := OK.GoLow()
|
||||
assert.Equal(t, 107, wentLower.Schema.KeyNode.Line)
|
||||
assert.Equal(t, 11, wentLower.Schema.KeyNode.Column)
|
||||
|
||||
}
|
||||
|
||||
@@ -21,8 +21,8 @@ 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 orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Expression *orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Callback
|
||||
}
|
||||
|
||||
@@ -31,13 +31,11 @@ func NewCallback(lowCallback *low.Callback) *Callback {
|
||||
n := new(Callback)
|
||||
n.low = lowCallback
|
||||
n.Expression = orderedmap.New[string, *PathItem]()
|
||||
for pair := orderedmap.First(lowCallback.Expression.Value); pair != nil; pair = pair.Next() {
|
||||
for pair := orderedmap.First(lowCallback.Expression); 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 {
|
||||
n.Extensions[k.Value] = v.Value
|
||||
}
|
||||
|
||||
n.Extensions = high.ExtractExtensions(lowCallback.Extensions)
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -51,36 +49,50 @@ func (c *Callback) GoLowUntyped() any {
|
||||
return c.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Callback object as a byte slice.
|
||||
// Render will return a YAML representation of the Paths object as a byte slice.
|
||||
func (c *Callback) Render() ([]byte, error) {
|
||||
return yaml.Marshal(c)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Callback object.
|
||||
func (c *Callback) RenderInline() ([]byte, error) {
|
||||
d, _ := c.MarshalYAMLInline()
|
||||
return yaml.Marshal(d)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Paths object.
|
||||
func (c *Callback) MarshalYAML() (interface{}, error) {
|
||||
// map keys correctly.
|
||||
m := utils.CreateEmptyMapNode()
|
||||
type cbItem struct {
|
||||
cb *PathItem
|
||||
exp string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
type pathItem struct {
|
||||
pi *PathItem
|
||||
path string
|
||||
line int
|
||||
style yaml.Style
|
||||
rendered *yaml.Node
|
||||
}
|
||||
var mapped []*cbItem
|
||||
var mapped []*pathItem
|
||||
|
||||
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.
|
||||
k := pair.Key()
|
||||
pi := pair.Value()
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
var style yaml.Style
|
||||
if c.low != nil {
|
||||
for lPair := orderedmap.First(c.low.Expression.Value); lPair != nil; lPair = lPair.Next() {
|
||||
if lPair.Key().Value == pair.Key() {
|
||||
ln = lPair.Key().KeyNode.Line
|
||||
lpi := c.low.FindExpression(k)
|
||||
if lpi != nil {
|
||||
ln = lpi.ValueNode.Line
|
||||
}
|
||||
|
||||
for pair := orderedmap.First(c.low.Expression); pair != nil; pair = pair.Next() {
|
||||
if pair.Key().Value == k {
|
||||
style = pair.Key().KeyNode.Style
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &cbItem{pair.Value(), pair.Key(), ln, nil})
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, style, nil})
|
||||
}
|
||||
|
||||
// extract extensions
|
||||
nb := high.NewNodeBuilder(c, c.low)
|
||||
extNode := nb.Render()
|
||||
if extNode != nil && extNode.Content != nil {
|
||||
@@ -90,23 +102,101 @@ func (c *Callback) MarshalYAML() (interface{}, error) {
|
||||
label = extNode.Content[u].Value
|
||||
continue
|
||||
}
|
||||
mapped = append(mapped, &cbItem{nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u]})
|
||||
mapped = append(mapped, &pathItem{
|
||||
nil, label,
|
||||
extNode.Content[u].Line, 0, extNode.Content[u],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].cb != nil {
|
||||
rendered, _ := mapped[j].cb.MarshalYAML()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].exp))
|
||||
for _, mp := range mapped {
|
||||
if mp.pi != nil {
|
||||
rendered, _ := mp.pi.MarshalYAML()
|
||||
|
||||
kn := utils.CreateStringNode(mp.path)
|
||||
kn.Style = mp.style
|
||||
|
||||
m.Content = append(m.Content, kn)
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mapped[j].ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].exp))
|
||||
m.Content = append(m.Content, mapped[j].ext)
|
||||
if mp.rendered != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mp.path))
|
||||
m.Content = append(m.Content, mp.rendered)
|
||||
}
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *Callback) MarshalYAMLInline() (interface{}, error) {
|
||||
// map keys correctly.
|
||||
m := utils.CreateEmptyMapNode()
|
||||
type pathItem struct {
|
||||
pi *PathItem
|
||||
path string
|
||||
line int
|
||||
style yaml.Style
|
||||
rendered *yaml.Node
|
||||
}
|
||||
var mapped []*pathItem
|
||||
|
||||
for pair := orderedmap.First(c.Expression); 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.
|
||||
var style yaml.Style
|
||||
if c.low != nil {
|
||||
lpi := c.low.FindExpression(k)
|
||||
if lpi != nil {
|
||||
ln = lpi.ValueNode.Line
|
||||
}
|
||||
|
||||
for pair := orderedmap.First(c.low.Expression); pair != nil; pair = pair.Next() {
|
||||
if pair.Key().Value == k {
|
||||
style = pair.Key().KeyNode.Style
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, style, nil})
|
||||
}
|
||||
|
||||
nb := high.NewNodeBuilder(c, c.low)
|
||||
nb.Resolve = true
|
||||
extNode := nb.Render()
|
||||
if extNode != nil && extNode.Content != nil {
|
||||
var label string
|
||||
for u := range extNode.Content {
|
||||
if u%2 == 0 {
|
||||
label = extNode.Content[u].Value
|
||||
continue
|
||||
}
|
||||
mapped = append(mapped, &pathItem{
|
||||
nil, label,
|
||||
extNode.Content[u].Line, 0, extNode.Content[u],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for _, mp := range mapped {
|
||||
if mp.pi != nil {
|
||||
rendered, _ := mp.pi.MarshalYAMLInline()
|
||||
|
||||
kn := utils.CreateStringNode(mp.path)
|
||||
kn.Style = mp.style
|
||||
|
||||
m.Content = append(m.Content, kn)
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mp.rendered != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mp.path))
|
||||
m.Content = append(m.Content, mp.rendered)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,14 @@ import (
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestCallback_MarshalYAML(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-burgers", utils.CreateStringNode("why not?"))
|
||||
|
||||
cb := &Callback{
|
||||
Expression: orderedmap.ToOrderedMap(map[string]*PathItem{
|
||||
@@ -31,9 +34,7 @@ func TestCallback_MarshalYAML(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}),
|
||||
Extensions: map[string]any{
|
||||
"x-burgers": "why not?",
|
||||
},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := cb.Render()
|
||||
@@ -43,7 +44,10 @@ func TestCallback_MarshalYAML(t *testing.T) {
|
||||
|
||||
// mutate
|
||||
cb.Expression.GetOrZero("https://pb33f.io").Get.OperationId = "blim-blam"
|
||||
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
|
||||
|
||||
ext = orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-burgers", utils.CreateStringNode("yes please!"))
|
||||
cb.Extensions = ext
|
||||
|
||||
rend, _ = cb.Render()
|
||||
// there is no way to determine order in brand new maps, so we have to check length.
|
||||
@@ -72,7 +76,10 @@ func TestCallback_MarshalYAML(t *testing.T) {
|
||||
|
||||
r := NewCallback(&n)
|
||||
|
||||
assert.Equal(t, "please", r.Extensions["x-break-everything"])
|
||||
var xBreakEverything string
|
||||
_ = r.Extensions.GetOrZero("x-break-everything").Decode(&xBreakEverything)
|
||||
|
||||
assert.Equal(t, "please", xBreakEverything)
|
||||
|
||||
rend, _ = r.Render()
|
||||
assert.Equal(t, k, strings.TrimSpace(string(rend)))
|
||||
|
||||
@@ -22,16 +22,16 @@ 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 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:"-"`
|
||||
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 *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Components
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ type Components struct {
|
||||
func NewComponents(comp *low.Components) *Components {
|
||||
c := new(Components)
|
||||
c.low = comp
|
||||
if len(comp.Extensions) > 0 {
|
||||
if orderedmap.Len(comp.Extensions) > 0 {
|
||||
c.Extensions = high.ExtractExtensions(comp.Extensions)
|
||||
}
|
||||
cbMap := orderedmap.New[string, *Callback]()
|
||||
@@ -109,12 +109,12 @@ func NewComponents(comp *low.Components) *Components {
|
||||
|
||||
// contains a component build result.
|
||||
type componentResult[T any] struct {
|
||||
res T
|
||||
key string
|
||||
res T
|
||||
key string
|
||||
}
|
||||
|
||||
// buildComponent builds component structs from low level structs.
|
||||
func buildComponent[IN any, OUT any](inMap orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]], outMap orderedmap.Map[string, OUT], translateItem func(IN) OUT) {
|
||||
func buildComponent[IN any, OUT any](inMap *orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[IN]], outMap *orderedmap.Map[string, OUT], translateItem func(IN) OUT) {
|
||||
translateFunc := func(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
|
||||
}
|
||||
@@ -126,7 +126,7 @@ func buildComponent[IN any, OUT any](inMap orderedmap.Map[lowmodel.KeyReference[
|
||||
}
|
||||
|
||||
// buildSchema builds a schema from low level structs.
|
||||
func buildSchema(inMap orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*base.SchemaProxy]], outMap orderedmap.Map[string, *highbase.SchemaProxy]) {
|
||||
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
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
|
||||
// Document represents a high-level OpenAPI 3 document (both 3.0 & 3.1). A Document is the root of the specification.
|
||||
type Document struct {
|
||||
|
||||
// Version is the version of OpenAPI being used, extracted from the 'openapi: x.x.x' definition.
|
||||
// This is not a standard property of the OpenAPI model, it's a convenience mechanism only.
|
||||
Version string `json:"openapi,omitempty" yaml:"openapi,omitempty"`
|
||||
@@ -54,7 +53,7 @@ type Document struct {
|
||||
// an empty security requirement ({}) can be included in the array.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
|
||||
Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"`
|
||||
//Security []*base.SecurityRequirement `json:"-" yaml:"-"`
|
||||
// Security []*base.SecurityRequirement `json:"-" yaml:"-"`
|
||||
|
||||
// Tags is a slice of base.Tag instances defined by the specification
|
||||
// A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on
|
||||
@@ -69,7 +68,7 @@ type Document struct {
|
||||
ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
|
||||
// Extensions contains all custom extensions defined for the top-level document.
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
|
||||
// JsonSchemaDialect is a 3.1+ property that sets the dialect to use for validating *base.Schema definitions
|
||||
// The default value for the $schema keyword within Schema Objects contained within this OAS document.
|
||||
@@ -83,7 +82,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 orderedmap.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
|
||||
@@ -122,7 +121,7 @@ func NewDocument(document *low.Document) *Document {
|
||||
if !document.ExternalDocs.IsEmpty() {
|
||||
d.ExternalDocs = base.NewExternalDoc(document.ExternalDocs.Value)
|
||||
}
|
||||
if len(document.Extensions) > 0 {
|
||||
if orderedmap.Len(document.Extensions) > 0 {
|
||||
d.Extensions = high.ExtractExtensions(document.Extensions)
|
||||
}
|
||||
if !document.Components.IsEmpty() {
|
||||
|
||||
@@ -51,7 +51,11 @@ func BenchmarkNewDocument(b *testing.B) {
|
||||
func TestNewDocument_Extensions(t *testing.T) {
|
||||
initTest()
|
||||
h := NewDocument(lowDoc)
|
||||
assert.Equal(t, "darkside", h.Extensions["x-something-something"])
|
||||
|
||||
var xSomethingSomething string
|
||||
_ = h.Extensions.GetOrZero("x-something-something").Decode(&xSomethingSomething)
|
||||
|
||||
assert.Equal(t, "darkside", xSomethingSomething)
|
||||
}
|
||||
|
||||
func TestNewDocument_ExternalDocs(t *testing.T) {
|
||||
@@ -133,15 +137,28 @@ func TestNewDocument_Servers(t *testing.T) {
|
||||
func TestNewDocument_Tags(t *testing.T) {
|
||||
initTest()
|
||||
h := NewDocument(lowDoc)
|
||||
|
||||
var xInternalTing string
|
||||
_ = h.Tags[0].Extensions.GetOrZero("x-internal-ting").Decode(&xInternalTing)
|
||||
|
||||
var xInternalTong int64
|
||||
_ = h.Tags[0].Extensions.GetOrZero("x-internal-tong").Decode(&xInternalTong)
|
||||
|
||||
var xInternalTang float64
|
||||
_ = h.Tags[0].Extensions.GetOrZero("x-internal-tang").Decode(&xInternalTang)
|
||||
|
||||
assert.Len(t, h.Tags, 2)
|
||||
assert.Equal(t, "Burgers", h.Tags[0].Name)
|
||||
assert.Equal(t, "All kinds of yummy burgers.", h.Tags[0].Description)
|
||||
assert.Equal(t, "Find out more", h.Tags[0].ExternalDocs.Description)
|
||||
assert.Equal(t, "https://pb33f.io", h.Tags[0].ExternalDocs.URL)
|
||||
assert.Equal(t, "somethingSpecial", h.Tags[0].Extensions["x-internal-ting"])
|
||||
assert.Equal(t, int64(1), h.Tags[0].Extensions["x-internal-tong"])
|
||||
assert.Equal(t, 1.2, h.Tags[0].Extensions["x-internal-tang"])
|
||||
assert.True(t, h.Tags[0].Extensions["x-internal-tung"].(bool))
|
||||
assert.Equal(t, "somethingSpecial", xInternalTing)
|
||||
assert.Equal(t, int64(1), xInternalTong)
|
||||
assert.Equal(t, 1.2, xInternalTang)
|
||||
|
||||
var tung bool
|
||||
_ = h.Tags[0].Extensions.GetOrZero("x-internal-tung").Decode(&tung)
|
||||
assert.True(t, tung)
|
||||
|
||||
wentLow := h.Tags[1].GoLow()
|
||||
assert.Equal(t, 39, wentLow.Description.KeyNode.Line)
|
||||
@@ -191,7 +208,10 @@ func TestNewDocument_Components_Callbacks(t *testing.T) {
|
||||
h.Components.Callbacks.GetOrZero("BurgerCallback").GoLow().FindExpression("{$request.query.queryUrl}").ValueNode.Column,
|
||||
)
|
||||
|
||||
assert.Equal(t, "please", h.Components.Callbacks.GetOrZero("BurgerCallback").Extensions["x-break-everything"])
|
||||
var xBreakEverything string
|
||||
_ = h.Components.Callbacks.GetOrZero("BurgerCallback").Extensions.GetOrZero("x-break-everything").Decode(&xBreakEverything)
|
||||
|
||||
assert.Equal(t, "please", xBreakEverything)
|
||||
|
||||
for pair := orderedmap.First(h.Components.GoLow().Callbacks.Value); pair != nil; pair = pair.Next() {
|
||||
if pair.Key().Value == "BurgerCallback" {
|
||||
@@ -209,7 +229,9 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
|
||||
goLow := h.Components.GoLow()
|
||||
|
||||
a := h.Components.Schemas.GetOrZero("Error")
|
||||
abcd := a.Schema().Properties.GetOrZero("message").Schema().Example
|
||||
|
||||
var abcd string
|
||||
_ = a.Schema().Properties.GetOrZero("message").Schema().Example.Decode(&abcd)
|
||||
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)
|
||||
@@ -218,13 +240,21 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
|
||||
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.GetOrZero("fries").Schema().Description)
|
||||
assert.Equal(t, int64(2), b.Schema().Properties.GetOrZero("numPatties").Schema().Example)
|
||||
|
||||
var numPattiesExample int64
|
||||
_ = b.Schema().Properties.GetOrZero("numPatties").Schema().Example.Decode(&numPattiesExample)
|
||||
|
||||
assert.Equal(t, int64(2), numPattiesExample)
|
||||
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.GetOrZero("Fries")
|
||||
assert.Equal(t, "salt", f.Schema().Properties.GetOrZero("seasoning").Schema().Items.A.Schema().Example)
|
||||
|
||||
var seasoningExample string
|
||||
_ = f.Schema().Properties.GetOrZero("seasoning").Schema().Items.A.Schema().Example.Decode(&seasoningExample)
|
||||
|
||||
assert.Equal(t, "salt", seasoningExample)
|
||||
assert.Len(t, f.Schema().Properties.GetOrZero("favoriteDrink").Schema().Properties.GetOrZero("drinkType").Schema().Enum, 1)
|
||||
|
||||
d := h.Components.Schemas.GetOrZero("Drink")
|
||||
@@ -240,7 +270,11 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
|
||||
assert.Equal(t, 523, pl.Schema().XML.GoLow().Name.ValueNode.Line)
|
||||
|
||||
ext := h.Components.Extensions
|
||||
assert.Equal(t, "loud", ext["x-screaming-baby"])
|
||||
|
||||
var xScreamingBaby string
|
||||
_ = ext.GetOrZero("x-screaming-baby").Decode(&xScreamingBaby)
|
||||
|
||||
assert.Equal(t, "loud", xScreamingBaby)
|
||||
}
|
||||
|
||||
func TestNewDocument_Components_Headers(t *testing.T) {
|
||||
@@ -319,8 +353,12 @@ func TestNewDocument_Components_Parameters(t *testing.T) {
|
||||
assert.Equal(t, "burgerHeader", bh.Name)
|
||||
assert.Equal(t, 392, bh.GoLow().Name.KeyNode.Line)
|
||||
assert.Equal(t, 2, orderedmap.Len(bh.Schema.Schema().Properties))
|
||||
assert.Equal(t, "big-mac", bh.Example)
|
||||
assert.True(t, bh.Required)
|
||||
|
||||
var example string
|
||||
_ = bh.Example.Decode(&example)
|
||||
|
||||
assert.Equal(t, "big-mac", example)
|
||||
assert.True(t, *bh.Required)
|
||||
assert.Equal(
|
||||
t,
|
||||
"this is a header",
|
||||
@@ -341,8 +379,12 @@ func TestNewDocument_Paths(t *testing.T) {
|
||||
func testBurgerShop(t *testing.T, h *Document, checkLines bool) {
|
||||
burgersOp := h.Paths.PathItems.GetOrZero("/burgers")
|
||||
|
||||
assert.Len(t, burgersOp.GetOperations(), 1)
|
||||
assert.Equal(t, "meaty", burgersOp.Extensions["x-burger-meta"])
|
||||
assert.Equal(t, 1, burgersOp.GetOperations().Len())
|
||||
|
||||
var xBurgerMeta string
|
||||
_ = burgersOp.Extensions.GetOrZero("x-burger-meta").Decode(&xBurgerMeta)
|
||||
|
||||
assert.Equal(t, "meaty", xBurgerMeta)
|
||||
assert.Nil(t, burgersOp.Get)
|
||||
assert.Nil(t, burgersOp.Put)
|
||||
assert.Nil(t, burgersOp.Patch)
|
||||
|
||||
@@ -14,11 +14,11 @@ import (
|
||||
// Encoding represents an OpenAPI 3+ Encoding object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||
type Encoding struct {
|
||||
ContentType string `json:"contentType,omitempty" yaml:"contentType,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"`
|
||||
ContentType string `json:"contentType,omitempty" yaml:"contentType,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"`
|
||||
low *low.Encoding
|
||||
}
|
||||
|
||||
@@ -28,7 +28,9 @@ func NewEncoding(encoding *low.Encoding) *Encoding {
|
||||
e.low = encoding
|
||||
e.ContentType = encoding.ContentType.Value
|
||||
e.Style = encoding.Style.Value
|
||||
e.Explode = &encoding.Explode.Value
|
||||
if !encoding.Explode.IsEmpty() {
|
||||
e.Explode = &encoding.Explode.Value
|
||||
}
|
||||
e.AllowReserved = encoding.AllowReserved.Value
|
||||
e.Headers = ExtractHeaders(encoding.Headers.Value)
|
||||
return e
|
||||
@@ -56,7 +58,7 @@ 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 orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Encoding]]) orderedmap.Map[string, *Encoding] {
|
||||
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))
|
||||
|
||||
@@ -16,18 +16,18 @@ import (
|
||||
// Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
||||
type Header struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,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"`
|
||||
Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,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:"-"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,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"`
|
||||
Schema *highbase.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example *yaml.Node `json:"example,omitempty" yaml:"example,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 *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Header
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ func NewHeader(header *low.Header) *Header {
|
||||
h.Content = ExtractContent(header.Content.Value)
|
||||
h.Example = header.Example.Value
|
||||
h.Examples = highbase.ExtractExamples(header.Examples.Value)
|
||||
h.Extensions = high.ExtractExtensions(header.Extensions)
|
||||
return h
|
||||
}
|
||||
|
||||
@@ -66,7 +67,7 @@ func (h *Header) GoLowUntyped() any {
|
||||
}
|
||||
|
||||
// ExtractHeaders will extract a hard to navigate low-level Header map, into simple high-level one.
|
||||
func ExtractHeaders(elements orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.Header]]) orderedmap.Map[string, *Header] {
|
||||
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))
|
||||
|
||||
@@ -9,10 +9,14 @@ import (
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestHeader_MarshalYAML(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-burgers", utils.CreateStringNode("why not?"))
|
||||
|
||||
header := &Header{
|
||||
Description: "A header",
|
||||
@@ -22,11 +26,11 @@ func TestHeader_MarshalYAML(t *testing.T) {
|
||||
Style: "simple",
|
||||
Explode: true,
|
||||
AllowReserved: true,
|
||||
Example: "example",
|
||||
Example: utils.CreateStringNode("example"),
|
||||
Examples: orderedmap.ToOrderedMap(map[string]*base.Example{
|
||||
"example": {Value: "example"},
|
||||
"example": {Value: utils.CreateStringNode("example")},
|
||||
}),
|
||||
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := header.Render()
|
||||
@@ -45,5 +49,4 @@ examples:
|
||||
x-burgers: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
@@ -23,13 +23,13 @@ import (
|
||||
// in an operation and using them as parameters while invoking the linked operation.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
||||
type Link struct {
|
||||
OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
|
||||
OperationId string `json:"operationId,omitempty" yaml:"operationId,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"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
|
||||
OperationId string `json:"operationId,omitempty" yaml:"operationId,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"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Link
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
@@ -17,11 +18,11 @@ import (
|
||||
// Each Media Type Object provides schema and examples for the media type identified by its key.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
||||
type MediaType struct {
|
||||
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,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:"-"`
|
||||
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,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 *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.MediaType
|
||||
}
|
||||
|
||||
@@ -73,7 +74,7 @@ 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 orderedmap.Map[lowmodel.KeyReference[string], lowmodel.ValueReference[*low.MediaType]]) orderedmap.Map[string, *MediaType] {
|
||||
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]{
|
||||
@@ -85,6 +86,6 @@ func ExtractContent(elements orderedmap.Map[lowmodel.KeyReference[string], lowmo
|
||||
extracted.Set(value.key, value.result)
|
||||
return nil
|
||||
}
|
||||
_ = orderedmap.TranslateMapParallel(elements, translateFunc, resultFunc)
|
||||
_ = datamodel.TranslateMapParallel(elements, translateFunc, resultFunc)
|
||||
return extracted
|
||||
}
|
||||
|
||||
@@ -35,8 +35,7 @@ func TestMediaType_MarshalYAMLInline(t *testing.T) {
|
||||
yml, _ := mt.Render()
|
||||
|
||||
// the rendered output should be a ref to the media type.
|
||||
op := `schema:
|
||||
$ref: '#/components/schemas/Pet'`
|
||||
op := `schema: {"$ref": "#/components/schemas/Pet"}`
|
||||
|
||||
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||
|
||||
@@ -45,30 +44,30 @@ func TestMediaType_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
op = `schema:
|
||||
required:
|
||||
- name
|
||||
- photoUrls
|
||||
- "name"
|
||||
- "photoUrls"
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
"id":
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
example: 10
|
||||
name:
|
||||
"name":
|
||||
type: "string"
|
||||
example: "doggie"
|
||||
category:
|
||||
"category":
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
"id":
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
example: 1
|
||||
name:
|
||||
"name":
|
||||
type: "string"
|
||||
example: "Dogs"
|
||||
xml:
|
||||
name: "category"
|
||||
photoUrls:
|
||||
"photoUrls":
|
||||
type: "array"
|
||||
xml:
|
||||
wrapped: true
|
||||
@@ -76,27 +75,27 @@ func TestMediaType_MarshalYAMLInline(t *testing.T) {
|
||||
type: "string"
|
||||
xml:
|
||||
name: "photoUrl"
|
||||
tags:
|
||||
"tags":
|
||||
type: "array"
|
||||
xml:
|
||||
wrapped: true
|
||||
items:
|
||||
type: "object"
|
||||
properties:
|
||||
id:
|
||||
"id":
|
||||
type: "integer"
|
||||
format: "int64"
|
||||
name:
|
||||
"name":
|
||||
type: "string"
|
||||
xml:
|
||||
name: "tag"
|
||||
status:
|
||||
"status":
|
||||
type: "string"
|
||||
description: "pet status in the store"
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
- "available"
|
||||
- "pending"
|
||||
- "sold"
|
||||
xml:
|
||||
name: "pet"
|
||||
example: testing a nice mutation`
|
||||
@@ -104,7 +103,6 @@ example: testing a nice mutation`
|
||||
yml, _ = mt.RenderInline()
|
||||
|
||||
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||
|
||||
}
|
||||
|
||||
func TestMediaType_MarshalYAML(t *testing.T) {
|
||||
@@ -125,22 +123,19 @@ func TestMediaType_MarshalYAML(t *testing.T) {
|
||||
yml, _ := mt.Render()
|
||||
|
||||
// the rendered output should be a ref to the media type.
|
||||
op := `schema:
|
||||
$ref: '#/components/schemas/Pet'`
|
||||
op := `schema: {"$ref": "#/components/schemas/Pet"}`
|
||||
|
||||
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||
|
||||
// modify the media type to have an example
|
||||
mt.Example = "testing a nice mutation"
|
||||
|
||||
op = `schema:
|
||||
$ref: '#/components/schemas/Pet'
|
||||
op = `schema: {"$ref": "#/components/schemas/Pet"}
|
||||
example: testing a nice mutation`
|
||||
|
||||
yml, _ = mt.Render()
|
||||
|
||||
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||
|
||||
}
|
||||
|
||||
func TestMediaType_Examples(t *testing.T) {
|
||||
|
||||
@@ -13,11 +13,11 @@ import (
|
||||
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
||||
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 orderedmap.Map[string, string] `json:"scopes,omitempty" yaml:"scopes,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
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 *orderedmap.Map[string, string] `json:"scopes,omitempty" yaml:"scopes,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.OAuthFlow
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
|
||||
o.AuthorizationUrl = flow.AuthorizationUrl.Value
|
||||
o.RefreshUrl = flow.RefreshUrl.Value
|
||||
scopes := orderedmap.New[string, string]()
|
||||
for pair := flow.Scopes.Value.First(); pair != nil; pair = pair.Next() {
|
||||
for pair := orderedmap.First(flow.Scopes.Value); pair != nil; pair = pair.Next() {
|
||||
scopes.Set(pair.Key().Value, pair.Value().Value)
|
||||
}
|
||||
o.Scopes = scopes
|
||||
|
||||
@@ -8,7 +8,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestOAuthFlow_MarshalYAML(t *testing.T) {
|
||||
@@ -37,7 +39,9 @@ scopes:
|
||||
|
||||
// mutate
|
||||
oflow.Scopes = nil
|
||||
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-burgers", utils.CreateStringNode("why not?"))
|
||||
oflow.Extensions = ext
|
||||
|
||||
desired = `authorizationUrl: https://pb33f.io
|
||||
tokenUrl: https://pb33f.io/token
|
||||
|
||||
@@ -6,17 +6,18 @@ 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"
|
||||
)
|
||||
|
||||
// OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
|
||||
type OAuthFlows struct {
|
||||
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
|
||||
Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
|
||||
ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
|
||||
AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
|
||||
Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
|
||||
ClientCredentials *OAuthFlow `json:"clientCredentials,omitempty" yaml:"clientCredentials,omitempty"`
|
||||
AuthorizationCode *OAuthFlow `json:"authorizationCode,omitempty" yaml:"authorizationCode,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.OAuthFlows
|
||||
}
|
||||
|
||||
|
||||
@@ -17,19 +17,19 @@ import (
|
||||
// happens here. The entire being for existence of this library and the specification, is this Operation.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#operation-object
|
||||
type Operation struct {
|
||||
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
|
||||
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 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"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
|
||||
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 *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"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Operation
|
||||
}
|
||||
|
||||
@@ -45,7 +45,9 @@ func NewOperation(operation *low.Operation) *Operation {
|
||||
}
|
||||
o.Tags = tags
|
||||
o.Summary = operation.Summary.Value
|
||||
o.Deprecated = &operation.Deprecated.Value
|
||||
if !operation.Deprecated.IsEmpty() {
|
||||
o.Deprecated = &operation.Deprecated.Value
|
||||
}
|
||||
o.Description = operation.Description.Value
|
||||
if !operation.ExternalDocs.IsEmpty() {
|
||||
o.ExternalDocs = base.NewExternalDoc(operation.ExternalDocs.Value)
|
||||
|
||||
@@ -50,13 +50,13 @@ 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.GetOrZero("testCallback").Expression, "{$request.body#/callbackUrl}")
|
||||
|
||||
assert.NotNil(t, r.Callbacks.GetOrZero("testCallback"))
|
||||
assert.NotNil(t, r.Callbacks.GetOrZero("testCallback").Expression.GetOrZero("{$request.body#/callbackUrl}"))
|
||||
assert.Equal(t, 3, r.GoLow().Callbacks.KeyNode.Line)
|
||||
}
|
||||
|
||||
func TestOperation_MarshalYAML(t *testing.T) {
|
||||
|
||||
op := &Operation{
|
||||
Tags: []string{"test"},
|
||||
Summary: "nice",
|
||||
@@ -90,11 +90,9 @@ requestBody:
|
||||
description: dice`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
op := &Operation{
|
||||
Tags: []string{"test"},
|
||||
Summary: "nice",
|
||||
@@ -128,7 +126,6 @@ requestBody:
|
||||
description: dice`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_EmptySecurity(t *testing.T) {
|
||||
@@ -147,7 +144,6 @@ security: []`
|
||||
|
||||
assert.NotNil(t, r.Security)
|
||||
assert.Len(t, r.Security, 0)
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_NoSecurity(t *testing.T) {
|
||||
@@ -164,5 +160,4 @@ func TestOperation_NoSecurity(t *testing.T) {
|
||||
r := NewOperation(&n)
|
||||
|
||||
assert.Nil(t, r.Security)
|
||||
|
||||
}
|
||||
|
||||
@@ -16,20 +16,20 @@ import (
|
||||
// A unique parameter is defined by a combination of a name and location.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
|
||||
type Parameter struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,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"`
|
||||
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,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:"-"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required *bool `json:"required,renderZero,omitempty" yaml:"required,renderZero,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
|
||||
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
||||
Explode *bool `json:"explode,renderZero,omitempty" yaml:"explode,renderZero,omitempty"`
|
||||
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
|
||||
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example *yaml.Node `json:"example,omitempty" yaml:"example,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 *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Parameter
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ func NewParameter(param *low.Parameter) *Parameter {
|
||||
if !param.Schema.IsEmpty() {
|
||||
p.Schema = base.NewSchemaProxy(¶m.Schema)
|
||||
}
|
||||
p.Required = param.Required.Value
|
||||
if !param.Required.IsEmpty() {
|
||||
p.Required = ¶m.Required.Value
|
||||
}
|
||||
p.Example = param.Example.Value
|
||||
p.Examples = base.ExtractExamples(param.Examples.Value)
|
||||
p.Content = ExtractContent(param.Content.Value)
|
||||
|
||||
@@ -9,10 +9,14 @@ import (
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestParameter_MarshalYAML(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-burgers", utils.CreateStringNode("why not?"))
|
||||
|
||||
explode := true
|
||||
param := Parameter{
|
||||
@@ -23,11 +27,11 @@ func TestParameter_MarshalYAML(t *testing.T) {
|
||||
Style: "simple",
|
||||
Explode: &explode,
|
||||
AllowReserved: true,
|
||||
Example: "example",
|
||||
Example: utils.CreateStringNode("example"),
|
||||
Examples: orderedmap.ToOrderedMap(map[string]*base.Example{
|
||||
"example": {Value: "example"},
|
||||
"example": {Value: utils.CreateStringNode("example")},
|
||||
}),
|
||||
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := param.Render()
|
||||
@@ -49,6 +53,8 @@ x-burgers: why not?`
|
||||
}
|
||||
|
||||
func TestParameter_MarshalYAMLInline(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-burgers", utils.CreateStringNode("why not?"))
|
||||
|
||||
explode := true
|
||||
param := Parameter{
|
||||
@@ -59,11 +65,11 @@ func TestParameter_MarshalYAMLInline(t *testing.T) {
|
||||
Style: "simple",
|
||||
Explode: &explode,
|
||||
AllowReserved: true,
|
||||
Example: "example",
|
||||
Example: utils.CreateStringNode("example"),
|
||||
Examples: orderedmap.ToOrderedMap(map[string]*base.Example{
|
||||
"example": {Value: "example"},
|
||||
"example": {Value: utils.CreateStringNode("example")},
|
||||
}),
|
||||
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := param.RenderInline()
|
||||
@@ -85,7 +91,6 @@ x-burgers: why not?`
|
||||
}
|
||||
|
||||
func TestParameter_IsExploded(t *testing.T) {
|
||||
|
||||
explode := true
|
||||
param := Parameter{
|
||||
Explode: &explode,
|
||||
@@ -106,7 +111,6 @@ func TestParameter_IsExploded(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParameter_IsDefaultFormEncoding(t *testing.T) {
|
||||
|
||||
param := Parameter{}
|
||||
assert.True(t, param.IsDefaultFormEncoding())
|
||||
|
||||
@@ -133,7 +137,6 @@ func TestParameter_IsDefaultFormEncoding(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParameter_IsDefaultHeaderEncoding(t *testing.T) {
|
||||
|
||||
param := Parameter{}
|
||||
assert.True(t, param.IsDefaultHeaderEncoding())
|
||||
|
||||
@@ -163,8 +166,6 @@ func TestParameter_IsDefaultHeaderEncoding(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParameter_IsDefaultPathEncoding(t *testing.T) {
|
||||
|
||||
param := Parameter{}
|
||||
assert.True(t, param.IsDefaultPathEncoding())
|
||||
|
||||
}
|
||||
|
||||
@@ -4,8 +4,13 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"slices"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowV3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -27,24 +32,24 @@ const (
|
||||
// are available.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#path-item-object
|
||||
type PathItem struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Get *Operation `json:"get,omitempty" yaml:"get,omitempty"`
|
||||
Put *Operation `json:"put,omitempty" yaml:"put,omitempty"`
|
||||
Post *Operation `json:"post,omitempty" yaml:"post,omitempty"`
|
||||
Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
|
||||
Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
Head *Operation `json:"head,omitempty" yaml:"head,omitempty"`
|
||||
Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
Trace *Operation `json:"trace,omitempty" yaml:"trace,omitempty"`
|
||||
Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"`
|
||||
Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *low.PathItem
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Get *Operation `json:"get,omitempty" yaml:"get,omitempty"`
|
||||
Put *Operation `json:"put,omitempty" yaml:"put,omitempty"`
|
||||
Post *Operation `json:"post,omitempty" yaml:"post,omitempty"`
|
||||
Delete *Operation `json:"delete,omitempty" yaml:"delete,omitempty"`
|
||||
Options *Operation `json:"options,omitempty" yaml:"options,omitempty"`
|
||||
Head *Operation `json:"head,omitempty" yaml:"head,omitempty"`
|
||||
Patch *Operation `json:"patch,omitempty" yaml:"patch,omitempty"`
|
||||
Trace *Operation `json:"trace,omitempty" yaml:"trace,omitempty"`
|
||||
Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"`
|
||||
Parameters []*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *lowV3.PathItem
|
||||
}
|
||||
|
||||
// NewPathItem creates a new high-level PathItem instance from a low-level one.
|
||||
func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
func NewPathItem(pathItem *lowV3.PathItem) *PathItem {
|
||||
pi := new(PathItem)
|
||||
pi.low = pathItem
|
||||
pi.Description = pathItem.Description.Value
|
||||
@@ -62,7 +67,7 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
op *Operation
|
||||
}
|
||||
opChan := make(chan opResult)
|
||||
var buildOperation = func(method int, op *low.Operation, c chan opResult) {
|
||||
buildOperation := func(method int, op *lowV3.Operation, c chan opResult) {
|
||||
if op == nil {
|
||||
c <- opResult{method: method, op: nil}
|
||||
return
|
||||
@@ -120,7 +125,7 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||
}
|
||||
|
||||
// GoLow returns the low level instance of PathItem, used to build the high-level one.
|
||||
func (p *PathItem) GoLow() *low.PathItem {
|
||||
func (p *PathItem) GoLow() *lowV3.PathItem {
|
||||
return p.low
|
||||
}
|
||||
|
||||
@@ -129,32 +134,65 @@ func (p *PathItem) GoLowUntyped() any {
|
||||
return p.low
|
||||
}
|
||||
|
||||
func (p *PathItem) GetOperations() map[string]*Operation {
|
||||
o := make(map[string]*Operation)
|
||||
func (p *PathItem) GetOperations() *orderedmap.Map[string, *Operation] {
|
||||
o := orderedmap.New[string, *Operation]()
|
||||
|
||||
// TODO: this is a bit of a hack, but it works for now. We might just want to actually pull the data out of the document as a map and split it into the individual operations
|
||||
|
||||
type op struct {
|
||||
name string
|
||||
op *Operation
|
||||
line int
|
||||
}
|
||||
|
||||
getLine := func(field string, idx int) int {
|
||||
if p.GoLow() == nil {
|
||||
return idx
|
||||
}
|
||||
|
||||
l, ok := reflect.ValueOf(p.GoLow()).Elem().FieldByName(field).Interface().(low.NodeReference[*lowV3.Operation])
|
||||
if !ok || l.GetKeyNode() == nil {
|
||||
return idx
|
||||
}
|
||||
|
||||
return l.GetKeyNode().Line
|
||||
}
|
||||
|
||||
ops := []op{}
|
||||
|
||||
if p.Get != nil {
|
||||
o[low.GetLabel] = p.Get
|
||||
ops = append(ops, op{name: lowV3.GetLabel, op: p.Get, line: getLine("Get", -8)})
|
||||
}
|
||||
if p.Put != nil {
|
||||
o[low.PutLabel] = p.Put
|
||||
ops = append(ops, op{name: lowV3.PutLabel, op: p.Put, line: getLine("Put", -7)})
|
||||
}
|
||||
if p.Post != nil {
|
||||
o[low.PostLabel] = p.Post
|
||||
ops = append(ops, op{name: lowV3.PostLabel, op: p.Post, line: getLine("Post", -6)})
|
||||
}
|
||||
if p.Delete != nil {
|
||||
o[low.DeleteLabel] = p.Delete
|
||||
ops = append(ops, op{name: lowV3.DeleteLabel, op: p.Delete, line: getLine("Delete", -5)})
|
||||
}
|
||||
if p.Options != nil {
|
||||
o[low.OptionsLabel] = p.Options
|
||||
ops = append(ops, op{name: lowV3.OptionsLabel, op: p.Options, line: getLine("Options", -4)})
|
||||
}
|
||||
if p.Head != nil {
|
||||
o[low.HeadLabel] = p.Head
|
||||
ops = append(ops, op{name: lowV3.HeadLabel, op: p.Head, line: getLine("Head", -3)})
|
||||
}
|
||||
if p.Patch != nil {
|
||||
o[low.PatchLabel] = p.Patch
|
||||
ops = append(ops, op{name: lowV3.PatchLabel, op: p.Patch, line: getLine("Patch", -2)})
|
||||
}
|
||||
if p.Trace != nil {
|
||||
o[low.TraceLabel] = p.Trace
|
||||
ops = append(ops, op{name: lowV3.TraceLabel, op: p.Trace, line: getLine("Trace", -1)})
|
||||
}
|
||||
|
||||
slices.SortStableFunc(ops, func(a op, b op) int {
|
||||
return a.line - b.line
|
||||
})
|
||||
|
||||
for _, op := range ops {
|
||||
o.Set(op.name, op.op)
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
|
||||
@@ -67,11 +67,19 @@ trace:
|
||||
|
||||
r := NewPathItem(&n)
|
||||
|
||||
assert.Len(t, r.GetOperations(), 8)
|
||||
assert.Equal(t, 8, r.GetOperations().Len())
|
||||
|
||||
// test that the operations are in the correct order
|
||||
expectedOrder := []string{"get", "put", "post", "patch", "delete", "head", "options", "trace"}
|
||||
|
||||
i := 0
|
||||
for pair := r.GetOperations().First(); pair != nil; pair = pair.Next() {
|
||||
assert.Equal(t, expectedOrder[i], pair.Value().Description)
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
func TestPathItem_MarshalYAML(t *testing.T) {
|
||||
|
||||
pi := &PathItem{
|
||||
Description: "a path item",
|
||||
Summary: "It's a test, don't worry about it, Jim",
|
||||
@@ -112,7 +120,6 @@ parameters:
|
||||
}
|
||||
|
||||
func TestPathItem_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
pi := &PathItem{
|
||||
Description: "a path item",
|
||||
Summary: "It's a test, don't worry about it, Jim",
|
||||
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
// constraints.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#paths-object
|
||||
type Paths struct {
|
||||
PathItems orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
PathItems *orderedmap.Map[string, *PathItem] `json:"-" yaml:"-"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *v3low.Paths
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
|
||||
pi *PathItem
|
||||
path string
|
||||
line int
|
||||
style yaml.Style
|
||||
rendered *yaml.Node
|
||||
}
|
||||
var mapped []*pathItem
|
||||
@@ -89,13 +90,21 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
|
||||
k := pair.Key()
|
||||
pi := pair.Value()
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
var style yaml.Style
|
||||
if p.low != nil {
|
||||
lpi := p.low.FindPath(k)
|
||||
if lpi != nil {
|
||||
ln = lpi.ValueNode.Line
|
||||
}
|
||||
|
||||
for pair := orderedmap.First(p.low.PathItems); pair != nil; pair = pair.Next() {
|
||||
if pair.Key().Value == k {
|
||||
style = pair.Key().KeyNode.Style
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, nil})
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, style, nil})
|
||||
}
|
||||
|
||||
nb := high.NewNodeBuilder(p, p.low)
|
||||
@@ -107,23 +116,29 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
|
||||
label = extNode.Content[u].Value
|
||||
continue
|
||||
}
|
||||
mapped = append(mapped, &pathItem{nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u]})
|
||||
mapped = append(mapped, &pathItem{
|
||||
nil, label,
|
||||
extNode.Content[u].Line, 0, extNode.Content[u],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].pi != nil {
|
||||
rendered, _ := mapped[j].pi.MarshalYAML()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].path))
|
||||
for _, mp := range mapped {
|
||||
if mp.pi != nil {
|
||||
rendered, _ := mp.pi.MarshalYAML()
|
||||
|
||||
kn := utils.CreateStringNode(mp.path)
|
||||
kn.Style = mp.style
|
||||
|
||||
m.Content = append(m.Content, kn)
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mapped[j].rendered != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].path))
|
||||
m.Content = append(m.Content, mapped[j].rendered)
|
||||
if mp.rendered != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mp.path))
|
||||
m.Content = append(m.Content, mp.rendered)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +152,7 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
|
||||
pi *PathItem
|
||||
path string
|
||||
line int
|
||||
style yaml.Style
|
||||
rendered *yaml.Node
|
||||
}
|
||||
var mapped []*pathItem
|
||||
@@ -145,13 +161,21 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
|
||||
k := pair.Key()
|
||||
pi := pair.Value()
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
var style yaml.Style
|
||||
if p.low != nil {
|
||||
lpi := p.low.FindPath(k)
|
||||
if lpi != nil {
|
||||
ln = lpi.ValueNode.Line
|
||||
}
|
||||
|
||||
for pair := orderedmap.First(p.low.PathItems); pair != nil; pair = pair.Next() {
|
||||
if pair.Key().Value == k {
|
||||
style = pair.Key().KeyNode.Style
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, nil})
|
||||
mapped = append(mapped, &pathItem{pi, k, ln, style, nil})
|
||||
}
|
||||
|
||||
nb := high.NewNodeBuilder(p, p.low)
|
||||
@@ -164,23 +188,29 @@ func (p *Paths) MarshalYAMLInline() (interface{}, error) {
|
||||
label = extNode.Content[u].Value
|
||||
continue
|
||||
}
|
||||
mapped = append(mapped, &pathItem{nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u]})
|
||||
mapped = append(mapped, &pathItem{
|
||||
nil, label,
|
||||
extNode.Content[u].Line, 0, extNode.Content[u],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].pi != nil {
|
||||
rendered, _ := mapped[j].pi.MarshalYAMLInline()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].path))
|
||||
for _, mp := range mapped {
|
||||
if mp.pi != nil {
|
||||
rendered, _ := mp.pi.MarshalYAMLInline()
|
||||
|
||||
kn := utils.CreateStringNode(mp.path)
|
||||
kn.Style = mp.style
|
||||
|
||||
m.Content = append(m.Content, kn)
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mapped[j].rendered != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].path))
|
||||
m.Content = append(m.Content, mapped[j].rendered)
|
||||
if mp.rendered != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mp.path))
|
||||
m.Content = append(m.Content, mp.rendered)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
// RequestBody represents a high-level OpenAPI 3+ RequestBody object, backed by a low-level one.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
|
||||
type RequestBody struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,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:"-"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Content *orderedmap.Map[string, *MediaType] `json:"content,omitempty" yaml:"content,omitempty"`
|
||||
Required *bool `json:"required,omitempty" yaml:"required,renderZero,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.RequestBody
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ func NewRequestBody(rb *low.RequestBody) *RequestBody {
|
||||
r := new(RequestBody)
|
||||
r.low = rb
|
||||
r.Description = rb.Description.Value
|
||||
if rb.Required.ValueNode != nil {
|
||||
if !rb.Required.IsEmpty() {
|
||||
r.Required = &rb.Required.Value
|
||||
}
|
||||
r.Extensions = high.ExtractExtensions(rb.Extensions)
|
||||
|
||||
@@ -7,16 +7,21 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestRequestBody_MarshalYAML(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-high-gravity", utils.CreateStringNode("why not?"))
|
||||
|
||||
rb := true
|
||||
req := &RequestBody{
|
||||
Description: "beer",
|
||||
Required: &rb,
|
||||
Extensions: map[string]interface{}{"x-high-gravity": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := req.Render()
|
||||
@@ -26,16 +31,17 @@ required: true
|
||||
x-high-gravity: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestRequestBody_MarshalYAMLInline(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-high-gravity", utils.CreateStringNode("why not?"))
|
||||
|
||||
rb := true
|
||||
req := &RequestBody{
|
||||
Description: "beer",
|
||||
Required: &rb,
|
||||
Extensions: map[string]interface{}{"x-high-gravity": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := req.RenderInline()
|
||||
@@ -45,15 +51,17 @@ required: true
|
||||
x-high-gravity: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestRequestBody_MarshalNoRequired(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-high-gravity", utils.CreateStringNode("why not?"))
|
||||
|
||||
rb := false
|
||||
req := &RequestBody{
|
||||
Description: "beer",
|
||||
Required: &rb,
|
||||
Extensions: map[string]interface{}{"x-high-gravity": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := req.Render()
|
||||
@@ -63,14 +71,15 @@ required: false
|
||||
x-high-gravity: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestRequestBody_MarshalRequiredNil(t *testing.T) {
|
||||
ext := orderedmap.New[string, *yaml.Node]()
|
||||
ext.Set("x-high-gravity", utils.CreateStringNode("why not?"))
|
||||
|
||||
req := &RequestBody{
|
||||
Description: "beer",
|
||||
Extensions: map[string]interface{}{"x-high-gravity": "why not?"},
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
rend, _ := req.Render()
|
||||
@@ -79,5 +88,4 @@ func TestRequestBody_MarshalRequiredNil(t *testing.T) {
|
||||
x-high-gravity: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@ import (
|
||||
// operations based on the response.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#response-object
|
||||
type Response struct {
|
||||
Description string `json:"description,omitempty" yaml:"description,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:"-"`
|
||||
Description string `json:"description,omitempty" yaml:"description,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 *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Response
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
// with hard coded line and column numbers in them, changing the spec above the bottom will
|
||||
// create pointless test changes. So here is a standalone test. you know... for science.
|
||||
func TestNewResponse(t *testing.T) {
|
||||
|
||||
yml := `description: this is a response
|
||||
headers:
|
||||
someHeader:
|
||||
@@ -46,14 +45,16 @@ links:
|
||||
|
||||
assert.Equal(t, 1, orderedmap.Len(r.Headers))
|
||||
assert.Equal(t, 1, orderedmap.Len(r.Content))
|
||||
assert.Equal(t, "pizza!", r.Extensions["x-pizza-man"])
|
||||
|
||||
var xPizzaMan string
|
||||
_ = r.Extensions.GetOrZero("x-pizza-man").Decode(&xPizzaMan)
|
||||
|
||||
assert.Equal(t, "pizza!", xPizzaMan)
|
||||
assert.Equal(t, 1, orderedmap.Len(r.Links))
|
||||
assert.Equal(t, 1, r.GoLow().Description.KeyNode.Line)
|
||||
|
||||
}
|
||||
|
||||
func TestResponse_MarshalYAML(t *testing.T) {
|
||||
|
||||
yml := `description: this is a response
|
||||
headers:
|
||||
someHeader:
|
||||
@@ -77,11 +78,9 @@ links:
|
||||
|
||||
rend, _ := r.Render()
|
||||
assert.Equal(t, yml, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestResponse_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
yml := `description: this is a response
|
||||
headers:
|
||||
someHeader:
|
||||
@@ -105,5 +104,4 @@ links:
|
||||
|
||||
rend, _ := r.RenderInline()
|
||||
assert.Equal(t, yml, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
@@ -31,9 +31,9 @@ import (
|
||||
// be the response for a successful operation call.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#responses-object
|
||||
type Responses struct {
|
||||
Codes orderedmap.Map[string, *Response] `json:"-" yaml:"-"`
|
||||
Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Codes *orderedmap.Map[string, *Response] `json:"-" yaml:"-"`
|
||||
Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Responses
|
||||
}
|
||||
|
||||
@@ -93,23 +93,26 @@ func (r *Responses) MarshalYAML() (interface{}, error) {
|
||||
// map keys correctly.
|
||||
m := utils.CreateEmptyMapNode()
|
||||
type responseItem struct {
|
||||
resp *Response
|
||||
code string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
resp *Response
|
||||
code string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
style yaml.Style
|
||||
}
|
||||
var mapped []*responseItem
|
||||
|
||||
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.
|
||||
var style yaml.Style
|
||||
if r.low != nil {
|
||||
for lPair := orderedmap.First(r.low.Codes); lPair != nil; lPair = lPair.Next() {
|
||||
if lPair.Key().Value == pair.Key() {
|
||||
ln = lPair.Key().KeyNode.Line
|
||||
style = lPair.Key().KeyNode.Style
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &responseItem{pair.Value(), pair.Key(), ln, nil})
|
||||
mapped = append(mapped, &responseItem{pair.Value(), pair.Key(), ln, nil, style})
|
||||
}
|
||||
|
||||
// extract extensions
|
||||
@@ -124,7 +127,7 @@ func (r *Responses) MarshalYAML() (interface{}, error) {
|
||||
}
|
||||
mapped = append(mapped, &responseItem{
|
||||
nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u],
|
||||
extNode.Content[u].Line, extNode.Content[u], 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -132,15 +135,19 @@ func (r *Responses) MarshalYAML() (interface{}, error) {
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].resp != nil {
|
||||
rendered, _ := mapped[j].resp.MarshalYAML()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
for _, mp := range mapped {
|
||||
if mp.resp != nil {
|
||||
rendered, _ := mp.resp.MarshalYAML()
|
||||
|
||||
kn := utils.CreateStringNode(mp.code)
|
||||
kn.Style = mp.style
|
||||
|
||||
m.Content = append(m.Content, kn)
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mapped[j].ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
m.Content = append(m.Content, mapped[j].ext)
|
||||
if mp.ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mp.code))
|
||||
m.Content = append(m.Content, mp.ext)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -151,23 +158,26 @@ func (r *Responses) MarshalYAMLInline() (interface{}, error) {
|
||||
// map keys correctly.
|
||||
m := utils.CreateEmptyMapNode()
|
||||
type responseItem struct {
|
||||
resp *Response
|
||||
code string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
resp *Response
|
||||
code string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
style yaml.Style
|
||||
}
|
||||
var mapped []*responseItem
|
||||
|
||||
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.
|
||||
var style yaml.Style
|
||||
if r.low != nil {
|
||||
for lPair := orderedmap.First(r.low.Codes); lPair != nil; lPair = lPair.Next() {
|
||||
if lPair.Key().Value == pair.Key() {
|
||||
ln = lPair.Key().KeyNode.Line
|
||||
style = lPair.Key().KeyNode.Style
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &responseItem{pair.Value(), pair.Key(), ln, nil})
|
||||
mapped = append(mapped, &responseItem{pair.Value(), pair.Key(), ln, nil, style})
|
||||
}
|
||||
|
||||
// extract extensions
|
||||
@@ -183,7 +193,7 @@ func (r *Responses) MarshalYAMLInline() (interface{}, error) {
|
||||
}
|
||||
mapped = append(mapped, &responseItem{
|
||||
nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u],
|
||||
extNode.Content[u].Line, extNode.Content[u], 0,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -191,16 +201,20 @@ func (r *Responses) MarshalYAMLInline() (interface{}, error) {
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].resp != nil {
|
||||
rendered, _ := mapped[j].resp.MarshalYAMLInline()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
for _, mp := range mapped {
|
||||
if mp.resp != nil {
|
||||
rendered, _ := mp.resp.MarshalYAMLInline()
|
||||
|
||||
kn := utils.CreateStringNode(mp.code)
|
||||
kn.Style = mp.style
|
||||
|
||||
m.Content = append(m.Content, kn)
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
|
||||
}
|
||||
if mapped[j].ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
m.Content = append(m.Content, mapped[j].ext)
|
||||
if mp.ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mp.code))
|
||||
m.Content = append(m.Content, mp.ext)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -20,15 +21,15 @@ import (
|
||||
// Recommended for most use case is Authorization Code Grant flow with PKCE.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
||||
type SecurityScheme struct {
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
|
||||
BearerFormat string `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"`
|
||||
Flows *OAuthFlows `json:"flows,omitempty" yaml:"flows,omitempty"`
|
||||
OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
|
||||
BearerFormat string `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"`
|
||||
Flows *OAuthFlows `json:"flows,omitempty" yaml:"flows,omitempty"`
|
||||
OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.SecurityScheme
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
// Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
||||
type Server struct {
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Variables orderedmap.Map[string, *ServerVariable] `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Variables *orderedmap.Map[string, *ServerVariable] `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||
Extensions *orderedmap.Map[string, *yaml.Node] `json:"-" yaml:"-"`
|
||||
low *low.Server
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@ import (
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||
type Discriminator struct {
|
||||
PropertyName low.NodeReference[string]
|
||||
Mapping low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]
|
||||
Mapping low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[string]]]
|
||||
low.Reference
|
||||
}
|
||||
|
||||
// FindMappingValue will return a ValueReference containing the string mapping value
|
||||
func (d *Discriminator) FindMappingValue(key string) *low.ValueReference[string] {
|
||||
for pair := d.Mapping.Value.First(); pair != nil; pair = pair.Next() {
|
||||
for pair := orderedmap.First(d.Mapping.Value); pair != nil; pair = pair.Next() {
|
||||
if pair.Key().Value == key {
|
||||
v := pair.Value()
|
||||
return &v
|
||||
@@ -45,7 +45,7 @@ func (d *Discriminator) Hash() [32]byte {
|
||||
f = append(f, d.PropertyName.Value)
|
||||
}
|
||||
|
||||
for pair := orderedmap.First(d.Mapping.Value); pair != nil; pair = pair.Next() {
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(d.Mapping.Value)); pair != nil; pair = pair.Next() {
|
||||
f = append(f, pair.Value().Value)
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,11 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"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"
|
||||
)
|
||||
@@ -23,15 +22,15 @@ import (
|
||||
type Example struct {
|
||||
Summary low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
Value low.NodeReference[any]
|
||||
Value low.NodeReference[*yaml.Node]
|
||||
ExternalValue low.NodeReference[string]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||
func (ex *Example) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, ex.Extensions)
|
||||
func (ex *Example) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap[*yaml.Node](ext, ex.Extensions)
|
||||
}
|
||||
|
||||
// Hash will return a consistent SHA256 Hash of the Discriminator object
|
||||
@@ -43,21 +42,15 @@ func (ex *Example) Hash() [32]byte {
|
||||
if ex.Description.Value != "" {
|
||||
f = append(f, ex.Description.Value)
|
||||
}
|
||||
if ex.Value.Value != "" {
|
||||
if ex.Value.Value != nil && !ex.Value.Value.IsZero() {
|
||||
// this could be anything!
|
||||
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(ex.Value.Value)))))
|
||||
b, _ := yaml.Marshal(ex.Value.Value)
|
||||
f = append(f, fmt.Sprintf("%x", sha256.Sum256(b)))
|
||||
}
|
||||
if ex.ExternalValue.Value != "" {
|
||||
f = append(f, ex.ExternalValue.Value)
|
||||
}
|
||||
keys := make([]string, len(ex.Extensions))
|
||||
z := 0
|
||||
for k := range ex.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(ex.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(ex.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -70,32 +63,8 @@ func (ex *Example) Build(_ context.Context, _, root *yaml.Node, _ *index.SpecInd
|
||||
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
||||
|
||||
if vn != nil {
|
||||
var n map[string]interface{}
|
||||
err := vn.Decode(&n)
|
||||
if err != nil {
|
||||
// if not a map, then try an array
|
||||
var k []interface{}
|
||||
err = vn.Decode(&k)
|
||||
if err != nil {
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = vn.Decode(&j)
|
||||
ex.Value = low.NodeReference[any]{
|
||||
Value: j,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ex.Value = low.NodeReference[any]{
|
||||
Value: k,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
ex.Value = low.NodeReference[any]{
|
||||
Value: n,
|
||||
ex.Value = low.NodeReference[*yaml.Node]{
|
||||
Value: vn,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
@@ -105,33 +74,6 @@ func (ex *Example) Build(_ context.Context, _, root *yaml.Node, _ *index.SpecInd
|
||||
}
|
||||
|
||||
// GetExtensions will return Example extensions to satisfy the HasExtensions interface.
|
||||
func (ex *Example) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (ex *Example) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return ex.Extensions
|
||||
}
|
||||
|
||||
// ExtractExampleValue will extract a primitive example value (if possible), or just the raw Value property if not.
|
||||
func ExtractExampleValue(exp *yaml.Node) any {
|
||||
if utils.IsNodeBoolValue(exp) {
|
||||
v, _ := strconv.ParseBool(exp.Value)
|
||||
return v
|
||||
}
|
||||
if utils.IsNodeIntValue(exp) {
|
||||
v, _ := strconv.ParseInt(exp.Value, 10, 64)
|
||||
return v
|
||||
}
|
||||
if utils.IsNodeFloatValue(exp) {
|
||||
v, _ := strconv.ParseFloat(exp.Value, 64)
|
||||
return v
|
||||
}
|
||||
if utils.IsNodeMap(exp) {
|
||||
var m map[string]interface{}
|
||||
_ = exp.Decode(&m)
|
||||
return m
|
||||
}
|
||||
if utils.IsNodeArray(exp) {
|
||||
var m []interface{}
|
||||
_ = exp.Decode(&m)
|
||||
return m
|
||||
}
|
||||
return exp.Value
|
||||
}
|
||||
|
||||
@@ -5,15 +5,17 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExample_Build_Success_NoValue(t *testing.T) {
|
||||
|
||||
yml := `summary: hot
|
||||
description: cakes
|
||||
x-cake: hot`
|
||||
@@ -32,13 +34,13 @@ x-cake: hot`
|
||||
assert.Equal(t, "hot", n.Summary.Value)
|
||||
assert.Equal(t, "cakes", n.Description.Value)
|
||||
assert.Nil(t, n.Value.Value)
|
||||
ext := n.FindExtension("x-cake")
|
||||
assert.NotNil(t, ext)
|
||||
assert.Equal(t, "hot", ext.Value)
|
||||
|
||||
var xCake string
|
||||
_ = n.FindExtension("x-cake").Value.Decode(&xCake)
|
||||
assert.Equal(t, "hot", xCake)
|
||||
}
|
||||
|
||||
func TestExample_Build_Success_Simple(t *testing.T) {
|
||||
|
||||
yml := `summary: hot
|
||||
description: cakes
|
||||
value: a string example
|
||||
@@ -57,14 +59,17 @@ x-cake: hot`
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "hot", n.Summary.Value)
|
||||
assert.Equal(t, "cakes", n.Description.Value)
|
||||
assert.Equal(t, "a string example", n.Value.Value)
|
||||
ext := n.FindExtension("x-cake")
|
||||
assert.NotNil(t, ext)
|
||||
assert.Equal(t, "hot", ext.Value)
|
||||
|
||||
var example string
|
||||
err = n.Value.Value.Decode(&example)
|
||||
assert.Equal(t, "a string example", example)
|
||||
|
||||
var xCake string
|
||||
_ = n.FindExtension("x-cake").Value.Decode(&xCake)
|
||||
assert.Equal(t, "hot", xCake)
|
||||
}
|
||||
|
||||
func TestExample_Build_Success_Object(t *testing.T) {
|
||||
|
||||
yml := `summary: hot
|
||||
description: cakes
|
||||
value:
|
||||
@@ -85,17 +90,15 @@ value:
|
||||
assert.Equal(t, "hot", n.Summary.Value)
|
||||
assert.Equal(t, "cakes", n.Description.Value)
|
||||
|
||||
if v, ok := n.Value.Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "oven", v["pizza"])
|
||||
assert.Equal(t, "pizza", v["yummy"])
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
var m map[string]interface{}
|
||||
err = n.Value.Value.Decode(&m)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "oven", m["pizza"])
|
||||
assert.Equal(t, "pizza", m["yummy"])
|
||||
}
|
||||
|
||||
func TestExample_Build_Success_Array(t *testing.T) {
|
||||
|
||||
yml := `summary: hot
|
||||
description: cakes
|
||||
value:
|
||||
@@ -116,16 +119,15 @@ value:
|
||||
assert.Equal(t, "hot", n.Summary.Value)
|
||||
assert.Equal(t, "cakes", n.Description.Value)
|
||||
|
||||
if v, ok := n.Value.Value.([]interface{}); ok {
|
||||
assert.Equal(t, "wow", v[0])
|
||||
assert.Equal(t, "such array", v[1])
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
var a []any
|
||||
err = n.Value.Value.Decode(&a)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "wow", a[0])
|
||||
assert.Equal(t, "such array", a[1])
|
||||
}
|
||||
|
||||
func TestExample_Build_Success_MergeNode(t *testing.T) {
|
||||
|
||||
yml := `x-things: &things
|
||||
summary: hot
|
||||
description: cakes
|
||||
@@ -148,71 +150,15 @@ func TestExample_Build_Success_MergeNode(t *testing.T) {
|
||||
assert.Equal(t, "hot", n.Summary.Value)
|
||||
assert.Equal(t, "cakes", n.Description.Value)
|
||||
|
||||
if v, ok := n.Value.Value.([]interface{}); ok {
|
||||
assert.Equal(t, "wow", v[0])
|
||||
assert.Equal(t, "such array", v[1])
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
var a []any
|
||||
err = n.Value.GetValue().Decode(&a)
|
||||
require.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestExample_ExtractExampleValue_Map(t *testing.T) {
|
||||
|
||||
yml := `hot:
|
||||
summer: nights
|
||||
pizza: oven`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
|
||||
val := ExtractExampleValue(idxNode.Content[0])
|
||||
if v, ok := val.(map[string]interface{}); ok {
|
||||
if r, rok := v["hot"].(map[string]interface{}); rok {
|
||||
assert.Equal(t, "nights", r["summer"])
|
||||
assert.Equal(t, "oven", r["pizza"])
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExample_ExtractExampleValue_Slice(t *testing.T) {
|
||||
|
||||
yml := `- hot:
|
||||
summer: nights
|
||||
- hotter:
|
||||
pizza: oven`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
|
||||
val := ExtractExampleValue(idxNode.Content[0])
|
||||
if v, ok := val.([]interface{}); ok {
|
||||
for w := range v {
|
||||
if r, rok := v[w].(map[string]interface{}); rok {
|
||||
for k := range r {
|
||||
if k == "hotter" {
|
||||
assert.Equal(t, "oven", r[k].(map[string]interface{})["pizza"])
|
||||
}
|
||||
if k == "hot" {
|
||||
assert.Equal(t, "nights", r[k].(map[string]interface{})["summer"])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
assert.Fail(t, "failed to decode correctly.")
|
||||
}
|
||||
assert.Equal(t, "wow", a[0])
|
||||
assert.Equal(t, "such array", a[1])
|
||||
}
|
||||
|
||||
func TestExample_Hash(t *testing.T) {
|
||||
|
||||
left := `summary: hot
|
||||
description: cakes
|
||||
x-burger: nice
|
||||
@@ -242,13 +188,5 @@ x-burger: nice`
|
||||
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
|
||||
|
||||
assert.Equal(t, lDoc.Hash(), rDoc.Hash())
|
||||
assert.Len(t, lDoc.GetExtensions(), 1)
|
||||
}
|
||||
|
||||
func TestExtractExampleValue(t *testing.T) {
|
||||
assert.True(t, ExtractExampleValue(&yaml.Node{Tag: "!!bool", Value: "true"}).(bool))
|
||||
assert.Equal(t, int64(10), ExtractExampleValue(&yaml.Node{Tag: "!!int", Value: "10"}).(int64))
|
||||
assert.Equal(t, 33.2, ExtractExampleValue(&yaml.Node{Tag: "!!float", Value: "33.2"}).(float64))
|
||||
assert.Equal(t, "WHAT A NICE COW", ExtractExampleValue(&yaml.Node{Tag: "!!str", Value: "WHAT A NICE COW"}))
|
||||
|
||||
assert.Equal(t, 1, orderedmap.Len(lDoc.GetExtensions()))
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ package base
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3
|
||||
@@ -24,13 +24,13 @@ import (
|
||||
type ExternalDoc struct {
|
||||
Description low.NodeReference[string]
|
||||
URL low.NodeReference[string]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||
func (ex *ExternalDoc) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, ex.Extensions)
|
||||
func (ex *ExternalDoc) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap[*yaml.Node](ext, ex.Extensions)
|
||||
}
|
||||
|
||||
// Build will extract extensions from the ExternalDoc instance.
|
||||
@@ -43,7 +43,7 @@ func (ex *ExternalDoc) Build(_ context.Context, _, root *yaml.Node, idx *index.S
|
||||
}
|
||||
|
||||
// GetExtensions returns all ExternalDoc extensions and satisfies the low.HasExtensions interface.
|
||||
func (ex *ExternalDoc) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (ex *ExternalDoc) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return ex.Extensions
|
||||
}
|
||||
|
||||
@@ -53,13 +53,6 @@ func (ex *ExternalDoc) Hash() [32]byte {
|
||||
ex.Description.Value,
|
||||
ex.URL.Value,
|
||||
}
|
||||
keys := make([]string, len(ex.Extensions))
|
||||
z := 0
|
||||
for k := range ex.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(ex.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(ex.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExternalDoc_FindExtension(t *testing.T) {
|
||||
|
||||
yml := `x-fish: cake`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -26,12 +27,14 @@ func TestExternalDoc_FindExtension(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "cake", n.FindExtension("x-fish").Value)
|
||||
|
||||
var xFish string
|
||||
_ = n.FindExtension("x-fish").Value.Decode(&xFish)
|
||||
|
||||
assert.Equal(t, "cake", xFish)
|
||||
}
|
||||
|
||||
func TestExternalDoc_Build(t *testing.T) {
|
||||
|
||||
yml := `url: https://pb33f.io
|
||||
description: the ranch
|
||||
x-b33f: princess`
|
||||
@@ -49,14 +52,13 @@ x-b33f: princess`
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://pb33f.io", n.URL.Value)
|
||||
assert.Equal(t, "the ranch", n.Description.Value)
|
||||
ext := n.FindExtension("x-b33f")
|
||||
assert.NotNil(t, ext)
|
||||
assert.Equal(t, "princess", ext.Value)
|
||||
|
||||
var xB33f string
|
||||
_ = n.FindExtension("x-b33f").Value.Decode(&xB33f)
|
||||
assert.Equal(t, "princess", xB33f)
|
||||
}
|
||||
|
||||
func TestExternalDoc_Hash(t *testing.T) {
|
||||
|
||||
left := `url: https://pb33f.io
|
||||
description: the ranch
|
||||
x-b33f: princess`
|
||||
@@ -78,5 +80,5 @@ description: the ranch`
|
||||
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
|
||||
|
||||
assert.Equal(t, lDoc.Hash(), rDoc.Hash())
|
||||
assert.Len(t, lDoc.GetExtensions(), 1)
|
||||
assert.Equal(t, 1, orderedmap.Len(lDoc.GetExtensions()))
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ package base
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
@@ -31,17 +31,17 @@ type Info struct {
|
||||
Contact low.NodeReference[*Contact]
|
||||
License low.NodeReference[*License]
|
||||
Version low.NodeReference[string]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
// FindExtension attempts to locate an extension with the supplied key
|
||||
func (i *Info) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap(ext, i.Extensions)
|
||||
func (i *Info) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(ext, i.Extensions)
|
||||
}
|
||||
|
||||
// GetExtensions returns all extensions for Info
|
||||
func (i *Info) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (i *Info) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return i.Extensions
|
||||
}
|
||||
|
||||
@@ -87,13 +87,6 @@ func (i *Info) Hash() [32]byte {
|
||||
if !i.Version.IsEmpty() {
|
||||
f = append(f, i.Version.Value)
|
||||
}
|
||||
keys := make([]string, len(i.Extensions))
|
||||
z := 0
|
||||
for k := range i.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(i.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(i.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -54,10 +55,11 @@ x-cli-name: pizza cli`
|
||||
assert.Equal(t, "magic", lic.Name.Value)
|
||||
assert.Equal(t, "https://pb33f.io/license", lic.URL.Value)
|
||||
|
||||
cliName := n.FindExtension("x-cli-name")
|
||||
assert.NotNil(t, cliName)
|
||||
assert.Equal(t, "pizza cli", cliName.Value)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
var xCliName string
|
||||
_ = n.FindExtension("x-cli-name").Value.Decode(&xCliName)
|
||||
|
||||
assert.Equal(t, "pizza cli", xCliName)
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
func TestContact_Build(t *testing.T) {
|
||||
|
||||
@@ -82,7 +82,7 @@ type Schema struct {
|
||||
Discriminator low.NodeReference[*Discriminator]
|
||||
|
||||
// in 3.1 examples can be an array (which is recommended)
|
||||
Examples low.NodeReference[[]low.ValueReference[any]]
|
||||
Examples low.NodeReference[[]low.ValueReference[*yaml.Node]]
|
||||
// in 3.1 PrefixItems provides tuple validation using prefixItems.
|
||||
PrefixItems low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
||||
// in 3.1 Contains is used by arrays and points to a Schema.
|
||||
@@ -97,8 +97,8 @@ type Schema struct {
|
||||
If low.NodeReference[*SchemaProxy]
|
||||
Else low.NodeReference[*SchemaProxy]
|
||||
Then low.NodeReference[*SchemaProxy]
|
||||
DependentSchemas low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
|
||||
PatternProperties low.NodeReference[orderedmap.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]]
|
||||
@@ -119,23 +119,23 @@ type Schema struct {
|
||||
MaxProperties low.NodeReference[int64]
|
||||
MinProperties low.NodeReference[int64]
|
||||
Required low.NodeReference[[]low.ValueReference[string]]
|
||||
Enum low.NodeReference[[]low.ValueReference[any]]
|
||||
Enum low.NodeReference[[]low.ValueReference[*yaml.Node]]
|
||||
Not low.NodeReference[*SchemaProxy]
|
||||
Properties low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
|
||||
Properties low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]
|
||||
AdditionalProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]
|
||||
Description low.NodeReference[string]
|
||||
ContentEncoding low.NodeReference[string]
|
||||
ContentMediaType low.NodeReference[string]
|
||||
Default low.NodeReference[any]
|
||||
Const low.NodeReference[any]
|
||||
Default low.NodeReference[*yaml.Node]
|
||||
Const low.NodeReference[*yaml.Node]
|
||||
Nullable low.NodeReference[bool]
|
||||
ReadOnly low.NodeReference[bool]
|
||||
WriteOnly low.NodeReference[bool]
|
||||
XML low.NodeReference[*XML]
|
||||
ExternalDocs low.NodeReference[*ExternalDoc]
|
||||
Example low.NodeReference[any]
|
||||
Example low.NodeReference[*yaml.Node]
|
||||
Deprecated low.NodeReference[bool]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
|
||||
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
||||
ParentProxy *SchemaProxy
|
||||
@@ -255,23 +255,13 @@ func (s *Schema) Hash() [32]byte {
|
||||
|
||||
keys = make([]string, len(s.Enum.Value))
|
||||
for i := range s.Enum.Value {
|
||||
keys[i] = fmt.Sprint(s.Enum.Value[i].Value)
|
||||
keys[i] = low.ValueToString(s.Enum.Value[i].Value)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
d = append(d, keys...)
|
||||
|
||||
for i := range s.Enum.Value {
|
||||
d = append(d, fmt.Sprint(s.Enum.Value[i].Value))
|
||||
}
|
||||
propKeys := make([]string, orderedmap.Len(s.Properties.Value))
|
||||
z := 0
|
||||
for pair := orderedmap.First(s.Properties.Value); pair != nil; pair = pair.Next() {
|
||||
propKeys[z] = pair.Key().Value
|
||||
z++
|
||||
}
|
||||
sort.Strings(propKeys)
|
||||
for k := range propKeys {
|
||||
d = append(d, low.GenerateHashString(s.FindProperty(propKeys[k]).Value))
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(s.Properties.Value)); pair != nil; pair = pair.Next() {
|
||||
d = append(d, fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value)))
|
||||
}
|
||||
if s.XML.Value != nil {
|
||||
d = append(d, low.GenerateHashString(s.XML.Value))
|
||||
@@ -287,7 +277,7 @@ func (s *Schema) Hash() [32]byte {
|
||||
if len(s.OneOf.Value) > 0 {
|
||||
oneOfKeys := make([]string, len(s.OneOf.Value))
|
||||
oneOfEntities := make(map[string]*SchemaProxy)
|
||||
z = 0
|
||||
z := 0
|
||||
for i := range s.OneOf.Value {
|
||||
g := s.OneOf.Value[i].Value
|
||||
r := low.GenerateHashString(g)
|
||||
@@ -305,7 +295,7 @@ func (s *Schema) Hash() [32]byte {
|
||||
if len(s.AllOf.Value) > 0 {
|
||||
allOfKeys := make([]string, len(s.AllOf.Value))
|
||||
allOfEntities := make(map[string]*SchemaProxy)
|
||||
z = 0
|
||||
z := 0
|
||||
for i := range s.AllOf.Value {
|
||||
g := s.AllOf.Value[i].Value
|
||||
r := low.GenerateHashString(g)
|
||||
@@ -323,7 +313,7 @@ func (s *Schema) Hash() [32]byte {
|
||||
if len(s.AnyOf.Value) > 0 {
|
||||
anyOfKeys := make([]string, len(s.AnyOf.Value))
|
||||
anyOfEntities := make(map[string]*SchemaProxy)
|
||||
z = 0
|
||||
z := 0
|
||||
for i := range s.AnyOf.Value {
|
||||
g := s.AnyOf.Value[i].Value
|
||||
r := low.GenerateHashString(g)
|
||||
@@ -372,32 +362,18 @@ func (s *Schema) Hash() [32]byte {
|
||||
d = append(d, fmt.Sprint(s.Anchor.Value))
|
||||
}
|
||||
|
||||
depSchemasKeys := make([]string, orderedmap.Len(s.DependentSchemas.Value))
|
||||
z = 0
|
||||
for pair := orderedmap.First(s.DependentSchemas.Value); pair != nil; pair = pair.Next() {
|
||||
depSchemasKeys[z] = pair.Key().Value
|
||||
z++
|
||||
}
|
||||
sort.Strings(depSchemasKeys)
|
||||
for k := range depSchemasKeys {
|
||||
d = append(d, low.GenerateHashString(s.FindDependentSchema(depSchemasKeys[k]).Value))
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(s.DependentSchemas.Value)); pair != nil; pair = pair.Next() {
|
||||
d = append(d, fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value)))
|
||||
}
|
||||
|
||||
patternPropsKeys := make([]string, orderedmap.Len(s.PatternProperties.Value))
|
||||
z = 0
|
||||
for pair := orderedmap.First(s.PatternProperties.Value); pair != nil; pair = pair.Next() {
|
||||
patternPropsKeys[z] = pair.Key().Value
|
||||
z++
|
||||
}
|
||||
sort.Strings(patternPropsKeys)
|
||||
for k := range patternPropsKeys {
|
||||
d = append(d, low.GenerateHashString(s.FindPatternProperty(patternPropsKeys[k]).Value))
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(s.PatternProperties.Value)); pair != nil; pair = pair.Next() {
|
||||
d = append(d, fmt.Sprintf("%s-%s", pair.Key().Value, low.GenerateHashString(pair.Value().Value)))
|
||||
}
|
||||
|
||||
if len(s.PrefixItems.Value) > 0 {
|
||||
itemsKeys := make([]string, len(s.PrefixItems.Value))
|
||||
itemsEntities := make(map[string]*SchemaProxy)
|
||||
z = 0
|
||||
z := 0
|
||||
for i := range s.PrefixItems.Value {
|
||||
g := s.PrefixItems.Value[i].Value
|
||||
r := low.GenerateHashString(g)
|
||||
@@ -411,15 +387,7 @@ func (s *Schema) Hash() [32]byte {
|
||||
}
|
||||
}
|
||||
|
||||
// add extensions to hash
|
||||
keys = make([]string, len(s.Extensions))
|
||||
z = 0
|
||||
for k := range s.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(s.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
d = append(d, keys...)
|
||||
d = append(d, low.HashExtensions(s.Extensions)...)
|
||||
if s.Example.Value != nil {
|
||||
d = append(d, low.GenerateHashString(s.Example.Value))
|
||||
}
|
||||
@@ -435,12 +403,9 @@ func (s *Schema) Hash() [32]byte {
|
||||
d = append(d, fmt.Sprint(s.MaxContains.Value))
|
||||
}
|
||||
if !s.Examples.IsEmpty() {
|
||||
var xph []string
|
||||
for w := range s.Examples.Value {
|
||||
xph = append(xph, low.GenerateHashString(s.Examples.Value[w].Value))
|
||||
for _, ex := range s.Examples.Value {
|
||||
d = append(d, low.GenerateHashString(ex.Value))
|
||||
}
|
||||
sort.Strings(xph)
|
||||
d = append(d, strings.Join(xph, "|"))
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(d, "|")))
|
||||
}
|
||||
@@ -464,7 +429,7 @@ func (s *Schema) FindPatternProperty(name string) *low.ValueReference[*SchemaPro
|
||||
}
|
||||
|
||||
// GetExtensions returns all extensions for Schema
|
||||
func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (s *Schema) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return s.Extensions
|
||||
}
|
||||
|
||||
@@ -649,20 +614,20 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
}
|
||||
|
||||
// handle example if set. (3.0)
|
||||
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
|
||||
_, expLabel, expNode := utils.FindKeyNodeFullTop(ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
s.Example = low.NodeReference[any]{Value: ExtractExampleValue(expNode), KeyNode: expLabel, ValueNode: expNode}
|
||||
s.Example = low.NodeReference[*yaml.Node]{Value: expNode, KeyNode: expLabel, ValueNode: expNode}
|
||||
}
|
||||
|
||||
// handle examples if set.(3.1)
|
||||
_, expArrLabel, expArrNode := utils.FindKeyNodeFullTop(ExamplesLabel, root.Content)
|
||||
if expArrNode != nil {
|
||||
if utils.IsNodeArray(expArrNode) {
|
||||
var examples []low.ValueReference[any]
|
||||
var examples []low.ValueReference[*yaml.Node]
|
||||
for i := range expArrNode.Content {
|
||||
examples = append(examples, low.ValueReference[any]{Value: ExtractExampleValue(expArrNode.Content[i]), ValueNode: expArrNode.Content[i]})
|
||||
examples = append(examples, low.ValueReference[*yaml.Node]{Value: expArrNode.Content[i], ValueNode: expArrNode.Content[i]})
|
||||
}
|
||||
s.Examples = low.NodeReference[[]low.ValueReference[any]]{
|
||||
s.Examples = low.NodeReference[[]low.ValueReference[*yaml.Node]]{
|
||||
Value: examples,
|
||||
ValueNode: expArrNode,
|
||||
KeyNode: expArrLabel,
|
||||
@@ -1035,7 +1000,7 @@ func (s *Schema) Build(ctx context.Context, root *yaml.Node, idx *index.SpecInde
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]], error) {
|
||||
func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex, label string) (*low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]], error) {
|
||||
_, propLabel, propsNode := utils.FindKeyNodeFullTop(label, root.Content)
|
||||
if propsNode != nil {
|
||||
propertyMap := orderedmap.New[low.KeyReference[string], low.ValueReference[*SchemaProxy]]()
|
||||
@@ -1047,12 +1012,12 @@ func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex
|
||||
}
|
||||
|
||||
// check our prop isn't reference
|
||||
isRef := false
|
||||
refString := ""
|
||||
var refNode *yaml.Node
|
||||
if h, _, l := utils.IsNodeRefValue(prop); h {
|
||||
ref, _, _, _ := low.LocateRefNodeWithContext(ctx, prop, idx)
|
||||
if ref != nil {
|
||||
isRef = true
|
||||
refNode = prop
|
||||
prop = ref
|
||||
refString = l
|
||||
} else {
|
||||
@@ -1061,16 +1026,19 @@ func buildPropertyMap(ctx context.Context, root *yaml.Node, idx *index.SpecIndex
|
||||
}
|
||||
}
|
||||
|
||||
sp := &SchemaProxy{ctx: ctx, kn: currentProp, vn: prop, idx: idx}
|
||||
sp.SetReference(refString, refNode)
|
||||
|
||||
propertyMap.Set(low.KeyReference[string]{
|
||||
KeyNode: currentProp,
|
||||
Value: currentProp.Value,
|
||||
}, low.ValueReference[*SchemaProxy]{
|
||||
Value: &SchemaProxy{ctx: ctx, kn: currentProp, vn: prop, idx: idx, isReference: isRef, referenceLookup: refString},
|
||||
Value: sp,
|
||||
ValueNode: prop,
|
||||
})
|
||||
}
|
||||
|
||||
return &low.NodeReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]{
|
||||
return &low.NodeReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[*SchemaProxy]]]{
|
||||
Value: propertyMap,
|
||||
KeyNode: propLabel,
|
||||
ValueNode: propsNode,
|
||||
@@ -1112,7 +1080,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
syncChan := make(chan buildResult)
|
||||
|
||||
// build out a SchemaProxy for every sub-schema.
|
||||
build := func(pctx context.Context, kn *yaml.Node, vn *yaml.Node, schemaIdx int, c chan buildResult,
|
||||
build := func(pctx context.Context, kn, vn *yaml.Node, rf *yaml.Node, schemaIdx int, c chan buildResult,
|
||||
isRef bool, refLocation string,
|
||||
) {
|
||||
// a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can
|
||||
@@ -1127,8 +1095,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
sp.idx = idx
|
||||
sp.ctx = pctx
|
||||
if isRef {
|
||||
sp.referenceLookup = refLocation
|
||||
sp.isReference = true
|
||||
sp.SetReference(refLocation, rf)
|
||||
}
|
||||
res := &low.ValueReference[*SchemaProxy]{
|
||||
Value: sp,
|
||||
@@ -1142,6 +1109,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
|
||||
isRef := false
|
||||
refLocation := ""
|
||||
var refNode *yaml.Node
|
||||
foundCtx := ctx
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
h := false
|
||||
@@ -1149,6 +1117,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
isRef = true
|
||||
ref, _, _, fctx := low.LocateRefNodeWithContext(ctx, valueNode, idx)
|
||||
if ref != nil {
|
||||
refNode = valueNode
|
||||
valueNode = ref
|
||||
foundCtx = fctx
|
||||
} else {
|
||||
@@ -1159,7 +1128,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
|
||||
// this only runs once, however to keep things consistent, it makes sense to use the same async method
|
||||
// that arrays will use.
|
||||
go build(foundCtx, labelNode, valueNode, -1, syncChan, isRef, refLocation)
|
||||
go build(foundCtx, labelNode, valueNode, refNode, -1, syncChan, isRef, refLocation)
|
||||
select {
|
||||
case r := <-syncChan:
|
||||
schemas <- schemaProxyBuildResult{
|
||||
@@ -1181,6 +1150,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
isRef = true
|
||||
ref, _, _, fctx := low.LocateRefNodeWithContext(ctx, vn, idx)
|
||||
if ref != nil {
|
||||
refNode = vn
|
||||
vn = ref
|
||||
foundCtx = fctx
|
||||
} else {
|
||||
@@ -1191,7 +1161,7 @@ func buildSchema(ctx context.Context, schemas chan schemaProxyBuildResult, label
|
||||
}
|
||||
}
|
||||
refBuilds++
|
||||
go build(foundCtx, vn, vn, i, syncChan, isRef, refLocation)
|
||||
go build(foundCtx, vn, vn, refNode, i, syncChan, isRef, refLocation)
|
||||
}
|
||||
|
||||
completedBuilds := 0
|
||||
@@ -1225,12 +1195,11 @@ func ExtractSchema(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (
|
||||
var schLabel, schNode *yaml.Node
|
||||
errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d"
|
||||
|
||||
isRef := false
|
||||
refLocation := ""
|
||||
var refNode *yaml.Node
|
||||
|
||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||
// locate reference in index.
|
||||
isRef = true
|
||||
ref, fIdx, _, nCtx := low.LocateRefNodeWithContext(ctx, root, idx)
|
||||
if ref != nil {
|
||||
schNode = ref
|
||||
@@ -1250,9 +1219,9 @@ func ExtractSchema(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (
|
||||
if schNode != nil {
|
||||
h := false
|
||||
if h, _, refLocation = utils.IsNodeRefValue(schNode); h {
|
||||
isRef = true
|
||||
ref, foundIdx, _, nCtx := low.LocateRefNodeWithContext(ctx, schNode, idx)
|
||||
if ref != nil {
|
||||
refNode = schNode
|
||||
schNode = ref
|
||||
if foundIdx != nil {
|
||||
// TODO: check on this
|
||||
@@ -1273,11 +1242,16 @@ func ExtractSchema(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (
|
||||
|
||||
if schNode != nil {
|
||||
// check if schema has already been built.
|
||||
schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx, ctx: ctx, isReference: isRef, referenceLookup: refLocation}
|
||||
return &low.NodeReference[*SchemaProxy]{
|
||||
Value: schema, KeyNode: schLabel, ValueNode: schNode, ReferenceNode: isRef,
|
||||
Reference: refLocation,
|
||||
}, nil
|
||||
schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx, ctx: ctx}
|
||||
schema.SetReference(refLocation, refNode)
|
||||
|
||||
n := &low.NodeReference[*SchemaProxy]{
|
||||
Value: schema,
|
||||
KeyNode: schLabel,
|
||||
ValueNode: schNode,
|
||||
}
|
||||
n.SetReference(refLocation, refNode)
|
||||
return n, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ package base
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -44,14 +46,13 @@ import (
|
||||
// it's not actually JSONSchema until 3.1, so lots of times a bad schema will break parsing. Errors are only found
|
||||
// when a schema is needed, so the rest of the document is parsed and ready to use.
|
||||
type SchemaProxy struct {
|
||||
kn *yaml.Node
|
||||
vn *yaml.Node
|
||||
idx *index.SpecIndex
|
||||
rendered *Schema
|
||||
buildError error
|
||||
isReference bool // Is the schema underneath originally a $ref?
|
||||
referenceLookup string // If the schema is a $ref, what's its name?
|
||||
ctx context.Context
|
||||
low.Reference
|
||||
kn *yaml.Node
|
||||
vn *yaml.Node
|
||||
idx *index.SpecIndex
|
||||
rendered *Schema
|
||||
buildError error
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// Build will prepare the SchemaProxy for rendering, it does not build the Schema, only sets up internal state.
|
||||
@@ -62,8 +63,7 @@ func (sp *SchemaProxy) Build(ctx context.Context, key, value *yaml.Node, idx *in
|
||||
sp.idx = idx
|
||||
sp.ctx = ctx
|
||||
if rf, _, r := utils.IsNodeRefValue(value); rf {
|
||||
sp.isReference = true
|
||||
sp.referenceLookup = r
|
||||
sp.SetReference(r, value)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -101,36 +101,6 @@ func (sp *SchemaProxy) GetBuildError() error {
|
||||
return sp.buildError
|
||||
}
|
||||
|
||||
// IsSchemaReference returns true if the Schema that this SchemaProxy represents, is actually a reference to
|
||||
// a Schema contained within Components or Definitions. There is no difference in the mechanism used to resolve the
|
||||
// Schema when calling Schema(), however if we want to know if this schema was originally a reference, we won't
|
||||
// be able to determine that from the model, without this bit.
|
||||
func (sp *SchemaProxy) IsSchemaReference() bool {
|
||||
return sp.isReference
|
||||
}
|
||||
|
||||
// IsReference is an alias for IsSchemaReference() except it's compatible wih the IsReferenced interface type.
|
||||
func (sp *SchemaProxy) IsReference() bool {
|
||||
return sp.IsSchemaReference()
|
||||
}
|
||||
|
||||
// GetReference is an alias for GetSchemaReference() except it's compatible wih the IsReferenced interface type.
|
||||
func (sp *SchemaProxy) GetReference() string {
|
||||
return sp.GetSchemaReference()
|
||||
}
|
||||
|
||||
// SetReference will set the reference lookup for this SchemaProxy.
|
||||
func (sp *SchemaProxy) SetReference(ref string) {
|
||||
sp.referenceLookup = ref
|
||||
}
|
||||
|
||||
// GetSchemaReference will return the lookup defined by the $ref that this schema points to. If the schema
|
||||
// is inline, and not a reference, then this method returns an empty string. Only useful when combined with
|
||||
// IsSchemaReference()
|
||||
func (sp *SchemaProxy) GetSchemaReference() string {
|
||||
return sp.referenceLookup
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) GetSchemaReferenceLocation() *index.NodeOrigin {
|
||||
if sp.idx != nil {
|
||||
origin := sp.idx.FindNodeOrigin(sp.vn)
|
||||
@@ -158,11 +128,11 @@ func (sp *SchemaProxy) GetValueNode() *yaml.Node {
|
||||
// Hash will return a consistent SHA256 Hash of the SchemaProxy object (it will resolve it)
|
||||
func (sp *SchemaProxy) Hash() [32]byte {
|
||||
if sp.rendered != nil {
|
||||
if !sp.isReference {
|
||||
if !sp.IsReference() {
|
||||
return sp.rendered.Hash()
|
||||
}
|
||||
} else {
|
||||
if !sp.isReference {
|
||||
if !sp.IsReference() {
|
||||
// only resolve this proxy if it's not a ref.
|
||||
sch := sp.Schema()
|
||||
sp.rendered = sch
|
||||
@@ -170,5 +140,5 @@ func (sp *SchemaProxy) Hash() [32]byte {
|
||||
}
|
||||
}
|
||||
// hash reference value only, do not resolve!
|
||||
return sha256.Sum256([]byte(sp.referenceLookup))
|
||||
return sha256.Sum256([]byte(sp.GetReference()))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSchemaProxy_Build(t *testing.T) {
|
||||
|
||||
yml := `x-windows: washed
|
||||
description: something`
|
||||
|
||||
@@ -24,29 +25,25 @@ description: something`
|
||||
err := sch.Build(context.Background(), &idxNode, idxNode.Content[0], nil)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "db2a35dd6fb3d9481d0682571b9d687616bb2a34c1887f7863f0b2e769ca7b23",
|
||||
assert.Equal(t, "e20c009d370944d177c0b46e8fa29e15fadc3a6f9cca6bb251ff9e120265fc96",
|
||||
low.GenerateHashString(&sch))
|
||||
|
||||
assert.Equal(t, "something", sch.Schema().Description.Value)
|
||||
assert.Empty(t, sch.GetSchemaReference())
|
||||
assert.Equal(t, "something", sch.Schema().Description.GetValue())
|
||||
assert.Empty(t, sch.GetReference())
|
||||
assert.NotNil(t, sch.GetKeyNode())
|
||||
assert.NotNil(t, sch.GetValueNode())
|
||||
assert.False(t, sch.IsSchemaReference())
|
||||
assert.False(t, sch.IsReference())
|
||||
assert.Empty(t, sch.GetReference())
|
||||
sch.SetReference("coffee")
|
||||
sch.SetReference("coffee", nil)
|
||||
assert.Equal(t, "coffee", sch.GetReference())
|
||||
|
||||
// already rendered, should spit out the same
|
||||
assert.Equal(t, "db2a35dd6fb3d9481d0682571b9d687616bb2a34c1887f7863f0b2e769ca7b23",
|
||||
assert.Equal(t, "37290d74ac4d186e3a8e5785d259d2ec04fac91ae28092e7620ec8bc99e830aa",
|
||||
low.GenerateHashString(&sch))
|
||||
|
||||
assert.Len(t, sch.Schema().GetExtensions(), 1)
|
||||
|
||||
assert.Equal(t, 1, orderedmap.Len(sch.Schema().GetExtensions()))
|
||||
}
|
||||
|
||||
func TestSchemaProxy_Build_CheckRef(t *testing.T) {
|
||||
|
||||
yml := `$ref: wat`
|
||||
|
||||
var sch SchemaProxy
|
||||
@@ -55,14 +52,13 @@ func TestSchemaProxy_Build_CheckRef(t *testing.T) {
|
||||
|
||||
err := sch.Build(context.Background(), nil, idxNode.Content[0], nil)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, sch.IsSchemaReference())
|
||||
assert.Equal(t, "wat", sch.GetSchemaReference())
|
||||
assert.True(t, sch.IsReference())
|
||||
assert.Equal(t, "wat", sch.GetReference())
|
||||
assert.Equal(t, "f00a787f7492a95e165b470702f4fe9373583fbdc025b2c8bdf0262cc48fcff4",
|
||||
low.GenerateHashString(&sch))
|
||||
}
|
||||
|
||||
func TestSchemaProxy_Build_HashInline(t *testing.T) {
|
||||
|
||||
yml := `type: int`
|
||||
|
||||
var sch SchemaProxy
|
||||
@@ -71,14 +67,13 @@ func TestSchemaProxy_Build_HashInline(t *testing.T) {
|
||||
|
||||
err := sch.Build(context.Background(), nil, idxNode.Content[0], nil)
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, sch.IsSchemaReference())
|
||||
assert.False(t, sch.IsReference())
|
||||
assert.NotNil(t, sch.Schema())
|
||||
assert.Equal(t, "6da88c34ba124c41f977db66a4fc5c1a951708d285c81bb0d47c3206f4c27ca8",
|
||||
low.GenerateHashString(&sch))
|
||||
}
|
||||
|
||||
func TestSchemaProxy_Build_UsingMergeNodes(t *testing.T) {
|
||||
|
||||
yml := `
|
||||
x-common-definitions:
|
||||
life_cycle_types: &life_cycle_types_def
|
||||
@@ -95,11 +90,9 @@ x-common-definitions:
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, sch.Schema().Enum.Value, 3)
|
||||
assert.Equal(t, "The type of life cycle", sch.Schema().Description.Value)
|
||||
|
||||
}
|
||||
|
||||
func TestSchemaProxy_GetSchemaReferenceLocation(t *testing.T) {
|
||||
|
||||
yml := `type: object
|
||||
properties:
|
||||
name:
|
||||
@@ -159,5 +152,4 @@ properties:
|
||||
err = schC.Build(context.Background(), nil, idxNodeA.Content[0], nil)
|
||||
origin = schC.GetSchemaReferenceLocation()
|
||||
assert.Nil(t, origin)
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
@@ -9,7 +11,6 @@ import (
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func test_get_schema_blob() string {
|
||||
@@ -192,8 +193,8 @@ func Test_Schema(t *testing.T) {
|
||||
assert.Equal(t, "an xml namespace", j.XML.Value.Namespace.Value)
|
||||
assert.Equal(t, "a prefix", j.XML.Value.Prefix.Value)
|
||||
assert.Equal(t, true, j.XML.Value.Attribute.Value)
|
||||
assert.Len(t, j.XML.Value.Extensions, 1)
|
||||
assert.Len(t, j.XML.Value.GetExtensions(), 1)
|
||||
assert.Equal(t, 1, orderedmap.Len(j.XML.Value.Extensions))
|
||||
assert.Equal(t, 1, orderedmap.Len(j.XML.Value.GetExtensions()))
|
||||
|
||||
assert.NotNil(t, v.Value.Schema().AdditionalProperties.Value)
|
||||
|
||||
@@ -213,12 +214,20 @@ func Test_Schema(t *testing.T) {
|
||||
io := v.Value.Schema()
|
||||
|
||||
assert.Equal(t, "allOfA description", io.Description.Value)
|
||||
assert.Equal(t, "allOfAExp", io.Example.Value)
|
||||
|
||||
var ioExample string
|
||||
_ = io.Example.GetValueNode().Decode(&ioExample)
|
||||
|
||||
assert.Equal(t, "allOfAExp", ioExample)
|
||||
|
||||
qw := f.FindProperty("allOfB").Value.Schema()
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "allOfB description", qw.Description.Value)
|
||||
assert.Equal(t, "allOfBExp", qw.Example.Value)
|
||||
|
||||
var qwExample string
|
||||
_ = qw.Example.GetValueNode().Decode(&qwExample)
|
||||
|
||||
assert.Equal(t, "allOfBExp", qwExample)
|
||||
|
||||
// check polymorphic values anyOf
|
||||
assert.Equal(t, "an anyOf thing", sch.AnyOf.Value[0].Value.Schema().Description.Value)
|
||||
@@ -227,12 +236,18 @@ func Test_Schema(t *testing.T) {
|
||||
v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "anyOfA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "anyOfAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
var vSchemaExample string
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
|
||||
assert.Equal(t, "anyOfAExp", vSchemaExample)
|
||||
|
||||
v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "anyOfB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "anyOfBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "anyOfBExp", vSchemaExample)
|
||||
|
||||
// check polymorphic values oneOf
|
||||
assert.Equal(t, "a oneof thing", sch.OneOf.Value[0].Value.Schema().Description.Value)
|
||||
@@ -241,12 +256,16 @@ func Test_Schema(t *testing.T) {
|
||||
v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "oneOfA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "oneOfAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "oneOfAExp", vSchemaExample)
|
||||
|
||||
v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "oneOfB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "oneOfBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "oneOfBExp", vSchemaExample)
|
||||
|
||||
// check values NOT
|
||||
assert.Equal(t, "a not thing", sch.Not.Value.Schema().Description.Value)
|
||||
@@ -255,12 +274,16 @@ func Test_Schema(t *testing.T) {
|
||||
v = sch.Not.Value.Schema().FindProperty("notA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "notA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "notAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "notAExp", vSchemaExample)
|
||||
|
||||
v = sch.Not.Value.Schema().FindProperty("notB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "notB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "notBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "notBExp", vSchemaExample)
|
||||
|
||||
// check values Items
|
||||
assert.Equal(t, "an items thing", sch.Items.Value.A.Schema().Description.Value)
|
||||
@@ -269,12 +292,16 @@ func Test_Schema(t *testing.T) {
|
||||
v = sch.Items.Value.A.Schema().FindProperty("itemsA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "itemsAExp", vSchemaExample)
|
||||
|
||||
v = sch.Items.Value.A.Schema().FindProperty("itemsB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "itemsBExp", vSchemaExample)
|
||||
|
||||
// check values PrefixItems
|
||||
assert.Equal(t, "an items thing", sch.PrefixItems.Value[0].Value.Schema().Description.Value)
|
||||
@@ -283,17 +310,21 @@ func Test_Schema(t *testing.T) {
|
||||
v = sch.PrefixItems.Value[0].Value.Schema().FindProperty("itemsA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValueNode().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "itemsAExp", vSchemaExample)
|
||||
|
||||
v = sch.PrefixItems.Value[0].Value.Schema().FindProperty("itemsB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
_ = v.GetValue().Schema().Example.GetValue().Decode(&vSchemaExample)
|
||||
assert.Equal(t, "itemsBExp", vSchemaExample)
|
||||
|
||||
// check discriminator
|
||||
assert.NotNil(t, sch.Discriminator.Value)
|
||||
assert.Equal(t, "athing", sch.Discriminator.Value.PropertyName.Value)
|
||||
assert.Len(t, sch.Discriminator.Value.Mapping.Value, 2)
|
||||
assert.Equal(t, 2, sch.Discriminator.GetValue().Mapping.GetValue().Len())
|
||||
mv := sch.Discriminator.Value.FindMappingValue("log")
|
||||
assert.Equal(t, "cat", mv.Value)
|
||||
mv = sch.Discriminator.Value.FindMappingValue("pizza")
|
||||
@@ -429,12 +460,20 @@ const: tasty`
|
||||
assert.Equal(t, float64(12), sch.ExclusiveMinimum.Value.B)
|
||||
assert.Equal(t, float64(13), sch.ExclusiveMaximum.Value.B)
|
||||
assert.Len(t, sch.Examples.Value, 1)
|
||||
assert.Equal(t, "testing", sch.Examples.Value[0].Value)
|
||||
|
||||
var example0 string
|
||||
_ = sch.Examples.GetValue()[0].GetValue().Decode(&example0)
|
||||
|
||||
assert.Equal(t, "testing", example0)
|
||||
assert.Equal(t, "fish64", sch.ContentEncoding.Value)
|
||||
assert.Equal(t, "fish/paste", sch.ContentMediaType.Value)
|
||||
assert.True(t, sch.Items.Value.IsB())
|
||||
assert.True(t, sch.Items.Value.B)
|
||||
assert.Equal(t, "tasty", sch.Const.Value)
|
||||
|
||||
var schConst string
|
||||
_ = sch.Const.GetValue().Decode(&schConst)
|
||||
|
||||
assert.Equal(t, "tasty", schConst)
|
||||
}
|
||||
|
||||
func TestSchema_Build_PropsLookup(t *testing.T) {
|
||||
@@ -986,7 +1025,11 @@ schema:
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res.Value)
|
||||
sch := res.Value.Schema()
|
||||
assert.Equal(t, 5, sch.Default.Value)
|
||||
|
||||
var def int
|
||||
_ = sch.Default.GetValueNode().Decode(&def)
|
||||
|
||||
assert.Equal(t, 5, def)
|
||||
}
|
||||
|
||||
func TestExtractSchema_ConstPrimitive(t *testing.T) {
|
||||
@@ -1002,7 +1045,11 @@ schema:
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res.Value)
|
||||
sch := res.Value.Schema()
|
||||
assert.Equal(t, 5, sch.Const.Value)
|
||||
|
||||
var cnst int
|
||||
_ = sch.Const.GetValueNode().Decode(&cnst)
|
||||
|
||||
assert.Equal(t, 5, cnst)
|
||||
}
|
||||
|
||||
func TestExtractSchema_Ref(t *testing.T) {
|
||||
@@ -1785,7 +1832,6 @@ components:
|
||||
res, e := ExtractSchema(context.Background(), idxNode.Content[0], idx)
|
||||
assert.Nil(t, res)
|
||||
assert.Equal(t, "schema build failed: reference '[empty]' cannot be found at line 2, col 9", e.Error())
|
||||
|
||||
}
|
||||
|
||||
func TestSchema_EmptyRef(t *testing.T) {
|
||||
@@ -1814,5 +1860,4 @@ components:
|
||||
res, e := ExtractSchema(context.Background(), idxNode.Content[0], idx)
|
||||
assert.Nil(t, res)
|
||||
assert.Equal(t, "schema build failed: reference '[empty]' cannot be found at line 1, col 7", e.Error())
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import (
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/#security-requirement-object
|
||||
type SecurityRequirement struct {
|
||||
Requirements low.ValueReference[orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]]
|
||||
Requirements low.ValueReference[*orderedmap.Map[low.KeyReference[string], low.ValueReference[[]low.ValueReference[string]]]]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func (s *SecurityRequirement) Build(_ context.Context, _, root *yaml.Node, _ *in
|
||||
},
|
||||
)
|
||||
}
|
||||
s.Requirements = low.ValueReference[orderedmap.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,
|
||||
}
|
||||
@@ -91,22 +91,14 @@ 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, orderedmap.Len(s.Requirements.Value))
|
||||
var valKeys []string
|
||||
for pair := orderedmap.First(s.Requirements.Value); pair != nil; pair = pair.Next() {
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(s.Requirements.Value)); pair != nil; pair = pair.Next() {
|
||||
var vals []string
|
||||
for y := range pair.Value().Value {
|
||||
vals = append(vals, pair.Value().Value[y].Value)
|
||||
}
|
||||
sort.Strings(vals)
|
||||
valKeys = append(valKeys, pair.Key().Value)
|
||||
if len(vals) > 0 {
|
||||
values[pair.Key().Value] = vals
|
||||
}
|
||||
}
|
||||
sort.Strings(valKeys)
|
||||
for val := range valKeys {
|
||||
f = append(f, fmt.Sprintf("%s-%s", valKeys[val], strings.Join(values[valKeys[val]], "|")))
|
||||
|
||||
f = append(f, fmt.Sprintf("%s-%s", pair.Key().Value, strings.Join(vals, "|")))
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ package base
|
||||
import (
|
||||
"context"
|
||||
"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"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Tag represents a low-level Tag instance that is backed by a low-level one.
|
||||
@@ -25,13 +25,13 @@ type Tag struct {
|
||||
Name low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
ExternalDocs low.NodeReference[*ExternalDoc]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||
func (t *Tag) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, t.Extensions)
|
||||
func (t *Tag) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(ext, t.Extensions)
|
||||
}
|
||||
|
||||
// Build will extract extensions and external docs for the Tag.
|
||||
@@ -48,7 +48,7 @@ func (t *Tag) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecInde
|
||||
}
|
||||
|
||||
// GetExtensions returns all Tag extensions and satisfies the low.HasExtensions interface.
|
||||
func (t *Tag) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (t *Tag) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return t.Extensions
|
||||
}
|
||||
|
||||
@@ -64,13 +64,6 @@ func (t *Tag) Hash() [32]byte {
|
||||
if !t.ExternalDocs.IsEmpty() {
|
||||
f = append(f, low.GenerateHashString(t.ExternalDocs.Value))
|
||||
}
|
||||
keys := make([]string, len(t.Extensions))
|
||||
z := 0
|
||||
for k := range t.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(t.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(t.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ package base
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTag_Build(t *testing.T) {
|
||||
|
||||
yml := `name: a tag
|
||||
description: a description
|
||||
externalDocs:
|
||||
@@ -33,13 +34,15 @@ x-coffee: tasty`
|
||||
assert.Equal(t, "a tag", n.Name.Value)
|
||||
assert.Equal(t, "a description", n.Description.Value)
|
||||
assert.Equal(t, "https://pb33f.io", n.ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, "tasty", n.FindExtension("x-coffee").Value)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
|
||||
var xCoffee string
|
||||
_ = n.FindExtension("x-coffee").GetValue().Decode(&xCoffee)
|
||||
|
||||
assert.Equal(t, "tasty", xCoffee)
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
func TestTag_Build_Error(t *testing.T) {
|
||||
|
||||
yml := `name: a tag
|
||||
description: a description
|
||||
externalDocs:
|
||||
@@ -58,7 +61,6 @@ externalDocs:
|
||||
}
|
||||
|
||||
func TestTag_Hash(t *testing.T) {
|
||||
|
||||
left := `name: melody
|
||||
description: my princess
|
||||
externalDocs:
|
||||
@@ -84,5 +86,4 @@ x-b33f: princess`
|
||||
_ = rDoc.Build(context.Background(), nil, rNode.Content[0], nil)
|
||||
|
||||
assert.Equal(t, lDoc.Hash(), rDoc.Hash())
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,13 @@ package base
|
||||
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"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// XML represents a low-level representation of an XML object defined by all versions of OpenAPI.
|
||||
@@ -26,7 +27,7 @@ type XML struct {
|
||||
Prefix low.NodeReference[string]
|
||||
Attribute low.NodeReference[bool]
|
||||
Wrapped low.NodeReference[bool]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
*low.Reference
|
||||
}
|
||||
|
||||
@@ -40,7 +41,7 @@ func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||
}
|
||||
|
||||
// GetExtensions returns all Tag extensions and satisfies the low.HasExtensions interface.
|
||||
func (x *XML) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (x *XML) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return x.Extensions
|
||||
}
|
||||
|
||||
@@ -62,13 +63,6 @@ func (x *XML) Hash() [32]byte {
|
||||
if !x.Wrapped.IsEmpty() {
|
||||
f = append(f, fmt.Sprint(x.Wrapped.Value))
|
||||
}
|
||||
keys := make([]string, len(x.Extensions))
|
||||
z := 0
|
||||
for k := range x.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(x.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(x.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -7,37 +7,24 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"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"
|
||||
"net/url"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FindItemInMap 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 FindItemInMap[T any](item string, collection map[KeyReference[string]]ValueReference[T]) *ValueReference[T] {
|
||||
for n, o := range collection {
|
||||
if n.Value == item {
|
||||
return &o
|
||||
}
|
||||
if strings.EqualFold(item, n.Value) {
|
||||
return &o
|
||||
}
|
||||
}
|
||||
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] {
|
||||
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 {
|
||||
@@ -50,6 +37,18 @@ func FindItemInOrderedMap[T any](item string, collection orderedmap.Map[KeyRefer
|
||||
return nil
|
||||
}
|
||||
|
||||
// HashExtensions will generate a hash from the low representation of extensions.
|
||||
func HashExtensions(ext *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]) []string {
|
||||
f := []string{}
|
||||
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(ext)); pair != nil; pair = pair.Next() {
|
||||
b, _ := yaml.Marshal(pair.Value().GetValue())
|
||||
f = append(f, fmt.Sprintf("%s-%x", pair.Key().Value, sha256.Sum256([]byte(b))))
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// 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{
|
||||
@@ -68,7 +67,6 @@ func generateIndexCollection(idx *index.SpecIndex) []func() map[string]*index.Re
|
||||
}
|
||||
|
||||
func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.SpecIndex) (*yaml.Node, *index.SpecIndex, error, context.Context) {
|
||||
|
||||
if rf, _, rv := utils.IsNodeRefValue(root); rf {
|
||||
|
||||
if rv == "" {
|
||||
@@ -112,9 +110,7 @@ func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.S
|
||||
explodedRefValue := strings.Split(rv, "#")
|
||||
if len(explodedRefValue) == 2 {
|
||||
if !strings.HasPrefix(explodedRefValue[0], "http") {
|
||||
|
||||
if !filepath.IsAbs(explodedRefValue[0]) {
|
||||
|
||||
if strings.HasPrefix(specPath, "http") {
|
||||
u, _ := url.Parse(specPath)
|
||||
p := ""
|
||||
@@ -137,7 +133,6 @@ func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.S
|
||||
}
|
||||
rv = fmt.Sprintf("%s#%s", abs, explodedRefValue[1])
|
||||
} else {
|
||||
|
||||
// check for a config baseURL and use that if it exists.
|
||||
if idx.GetConfig().BaseURL != nil {
|
||||
|
||||
@@ -154,11 +149,8 @@ func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.S
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if !strings.HasPrefix(explodedRefValue[0], "http") {
|
||||
|
||||
if !filepath.IsAbs(explodedRefValue[0]) {
|
||||
|
||||
if strings.HasPrefix(specPath, "http") {
|
||||
u, _ := url.Parse(specPath)
|
||||
p := filepath.Dir(u.Path)
|
||||
@@ -173,7 +165,6 @@ func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.S
|
||||
rv = abs
|
||||
|
||||
} else {
|
||||
|
||||
// check for a config baseURL and use that if it exists.
|
||||
if idx.GetConfig().BaseURL != nil {
|
||||
u := *idx.GetConfig().BaseURL
|
||||
@@ -211,7 +202,6 @@ func LocateRefNodeWithContext(ctx context.Context, root *yaml.Node, idx *index.S
|
||||
rv, root.Line, root.Column), ctx
|
||||
}
|
||||
return nil, idx, nil, ctx
|
||||
|
||||
}
|
||||
|
||||
// LocateRefNode will perform a complete lookup for a $ref node. This function searches the entire index for
|
||||
@@ -227,10 +217,12 @@ func ExtractObjectRaw[T Buildable[N], N any](ctx context.Context, key, root *yam
|
||||
var circError error
|
||||
var isReference bool
|
||||
var referenceValue string
|
||||
var refNode *yaml.Node
|
||||
root = utils.NodeAlias(root)
|
||||
if h, _, rv := utils.IsNodeRefValue(root); h {
|
||||
ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, root, idx)
|
||||
if ref != nil {
|
||||
refNode = root
|
||||
root = ref
|
||||
isReference = true
|
||||
referenceValue = rv
|
||||
@@ -257,7 +249,7 @@ func ExtractObjectRaw[T Buildable[N], N any](ctx context.Context, key, root *yam
|
||||
|
||||
// if this is a reference, keep track of the reference in the value
|
||||
if isReference {
|
||||
SetReference(n, referenceValue)
|
||||
SetReference(n, referenceValue, refNode)
|
||||
}
|
||||
|
||||
// do we want to throw an error as well if circular error reporting is on?
|
||||
@@ -274,10 +266,12 @@ func ExtractObject[T Buildable[N], N any](ctx context.Context, label string, roo
|
||||
var circError error
|
||||
var isReference bool
|
||||
var referenceValue string
|
||||
var refNode *yaml.Node
|
||||
root = utils.NodeAlias(root)
|
||||
if rf, rl, refVal := utils.IsNodeRefValue(root); rf {
|
||||
ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, root, idx)
|
||||
if ref != nil {
|
||||
refNode = root
|
||||
vn = ref
|
||||
ln = rl
|
||||
isReference = true
|
||||
@@ -298,6 +292,7 @@ func ExtractObject[T Buildable[N], N any](ctx context.Context, label string, roo
|
||||
if h, _, rVal := utils.IsNodeRefValue(vn); h {
|
||||
ref, fIdx, lerr, nCtx := LocateRefNodeWithContext(ctx, vn, idx)
|
||||
if ref != nil {
|
||||
refNode = vn
|
||||
vn = ref
|
||||
if fIdx != nil {
|
||||
idx = fIdx
|
||||
@@ -331,16 +326,15 @@ func ExtractObject[T Buildable[N], N any](ctx context.Context, label string, roo
|
||||
|
||||
// if this is a reference, keep track of the reference in the value
|
||||
if isReference {
|
||||
SetReference(n, referenceValue)
|
||||
SetReference(n, referenceValue, refNode)
|
||||
}
|
||||
|
||||
res := NodeReference[T]{
|
||||
Value: n,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
ReferenceNode: isReference,
|
||||
Reference: referenceValue,
|
||||
Value: n,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
res.SetReference(referenceValue, refNode)
|
||||
|
||||
// do we want to throw an error as well if circular error reporting is on?
|
||||
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||
@@ -349,12 +343,13 @@ func ExtractObject[T Buildable[N], N any](ctx context.Context, label string, roo
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func SetReference(obj any, ref string) {
|
||||
func SetReference(obj any, ref string, refNode *yaml.Node) {
|
||||
if obj == nil {
|
||||
return
|
||||
}
|
||||
if r, ok := obj.(IsReferenced); ok {
|
||||
r.SetReference(ref)
|
||||
|
||||
if r, ok := obj.(SetReferencer); ok {
|
||||
r.SetReference(ref, refNode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,9 +424,12 @@ func ExtractArray[T Buildable[N], N any](ctx context.Context, label string, root
|
||||
foundCtx := ctx
|
||||
foundIndex := idx
|
||||
|
||||
var refNode *yaml.Node
|
||||
|
||||
if rf, _, rv := utils.IsNodeRefValue(node); rf {
|
||||
refg, fIdx, err, nCtx := LocateRefEnd(ctx, node, idx, 0)
|
||||
if refg != nil {
|
||||
refNode = node
|
||||
node = refg
|
||||
localReferenceValue = rv
|
||||
foundIndex = fIdx
|
||||
@@ -457,15 +455,16 @@ func ExtractArray[T Buildable[N], N any](ctx context.Context, label string, root
|
||||
}
|
||||
|
||||
if localReferenceValue != "" {
|
||||
SetReference(n, localReferenceValue)
|
||||
SetReference(n, localReferenceValue, refNode)
|
||||
}
|
||||
|
||||
items = append(items, ValueReference[T]{
|
||||
Value: n,
|
||||
ValueNode: node,
|
||||
ReferenceNode: localReferenceValue != "",
|
||||
Reference: localReferenceValue,
|
||||
})
|
||||
v := ValueReference[T]{
|
||||
Value: n,
|
||||
ValueNode: node,
|
||||
}
|
||||
v.SetReference(localReferenceValue, refNode)
|
||||
|
||||
items = append(items, v)
|
||||
}
|
||||
}
|
||||
// include circular errors?
|
||||
@@ -475,23 +474,6 @@ func ExtractArray[T Buildable[N], N any](ctx context.Context, label string, root
|
||||
return items, ln, vn, nil
|
||||
}
|
||||
|
||||
// ExtractExample will extract a value supplied as an example into a NodeReference. Value can be anything.
|
||||
// the node value is untyped, so casting will be required when trying to use it.
|
||||
func ExtractExample(expNode, expLabel *yaml.Node) NodeReference[any] {
|
||||
ref := NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
|
||||
if utils.IsNodeMap(expNode) {
|
||||
var decoded map[string]interface{}
|
||||
_ = expNode.Decode(&decoded)
|
||||
ref.Value = decoded
|
||||
}
|
||||
if utils.IsNodeArray(expNode) {
|
||||
var decoded []interface{}
|
||||
_ = expNode.Decode(&decoded)
|
||||
ref.Value = decoded
|
||||
}
|
||||
return ref
|
||||
}
|
||||
|
||||
// ExtractMapNoLookupExtensions will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'NoLookup' part
|
||||
// refers to the fact that there is no key supplied as part of the extraction, there is no lookup performed and the
|
||||
// root yaml.Node pointer is used directly. Pass a true bit to includeExtensions to include extension keys in the map.
|
||||
@@ -502,7 +484,7 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
|
||||
root *yaml.Node,
|
||||
idx *index.SpecIndex,
|
||||
includeExtensions bool,
|
||||
) (orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
|
||||
) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
|
||||
valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]()
|
||||
var circError error
|
||||
if utils.IsNodeMap(root) {
|
||||
@@ -540,10 +522,12 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
|
||||
|
||||
var isReference bool
|
||||
var referenceValue string
|
||||
var refNode *yaml.Node
|
||||
// if value is a reference, we have to look it up in the index!
|
||||
if h, _, rv := utils.IsNodeRefValue(node); h {
|
||||
ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, node, idx)
|
||||
if ref != nil {
|
||||
refNode = node
|
||||
node = ref
|
||||
isReference = true
|
||||
referenceValue = rv
|
||||
@@ -570,19 +554,21 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
|
||||
return nil, berr
|
||||
}
|
||||
if isReference {
|
||||
SetReference(n, referenceValue)
|
||||
SetReference(n, referenceValue, refNode)
|
||||
}
|
||||
if currentKey != nil {
|
||||
v := ValueReference[PT]{
|
||||
Value: n,
|
||||
ValueNode: node,
|
||||
}
|
||||
v.SetReference(referenceValue, refNode)
|
||||
|
||||
valueMap.Set(
|
||||
KeyReference[string]{
|
||||
Value: currentKey.Value,
|
||||
KeyNode: currentKey,
|
||||
},
|
||||
ValueReference[PT]{
|
||||
Value: n,
|
||||
ValueNode: node,
|
||||
Reference: referenceValue,
|
||||
},
|
||||
v,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -591,7 +577,6 @@ func ExtractMapNoLookupExtensions[PT Buildable[N], N any](
|
||||
return valueMap, circError
|
||||
}
|
||||
return valueMap, nil
|
||||
|
||||
}
|
||||
|
||||
// ExtractMapNoLookup will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'NoLookup' part
|
||||
@@ -603,7 +588,7 @@ func ExtractMapNoLookup[PT Buildable[N], N any](
|
||||
ctx context.Context,
|
||||
root *yaml.Node,
|
||||
idx *index.SpecIndex,
|
||||
) (orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
|
||||
) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], error) {
|
||||
return ExtractMapNoLookupExtensions[PT, N](ctx, root, idx, false)
|
||||
}
|
||||
|
||||
@@ -624,7 +609,186 @@ func ExtractMapExtensions[PT Buildable[N], N any](
|
||||
root *yaml.Node,
|
||||
idx *index.SpecIndex,
|
||||
extensions bool,
|
||||
) (orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) {
|
||||
) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) {
|
||||
var labelNode, valueNode *yaml.Node
|
||||
var circError error
|
||||
root = utils.NodeAlias(root)
|
||||
if rf, rl, _ := utils.IsNodeRefValue(root); rf {
|
||||
// locate reference in index.
|
||||
ref, fIdx, err, fCtx := LocateRefNodeWithContext(ctx, root, idx)
|
||||
if ref != nil {
|
||||
valueNode = ref
|
||||
labelNode = rl
|
||||
ctx = fCtx
|
||||
idx = fIdx
|
||||
if err != nil {
|
||||
circError = err
|
||||
}
|
||||
} else {
|
||||
return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s",
|
||||
root.Content[1].Value)
|
||||
}
|
||||
} else {
|
||||
_, labelNode, valueNode = utils.FindKeyNodeFull(label, root.Content)
|
||||
valueNode = utils.NodeAlias(valueNode)
|
||||
if valueNode != nil {
|
||||
if h, _, _ := utils.IsNodeRefValue(valueNode); h {
|
||||
ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, valueNode, idx)
|
||||
if ref != nil {
|
||||
valueNode = ref
|
||||
idx = fIdx
|
||||
ctx = nCtx
|
||||
if err != nil {
|
||||
circError = err
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
return nil, labelNode, valueNode, fmt.Errorf("map build failed: reference cannot be found: %s",
|
||||
err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if valueNode != nil {
|
||||
valueMap := orderedmap.New[KeyReference[string], ValueReference[PT]]()
|
||||
|
||||
type buildInput struct {
|
||||
label *yaml.Node
|
||||
value *yaml.Node
|
||||
}
|
||||
in := make(chan buildInput)
|
||||
out := make(chan mappingResult[PT])
|
||||
done := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2) // input and output goroutines.
|
||||
|
||||
// TranslatePipeline input.
|
||||
go func() {
|
||||
defer func() {
|
||||
close(in)
|
||||
wg.Done()
|
||||
}()
|
||||
var currentLabelNode *yaml.Node
|
||||
for i, en := range valueNode.Content {
|
||||
if !extensions {
|
||||
if strings.HasPrefix(en.Value, "x-") {
|
||||
continue // yo, don't pay any attention to extensions, not here anyway.
|
||||
}
|
||||
}
|
||||
|
||||
en = utils.NodeAlias(en)
|
||||
if i%2 == 0 {
|
||||
currentLabelNode = en
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case in <- buildInput{
|
||||
label: currentLabelNode,
|
||||
value: en,
|
||||
}:
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// TranslatePipeline output.
|
||||
go func() {
|
||||
for {
|
||||
result, ok := <-out
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
valueMap.Set(result.k, result.v)
|
||||
}
|
||||
close(done)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
translateFunc := func(input buildInput) (mappingResult[PT], error) {
|
||||
foundIndex := idx
|
||||
foundContext := ctx
|
||||
|
||||
en := input.value
|
||||
|
||||
var refNode *yaml.Node
|
||||
var referenceValue string
|
||||
// check our valueNode isn't a reference still.
|
||||
if h, _, refVal := utils.IsNodeRefValue(en); h {
|
||||
ref, fIdx, err, nCtx := LocateRefNodeWithContext(ctx, en, idx)
|
||||
if ref != nil {
|
||||
refNode = en
|
||||
en = ref
|
||||
referenceValue = refVal
|
||||
if fIdx != nil {
|
||||
foundIndex = fIdx
|
||||
}
|
||||
foundContext = nCtx
|
||||
if err != nil {
|
||||
circError = err
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
return mappingResult[PT]{}, fmt.Errorf("flat map build failed: reference cannot be found: %s",
|
||||
err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var n PT = new(N)
|
||||
en = utils.NodeAlias(en)
|
||||
_ = BuildModel(en, n)
|
||||
err := n.Build(foundContext, input.label, en, foundIndex)
|
||||
if err != nil {
|
||||
return mappingResult[PT]{}, err
|
||||
}
|
||||
|
||||
if referenceValue != "" {
|
||||
SetReference(n, referenceValue, refNode)
|
||||
}
|
||||
|
||||
v := ValueReference[PT]{
|
||||
Value: n,
|
||||
ValueNode: en,
|
||||
}
|
||||
v.SetReference(referenceValue, refNode)
|
||||
|
||||
return mappingResult[PT]{
|
||||
k: KeyReference[string]{
|
||||
KeyNode: input.label,
|
||||
Value: input.label.Value,
|
||||
},
|
||||
v: v,
|
||||
}, nil
|
||||
}
|
||||
err := datamodel.TranslatePipeline[buildInput, mappingResult[PT]](in, out, translateFunc)
|
||||
wg.Wait()
|
||||
if err != nil {
|
||||
return nil, labelNode, valueNode, err
|
||||
}
|
||||
if circError != nil && !idx.AllowCircularReferenceResolving() {
|
||||
return valueMap, labelNode, valueNode, circError
|
||||
}
|
||||
return valueMap, labelNode, valueNode, nil
|
||||
}
|
||||
return nil, labelNode, valueNode, nil
|
||||
}
|
||||
|
||||
// ExtractMapExtensions will extract a map of KeyReference and ValueReference from a root yaml.Node. The 'label' is
|
||||
// used to locate the node to be extracted from the root node supplied. Supply a bit to decide if extensions should
|
||||
// be included or not. required in some use cases.
|
||||
//
|
||||
// The second return value is the yaml.Node found for the 'label' and the third return value is the yaml.Node
|
||||
// found for the value extracted from the label node.
|
||||
func ExtractMapExtensionsOld[PT Buildable[N], N any](
|
||||
ctx context.Context,
|
||||
label string,
|
||||
root *yaml.Node,
|
||||
idx *index.SpecIndex,
|
||||
extensions bool,
|
||||
) (*orderedmap.Map[KeyReference[string], ValueReference[PT]], *yaml.Node, *yaml.Node, error) {
|
||||
var referenceValue string
|
||||
var labelNode, valueNode *yaml.Node
|
||||
var circError error
|
||||
@@ -687,7 +851,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
|
||||
}
|
||||
|
||||
if ref != "" {
|
||||
SetReference(n, ref)
|
||||
SetReference(n, ref, nil)
|
||||
}
|
||||
|
||||
c <- mappingResult[PT]{
|
||||
@@ -698,7 +862,7 @@ func ExtractMapExtensions[PT Buildable[N], N any](
|
||||
v: ValueReference[PT]{
|
||||
Value: n,
|
||||
ValueNode: value,
|
||||
Reference: ref,
|
||||
// Reference: ref,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -773,7 +937,7 @@ func ExtractMap[PT Buildable[N], N any](
|
||||
label string,
|
||||
root *yaml.Node,
|
||||
idx *index.SpecIndex,
|
||||
) (orderedmap.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](ctx, label, root, idx, false)
|
||||
}
|
||||
|
||||
@@ -781,7 +945,7 @@ func ExtractMap[PT Buildable[N], N any](
|
||||
//
|
||||
// Maps
|
||||
//
|
||||
// map[string]interface{} for maps
|
||||
// *orderedmap.Map[string, *yaml.Node] for maps
|
||||
//
|
||||
// Slices
|
||||
//
|
||||
@@ -790,54 +954,15 @@ func ExtractMap[PT Buildable[N], N any](
|
||||
// int, float, bool, string
|
||||
//
|
||||
// int64, float64, bool, string
|
||||
func ExtractExtensions(root *yaml.Node) map[KeyReference[string]]ValueReference[any] {
|
||||
func ExtractExtensions(root *yaml.Node) *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]] {
|
||||
root = utils.NodeAlias(root)
|
||||
extensions := utils.FindExtensionNodes(root.Content)
|
||||
extensionMap := make(map[KeyReference[string]]ValueReference[any])
|
||||
extensionMap := orderedmap.New[KeyReference[string], ValueReference[*yaml.Node]]()
|
||||
for _, ext := range extensions {
|
||||
if utils.IsNodeMap(ext.Value) {
|
||||
var v interface{}
|
||||
_ = ext.Value.Decode(&v)
|
||||
extensionMap[KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}] = ValueReference[any]{Value: v, ValueNode: ext.Value}
|
||||
}
|
||||
if utils.IsNodeStringValue(ext.Value) {
|
||||
extensionMap[KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}] = ValueReference[any]{Value: ext.Value.Value, ValueNode: ext.Value}
|
||||
}
|
||||
if utils.IsNodeFloatValue(ext.Value) {
|
||||
fv, _ := strconv.ParseFloat(ext.Value.Value, 64)
|
||||
extensionMap[KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}] = ValueReference[any]{Value: fv, ValueNode: ext.Value}
|
||||
}
|
||||
if utils.IsNodeIntValue(ext.Value) {
|
||||
iv, _ := strconv.ParseInt(ext.Value.Value, 10, 64)
|
||||
extensionMap[KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}] = ValueReference[any]{Value: iv, ValueNode: ext.Value}
|
||||
}
|
||||
if utils.IsNodeBoolValue(ext.Value) {
|
||||
bv, _ := strconv.ParseBool(ext.Value.Value)
|
||||
extensionMap[KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}] = ValueReference[any]{Value: bv, ValueNode: ext.Value}
|
||||
}
|
||||
if utils.IsNodeArray(ext.Value) {
|
||||
var v []interface{}
|
||||
_ = ext.Value.Decode(&v)
|
||||
extensionMap[KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}] = ValueReference[any]{Value: v, ValueNode: ext.Value}
|
||||
}
|
||||
extensionMap.Set(KeyReference[string]{
|
||||
Value: ext.Key.Value,
|
||||
KeyNode: ext.Key,
|
||||
}, ValueReference[*yaml.Node]{Value: ext.Value, ValueNode: ext.Value})
|
||||
}
|
||||
return extensionMap
|
||||
}
|
||||
@@ -869,6 +994,10 @@ func GenerateHashString(v any) string {
|
||||
return fmt.Sprintf(HASH, h.Hash())
|
||||
}
|
||||
}
|
||||
if n, ok := v.(*yaml.Node); ok {
|
||||
b, _ := yaml.Marshal(n)
|
||||
return fmt.Sprintf(HASH, sha256.Sum256(b))
|
||||
}
|
||||
// if we get here, we're a primitive, check if we're a pointer and de-point
|
||||
if reflect.TypeOf(v).Kind() == reflect.Ptr {
|
||||
v = reflect.ValueOf(v).Elem().Interface()
|
||||
@@ -876,6 +1005,15 @@ func GenerateHashString(v any) string {
|
||||
return fmt.Sprintf(HASH, sha256.Sum256([]byte(fmt.Sprint(v))))
|
||||
}
|
||||
|
||||
func ValueToString(v any) string {
|
||||
if n, ok := v.(*yaml.Node); ok {
|
||||
b, _ := yaml.Marshal(n)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
return fmt.Sprint(v)
|
||||
}
|
||||
|
||||
// LocateRefEnd will perform a complete lookup for a $ref node. This function searches the entire index for
|
||||
// the reference being supplied. If there is a match found, the reference *yaml.Node is returned.
|
||||
// the function operates recursively and will keep iterating through references until it finds a non-reference
|
||||
|
||||
@@ -7,51 +7,52 @@ import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"golang.org/x/sync/syncmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/sync/syncmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFindItemInMap(t *testing.T) {
|
||||
v := make(map[KeyReference[string]]ValueReference[string])
|
||||
v[KeyReference[string]{
|
||||
func TestFindItemInOrderedMap(t *testing.T) {
|
||||
v := orderedmap.New[KeyReference[string], ValueReference[string]]()
|
||||
v.Set(KeyReference[string]{
|
||||
Value: "pizza",
|
||||
}] = ValueReference[string]{
|
||||
}, ValueReference[string]{
|
||||
Value: "pie",
|
||||
}
|
||||
assert.Equal(t, "pie", FindItemInMap("pizza", v).Value)
|
||||
})
|
||||
assert.Equal(t, "pie", FindItemInOrderedMap("pizza", v).Value)
|
||||
}
|
||||
|
||||
func TestFindItemInMap_WrongCase(t *testing.T) {
|
||||
v := make(map[KeyReference[string]]ValueReference[string])
|
||||
v[KeyReference[string]{
|
||||
func TestFindItemInOrderedMap_WrongCase(t *testing.T) {
|
||||
v := orderedmap.New[KeyReference[string], ValueReference[string]]()
|
||||
v.Set(KeyReference[string]{
|
||||
Value: "pizza",
|
||||
}] = ValueReference[string]{
|
||||
}, ValueReference[string]{
|
||||
Value: "pie",
|
||||
}
|
||||
assert.Equal(t, "pie", FindItemInMap("PIZZA", v).Value)
|
||||
})
|
||||
assert.Equal(t, "pie", FindItemInOrderedMap("PIZZA", v).Value)
|
||||
}
|
||||
|
||||
func TestFindItemInMap_Error(t *testing.T) {
|
||||
v := make(map[KeyReference[string]]ValueReference[string])
|
||||
v[KeyReference[string]{
|
||||
func TestFindItemInOrderedMap_Error(t *testing.T) {
|
||||
v := orderedmap.New[KeyReference[string], ValueReference[string]]()
|
||||
v.Set(KeyReference[string]{
|
||||
Value: "pizza",
|
||||
}] = ValueReference[string]{
|
||||
}, ValueReference[string]{
|
||||
Value: "pie",
|
||||
}
|
||||
assert.Nil(t, FindItemInMap("nuggets", v))
|
||||
})
|
||||
assert.Nil(t, FindItemInOrderedMap("nuggets", v))
|
||||
}
|
||||
|
||||
func TestLocateRefNode(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
cake:
|
||||
@@ -69,11 +70,9 @@ func TestLocateRefNode(t *testing.T) {
|
||||
|
||||
located, _, _ := LocateRefNode(cNode.Content[0], idx)
|
||||
assert.NotNil(t, located)
|
||||
|
||||
}
|
||||
|
||||
func TestLocateRefNode_BadNode(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
cake:
|
||||
@@ -94,11 +93,9 @@ func TestLocateRefNode_BadNode(t *testing.T) {
|
||||
// should both be empty.
|
||||
assert.Nil(t, located)
|
||||
assert.Nil(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestLocateRefNode_Path(t *testing.T) {
|
||||
|
||||
yml := `paths:
|
||||
/burger/time:
|
||||
description: hello`
|
||||
@@ -115,11 +112,9 @@ func TestLocateRefNode_Path(t *testing.T) {
|
||||
|
||||
located, _, _ := LocateRefNode(cNode.Content[0], idx)
|
||||
assert.NotNil(t, located)
|
||||
|
||||
}
|
||||
|
||||
func TestLocateRefNode_Path_NotFound(t *testing.T) {
|
||||
|
||||
yml := `paths:
|
||||
/burger/time:
|
||||
description: hello`
|
||||
@@ -137,7 +132,6 @@ func TestLocateRefNode_Path_NotFound(t *testing.T) {
|
||||
located, _, err := LocateRefNode(cNode.Content[0], idx)
|
||||
assert.Nil(t, located)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
type pizza struct {
|
||||
@@ -149,7 +143,6 @@ func (p *pizza) Build(_ context.Context, _, _ *yaml.Node, _ *index.SpecIndex) er
|
||||
}
|
||||
|
||||
func TestExtractObject(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -173,7 +166,6 @@ func TestExtractObject(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractObject_Ref(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -197,7 +189,6 @@ func TestExtractObject_Ref(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractObject_DoubleRef(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
cake:
|
||||
@@ -282,7 +273,6 @@ func TestExtractObject_DoubleRef_Circular_Fail(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractObject_DoubleRef_Circular_Direct(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
loopy:
|
||||
@@ -312,7 +302,6 @@ func TestExtractObject_DoubleRef_Circular_Direct(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractObject_DoubleRef_Circular_Direct_Fail(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
loopy:
|
||||
@@ -338,7 +327,6 @@ func TestExtractObject_DoubleRef_Circular_Direct_Fail(t *testing.T) {
|
||||
|
||||
_, err := ExtractObject[*pizza](context.Background(), "tags", cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
type test_borked struct {
|
||||
@@ -374,7 +362,6 @@ func (t *test_Good) Build(_ context.Context, _, root *yaml.Node, idx *index.Spec
|
||||
}
|
||||
|
||||
func TestExtractObject_BadLowLevelModel(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
hey:`
|
||||
@@ -391,11 +378,9 @@ func TestExtractObject_BadLowLevelModel(t *testing.T) {
|
||||
|
||||
_, err := ExtractObject[*test_noGood](context.Background(), "thing", &cNode, idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObject_BadBuild(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
hey:`
|
||||
@@ -412,11 +397,9 @@ func TestExtractObject_BadBuild(t *testing.T) {
|
||||
|
||||
_, err := ExtractObject[*test_almostGood](context.Background(), "thing", &cNode, idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObject_BadLabel(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
hey:`
|
||||
@@ -434,11 +417,9 @@ func TestExtractObject_BadLabel(t *testing.T) {
|
||||
res, err := ExtractObject[*test_almostGood](context.Background(), "ding", &cNode, idx)
|
||||
assert.Nil(t, res.Value)
|
||||
assert.NoError(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObject_PathIsCircular(t *testing.T) {
|
||||
|
||||
// first we need an index.
|
||||
yml := `paths:
|
||||
'/something/here':
|
||||
@@ -467,11 +448,9 @@ func TestExtractObject_PathIsCircular(t *testing.T) {
|
||||
res, err := ExtractObject[*test_Good](context.Background(), "thing", &rootNode, idx)
|
||||
assert.NotNil(t, res.Value)
|
||||
assert.Error(t, err) // circular error would have been thrown.
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObject_PathIsCircular_IgnoreErrors(t *testing.T) {
|
||||
|
||||
// first we need an index.
|
||||
yml := `paths:
|
||||
'/something/here':
|
||||
@@ -503,11 +482,9 @@ func TestExtractObject_PathIsCircular_IgnoreErrors(t *testing.T) {
|
||||
res, err := ExtractObject[*test_Good](context.Background(), "thing", &rootNode, idx)
|
||||
assert.NotNil(t, res.Value)
|
||||
assert.NoError(t, err) // circular error would have been thrown, but we're ignoring them.
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObjectRaw(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -530,7 +507,6 @@ func TestExtractObjectRaw(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractObjectRaw_With_Ref(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -555,7 +531,6 @@ func TestExtractObjectRaw_With_Ref(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractObjectRaw_Ref_Circular(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -579,11 +554,9 @@ func TestExtractObjectRaw_Ref_Circular(t *testing.T) {
|
||||
tag, err, _, _ := ExtractObjectRaw[*pizza](context.Background(), nil, cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.NotNil(t, tag)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObjectRaw_RefBroken(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -601,11 +574,9 @@ func TestExtractObjectRaw_RefBroken(t *testing.T) {
|
||||
tag, err, _, _ := ExtractObjectRaw[*pizza](context.Background(), nil, cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, tag)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObjectRaw_Ref_NonBuildable(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -622,11 +593,9 @@ func TestExtractObjectRaw_Ref_NonBuildable(t *testing.T) {
|
||||
|
||||
_, err, _, _ := ExtractObjectRaw[*test_noGood](context.Background(), nil, cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractObjectRaw_Ref_AlmostBuildable(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -643,11 +612,9 @@ func TestExtractObjectRaw_Ref_AlmostBuildable(t *testing.T) {
|
||||
|
||||
_, err, _, _ := ExtractObjectRaw[*test_almostGood](context.Background(), nil, cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestExtractArray(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -675,7 +642,6 @@ func TestExtractArray(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
things:
|
||||
@@ -702,7 +668,6 @@ func TestExtractArray_Ref(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Unbuildable(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
things:
|
||||
@@ -726,7 +691,6 @@ func TestExtractArray_Ref_Unbuildable(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Circular(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -754,7 +718,6 @@ func TestExtractArray_Ref_Circular(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Bad(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -782,7 +745,6 @@ func TestExtractArray_Ref_Bad(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Nested(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -811,7 +773,6 @@ func TestExtractArray_Ref_Nested(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Nested_Circular(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -840,7 +801,6 @@ func TestExtractArray_Ref_Nested_Circular(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Nested_BadRef(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -867,7 +827,6 @@ func TestExtractArray_Ref_Nested_BadRef(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_Ref_Nested_CircularFlat(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -896,7 +855,6 @@ func TestExtractArray_Ref_Nested_CircularFlat(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_BadBuild(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:`
|
||||
@@ -918,7 +876,6 @@ func TestExtractArray_BadBuild(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractArray_BadRefPropsTupe(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
parameters:
|
||||
cakes:
|
||||
@@ -940,45 +897,7 @@ func TestExtractArray_BadRefPropsTupe(t *testing.T) {
|
||||
assert.Len(t, things, 0)
|
||||
}
|
||||
|
||||
func TestExtractExample_String(t *testing.T) {
|
||||
yml := `hi`
|
||||
var e yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &e)
|
||||
|
||||
exp := ExtractExample(e.Content[0], e.Content[0])
|
||||
assert.NotNil(t, exp.Value)
|
||||
assert.Equal(t, "hi", exp.Value)
|
||||
}
|
||||
func TestExtractExample_Map(t *testing.T) {
|
||||
yml := `one: two`
|
||||
var e yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &e)
|
||||
|
||||
exp := ExtractExample(e.Content[0], e.Content[0])
|
||||
assert.NotNil(t, exp.Value)
|
||||
if n, ok := exp.Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "two", n["one"])
|
||||
} else {
|
||||
panic("example unpacked incorrectly.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractExample_Array(t *testing.T) {
|
||||
yml := `- hello`
|
||||
var e yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &e)
|
||||
|
||||
exp := ExtractExample(e.Content[0], e.Content[0])
|
||||
assert.NotNil(t, exp.Value)
|
||||
if n, ok := exp.Value.([]interface{}); ok {
|
||||
assert.Equal(t, "hello", n[0])
|
||||
} else {
|
||||
panic("example unpacked incorrectly.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractMapFlatNoLookup(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -997,11 +916,9 @@ one:
|
||||
things, err := ExtractMapNoLookup[*test_Good](context.Background(), cNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMap_NoLookupWithExtensions(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -1031,7 +948,6 @@ one:
|
||||
}
|
||||
|
||||
func TestExtractMap_NoLookupWithExtensions_UsingMerge(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -1053,11 +969,9 @@ one:
|
||||
things, err := ExtractMapNoLookupExtensions[*test_Good](context.Background(), cNode.Content[0], idx, true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 4, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMap_NoLookupWithoutExtensions(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -1083,7 +997,6 @@ one:
|
||||
}
|
||||
|
||||
func TestExtractMap_WithExtensions(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -1105,7 +1018,6 @@ one:
|
||||
}
|
||||
|
||||
func TestExtractMap_WithoutExtensions(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -1127,7 +1039,6 @@ one:
|
||||
}
|
||||
|
||||
func TestExtractMapFlatNoLookup_Ref(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -1149,11 +1060,9 @@ one:
|
||||
things, err := ExtractMapNoLookup[*test_Good](context.Background(), cNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlatNoLookup_Ref_Bad(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -1175,11 +1084,9 @@ one:
|
||||
things, err := ExtractMapNoLookup[*test_Good](context.Background(), cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Zero(t, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlatNoLookup_Ref_Circular(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
thongs:
|
||||
@@ -1207,11 +1114,9 @@ one:
|
||||
things, err := ExtractMapNoLookup[*test_Good](context.Background(), cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlatNoLookup_Ref_BadBuild(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -1233,11 +1138,9 @@ hello:
|
||||
things, err := ExtractMapNoLookup[*test_noGood](context.Background(), cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Zero(t, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlatNoLookup_Ref_AlmostBuild(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
pizza:
|
||||
@@ -1259,11 +1162,9 @@ one:
|
||||
things, err := ExtractMapNoLookup[*test_almostGood](context.Background(), cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Zero(t, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat(t *testing.T) {
|
||||
|
||||
yml := `components:`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -1282,11 +1183,9 @@ one:
|
||||
things, _, _, err := ExtractMap[*test_Good](context.Background(), "one", cNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_Ref(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stank:
|
||||
@@ -1313,11 +1212,9 @@ one:
|
||||
for pair := orderedmap.First(things); pair != nil; pair = pair.Next() {
|
||||
assert.Equal(t, 99, pair.Value().Value.AlmostWork.Value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_DoubleRef(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stank:
|
||||
@@ -1346,7 +1243,6 @@ func TestExtractMapFlat_DoubleRef(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_DoubleRef_Error(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stank:
|
||||
@@ -1369,11 +1265,9 @@ func TestExtractMapFlat_DoubleRef_Error(t *testing.T) {
|
||||
things, _, _, err := ExtractMap[*test_almostGood](context.Background(), "one", cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Zero(t, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_DoubleRef_Error_NotFound(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stank:
|
||||
@@ -1396,11 +1290,9 @@ func TestExtractMapFlat_DoubleRef_Error_NotFound(t *testing.T) {
|
||||
things, _, _, err := ExtractMap[*test_almostGood](context.Background(), "one", cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Zero(t, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_DoubleRef_Circles(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stonk:
|
||||
@@ -1428,11 +1320,9 @@ func TestExtractMapFlat_DoubleRef_Circles(t *testing.T) {
|
||||
things, _, _, err := ExtractMap[*test_Good](context.Background(), "one", cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, 1, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_Ref_Error(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stank:
|
||||
@@ -1455,11 +1345,9 @@ func TestExtractMapFlat_Ref_Error(t *testing.T) {
|
||||
things, _, _, err := ExtractMap[*test_almostGood](context.Background(), "one", cNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
assert.Zero(t, orderedmap.Len(things))
|
||||
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_Ref_Circ_Error(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stink:
|
||||
@@ -1488,7 +1376,6 @@ func TestExtractMapFlat_Ref_Circ_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_Ref_Nested_Circ_Error(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stink:
|
||||
@@ -1518,7 +1405,6 @@ func TestExtractMapFlat_Ref_Nested_Circ_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_Ref_Nested_Error(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stink:
|
||||
@@ -1544,7 +1430,6 @@ func TestExtractMapFlat_Ref_Nested_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_BadKey_Ref_Nested_Error(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stink:
|
||||
@@ -1570,7 +1455,6 @@ func TestExtractMapFlat_BadKey_Ref_Nested_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractMapFlat_Ref_Bad(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
stink:
|
||||
@@ -1599,7 +1483,6 @@ func TestExtractMapFlat_Ref_Bad(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestExtractExtensions(t *testing.T) {
|
||||
|
||||
yml := `x-bing: ding
|
||||
x-bong: 1
|
||||
x-ling: true
|
||||
@@ -1612,25 +1495,27 @@ x-tacos: [1,2,3]`
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
|
||||
r := ExtractExtensions(idxNode.Content[0])
|
||||
assert.Len(t, r, 6)
|
||||
for i := range r {
|
||||
switch i.Value {
|
||||
assert.Equal(t, 6, orderedmap.Len(r))
|
||||
for pair := orderedmap.First(r); pair != nil; pair = pair.Next() {
|
||||
var v any
|
||||
_ = pair.Value().Value.Decode(&v)
|
||||
|
||||
switch pair.Key().Value {
|
||||
case "x-bing":
|
||||
assert.Equal(t, "ding", r[i].Value)
|
||||
assert.Equal(t, "ding", v)
|
||||
case "x-bong":
|
||||
assert.Equal(t, int64(1), r[i].Value)
|
||||
assert.Equal(t, 1, v)
|
||||
case "x-ling":
|
||||
assert.Equal(t, true, r[i].Value)
|
||||
assert.Equal(t, true, v)
|
||||
case "x-long":
|
||||
assert.Equal(t, 0.99, r[i].Value)
|
||||
assert.Equal(t, 0.99, v)
|
||||
case "x-fish":
|
||||
if a, ok := r[i].Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "yeah", a["woo"])
|
||||
} else {
|
||||
panic("should not fail casting")
|
||||
}
|
||||
var m map[string]any
|
||||
err := pair.Value().Value.Decode(&m)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "yeah", m["woo"])
|
||||
case "x-tacos":
|
||||
assert.Len(t, r[i].Value, 3)
|
||||
assert.Len(t, v, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1650,8 +1535,8 @@ func (f test_fresh) Hash() [32]byte {
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(data, "|")))
|
||||
}
|
||||
func TestAreEqual(t *testing.T) {
|
||||
|
||||
func TestAreEqual(t *testing.T) {
|
||||
var hey *test_fresh
|
||||
|
||||
assert.True(t, AreEqual(test_fresh{val: "hello"}, test_fresh{val: "hello"}))
|
||||
@@ -1664,7 +1549,6 @@ func TestAreEqual(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGenerateHashString(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824",
|
||||
GenerateHashString(test_fresh{val: "hello"}))
|
||||
|
||||
@@ -1676,46 +1560,39 @@ func TestGenerateHashString(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "",
|
||||
GenerateHashString(nil))
|
||||
|
||||
}
|
||||
|
||||
func TestGenerateHashString_Pointer(t *testing.T) {
|
||||
|
||||
val := true
|
||||
assert.Equal(t, "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b",
|
||||
GenerateHashString(test_fresh{thang: &val}))
|
||||
|
||||
assert.Equal(t, "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b",
|
||||
GenerateHashString(&val))
|
||||
|
||||
}
|
||||
|
||||
func TestSetReference(t *testing.T) {
|
||||
|
||||
type testObj struct {
|
||||
*Reference
|
||||
}
|
||||
|
||||
n := testObj{Reference: &Reference{}}
|
||||
SetReference(&n, "#/pigeon/street")
|
||||
SetReference(&n, "#/pigeon/street", nil)
|
||||
|
||||
assert.Equal(t, "#/pigeon/street", n.GetReference())
|
||||
|
||||
}
|
||||
|
||||
func TestSetReference_nil(t *testing.T) {
|
||||
|
||||
type testObj struct {
|
||||
*Reference
|
||||
}
|
||||
|
||||
n := testObj{Reference: &Reference{}}
|
||||
SetReference(nil, "#/pigeon/street")
|
||||
SetReference(nil, "#/pigeon/street", nil)
|
||||
assert.NotEqual(t, "#/pigeon/street", n.GetReference())
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_HttpLink(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1741,7 +1618,6 @@ func TestLocateRefNode_CurrentPathKey_HttpLink(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_HttpLink_Local(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1767,7 +1643,6 @@ func TestLocateRefNode_CurrentPathKey_HttpLink_Local(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_HttpLink_RemoteCtx(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1792,7 +1667,6 @@ func TestLocateRefNode_CurrentPathKey_HttpLink_RemoteCtx(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_HttpLink_RemoteCtx_WithPath(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1817,7 +1691,6 @@ func TestLocateRefNode_CurrentPathKey_HttpLink_RemoteCtx_WithPath(t *testing.T)
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_Path_Link(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1842,7 +1715,6 @@ func TestLocateRefNode_CurrentPathKey_Path_Link(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_Path_URL(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1869,7 +1741,6 @@ func TestLocateRefNode_CurrentPathKey_Path_URL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_CurrentPathKey_DeeperPath_URL(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1896,7 +1767,6 @@ func TestLocateRefNode_CurrentPathKey_DeeperPath_URL(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_NoExplode(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1923,7 +1793,6 @@ func TestLocateRefNode_NoExplode(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_NoExplode_HTTP(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1951,7 +1820,6 @@ func TestLocateRefNode_NoExplode_HTTP(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_NoExplode_NoSpecPath(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -1979,7 +1847,6 @@ func TestLocateRefNode_NoExplode_NoSpecPath(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefNode_DoARealLookup(t *testing.T) {
|
||||
|
||||
no := yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Content: []*yaml.Node{
|
||||
@@ -2020,7 +1887,6 @@ func TestLocateRefNode_DoARealLookup(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefEndNoRef_NoName(t *testing.T) {
|
||||
|
||||
r := &yaml.Node{Content: []*yaml.Node{{Kind: yaml.ScalarNode, Value: "$ref"}, {Kind: yaml.ScalarNode, Value: ""}}}
|
||||
n, i, e, c := LocateRefEnd(nil, r, nil, 0)
|
||||
assert.Nil(t, n)
|
||||
@@ -2030,7 +1896,6 @@ func TestLocateRefEndNoRef_NoName(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefEndNoRef(t *testing.T) {
|
||||
|
||||
r := &yaml.Node{Content: []*yaml.Node{{Kind: yaml.ScalarNode, Value: "$ref"}, {Kind: yaml.ScalarNode, Value: "cake"}}}
|
||||
n, i, e, c := LocateRefEnd(context.Background(), r, index.NewSpecIndexWithConfig(r, index.CreateClosedAPIIndexConfig()), 0)
|
||||
assert.Nil(t, n)
|
||||
@@ -2049,7 +1914,6 @@ func TestLocateRefEnd_TooDeep(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefEnd_Loop(t *testing.T) {
|
||||
|
||||
yml, _ := os.ReadFile("../../test_specs/first.yaml")
|
||||
var bsn yaml.Node
|
||||
_ = yaml.Unmarshal(yml, &bsn)
|
||||
@@ -2094,7 +1958,6 @@ func TestLocateRefEnd_Loop(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefEnd_Loop_WithResolve(t *testing.T) {
|
||||
|
||||
yml, _ := os.ReadFile("../../test_specs/first.yaml")
|
||||
var bsn yaml.Node
|
||||
_ = yaml.Unmarshal(yml, &bsn)
|
||||
@@ -2139,7 +2002,6 @@ func TestLocateRefEnd_Loop_WithResolve(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestLocateRefEnd_Empty(t *testing.T) {
|
||||
|
||||
yml, _ := os.ReadFile("../../test_specs/first.yaml")
|
||||
var bsn yaml.Node
|
||||
_ = yaml.Unmarshal(yml, &bsn)
|
||||
@@ -2184,7 +2046,6 @@ func TestLocateRefEnd_Empty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestArray_NotRefNotArray(t *testing.T) {
|
||||
|
||||
yml := ``
|
||||
var idxNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
@@ -2201,5 +2062,4 @@ func TestArray_NotRefNotArray(t *testing.T) {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, err.Error(), "array build failed, input is not an array, line 2, column 3")
|
||||
assert.Len(t, things, 0)
|
||||
|
||||
}
|
||||
|
||||
@@ -78,72 +78,63 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
||||
|
||||
switch field.Type() {
|
||||
|
||||
case reflect.TypeOf(map[string]NodeReference[any]{}):
|
||||
case reflect.TypeOf(orderedmap.New[string, NodeReference[*yaml.Node]]()):
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[any])
|
||||
items := orderedmap.New[string, NodeReference[*yaml.Node]]()
|
||||
var currentLabel string
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentLabel = sliceItem.Value
|
||||
continue
|
||||
}
|
||||
var decoded map[string]interface{}
|
||||
// I cannot think of a way to make this error out by this point.
|
||||
_ = sliceItem.Decode(&decoded)
|
||||
items[currentLabel] = NodeReference[any]{
|
||||
Value: decoded,
|
||||
items.Set(currentLabel, NodeReference[*yaml.Node]{
|
||||
Value: sliceItem,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(map[string]NodeReference[string]{}):
|
||||
case reflect.TypeOf(orderedmap.New[string, NodeReference[string]]()):
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[string])
|
||||
items := orderedmap.New[string, NodeReference[string]]()
|
||||
var currentLabel string
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentLabel = sliceItem.Value
|
||||
continue
|
||||
}
|
||||
items[currentLabel] = NodeReference[string]{
|
||||
items.Set(currentLabel, NodeReference[string]{
|
||||
Value: fmt.Sprintf("%v", sliceItem.Value),
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[any]{}):
|
||||
case reflect.TypeOf(NodeReference[*yaml.Node]{}):
|
||||
|
||||
var decoded interface{}
|
||||
_ = valueNode.Decode(&decoded)
|
||||
if field.CanSet() {
|
||||
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
|
||||
or := NodeReference[*yaml.Node]{Value: valueNode, ValueNode: valueNode, KeyNode: keyNode}
|
||||
field.Set(reflect.ValueOf(or))
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[any]{}):
|
||||
case reflect.TypeOf([]NodeReference[*yaml.Node]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[any]
|
||||
var items []NodeReference[*yaml.Node]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
var decoded map[string]interface{}
|
||||
err := sliceItem.Decode(&decoded)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
items = append(items, NodeReference[any]{
|
||||
Value: decoded,
|
||||
items = append(items, NodeReference[*yaml.Node]{
|
||||
Value: sliceItem,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
@@ -340,57 +331,9 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
||||
}
|
||||
}
|
||||
|
||||
// helper for unpacking string maps.
|
||||
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
|
||||
// helper for unpacking string maps.
|
||||
case reflect.TypeOf(orderedmap.New[KeyReference[string], ValueReference[string]]()):
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[KeyReference[string]]ValueReference[string])
|
||||
var cf *yaml.Node
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
cf = sliceItem
|
||||
continue
|
||||
}
|
||||
items[KeyReference[string]{
|
||||
Value: cf.Value,
|
||||
KeyNode: cf,
|
||||
}] = ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(KeyReference[map[KeyReference[string]]ValueReference[string]]{}):
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[KeyReference[string]]ValueReference[string])
|
||||
var cf *yaml.Node
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
cf = sliceItem
|
||||
continue
|
||||
}
|
||||
items[KeyReference[string]{
|
||||
Value: cf.Value,
|
||||
KeyNode: cf,
|
||||
}] = ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
}
|
||||
}
|
||||
ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(ref))
|
||||
}
|
||||
}
|
||||
case reflect.TypeOf(NodeReference[orderedmap.Map[KeyReference[string], ValueReference[string]]]{}):
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := orderedmap.New[KeyReference[string], ValueReference[string]]()
|
||||
@@ -408,7 +351,55 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
ref := NodeReference[orderedmap.Map[KeyReference[string], ValueReference[string]]]{
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(KeyReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{}):
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := orderedmap.New[KeyReference[string], ValueReference[string]]()
|
||||
var cf *yaml.Node
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
cf = sliceItem
|
||||
continue
|
||||
}
|
||||
items.Set(KeyReference[string]{
|
||||
Value: cf.Value,
|
||||
KeyNode: cf,
|
||||
}, ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
ref := KeyReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(ref))
|
||||
}
|
||||
}
|
||||
case reflect.TypeOf(NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{}):
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := orderedmap.New[KeyReference[string], ValueReference[string]]()
|
||||
var cf *yaml.Node
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
cf = sliceItem
|
||||
continue
|
||||
}
|
||||
items.Set(KeyReference[string]{
|
||||
Value: cf.Value,
|
||||
KeyNode: cf,
|
||||
}, ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
ref := NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
@@ -436,34 +427,18 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
|
||||
case reflect.TypeOf(NodeReference[[]ValueReference[*yaml.Node]]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []ValueReference[any]
|
||||
var items []ValueReference[*yaml.Node]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
|
||||
var val any
|
||||
if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) {
|
||||
if utils.IsNodeIntValue(sliceItem) {
|
||||
val, _ = strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||
} else {
|
||||
val, _ = strconv.ParseFloat(sliceItem.Value, 64)
|
||||
}
|
||||
}
|
||||
if utils.IsNodeBoolValue(sliceItem) {
|
||||
val, _ = strconv.ParseBool(sliceItem.Value)
|
||||
}
|
||||
if utils.IsNodeStringValue(sliceItem) {
|
||||
val = sliceItem.Value
|
||||
}
|
||||
|
||||
items = append(items, ValueReference[any]{
|
||||
Value: val,
|
||||
items = append(items, ValueReference[*yaml.Node]{
|
||||
Value: sliceItem,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
n := NodeReference[[]ValueReference[any]]{
|
||||
n := NodeReference[[]ValueReference[*yaml.Node]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
|
||||
@@ -25,11 +25,11 @@ type hotdog struct {
|
||||
Temps []NodeReference[int]
|
||||
HighTemps []NodeReference[int64]
|
||||
Buns []NodeReference[bool]
|
||||
UnknownElements NodeReference[any]
|
||||
LotsOfUnknowns []NodeReference[any]
|
||||
Where map[string]NodeReference[any]
|
||||
There map[string]NodeReference[string]
|
||||
AllTheThings NodeReference[orderedmap.Map[KeyReference[string], ValueReference[string]]]
|
||||
UnknownElements NodeReference[*yaml.Node]
|
||||
LotsOfUnknowns []NodeReference[*yaml.Node]
|
||||
Where *orderedmap.Map[string, NodeReference[*yaml.Node]]
|
||||
There *orderedmap.Map[string, NodeReference[string]]
|
||||
AllTheThings NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]
|
||||
}
|
||||
|
||||
func TestBuildModel_Mismatch(t *testing.T) {
|
||||
@@ -126,11 +126,15 @@ allTheThings:
|
||||
assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value)
|
||||
assert.Equal(t, 2, hd.Temps[1].Value)
|
||||
assert.Equal(t, 27, hd.Temps[1].ValueNode.Line)
|
||||
assert.Len(t, hd.UnknownElements.Value, 2)
|
||||
|
||||
var unknownElements map[string]any
|
||||
_ = hd.UnknownElements.Value.Decode(&unknownElements)
|
||||
|
||||
assert.Len(t, unknownElements, 2)
|
||||
assert.Len(t, hd.LotsOfUnknowns, 3)
|
||||
assert.Len(t, hd.Where, 2)
|
||||
assert.Len(t, hd.There, 2)
|
||||
assert.Equal(t, "bear", hd.There["care"].Value)
|
||||
assert.Equal(t, 2, orderedmap.Len(hd.Where))
|
||||
assert.Equal(t, 2, orderedmap.Len(hd.There))
|
||||
assert.Equal(t, "bear", hd.There.GetOrZero("care").Value)
|
||||
assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
|
||||
|
||||
allTheThings := hd.AllTheThings.Value
|
||||
@@ -201,27 +205,9 @@ thing: yeah`
|
||||
assert.Equal(t, "yeah", ins.Thing.Value)
|
||||
}
|
||||
|
||||
func TestSetField_NodeRefAny_Error(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing []NodeReference[any]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
- 999
|
||||
- false`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.Error(t, try)
|
||||
}
|
||||
|
||||
func TestSetField_MapHelperWrapped(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||
Thing KeyReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
@@ -236,12 +222,12 @@ func TestSetField_MapHelperWrapped(t *testing.T) {
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
assert.Equal(t, 3, orderedmap.Len(ins.Thing.Value))
|
||||
}
|
||||
|
||||
func TestSetField_MapHelper(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing map[KeyReference[string]]ValueReference[string]
|
||||
Thing *orderedmap.Map[KeyReference[string], ValueReference[string]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
@@ -256,7 +242,7 @@ func TestSetField_MapHelper(t *testing.T) {
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing, 3)
|
||||
assert.Equal(t, 3, orderedmap.Len(ins.Thing))
|
||||
}
|
||||
|
||||
func TestSetField_ArrayHelper(t *testing.T) {
|
||||
@@ -281,7 +267,7 @@ func TestSetField_ArrayHelper(t *testing.T) {
|
||||
|
||||
func TestSetField_Enum_Helper(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
Thing NodeReference[[]ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
@@ -324,7 +310,7 @@ func TestSetField_Default_Helper(t *testing.T) {
|
||||
|
||||
func TestHandleSlicesOfInts(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
Thing NodeReference[[]ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
@@ -338,13 +324,20 @@ func TestHandleSlicesOfInts(t *testing.T) {
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Equal(t, int64(5), ins.Thing.Value[0].Value)
|
||||
assert.Equal(t, 1.234, ins.Thing.Value[1].Value)
|
||||
|
||||
var thing0 int64
|
||||
_ = ins.Thing.GetValue()[0].Value.Decode(&thing0)
|
||||
|
||||
var thing1 float64
|
||||
_ = ins.Thing.GetValue()[1].Value.Decode(&thing1)
|
||||
|
||||
assert.Equal(t, int64(5), thing0)
|
||||
assert.Equal(t, 1.234, thing1)
|
||||
}
|
||||
|
||||
func TestHandleSlicesOfBools(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
Thing NodeReference[[]ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
@@ -357,9 +350,16 @@ func TestHandleSlicesOfBools(t *testing.T) {
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
|
||||
var thing0 bool
|
||||
_ = ins.Thing.GetValue()[0].Value.Decode(&thing0)
|
||||
|
||||
var thing1 bool
|
||||
_ = ins.Thing.GetValue()[1].Value.Decode(&thing1)
|
||||
|
||||
assert.NoError(t, try)
|
||||
assert.Equal(t, true, ins.Thing.Value[0].Value)
|
||||
assert.Equal(t, false, ins.Thing.Value[1].Value)
|
||||
assert.Equal(t, true, thing0)
|
||||
assert.Equal(t, false, thing1)
|
||||
}
|
||||
|
||||
func TestSetField_Ignore(t *testing.T) {
|
||||
@@ -387,7 +387,7 @@ func TestSetField_Ignore(t *testing.T) {
|
||||
|
||||
func TestBuildModelAsync(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||
Thing KeyReference[*orderedmap.Map[KeyReference[string], ValueReference[string]]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
@@ -405,28 +405,5 @@ func TestBuildModelAsync(t *testing.T) {
|
||||
wg.Add(1)
|
||||
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
|
||||
wg.Wait()
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
}
|
||||
|
||||
func TestBuildModelAsync_Error(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing []NodeReference[any]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
- 999
|
||||
- false`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
var errors []error
|
||||
wg.Add(1)
|
||||
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
|
||||
wg.Wait()
|
||||
assert.Len(t, errors, 1)
|
||||
assert.Len(t, ins.Thing, 0)
|
||||
assert.Equal(t, 3, orderedmap.Len(ins.Thing.Value))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
package low
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type SharedParameters interface {
|
||||
HasDescription
|
||||
Hash() [32]byte
|
||||
@@ -30,7 +35,7 @@ type SwaggerParameter interface {
|
||||
GetType() *NodeReference[string]
|
||||
GetFormat() *NodeReference[string]
|
||||
GetCollectionFormat() *NodeReference[string]
|
||||
GetDefault() *NodeReference[any]
|
||||
GetDefault() *NodeReference[*yaml.Node]
|
||||
GetMaximum() *NodeReference[int]
|
||||
GetExclusiveMaximum() *NodeReference[bool]
|
||||
GetMinimum() *NodeReference[int]
|
||||
@@ -41,7 +46,7 @@ type SwaggerParameter interface {
|
||||
GetMaxItems() *NodeReference[int]
|
||||
GetMinItems() *NodeReference[int]
|
||||
GetUniqueItems() *NodeReference[bool]
|
||||
GetEnum() *NodeReference[[]ValueReference[any]]
|
||||
GetEnum() *NodeReference[[]ValueReference[*yaml.Node]]
|
||||
GetMultipleOf() *NodeReference[int]
|
||||
}
|
||||
|
||||
@@ -51,7 +56,7 @@ type SwaggerHeader interface {
|
||||
GetType() *NodeReference[string]
|
||||
GetFormat() *NodeReference[string]
|
||||
GetCollectionFormat() *NodeReference[string]
|
||||
GetDefault() *NodeReference[any]
|
||||
GetDefault() *NodeReference[*yaml.Node]
|
||||
GetMaximum() *NodeReference[int]
|
||||
GetExclusiveMaximum() *NodeReference[bool]
|
||||
GetMinimum() *NodeReference[int]
|
||||
@@ -62,7 +67,7 @@ type SwaggerHeader interface {
|
||||
GetMaxItems() *NodeReference[int]
|
||||
GetMinItems() *NodeReference[int]
|
||||
GetUniqueItems() *NodeReference[bool]
|
||||
GetEnum() *NodeReference[[]ValueReference[any]]
|
||||
GetEnum() *NodeReference[[]ValueReference[*yaml.Node]]
|
||||
GetMultipleOf() *NodeReference[int]
|
||||
GetItems() *NodeReference[any] // requires cast.
|
||||
}
|
||||
@@ -74,7 +79,7 @@ type OpenAPIHeader interface {
|
||||
GetStyle() *NodeReference[string]
|
||||
GetAllowReserved() *NodeReference[bool]
|
||||
GetExplode() *NodeReference[bool]
|
||||
GetExample() *NodeReference[any]
|
||||
GetExample() *NodeReference[*yaml.Node]
|
||||
GetRequired() *NodeReference[bool]
|
||||
GetAllowEmptyValue() *NodeReference[bool]
|
||||
GetSchema() *NodeReference[any] // requires cast.
|
||||
@@ -88,13 +93,12 @@ type OpenAPIParameter interface {
|
||||
GetStyle() *NodeReference[string]
|
||||
GetAllowReserved() *NodeReference[bool]
|
||||
GetExplode() *NodeReference[bool]
|
||||
GetExample() *NodeReference[any]
|
||||
GetExample() *NodeReference[*yaml.Node]
|
||||
GetExamples() *NodeReference[any] // requires cast.
|
||||
GetContent() *NodeReference[any] // requires cast.
|
||||
}
|
||||
|
||||
//TODO: this needs to be fixed, move returns to pointers.
|
||||
|
||||
// TODO: this needs to be fixed, move returns to pointers.
|
||||
type SharedOperations interface {
|
||||
GetOperationId() NodeReference[string]
|
||||
GetExternalDocs() NodeReference[any]
|
||||
@@ -102,7 +106,7 @@ type SharedOperations interface {
|
||||
GetTags() NodeReference[[]ValueReference[string]]
|
||||
GetSummary() NodeReference[string]
|
||||
GetDeprecated() NodeReference[bool]
|
||||
GetExtensions() map[KeyReference[string]]ValueReference[any]
|
||||
GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]
|
||||
GetResponses() NodeReference[any] // requires cast.
|
||||
GetParameters() NodeReference[any] // requires cast.
|
||||
GetSecurity() NodeReference[any] // requires cast.
|
||||
@@ -117,6 +121,6 @@ type SwaggerOperations interface {
|
||||
|
||||
type OpenAPIOperations interface {
|
||||
SharedOperations
|
||||
GetCallbacks() NodeReference[map[KeyReference[string]]ValueReference[any]] // requires cast
|
||||
GetServers() NodeReference[any] // requires cast.
|
||||
GetCallbacks() NodeReference[*orderedmap.Map[KeyReference[string], ValueReference[any]]] // requires cast
|
||||
GetServers() NodeReference[any] // requires cast.
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ package low
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -13,25 +15,35 @@ const (
|
||||
)
|
||||
|
||||
type Reference struct {
|
||||
Reference string `json:"-" yaml:"-"`
|
||||
refNode *yaml.Node
|
||||
reference string
|
||||
}
|
||||
|
||||
func (r *Reference) GetReference() string {
|
||||
return r.Reference
|
||||
func (r Reference) GetReference() string {
|
||||
return r.reference
|
||||
}
|
||||
|
||||
func (r *Reference) IsReference() bool {
|
||||
return r.Reference != ""
|
||||
func (r Reference) IsReference() bool {
|
||||
return r.reference != ""
|
||||
}
|
||||
|
||||
func (r *Reference) SetReference(ref string) {
|
||||
r.Reference = ref
|
||||
func (r Reference) GetReferenceNode() *yaml.Node {
|
||||
return r.refNode
|
||||
}
|
||||
|
||||
func (r *Reference) SetReference(ref string, node *yaml.Node) {
|
||||
r.reference = ref
|
||||
r.refNode = node
|
||||
}
|
||||
|
||||
type IsReferenced interface {
|
||||
IsReference() bool
|
||||
GetReference() string
|
||||
SetReference(string)
|
||||
GetReferenceNode() *yaml.Node
|
||||
}
|
||||
|
||||
type SetReferencer interface {
|
||||
SetReference(ref string, node *yaml.Node)
|
||||
}
|
||||
|
||||
// Buildable is an interface for any struct that can be 'built out'. This means that a struct can accept
|
||||
@@ -63,16 +75,14 @@ type Hashable interface {
|
||||
|
||||
// HasExtensions is implemented by any object that exposes extensions
|
||||
type HasExtensions[T any] interface {
|
||||
|
||||
// GetExtensions returns generic low level extensions
|
||||
GetExtensions() map[KeyReference[string]]ValueReference[any]
|
||||
GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// HasExtensionsUntyped is implemented by any object that exposes extensions
|
||||
type HasExtensionsUntyped interface {
|
||||
|
||||
// GetExtensions returns generic low level extensions
|
||||
GetExtensions() map[KeyReference[string]]ValueReference[any]
|
||||
GetExtensions() *orderedmap.Map[KeyReference[string], ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// HasValue is implemented by NodeReference and ValueReference to return the yaml.Node backing the value.
|
||||
@@ -98,6 +108,7 @@ type HasKeyNode interface {
|
||||
// a key yaml.Node that points to the key node that contains the value node, and the value node that contains
|
||||
// the actual value.
|
||||
type NodeReference[T any] struct {
|
||||
Reference
|
||||
|
||||
// The value being referenced
|
||||
Value T
|
||||
@@ -108,19 +119,14 @@ type NodeReference[T any] struct {
|
||||
// The yaml.Node that is the key, that contains the value.
|
||||
KeyNode *yaml.Node
|
||||
|
||||
// Is this value actually a reference in the original tree?
|
||||
ReferenceNode bool
|
||||
|
||||
// If HasReference is true, then Reference contains the original $ref value.
|
||||
Reference string
|
||||
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
var _ HasValueNodeUntyped = &NodeReference[any]{}
|
||||
|
||||
// KeyReference is a low-level container for key nodes holding a Value of type T. A KeyNode is a pointer to the
|
||||
// yaml.Node that holds a key to a value.
|
||||
type KeyReference[T any] struct {
|
||||
|
||||
// The value being referenced.
|
||||
Value T
|
||||
|
||||
@@ -131,18 +137,13 @@ type KeyReference[T any] struct {
|
||||
// ValueReference is a low-level container for value nodes that hold a Value of type T. A ValueNode is a pointer
|
||||
// to the yaml.Node that holds the value.
|
||||
type ValueReference[T any] struct {
|
||||
Reference
|
||||
|
||||
// The value being referenced.
|
||||
Value T
|
||||
|
||||
// The yaml.Node that holds the referenced value
|
||||
ValueNode *yaml.Node
|
||||
|
||||
// Is this value actually a reference in the original tree?
|
||||
ReferenceNode bool
|
||||
|
||||
// If HasReference is true, then Reference contains the original $ref value.
|
||||
Reference string
|
||||
}
|
||||
|
||||
// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored)
|
||||
@@ -158,32 +159,6 @@ func (n NodeReference[T]) NodeLineNumber() int {
|
||||
}
|
||||
}
|
||||
|
||||
func (n NodeReference[T]) GetReference() string {
|
||||
return n.Reference
|
||||
}
|
||||
|
||||
func (n NodeReference[T]) SetReference(ref string) {
|
||||
n.Reference = ref
|
||||
}
|
||||
|
||||
// IsReference will return true if the key node contains a $ref key.
|
||||
func (n NodeReference[T]) IsReference() bool {
|
||||
if n.ReferenceNode {
|
||||
return true
|
||||
}
|
||||
if n.KeyNode != nil {
|
||||
for k := range n.KeyNode.Content {
|
||||
if k%2 == 0 {
|
||||
if n.KeyNode.Content[k].Value == "$ref" {
|
||||
n.ReferenceNode = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GenerateMapKey will return a string based on the line and column number of the node, e.g. 33:56 for line 33, col 56.
|
||||
func (n NodeReference[T]) GenerateMapKey() string {
|
||||
return fmt.Sprintf("%d:%d", n.ValueNode.Line, n.ValueNode.Column)
|
||||
@@ -251,36 +226,19 @@ func (n ValueReference[T]) GetValueUntyped() any {
|
||||
return n.Value
|
||||
}
|
||||
|
||||
func (n ValueReference[T]) GetReference() string {
|
||||
return n.Reference
|
||||
}
|
||||
|
||||
func (n ValueReference[T]) SetReference(ref string) {
|
||||
n.Reference = ref
|
||||
}
|
||||
|
||||
// IsReference will return true if the key node contains a $ref
|
||||
func (n ValueReference[T]) IsReference() bool {
|
||||
if n.Reference != "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (n ValueReference[T]) MarshalYAML() (interface{}, error) {
|
||||
if n.IsReference() {
|
||||
nodes := make([]*yaml.Node, 2)
|
||||
nodes[0] = utils.CreateStringNode("$ref")
|
||||
nodes[1] = utils.CreateStringNode(n.Reference)
|
||||
m := utils.CreateEmptyMapNode()
|
||||
m.Content = nodes
|
||||
return m, nil
|
||||
return n.GetReferenceNode(), nil
|
||||
}
|
||||
var h yaml.Node
|
||||
e := n.ValueNode.Decode(&h)
|
||||
return h, e
|
||||
}
|
||||
|
||||
func (n KeyReference[T]) MarshalYAML() (interface{}, error) {
|
||||
return n.KeyNode, nil
|
||||
}
|
||||
|
||||
// IsEmpty will return true if this reference has no key or value nodes assigned (it's been ignored)
|
||||
func (n KeyReference[T]) IsEmpty() bool {
|
||||
return n.KeyNode == nil
|
||||
|
||||
@@ -6,10 +6,11 @@ package low
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -99,7 +100,6 @@ func TestKeyReference_GenerateMapKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsCircular_LookupFromJourney(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
Something:
|
||||
@@ -136,7 +136,6 @@ func TestIsCircular_LookupFromJourney(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsCircular_LookupFromJourney_Optional(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
Something:
|
||||
@@ -237,7 +236,6 @@ func TestIsCircular_LookupFromLoopPoint_Optional(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsCircular_FromRefLookup(t *testing.T) {
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
NotCircle:
|
||||
@@ -558,21 +556,17 @@ func TestGetCircularReferenceResult_NothingFound(t *testing.T) {
|
||||
func TestHashToString(t *testing.T) {
|
||||
assert.Equal(t, "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5",
|
||||
HashToString(sha256.Sum256([]byte("12345"))))
|
||||
|
||||
}
|
||||
|
||||
func TestReference_IsReference(t *testing.T) {
|
||||
ref := Reference{
|
||||
Reference: "#/components/schemas/SomeSchema",
|
||||
}
|
||||
ref := Reference{}
|
||||
ref.SetReference("#/components/schemas/SomeSchema", nil)
|
||||
assert.True(t, ref.IsReference())
|
||||
|
||||
}
|
||||
|
||||
func TestNodeReference_NodeLineNumber(t *testing.T) {
|
||||
|
||||
n := utils.CreateStringNode("pizza")
|
||||
nr := NodeReference[string]{
|
||||
nr := &NodeReference[string]{
|
||||
Value: "pizza",
|
||||
ValueNode: n,
|
||||
}
|
||||
@@ -582,51 +576,36 @@ func TestNodeReference_NodeLineNumber(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNodeReference_NodeLineNumberEmpty(t *testing.T) {
|
||||
|
||||
nr := NodeReference[string]{
|
||||
nr := &NodeReference[string]{
|
||||
Value: "pizza",
|
||||
}
|
||||
assert.Equal(t, 0, nr.NodeLineNumber())
|
||||
}
|
||||
|
||||
func TestNodeReference_GetReference(t *testing.T) {
|
||||
|
||||
nr := NodeReference[string]{
|
||||
Reference: "#/happy/sunday",
|
||||
}
|
||||
nr := &NodeReference[string]{}
|
||||
nr.SetReference("#/happy/sunday", nil)
|
||||
assert.Equal(t, "#/happy/sunday", nr.GetReference())
|
||||
}
|
||||
|
||||
func TestNodeReference_SetReference(t *testing.T) {
|
||||
|
||||
nr := NodeReference[string]{}
|
||||
nr.SetReference("#/happy/sunday")
|
||||
}
|
||||
|
||||
func TestNodeReference_IsReference(t *testing.T) {
|
||||
|
||||
nr := NodeReference[string]{
|
||||
ReferenceNode: true,
|
||||
}
|
||||
assert.True(t, nr.IsReference())
|
||||
nr := &NodeReference[string]{}
|
||||
nr.SetReference("#/happy/sunday", nil)
|
||||
}
|
||||
|
||||
func TestNodeReference_GetKeyNode(t *testing.T) {
|
||||
|
||||
nr := NodeReference[string]{
|
||||
nr := &NodeReference[string]{
|
||||
KeyNode: utils.CreateStringNode("pizza"),
|
||||
}
|
||||
assert.Equal(t, "pizza", nr.GetKeyNode().Value)
|
||||
|
||||
}
|
||||
|
||||
func TestNodeReference_GetValueUntyped(t *testing.T) {
|
||||
|
||||
type anything struct {
|
||||
thing string
|
||||
}
|
||||
|
||||
nr := NodeReference[any]{
|
||||
nr := &NodeReference[any]{
|
||||
Value: anything{thing: "ding"},
|
||||
}
|
||||
|
||||
@@ -634,7 +613,6 @@ func TestNodeReference_GetValueUntyped(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValueReference_NodeLineNumber(t *testing.T) {
|
||||
|
||||
n := utils.CreateStringNode("pizza")
|
||||
nr := ValueReference[string]{
|
||||
Value: "pizza",
|
||||
@@ -646,7 +624,6 @@ func TestValueReference_NodeLineNumber(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValueReference_NodeLineNumber_Nil(t *testing.T) {
|
||||
|
||||
nr := ValueReference[string]{
|
||||
Value: "pizza",
|
||||
}
|
||||
@@ -655,21 +632,12 @@ func TestValueReference_NodeLineNumber_Nil(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValueReference_GetReference(t *testing.T) {
|
||||
|
||||
nr := ValueReference[string]{
|
||||
Reference: "#/happy/sunday",
|
||||
}
|
||||
nr := ValueReference[string]{}
|
||||
nr.SetReference("#/happy/sunday", nil)
|
||||
assert.Equal(t, "#/happy/sunday", nr.GetReference())
|
||||
}
|
||||
|
||||
func TestValueReference_SetReference(t *testing.T) {
|
||||
|
||||
nr := ValueReference[string]{}
|
||||
nr.SetReference("#/happy/sunday")
|
||||
}
|
||||
|
||||
func TestValueReference_GetValueUntyped(t *testing.T) {
|
||||
|
||||
type anything struct {
|
||||
thing string
|
||||
}
|
||||
@@ -681,28 +649,15 @@ func TestValueReference_GetValueUntyped(t *testing.T) {
|
||||
assert.Equal(t, "{ding}", fmt.Sprint(nr.GetValueUntyped()))
|
||||
}
|
||||
|
||||
func TestValueReference_IsReference(t *testing.T) {
|
||||
|
||||
nr := NodeReference[string]{
|
||||
ReferenceNode: true,
|
||||
}
|
||||
assert.True(t, nr.IsReference())
|
||||
}
|
||||
|
||||
func TestValueReference_MarshalYAML_Ref(t *testing.T) {
|
||||
|
||||
nr := ValueReference[string]{
|
||||
ReferenceNode: true,
|
||||
Reference: "#/burgers/beer",
|
||||
}
|
||||
nr := ValueReference[string]{}
|
||||
nr.SetReference("#/burgers/beer", nil)
|
||||
|
||||
data, _ := yaml.Marshal(nr)
|
||||
assert.Equal(t, `$ref: '#/burgers/beer'`, strings.TrimSpace(string(data)))
|
||||
|
||||
}
|
||||
|
||||
func TestValueReference_MarshalYAML(t *testing.T) {
|
||||
|
||||
v := map[string]interface{}{
|
||||
"beer": "burger",
|
||||
"wine": "cheese",
|
||||
@@ -725,7 +680,6 @@ wine: cheese`
|
||||
}
|
||||
|
||||
func TestKeyReference_GetValueUntyped(t *testing.T) {
|
||||
|
||||
type anything struct {
|
||||
thing string
|
||||
}
|
||||
|
||||
@@ -6,9 +6,10 @@ package v2
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
@@ -23,7 +24,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 orderedmap.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.
|
||||
@@ -32,7 +33,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 orderedmap.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.
|
||||
@@ -41,7 +42,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 orderedmap.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
|
||||
@@ -50,7 +51,7 @@ type SecurityDefinitions struct {
|
||||
// arrays or models.
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
type Definitions struct {
|
||||
Schemas orderedmap.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.
|
||||
@@ -77,46 +78,79 @@ func (s *SecurityDefinitions) FindSecurityDefinition(securityDef string) *low.Va
|
||||
func (d *Definitions) Build(ctx context.Context, _, 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
|
||||
totalDefinitions := 0
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*base.SchemaProxy], e chan error) {
|
||||
|
||||
obj, err, _, rv := low.ExtractObjectRaw[*base.SchemaProxy](ctx, label, value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult[*base.SchemaProxy]{k: label, v: low.ValueReference[*base.SchemaProxy]{
|
||||
Value: obj, ValueNode: value, Reference: rv,
|
||||
}}
|
||||
type buildInput struct {
|
||||
label *yaml.Node
|
||||
value *yaml.Node
|
||||
}
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
defLabel = root.Content[i]
|
||||
continue
|
||||
}
|
||||
totalDefinitions++
|
||||
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
|
||||
}
|
||||
|
||||
completedDefs := 0
|
||||
results := orderedmap.New[low.KeyReference[string], low.ValueReference[*base.SchemaProxy]]()
|
||||
for completedDefs < totalDefinitions {
|
||||
select {
|
||||
case err := <-errorChan:
|
||||
return err
|
||||
case sch := <-resultChan:
|
||||
completedDefs++
|
||||
key := low.KeyReference[string]{
|
||||
Value: sch.k.Value,
|
||||
KeyNode: sch.k,
|
||||
in := make(chan buildInput)
|
||||
out := make(chan definitionResult[*base.SchemaProxy])
|
||||
done := make(chan struct{})
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(2) // input and output goroutines.
|
||||
|
||||
// TranslatePipeline input.
|
||||
go func() {
|
||||
defer func() {
|
||||
close(in)
|
||||
wg.Done()
|
||||
}()
|
||||
var label *yaml.Node
|
||||
for i, value := range root.Content {
|
||||
if i%2 == 0 {
|
||||
label = value
|
||||
continue
|
||||
}
|
||||
|
||||
select {
|
||||
case in <- buildInput{
|
||||
label: label,
|
||||
value: value,
|
||||
}:
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
results.Set(key, sch.v)
|
||||
}
|
||||
}()
|
||||
|
||||
// TranslatePipeline output.
|
||||
go func() {
|
||||
for {
|
||||
result, ok := <-out
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
|
||||
key := low.KeyReference[string]{
|
||||
Value: result.k.Value,
|
||||
KeyNode: result.k,
|
||||
}
|
||||
results.Set(key, result.v)
|
||||
}
|
||||
close(done)
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
translateFunc := func(value buildInput) (definitionResult[*base.SchemaProxy], error) {
|
||||
obj, err, _, rv := low.ExtractObjectRaw[*base.SchemaProxy](ctx, value.label, value.value, idx)
|
||||
if err != nil {
|
||||
return definitionResult[*base.SchemaProxy]{}, err
|
||||
}
|
||||
|
||||
v := low.ValueReference[*base.SchemaProxy]{
|
||||
Value: obj, ValueNode: value.value,
|
||||
}
|
||||
v.SetReference(rv, value.value)
|
||||
|
||||
return definitionResult[*base.SchemaProxy]{k: value.label, v: v}, nil
|
||||
}
|
||||
|
||||
err := datamodel.TranslatePipeline[buildInput, definitionResult[*base.SchemaProxy]](in, out, translateFunc)
|
||||
wg.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Schemas = results
|
||||
return nil
|
||||
}
|
||||
@@ -124,15 +158,8 @@ func (d *Definitions) Build(ctx context.Context, _, root *yaml.Node, idx *index.
|
||||
// Hash will return a consistent SHA256 Hash of the Definitions object
|
||||
func (d *Definitions) Hash() [32]byte {
|
||||
var f []string
|
||||
keys := make([]string, orderedmap.Len(d.Schemas))
|
||||
z := 0
|
||||
for pair := orderedmap.First(d.Schemas); pair != nil; pair = pair.Next() {
|
||||
keys[z] = pair.Key().Value
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for k := range keys {
|
||||
f = append(f, low.GenerateHashString(d.FindSchema(keys[k]).Value))
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(d.Schemas)); pair != nil; pair = pair.Next() {
|
||||
f = append(f, low.GenerateHashString(d.FindSchema(pair.Key().Value).Value))
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
@@ -143,15 +170,21 @@ func (pd *ParameterDefinitions) Build(ctx context.Context, _, root *yaml.Node, i
|
||||
resultChan := make(chan definitionResult[*Parameter])
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*Parameter], e chan error) {
|
||||
|
||||
buildFunc := func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*Parameter], e chan error,
|
||||
) {
|
||||
obj, err, _, rv := low.ExtractObjectRaw[*Parameter](ctx, label, value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult[*Parameter]{k: label, v: low.ValueReference[*Parameter]{Value: obj,
|
||||
ValueNode: value, Reference: rv}}
|
||||
|
||||
v := low.ValueReference[*Parameter]{
|
||||
Value: obj,
|
||||
ValueNode: value,
|
||||
}
|
||||
v.SetReference(rv, value)
|
||||
|
||||
r <- definitionResult[*Parameter]{k: label, v: v}
|
||||
}
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
@@ -193,15 +226,21 @@ func (r *ResponsesDefinitions) Build(ctx context.Context, _, root *yaml.Node, id
|
||||
resultChan := make(chan definitionResult[*Response])
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*Response], e chan error) {
|
||||
|
||||
buildFunc := func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*Response], e chan error,
|
||||
) {
|
||||
obj, err, _, rv := low.ExtractObjectRaw[*Response](ctx, label, value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult[*Response]{k: label, v: low.ValueReference[*Response]{Value: obj,
|
||||
ValueNode: value, Reference: rv}}
|
||||
|
||||
v := low.ValueReference[*Response]{
|
||||
Value: obj,
|
||||
ValueNode: value,
|
||||
}
|
||||
v.SetReference(rv, value)
|
||||
|
||||
r <- definitionResult[*Response]{k: label, v: v}
|
||||
}
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
@@ -238,16 +277,20 @@ func (s *SecurityDefinitions) Build(ctx context.Context, _, root *yaml.Node, idx
|
||||
var defLabel *yaml.Node
|
||||
totalDefinitions := 0
|
||||
|
||||
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*SecurityScheme], e chan error) {
|
||||
|
||||
buildFunc := func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
|
||||
r chan definitionResult[*SecurityScheme], e chan error,
|
||||
) {
|
||||
obj, err, _, rv := low.ExtractObjectRaw[*SecurityScheme](ctx, label, value, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
}
|
||||
r <- definitionResult[*SecurityScheme]{k: label, v: low.ValueReference[*SecurityScheme]{
|
||||
Value: obj, ValueNode: value, Reference: rv,
|
||||
}}
|
||||
|
||||
v := low.ValueReference[*SecurityScheme]{
|
||||
Value: obj, ValueNode: value,
|
||||
}
|
||||
v.SetReference(rv, value)
|
||||
|
||||
r <- definitionResult[*SecurityScheme]{k: label, v: v}
|
||||
}
|
||||
|
||||
for i := range root.Content {
|
||||
|
||||
@@ -6,8 +6,6 @@ package v2
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
@@ -21,12 +19,12 @@ import (
|
||||
// Allows sharing examples for operation responses
|
||||
// - https://swagger.io/specification/v2/#exampleObject
|
||||
type Examples struct {
|
||||
Values orderedmap.Map[low.KeyReference[string], low.ValueReference[any]]
|
||||
Values *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// FindExample attempts to locate an example value, using a key label.
|
||||
func (e *Examples) FindExample(name string) *low.ValueReference[any] {
|
||||
return low.FindItemInOrderedMap[any](name, e.Values)
|
||||
func (e *Examples) FindExample(name string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(name, e.Values)
|
||||
}
|
||||
|
||||
// Build will extract all examples and will attempt to unmarshal content into a map or slice based on type.
|
||||
@@ -34,54 +32,21 @@ func (e *Examples) Build(_ context.Context, _, root *yaml.Node, _ *index.SpecInd
|
||||
root = utils.NodeAlias(root)
|
||||
utils.CheckForMergeNodes(root)
|
||||
var keyNode, currNode *yaml.Node
|
||||
var err error
|
||||
e.Values = orderedmap.New[low.KeyReference[string], low.ValueReference[any]]()
|
||||
e.Values = orderedmap.New[low.KeyReference[string], low.ValueReference[*yaml.Node]]()
|
||||
for i := range root.Content {
|
||||
if i%2 == 0 {
|
||||
keyNode = root.Content[i]
|
||||
continue
|
||||
}
|
||||
currNode = root.Content[i]
|
||||
var n map[string]interface{}
|
||||
err = currNode.Decode(&n)
|
||||
if err != nil {
|
||||
var k []interface{}
|
||||
err = currNode.Decode(&k)
|
||||
if err != nil {
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = currNode.Decode(&j)
|
||||
e.Values.Set(
|
||||
low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
},
|
||||
low.ValueReference[any]{
|
||||
Value: j,
|
||||
ValueNode: currNode,
|
||||
},
|
||||
)
|
||||
continue
|
||||
}
|
||||
e.Values.Set(
|
||||
low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
},
|
||||
low.ValueReference[any]{
|
||||
Value: k,
|
||||
ValueNode: currNode,
|
||||
},
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
e.Values.Set(
|
||||
low.KeyReference[string]{
|
||||
Value: keyNode.Value,
|
||||
KeyNode: keyNode,
|
||||
},
|
||||
low.ValueReference[any]{
|
||||
Value: n,
|
||||
low.ValueReference[*yaml.Node]{
|
||||
Value: currNode,
|
||||
ValueNode: currNode,
|
||||
},
|
||||
)
|
||||
@@ -92,15 +57,8 @@ func (e *Examples) Build(_ context.Context, _, root *yaml.Node, _ *index.SpecInd
|
||||
// Hash will return a consistent SHA256 Hash of the Examples object
|
||||
func (e *Examples) Hash() [32]byte {
|
||||
var f []string
|
||||
keys := make([]string, orderedmap.Len(e.Values))
|
||||
z := 0
|
||||
for pair := orderedmap.First(e.Values); pair != nil; pair = pair.Next() {
|
||||
keys[z] = pair.Key().Value
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for k := range keys {
|
||||
f = append(f, fmt.Sprintf("%v", e.FindExample(keys[k]).Value))
|
||||
for pair := orderedmap.First(orderedmap.SortAlpha(e.Values)); pair != nil; pair = pair.Next() {
|
||||
f = append(f, low.GenerateHashString(pair.Value().Value))
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
"context"
|
||||
"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"
|
||||
)
|
||||
|
||||
// Header Represents a low-level Swagger / OpenAPI 2 Header object.
|
||||
@@ -25,7 +27,7 @@ type Header struct {
|
||||
Description low.NodeReference[string]
|
||||
Items low.NodeReference[*Items]
|
||||
CollectionFormat low.NodeReference[string]
|
||||
Default low.NodeReference[any]
|
||||
Default low.NodeReference[*yaml.Node]
|
||||
Maximum low.NodeReference[int]
|
||||
ExclusiveMaximum low.NodeReference[bool]
|
||||
Minimum low.NodeReference[int]
|
||||
@@ -36,18 +38,18 @@ type Header struct {
|
||||
MaxItems low.NodeReference[int]
|
||||
MinItems low.NodeReference[int]
|
||||
UniqueItems low.NodeReference[bool]
|
||||
Enum low.NodeReference[[]low.ValueReference[any]]
|
||||
Enum low.NodeReference[[]low.ValueReference[*yaml.Node]]
|
||||
MultipleOf low.NodeReference[int]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// FindExtension will attempt to locate an extension value using a name lookup.
|
||||
func (h *Header) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, h.Extensions)
|
||||
func (h *Header) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(ext, h.Extensions)
|
||||
}
|
||||
|
||||
// GetExtensions returns all Header extensions and satisfies the low.HasExtensions interface.
|
||||
func (h *Header) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (h *Header) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return h.Extensions
|
||||
}
|
||||
|
||||
@@ -64,37 +66,14 @@ func (h *Header) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecI
|
||||
|
||||
_, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content)
|
||||
if vn != nil {
|
||||
var n map[string]interface{}
|
||||
err = vn.Decode(&n)
|
||||
if err != nil {
|
||||
// if not a map, then try an array
|
||||
var k []interface{}
|
||||
err = vn.Decode(&k)
|
||||
if err != nil {
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = vn.Decode(&j)
|
||||
h.Default = low.NodeReference[any]{
|
||||
Value: j,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
h.Default = low.NodeReference[any]{
|
||||
Value: k,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
h.Default = low.NodeReference[any]{
|
||||
Value: n,
|
||||
h.Default = low.NodeReference[*yaml.Node]{
|
||||
Value: vn,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -113,8 +92,8 @@ func (h *Header) Hash() [32]byte {
|
||||
if h.CollectionFormat.Value != "" {
|
||||
f = append(f, h.CollectionFormat.Value)
|
||||
}
|
||||
if h.Default.Value != "" {
|
||||
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(h.Default.Value)))))
|
||||
if h.Default.Value != nil && !h.Default.Value.IsZero() {
|
||||
f = append(f, low.GenerateHashString(h.Default.Value))
|
||||
}
|
||||
f = append(f, fmt.Sprint(h.Maximum.Value))
|
||||
f = append(f, fmt.Sprint(h.Minimum.Value))
|
||||
@@ -129,24 +108,17 @@ func (h *Header) Hash() [32]byte {
|
||||
if h.Pattern.Value != "" {
|
||||
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(h.Pattern.Value)))))
|
||||
}
|
||||
f = append(f, low.HashExtensions(h.Extensions)...)
|
||||
|
||||
keys := make([]string, len(h.Extensions))
|
||||
keys := make([]string, len(h.Enum.Value))
|
||||
z := 0
|
||||
for k := range h.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(h.Extensions[k].Value))))
|
||||
for k := range h.Enum.Value {
|
||||
keys[z] = low.ValueToString(h.Enum.Value[k].Value)
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
|
||||
keys = make([]string, len(h.Enum.Value))
|
||||
z = 0
|
||||
for k := range h.Enum.Value {
|
||||
keys[z] = fmt.Sprint(h.Enum.Value[k].Value)
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
if h.Items.Value != nil {
|
||||
f = append(f, low.GenerateHashString(h.Items.Value))
|
||||
}
|
||||
@@ -158,12 +130,15 @@ func (h *Header) Hash() [32]byte {
|
||||
func (h *Header) GetType() *low.NodeReference[string] {
|
||||
return &h.Type
|
||||
}
|
||||
|
||||
func (h *Header) GetDescription() *low.NodeReference[string] {
|
||||
return &h.Description
|
||||
}
|
||||
|
||||
func (h *Header) GetFormat() *low.NodeReference[string] {
|
||||
return &h.Format
|
||||
}
|
||||
|
||||
func (h *Header) GetItems() *low.NodeReference[any] {
|
||||
i := low.NodeReference[any]{
|
||||
KeyNode: h.Items.KeyNode,
|
||||
@@ -172,45 +147,59 @@ func (h *Header) GetItems() *low.NodeReference[any] {
|
||||
}
|
||||
return &i
|
||||
}
|
||||
|
||||
func (h *Header) GetCollectionFormat() *low.NodeReference[string] {
|
||||
return &h.CollectionFormat
|
||||
}
|
||||
func (h *Header) GetDefault() *low.NodeReference[any] {
|
||||
|
||||
func (h *Header) GetDefault() *low.NodeReference[*yaml.Node] {
|
||||
return &h.Default
|
||||
}
|
||||
|
||||
func (h *Header) GetMaximum() *low.NodeReference[int] {
|
||||
return &h.Maximum
|
||||
}
|
||||
|
||||
func (h *Header) GetExclusiveMaximum() *low.NodeReference[bool] {
|
||||
return &h.ExclusiveMaximum
|
||||
}
|
||||
|
||||
func (h *Header) GetMinimum() *low.NodeReference[int] {
|
||||
return &h.Minimum
|
||||
}
|
||||
|
||||
func (h *Header) GetExclusiveMinimum() *low.NodeReference[bool] {
|
||||
return &h.ExclusiveMinimum
|
||||
}
|
||||
|
||||
func (h *Header) GetMaxLength() *low.NodeReference[int] {
|
||||
return &h.MaxLength
|
||||
}
|
||||
|
||||
func (h *Header) GetMinLength() *low.NodeReference[int] {
|
||||
return &h.MinLength
|
||||
}
|
||||
|
||||
func (h *Header) GetPattern() *low.NodeReference[string] {
|
||||
return &h.Pattern
|
||||
}
|
||||
|
||||
func (h *Header) GetMaxItems() *low.NodeReference[int] {
|
||||
return &h.MaxItems
|
||||
}
|
||||
|
||||
func (h *Header) GetMinItems() *low.NodeReference[int] {
|
||||
return &h.MinItems
|
||||
}
|
||||
|
||||
func (h *Header) GetUniqueItems() *low.NodeReference[bool] {
|
||||
return &h.UniqueItems
|
||||
}
|
||||
func (h *Header) GetEnum() *low.NodeReference[[]low.ValueReference[any]] {
|
||||
|
||||
func (h *Header) GetEnum() *low.NodeReference[[]low.ValueReference[*yaml.Node]] {
|
||||
return &h.Enum
|
||||
}
|
||||
|
||||
func (h *Header) GetMultipleOf() *low.NodeReference[int] {
|
||||
return &h.MultipleOf
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeader_Build(t *testing.T) {
|
||||
|
||||
yml := `items:
|
||||
$ref: break`
|
||||
|
||||
@@ -28,11 +29,9 @@ func TestHeader_Build(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestHeader_DefaultAsSlice(t *testing.T) {
|
||||
|
||||
yml := `x-ext: thing
|
||||
default:
|
||||
- why
|
||||
@@ -48,12 +47,15 @@ default:
|
||||
_ = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
|
||||
assert.NotNil(t, n.Default.Value)
|
||||
assert.Len(t, n.Default.Value, 3)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
|
||||
var def []string
|
||||
_ = n.Default.GetValue().Decode(&def)
|
||||
|
||||
assert.Len(t, def, 3)
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
func TestHeader_DefaultAsObject(t *testing.T) {
|
||||
|
||||
yml := `default:
|
||||
lets:
|
||||
create:
|
||||
@@ -72,7 +74,6 @@ func TestHeader_DefaultAsObject(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHeader_NoDefault(t *testing.T) {
|
||||
|
||||
yml := `minimum: 12`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -87,7 +88,6 @@ func TestHeader_NoDefault(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHeader_Hash_n_Grab(t *testing.T) {
|
||||
|
||||
yml := `description: head
|
||||
type: string
|
||||
format: left
|
||||
@@ -160,7 +160,11 @@ pattern: wow
|
||||
assert.Equal(t, "left", n.GetFormat().Value)
|
||||
assert.Equal(t, "left", n.GetFormat().Value)
|
||||
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
|
||||
assert.Equal(t, "shut that door!", n.GetDefault().Value)
|
||||
|
||||
var def string
|
||||
_ = n.GetDefault().Value.Decode(&def)
|
||||
assert.Equal(t, "shut that door!", def)
|
||||
|
||||
assert.Equal(t, 10, n.GetMaximum().Value)
|
||||
assert.Equal(t, 1, n.GetMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMinimum().Value)
|
||||
@@ -174,6 +178,8 @@ pattern: wow
|
||||
assert.Equal(t, "wow", n.GetPattern().Value)
|
||||
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
|
||||
assert.Len(t, n.GetEnum().Value, 2)
|
||||
assert.Equal(t, "large", n.FindExtension("x-belly").Value)
|
||||
|
||||
var xBelly string
|
||||
_ = n.FindExtension("x-belly").GetValue().Decode(&xBelly)
|
||||
assert.Equal(t, "large", xBelly)
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@ import (
|
||||
"context"
|
||||
"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"
|
||||
)
|
||||
|
||||
// Items is a low-level representation of a Swagger / OpenAPI 2 Items object.
|
||||
@@ -25,7 +27,7 @@ type Items struct {
|
||||
Format low.NodeReference[string]
|
||||
CollectionFormat low.NodeReference[string]
|
||||
Items low.NodeReference[*Items]
|
||||
Default low.NodeReference[any]
|
||||
Default low.NodeReference[*yaml.Node]
|
||||
Maximum low.NodeReference[int]
|
||||
ExclusiveMaximum low.NodeReference[bool]
|
||||
Minimum low.NodeReference[int]
|
||||
@@ -36,18 +38,18 @@ type Items struct {
|
||||
MaxItems low.NodeReference[int]
|
||||
MinItems low.NodeReference[int]
|
||||
UniqueItems low.NodeReference[bool]
|
||||
Enum low.NodeReference[[]low.ValueReference[any]]
|
||||
Enum low.NodeReference[[]low.ValueReference[*yaml.Node]]
|
||||
MultipleOf low.NodeReference[int]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// FindExtension will attempt to locate an extension value using a name lookup.
|
||||
func (i *Items) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, i.Extensions)
|
||||
func (i *Items) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(ext, i.Extensions)
|
||||
}
|
||||
|
||||
// GetExtensions returns all Items extensions and satisfies the low.HasExtensions interface.
|
||||
func (i *Items) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (i *Items) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return i.Extensions
|
||||
}
|
||||
|
||||
@@ -63,8 +65,8 @@ func (i *Items) Hash() [32]byte {
|
||||
if i.CollectionFormat.Value != "" {
|
||||
f = append(f, i.CollectionFormat.Value)
|
||||
}
|
||||
if i.Default.Value != "" {
|
||||
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(i.Default.Value)))))
|
||||
if i.Default.Value != nil && !i.Default.Value.IsZero() {
|
||||
f = append(f, low.GenerateHashString(i.Default.Value))
|
||||
}
|
||||
f = append(f, fmt.Sprint(i.Maximum.Value))
|
||||
f = append(f, fmt.Sprint(i.Minimum.Value))
|
||||
@@ -82,7 +84,7 @@ func (i *Items) Hash() [32]byte {
|
||||
keys := make([]string, len(i.Enum.Value))
|
||||
z := 0
|
||||
for k := range i.Enum.Value {
|
||||
keys[z] = fmt.Sprint(i.Enum.Value[k].Value)
|
||||
keys[z] = low.ValueToString(i.Enum.Value[k].Value)
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
@@ -91,14 +93,7 @@ func (i *Items) Hash() [32]byte {
|
||||
if i.Items.Value != nil {
|
||||
f = append(f, low.GenerateHashString(i.Items.Value))
|
||||
}
|
||||
keys = make([]string, len(i.Extensions))
|
||||
z = 0
|
||||
for k := range i.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(i.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(i.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -115,32 +110,8 @@ func (i *Items) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIn
|
||||
|
||||
_, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content)
|
||||
if vn != nil {
|
||||
var n map[string]interface{}
|
||||
err := vn.Decode(&n)
|
||||
if err != nil {
|
||||
// if not a map, then try an array
|
||||
var k []interface{}
|
||||
err = vn.Decode(&k)
|
||||
if err != nil {
|
||||
// lets just default to interface
|
||||
var j interface{}
|
||||
_ = vn.Decode(&j)
|
||||
i.Default = low.NodeReference[any]{
|
||||
Value: j,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
i.Default = low.NodeReference[any]{
|
||||
Value: k,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
i.Default = low.NodeReference[any]{
|
||||
Value: n,
|
||||
i.Default = low.NodeReference[*yaml.Node]{
|
||||
Value: vn,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
@@ -154,9 +125,11 @@ func (i *Items) Build(ctx context.Context, _, root *yaml.Node, idx *index.SpecIn
|
||||
func (i *Items) GetType() *low.NodeReference[string] {
|
||||
return &i.Type
|
||||
}
|
||||
|
||||
func (i *Items) GetFormat() *low.NodeReference[string] {
|
||||
return &i.Format
|
||||
}
|
||||
|
||||
func (i *Items) GetItems() *low.NodeReference[any] {
|
||||
k := low.NodeReference[any]{
|
||||
KeyNode: i.Items.KeyNode,
|
||||
@@ -165,48 +138,63 @@ func (i *Items) GetItems() *low.NodeReference[any] {
|
||||
}
|
||||
return &k
|
||||
}
|
||||
|
||||
func (i *Items) GetCollectionFormat() *low.NodeReference[string] {
|
||||
return &i.CollectionFormat
|
||||
}
|
||||
|
||||
func (i *Items) GetDescription() *low.NodeReference[string] {
|
||||
return nil // not implemented, but required to align with header contract
|
||||
}
|
||||
func (i *Items) GetDefault() *low.NodeReference[any] {
|
||||
|
||||
func (i *Items) GetDefault() *low.NodeReference[*yaml.Node] {
|
||||
return &i.Default
|
||||
}
|
||||
|
||||
func (i *Items) GetMaximum() *low.NodeReference[int] {
|
||||
return &i.Maximum
|
||||
}
|
||||
|
||||
func (i *Items) GetExclusiveMaximum() *low.NodeReference[bool] {
|
||||
return &i.ExclusiveMaximum
|
||||
}
|
||||
|
||||
func (i *Items) GetMinimum() *low.NodeReference[int] {
|
||||
return &i.Minimum
|
||||
}
|
||||
|
||||
func (i *Items) GetExclusiveMinimum() *low.NodeReference[bool] {
|
||||
return &i.ExclusiveMinimum
|
||||
}
|
||||
|
||||
func (i *Items) GetMaxLength() *low.NodeReference[int] {
|
||||
return &i.MaxLength
|
||||
}
|
||||
|
||||
func (i *Items) GetMinLength() *low.NodeReference[int] {
|
||||
return &i.MinLength
|
||||
}
|
||||
|
||||
func (i *Items) GetPattern() *low.NodeReference[string] {
|
||||
return &i.Pattern
|
||||
}
|
||||
|
||||
func (i *Items) GetMaxItems() *low.NodeReference[int] {
|
||||
return &i.MaxItems
|
||||
}
|
||||
|
||||
func (i *Items) GetMinItems() *low.NodeReference[int] {
|
||||
return &i.MinItems
|
||||
}
|
||||
|
||||
func (i *Items) GetUniqueItems() *low.NodeReference[bool] {
|
||||
return &i.UniqueItems
|
||||
}
|
||||
func (i *Items) GetEnum() *low.NodeReference[[]low.ValueReference[any]] {
|
||||
|
||||
func (i *Items) GetEnum() *low.NodeReference[[]low.ValueReference[*yaml.Node]] {
|
||||
return &i.Enum
|
||||
}
|
||||
|
||||
func (i *Items) GetMultipleOf() *low.NodeReference[int] {
|
||||
return &i.MultipleOf
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestItems_Build(t *testing.T) {
|
||||
|
||||
yml := `items:
|
||||
$ref: break`
|
||||
|
||||
@@ -31,7 +32,6 @@ func TestItems_Build(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestItems_DefaultAsSlice(t *testing.T) {
|
||||
|
||||
yml := `x-thing: thing
|
||||
default:
|
||||
- pizza
|
||||
@@ -45,12 +45,14 @@ default:
|
||||
_ = low.BuildModel(&idxNode, &n)
|
||||
_ = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
|
||||
assert.Len(t, n.Default.Value, 2)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
var def []string
|
||||
_ = n.Default.Value.Decode(&def)
|
||||
|
||||
assert.Len(t, def, 2)
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
func TestItems_DefaultAsMap(t *testing.T) {
|
||||
|
||||
yml := `default:
|
||||
hot: pizza
|
||||
tasty: beer`
|
||||
@@ -63,12 +65,13 @@ func TestItems_DefaultAsMap(t *testing.T) {
|
||||
_ = low.BuildModel(&idxNode, &n)
|
||||
_ = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
|
||||
assert.Len(t, n.Default.Value, 2)
|
||||
var def map[string]string
|
||||
_ = n.Default.GetValue().Decode(&def)
|
||||
|
||||
assert.Len(t, def, 2)
|
||||
}
|
||||
|
||||
func TestItems_Hash_n_Grab(t *testing.T) {
|
||||
|
||||
yml := `type: string
|
||||
format: left
|
||||
collectionFormat: nice
|
||||
@@ -138,7 +141,10 @@ pattern: wow
|
||||
assert.Equal(t, "left", n.GetFormat().Value)
|
||||
assert.Equal(t, "left", n.GetFormat().Value)
|
||||
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
|
||||
assert.Equal(t, "shut that door!", n.GetDefault().Value)
|
||||
|
||||
var def string
|
||||
_ = n.GetDefault().Value.Decode(&def)
|
||||
assert.Equal(t, "shut that door!", def)
|
||||
assert.Equal(t, 10, n.GetMaximum().Value)
|
||||
assert.Equal(t, 1, n.GetMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMinimum().Value)
|
||||
@@ -152,7 +158,8 @@ pattern: wow
|
||||
assert.Equal(t, "wow", n.GetPattern().Value)
|
||||
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
|
||||
assert.Len(t, n.GetEnum().Value, 2)
|
||||
assert.Equal(t, "large", n.FindExtension("x-belly").Value)
|
||||
assert.Nil(t, n.GetDescription())
|
||||
|
||||
var xBelly string
|
||||
_ = n.FindExtension("x-belly").GetValue().Decode(&xBelly)
|
||||
assert.Equal(t, "large", xBelly)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,15 @@ import (
|
||||
"context"
|
||||
"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 represents a low-level Swagger / OpenAPI 2 Operation object.
|
||||
@@ -33,7 +35,7 @@ type Operation struct {
|
||||
Schemes low.NodeReference[[]low.ValueReference[string]]
|
||||
Deprecated low.NodeReference[bool]
|
||||
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// Build will extract external docs, extensions, parameters, responses and security requirements.
|
||||
@@ -150,14 +152,7 @@ func (o *Operation) Hash() [32]byte {
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
keys = make([]string, len(o.Extensions))
|
||||
z := 0
|
||||
for k := range o.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(o.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(o.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -185,7 +180,7 @@ func (o *Operation) GetOperationId() low.NodeReference[string] {
|
||||
func (o *Operation) GetDeprecated() low.NodeReference[bool] {
|
||||
return o.Deprecated
|
||||
}
|
||||
func (o *Operation) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (o *Operation) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return o.Extensions
|
||||
}
|
||||
func (o *Operation) GetResponses() low.NodeReference[any] {
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
)
|
||||
|
||||
func TestOperation_Build_ExternalDocs(t *testing.T) {
|
||||
|
||||
yml := `externalDocs:
|
||||
$ref: break`
|
||||
|
||||
@@ -31,11 +30,9 @@ func TestOperation_Build_ExternalDocs(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_Build_Params(t *testing.T) {
|
||||
|
||||
yml := `parameters:
|
||||
$ref: break`
|
||||
|
||||
@@ -50,11 +47,9 @@ func TestOperation_Build_Params(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_Build_Responses(t *testing.T) {
|
||||
|
||||
yml := `responses:
|
||||
$ref: break`
|
||||
|
||||
@@ -69,11 +64,9 @@ func TestOperation_Build_Responses(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_Build_Security(t *testing.T) {
|
||||
|
||||
yml := `security:
|
||||
$ref: break`
|
||||
|
||||
@@ -88,11 +81,9 @@ func TestOperation_Build_Security(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestOperation_Hash_n_Grab(t *testing.T) {
|
||||
|
||||
yml := `tags:
|
||||
- nice
|
||||
- hat
|
||||
@@ -185,5 +176,5 @@ security:
|
||||
assert.True(t, n.GetDeprecated().Value)
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetResponses().Value.(*Responses).Codes))
|
||||
assert.Len(t, n.GetSecurity().Value, 1)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
@@ -7,13 +7,15 @@ import (
|
||||
"context"
|
||||
"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 low-level Swagger / OpenAPI 2 Parameter object.
|
||||
@@ -68,7 +70,7 @@ type Parameter struct {
|
||||
Schema low.NodeReference[*base.SchemaProxy]
|
||||
Items low.NodeReference[*Items]
|
||||
CollectionFormat low.NodeReference[string]
|
||||
Default low.NodeReference[any]
|
||||
Default low.NodeReference[*yaml.Node]
|
||||
Maximum low.NodeReference[int]
|
||||
ExclusiveMaximum low.NodeReference[bool]
|
||||
Minimum low.NodeReference[int]
|
||||
@@ -79,18 +81,18 @@ type Parameter struct {
|
||||
MaxItems low.NodeReference[int]
|
||||
MinItems low.NodeReference[int]
|
||||
UniqueItems low.NodeReference[bool]
|
||||
Enum low.NodeReference[[]low.ValueReference[any]]
|
||||
Enum low.NodeReference[[]low.ValueReference[*yaml.Node]]
|
||||
MultipleOf low.NodeReference[int]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// FindExtension attempts to locate a extension value given a name.
|
||||
func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, p.Extensions)
|
||||
func (p *Parameter) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(ext, p.Extensions)
|
||||
}
|
||||
|
||||
// GetExtensions returns all Parameter extensions and satisfies the low.HasExtensions interface.
|
||||
func (p *Parameter) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (p *Parameter) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return p.Extensions
|
||||
}
|
||||
|
||||
@@ -114,30 +116,8 @@ func (p *Parameter) Build(ctx context.Context, _, root *yaml.Node, idx *index.Sp
|
||||
|
||||
_, ln, vn := utils.FindKeyNodeFull(DefaultLabel, root.Content)
|
||||
if vn != nil {
|
||||
var n map[string]interface{}
|
||||
err := vn.Decode(&n)
|
||||
if err != nil {
|
||||
var k []interface{}
|
||||
err = vn.Decode(&k)
|
||||
if err != nil {
|
||||
var j interface{}
|
||||
_ = vn.Decode(&j)
|
||||
p.Default = low.NodeReference[any]{
|
||||
Value: j,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
p.Default = low.NodeReference[any]{
|
||||
Value: k,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
p.Default = low.NodeReference[any]{
|
||||
Value: n,
|
||||
p.Default = low.NodeReference[*yaml.Node]{
|
||||
Value: vn,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
@@ -172,8 +152,8 @@ func (p *Parameter) Hash() [32]byte {
|
||||
if p.CollectionFormat.Value != "" {
|
||||
f = append(f, p.CollectionFormat.Value)
|
||||
}
|
||||
if p.Default.Value != "" {
|
||||
f = append(f, fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprint(p.Default.Value)))))
|
||||
if p.Default.Value != nil && !p.Default.Value.IsZero() {
|
||||
f = append(f, low.GenerateHashString(p.Default.Value))
|
||||
}
|
||||
f = append(f, fmt.Sprint(p.Maximum.Value))
|
||||
f = append(f, fmt.Sprint(p.Minimum.Value))
|
||||
@@ -192,20 +172,13 @@ func (p *Parameter) Hash() [32]byte {
|
||||
keys := make([]string, len(p.Enum.Value))
|
||||
z := 0
|
||||
for k := range p.Enum.Value {
|
||||
keys[z] = fmt.Sprint(p.Enum.Value[k].Value)
|
||||
keys[z] = low.ValueToString(p.Enum.Value[k].Value)
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
|
||||
keys = make([]string, len(p.Extensions))
|
||||
z = 0
|
||||
for k := range p.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(p.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(p.Extensions)...)
|
||||
if p.Items.Value != nil {
|
||||
f = append(f, fmt.Sprintf("%x", p.Items.Value.Hash()))
|
||||
}
|
||||
@@ -217,21 +190,27 @@ func (p *Parameter) Hash() [32]byte {
|
||||
func (p *Parameter) GetName() *low.NodeReference[string] {
|
||||
return &p.Name
|
||||
}
|
||||
|
||||
func (p *Parameter) GetIn() *low.NodeReference[string] {
|
||||
return &p.In
|
||||
}
|
||||
|
||||
func (p *Parameter) GetType() *low.NodeReference[string] {
|
||||
return &p.Type
|
||||
}
|
||||
|
||||
func (p *Parameter) GetDescription() *low.NodeReference[string] {
|
||||
return &p.Description
|
||||
}
|
||||
|
||||
func (p *Parameter) GetRequired() *low.NodeReference[bool] {
|
||||
return &p.Required
|
||||
}
|
||||
|
||||
func (p *Parameter) GetAllowEmptyValue() *low.NodeReference[bool] {
|
||||
return &p.AllowEmptyValue
|
||||
}
|
||||
|
||||
func (p *Parameter) GetSchema() *low.NodeReference[any] {
|
||||
i := low.NodeReference[any]{
|
||||
KeyNode: p.Schema.KeyNode,
|
||||
@@ -240,9 +219,11 @@ func (p *Parameter) GetSchema() *low.NodeReference[any] {
|
||||
}
|
||||
return &i
|
||||
}
|
||||
|
||||
func (p *Parameter) GetFormat() *low.NodeReference[string] {
|
||||
return &p.Format
|
||||
}
|
||||
|
||||
func (p *Parameter) GetItems() *low.NodeReference[any] {
|
||||
i := low.NodeReference[any]{
|
||||
KeyNode: p.Items.KeyNode,
|
||||
@@ -251,45 +232,59 @@ func (p *Parameter) GetItems() *low.NodeReference[any] {
|
||||
}
|
||||
return &i
|
||||
}
|
||||
|
||||
func (p *Parameter) GetCollectionFormat() *low.NodeReference[string] {
|
||||
return &p.CollectionFormat
|
||||
}
|
||||
func (p *Parameter) GetDefault() *low.NodeReference[any] {
|
||||
|
||||
func (p *Parameter) GetDefault() *low.NodeReference[*yaml.Node] {
|
||||
return &p.Default
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMaximum() *low.NodeReference[int] {
|
||||
return &p.Maximum
|
||||
}
|
||||
|
||||
func (p *Parameter) GetExclusiveMaximum() *low.NodeReference[bool] {
|
||||
return &p.ExclusiveMaximum
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMinimum() *low.NodeReference[int] {
|
||||
return &p.Minimum
|
||||
}
|
||||
|
||||
func (p *Parameter) GetExclusiveMinimum() *low.NodeReference[bool] {
|
||||
return &p.ExclusiveMinimum
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMaxLength() *low.NodeReference[int] {
|
||||
return &p.MaxLength
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMinLength() *low.NodeReference[int] {
|
||||
return &p.MinLength
|
||||
}
|
||||
|
||||
func (p *Parameter) GetPattern() *low.NodeReference[string] {
|
||||
return &p.Pattern
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMaxItems() *low.NodeReference[int] {
|
||||
return &p.MaxItems
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMinItems() *low.NodeReference[int] {
|
||||
return &p.MinItems
|
||||
}
|
||||
|
||||
func (p *Parameter) GetUniqueItems() *low.NodeReference[bool] {
|
||||
return &p.UniqueItems
|
||||
}
|
||||
func (p *Parameter) GetEnum() *low.NodeReference[[]low.ValueReference[any]] {
|
||||
|
||||
func (p *Parameter) GetEnum() *low.NodeReference[[]low.ValueReference[*yaml.Node]] {
|
||||
return &p.Enum
|
||||
}
|
||||
|
||||
func (p *Parameter) GetMultipleOf() *low.NodeReference[int] {
|
||||
return &p.MultipleOf
|
||||
}
|
||||
|
||||
@@ -5,16 +5,17 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"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/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParameter_Build(t *testing.T) {
|
||||
|
||||
yml := `$ref: break`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -28,11 +29,9 @@ func TestParameter_Build(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestParameter_Build_Items(t *testing.T) {
|
||||
|
||||
yml := `items:
|
||||
$ref: break`
|
||||
|
||||
@@ -47,11 +46,9 @@ func TestParameter_Build_Items(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestParameter_DefaultSlice(t *testing.T) {
|
||||
|
||||
yml := `default:
|
||||
- things
|
||||
- junk
|
||||
@@ -65,11 +62,14 @@ func TestParameter_DefaultSlice(t *testing.T) {
|
||||
_ = low.BuildModel(&idxNode, &n)
|
||||
|
||||
_ = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Len(t, n.Default.Value.([]any), 3)
|
||||
|
||||
var a []any
|
||||
_ = n.Default.Value.Decode(&a)
|
||||
|
||||
assert.Len(t, a, 3)
|
||||
}
|
||||
|
||||
func TestParameter_DefaultMap(t *testing.T) {
|
||||
|
||||
yml := `default:
|
||||
things: junk
|
||||
stuff: more junk`
|
||||
@@ -82,11 +82,14 @@ func TestParameter_DefaultMap(t *testing.T) {
|
||||
_ = low.BuildModel(&idxNode, &n)
|
||||
|
||||
_ = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Len(t, n.Default.Value.(map[string]any), 2)
|
||||
|
||||
var m map[string]any
|
||||
_ = n.Default.Value.Decode(&m)
|
||||
|
||||
assert.Len(t, m, 2)
|
||||
}
|
||||
|
||||
func TestParameter_NoDefaultNoError(t *testing.T) {
|
||||
|
||||
yml := `name: pizza-pie`
|
||||
|
||||
var idxNode yaml.Node
|
||||
@@ -101,7 +104,6 @@ func TestParameter_NoDefaultNoError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestParameter_Hash_n_Grab(t *testing.T) {
|
||||
|
||||
yml := `name: mcmuffin
|
||||
in: my-belly
|
||||
description: tasty!
|
||||
@@ -185,7 +187,10 @@ allowEmptyValue: true
|
||||
assert.Equal(t, "left", n.GetFormat().Value)
|
||||
assert.Equal(t, "left", n.GetFormat().Value)
|
||||
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
|
||||
assert.Equal(t, "shut that door!", n.GetDefault().Value)
|
||||
|
||||
var def string
|
||||
_ = n.GetDefault().Value.Decode(&def)
|
||||
assert.Equal(t, "shut that door!", def)
|
||||
assert.Equal(t, 10, n.GetMaximum().Value)
|
||||
assert.Equal(t, 1, n.GetMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMinimum().Value)
|
||||
@@ -199,7 +204,10 @@ allowEmptyValue: true
|
||||
assert.Equal(t, "wow", n.GetPattern().Value)
|
||||
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
|
||||
assert.Len(t, n.GetEnum().Value, 2)
|
||||
assert.Equal(t, "large", n.FindExtension("x-belly").Value)
|
||||
|
||||
var xBelly string
|
||||
_ = n.FindExtension("x-belly").Value.Decode(&xBelly)
|
||||
assert.Equal(t, "large", xBelly)
|
||||
assert.Equal(t, "tasty!", n.GetDescription().Value)
|
||||
assert.Equal(t, "mcmuffin", n.GetName().Value)
|
||||
assert.Equal(t, "my-belly", n.GetIn().Value)
|
||||
@@ -208,6 +216,5 @@ allowEmptyValue: true
|
||||
assert.Equal(t, "int", v.Value.A) // A is v2
|
||||
assert.True(t, n.GetRequired().Value)
|
||||
assert.True(t, n.GetAllowEmptyValue().Value)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
@@ -13,6 +13,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"
|
||||
)
|
||||
@@ -34,16 +35,16 @@ type PathItem struct {
|
||||
Head low.NodeReference[*Operation]
|
||||
Patch low.NodeReference[*Operation]
|
||||
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]]
|
||||
}
|
||||
|
||||
// FindExtension will attempt to locate an extension given a name.
|
||||
func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, p.Extensions)
|
||||
func (p *PathItem) FindExtension(ext string) *low.ValueReference[*yaml.Node] {
|
||||
return low.FindItemInOrderedMap(ext, p.Extensions)
|
||||
}
|
||||
|
||||
// GetExtensions returns all PathItem extensions and satisfies the low.HasExtensions interface.
|
||||
func (p *PathItem) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
func (p *PathItem) GetExtensions() *orderedmap.Map[low.KeyReference[string], low.ValueReference[*yaml.Node]] {
|
||||
return p.Extensions
|
||||
}
|
||||
|
||||
@@ -153,8 +154,8 @@ func (p *PathItem) Build(ctx context.Context, _, root *yaml.Node, idx *index.Spe
|
||||
}
|
||||
}
|
||||
|
||||
//all operations have been superficially built,
|
||||
//now we need to build out the operation, we will do this asynchronously for speed.
|
||||
// all operations have been superficially built,
|
||||
// now we need to build out the operation, we will do this asynchronously for speed.
|
||||
opBuildChan := make(chan bool)
|
||||
opErrorChan := make(chan error)
|
||||
|
||||
@@ -223,13 +224,6 @@ func (p *PathItem) Hash() [32]byte {
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
keys = make([]string, len(p.Extensions))
|
||||
z := 0
|
||||
for k := range p.Extensions {
|
||||
keys[z] = fmt.Sprintf("%s-%x", k.Value, sha256.Sum256([]byte(fmt.Sprint(p.Extensions[k].Value))))
|
||||
z++
|
||||
}
|
||||
sort.Strings(keys)
|
||||
f = append(f, keys...)
|
||||
f = append(f, low.HashExtensions(p.Extensions)...)
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
@@ -5,15 +5,16 @@ package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/orderedmap"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPathItem_Build_Params(t *testing.T) {
|
||||
|
||||
yml := `parameters:
|
||||
$ref: break`
|
||||
|
||||
@@ -28,11 +29,9 @@ func TestPathItem_Build_Params(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestPathItem_Build_MethodFail(t *testing.T) {
|
||||
|
||||
yml := `post:
|
||||
$ref: break`
|
||||
|
||||
@@ -47,11 +46,9 @@ func TestPathItem_Build_MethodFail(t *testing.T) {
|
||||
|
||||
err = n.Build(context.Background(), nil, idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestPathItem_Hash(t *testing.T) {
|
||||
|
||||
yml := `get:
|
||||
description: get me up
|
||||
put:
|
||||
@@ -108,6 +105,5 @@ parameters:
|
||||
|
||||
// hash
|
||||
assert.Equal(t, n.Hash(), n2.Hash())
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
|
||||
assert.Equal(t, 1, orderedmap.Len(n.GetExtensions()))
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user