mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Added support for unevaluatedProperties as Schema and bool #118
Also ran `gofmt` across the entire project. Things need cleaning up. Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
@@ -32,14 +32,17 @@ const (
|
||||
)
|
||||
|
||||
// OpenAPI3SchemaData is an embedded version of the OpenAPI 3 Schema
|
||||
//
|
||||
//go:embed schemas/oas3-schema.json
|
||||
var OpenAPI3SchemaData string // embedded OAS3 schema
|
||||
|
||||
// OpenAPI31SchemaData is an embedded version of the OpenAPI 3.1 Schema
|
||||
//
|
||||
//go:embed schemas/oas31-schema.json
|
||||
var OpenAPI31SchemaData string // embedded OAS31 schema
|
||||
|
||||
// OpenAPI2SchemaData is an embedded version of the OpenAPI 2 (Swagger) Schema
|
||||
//
|
||||
//go:embed schemas/swagger2-schema.json
|
||||
var OpenAPI2SchemaData string // embedded OAS3 schema
|
||||
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
package datamodel
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewClosedDocumentConfiguration(t *testing.T) {
|
||||
cfg := NewClosedDocumentConfiguration()
|
||||
assert.False(t, cfg.AllowRemoteReferences)
|
||||
assert.False(t, cfg.AllowFileReferences)
|
||||
cfg := NewClosedDocumentConfiguration()
|
||||
assert.False(t, cfg.AllowRemoteReferences)
|
||||
assert.False(t, cfg.AllowFileReferences)
|
||||
}
|
||||
|
||||
func TestNewOpenDocumentConfiguration(t *testing.T) {
|
||||
cfg := NewOpenDocumentConfiguration()
|
||||
assert.True(t, cfg.AllowRemoteReferences)
|
||||
assert.True(t, cfg.AllowFileReferences)
|
||||
cfg := NewOpenDocumentConfiguration()
|
||||
assert.True(t, cfg.AllowRemoteReferences)
|
||||
assert.True(t, cfg.AllowFileReferences)
|
||||
}
|
||||
|
||||
@@ -9,4 +9,3 @@
|
||||
// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure
|
||||
// that all the latest features are collected, without damaging backwards compatibility.
|
||||
package base
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
// Contact represents a high-level representation of the Contact definitions found at
|
||||
// v2 - https://swagger.io/specification/v2/#contactObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#contactObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
|
||||
type Contact struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
@@ -47,6 +48,3 @@ func (c *Contact) MarshalYAML() (interface{}, error) {
|
||||
nb := low2.NewNodeBuilder(c, c.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
// which is used to inform the consumer of the document of an alternative schema based on the value associated with it.
|
||||
//
|
||||
// When using the discriminator, inline schemas will not be considered.
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||
//
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||
type Discriminator struct {
|
||||
PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"`
|
||||
Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`
|
||||
|
||||
@@ -44,7 +44,6 @@ func (d *DynamicValue[A, B]) RenderInline() ([]byte, error) {
|
||||
return yaml.Marshal(d)
|
||||
}
|
||||
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the DynamicValue object.
|
||||
func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) {
|
||||
// this is a custom renderer, we can't use the NodeBuilder out of the gate.
|
||||
|
||||
@@ -11,7 +11,8 @@ import (
|
||||
)
|
||||
|
||||
// Example represents a high-level Example object as defined by OpenAPI 3+
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
||||
//
|
||||
// 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"`
|
||||
|
||||
@@ -12,8 +12,9 @@ import (
|
||||
// ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3
|
||||
//
|
||||
// Allows referencing an external resource for extended documentation.
|
||||
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
||||
//
|
||||
// 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"`
|
||||
|
||||
@@ -200,5 +200,3 @@ x-cake:
|
||||
bytes, _ := highInfo.Render()
|
||||
assert.Len(t, bytes, 275)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,56 +4,56 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLicense_Render(t *testing.T) {
|
||||
|
||||
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
|
||||
dat, _ := highL.Render()
|
||||
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
|
||||
dat, _ := highL.Render()
|
||||
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &cNode)
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &cNode)
|
||||
|
||||
// build low
|
||||
var lowLicense lowbase.License
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||
// build low
|
||||
var lowLicense lowbase.License
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||
|
||||
// build high
|
||||
highLicense := NewLicense(&lowLicense)
|
||||
// build high
|
||||
highLicense := NewLicense(&lowLicense)
|
||||
|
||||
assert.Equal(t, "MIT", highLicense.Name)
|
||||
assert.Equal(t, "https://pb33f.io", highLicense.URL)
|
||||
assert.Equal(t, "MIT", highLicense.Name)
|
||||
assert.Equal(t, "https://pb33f.io", highLicense.URL)
|
||||
|
||||
}
|
||||
|
||||
func TestLicense_RenderEqual(t *testing.T) {
|
||||
|
||||
yml := `name: MIT
|
||||
yml := `name: MIT
|
||||
url: https://pb33f.io/not-real
|
||||
`
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||
|
||||
// build low
|
||||
var lowLicense lowbase.License
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||
_ = lowLicense.Build(cNode.Content[0], nil)
|
||||
// build low
|
||||
var lowLicense lowbase.License
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||
_ = lowLicense.Build(cNode.Content[0], nil)
|
||||
|
||||
// build high
|
||||
highLicense := NewLicense(&lowLicense)
|
||||
// build high
|
||||
highLicense := NewLicense(&lowLicense)
|
||||
|
||||
assert.Equal(t, "MIT", highLicense.Name)
|
||||
assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL)
|
||||
assert.Equal(t, "MIT", highLicense.Name)
|
||||
assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL)
|
||||
|
||||
// re-render and ensure everything is in the same order as before.
|
||||
bytes, _ := highLicense.Render()
|
||||
assert.Equal(t, yml, string(bytes))
|
||||
// re-render and ensure everything is in the same order as before.
|
||||
bytes, _ := highLicense.Render()
|
||||
assert.Equal(t, yml, string(bytes))
|
||||
|
||||
}
|
||||
@@ -10,8 +10,9 @@ import (
|
||||
)
|
||||
|
||||
// License is a high-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
|
||||
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||
type License struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
)
|
||||
|
||||
// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1
|
||||
@@ -22,8 +22,8 @@ import (
|
||||
// - v3 schema: https://swagger.io/specification/#schema-object
|
||||
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
|
||||
type Schema struct {
|
||||
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
|
||||
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
||||
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
|
||||
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
||||
|
||||
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
||||
// In version 3.1, ExclusiveMaximum is a number.
|
||||
@@ -33,76 +33,79 @@ type Schema struct {
|
||||
// In version 3.1, ExclusiveMinimum is a number.
|
||||
ExclusiveMinimum *DynamicValue[bool, float64] `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
|
||||
|
||||
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
||||
// in version 3.1, Type can be multiple values
|
||||
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
||||
// in version 3.1, Type can be multiple values
|
||||
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||
|
||||
// Schemas are resolved on demand using a SchemaProxy
|
||||
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
|
||||
// Schemas are resolved on demand using a SchemaProxy
|
||||
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
|
||||
|
||||
// Polymorphic Schemas are only available in version 3+
|
||||
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
|
||||
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
|
||||
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
|
||||
// Polymorphic Schemas are only available in version 3+
|
||||
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
|
||||
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
|
||||
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"`
|
||||
// in 3.1 examples can be an array (which is recommended)
|
||||
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
|
||||
// in 3.1 prefixItems provides tuple validation support.
|
||||
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,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 map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
|
||||
PatternProperties 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"`
|
||||
UnevaluatedProperties *SchemaProxy `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,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 map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
|
||||
PatternProperties 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 Items can be a Schema or a boolean
|
||||
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
// in 3.1 UnevaluatedProperties can be a Schema or a boolean
|
||||
// https://github.com/pb33f/libopenapi/issues/118
|
||||
UnevaluatedProperties *DynamicValue[*SchemaProxy, *bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
|
||||
|
||||
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
|
||||
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
|
||||
// in 3.1 Items can be a Schema or a boolean
|
||||
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
|
||||
// Compatible with all versions
|
||||
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
|
||||
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
|
||||
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
|
||||
Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,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 any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Default any `json:"default,omitempty" yaml:"default,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:"-"`
|
||||
low *base.Schema
|
||||
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
|
||||
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
|
||||
|
||||
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
||||
ParentProxy *SchemaProxy `json:"-" yaml:"-"`
|
||||
// Compatible with all versions
|
||||
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
|
||||
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
|
||||
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
|
||||
Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,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 any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Default any `json:"default,omitempty" yaml:"default,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:"-"`
|
||||
low *base.Schema
|
||||
|
||||
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
||||
ParentProxy *SchemaProxy `json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
// NewSchema will create a new high-level schema from a low-level one.
|
||||
@@ -164,302 +167,318 @@ func NewSchema(schema *base.Schema) *Schema {
|
||||
s.MinProperties = &schema.MinProperties.Value
|
||||
}
|
||||
|
||||
if !schema.MaxContains.IsEmpty() {
|
||||
s.MaxContains = &schema.MaxContains.Value
|
||||
}
|
||||
if !schema.MinContains.IsEmpty() {
|
||||
s.MinContains = &schema.MinContains.Value
|
||||
}
|
||||
if !schema.UniqueItems.IsEmpty() {
|
||||
s.UniqueItems = &schema.UniqueItems.Value
|
||||
}
|
||||
if !schema.Contains.IsEmpty() {
|
||||
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Contains.ValueNode,
|
||||
Value: schema.Contains.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.If.IsEmpty() {
|
||||
s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.If.ValueNode,
|
||||
Value: schema.If.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.Else.IsEmpty() {
|
||||
s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Else.ValueNode,
|
||||
Value: schema.Else.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.Then.IsEmpty() {
|
||||
s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Then.ValueNode,
|
||||
Value: schema.Then.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.PropertyNames.IsEmpty() {
|
||||
s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.PropertyNames.ValueNode,
|
||||
Value: schema.PropertyNames.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.UnevaluatedItems.IsEmpty() {
|
||||
s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.UnevaluatedItems.ValueNode,
|
||||
Value: schema.UnevaluatedItems.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.UnevaluatedProperties.IsEmpty() {
|
||||
s.UnevaluatedProperties = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.UnevaluatedProperties.ValueNode,
|
||||
Value: schema.UnevaluatedProperties.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.MaxContains.IsEmpty() {
|
||||
s.MaxContains = &schema.MaxContains.Value
|
||||
}
|
||||
if !schema.MinContains.IsEmpty() {
|
||||
s.MinContains = &schema.MinContains.Value
|
||||
}
|
||||
if !schema.UniqueItems.IsEmpty() {
|
||||
s.UniqueItems = &schema.UniqueItems.Value
|
||||
}
|
||||
if !schema.Contains.IsEmpty() {
|
||||
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Contains.ValueNode,
|
||||
Value: schema.Contains.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.If.IsEmpty() {
|
||||
s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.If.ValueNode,
|
||||
Value: schema.If.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.Else.IsEmpty() {
|
||||
s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Else.ValueNode,
|
||||
Value: schema.Else.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.Then.IsEmpty() {
|
||||
s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Then.ValueNode,
|
||||
Value: schema.Then.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.PropertyNames.IsEmpty() {
|
||||
s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.PropertyNames.ValueNode,
|
||||
Value: schema.PropertyNames.Value,
|
||||
}}
|
||||
}
|
||||
if !schema.UnevaluatedItems.IsEmpty() {
|
||||
s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.UnevaluatedItems.ValueNode,
|
||||
Value: schema.UnevaluatedItems.Value,
|
||||
}}
|
||||
}
|
||||
// check if unevaluated properties is a schema
|
||||
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsA() {
|
||||
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
|
||||
A: &SchemaProxy{
|
||||
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.UnevaluatedProperties.ValueNode,
|
||||
Value: schema.UnevaluatedProperties.Value.A,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
s.Pattern = schema.Pattern.Value
|
||||
s.Format = schema.Format.Value
|
||||
// check if unevaluated properties is a bool
|
||||
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsB() {
|
||||
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
|
||||
B: schema.UnevaluatedProperties.Value.B,
|
||||
}
|
||||
}
|
||||
|
||||
// 3.0 spec is a single value
|
||||
if !schema.Type.IsEmpty() && schema.Type.Value.IsA() {
|
||||
s.Type = []string{schema.Type.Value.A}
|
||||
}
|
||||
// 3.1 spec may have multiple values
|
||||
if !schema.Type.IsEmpty() && schema.Type.Value.IsB() {
|
||||
for i := range schema.Type.Value.B {
|
||||
s.Type = append(s.Type, schema.Type.Value.B[i].Value)
|
||||
}
|
||||
}
|
||||
if schema.AdditionalProperties.Value != nil {
|
||||
if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok {
|
||||
s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
KeyNode: schema.AdditionalProperties.KeyNode,
|
||||
ValueNode: schema.AdditionalProperties.ValueNode,
|
||||
Value: addPropSchema,
|
||||
})
|
||||
} else {
|
||||
// TODO: check for slice and map types and unpack correctly.
|
||||
if !schema.UnevaluatedProperties.IsEmpty() {
|
||||
|
||||
s.AdditionalProperties = schema.AdditionalProperties.Value
|
||||
}
|
||||
}
|
||||
s.Description = schema.Description.Value
|
||||
s.Default = schema.Default.Value
|
||||
if !schema.Nullable.IsEmpty() {
|
||||
s.Nullable = &schema.Nullable.Value
|
||||
}
|
||||
if !schema.ReadOnly.IsEmpty() {
|
||||
s.ReadOnly = schema.ReadOnly.Value
|
||||
}
|
||||
if !schema.WriteOnly.IsEmpty() {
|
||||
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))
|
||||
for i := 0; i < len(schema.Examples.Value); i++ {
|
||||
examples[i] = schema.Examples.Value[i].Value
|
||||
}
|
||||
s.Examples = examples
|
||||
}
|
||||
s.Extensions = high.ExtractExtensions(schema.Extensions)
|
||||
if !schema.Discriminator.IsEmpty() {
|
||||
s.Discriminator = NewDiscriminator(schema.Discriminator.Value)
|
||||
}
|
||||
if !schema.XML.IsEmpty() {
|
||||
s.XML = NewXML(schema.XML.Value)
|
||||
}
|
||||
if !schema.ExternalDocs.IsEmpty() {
|
||||
s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value)
|
||||
}
|
||||
var req []string
|
||||
for i := range schema.Required.Value {
|
||||
req = append(req, schema.Required.Value[i].Value)
|
||||
}
|
||||
s.Required = req
|
||||
}
|
||||
|
||||
var enum []any
|
||||
if !schema.Anchor.IsEmpty() {
|
||||
s.Anchor = schema.Anchor.Value
|
||||
}
|
||||
s.Pattern = schema.Pattern.Value
|
||||
s.Format = schema.Format.Value
|
||||
|
||||
// TODO: check this behavior.
|
||||
for i := range schema.Enum.Value {
|
||||
enum = append(enum, schema.Enum.Value[i].Value)
|
||||
}
|
||||
s.Enum = enum
|
||||
// 3.0 spec is a single value
|
||||
if !schema.Type.IsEmpty() && schema.Type.Value.IsA() {
|
||||
s.Type = []string{schema.Type.Value.A}
|
||||
}
|
||||
// 3.1 spec may have multiple values
|
||||
if !schema.Type.IsEmpty() && schema.Type.Value.IsB() {
|
||||
for i := range schema.Type.Value.B {
|
||||
s.Type = append(s.Type, schema.Type.Value.B[i].Value)
|
||||
}
|
||||
}
|
||||
if schema.AdditionalProperties.Value != nil {
|
||||
if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok {
|
||||
s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
KeyNode: schema.AdditionalProperties.KeyNode,
|
||||
ValueNode: schema.AdditionalProperties.ValueNode,
|
||||
Value: addPropSchema,
|
||||
})
|
||||
} else {
|
||||
// TODO: check for slice and map types and unpack correctly.
|
||||
|
||||
// async work.
|
||||
// any polymorphic properties need to be handled in their own threads
|
||||
// any properties each need to be processed in their own thread.
|
||||
// we go as fast as we can.
|
||||
polyCompletedChan := make(chan bool)
|
||||
propsChan := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
s.AdditionalProperties = schema.AdditionalProperties.Value
|
||||
}
|
||||
}
|
||||
s.Description = schema.Description.Value
|
||||
s.Default = schema.Default.Value
|
||||
if !schema.Nullable.IsEmpty() {
|
||||
s.Nullable = &schema.Nullable.Value
|
||||
}
|
||||
if !schema.ReadOnly.IsEmpty() {
|
||||
s.ReadOnly = schema.ReadOnly.Value
|
||||
}
|
||||
if !schema.WriteOnly.IsEmpty() {
|
||||
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))
|
||||
for i := 0; i < len(schema.Examples.Value); i++ {
|
||||
examples[i] = schema.Examples.Value[i].Value
|
||||
}
|
||||
s.Examples = examples
|
||||
}
|
||||
s.Extensions = high.ExtractExtensions(schema.Extensions)
|
||||
if !schema.Discriminator.IsEmpty() {
|
||||
s.Discriminator = NewDiscriminator(schema.Discriminator.Value)
|
||||
}
|
||||
if !schema.XML.IsEmpty() {
|
||||
s.XML = NewXML(schema.XML.Value)
|
||||
}
|
||||
if !schema.ExternalDocs.IsEmpty() {
|
||||
s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value)
|
||||
}
|
||||
var req []string
|
||||
for i := range schema.Required.Value {
|
||||
req = append(req, schema.Required.Value[i].Value)
|
||||
}
|
||||
s.Required = req
|
||||
|
||||
type buildResult struct {
|
||||
idx int
|
||||
s *SchemaProxy
|
||||
}
|
||||
var enum []any
|
||||
if !schema.Anchor.IsEmpty() {
|
||||
s.Anchor = schema.Anchor.Value
|
||||
}
|
||||
|
||||
// for every item, build schema async
|
||||
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
|
||||
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: sch.ValueNode,
|
||||
Value: sch.Value,
|
||||
Reference: sch.GetReference(),
|
||||
}}
|
||||
// TODO: check this behavior.
|
||||
for i := range schema.Enum.Value {
|
||||
enum = append(enum, schema.Enum.Value[i].Value)
|
||||
}
|
||||
s.Enum = enum
|
||||
|
||||
bChan <- buildResult{idx: idx, s: p}
|
||||
}
|
||||
// async work.
|
||||
// any polymorphic properties need to be handled in their own threads
|
||||
// any properties each need to be processed in their own thread.
|
||||
// we go as fast as we can.
|
||||
polyCompletedChan := make(chan bool)
|
||||
propsChan := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
|
||||
// schema async
|
||||
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
|
||||
doneChan chan bool, e chan error,
|
||||
) {
|
||||
bChan := make(chan buildResult)
|
||||
totalSchemas := len(schemas)
|
||||
for i := range schemas {
|
||||
go buildSchema(schemas[i], i, bChan)
|
||||
}
|
||||
j := 0
|
||||
for j < totalSchemas {
|
||||
select {
|
||||
case r := <-bChan:
|
||||
j++
|
||||
(*items)[r.idx] = r.s
|
||||
}
|
||||
}
|
||||
doneChan <- true
|
||||
}
|
||||
type buildResult struct {
|
||||
idx int
|
||||
s *SchemaProxy
|
||||
}
|
||||
|
||||
// props async
|
||||
var plock sync.Mutex
|
||||
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
|
||||
props map[string]*SchemaProxy, sw int,
|
||||
) {
|
||||
plock.Lock()
|
||||
props[k.Value] = &SchemaProxy{
|
||||
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
Value: v.Value,
|
||||
KeyNode: k.KeyNode,
|
||||
ValueNode: v.ValueNode,
|
||||
},
|
||||
}
|
||||
plock.Unlock()
|
||||
// for every item, build schema async
|
||||
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
|
||||
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: sch.ValueNode,
|
||||
Value: sch.Value,
|
||||
Reference: sch.GetReference(),
|
||||
}}
|
||||
|
||||
switch sw {
|
||||
case 0:
|
||||
s.Properties = props
|
||||
case 1:
|
||||
s.DependentSchemas = props
|
||||
case 2:
|
||||
s.PatternProperties = props
|
||||
}
|
||||
c <- true
|
||||
}
|
||||
bChan <- buildResult{idx: idx, s: p}
|
||||
}
|
||||
|
||||
props := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.Properties.Value {
|
||||
go buildProps(k, v, propsChan, props, 0)
|
||||
}
|
||||
// schema async
|
||||
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
|
||||
doneChan chan bool, e chan error,
|
||||
) {
|
||||
bChan := make(chan buildResult)
|
||||
totalSchemas := len(schemas)
|
||||
for i := range schemas {
|
||||
go buildSchema(schemas[i], i, bChan)
|
||||
}
|
||||
j := 0
|
||||
for j < totalSchemas {
|
||||
select {
|
||||
case r := <-bChan:
|
||||
j++
|
||||
(*items)[r.idx] = r.s
|
||||
}
|
||||
}
|
||||
doneChan <- true
|
||||
}
|
||||
|
||||
dependents := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.DependentSchemas.Value {
|
||||
go buildProps(k, v, propsChan, dependents, 1)
|
||||
}
|
||||
patternProps := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.PatternProperties.Value {
|
||||
go buildProps(k, v, propsChan, patternProps, 2)
|
||||
}
|
||||
// props async
|
||||
var plock sync.Mutex
|
||||
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
|
||||
props map[string]*SchemaProxy, sw int,
|
||||
) {
|
||||
plock.Lock()
|
||||
props[k.Value] = &SchemaProxy{
|
||||
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
Value: v.Value,
|
||||
KeyNode: k.KeyNode,
|
||||
ValueNode: v.ValueNode,
|
||||
},
|
||||
}
|
||||
plock.Unlock()
|
||||
|
||||
var allOf []*SchemaProxy
|
||||
var oneOf []*SchemaProxy
|
||||
var anyOf []*SchemaProxy
|
||||
var not *SchemaProxy
|
||||
var items *DynamicValue[*SchemaProxy, bool]
|
||||
var prefixItems []*SchemaProxy
|
||||
switch sw {
|
||||
case 0:
|
||||
s.Properties = props
|
||||
case 1:
|
||||
s.DependentSchemas = props
|
||||
case 2:
|
||||
s.PatternProperties = props
|
||||
}
|
||||
c <- true
|
||||
}
|
||||
|
||||
children := 0
|
||||
if !schema.AllOf.IsEmpty() {
|
||||
children++
|
||||
allOf = make([]*SchemaProxy, len(schema.AllOf.Value))
|
||||
go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.AnyOf.IsEmpty() {
|
||||
children++
|
||||
anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value))
|
||||
go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.OneOf.IsEmpty() {
|
||||
children++
|
||||
oneOf = make([]*SchemaProxy, len(schema.OneOf.Value))
|
||||
go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.Not.IsEmpty() {
|
||||
not = NewSchemaProxy(&schema.Not)
|
||||
}
|
||||
if !schema.Items.IsEmpty() {
|
||||
if schema.Items.Value.IsA() {
|
||||
items = &DynamicValue[*SchemaProxy, bool]{A: &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Items.ValueNode,
|
||||
Value: schema.Items.Value.A,
|
||||
KeyNode: schema.Items.KeyNode,
|
||||
}}}
|
||||
} else {
|
||||
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
|
||||
}
|
||||
}
|
||||
if !schema.PrefixItems.IsEmpty() {
|
||||
children++
|
||||
prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value))
|
||||
go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan)
|
||||
}
|
||||
props := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.Properties.Value {
|
||||
go buildProps(k, v, propsChan, props, 0)
|
||||
}
|
||||
|
||||
completeChildren := 0
|
||||
completedProps := 0
|
||||
totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value)
|
||||
if totalProps+children > 0 {
|
||||
allDone:
|
||||
for true {
|
||||
select {
|
||||
case <-polyCompletedChan:
|
||||
completeChildren++
|
||||
if totalProps == completedProps && children == completeChildren {
|
||||
break allDone
|
||||
}
|
||||
case <-propsChan:
|
||||
completedProps++
|
||||
if totalProps == completedProps && children == completeChildren {
|
||||
break allDone
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.OneOf = oneOf
|
||||
s.AnyOf = anyOf
|
||||
s.AllOf = allOf
|
||||
s.Items = items
|
||||
s.PrefixItems = prefixItems
|
||||
s.Not = not
|
||||
return s
|
||||
dependents := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.DependentSchemas.Value {
|
||||
go buildProps(k, v, propsChan, dependents, 1)
|
||||
}
|
||||
patternProps := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.PatternProperties.Value {
|
||||
go buildProps(k, v, propsChan, patternProps, 2)
|
||||
}
|
||||
|
||||
var allOf []*SchemaProxy
|
||||
var oneOf []*SchemaProxy
|
||||
var anyOf []*SchemaProxy
|
||||
var not *SchemaProxy
|
||||
var items *DynamicValue[*SchemaProxy, bool]
|
||||
var prefixItems []*SchemaProxy
|
||||
|
||||
children := 0
|
||||
if !schema.AllOf.IsEmpty() {
|
||||
children++
|
||||
allOf = make([]*SchemaProxy, len(schema.AllOf.Value))
|
||||
go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.AnyOf.IsEmpty() {
|
||||
children++
|
||||
anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value))
|
||||
go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.OneOf.IsEmpty() {
|
||||
children++
|
||||
oneOf = make([]*SchemaProxy, len(schema.OneOf.Value))
|
||||
go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.Not.IsEmpty() {
|
||||
not = NewSchemaProxy(&schema.Not)
|
||||
}
|
||||
if !schema.Items.IsEmpty() {
|
||||
if schema.Items.Value.IsA() {
|
||||
items = &DynamicValue[*SchemaProxy, bool]{A: &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||
ValueNode: schema.Items.ValueNode,
|
||||
Value: schema.Items.Value.A,
|
||||
KeyNode: schema.Items.KeyNode,
|
||||
}}}
|
||||
} else {
|
||||
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
|
||||
}
|
||||
}
|
||||
if !schema.PrefixItems.IsEmpty() {
|
||||
children++
|
||||
prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value))
|
||||
go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan)
|
||||
}
|
||||
|
||||
completeChildren := 0
|
||||
completedProps := 0
|
||||
totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value)
|
||||
if totalProps+children > 0 {
|
||||
allDone:
|
||||
for true {
|
||||
select {
|
||||
case <-polyCompletedChan:
|
||||
completeChildren++
|
||||
if totalProps == completedProps && children == completeChildren {
|
||||
break allDone
|
||||
}
|
||||
case <-propsChan:
|
||||
completedProps++
|
||||
if totalProps == completedProps && children == completeChildren {
|
||||
break allDone
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.OneOf = oneOf
|
||||
s.AnyOf = anyOf
|
||||
s.AllOf = allOf
|
||||
s.Items = items
|
||||
s.PrefixItems = prefixItems
|
||||
s.Not = not
|
||||
return s
|
||||
}
|
||||
|
||||
// GoLow will return the low-level instance of Schema that was used to create the high level one.
|
||||
func (s *Schema) GoLow() *base.Schema {
|
||||
return s.low
|
||||
return s.low
|
||||
}
|
||||
|
||||
// GoLowUntyped will return the low-level Schema instance that was used to create the high-level one, with no type
|
||||
func (s *Schema) GoLowUntyped() any {
|
||||
return s.low
|
||||
return s.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Schema object as a byte slice.
|
||||
func (s *Schema) Render() ([]byte, error) {
|
||||
return yaml.Marshal(s)
|
||||
return yaml.Marshal(s)
|
||||
}
|
||||
|
||||
// RenderInline will return a YAML representation of the Schema object as a byte slice. All of the
|
||||
@@ -467,18 +486,18 @@ func (s *Schema) Render() ([]byte, error) {
|
||||
//
|
||||
// Make sure you don't have any circular references!
|
||||
func (s *Schema) RenderInline() ([]byte, error) {
|
||||
d, _ := s.MarshalYAMLInline()
|
||||
return yaml.Marshal(d)
|
||||
d, _ := s.MarshalYAMLInline()
|
||||
return yaml.Marshal(d)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
|
||||
func (s *Schema) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
func (s *Schema) MarshalYAMLInline() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
nb.Resolve = true
|
||||
return nb.Render(), nil
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
nb.Resolve = true
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
||||
const ymlComponents = `components:
|
||||
const ymlComponents = `components:
|
||||
schemas:
|
||||
rice:
|
||||
type: string
|
||||
@@ -27,42 +27,41 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
||||
rice:
|
||||
$ref: '#/components/schemas/rice'`
|
||||
|
||||
idx := func() *index.SpecIndex {
|
||||
var idxNode yaml.Node
|
||||
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
||||
assert.NoError(t, err)
|
||||
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
}()
|
||||
idx := func() *index.SpecIndex {
|
||||
var idxNode yaml.Node
|
||||
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
||||
assert.NoError(t, err)
|
||||
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
}()
|
||||
|
||||
const ref = "#/components/schemas/nice"
|
||||
const ymlSchema = `$ref: '` + ref + `'`
|
||||
var node yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(ymlSchema), &node)
|
||||
const ref = "#/components/schemas/nice"
|
||||
const ymlSchema = `$ref: '` + ref + `'`
|
||||
var node yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(ymlSchema), &node)
|
||||
|
||||
lowProxy := new(lowbase.SchemaProxy)
|
||||
err := lowProxy.Build(node.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
lowProxy := new(lowbase.SchemaProxy)
|
||||
err := lowProxy.Build(node.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||
Value: lowProxy,
|
||||
}
|
||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||
Value: lowProxy,
|
||||
}
|
||||
|
||||
sp := NewSchemaProxy(&lowRef)
|
||||
sp := NewSchemaProxy(&lowRef)
|
||||
|
||||
rend, _ := sp.Render()
|
||||
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
|
||||
rend, _ := sp.Render()
|
||||
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
func TestCreateSchemaProxy(t *testing.T) {
|
||||
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
|
||||
assert.Equal(t, "iAmASchema", sp.rendered.Description)
|
||||
assert.False(t, sp.IsReference())
|
||||
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
|
||||
assert.Equal(t, "iAmASchema", sp.rendered.Description)
|
||||
assert.False(t, sp.IsReference())
|
||||
}
|
||||
|
||||
func TestCreateSchemaProxyRef(t *testing.T) {
|
||||
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
|
||||
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
|
||||
assert.True(t, sp.IsReference())
|
||||
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
|
||||
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
|
||||
assert.True(t, sp.IsReference())
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ import (
|
||||
// security schemes declared in it which are all required (that is, there is a logical AND between the schemes).
|
||||
//
|
||||
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
type SecurityRequirement struct {
|
||||
Requirements map[string][]string `json:"-" yaml:"-"`
|
||||
low *base.SecurityRequirement
|
||||
@@ -108,7 +108,7 @@ func (s *SecurityRequirement) MarshalYAML() (interface{}, error) {
|
||||
for t := range keys[k].val {
|
||||
reqs[t] = &req{val: keys[k].val[t], line: 9999 + t}
|
||||
if keys[k].lowVal != nil {
|
||||
for _ = range keys[k].lowVal.Value[t].Value {
|
||||
for range keys[k].lowVal.Value[t].Value {
|
||||
fh := keys[k].val[t]
|
||||
df := keys[k].lowVal.Value[t].Value
|
||||
if fh == df {
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by
|
||||
@@ -16,49 +16,49 @@ import (
|
||||
//
|
||||
// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be
|
||||
// used to add that information. See examples for expected behavior.
|
||||
// v2 - https://swagger.io/specification/v2/#xmlObject
|
||||
// v3 - https://swagger.io/specification/#xml-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#xmlObject
|
||||
// v3 - https://swagger.io/specification/#xml-object
|
||||
type XML struct {
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
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
|
||||
low *low.XML
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||
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
|
||||
low *low.XML
|
||||
}
|
||||
|
||||
// NewXML creates a new high-level XML instance from a low-level one.
|
||||
func NewXML(xml *low.XML) *XML {
|
||||
x := new(XML)
|
||||
x.low = xml
|
||||
x.Name = xml.Name.Value
|
||||
x.Namespace = xml.Namespace.Value
|
||||
x.Prefix = xml.Prefix.Value
|
||||
x.Attribute = xml.Attribute.Value
|
||||
x.Wrapped = xml.Wrapped.Value
|
||||
x.Extensions = high.ExtractExtensions(xml.Extensions)
|
||||
return x
|
||||
x := new(XML)
|
||||
x.low = xml
|
||||
x.Name = xml.Name.Value
|
||||
x.Namespace = xml.Namespace.Value
|
||||
x.Prefix = xml.Prefix.Value
|
||||
x.Attribute = xml.Attribute.Value
|
||||
x.Wrapped = xml.Wrapped.Value
|
||||
x.Extensions = high.ExtractExtensions(xml.Extensions)
|
||||
return x
|
||||
}
|
||||
|
||||
// GoLow returns the low level XML reference used to create the high level one.
|
||||
func (x *XML) GoLow() *low.XML {
|
||||
return x.low
|
||||
return x.low
|
||||
}
|
||||
|
||||
// GoLowUntyped will return the low-level XML instance that was used to create the high-level one, with no type
|
||||
func (x *XML) GoLowUntyped() any {
|
||||
return x.low
|
||||
return x.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the XML object as a byte slice.
|
||||
func (x *XML) Render() ([]byte, error) {
|
||||
return yaml.Marshal(x)
|
||||
return yaml.Marshal(x)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the XML object.
|
||||
func (x *XML) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(x, x.low)
|
||||
return nb.Render(), nil
|
||||
nb := high.NewNodeBuilder(x, x.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -58,8 +58,9 @@ func ExtractExtensions(extensions map[low.KeyReference[string]]low.ValueReferenc
|
||||
// `low` represents the HIGH type of the object that contains the extensions.
|
||||
//
|
||||
// to use:
|
||||
// schema := schemaProxy.Schema() // any high-level object that has extensions
|
||||
// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema)
|
||||
//
|
||||
// schema := schemaProxy.Schema() // any high-level object that has extensions
|
||||
// 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)
|
||||
ext := low.GoLow().GetExtensions()
|
||||
@@ -75,4 +76,3 @@ func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,127 +4,127 @@
|
||||
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/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestExtractExtensions(t *testing.T) {
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
Value: "pb33f",
|
||||
}] = low.ValueReference[any]{
|
||||
Value: "new cowboy in town",
|
||||
}
|
||||
ext := ExtractExtensions(n)
|
||||
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
Value: "pb33f",
|
||||
}] = low.ValueReference[any]{
|
||||
Value: "new cowboy in town",
|
||||
}
|
||||
ext := ExtractExtensions(n)
|
||||
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
||||
}
|
||||
|
||||
type textExtension struct {
|
||||
Cowboy string
|
||||
Power int
|
||||
Cowboy string
|
||||
Power int
|
||||
}
|
||||
|
||||
type parent struct {
|
||||
low *child
|
||||
low *child
|
||||
}
|
||||
|
||||
func (p *parent) GoLow() *child {
|
||||
return p.low
|
||||
return p.low
|
||||
}
|
||||
|
||||
type child struct {
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
return c.Extensions
|
||||
return c.Extensions
|
||||
}
|
||||
|
||||
func TestUnpackExtensions(t *testing.T) {
|
||||
|
||||
var resultA, resultB yaml.Node
|
||||
var resultA, resultB yaml.Node
|
||||
|
||||
ymlA := `
|
||||
ymlA := `
|
||||
cowboy: buckaroo
|
||||
power: 100`
|
||||
|
||||
ymlB := `
|
||||
ymlB := `
|
||||
cowboy: frogman
|
||||
power: 2`
|
||||
|
||||
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||
assert.NoError(t, err)
|
||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||
assert.NoError(t, err)
|
||||
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||
assert.NoError(t, err)
|
||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-a",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultA.Content[0],
|
||||
}
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-a",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultA.Content[0],
|
||||
}
|
||||
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-b",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultB.Content[0],
|
||||
}
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-b",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultB.Content[0],
|
||||
}
|
||||
|
||||
c := new(child)
|
||||
c.Extensions = n
|
||||
c := new(child)
|
||||
c.Extensions = n
|
||||
|
||||
p := new(parent)
|
||||
p.low = c
|
||||
p := new(parent)
|
||||
p.low = c
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
func TestUnpackExtensions_Fail(t *testing.T) {
|
||||
|
||||
var resultA, resultB yaml.Node
|
||||
var resultA, resultB yaml.Node
|
||||
|
||||
ymlA := `
|
||||
ymlA := `
|
||||
cowboy: buckaroo
|
||||
power: 100`
|
||||
|
||||
// this is incorrect types, unpacking will fail.
|
||||
ymlB := `
|
||||
// this is incorrect types, unpacking will fail.
|
||||
ymlB := `
|
||||
cowboy: 0
|
||||
power: hello`
|
||||
|
||||
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||
assert.NoError(t, err)
|
||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||
assert.NoError(t, err)
|
||||
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||
assert.NoError(t, err)
|
||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||
assert.NoError(t, err)
|
||||
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-a",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultA.Content[0],
|
||||
}
|
||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-a",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultA.Content[0],
|
||||
}
|
||||
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-b",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultB.Content[0],
|
||||
}
|
||||
n[low.KeyReference[string]{
|
||||
Value: "x-rancher-b",
|
||||
}] = low.ValueReference[any]{
|
||||
ValueNode: resultB.Content[0],
|
||||
}
|
||||
|
||||
c := new(child)
|
||||
c.Extensions = n
|
||||
c := new(child)
|
||||
c.Extensions = n
|
||||
|
||||
p := new(parent)
|
||||
p.low = c
|
||||
p := new(parent)
|
||||
p.low = c
|
||||
|
||||
res, er := UnpackExtensions[textExtension, *child](p)
|
||||
assert.Error(t, er)
|
||||
assert.Empty(t, res)
|
||||
res, er := UnpackExtensions[textExtension, *child](p)
|
||||
assert.Error(t, er)
|
||||
assert.Empty(t, res)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
//
|
||||
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
|
||||
// arrays or models.
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
type Definitions struct {
|
||||
Definitions map[string]*highbase.SchemaProxy
|
||||
low *low.Definitions
|
||||
|
||||
@@ -7,7 +7,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
|
||||
// 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
|
||||
// - https://swagger.io/specification/v2/#exampleObject
|
||||
type Example struct {
|
||||
Values map[string]any
|
||||
low *low.Examples
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
// Header Represents a high-level Swagger / OpenAPI 2 Header object, backed by a low-level one.
|
||||
// A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties.
|
||||
// - https://swagger.io/specification/v2/#headerObject
|
||||
// - https://swagger.io/specification/v2/#headerObject
|
||||
type Header struct {
|
||||
Type string
|
||||
Format string
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// Items is a high-level representation of a Swagger / OpenAPI 2 Items object, backed by a low level one.
|
||||
// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not
|
||||
// located in "body"
|
||||
// - https://swagger.io/specification/v2/#itemsObject
|
||||
// - https://swagger.io/specification/v2/#itemsObject
|
||||
type Items struct {
|
||||
Type string
|
||||
Format string
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// Operation represents a high-level Swagger / OpenAPI 2 Operation object, backed by a low-level one.
|
||||
// It describes a single API operation on a path.
|
||||
// - https://swagger.io/specification/v2/#operationObject
|
||||
// - https://swagger.io/specification/v2/#operationObject
|
||||
type Operation struct {
|
||||
Tags []string
|
||||
Summary string
|
||||
|
||||
@@ -16,29 +16,39 @@ import (
|
||||
// There are five possible parameter types.
|
||||
//
|
||||
// Path
|
||||
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
|
||||
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
|
||||
//
|
||||
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
|
||||
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
|
||||
//
|
||||
// Query
|
||||
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
|
||||
//
|
||||
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
|
||||
//
|
||||
// Header
|
||||
// Custom headers that are expected as part of the request.
|
||||
//
|
||||
// Custom headers that are expected as part of the request.
|
||||
//
|
||||
// Body
|
||||
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
|
||||
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
|
||||
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
|
||||
//
|
||||
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
|
||||
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
|
||||
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
|
||||
//
|
||||
// Form
|
||||
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
|
||||
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
|
||||
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
||||
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
||||
// parameters have a different format based on the content-type used (for further details,
|
||||
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
||||
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
||||
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
||||
// being transferred.
|
||||
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
||||
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
|
||||
// submit-name. This type of form parameters is more commonly used for file transfers
|
||||
//
|
||||
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
|
||||
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
|
||||
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
||||
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
||||
// parameters have a different format based on the content-type used (for further details,
|
||||
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
||||
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
||||
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
||||
// being transferred.
|
||||
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
||||
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
|
||||
// submit-name. This type of form parameters is more commonly used for file transfers
|
||||
//
|
||||
// https://swagger.io/specification/v2/#parameterObject
|
||||
type Parameter struct {
|
||||
Name string
|
||||
|
||||
@@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
//
|
||||
// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be
|
||||
// referenced to the ones defined here. It does not define global operation parameters
|
||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||
type ParameterDefinitions struct {
|
||||
Definitions map[string]*Parameter
|
||||
low *low.ParameterDefinitions
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
)
|
||||
|
||||
// PathItem represents a high-level Swagger / OpenAPI 2 PathItem object backed by a low-level one.
|
||||
@@ -13,128 +13,128 @@ import (
|
||||
// Describes the operations available on a single path. A Path Item may be empty, due to ACL constraints.
|
||||
// The path itself is still exposed to the tooling, but will not know which operations and parameters
|
||||
// are available.
|
||||
// - https://swagger.io/specification/v2/#pathItemObject
|
||||
// - https://swagger.io/specification/v2/#pathItemObject
|
||||
type PathItem struct {
|
||||
Ref string
|
||||
Get *Operation
|
||||
Put *Operation
|
||||
Post *Operation
|
||||
Delete *Operation
|
||||
Options *Operation
|
||||
Head *Operation
|
||||
Patch *Operation
|
||||
Parameters []*Parameter
|
||||
Extensions map[string]any
|
||||
low *low.PathItem
|
||||
Ref string
|
||||
Get *Operation
|
||||
Put *Operation
|
||||
Post *Operation
|
||||
Delete *Operation
|
||||
Options *Operation
|
||||
Head *Operation
|
||||
Patch *Operation
|
||||
Parameters []*Parameter
|
||||
Extensions map[string]any
|
||||
low *low.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 {
|
||||
p := new(PathItem)
|
||||
p.low = pathItem
|
||||
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
|
||||
if !pathItem.Parameters.IsEmpty() {
|
||||
var params []*Parameter
|
||||
for k := range pathItem.Parameters.Value {
|
||||
params = append(params, NewParameter(pathItem.Parameters.Value[k].Value))
|
||||
}
|
||||
p.Parameters = params
|
||||
}
|
||||
var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) {
|
||||
resChan <- asyncResult[*Operation]{
|
||||
key: method,
|
||||
result: NewOperation(op),
|
||||
}
|
||||
}
|
||||
totalOperations := 0
|
||||
resChan := make(chan asyncResult[*Operation])
|
||||
if !pathItem.Get.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.GetLabel, pathItem.Get.Value, resChan)
|
||||
}
|
||||
if !pathItem.Put.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.PutLabel, pathItem.Put.Value, resChan)
|
||||
}
|
||||
if !pathItem.Post.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.PostLabel, pathItem.Post.Value, resChan)
|
||||
}
|
||||
if !pathItem.Patch.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan)
|
||||
}
|
||||
if !pathItem.Delete.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan)
|
||||
}
|
||||
if !pathItem.Head.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan)
|
||||
}
|
||||
if !pathItem.Options.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan)
|
||||
}
|
||||
completedOperations := 0
|
||||
for completedOperations < totalOperations {
|
||||
select {
|
||||
case r := <-resChan:
|
||||
switch r.key {
|
||||
case low.GetLabel:
|
||||
completedOperations++
|
||||
p.Get = r.result
|
||||
case low.PutLabel:
|
||||
completedOperations++
|
||||
p.Put = r.result
|
||||
case low.PostLabel:
|
||||
completedOperations++
|
||||
p.Post = r.result
|
||||
case low.PatchLabel:
|
||||
completedOperations++
|
||||
p.Patch = r.result
|
||||
case low.DeleteLabel:
|
||||
completedOperations++
|
||||
p.Delete = r.result
|
||||
case low.HeadLabel:
|
||||
completedOperations++
|
||||
p.Head = r.result
|
||||
case low.OptionsLabel:
|
||||
completedOperations++
|
||||
p.Options = r.result
|
||||
}
|
||||
}
|
||||
}
|
||||
return p
|
||||
p := new(PathItem)
|
||||
p.low = pathItem
|
||||
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
|
||||
if !pathItem.Parameters.IsEmpty() {
|
||||
var params []*Parameter
|
||||
for k := range pathItem.Parameters.Value {
|
||||
params = append(params, NewParameter(pathItem.Parameters.Value[k].Value))
|
||||
}
|
||||
p.Parameters = params
|
||||
}
|
||||
var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) {
|
||||
resChan <- asyncResult[*Operation]{
|
||||
key: method,
|
||||
result: NewOperation(op),
|
||||
}
|
||||
}
|
||||
totalOperations := 0
|
||||
resChan := make(chan asyncResult[*Operation])
|
||||
if !pathItem.Get.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.GetLabel, pathItem.Get.Value, resChan)
|
||||
}
|
||||
if !pathItem.Put.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.PutLabel, pathItem.Put.Value, resChan)
|
||||
}
|
||||
if !pathItem.Post.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.PostLabel, pathItem.Post.Value, resChan)
|
||||
}
|
||||
if !pathItem.Patch.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan)
|
||||
}
|
||||
if !pathItem.Delete.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan)
|
||||
}
|
||||
if !pathItem.Head.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan)
|
||||
}
|
||||
if !pathItem.Options.IsEmpty() {
|
||||
totalOperations++
|
||||
go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan)
|
||||
}
|
||||
completedOperations := 0
|
||||
for completedOperations < totalOperations {
|
||||
select {
|
||||
case r := <-resChan:
|
||||
switch r.key {
|
||||
case low.GetLabel:
|
||||
completedOperations++
|
||||
p.Get = r.result
|
||||
case low.PutLabel:
|
||||
completedOperations++
|
||||
p.Put = r.result
|
||||
case low.PostLabel:
|
||||
completedOperations++
|
||||
p.Post = r.result
|
||||
case low.PatchLabel:
|
||||
completedOperations++
|
||||
p.Patch = r.result
|
||||
case low.DeleteLabel:
|
||||
completedOperations++
|
||||
p.Delete = r.result
|
||||
case low.HeadLabel:
|
||||
completedOperations++
|
||||
p.Head = r.result
|
||||
case low.OptionsLabel:
|
||||
completedOperations++
|
||||
p.Options = r.result
|
||||
}
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// GoLow returns the low-level PathItem used to create the high-level one.
|
||||
func (p *PathItem) GoLow() *low.PathItem {
|
||||
return p.low
|
||||
return p.low
|
||||
}
|
||||
|
||||
func (p *PathItem) GetOperations() map[string]*Operation {
|
||||
o := make(map[string]*Operation)
|
||||
if p.Get != nil {
|
||||
o[low.GetLabel] = p.Get
|
||||
}
|
||||
if p.Put != nil {
|
||||
o[low.PutLabel] = p.Put
|
||||
}
|
||||
if p.Post != nil {
|
||||
o[low.PostLabel] = p.Post
|
||||
}
|
||||
if p.Delete != nil {
|
||||
o[low.DeleteLabel] = p.Delete
|
||||
}
|
||||
if p.Options != nil {
|
||||
o[low.OptionsLabel] = p.Options
|
||||
}
|
||||
if p.Head != nil {
|
||||
o[low.HeadLabel] = p.Head
|
||||
}
|
||||
if p.Patch != nil {
|
||||
o[low.PatchLabel] = p.Patch
|
||||
}
|
||||
return o
|
||||
o := make(map[string]*Operation)
|
||||
if p.Get != nil {
|
||||
o[low.GetLabel] = p.Get
|
||||
}
|
||||
if p.Put != nil {
|
||||
o[low.PutLabel] = p.Put
|
||||
}
|
||||
if p.Post != nil {
|
||||
o[low.PostLabel] = p.Post
|
||||
}
|
||||
if p.Delete != nil {
|
||||
o[low.DeleteLabel] = p.Delete
|
||||
}
|
||||
if p.Options != nil {
|
||||
o[low.OptionsLabel] = p.Options
|
||||
}
|
||||
if p.Head != nil {
|
||||
o[low.HeadLabel] = p.Head
|
||||
}
|
||||
if p.Patch != nil {
|
||||
o[low.PatchLabel] = p.Patch
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPathItem_GetOperations(t *testing.T) {
|
||||
|
||||
yml := `get:
|
||||
yml := `get:
|
||||
description: get
|
||||
put:
|
||||
description: put
|
||||
@@ -30,15 +30,15 @@ options:
|
||||
description: options
|
||||
`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n v2.PathItem
|
||||
_ = low.BuildModel(&idxNode, &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n v2.PathItem
|
||||
_ = low.BuildModel(&idxNode, &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
r := NewPathItem(&n)
|
||||
r := NewPathItem(&n)
|
||||
|
||||
assert.Len(t, r.GetOperations(), 7)
|
||||
assert.Len(t, r.GetOperations(), 7)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
|
||||
// Response describes a single response from an API Operation
|
||||
// - https://swagger.io/specification/v2/#responseObject
|
||||
// - https://swagger.io/specification/v2/#responseObject
|
||||
type Response struct {
|
||||
Description string
|
||||
Schema *base.SchemaProxy
|
||||
|
||||
@@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
//
|
||||
// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be
|
||||
// referenced to the ones defined here. It does not define global operation responses
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
type ResponsesDefinitions struct {
|
||||
Definitions map[string]*Response
|
||||
low *low.ResponsesDefinitions
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
// Scopes is a high-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object, that is backed by a low-level one.
|
||||
//
|
||||
// Scopes lists the available scopes for an OAuth2 security scheme.
|
||||
// - https://swagger.io/specification/v2/#scopesObject
|
||||
// - https://swagger.io/specification/v2/#scopesObject
|
||||
type Scopes struct {
|
||||
Values map[string]string
|
||||
low *low.Scopes
|
||||
|
||||
@@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||
//
|
||||
// A declaration of the security schemes available to be used in the specification. This does not enforce the security
|
||||
// schemes on the operations and only serves to provide the relevant details for each scheme
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
type SecurityDefinitions struct {
|
||||
Definitions map[string]*SecurityScheme
|
||||
low *low.SecurityDefinitions
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are
|
||||
// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows
|
||||
// (implicit, password, application and access code)
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
type SecurityScheme struct {
|
||||
Type string
|
||||
Description string
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected
|
||||
// responses. The key value used to identify the path item object is an expression, evaluated at runtime,
|
||||
// that identifies a URL to use for the callback operation.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||
type Callback struct {
|
||||
Expression map[string]*PathItem `json:"-" yaml:"-"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
|
||||
@@ -4,49 +4,49 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCallback_MarshalYAML(t *testing.T) {
|
||||
|
||||
cb := &Callback{
|
||||
Expression: map[string]*PathItem{
|
||||
"https://pb33f.io": {
|
||||
Get: &Operation{
|
||||
OperationId: "oneTwoThree",
|
||||
},
|
||||
},
|
||||
"https://pb33f.io/libopenapi": {
|
||||
Get: &Operation{
|
||||
OperationId: "openaypeeeye",
|
||||
},
|
||||
},
|
||||
},
|
||||
Extensions: map[string]any{
|
||||
"x-burgers": "why not?",
|
||||
},
|
||||
}
|
||||
cb := &Callback{
|
||||
Expression: map[string]*PathItem{
|
||||
"https://pb33f.io": {
|
||||
Get: &Operation{
|
||||
OperationId: "oneTwoThree",
|
||||
},
|
||||
},
|
||||
"https://pb33f.io/libopenapi": {
|
||||
Get: &Operation{
|
||||
OperationId: "openaypeeeye",
|
||||
},
|
||||
},
|
||||
},
|
||||
Extensions: map[string]any{
|
||||
"x-burgers": "why not?",
|
||||
},
|
||||
}
|
||||
|
||||
rend, _ := cb.Render()
|
||||
rend, _ := cb.Render()
|
||||
|
||||
// there is no way to determine order in brand new maps, so we have to check length.
|
||||
assert.Len(t, rend, 152)
|
||||
// there is no way to determine order in brand new maps, so we have to check length.
|
||||
assert.Len(t, rend, 152)
|
||||
|
||||
// mutate
|
||||
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
|
||||
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
|
||||
// mutate
|
||||
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
|
||||
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
|
||||
|
||||
rend, _ = cb.Render()
|
||||
// there is no way to determine order in brand new maps, so we have to check length.
|
||||
assert.Len(t, rend, 153)
|
||||
rend, _ = cb.Render()
|
||||
// there is no way to determine order in brand new maps, so we have to check length.
|
||||
assert.Len(t, rend, 153)
|
||||
|
||||
k := `x-break-everything: please
|
||||
k := `x-break-everything: please
|
||||
'{$request.query.queryUrl}':
|
||||
post:
|
||||
description: Callback payload
|
||||
@@ -58,19 +58,19 @@ func TestCallback_MarshalYAML(t *testing.T) {
|
||||
schema:
|
||||
type: string`
|
||||
|
||||
var idxNode yaml.Node
|
||||
err := yaml.Unmarshal([]byte(k), &idxNode)
|
||||
assert.NoError(t, err)
|
||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
var idxNode yaml.Node
|
||||
err := yaml.Unmarshal([]byte(k), &idxNode)
|
||||
assert.NoError(t, err)
|
||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
|
||||
var n v3.Callback
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n v3.Callback
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
r := NewCallback(&n)
|
||||
r := NewCallback(&n)
|
||||
|
||||
assert.Equal(t, "please", r.Extensions["x-break-everything"])
|
||||
assert.Equal(t, "please", r.Extensions["x-break-everything"])
|
||||
|
||||
rend, _ = r.Render()
|
||||
assert.Equal(t, k, strings.TrimSpace(string(rend)))
|
||||
rend, _ = r.Render()
|
||||
assert.Equal(t, k, strings.TrimSpace(string(rend)))
|
||||
}
|
||||
|
||||
@@ -28,18 +28,18 @@ const (
|
||||
//
|
||||
// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object
|
||||
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||
type Components struct {
|
||||
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
|
||||
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
|
||||
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
|
||||
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
|
||||
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
|
||||
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
|
||||
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
|
||||
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
|
||||
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
|
||||
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
|
||||
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *low.Components
|
||||
}
|
||||
|
||||
|
||||
@@ -4,53 +4,53 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestComponents_MarshalYAML(t *testing.T) {
|
||||
|
||||
comp := &Components{
|
||||
Responses: map[string]*Response{
|
||||
"200": {
|
||||
Description: "OK",
|
||||
},
|
||||
},
|
||||
Parameters: map[string]*Parameter{
|
||||
"id": {
|
||||
Name: "id",
|
||||
In: "path",
|
||||
},
|
||||
},
|
||||
RequestBodies: map[string]*RequestBody{
|
||||
"body": {
|
||||
Content: map[string]*MediaType{
|
||||
"application/json": {
|
||||
Example: "why?",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
comp := &Components{
|
||||
Responses: map[string]*Response{
|
||||
"200": {
|
||||
Description: "OK",
|
||||
},
|
||||
},
|
||||
Parameters: map[string]*Parameter{
|
||||
"id": {
|
||||
Name: "id",
|
||||
In: "path",
|
||||
},
|
||||
},
|
||||
RequestBodies: map[string]*RequestBody{
|
||||
"body": {
|
||||
Content: map[string]*MediaType{
|
||||
"application/json": {
|
||||
Example: "why?",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
dat, _ := comp.Render()
|
||||
dat, _ := comp.Render()
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &idxNode)
|
||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &idxNode)
|
||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
|
||||
var n v3.Components
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n v3.Components
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
r := NewComponents(&n)
|
||||
r := NewComponents(&n)
|
||||
|
||||
desired := `responses:
|
||||
desired := `responses:
|
||||
"200":
|
||||
description: OK
|
||||
parameters:
|
||||
@@ -63,6 +63,6 @@ requestBodies:
|
||||
application/json:
|
||||
example: why?`
|
||||
|
||||
dat, _ = r.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||
dat, _ = r.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// Encoding represents an OpenAPI 3+ Encoding object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||
type Encoding struct {
|
||||
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
|
||||
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||
|
||||
@@ -4,64 +4,64 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEncoding_MarshalYAML(t *testing.T) {
|
||||
|
||||
explode := true
|
||||
encoding := &Encoding{
|
||||
ContentType: "application/json",
|
||||
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}},
|
||||
Style: "simple",
|
||||
Explode: &explode,
|
||||
}
|
||||
explode := true
|
||||
encoding := &Encoding{
|
||||
ContentType: "application/json",
|
||||
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}},
|
||||
Style: "simple",
|
||||
Explode: &explode,
|
||||
}
|
||||
|
||||
rend, _ := encoding.Render()
|
||||
rend, _ := encoding.Render()
|
||||
|
||||
desired := `contentType: application/json
|
||||
desired := `contentType: application/json
|
||||
headers:
|
||||
x-pizza-time:
|
||||
description: oh yes please
|
||||
style: simple
|
||||
explode: true`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
explode = false
|
||||
encoding.Explode = &explode
|
||||
rend, _ = encoding.Render()
|
||||
explode = false
|
||||
encoding.Explode = &explode
|
||||
rend, _ = encoding.Render()
|
||||
|
||||
desired = `contentType: application/json
|
||||
desired = `contentType: application/json
|
||||
headers:
|
||||
x-pizza-time:
|
||||
description: oh yes please
|
||||
style: simple`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
encoding.Explode = nil
|
||||
rend, _ = encoding.Render()
|
||||
encoding.Explode = nil
|
||||
rend, _ = encoding.Render()
|
||||
|
||||
desired = `contentType: application/json
|
||||
desired = `contentType: application/json
|
||||
headers:
|
||||
x-pizza-time:
|
||||
description: oh yes please
|
||||
style: simple`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
encoding.Explode = &explode
|
||||
rend, _ = encoding.Render()
|
||||
encoding.Explode = &explode
|
||||
rend, _ = encoding.Render()
|
||||
|
||||
desired = `contentType: application/json
|
||||
desired = `contentType: application/json
|
||||
headers:
|
||||
x-pizza-time:
|
||||
description: oh yes please
|
||||
style: simple`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ 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
|
||||
// - 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"`
|
||||
|
||||
@@ -4,30 +4,30 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeader_MarshalYAML(t *testing.T) {
|
||||
|
||||
header := &Header{
|
||||
Description: "A header",
|
||||
Required: true,
|
||||
Deprecated: true,
|
||||
AllowEmptyValue: true,
|
||||
Style: "simple",
|
||||
Explode: true,
|
||||
AllowReserved: true,
|
||||
Example: "example",
|
||||
Examples: map[string]*base.Example{"example": {Value: "example"}},
|
||||
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
||||
}
|
||||
header := &Header{
|
||||
Description: "A header",
|
||||
Required: true,
|
||||
Deprecated: true,
|
||||
AllowEmptyValue: true,
|
||||
Style: "simple",
|
||||
Explode: true,
|
||||
AllowReserved: true,
|
||||
Example: "example",
|
||||
Examples: map[string]*base.Example{"example": {Value: "example"}},
|
||||
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
||||
}
|
||||
|
||||
rend, _ := header.Render()
|
||||
rend, _ := header.Render()
|
||||
|
||||
desired := `description: A header
|
||||
desired := `description: A header
|
||||
required: true
|
||||
deprecated: true
|
||||
allowEmptyValue: true
|
||||
@@ -40,6 +40,6 @@ examples:
|
||||
value: example
|
||||
x-burgers: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
@@ -4,27 +4,27 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLink_MarshalYAML(t *testing.T) {
|
||||
link := Link{
|
||||
OperationRef: "somewhere",
|
||||
OperationId: "somewhereOutThere",
|
||||
Parameters: map[string]string{
|
||||
"over": "theRainbow",
|
||||
},
|
||||
RequestBody: "hello?",
|
||||
Description: "are you there?",
|
||||
Server: &Server{
|
||||
URL: "https://pb33f.io",
|
||||
},
|
||||
}
|
||||
link := Link{
|
||||
OperationRef: "somewhere",
|
||||
OperationId: "somewhereOutThere",
|
||||
Parameters: map[string]string{
|
||||
"over": "theRainbow",
|
||||
},
|
||||
RequestBody: "hello?",
|
||||
Description: "are you there?",
|
||||
Server: &Server{
|
||||
URL: "https://pb33f.io",
|
||||
},
|
||||
}
|
||||
|
||||
dat, _ := link.Render()
|
||||
desired := `operationRef: somewhere
|
||||
dat, _ := link.Render()
|
||||
desired := `operationRef: somewhere
|
||||
operationId: somewhereOutThere
|
||||
parameters:
|
||||
over: theRainbow
|
||||
@@ -33,5 +33,5 @@ description: are you there?
|
||||
server:
|
||||
url: https://pb33f.io`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ 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
|
||||
// - 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"`
|
||||
|
||||
@@ -4,42 +4,42 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOAuthFlow_MarshalYAML(t *testing.T) {
|
||||
|
||||
oflow := &OAuthFlow{
|
||||
AuthorizationUrl: "https://pb33f.io",
|
||||
TokenUrl: "https://pb33f.io/token",
|
||||
RefreshUrl: "https://pb33f.io/refresh",
|
||||
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"},
|
||||
}
|
||||
oflow := &OAuthFlow{
|
||||
AuthorizationUrl: "https://pb33f.io",
|
||||
TokenUrl: "https://pb33f.io/token",
|
||||
RefreshUrl: "https://pb33f.io/refresh",
|
||||
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"},
|
||||
}
|
||||
|
||||
rend, _ := oflow.Render()
|
||||
rend, _ := oflow.Render()
|
||||
|
||||
desired := `authorizationUrl: https://pb33f.io
|
||||
desired := `authorizationUrl: https://pb33f.io
|
||||
tokenUrl: https://pb33f.io/token
|
||||
refreshUrl: https://pb33f.io/refresh
|
||||
scopes:
|
||||
chicken: nuggets
|
||||
beefy: soup`
|
||||
|
||||
// we can't check for equality, as the scopes map will be randomly ordered when created from scratch.
|
||||
assert.Len(t, desired, 149)
|
||||
// we can't check for equality, as the scopes map will be randomly ordered when created from scratch.
|
||||
assert.Len(t, desired, 149)
|
||||
|
||||
// mutate
|
||||
oflow.Scopes = nil
|
||||
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
|
||||
// mutate
|
||||
oflow.Scopes = nil
|
||||
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
|
||||
|
||||
desired = `authorizationUrl: https://pb33f.io
|
||||
desired = `authorizationUrl: https://pb33f.io
|
||||
tokenUrl: https://pb33f.io/token
|
||||
refreshUrl: https://pb33f.io/refresh
|
||||
x-burgers: why not?`
|
||||
|
||||
rend, _ = oflow.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
rend, _ = oflow.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// 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
|
||||
// - 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"`
|
||||
@@ -63,4 +63,3 @@ func (o *OAuthFlows) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(o, o.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ func TestOperation_MarshalYAML(t *testing.T) {
|
||||
},
|
||||
OperationId: "slice",
|
||||
Parameters: []*Parameter{
|
||||
&Parameter{
|
||||
{
|
||||
Name: "mice",
|
||||
},
|
||||
},
|
||||
@@ -103,7 +103,7 @@ func TestOperation_MarshalYAMLInline(t *testing.T) {
|
||||
},
|
||||
OperationId: "slice",
|
||||
Parameters: []*Parameter{
|
||||
&Parameter{
|
||||
{
|
||||
Name: "mice",
|
||||
},
|
||||
},
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery.
|
||||
// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice.
|
||||
// Recommended for most use case is Authorization Code Grant flow with PKCE.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
||||
// - 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"`
|
||||
|
||||
@@ -4,44 +4,44 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSecurityScheme_MarshalYAML(t *testing.T) {
|
||||
|
||||
ss := &SecurityScheme{
|
||||
Type: "apiKey",
|
||||
Description: "this is a description",
|
||||
Name: "superSecret",
|
||||
In: "header",
|
||||
Scheme: "https",
|
||||
}
|
||||
ss := &SecurityScheme{
|
||||
Type: "apiKey",
|
||||
Description: "this is a description",
|
||||
Name: "superSecret",
|
||||
In: "header",
|
||||
Scheme: "https",
|
||||
}
|
||||
|
||||
dat, _ := ss.Render()
|
||||
dat, _ := ss.Render()
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &idxNode)
|
||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &idxNode)
|
||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
|
||||
var n v3.SecurityScheme
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n v3.SecurityScheme
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
r := NewSecurityScheme(&n)
|
||||
r := NewSecurityScheme(&n)
|
||||
|
||||
dat, _ = r.Render()
|
||||
dat, _ = r.Render()
|
||||
|
||||
desired := `type: apiKey
|
||||
desired := `type: apiKey
|
||||
description: this is a description
|
||||
name: superSecret
|
||||
in: header
|
||||
scheme: https`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||
}
|
||||
|
||||
@@ -4,32 +4,32 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServer_MarshalYAML(t *testing.T) {
|
||||
|
||||
server := &Server{
|
||||
URL: "https://pb33f.io",
|
||||
Description: "the b33f",
|
||||
}
|
||||
server := &Server{
|
||||
URL: "https://pb33f.io",
|
||||
Description: "the b33f",
|
||||
}
|
||||
|
||||
desired := `url: https://pb33f.io
|
||||
desired := `url: https://pb33f.io
|
||||
description: the b33f`
|
||||
|
||||
rend, _ := server.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
rend, _ := server.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
|
||||
// mutate
|
||||
server.Variables = map[string]*ServerVariable{
|
||||
"rainbow": {
|
||||
Enum: []string{"one", "two", "three"},
|
||||
},
|
||||
}
|
||||
// mutate
|
||||
server.Variables = map[string]*ServerVariable{
|
||||
"rainbow": {
|
||||
Enum: []string{"one", "two", "three"},
|
||||
},
|
||||
}
|
||||
|
||||
desired = `url: https://pb33f.io
|
||||
desired = `url: https://pb33f.io
|
||||
description: the b33f
|
||||
variables:
|
||||
rainbow:
|
||||
@@ -38,6 +38,6 @@ variables:
|
||||
- two
|
||||
- three`
|
||||
|
||||
rend, _ = server.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
rend, _ = server.Render()
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
}
|
||||
|
||||
@@ -4,40 +4,40 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestServerVariable_MarshalYAML(t *testing.T) {
|
||||
|
||||
svar := &ServerVariable{
|
||||
Enum: []string{"one", "two", "three"},
|
||||
Description: "money day",
|
||||
}
|
||||
svar := &ServerVariable{
|
||||
Enum: []string{"one", "two", "three"},
|
||||
Description: "money day",
|
||||
}
|
||||
|
||||
desired := `enum:
|
||||
desired := `enum:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
description: money day`
|
||||
|
||||
svarRend, _ := svar.Render()
|
||||
svarRend, _ := svar.Render()
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
|
||||
|
||||
// mutate
|
||||
// mutate
|
||||
|
||||
svar.Default = "is moments away"
|
||||
svar.Default = "is moments away"
|
||||
|
||||
desired = `enum:
|
||||
desired = `enum:
|
||||
- one
|
||||
- two
|
||||
- three
|
||||
default: is moments away
|
||||
description: money day`
|
||||
|
||||
svarRend, _ = svar.Render()
|
||||
svarRend, _ = svar.Render()
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
|
||||
}
|
||||
|
||||
@@ -9,4 +9,3 @@
|
||||
// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure
|
||||
// that all the latest features are collected, without damaging backwards compatibility.
|
||||
package base
|
||||
|
||||
|
||||
@@ -12,8 +12,9 @@ import (
|
||||
)
|
||||
|
||||
// Contact represents a low-level representation of the Contact definitions found at
|
||||
// v2 - https://swagger.io/specification/v2/#contactObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#contactObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
|
||||
type Contact struct {
|
||||
Name low.NodeReference[string]
|
||||
URL low.NodeReference[string]
|
||||
@@ -42,4 +43,3 @@ func (c *Contact) Hash() [32]byte {
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,8 @@ import (
|
||||
// which is used to inform the consumer of the document of an alternative schema based on the value associated with it.
|
||||
//
|
||||
// When using the discriminator, inline schemas will not be considered.
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||
//
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||
type Discriminator struct {
|
||||
PropertyName low.NodeReference[string]
|
||||
Mapping low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]]
|
||||
|
||||
@@ -16,7 +16,8 @@ import (
|
||||
)
|
||||
|
||||
// Example represents a low-level Example object as defined by OpenAPI 3+
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
||||
//
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
||||
type Example struct {
|
||||
Summary low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
|
||||
@@ -16,8 +16,9 @@ import (
|
||||
// ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3
|
||||
//
|
||||
// Allows referencing an external resource for extended documentation.
|
||||
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
||||
type ExternalDoc struct {
|
||||
Description low.NodeReference[string]
|
||||
URL low.NodeReference[string]
|
||||
|
||||
@@ -12,8 +12,9 @@ import (
|
||||
)
|
||||
|
||||
// License is a low-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
|
||||
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||
type License struct {
|
||||
Name low.NodeReference[string]
|
||||
URL low.NodeReference[string]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@ import (
|
||||
// security schemes declared in it which are all required (that is, there is a logical AND between the schemes).
|
||||
//
|
||||
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/#security-requirement-object
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/#security-requirement-object
|
||||
type SecurityRequirement struct {
|
||||
Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
|
||||
*low.Reference
|
||||
|
||||
@@ -17,8 +17,8 @@ import (
|
||||
//
|
||||
// Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per
|
||||
// tag defined in the Operation Object instances.
|
||||
// - v2: https://swagger.io/specification/v2/#tagObject
|
||||
// - v3: https://swagger.io/specification/#tag-object
|
||||
// - v2: https://swagger.io/specification/v2/#tagObject
|
||||
// - v3: https://swagger.io/specification/#tag-object
|
||||
type Tag struct {
|
||||
Name low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
|
||||
@@ -16,8 +16,9 @@ import (
|
||||
//
|
||||
// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be
|
||||
// used to add that information. See examples for expected behavior.
|
||||
// v2 - https://swagger.io/specification/v2/#xmlObject
|
||||
// v3 - https://swagger.io/specification/#xml-object
|
||||
//
|
||||
// v2 - https://swagger.io/specification/v2/#xmlObject
|
||||
// v3 - https://swagger.io/specification/#xml-object
|
||||
type XML struct {
|
||||
Name low.NodeReference[string]
|
||||
Namespace low.NodeReference[string]
|
||||
|
||||
@@ -1024,7 +1024,7 @@ one:
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, things, 1)
|
||||
|
||||
for k, _ := range things {
|
||||
for k := range things {
|
||||
assert.Equal(t, "one", k.Value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
package low
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// BuildModel accepts a yaml.Node pointer and a model, which can be any struct. Using reflection, the model is
|
||||
@@ -20,198 +20,198 @@ import (
|
||||
//
|
||||
// BuildModel is non-recursive and will only build out a single layer of the node tree.
|
||||
func BuildModel(node *yaml.Node, model interface{}) error {
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
||||
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
||||
}
|
||||
v := reflect.ValueOf(model).Elem()
|
||||
num := v.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
||||
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
||||
}
|
||||
v := reflect.ValueOf(model).Elem()
|
||||
num := v.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
|
||||
fName := v.Type().Field(i).Name
|
||||
fName := v.Type().Field(i).Name
|
||||
|
||||
if fName == "Extensions" {
|
||||
continue // internal construct
|
||||
}
|
||||
if fName == "Extensions" {
|
||||
continue // internal construct
|
||||
}
|
||||
|
||||
if fName == "PathItems" {
|
||||
continue // internal construct
|
||||
}
|
||||
if fName == "PathItems" {
|
||||
continue // internal construct
|
||||
}
|
||||
|
||||
kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content)
|
||||
if vn == nil {
|
||||
// no point in going on.
|
||||
continue
|
||||
}
|
||||
kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content)
|
||||
if vn == nil {
|
||||
// no point in going on.
|
||||
continue
|
||||
}
|
||||
|
||||
field := v.FieldByName(fName)
|
||||
kind := field.Kind()
|
||||
switch kind {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
||||
err := SetField(&field, vn, kn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unable to parse unsupported type: %v", kind)
|
||||
}
|
||||
field := v.FieldByName(fName)
|
||||
kind := field.Kind()
|
||||
switch kind {
|
||||
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
||||
err := SetField(&field, vn, kn)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unable to parse unsupported type: %v", kind)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetField accepts a field reflection value, a yaml.Node valueNode and a yaml.Node keyNode. Using reflection, the
|
||||
// function will attempt to set the value of the field based on the key and value nodes. This method is only useful
|
||||
// for low-level models, it has no value to high-level ones.
|
||||
func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
|
||||
if valueNode == nil {
|
||||
return nil
|
||||
}
|
||||
if valueNode == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch field.Type() {
|
||||
switch field.Type() {
|
||||
|
||||
case reflect.TypeOf(map[string]NodeReference[any]{}):
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[any])
|
||||
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,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
case reflect.TypeOf(map[string]NodeReference[any]{}):
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[any])
|
||||
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,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(map[string]NodeReference[string]{}):
|
||||
case reflect.TypeOf(map[string]NodeReference[string]{}):
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[string])
|
||||
var currentLabel string
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentLabel = sliceItem.Value
|
||||
continue
|
||||
}
|
||||
items[currentLabel] = NodeReference[string]{
|
||||
Value: fmt.Sprintf("%v", sliceItem.Value),
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
if field.CanSet() {
|
||||
items := make(map[string]NodeReference[string])
|
||||
var currentLabel string
|
||||
for i, sliceItem := range valueNode.Content {
|
||||
if i%2 == 0 {
|
||||
currentLabel = sliceItem.Value
|
||||
continue
|
||||
}
|
||||
items[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[any]{}):
|
||||
|
||||
var decoded interface{}
|
||||
_ = valueNode.Decode(&decoded)
|
||||
if field.CanSet() {
|
||||
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
|
||||
field.Set(reflect.ValueOf(or))
|
||||
}
|
||||
var decoded interface{}
|
||||
_ = valueNode.Decode(&decoded)
|
||||
if field.CanSet() {
|
||||
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
|
||||
field.Set(reflect.ValueOf(or))
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[any]{}):
|
||||
case reflect.TypeOf([]NodeReference[any]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[any]
|
||||
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,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[any]
|
||||
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,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[string]{}):
|
||||
case reflect.TypeOf(NodeReference[string]{}):
|
||||
|
||||
if field.CanSet() {
|
||||
nr := NodeReference[string]{
|
||||
Value: fmt.Sprintf("%v", valueNode.Value),
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
if field.CanSet() {
|
||||
nr := NodeReference[string]{
|
||||
Value: fmt.Sprintf("%v", valueNode.Value),
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
|
||||
case reflect.TypeOf(ValueReference[string]{}):
|
||||
case reflect.TypeOf(ValueReference[string]{}):
|
||||
|
||||
if field.CanSet() {
|
||||
nr := ValueReference[string]{
|
||||
Value: fmt.Sprintf("%v", valueNode.Value),
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
if field.CanSet() {
|
||||
nr := ValueReference[string]{
|
||||
Value: fmt.Sprintf("%v", valueNode.Value),
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[bool]{}):
|
||||
case reflect.TypeOf(NodeReference[bool]{}):
|
||||
|
||||
if utils.IsNodeBoolValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
bv, _ := strconv.ParseBool(valueNode.Value)
|
||||
nr := NodeReference[bool]{
|
||||
Value: bv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeBoolValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
bv, _ := strconv.ParseBool(valueNode.Value)
|
||||
nr := NodeReference[bool]{
|
||||
Value: bv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[int]{}):
|
||||
case reflect.TypeOf(NodeReference[int]{}):
|
||||
|
||||
if utils.IsNodeIntValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.Atoi(valueNode.Value)
|
||||
nr := NodeReference[int]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeIntValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.Atoi(valueNode.Value)
|
||||
nr := NodeReference[int]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[int64]{}):
|
||||
case reflect.TypeOf(NodeReference[int64]{}):
|
||||
|
||||
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
||||
nr := NodeReference[int64]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
||||
nr := NodeReference[int64]{
|
||||
Value: fv,
|
||||
ValueNode: valueNode,
|
||||
KeyNode: keyNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(nr))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[float32]{}):
|
||||
case reflect.TypeOf(NodeReference[float32]{}):
|
||||
|
||||
if utils.IsNodeNumberValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
@@ -225,7 +225,7 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[float64]{}):
|
||||
case reflect.TypeOf(NodeReference[float64]{}):
|
||||
|
||||
if utils.IsNodeNumberValue(valueNode) {
|
||||
if field.CanSet() {
|
||||
@@ -239,249 +239,249 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[string]{}):
|
||||
case reflect.TypeOf([]NodeReference[string]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[string]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
items = append(items, NodeReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[string]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
items = append(items, NodeReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[float32]{}):
|
||||
case reflect.TypeOf([]NodeReference[float32]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[float32]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
||||
items = append(items, NodeReference[float32]{
|
||||
Value: float32(fv),
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[float32]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
||||
items = append(items, NodeReference[float32]{
|
||||
Value: float32(fv),
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[float64]{}):
|
||||
case reflect.TypeOf([]NodeReference[float64]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[float64]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
||||
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[float64]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
||||
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[int]{}):
|
||||
case reflect.TypeOf([]NodeReference[int]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[int]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
iv, _ := strconv.Atoi(sliceItem.Value)
|
||||
items = append(items, NodeReference[int]{
|
||||
Value: iv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[int]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
iv, _ := strconv.Atoi(sliceItem.Value)
|
||||
items = append(items, NodeReference[int]{
|
||||
Value: iv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[int64]{}):
|
||||
case reflect.TypeOf([]NodeReference[int64]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[int64]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||
items = append(items, NodeReference[int64]{
|
||||
Value: iv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[int64]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||
items = append(items, NodeReference[int64]{
|
||||
Value: iv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf([]NodeReference[bool]{}):
|
||||
case reflect.TypeOf([]NodeReference[bool]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[bool]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
bv, _ := strconv.ParseBool(sliceItem.Value)
|
||||
items = append(items, NodeReference[bool]{
|
||||
Value: bv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []NodeReference[bool]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
bv, _ := strconv.ParseBool(sliceItem.Value)
|
||||
items = append(items, NodeReference[bool]{
|
||||
Value: bv,
|
||||
ValueNode: sliceItem,
|
||||
KeyNode: valueNode,
|
||||
})
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
|
||||
// helper for unpacking string maps.
|
||||
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
|
||||
// helper for unpacking string maps.
|
||||
case reflect.TypeOf(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,
|
||||
}
|
||||
}
|
||||
field.Set(reflect.ValueOf(items))
|
||||
}
|
||||
}
|
||||
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]]{}):
|
||||
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[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 := NodeReference[map[KeyReference[string]]ValueReference[string]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(ref))
|
||||
}
|
||||
}
|
||||
case reflect.TypeOf(NodeReference[[]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[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 := NodeReference[map[KeyReference[string]]ValueReference[string]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(ref))
|
||||
}
|
||||
}
|
||||
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []ValueReference[string]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
items = append(items, ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
n := NodeReference[[]ValueReference[string]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(n))
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []ValueReference[string]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
items = append(items, ValueReference[string]{
|
||||
Value: sliceItem.Value,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
n := NodeReference[[]ValueReference[string]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(n))
|
||||
}
|
||||
}
|
||||
|
||||
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
|
||||
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
|
||||
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []ValueReference[any]
|
||||
for _, sliceItem := range valueNode.Content {
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
if field.CanSet() {
|
||||
var items []ValueReference[any]
|
||||
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
|
||||
}
|
||||
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,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
n := NodeReference[[]ValueReference[any]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(n))
|
||||
}
|
||||
}
|
||||
items = append(items, ValueReference[any]{
|
||||
Value: val,
|
||||
ValueNode: sliceItem,
|
||||
})
|
||||
}
|
||||
n := NodeReference[[]ValueReference[any]]{
|
||||
Value: items,
|
||||
KeyNode: keyNode,
|
||||
ValueNode: valueNode,
|
||||
}
|
||||
field.Set(reflect.ValueOf(n))
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// we want to ignore everything else, each model handles its own complex types.
|
||||
break
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
// we want to ignore everything else, each model handles its own complex types.
|
||||
break
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BuildModelAsync is a convenience function for calling BuildModel from a goroutine, requires a sync.WaitGroup
|
||||
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
|
||||
if n != nil {
|
||||
err := BuildModel(n, model)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
}
|
||||
}
|
||||
lwg.Done()
|
||||
if n != nil {
|
||||
err := BuildModel(n, model)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
}
|
||||
}
|
||||
lwg.Done()
|
||||
}
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
package low
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type hotdog struct {
|
||||
Name NodeReference[string]
|
||||
ValueName ValueReference[string]
|
||||
Fat NodeReference[int]
|
||||
Ketchup NodeReference[float32]
|
||||
Mustard NodeReference[float64]
|
||||
Grilled NodeReference[bool]
|
||||
MaxTemp NodeReference[int]
|
||||
MaxTempHigh NodeReference[int64]
|
||||
MaxTempAlt []NodeReference[int]
|
||||
Drinks []NodeReference[string]
|
||||
Sides []NodeReference[float32]
|
||||
BigSides []NodeReference[float64]
|
||||
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[map[KeyReference[string]]ValueReference[string]]
|
||||
Name NodeReference[string]
|
||||
ValueName ValueReference[string]
|
||||
Fat NodeReference[int]
|
||||
Ketchup NodeReference[float32]
|
||||
Mustard NodeReference[float64]
|
||||
Grilled NodeReference[bool]
|
||||
MaxTemp NodeReference[int]
|
||||
MaxTempHigh NodeReference[int64]
|
||||
MaxTempAlt []NodeReference[int]
|
||||
Drinks []NodeReference[string]
|
||||
Sides []NodeReference[float32]
|
||||
BigSides []NodeReference[float64]
|
||||
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[map[KeyReference[string]]ValueReference[string]]
|
||||
}
|
||||
|
||||
func TestBuildModel_Mismatch(t *testing.T) {
|
||||
|
||||
yml := `crisps: are tasty`
|
||||
yml := `crisps: are tasty`
|
||||
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
hd := hotdog{}
|
||||
cErr := BuildModel(&rootNode, &hd)
|
||||
assert.NoError(t, cErr)
|
||||
assert.Empty(t, hd.Name)
|
||||
hd := hotdog{}
|
||||
cErr := BuildModel(&rootNode, &hd)
|
||||
assert.NoError(t, cErr)
|
||||
assert.Empty(t, hd.Name)
|
||||
|
||||
}
|
||||
|
||||
func TestBuildModel(t *testing.T) {
|
||||
|
||||
yml := `name: yummy
|
||||
yml := `name: yummy
|
||||
valueName: yammy
|
||||
beef: true
|
||||
fat: 200
|
||||
@@ -105,347 +105,347 @@ allTheThings:
|
||||
beer: isGood
|
||||
cake: isNice`
|
||||
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
hd := hotdog{}
|
||||
cErr := BuildModel(rootNode.Content[0], &hd)
|
||||
assert.Equal(t, 200, hd.Fat.Value)
|
||||
assert.Equal(t, 4, hd.Fat.ValueNode.Line)
|
||||
assert.Equal(t, true, hd.Grilled.Value)
|
||||
assert.Equal(t, "yummy", hd.Name.Value)
|
||||
assert.Equal(t, "yammy", hd.ValueName.Value)
|
||||
assert.Equal(t, float32(200.45), hd.Ketchup.Value)
|
||||
assert.Len(t, hd.Drinks, 3)
|
||||
assert.Len(t, hd.Sides, 4)
|
||||
assert.Len(t, hd.BigSides, 4)
|
||||
assert.Len(t, hd.Temps, 2)
|
||||
assert.Len(t, hd.HighTemps, 2)
|
||||
assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value)
|
||||
assert.Len(t, hd.MaxTempAlt, 5)
|
||||
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)
|
||||
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, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
|
||||
hd := hotdog{}
|
||||
cErr := BuildModel(rootNode.Content[0], &hd)
|
||||
assert.Equal(t, 200, hd.Fat.Value)
|
||||
assert.Equal(t, 4, hd.Fat.ValueNode.Line)
|
||||
assert.Equal(t, true, hd.Grilled.Value)
|
||||
assert.Equal(t, "yummy", hd.Name.Value)
|
||||
assert.Equal(t, "yammy", hd.ValueName.Value)
|
||||
assert.Equal(t, float32(200.45), hd.Ketchup.Value)
|
||||
assert.Len(t, hd.Drinks, 3)
|
||||
assert.Len(t, hd.Sides, 4)
|
||||
assert.Len(t, hd.BigSides, 4)
|
||||
assert.Len(t, hd.Temps, 2)
|
||||
assert.Len(t, hd.HighTemps, 2)
|
||||
assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value)
|
||||
assert.Len(t, hd.MaxTempAlt, 5)
|
||||
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)
|
||||
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, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
|
||||
|
||||
allTheThings := hd.AllTheThings.Value
|
||||
for i := range allTheThings {
|
||||
if i.Value == "beer" {
|
||||
assert.Equal(t, "isGood", allTheThings[i].Value)
|
||||
}
|
||||
if i.Value == "cake" {
|
||||
assert.Equal(t, "isNice", allTheThings[i].Value)
|
||||
}
|
||||
}
|
||||
assert.NoError(t, cErr)
|
||||
allTheThings := hd.AllTheThings.Value
|
||||
for i := range allTheThings {
|
||||
if i.Value == "beer" {
|
||||
assert.Equal(t, "isGood", allTheThings[i].Value)
|
||||
}
|
||||
if i.Value == "cake" {
|
||||
assert.Equal(t, "isNice", allTheThings[i].Value)
|
||||
}
|
||||
}
|
||||
assert.NoError(t, cErr)
|
||||
}
|
||||
|
||||
func TestBuildModel_UseCopyNotRef(t *testing.T) {
|
||||
|
||||
yml := `cake: -99999`
|
||||
yml := `cake: -99999`
|
||||
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
hd := hotdog{}
|
||||
cErr := BuildModel(&rootNode, hd)
|
||||
assert.Error(t, cErr)
|
||||
assert.Empty(t, hd.Name)
|
||||
hd := hotdog{}
|
||||
cErr := BuildModel(&rootNode, hd)
|
||||
assert.Error(t, cErr)
|
||||
assert.Empty(t, hd.Name)
|
||||
|
||||
}
|
||||
|
||||
func TestBuildModel_UseUnsupportedPrimitive(t *testing.T) {
|
||||
|
||||
type notSupported struct {
|
||||
cake string
|
||||
}
|
||||
ns := notSupported{}
|
||||
yml := `cake: party`
|
||||
type notSupported struct {
|
||||
cake string
|
||||
}
|
||||
ns := notSupported{}
|
||||
yml := `cake: party`
|
||||
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
cErr := BuildModel(rootNode.Content[0], &ns)
|
||||
assert.Error(t, cErr)
|
||||
assert.Empty(t, ns.cake)
|
||||
cErr := BuildModel(rootNode.Content[0], &ns)
|
||||
assert.Error(t, cErr)
|
||||
assert.Empty(t, ns.cake)
|
||||
|
||||
}
|
||||
|
||||
func TestBuildModel_UsingInternalConstructs(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Extensions NodeReference[string]
|
||||
PathItems NodeReference[string]
|
||||
Thing NodeReference[string]
|
||||
}
|
||||
type internal struct {
|
||||
Extensions NodeReference[string]
|
||||
PathItems NodeReference[string]
|
||||
Thing NodeReference[string]
|
||||
}
|
||||
|
||||
yml := `extensions: one
|
||||
yml := `extensions: one
|
||||
pathItems: two
|
||||
thing: yeah`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
// try a null build
|
||||
try := BuildModel(nil, ins)
|
||||
assert.NoError(t, try)
|
||||
// try a null build
|
||||
try := BuildModel(nil, ins)
|
||||
assert.NoError(t, try)
|
||||
|
||||
cErr := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, cErr)
|
||||
assert.Empty(t, ins.PathItems.Value)
|
||||
assert.Empty(t, ins.Extensions.Value)
|
||||
assert.Equal(t, "yeah", ins.Thing.Value)
|
||||
cErr := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, cErr)
|
||||
assert.Empty(t, ins.PathItems.Value)
|
||||
assert.Empty(t, ins.Extensions.Value)
|
||||
assert.Equal(t, "yeah", ins.Thing.Value)
|
||||
}
|
||||
|
||||
func TestSetField_NodeRefAny_Error(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Thing []NodeReference[any]
|
||||
}
|
||||
type internal struct {
|
||||
Thing []NodeReference[any]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- 999
|
||||
- false`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
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)
|
||||
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]]
|
||||
}
|
||||
type internal struct {
|
||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
what: not
|
||||
chip: chop
|
||||
lip: lop`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
}
|
||||
|
||||
func TestSetField_MapHelper(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Thing map[KeyReference[string]]ValueReference[string]
|
||||
}
|
||||
type internal struct {
|
||||
Thing map[KeyReference[string]]ValueReference[string]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
what: not
|
||||
chip: chop
|
||||
lip: lop`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing, 3)
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing, 3)
|
||||
}
|
||||
|
||||
func TestSetField_ArrayHelper(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[string]]
|
||||
}
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[string]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- nice
|
||||
- rice
|
||||
- slice`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
}
|
||||
|
||||
func TestSetField_Enum_Helper(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
}
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- nice
|
||||
- rice
|
||||
- slice`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Len(t, ins.Thing.Value, 3)
|
||||
}
|
||||
|
||||
func TestSetField_Default_Helper(t *testing.T) {
|
||||
|
||||
type cake struct {
|
||||
thing int
|
||||
}
|
||||
type cake struct {
|
||||
thing int
|
||||
}
|
||||
|
||||
// this should be ignored, no custom objects in here my friend.
|
||||
type internal struct {
|
||||
Thing cake
|
||||
}
|
||||
// this should be ignored, no custom objects in here my friend.
|
||||
type internal struct {
|
||||
Thing cake
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
type: cake`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Equal(t, 0, ins.Thing.thing)
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Equal(t, 0, ins.Thing.thing)
|
||||
}
|
||||
|
||||
func TestHandleSlicesOfInts(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
}
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- 5
|
||||
- 1.234`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
|
||||
func TestHandleSlicesOfBools(t *testing.T) {
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
}
|
||||
type internal struct {
|
||||
Thing NodeReference[[]ValueReference[any]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- true
|
||||
- false`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Equal(t, true, ins.Thing.Value[0].Value)
|
||||
assert.Equal(t, false, ins.Thing.Value[1].Value)
|
||||
try := BuildModel(rootNode.Content[0], ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Equal(t, true, ins.Thing.Value[0].Value)
|
||||
assert.Equal(t, false, ins.Thing.Value[1].Value)
|
||||
}
|
||||
|
||||
func TestSetField_Ignore(t *testing.T) {
|
||||
|
||||
type Complex struct {
|
||||
name string
|
||||
}
|
||||
type internal struct {
|
||||
Thing *Complex
|
||||
}
|
||||
type Complex struct {
|
||||
name string
|
||||
}
|
||||
type internal struct {
|
||||
Thing *Complex
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- nice
|
||||
- rice
|
||||
- slice`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
|
||||
try := BuildModel(&rootNode, ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Nil(t, ins.Thing)
|
||||
try := BuildModel(&rootNode, ins)
|
||||
assert.NoError(t, try)
|
||||
assert.Nil(t, ins.Thing)
|
||||
}
|
||||
|
||||
func TestBuildModelAsync(t *testing.T) {
|
||||
|
||||
type internal struct {
|
||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||
}
|
||||
type internal struct {
|
||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
what: not
|
||||
chip: chop
|
||||
lip: lop`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
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, ins.Thing.Value, 3)
|
||||
var wg sync.WaitGroup
|
||||
var errors []error
|
||||
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]
|
||||
}
|
||||
type internal struct {
|
||||
Thing []NodeReference[any]
|
||||
}
|
||||
|
||||
yml := `thing:
|
||||
yml := `thing:
|
||||
- 999
|
||||
- false`
|
||||
|
||||
ins := new(internal)
|
||||
var rootNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
assert.NoError(t, mErr)
|
||||
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)
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ func TestNodeReference_Mutate(t *testing.T) {
|
||||
func TestNodeReference_RefNode(t *testing.T) {
|
||||
nr := new(NodeReference[string])
|
||||
nr.KeyNode = &yaml.Node{
|
||||
Content: []*yaml.Node{&yaml.Node{
|
||||
Content: []*yaml.Node{{
|
||||
Value: "$ref",
|
||||
}},
|
||||
}
|
||||
@@ -749,8 +749,3 @@ func TestKeyReference_GetKeyNode(t *testing.T) {
|
||||
assert.Equal(t, 3, nr.GetKeyNode().Line)
|
||||
assert.Equal(t, "pizza", nr.GetKeyNode().Value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
//
|
||||
// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be
|
||||
// referenced to the ones defined here. It does not define global operation parameters
|
||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||
type ParameterDefinitions struct {
|
||||
Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter]
|
||||
}
|
||||
@@ -26,7 +26,7 @@ type ParameterDefinitions struct {
|
||||
//
|
||||
// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be
|
||||
// referenced to the ones defined here. It does not define global operation responses
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
type ResponsesDefinitions struct {
|
||||
Definitions map[low.KeyReference[string]]low.ValueReference[*Response]
|
||||
}
|
||||
@@ -35,7 +35,7 @@ type ResponsesDefinitions struct {
|
||||
//
|
||||
// A declaration of the security schemes available to be used in the specification. This does not enforce the security
|
||||
// schemes on the operations and only serves to provide the relevant details for each scheme
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
type SecurityDefinitions struct {
|
||||
Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]
|
||||
}
|
||||
@@ -44,7 +44,7 @@ type SecurityDefinitions struct {
|
||||
//
|
||||
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
|
||||
// arrays or models.
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
type Definitions struct {
|
||||
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
|
||||
// Examples represents a low-level Swagger / OpenAPI 2 Example object.
|
||||
// Allows sharing examples for operation responses
|
||||
// - https://swagger.io/specification/v2/#exampleObject
|
||||
// - https://swagger.io/specification/v2/#exampleObject
|
||||
type Examples struct {
|
||||
Values map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// Header Represents a low-level Swagger / OpenAPI 2 Header object.
|
||||
//
|
||||
// A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties.
|
||||
// - https://swagger.io/specification/v2/#headerObject
|
||||
// - https://swagger.io/specification/v2/#headerObject
|
||||
type Header struct {
|
||||
Type low.NodeReference[string]
|
||||
Format low.NodeReference[string]
|
||||
|
||||
@@ -4,90 +4,90 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHeader_Build(t *testing.T) {
|
||||
|
||||
yml := `items:
|
||||
yml := `items:
|
||||
$ref: break`
|
||||
|
||||
var idxNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
assert.NoError(t, mErr)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
var idxNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
assert.NoError(t, mErr)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n Header
|
||||
err := low.BuildModel(idxNode.Content[0], &n)
|
||||
assert.NoError(t, err)
|
||||
var n Header
|
||||
err := low.BuildModel(idxNode.Content[0], &n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = n.Build(idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
err = n.Build(idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
|
||||
func TestHeader_DefaultAsSlice(t *testing.T) {
|
||||
|
||||
yml := `x-ext: thing
|
||||
yml := `x-ext: thing
|
||||
default:
|
||||
- why
|
||||
- so many
|
||||
- variations`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
assert.NotNil(t, n.Default.Value)
|
||||
assert.Len(t, n.Default.Value, 3)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
assert.NotNil(t, n.Default.Value)
|
||||
assert.Len(t, n.Default.Value, 3)
|
||||
assert.Len(t, n.GetExtensions(), 1)
|
||||
}
|
||||
|
||||
func TestHeader_DefaultAsObject(t *testing.T) {
|
||||
|
||||
yml := `default:
|
||||
yml := `default:
|
||||
lets:
|
||||
create:
|
||||
a:
|
||||
thing: ok?`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
assert.NotNil(t, n.Default.Value)
|
||||
assert.NotNil(t, n.Default.Value)
|
||||
}
|
||||
|
||||
func TestHeader_NoDefault(t *testing.T) {
|
||||
|
||||
yml := `minimum: 12`
|
||||
yml := `minimum: 12`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
assert.Equal(t, 12, n.Minimum.Value)
|
||||
assert.Equal(t, 12, n.Minimum.Value)
|
||||
}
|
||||
|
||||
func TestHeader_Hash_n_Grab(t *testing.T) {
|
||||
|
||||
yml := `description: head
|
||||
yml := `description: head
|
||||
type: string
|
||||
format: left
|
||||
collectionFormat: nice
|
||||
@@ -110,15 +110,15 @@ minItems: 1
|
||||
uniqueItems: true
|
||||
multipleOf: 12`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
var n Header
|
||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||
_ = n.Build(idxNode.Content[0], idx)
|
||||
|
||||
yml2 := `description: head
|
||||
yml2 := `description: head
|
||||
items:
|
||||
type: int
|
||||
format: left
|
||||
@@ -142,37 +142,37 @@ x-belly: large
|
||||
pattern: wow
|
||||
`
|
||||
|
||||
var idxNode2 yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml2), &idxNode2)
|
||||
idx2 := index.NewSpecIndex(&idxNode2)
|
||||
var idxNode2 yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml2), &idxNode2)
|
||||
idx2 := index.NewSpecIndex(&idxNode2)
|
||||
|
||||
var n2 Header
|
||||
_ = low.BuildModel(idxNode2.Content[0], &n2)
|
||||
_ = n2.Build(idxNode2.Content[0], idx2)
|
||||
var n2 Header
|
||||
_ = low.BuildModel(idxNode2.Content[0], &n2)
|
||||
_ = n2.Build(idxNode2.Content[0], idx2)
|
||||
|
||||
// hash
|
||||
assert.Equal(t, n.Hash(), n2.Hash())
|
||||
// hash
|
||||
assert.Equal(t, n.Hash(), n2.Hash())
|
||||
|
||||
// and grab
|
||||
assert.Equal(t, "string", n.GetType().Value)
|
||||
assert.Equal(t, "head", n.GetDescription().Value)
|
||||
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)
|
||||
assert.Equal(t, 10, n.GetMaximum().Value)
|
||||
assert.Equal(t, 1, n.GetMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMaximum().Value)
|
||||
assert.Equal(t, 10, n.GetMaxLength().Value)
|
||||
assert.Equal(t, 1, n.GetMinLength().Value)
|
||||
assert.Equal(t, 10, n.GetMaxItems().Value)
|
||||
assert.Equal(t, 1, n.GetMinItems().Value)
|
||||
assert.True(t, n.GetUniqueItems().Value)
|
||||
assert.Equal(t, 12, n.GetMultipleOf().Value)
|
||||
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)
|
||||
// and grab
|
||||
assert.Equal(t, "string", n.GetType().Value)
|
||||
assert.Equal(t, "head", n.GetDescription().Value)
|
||||
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)
|
||||
assert.Equal(t, 10, n.GetMaximum().Value)
|
||||
assert.Equal(t, 1, n.GetMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMinimum().Value)
|
||||
assert.True(t, n.GetExclusiveMaximum().Value)
|
||||
assert.Equal(t, 10, n.GetMaxLength().Value)
|
||||
assert.Equal(t, 1, n.GetMinLength().Value)
|
||||
assert.Equal(t, 10, n.GetMaxItems().Value)
|
||||
assert.Equal(t, 1, n.GetMinItems().Value)
|
||||
assert.True(t, n.GetUniqueItems().Value)
|
||||
assert.Equal(t, 12, n.GetMultipleOf().Value)
|
||||
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)
|
||||
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
//
|
||||
// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not
|
||||
// located in "body". Items, is actually identical to a Header, except it does not have description.
|
||||
// - https://swagger.io/specification/v2/#itemsObject
|
||||
// - https://swagger.io/specification/v2/#itemsObject
|
||||
type Items struct {
|
||||
Type low.NodeReference[string]
|
||||
Format low.NodeReference[string]
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// Operation represents a low-level Swagger / OpenAPI 2 Operation object.
|
||||
//
|
||||
// It describes a single API operation on a path.
|
||||
// - https://swagger.io/specification/v2/#operationObject
|
||||
// - https://swagger.io/specification/v2/#operationObject
|
||||
type Operation struct {
|
||||
Tags low.NodeReference[[]low.ValueReference[string]]
|
||||
Summary low.NodeReference[string]
|
||||
|
||||
@@ -22,29 +22,39 @@ import (
|
||||
// There are five possible parameter types.
|
||||
//
|
||||
// Path
|
||||
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
|
||||
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
|
||||
//
|
||||
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
|
||||
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
|
||||
//
|
||||
// Query
|
||||
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
|
||||
//
|
||||
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
|
||||
//
|
||||
// Header
|
||||
// Custom headers that are expected as part of the request.
|
||||
//
|
||||
// Custom headers that are expected as part of the request.
|
||||
//
|
||||
// Body
|
||||
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
|
||||
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
|
||||
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
|
||||
//
|
||||
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
|
||||
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
|
||||
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
|
||||
//
|
||||
// Form
|
||||
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
|
||||
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
|
||||
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
||||
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
||||
// parameters have a different format based on the content-type used (for further details,
|
||||
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
||||
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
||||
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
||||
// being transferred.
|
||||
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
||||
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
|
||||
// submit-name. This type of form parameters is more commonly used for file transfers
|
||||
//
|
||||
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
|
||||
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
|
||||
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
||||
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
||||
// parameters have a different format based on the content-type used (for further details,
|
||||
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
||||
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
||||
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
||||
// being transferred.
|
||||
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
||||
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
|
||||
// submit-name. This type of form parameters is more commonly used for file transfers
|
||||
//
|
||||
// https://swagger.io/specification/v2/#parameterObject
|
||||
type Parameter struct {
|
||||
Name low.NodeReference[string]
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
// The path itself is still exposed to the tooling, but will not know which operations and parameters
|
||||
// are available.
|
||||
//
|
||||
// - https://swagger.io/specification/v2/#pathItemObject
|
||||
// - https://swagger.io/specification/v2/#pathItemObject
|
||||
type PathItem struct {
|
||||
Ref low.NodeReference[string]
|
||||
Get low.NodeReference[*Operation]
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
|
||||
//
|
||||
// Response describes a single response from an API Operation
|
||||
// - https://swagger.io/specification/v2/#responseObject
|
||||
// - https://swagger.io/specification/v2/#responseObject
|
||||
type Response struct {
|
||||
Description low.NodeReference[string]
|
||||
Schema low.NodeReference[*base.SchemaProxy]
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
// Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object.
|
||||
//
|
||||
// Scopes lists the available scopes for an OAuth2 security scheme.
|
||||
// - https://swagger.io/specification/v2/#scopesObject
|
||||
// - https://swagger.io/specification/v2/#scopesObject
|
||||
type Scopes struct {
|
||||
Values map[low.KeyReference[string]]low.ValueReference[string]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are
|
||||
// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows
|
||||
// (implicit, password, application and access code)
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
type SecurityScheme struct {
|
||||
Type low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/resolver"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/resolver"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// processes a property of a Swagger document asynchronously using bool and error channels for signals.
|
||||
@@ -26,272 +26,272 @@ type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex,
|
||||
// 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 low.ValueReference[string]
|
||||
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
|
||||
Swagger low.ValueReference[string]
|
||||
|
||||
// Info represents a specification Info definition.
|
||||
// Provides metadata about the API. The metadata can be used by the clients if needed.
|
||||
// - https://swagger.io/specification/v2/#infoObject
|
||||
Info low.NodeReference[*base.Info]
|
||||
// Info represents a specification Info definition.
|
||||
// Provides metadata about the API. The metadata can be used by the clients if needed.
|
||||
// - https://swagger.io/specification/v2/#infoObject
|
||||
Info low.NodeReference[*base.Info]
|
||||
|
||||
// Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor
|
||||
// sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used
|
||||
// (including the port). The host does not support path templating.
|
||||
Host low.NodeReference[string]
|
||||
// Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor
|
||||
// sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used
|
||||
// (including the port). The host does not support path templating.
|
||||
Host low.NodeReference[string]
|
||||
|
||||
// BasePath is The base path on which the API is served, which is relative to the host. If it is not included,
|
||||
// the API is served directly under the host. The value MUST start with a leading slash (/).
|
||||
// The basePath does not support path templating.
|
||||
BasePath low.NodeReference[string]
|
||||
// BasePath is The base path on which the API is served, which is relative to the host. If it is not included,
|
||||
// the API is served directly under the host. The value MUST start with a leading slash (/).
|
||||
// The basePath does not support path templating.
|
||||
BasePath low.NodeReference[string]
|
||||
|
||||
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
|
||||
// If the schemes is not included, the default scheme to be used is the one used to access
|
||||
// the Swagger definition itself.
|
||||
Schemes low.NodeReference[[]low.ValueReference[string]]
|
||||
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
|
||||
// If the schemes is not included, the default scheme to be used is the one used to access
|
||||
// the Swagger definition itself.
|
||||
Schemes low.NodeReference[[]low.ValueReference[string]]
|
||||
|
||||
// Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on
|
||||
// specific API calls. Value MUST be as described under Mime Types.
|
||||
Consumes low.NodeReference[[]low.ValueReference[string]]
|
||||
// Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on
|
||||
// specific API calls. Value MUST be as described under Mime Types.
|
||||
Consumes low.NodeReference[[]low.ValueReference[string]]
|
||||
|
||||
// Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on
|
||||
// specific API calls. Value MUST be as described under Mime Types.
|
||||
Produces low.NodeReference[[]low.ValueReference[string]]
|
||||
// Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on
|
||||
// specific API calls. Value MUST be as described under Mime Types.
|
||||
Produces low.NodeReference[[]low.ValueReference[string]]
|
||||
|
||||
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
|
||||
// - https://swagger.io/specification/v2/#pathsObject
|
||||
Paths low.NodeReference[*Paths]
|
||||
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
|
||||
// - https://swagger.io/specification/v2/#pathsObject
|
||||
Paths low.NodeReference[*Paths]
|
||||
|
||||
// Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
Definitions low.NodeReference[*Definitions]
|
||||
// Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances
|
||||
// - https://swagger.io/specification/v2/#definitionsObject
|
||||
Definitions low.NodeReference[*Definitions]
|
||||
|
||||
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
|
||||
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
|
||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
|
||||
|
||||
// Parameters is an object to hold parameters that can be used across operations.
|
||||
// This property does not define global parameters for all operations.
|
||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||
Parameters low.NodeReference[*ParameterDefinitions]
|
||||
// Parameters is an object to hold parameters that can be used across operations.
|
||||
// This property does not define global parameters for all operations.
|
||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||
Parameters low.NodeReference[*ParameterDefinitions]
|
||||
|
||||
// Responses is an object to hold responses that can be used across operations.
|
||||
// This property does not define global responses for all operations.
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
Responses low.NodeReference[*ResponsesDefinitions]
|
||||
// Responses is an object to hold responses that can be used across operations.
|
||||
// This property does not define global responses for all operations.
|
||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||
Responses low.NodeReference[*ResponsesDefinitions]
|
||||
|
||||
// Security is a declaration of which security schemes are applied for the API as a whole. The list of values
|
||||
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
|
||||
// requirements). Individual operations can override this definition.
|
||||
// - https://swagger.io/specification/v2/#securityRequirementObject
|
||||
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
|
||||
// Security is a declaration of which security schemes are applied for the API as a whole. The list of values
|
||||
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
|
||||
// requirements). Individual operations can override this definition.
|
||||
// - https://swagger.io/specification/v2/#securityRequirementObject
|
||||
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
|
||||
|
||||
// Tags are A list of tags used by the specification with additional metadata.
|
||||
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
|
||||
// by the Operation Object must be declared. The tags that are not declared may be organized randomly or based
|
||||
// on the tools' logic. Each tag name in the list MUST be unique.
|
||||
// - https://swagger.io/specification/v2/#tagObject
|
||||
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
|
||||
// Tags are A list of tags used by the specification with additional metadata.
|
||||
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
|
||||
// by the Operation Object must be declared. The tags that are not declared may be organized randomly or based
|
||||
// on the tools' logic. Each tag name in the list MUST be unique.
|
||||
// - https://swagger.io/specification/v2/#tagObject
|
||||
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
|
||||
|
||||
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
|
||||
ExternalDocs low.NodeReference[*base.ExternalDoc]
|
||||
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
|
||||
ExternalDocs low.NodeReference[*base.ExternalDoc]
|
||||
|
||||
// Extensions contains all custom extensions defined for the top-level document.
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
// Extensions contains all custom extensions defined for the top-level document.
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
|
||||
// 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
|
||||
// the original details are required to continue the work.
|
||||
//
|
||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||
Index *index.SpecIndex
|
||||
// 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
|
||||
// the original details are required to continue the work.
|
||||
//
|
||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||
Index *index.SpecIndex
|
||||
|
||||
// SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read.
|
||||
//
|
||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||
SpecInfo *datamodel.SpecInfo
|
||||
// SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read.
|
||||
//
|
||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||
SpecInfo *datamodel.SpecInfo
|
||||
}
|
||||
|
||||
// FindExtension locates an extension from the root of the Swagger document.
|
||||
func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, s.Extensions)
|
||||
return low.FindItemInMap[any](ext, s.Extensions)
|
||||
}
|
||||
|
||||
// GetExtensions returns all Swagger/Top level extensions and satisfies the low.HasExtensions interface.
|
||||
func (s *Swagger) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||
return s.Extensions
|
||||
return s.Extensions
|
||||
}
|
||||
|
||||
// CreateDocumentFromConfig will create a new Swagger document from the provided SpecInfo and DocumentConfiguration.
|
||||
func CreateDocumentFromConfig(info *datamodel.SpecInfo,
|
||||
configuration *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
||||
return createDocument(info, configuration)
|
||||
configuration *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
||||
return createDocument(info, configuration)
|
||||
}
|
||||
|
||||
// CreateDocument will create a new Swagger document from the provided SpecInfo.
|
||||
//
|
||||
// Deprecated: Use CreateDocumentFromConfig instead.
|
||||
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
||||
return createDocument(info, &datamodel.DocumentConfiguration{
|
||||
AllowRemoteReferences: true,
|
||||
AllowFileReferences: true,
|
||||
})
|
||||
return createDocument(info, &datamodel.DocumentConfiguration{
|
||||
AllowRemoteReferences: true,
|
||||
AllowFileReferences: true,
|
||||
})
|
||||
}
|
||||
|
||||
func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
||||
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
|
||||
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
|
||||
|
||||
// build an index
|
||||
idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{
|
||||
BaseURL: config.BaseURL,
|
||||
AllowRemoteLookup: config.AllowRemoteReferences,
|
||||
AllowFileLookup: config.AllowFileReferences,
|
||||
})
|
||||
doc.Index = idx
|
||||
doc.SpecInfo = info
|
||||
// build an index
|
||||
idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{
|
||||
BaseURL: config.BaseURL,
|
||||
AllowRemoteLookup: config.AllowRemoteReferences,
|
||||
AllowFileLookup: config.AllowFileReferences,
|
||||
})
|
||||
doc.Index = idx
|
||||
doc.SpecInfo = info
|
||||
|
||||
var errors []error
|
||||
var errors []error
|
||||
|
||||
// build out swagger scalar variables.
|
||||
_ = low.BuildModel(info.RootNode.Content[0], &doc)
|
||||
// build out swagger scalar variables.
|
||||
_ = low.BuildModel(info.RootNode.Content[0], &doc)
|
||||
|
||||
// extract externalDocs
|
||||
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
// extract externalDocs
|
||||
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
|
||||
if err != nil {
|
||||
errors = append(errors, err)
|
||||
}
|
||||
|
||||
doc.ExternalDocs = extDocs
|
||||
doc.ExternalDocs = extDocs
|
||||
|
||||
// create resolver and check for circular references.
|
||||
resolve := resolver.NewResolver(idx)
|
||||
resolvingErrors := resolve.CheckForCircularReferences()
|
||||
// create resolver and check for circular references.
|
||||
resolve := resolver.NewResolver(idx)
|
||||
resolvingErrors := resolve.CheckForCircularReferences()
|
||||
|
||||
if len(resolvingErrors) > 0 {
|
||||
for r := range resolvingErrors {
|
||||
errors = append(errors, resolvingErrors[r])
|
||||
}
|
||||
}
|
||||
if len(resolvingErrors) > 0 {
|
||||
for r := range resolvingErrors {
|
||||
errors = append(errors, resolvingErrors[r])
|
||||
}
|
||||
}
|
||||
|
||||
extractionFuncs := []documentFunction{
|
||||
extractInfo,
|
||||
extractPaths,
|
||||
extractDefinitions,
|
||||
extractParamDefinitions,
|
||||
extractResponsesDefinitions,
|
||||
extractSecurityDefinitions,
|
||||
extractTags,
|
||||
extractSecurity,
|
||||
}
|
||||
doneChan := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
for i := range extractionFuncs {
|
||||
go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan)
|
||||
}
|
||||
completedExtractions := 0
|
||||
for completedExtractions < len(extractionFuncs) {
|
||||
select {
|
||||
case <-doneChan:
|
||||
completedExtractions++
|
||||
case e := <-errChan:
|
||||
completedExtractions++
|
||||
errors = append(errors, e)
|
||||
}
|
||||
}
|
||||
extractionFuncs := []documentFunction{
|
||||
extractInfo,
|
||||
extractPaths,
|
||||
extractDefinitions,
|
||||
extractParamDefinitions,
|
||||
extractResponsesDefinitions,
|
||||
extractSecurityDefinitions,
|
||||
extractTags,
|
||||
extractSecurity,
|
||||
}
|
||||
doneChan := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
for i := range extractionFuncs {
|
||||
go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan)
|
||||
}
|
||||
completedExtractions := 0
|
||||
for completedExtractions < len(extractionFuncs) {
|
||||
select {
|
||||
case <-doneChan:
|
||||
completedExtractions++
|
||||
case e := <-errChan:
|
||||
completedExtractions++
|
||||
errors = append(errors, e)
|
||||
}
|
||||
}
|
||||
|
||||
return &doc, errors
|
||||
return &doc, errors
|
||||
}
|
||||
|
||||
func (s *Swagger) GetExternalDocs() *low.NodeReference[any] {
|
||||
return &low.NodeReference[any]{
|
||||
KeyNode: s.ExternalDocs.KeyNode,
|
||||
ValueNode: s.ExternalDocs.ValueNode,
|
||||
Value: s.ExternalDocs.Value,
|
||||
}
|
||||
return &low.NodeReference[any]{
|
||||
KeyNode: s.ExternalDocs.KeyNode,
|
||||
ValueNode: s.ExternalDocs.ValueNode,
|
||||
Value: s.ExternalDocs.Value,
|
||||
}
|
||||
}
|
||||
|
||||
func extractInfo(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Info = info
|
||||
c <- true
|
||||
info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Info = info
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Paths = paths
|
||||
c <- true
|
||||
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Paths = paths
|
||||
c <- true
|
||||
}
|
||||
func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Definitions = def
|
||||
c <- true
|
||||
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Definitions = def
|
||||
c <- true
|
||||
}
|
||||
func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Parameters = param
|
||||
c <- true
|
||||
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Parameters = param
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Responses = resp
|
||||
c <- true
|
||||
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Responses = resp
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.SecurityDefinitions = sec
|
||||
c <- true
|
||||
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.SecurityDefinitions = sec
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
|
||||
Value: tags,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
c <- true
|
||||
tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
|
||||
Value: tags,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
c <- true
|
||||
}
|
||||
|
||||
func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
|
||||
Value: sec,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
c <- true
|
||||
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
|
||||
if err != nil {
|
||||
e <- err
|
||||
return
|
||||
}
|
||||
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
|
||||
Value: sec,
|
||||
KeyNode: ln,
|
||||
ValueNode: vn,
|
||||
}
|
||||
c <- true
|
||||
}
|
||||
|
||||
@@ -4,350 +4,350 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var doc *Swagger
|
||||
|
||||
func initTest() {
|
||||
if doc != nil {
|
||||
return
|
||||
}
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
var err []error
|
||||
doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
})
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
panic(err)
|
||||
}
|
||||
if doc != nil {
|
||||
return
|
||||
}
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
var err []error
|
||||
doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
})
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Print(err)
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkCreateDocument(b *testing.B) {
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
for i := 0; i < b.N; i++ {
|
||||
doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
})
|
||||
}
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
for i := 0; i < b.N; i++ {
|
||||
doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDocument(t *testing.T) {
|
||||
initTest()
|
||||
doc := doc
|
||||
assert.Equal(t, "2.0", doc.SpecInfo.Version)
|
||||
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
|
||||
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
|
||||
assert.Equal(t, "/v2", doc.BasePath.Value)
|
||||
assert.Len(t, doc.Parameters.Value.Definitions, 1)
|
||||
assert.Len(t, doc.Tags.Value, 3)
|
||||
assert.Len(t, doc.Schemes.Value, 2)
|
||||
assert.Len(t, doc.Definitions.Value.Schemas, 6)
|
||||
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3)
|
||||
assert.Len(t, doc.Paths.Value.PathItems, 15)
|
||||
assert.Len(t, doc.Responses.Value.Definitions, 2)
|
||||
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
|
||||
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
||||
assert.NotNil(t, doc.GetExternalDocs())
|
||||
assert.Len(t, doc.GetExtensions(), 1)
|
||||
initTest()
|
||||
doc := doc
|
||||
assert.Equal(t, "2.0", doc.SpecInfo.Version)
|
||||
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
|
||||
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
|
||||
assert.Equal(t, "/v2", doc.BasePath.Value)
|
||||
assert.Len(t, doc.Parameters.Value.Definitions, 1)
|
||||
assert.Len(t, doc.Tags.Value, 3)
|
||||
assert.Len(t, doc.Schemes.Value, 2)
|
||||
assert.Len(t, doc.Definitions.Value.Schemas, 6)
|
||||
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3)
|
||||
assert.Len(t, doc.Paths.Value.PathItems, 15)
|
||||
assert.Len(t, doc.Responses.Value.Definitions, 2)
|
||||
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
|
||||
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
||||
assert.NotNil(t, doc.GetExternalDocs())
|
||||
assert.Len(t, doc.GetExtensions(), 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_Info(t *testing.T) {
|
||||
initTest()
|
||||
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
|
||||
assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
|
||||
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
|
||||
initTest()
|
||||
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
|
||||
assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
|
||||
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
|
||||
}
|
||||
|
||||
func TestCreateDocument_Parameters(t *testing.T) {
|
||||
initTest()
|
||||
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
|
||||
assert.NotNil(t, simpleParam)
|
||||
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
|
||||
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
|
||||
initTest()
|
||||
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
|
||||
assert.NotNil(t, simpleParam)
|
||||
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
|
||||
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
|
||||
|
||||
}
|
||||
|
||||
func TestCreateDocument_Tags(t *testing.T) {
|
||||
initTest()
|
||||
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
|
||||
assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value)
|
||||
assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value)
|
||||
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
|
||||
initTest()
|
||||
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
|
||||
assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value)
|
||||
assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value)
|
||||
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
|
||||
}
|
||||
|
||||
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
|
||||
initTest()
|
||||
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
|
||||
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
|
||||
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
|
||||
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
|
||||
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
|
||||
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
|
||||
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
|
||||
initTest()
|
||||
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
|
||||
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
|
||||
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
|
||||
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
|
||||
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
|
||||
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
|
||||
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
|
||||
}
|
||||
|
||||
func TestCreateDocument_Definitions(t *testing.T) {
|
||||
initTest()
|
||||
apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema()
|
||||
assert.NotNil(t, apiResp)
|
||||
assert.Len(t, apiResp.Properties.Value, 3)
|
||||
assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A)
|
||||
initTest()
|
||||
apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema()
|
||||
assert.NotNil(t, apiResp)
|
||||
assert.Len(t, apiResp.Properties.Value, 3)
|
||||
assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A)
|
||||
|
||||
pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema()
|
||||
assert.NotNil(t, pet)
|
||||
assert.Len(t, pet.Required.Value, 2)
|
||||
pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema()
|
||||
assert.NotNil(t, pet)
|
||||
assert.Len(t, pet.Required.Value, 2)
|
||||
|
||||
// perform a deep inline lookup on a schema to ensure chains work
|
||||
assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value)
|
||||
// perform a deep inline lookup on a schema to ensure chains work
|
||||
assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value)
|
||||
|
||||
// check enums
|
||||
assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3)
|
||||
// check enums
|
||||
assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3)
|
||||
}
|
||||
|
||||
func TestCreateDocument_ResponseDefinitions(t *testing.T) {
|
||||
initTest()
|
||||
apiResp := doc.Responses.Value.FindResponse("200")
|
||||
assert.NotNil(t, apiResp)
|
||||
assert.Equal(t, "OK", apiResp.Value.Description.Value)
|
||||
assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value)
|
||||
initTest()
|
||||
apiResp := doc.Responses.Value.FindResponse("200")
|
||||
assert.NotNil(t, apiResp)
|
||||
assert.Equal(t, "OK", apiResp.Value.Description.Value)
|
||||
assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value)
|
||||
|
||||
header := apiResp.Value.FindHeader("noHeader")
|
||||
assert.NotNil(t, header)
|
||||
assert.True(t, header.Value.FindExtension("x-empty").Value.(bool))
|
||||
header := apiResp.Value.FindHeader("noHeader")
|
||||
assert.NotNil(t, header)
|
||||
assert.True(t, header.Value.FindExtension("x-empty").Value.(bool))
|
||||
|
||||
header = apiResp.Value.FindHeader("myHeader")
|
||||
if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "here", k["something"])
|
||||
} else {
|
||||
panic("should not fail.")
|
||||
}
|
||||
if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok {
|
||||
assert.Len(t, k, 2)
|
||||
assert.Equal(t, "two", k[1])
|
||||
} else {
|
||||
panic("should not fail.")
|
||||
}
|
||||
header = apiResp.Value.FindHeader("myHeader")
|
||||
if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "here", k["something"])
|
||||
} else {
|
||||
panic("should not fail.")
|
||||
}
|
||||
if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok {
|
||||
assert.Len(t, k, 2)
|
||||
assert.Equal(t, "two", k[1])
|
||||
} else {
|
||||
panic("should not fail.")
|
||||
}
|
||||
|
||||
header = apiResp.Value.FindHeader("yourHeader")
|
||||
assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value)
|
||||
header = apiResp.Value.FindHeader("yourHeader")
|
||||
assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value)
|
||||
|
||||
assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value)
|
||||
assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value)
|
||||
|
||||
}
|
||||
|
||||
func TestCreateDocument_Paths(t *testing.T) {
|
||||
initTest()
|
||||
uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value
|
||||
assert.NotNil(t, uploadImage)
|
||||
assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow"))
|
||||
assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value)
|
||||
assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value)
|
||||
assert.Equal(t, "successful operation",
|
||||
uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value)
|
||||
initTest()
|
||||
uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value
|
||||
assert.NotNil(t, uploadImage)
|
||||
assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow"))
|
||||
assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value)
|
||||
assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value)
|
||||
assert.Equal(t, "successful operation",
|
||||
uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value)
|
||||
|
||||
}
|
||||
|
||||
func TestCreateDocument_Bad(t *testing.T) {
|
||||
|
||||
yml := `swagger:
|
||||
yml := `swagger:
|
||||
$ref: bork`
|
||||
|
||||
info, err := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
assert.Nil(t, info)
|
||||
assert.Error(t, err)
|
||||
info, err := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
assert.Nil(t, info)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestCreateDocument_ExternalDocsBad(t *testing.T) {
|
||||
|
||||
yml := `externalDocs:
|
||||
yml := `externalDocs:
|
||||
$ref: bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_TagsBad(t *testing.T) {
|
||||
|
||||
yml := `tags:
|
||||
yml := `tags:
|
||||
$ref: bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_PathsBad(t *testing.T) {
|
||||
|
||||
yml := `paths:
|
||||
yml := `paths:
|
||||
"/hey":
|
||||
post:
|
||||
responses:
|
||||
"200":
|
||||
$ref: bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_SecurityBad(t *testing.T) {
|
||||
|
||||
yml := `security:
|
||||
yml := `security:
|
||||
$ref: `
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) {
|
||||
|
||||
yml := `securityDefinitions:
|
||||
yml := `securityDefinitions:
|
||||
$ref: `
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_ResponsesBad(t *testing.T) {
|
||||
|
||||
yml := `responses:
|
||||
yml := `responses:
|
||||
$ref: `
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_ParametersBad(t *testing.T) {
|
||||
|
||||
yml := `parameters:
|
||||
yml := `parameters:
|
||||
$ref: `
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_DefinitionsBad(t *testing.T) {
|
||||
|
||||
yml := `definitions:
|
||||
yml := `definitions:
|
||||
$ref: `
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_InfoBad(t *testing.T) {
|
||||
|
||||
yml := `info:
|
||||
yml := `info:
|
||||
$ref: `
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
wait := true
|
||||
for wait {
|
||||
select {
|
||||
case <-info.JsonParsingChannel:
|
||||
wait = false
|
||||
}
|
||||
}
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCircularReferenceError(t *testing.T) {
|
||||
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
circDoc, err := CreateDocument(info)
|
||||
assert.NotNil(t, circDoc)
|
||||
assert.Len(t, err, 3)
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
circDoc, err := CreateDocument(info)
|
||||
assert.NotNil(t, circDoc)
|
||||
assert.Len(t, err, 3)
|
||||
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected
|
||||
// responses. The key value used to identify the path item object is an expression, evaluated at runtime,
|
||||
// that identifies a URL to use for the callback operation.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||
type Callback struct {
|
||||
Expression low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
@@ -61,9 +61,9 @@ func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
Value: currentCB.Value,
|
||||
KeyNode: currentCB,
|
||||
}] = low.ValueReference[*PathItem]{
|
||||
Value: callback,
|
||||
ValueNode: callbackNode,
|
||||
Reference: rv,
|
||||
Value: callback,
|
||||
ValueNode: callbackNode,
|
||||
Reference: rv,
|
||||
}
|
||||
}
|
||||
if len(callbacks) > 0 {
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
//
|
||||
// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object
|
||||
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||
type Components struct {
|
||||
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
|
||||
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// Encoding represents a low-level OpenAPI 3+ Encoding object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||
type Encoding struct {
|
||||
ContentType low.NodeReference[string]
|
||||
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
// Header represents a low-level OpenAPI 3+ Header object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
||||
type Header struct {
|
||||
Description low.NodeReference[string]
|
||||
Required low.NodeReference[bool]
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
//
|
||||
// For computing links, and providing instructions to execute them, a runtime expression is used for accessing values
|
||||
// in an operation and using them as parameters while invoking the linked operation.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
||||
type Link struct {
|
||||
OperationRef low.NodeReference[string]
|
||||
OperationId low.NodeReference[string]
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// MediaType represents a low-level OpenAPI MediaType object.
|
||||
//
|
||||
// Each Media Type Object provides schema and examples for the media type identified by its key.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
||||
type MediaType struct {
|
||||
Schema low.NodeReference[*base.SchemaProxy]
|
||||
Example low.NodeReference[any]
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
|
||||
type OAuthFlows struct {
|
||||
Implicit low.NodeReference[*OAuthFlow]
|
||||
Password low.NodeReference[*OAuthFlow]
|
||||
@@ -87,7 +87,7 @@ func (o *OAuthFlows) Hash() [32]byte {
|
||||
}
|
||||
|
||||
// OAuthFlow represents a low-level OpenAPI 3+ OAuthFlow object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
||||
type OAuthFlow struct {
|
||||
AuthorizationUrl low.NodeReference[string]
|
||||
TokenUrl low.NodeReference[string]
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
//
|
||||
// An Operation is perhaps the most important object of the entire specification. Everything of value
|
||||
// happens here. The entire being for existence of this library and the specification, is this Operation.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#operation-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#operation-object
|
||||
type Operation struct {
|
||||
Tags low.NodeReference[[]low.ValueReference[string]]
|
||||
Summary low.NodeReference[string]
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
// Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one.
|
||||
//
|
||||
// A unique parameter is defined by a combination of a name and location.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
|
||||
type Parameter struct {
|
||||
Name low.NodeReference[string]
|
||||
In low.NodeReference[string]
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// RequestBody represents a low-level OpenAPI 3+ RequestBody object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
|
||||
type RequestBody struct {
|
||||
Description low.NodeReference[string]
|
||||
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery.
|
||||
// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice.
|
||||
// Recommended for most use case is Authorization Code Grant flow with PKCE.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
||||
type SecurityScheme struct {
|
||||
Type low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// Server represents a low-level OpenAPI 3+ Server object.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
||||
type Server struct {
|
||||
URL low.NodeReference[string]
|
||||
Description low.NodeReference[string]
|
||||
|
||||
@@ -4,31 +4,31 @@
|
||||
package datamodel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
const (
|
||||
// OpenApi3 is used by all OpenAPI 3+ docs
|
||||
OpenApi3 = "openapi"
|
||||
// OpenApi3 is used by all OpenAPI 3+ docs
|
||||
OpenApi3 = "openapi"
|
||||
|
||||
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
|
||||
OpenApi2 = "swagger"
|
||||
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
|
||||
OpenApi2 = "swagger"
|
||||
|
||||
// AsyncApi is used by akk AsyncAPI docs, all versions.
|
||||
AsyncApi = "asyncapi"
|
||||
// AsyncApi is used by akk AsyncAPI docs, all versions.
|
||||
AsyncApi = "asyncapi"
|
||||
)
|
||||
|
||||
func TestSpecInfo_GetJSONParsingChannel(t *testing.T) {
|
||||
|
||||
// dumb, but we need to ensure coverage is as high as we can make it.
|
||||
bchan := make(chan bool)
|
||||
si := &SpecInfo{JsonParsingChannel: bchan}
|
||||
assert.Equal(t, si.GetJSONParsingChannel(), bchan)
|
||||
// dumb, but we need to ensure coverage is as high as we can make it.
|
||||
bchan := make(chan bool)
|
||||
si := &SpecInfo{JsonParsingChannel: bchan}
|
||||
assert.Equal(t, si.GetJSONParsingChannel(), bchan)
|
||||
|
||||
}
|
||||
|
||||
@@ -115,147 +115,147 @@ info:
|
||||
version: '0.1.0'`
|
||||
|
||||
func TestExtractSpecInfo_ValidJSON(t *testing.T) {
|
||||
r, e := ExtractSpecInfo([]byte(goodJSON))
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Error(t, e)
|
||||
r, e := ExtractSpecInfo([]byte(goodJSON))
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_InvalidJSON(t *testing.T) {
|
||||
_, e := ExtractSpecInfo([]byte(badJSON))
|
||||
assert.Error(t, e)
|
||||
_, e := ExtractSpecInfo([]byte(badJSON))
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_Nothing(t *testing.T) {
|
||||
_, e := ExtractSpecInfo([]byte(""))
|
||||
assert.Error(t, e)
|
||||
_, e := ExtractSpecInfo([]byte(""))
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_ValidYAML(t *testing.T) {
|
||||
r, e := ExtractSpecInfo([]byte(goodYAML))
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Error(t, e)
|
||||
r, e := ExtractSpecInfo([]byte(goodYAML))
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_InvalidYAML(t *testing.T) {
|
||||
_, e := ExtractSpecInfo([]byte(badYAML))
|
||||
assert.Error(t, e)
|
||||
_, e := ExtractSpecInfo([]byte(badYAML))
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) {
|
||||
_, e := ExtractSpecInfo([]byte(OpenApiOne))
|
||||
assert.Error(t, e)
|
||||
_, e := ExtractSpecInfo([]byte(OpenApiOne))
|
||||
assert.Error(t, e)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_OpenAPI3(t *testing.T) {
|
||||
|
||||
r, e := ExtractSpecInfo([]byte(OpenApi3Spec))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, utils.OpenApi3, r.SpecType)
|
||||
assert.Equal(t, "3.0.1", r.Version)
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28")
|
||||
r, e := ExtractSpecInfo([]byte(OpenApi3Spec))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, utils.OpenApi3, r.SpecType)
|
||||
assert.Equal(t, "3.0.1", r.Version)
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28")
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_OpenAPIWat(t *testing.T) {
|
||||
|
||||
r, e := ExtractSpecInfo([]byte(OpenApiWat))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, OpenApi3, r.SpecType)
|
||||
assert.Equal(t, "3.2", r.Version)
|
||||
r, e := ExtractSpecInfo([]byte(OpenApiWat))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, OpenApi3, r.SpecType)
|
||||
assert.Equal(t, "3.2", r.Version)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_OpenAPI31(t *testing.T) {
|
||||
|
||||
r, e := ExtractSpecInfo([]byte(OpenApi31))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, OpenApi3, r.SpecType)
|
||||
assert.Equal(t, "3.1", r.Version)
|
||||
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07")
|
||||
r, e := ExtractSpecInfo([]byte(OpenApi31))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, OpenApi3, r.SpecType)
|
||||
assert.Equal(t, "3.1", r.Version)
|
||||
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07")
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) {
|
||||
|
||||
spec, e := ExtractSpecInfo([]byte(OpenApiFalse))
|
||||
assert.NoError(t, e)
|
||||
assert.Equal(t, "false", spec.Version)
|
||||
spec, e := ExtractSpecInfo([]byte(OpenApiFalse))
|
||||
assert.NoError(t, e)
|
||||
assert.Equal(t, "false", spec.Version)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_OpenAPI2(t *testing.T) {
|
||||
|
||||
r, e := ExtractSpecInfo([]byte(OpenApi2Spec))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, OpenApi2, r.SpecType)
|
||||
assert.Equal(t, "2.0.1", r.Version)
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#")
|
||||
r, e := ExtractSpecInfo([]byte(OpenApi2Spec))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, OpenApi2, r.SpecType)
|
||||
assert.Equal(t, "2.0.1", r.Version)
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#")
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) {
|
||||
|
||||
_, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd))
|
||||
assert.NotNil(t, e)
|
||||
assert.Equal(t,
|
||||
"spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error())
|
||||
_, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd))
|
||||
assert.NotNil(t, e)
|
||||
assert.Equal(t,
|
||||
"spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error())
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_AsyncAPI(t *testing.T) {
|
||||
|
||||
r, e := ExtractSpecInfo([]byte(AsyncAPISpec))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, AsyncApi, r.SpecType)
|
||||
assert.Equal(t, "2.0.0", r.Version)
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
r, e := ExtractSpecInfo([]byte(AsyncAPISpec))
|
||||
assert.Nil(t, e)
|
||||
assert.Equal(t, AsyncApi, r.SpecType)
|
||||
assert.Equal(t, "2.0.0", r.Version)
|
||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) {
|
||||
|
||||
_, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd))
|
||||
assert.NotNil(t, e)
|
||||
assert.Equal(t,
|
||||
"spec is defined as asyncapi, but has a major version that is invalid", e.Error())
|
||||
_, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd))
|
||||
assert.NotNil(t, e)
|
||||
assert.Equal(t,
|
||||
"spec is defined as asyncapi, but has a major version that is invalid", e.Error())
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) {
|
||||
|
||||
yml := `openapi:
|
||||
yml := `openapi:
|
||||
should: fail`
|
||||
|
||||
_, err := ExtractSpecInfo([]byte(yml))
|
||||
assert.Error(t, err)
|
||||
_, err := ExtractSpecInfo([]byte(yml))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) {
|
||||
|
||||
yml := `swagger:
|
||||
yml := `swagger:
|
||||
should: fail`
|
||||
|
||||
_, err := ExtractSpecInfo([]byte(yml))
|
||||
assert.Error(t, err)
|
||||
_, err := ExtractSpecInfo([]byte(yml))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) {
|
||||
|
||||
yml := `asyncapi:
|
||||
yml := `asyncapi:
|
||||
should: fail`
|
||||
|
||||
_, err := ExtractSpecInfo([]byte(yml))
|
||||
assert.Error(t, err)
|
||||
_, err := ExtractSpecInfo([]byte(yml))
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
func ExampleExtractSpecInfo() {
|
||||
|
||||
// load bytes from openapi spec file.
|
||||
bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json")
|
||||
// load bytes from openapi spec file.
|
||||
bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json")
|
||||
|
||||
// create a new *SpecInfo instance from loaded bytes
|
||||
specInfo, err := ExtractSpecInfo(bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot extract spec info: %e", err))
|
||||
}
|
||||
// create a new *SpecInfo instance from loaded bytes
|
||||
specInfo, err := ExtractSpecInfo(bytes)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot extract spec info: %e", err))
|
||||
}
|
||||
|
||||
// print out the version, format and filetype
|
||||
fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s",
|
||||
specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType)
|
||||
// print out the version, format and filetype
|
||||
fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s",
|
||||
specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType)
|
||||
|
||||
// Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json
|
||||
// Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,22 +4,22 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSpecIndex_Children(t *testing.T) {
|
||||
idx1 := new(SpecIndex)
|
||||
idx2 := new(SpecIndex)
|
||||
idx3 := new(SpecIndex)
|
||||
idx4 := new(SpecIndex)
|
||||
idx5 := new(SpecIndex)
|
||||
idx1.AddChild(idx2)
|
||||
idx1.AddChild(idx3)
|
||||
idx3.AddChild(idx4)
|
||||
idx4.AddChild(idx5)
|
||||
assert.Equal(t, 2, len(idx1.GetChildren()))
|
||||
assert.Equal(t, 1, len(idx3.GetChildren()))
|
||||
assert.Equal(t, 1, len(idx4.GetChildren()))
|
||||
assert.Equal(t, 0, len(idx5.GetChildren()))
|
||||
idx1 := new(SpecIndex)
|
||||
idx2 := new(SpecIndex)
|
||||
idx3 := new(SpecIndex)
|
||||
idx4 := new(SpecIndex)
|
||||
idx5 := new(SpecIndex)
|
||||
idx1.AddChild(idx2)
|
||||
idx1.AddChild(idx3)
|
||||
idx3.AddChild(idx4)
|
||||
idx4.AddChild(idx5)
|
||||
assert.Equal(t, 2, len(idx1.GetChildren()))
|
||||
assert.Equal(t, 1, len(idx3.GetChildren()))
|
||||
assert.Equal(t, 1, len(idx4.GetChildren()))
|
||||
assert.Equal(t, 0, len(idx5.GetChildren()))
|
||||
}
|
||||
|
||||
@@ -4,88 +4,88 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"gopkg.in/yaml.v3"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func isHttpMethod(val string) bool {
|
||||
switch strings.ToLower(val) {
|
||||
case methodTypes[0]:
|
||||
return true
|
||||
case methodTypes[1]:
|
||||
return true
|
||||
case methodTypes[2]:
|
||||
return true
|
||||
case methodTypes[3]:
|
||||
return true
|
||||
case methodTypes[4]:
|
||||
return true
|
||||
case methodTypes[5]:
|
||||
return true
|
||||
case methodTypes[6]:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
switch strings.ToLower(val) {
|
||||
case methodTypes[0]:
|
||||
return true
|
||||
case methodTypes[1]:
|
||||
return true
|
||||
case methodTypes[2]:
|
||||
return true
|
||||
case methodTypes[3]:
|
||||
return true
|
||||
case methodTypes[4]:
|
||||
return true
|
||||
case methodTypes[5]:
|
||||
return true
|
||||
case methodTypes[6]:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func DetermineReferenceResolveType(ref string) int {
|
||||
if ref != "" && ref[0] == '#' {
|
||||
return LocalResolve
|
||||
}
|
||||
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
|
||||
return HttpResolve
|
||||
}
|
||||
if strings.Contains(ref, ".json") ||
|
||||
strings.Contains(ref, ".yaml") ||
|
||||
strings.Contains(ref, ".yml") {
|
||||
return FileResolve
|
||||
}
|
||||
return -1
|
||||
if ref != "" && ref[0] == '#' {
|
||||
return LocalResolve
|
||||
}
|
||||
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
|
||||
return HttpResolve
|
||||
}
|
||||
if strings.Contains(ref, ".json") ||
|
||||
strings.Contains(ref, ".yaml") ||
|
||||
strings.Contains(ref, ".yml") {
|
||||
return FileResolve
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func boostrapIndexCollections(rootNode *yaml.Node, index *SpecIndex) {
|
||||
index.root = rootNode
|
||||
index.allRefs = make(map[string]*Reference)
|
||||
index.allMappedRefs = make(map[string]*Reference)
|
||||
index.refsByLine = make(map[string]map[int]bool)
|
||||
index.linesWithRefs = make(map[int]bool)
|
||||
index.pathRefs = make(map[string]map[string]*Reference)
|
||||
index.paramOpRefs = make(map[string]map[string]map[string][]*Reference)
|
||||
index.operationTagsRefs = make(map[string]map[string][]*Reference)
|
||||
index.operationDescriptionRefs = make(map[string]map[string]*Reference)
|
||||
index.operationSummaryRefs = make(map[string]map[string]*Reference)
|
||||
index.paramCompRefs = make(map[string]*Reference)
|
||||
index.paramAllRefs = make(map[string]*Reference)
|
||||
index.paramInlineDuplicateNames = make(map[string][]*Reference)
|
||||
index.globalTagRefs = make(map[string]*Reference)
|
||||
index.securitySchemeRefs = make(map[string]*Reference)
|
||||
index.requestBodiesRefs = make(map[string]*Reference)
|
||||
index.responsesRefs = make(map[string]*Reference)
|
||||
index.headersRefs = make(map[string]*Reference)
|
||||
index.examplesRefs = make(map[string]*Reference)
|
||||
index.callbacksRefs = make(map[string]map[string][]*Reference)
|
||||
index.linksRefs = make(map[string]map[string][]*Reference)
|
||||
index.callbackRefs = make(map[string]*Reference)
|
||||
index.externalSpecIndex = make(map[string]*SpecIndex)
|
||||
index.allComponentSchemaDefinitions = make(map[string]*Reference)
|
||||
index.allParameters = make(map[string]*Reference)
|
||||
index.allSecuritySchemes = make(map[string]*Reference)
|
||||
index.allRequestBodies = make(map[string]*Reference)
|
||||
index.allResponses = make(map[string]*Reference)
|
||||
index.allHeaders = make(map[string]*Reference)
|
||||
index.allExamples = make(map[string]*Reference)
|
||||
index.allLinks = make(map[string]*Reference)
|
||||
index.allCallbacks = make(map[string]*Reference)
|
||||
index.allExternalDocuments = make(map[string]*Reference)
|
||||
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
|
||||
index.polymorphicRefs = make(map[string]*Reference)
|
||||
index.refsWithSiblings = make(map[string]Reference)
|
||||
index.seenRemoteSources = make(map[string]*yaml.Node)
|
||||
index.seenLocalSources = make(map[string]*yaml.Node)
|
||||
index.opServersRefs = make(map[string]map[string][]*Reference)
|
||||
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
|
||||
index.componentIndexChan = make(chan bool)
|
||||
index.polyComponentIndexChan = make(chan bool)
|
||||
index.root = rootNode
|
||||
index.allRefs = make(map[string]*Reference)
|
||||
index.allMappedRefs = make(map[string]*Reference)
|
||||
index.refsByLine = make(map[string]map[int]bool)
|
||||
index.linesWithRefs = make(map[int]bool)
|
||||
index.pathRefs = make(map[string]map[string]*Reference)
|
||||
index.paramOpRefs = make(map[string]map[string]map[string][]*Reference)
|
||||
index.operationTagsRefs = make(map[string]map[string][]*Reference)
|
||||
index.operationDescriptionRefs = make(map[string]map[string]*Reference)
|
||||
index.operationSummaryRefs = make(map[string]map[string]*Reference)
|
||||
index.paramCompRefs = make(map[string]*Reference)
|
||||
index.paramAllRefs = make(map[string]*Reference)
|
||||
index.paramInlineDuplicateNames = make(map[string][]*Reference)
|
||||
index.globalTagRefs = make(map[string]*Reference)
|
||||
index.securitySchemeRefs = make(map[string]*Reference)
|
||||
index.requestBodiesRefs = make(map[string]*Reference)
|
||||
index.responsesRefs = make(map[string]*Reference)
|
||||
index.headersRefs = make(map[string]*Reference)
|
||||
index.examplesRefs = make(map[string]*Reference)
|
||||
index.callbacksRefs = make(map[string]map[string][]*Reference)
|
||||
index.linksRefs = make(map[string]map[string][]*Reference)
|
||||
index.callbackRefs = make(map[string]*Reference)
|
||||
index.externalSpecIndex = make(map[string]*SpecIndex)
|
||||
index.allComponentSchemaDefinitions = make(map[string]*Reference)
|
||||
index.allParameters = make(map[string]*Reference)
|
||||
index.allSecuritySchemes = make(map[string]*Reference)
|
||||
index.allRequestBodies = make(map[string]*Reference)
|
||||
index.allResponses = make(map[string]*Reference)
|
||||
index.allHeaders = make(map[string]*Reference)
|
||||
index.allExamples = make(map[string]*Reference)
|
||||
index.allLinks = make(map[string]*Reference)
|
||||
index.allCallbacks = make(map[string]*Reference)
|
||||
index.allExternalDocuments = make(map[string]*Reference)
|
||||
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
|
||||
index.polymorphicRefs = make(map[string]*Reference)
|
||||
index.refsWithSiblings = make(map[string]Reference)
|
||||
index.seenRemoteSources = make(map[string]*yaml.Node)
|
||||
index.seenLocalSources = make(map[string]*yaml.Node)
|
||||
index.opServersRefs = make(map[string]map[string][]*Reference)
|
||||
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
|
||||
index.componentIndexChan = make(chan bool)
|
||||
index.polyComponentIndexChan = make(chan bool)
|
||||
}
|
||||
|
||||
@@ -4,447 +4,445 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, schema := range schemasNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = schema.Value
|
||||
continue
|
||||
}
|
||||
var name string
|
||||
for i, schema := range schemasNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = schema.Value
|
||||
continue
|
||||
}
|
||||
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: schema,
|
||||
Path: fmt.Sprintf("$.components.schemas.%s", name),
|
||||
ParentNode: schemasNode,
|
||||
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}),
|
||||
}
|
||||
index.allComponentSchemaDefinitions[def] = ref
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: schema,
|
||||
Path: fmt.Sprintf("$.components.schemas.%s", name),
|
||||
ParentNode: schemasNode,
|
||||
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}),
|
||||
}
|
||||
index.allComponentSchemaDefinitions[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it
|
||||
func (index *SpecIndex) extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string) map[string][]string {
|
||||
if schemaNode == nil {
|
||||
return reqRefProps
|
||||
}
|
||||
if schemaNode == nil {
|
||||
return reqRefProps
|
||||
}
|
||||
|
||||
// If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties
|
||||
isRef, _, defPath := utils.IsNodeRefValue(schemaNode)
|
||||
if isRef {
|
||||
if _, ok := reqRefProps[defPath]; !ok {
|
||||
reqRefProps[defPath] = []string{}
|
||||
}
|
||||
}
|
||||
// If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties
|
||||
isRef, _, defPath := utils.IsNodeRefValue(schemaNode)
|
||||
if isRef {
|
||||
if _, ok := reqRefProps[defPath]; !ok {
|
||||
reqRefProps[defPath] = []string{}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a required parameters list, and return if none exists, as any properties will be optional
|
||||
_, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content)
|
||||
if requiredSeqNode == nil {
|
||||
return reqRefProps
|
||||
}
|
||||
// Check for a required parameters list, and return if none exists, as any properties will be optional
|
||||
_, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content)
|
||||
if requiredSeqNode == nil {
|
||||
return reqRefProps
|
||||
}
|
||||
|
||||
_, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content)
|
||||
if propertiesMapNode == nil {
|
||||
// TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong
|
||||
return reqRefProps
|
||||
}
|
||||
_, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content)
|
||||
if propertiesMapNode == nil {
|
||||
// TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong
|
||||
return reqRefProps
|
||||
}
|
||||
|
||||
name := ""
|
||||
for i, param := range propertiesMapNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = param.Value
|
||||
continue
|
||||
}
|
||||
name := ""
|
||||
for i, param := range propertiesMapNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = param.Value
|
||||
continue
|
||||
}
|
||||
|
||||
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
|
||||
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
|
||||
if paramPropertiesMapNode != nil {
|
||||
reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps)
|
||||
}
|
||||
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
|
||||
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
|
||||
if paramPropertiesMapNode != nil {
|
||||
reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps)
|
||||
}
|
||||
|
||||
// Check to see if the current property is polymorphic, and dive into that model if so
|
||||
for _, key := range []string{"allOf", "oneOf", "anyOf"} {
|
||||
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
|
||||
if ofNode != nil {
|
||||
for _, ofNodeItem := range ofNode.Content {
|
||||
reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check to see if the current property is polymorphic, and dive into that model if so
|
||||
for _, key := range []string{"allOf", "oneOf", "anyOf"} {
|
||||
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
|
||||
if ofNode != nil {
|
||||
for _, ofNodeItem := range ofNode.Content {
|
||||
reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Run through each of the required properties and extract _their_ required references
|
||||
for _, requiredPropertyNode := range requiredSeqNode.Content {
|
||||
_, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content)
|
||||
if requiredPropDefNode == nil {
|
||||
continue
|
||||
}
|
||||
// Run through each of the required properties and extract _their_ required references
|
||||
for _, requiredPropertyNode := range requiredSeqNode.Content {
|
||||
_, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content)
|
||||
if requiredPropDefNode == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
reqRefProps = index.extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
|
||||
}
|
||||
reqRefProps = index.extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
|
||||
}
|
||||
|
||||
return reqRefProps
|
||||
return reqRefProps
|
||||
}
|
||||
|
||||
// extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node
|
||||
func (index *SpecIndex) extractRequiredReferenceProperties(requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string {
|
||||
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
|
||||
if !isRef {
|
||||
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
|
||||
if defItems != nil {
|
||||
isRef, _, defPath = utils.IsNodeRefValue(defItems)
|
||||
}
|
||||
}
|
||||
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
|
||||
if !isRef {
|
||||
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
|
||||
if defItems != nil {
|
||||
isRef, _, defPath = utils.IsNodeRefValue(defItems)
|
||||
}
|
||||
}
|
||||
|
||||
if /* still */ !isRef {
|
||||
return reqRefProps
|
||||
}
|
||||
if /* still */ !isRef {
|
||||
return reqRefProps
|
||||
}
|
||||
|
||||
if _, ok := reqRefProps[defPath]; !ok {
|
||||
reqRefProps[defPath] = []string{}
|
||||
}
|
||||
reqRefProps[defPath] = append(reqRefProps[defPath], propName)
|
||||
if _, ok := reqRefProps[defPath]; !ok {
|
||||
reqRefProps[defPath] = []string{}
|
||||
}
|
||||
reqRefProps[defPath] = append(reqRefProps[defPath], propName)
|
||||
|
||||
return reqRefProps
|
||||
return reqRefProps
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentParameters(paramsNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, param := range paramsNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = param.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: param,
|
||||
}
|
||||
index.allParameters[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, param := range paramsNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = param.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: param,
|
||||
}
|
||||
index.allParameters[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentRequestBodies(requestBodiesNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, reqBod := range requestBodiesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = reqBod.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: reqBod,
|
||||
}
|
||||
index.allRequestBodies[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, reqBod := range requestBodiesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = reqBod.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: reqBod,
|
||||
}
|
||||
index.allRequestBodies[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentResponses(responsesNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, response := range responsesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = response.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: response,
|
||||
}
|
||||
index.allResponses[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, response := range responsesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = response.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: response,
|
||||
}
|
||||
index.allResponses[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentHeaders(headersNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, header := range headersNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = header.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: header,
|
||||
}
|
||||
index.allHeaders[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, header := range headersNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = header.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: header,
|
||||
}
|
||||
index.allHeaders[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentCallbacks(callbacksNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, callback := range callbacksNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = callback.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: callback,
|
||||
}
|
||||
index.allCallbacks[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, callback := range callbacksNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = callback.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: callback,
|
||||
}
|
||||
index.allCallbacks[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentLinks(linksNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, link := range linksNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = link.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: link,
|
||||
}
|
||||
index.allLinks[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, link := range linksNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = link.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: link,
|
||||
}
|
||||
index.allLinks[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentExamples(examplesNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, example := range examplesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = example.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: example,
|
||||
}
|
||||
index.allExamples[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, example := range examplesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = example.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: example,
|
||||
}
|
||||
index.allExamples[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yaml.Node, pathPrefix string) {
|
||||
var name string
|
||||
for i, secScheme := range securitySchemesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = secScheme.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: secScheme,
|
||||
ParentNode: securitySchemesNode,
|
||||
Path: fmt.Sprintf("$.components.securitySchemes.%s", name),
|
||||
}
|
||||
index.allSecuritySchemes[def] = ref
|
||||
}
|
||||
var name string
|
||||
for i, secScheme := range securitySchemesNode.Content {
|
||||
if i%2 == 0 {
|
||||
name = secScheme.Value
|
||||
continue
|
||||
}
|
||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||
ref := &Reference{
|
||||
Definition: def,
|
||||
Name: name,
|
||||
Node: secScheme,
|
||||
ParentNode: securitySchemesNode,
|
||||
Path: fmt.Sprintf("$.components.securitySchemes.%s", name),
|
||||
}
|
||||
index.allSecuritySchemes[def] = ref
|
||||
}
|
||||
}
|
||||
|
||||
func (index *SpecIndex) countUniqueInlineDuplicates() int {
|
||||
if index.componentsInlineParamUniqueCount > 0 {
|
||||
return index.componentsInlineParamUniqueCount
|
||||
}
|
||||
unique := 0
|
||||
for _, p := range index.paramInlineDuplicateNames {
|
||||
if len(p) == 1 {
|
||||
unique++
|
||||
}
|
||||
}
|
||||
index.componentsInlineParamUniqueCount = unique
|
||||
return unique
|
||||
if index.componentsInlineParamUniqueCount > 0 {
|
||||
return index.componentsInlineParamUniqueCount
|
||||
}
|
||||
unique := 0
|
||||
for _, p := range index.paramInlineDuplicateNames {
|
||||
if len(p) == 1 {
|
||||
unique++
|
||||
}
|
||||
}
|
||||
index.componentsInlineParamUniqueCount = unique
|
||||
return unique
|
||||
}
|
||||
|
||||
func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *yaml.Node, method string) {
|
||||
for i, param := range params {
|
||||
// param is ref
|
||||
if len(param.Content) > 0 && param.Content[0].Value == "$ref" {
|
||||
for i, param := range params {
|
||||
// param is ref
|
||||
if len(param.Content) > 0 && param.Content[0].Value == "$ref" {
|
||||
|
||||
paramRefName := param.Content[1].Value
|
||||
paramRef := index.allMappedRefs[paramRefName]
|
||||
paramRefName := param.Content[1].Value
|
||||
paramRef := index.allMappedRefs[paramRefName]
|
||||
|
||||
if index.paramOpRefs[pathItemNode.Value] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
if index.paramOpRefs[pathItemNode.Value] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
|
||||
}
|
||||
// if we know the path, but it's a new method
|
||||
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
}
|
||||
}
|
||||
// if we know the path, but it's a new method
|
||||
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
}
|
||||
|
||||
// if this is a duplicate, add an error and ignore it
|
||||
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
|
||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||
if method == "top" {
|
||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||
}
|
||||
// if this is a duplicate, add an error and ignore it
|
||||
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
|
||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||
if method == "top" {
|
||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||
}
|
||||
|
||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
||||
"index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName),
|
||||
Node: param,
|
||||
Path: path,
|
||||
})
|
||||
} else {
|
||||
if paramRef != nil {
|
||||
index.paramOpRefs[pathItemNode.Value][method][paramRefName] =
|
||||
append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef)
|
||||
}
|
||||
}
|
||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
||||
"index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName),
|
||||
Node: param,
|
||||
Path: path,
|
||||
})
|
||||
} else {
|
||||
if paramRef != nil {
|
||||
index.paramOpRefs[pathItemNode.Value][method][paramRefName] =
|
||||
append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef)
|
||||
}
|
||||
}
|
||||
|
||||
continue
|
||||
continue
|
||||
|
||||
} else {
|
||||
} else {
|
||||
|
||||
// param is inline.
|
||||
_, vn := utils.FindKeyNode("name", param.Content)
|
||||
// param is inline.
|
||||
_, vn := utils.FindKeyNode("name", param.Content)
|
||||
|
||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||
if method == "top" {
|
||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||
}
|
||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||
if method == "top" {
|
||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||
}
|
||||
|
||||
if vn == nil {
|
||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||
Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value",
|
||||
method, pathItemNode.Value, i),
|
||||
Node: param,
|
||||
Path: path,
|
||||
})
|
||||
continue
|
||||
}
|
||||
if vn == nil {
|
||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||
Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value",
|
||||
method, pathItemNode.Value, i),
|
||||
Node: param,
|
||||
Path: path,
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
ref := &Reference{
|
||||
Definition: vn.Value,
|
||||
Name: vn.Value,
|
||||
Node: param,
|
||||
Path: path,
|
||||
}
|
||||
if index.paramOpRefs[pathItemNode.Value] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
}
|
||||
ref := &Reference{
|
||||
Definition: vn.Value,
|
||||
Name: vn.Value,
|
||||
Node: param,
|
||||
Path: path,
|
||||
}
|
||||
if index.paramOpRefs[pathItemNode.Value] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
}
|
||||
|
||||
// if we know the path but this is a new method.
|
||||
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
}
|
||||
// if we know the path but this is a new method.
|
||||
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||
}
|
||||
|
||||
// if this is a duplicate name, check if the `in` type is also the same, if so, it's a duplicate.
|
||||
if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 {
|
||||
// if this is a duplicate name, check if the `in` type is also the same, if so, it's a duplicate.
|
||||
if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 {
|
||||
|
||||
currentNode := ref.Node
|
||||
checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name]
|
||||
_, currentIn := utils.FindKeyNodeTop("in", currentNode.Content)
|
||||
currentNode := ref.Node
|
||||
checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name]
|
||||
_, currentIn := utils.FindKeyNodeTop("in", currentNode.Content)
|
||||
|
||||
for _, checkNode := range checkNodes {
|
||||
for _, checkNode := range checkNodes {
|
||||
|
||||
_, checkIn := utils.FindKeyNodeTop("in", checkNode.Node.Content)
|
||||
_, checkIn := utils.FindKeyNodeTop("in", checkNode.Node.Content)
|
||||
|
||||
if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value {
|
||||
if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value {
|
||||
|
||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||
if method == "top" {
|
||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||
}
|
||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||
if method == "top" {
|
||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||
}
|
||||
|
||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
||||
"index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value),
|
||||
Node: param,
|
||||
Path: path,
|
||||
})
|
||||
} else {
|
||||
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
||||
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
||||
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
||||
"index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value),
|
||||
Node: param,
|
||||
Path: path,
|
||||
})
|
||||
} else {
|
||||
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
||||
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
||||
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runIndexFunction(funcs []func() int, wg *sync.WaitGroup) {
|
||||
for _, cFunc := range funcs {
|
||||
go func(wg *sync.WaitGroup, cf func() int) {
|
||||
cf()
|
||||
wg.Done()
|
||||
}(wg, cFunc)
|
||||
}
|
||||
for _, cFunc := range funcs {
|
||||
go func(wg *sync.WaitGroup, cf func() int) {
|
||||
cf()
|
||||
wg.Done()
|
||||
}(wg, cFunc)
|
||||
}
|
||||
}
|
||||
|
||||
func GenerateCleanSpecConfigBaseURL(baseURL *url.URL, dir string, includeFile bool) string {
|
||||
|
||||
cleanedPath := baseURL.Path // not cleaned yet!
|
||||
cleanedPath := baseURL.Path // not cleaned yet!
|
||||
|
||||
// create a slice of path segments from existing path
|
||||
pathSegs := strings.Split(cleanedPath, "/")
|
||||
dirSegs := strings.Split(dir, "/")
|
||||
// create a slice of path segments from existing path
|
||||
pathSegs := strings.Split(cleanedPath, "/")
|
||||
dirSegs := strings.Split(dir, "/")
|
||||
|
||||
var cleanedSegs []string
|
||||
if !includeFile {
|
||||
dirSegs = dirSegs[:len(dirSegs)-1]
|
||||
}
|
||||
var cleanedSegs []string
|
||||
if !includeFile {
|
||||
dirSegs = dirSegs[:len(dirSegs)-1]
|
||||
}
|
||||
|
||||
// relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them
|
||||
// down into services, please don't blast apart specs into a billion shards.
|
||||
if strings.Contains(dir, "../") {
|
||||
for s := range dirSegs {
|
||||
if dirSegs[s] == ".." {
|
||||
// chop off the last segment of the base path.
|
||||
if len(pathSegs) > 0 {
|
||||
pathSegs = pathSegs[:len(pathSegs)-1]
|
||||
}
|
||||
} else {
|
||||
cleanedSegs = append(cleanedSegs, dirSegs[s])
|
||||
}
|
||||
}
|
||||
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/"))
|
||||
} else {
|
||||
if !strings.HasPrefix(dir, "http") {
|
||||
if len(pathSegs) > 1 || len(dirSegs) > 1 {
|
||||
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/"))
|
||||
}
|
||||
} else {
|
||||
cleanedPath = strings.Join(dirSegs, "/")
|
||||
}
|
||||
}
|
||||
var p string
|
||||
if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") {
|
||||
p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath)
|
||||
} else {
|
||||
if !strings.Contains(cleanedPath, "/") {
|
||||
p = ""
|
||||
} else {
|
||||
p = cleanedPath
|
||||
}
|
||||
// relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them
|
||||
// down into services, please don't blast apart specs into a billion shards.
|
||||
if strings.Contains(dir, "../") {
|
||||
for s := range dirSegs {
|
||||
if dirSegs[s] == ".." {
|
||||
// chop off the last segment of the base path.
|
||||
if len(pathSegs) > 0 {
|
||||
pathSegs = pathSegs[:len(pathSegs)-1]
|
||||
}
|
||||
} else {
|
||||
cleanedSegs = append(cleanedSegs, dirSegs[s])
|
||||
}
|
||||
}
|
||||
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/"))
|
||||
} else {
|
||||
if !strings.HasPrefix(dir, "http") {
|
||||
if len(pathSegs) > 1 || len(dirSegs) > 1 {
|
||||
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/"))
|
||||
}
|
||||
} else {
|
||||
cleanedPath = strings.Join(dirSegs, "/")
|
||||
}
|
||||
}
|
||||
var p string
|
||||
if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") {
|
||||
p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath)
|
||||
} else {
|
||||
if !strings.Contains(cleanedPath, "/") {
|
||||
p = ""
|
||||
} else {
|
||||
p = cleanedPath
|
||||
}
|
||||
|
||||
}
|
||||
if strings.HasSuffix(p, "/") {
|
||||
p = p[:len(p)-1]
|
||||
}
|
||||
return p
|
||||
}
|
||||
if strings.HasSuffix(p, "/") {
|
||||
p = p[:len(p)-1]
|
||||
}
|
||||
return p
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,52 +4,52 @@
|
||||
package index
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/url"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGenerateCleanSpecConfigBaseURL(t *testing.T) {
|
||||
u, _ := url.Parse("https://pb33f.io/things/stuff")
|
||||
path := "."
|
||||
assert.Equal(t, "https://pb33f.io/things/stuff",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
u, _ := url.Parse("https://pb33f.io/things/stuff")
|
||||
path := "."
|
||||
assert.Equal(t, "https://pb33f.io/things/stuff",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
}
|
||||
|
||||
func TestGenerateCleanSpecConfigBaseURL_RelativeDeep(t *testing.T) {
|
||||
u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil")
|
||||
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
||||
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil")
|
||||
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
||||
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
|
||||
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, true))
|
||||
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, true))
|
||||
}
|
||||
|
||||
func TestGenerateCleanSpecConfigBaseURL_NoBaseURL(t *testing.T) {
|
||||
|
||||
u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil")
|
||||
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
||||
assert.Equal(t, "/things/stuff/foo/bar/baz",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil")
|
||||
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
||||
assert.Equal(t, "/things/stuff/foo/bar/baz",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
|
||||
assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, true))
|
||||
assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, true))
|
||||
}
|
||||
|
||||
func TestGenerateCleanSpecConfigBaseURL_HttpStrip(t *testing.T) {
|
||||
|
||||
u, _ := url.Parse(".")
|
||||
path := "http://thing.com/crap.yaml#thang"
|
||||
assert.Equal(t, "http://thing.com",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
u, _ := url.Parse(".")
|
||||
path := "http://thing.com/crap.yaml#thang"
|
||||
assert.Equal(t, "http://thing.com",
|
||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||
|
||||
assert.Equal(t, "",
|
||||
GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true))
|
||||
assert.Equal(t, "",
|
||||
GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true))
|
||||
}
|
||||
|
||||
func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) {
|
||||
c := CreateOpenAPIIndexConfig()
|
||||
idx := NewSpecIndexWithConfig(nil, c)
|
||||
assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil))
|
||||
c := CreateOpenAPIIndexConfig()
|
||||
idx := NewSpecIndexWithConfig(nil, c)
|
||||
assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil))
|
||||
}
|
||||
@@ -1,170 +1,170 @@
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"testing"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func TestNewResolver(t *testing.T) {
|
||||
assert.Nil(t, NewResolver(nil))
|
||||
assert.Nil(t, NewResolver(nil))
|
||||
}
|
||||
|
||||
func Benchmark_ResolveDocumentStripe(b *testing.B) {
|
||||
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||
for n := 0; n < b.N; n++ {
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(stripe, &rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
resolver := NewResolver(index)
|
||||
resolver.Resolve()
|
||||
}
|
||||
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||
for n := 0; n < b.N; n++ {
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(stripe, &rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
resolver := NewResolver(index)
|
||||
resolver.Resolve()
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_CircularSpec(t *testing.T) {
|
||||
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 3)
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 3)
|
||||
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestResolver_CheckForCircularReferences(t *testing.T) {
|
||||
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 3)
|
||||
assert.Len(t, resolver.GetResolvingErrors(), 3)
|
||||
assert.Len(t, resolver.GetCircularErrors(), 3)
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 3)
|
||||
assert.Len(t, resolver.GetResolvingErrors(), 3)
|
||||
assert.Len(t, resolver.GetCircularErrors(), 3)
|
||||
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) {
|
||||
circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
|
||||
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
|
||||
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
|
||||
|
||||
index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{
|
||||
AllowRemoteLookup: true,
|
||||
AllowFileLookup: true,
|
||||
BaseURL: baseURL,
|
||||
})
|
||||
index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{
|
||||
AllowRemoteLookup: true,
|
||||
AllowFileLookup: true,
|
||||
BaseURL: baseURL,
|
||||
})
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 0)
|
||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 0)
|
||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
||||
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestResolver_CircularReferencesRequiredValid(t *testing.T) {
|
||||
circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 0)
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 0)
|
||||
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) {
|
||||
circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(circular, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 2)
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 2)
|
||||
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestResolver_DeepJourney(t *testing.T) {
|
||||
var journey []*index.Reference
|
||||
for f := 0; f < 200; f++ {
|
||||
journey = append(journey, nil)
|
||||
}
|
||||
index := index.NewSpecIndex(nil)
|
||||
resolver := NewResolver(index)
|
||||
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
|
||||
var journey []*index.Reference
|
||||
for f := 0; f < 200; f++ {
|
||||
journey = append(journey, nil)
|
||||
}
|
||||
index := index.NewSpecIndex(nil)
|
||||
resolver := NewResolver(index)
|
||||
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_Stripe(t *testing.T) {
|
||||
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(stripe, &rootNode)
|
||||
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(stripe, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 3)
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 3)
|
||||
|
||||
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
|
||||
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0)
|
||||
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
|
||||
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0)
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_BurgerShop(t *testing.T) {
|
||||
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(mixedref, &rootNode)
|
||||
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(mixedref, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 0)
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 0)
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) {
|
||||
yml := `paths:
|
||||
yml := `paths:
|
||||
/hey:
|
||||
get:
|
||||
responses:
|
||||
@@ -184,20 +184,20 @@ components:
|
||||
tea:
|
||||
description: tea`
|
||||
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 0)
|
||||
circ := resolver.CheckForCircularReferences()
|
||||
assert.Len(t, circ, 0)
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_PolyCircRef(t *testing.T) {
|
||||
yml := `openapi: 3.1.0
|
||||
yml := `openapi: 3.1.0
|
||||
components:
|
||||
schemas:
|
||||
cheese:
|
||||
@@ -211,24 +211,24 @@ components:
|
||||
tea:
|
||||
description: tea`
|
||||
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
_ = resolver.CheckForCircularReferences()
|
||||
resolver.circularReferences[0].IsInfiniteLoop = true // override
|
||||
assert.Len(t, index.GetCircularReferences(), 1)
|
||||
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
|
||||
assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex)
|
||||
_ = resolver.CheckForCircularReferences()
|
||||
resolver.circularReferences[0].IsInfiniteLoop = true // override
|
||||
assert.Len(t, index.GetCircularReferences(), 1)
|
||||
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
|
||||
assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex)
|
||||
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_Missing(t *testing.T) {
|
||||
yml := `paths:
|
||||
yml := `paths:
|
||||
/hey:
|
||||
get:
|
||||
responses:
|
||||
@@ -247,95 +247,95 @@ components:
|
||||
butter:
|
||||
$ref: 'go home, I am drunk'`
|
||||
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
err := resolver.Resolve()
|
||||
assert.Len(t, err, 1)
|
||||
assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error())
|
||||
err := resolver.Resolve()
|
||||
assert.Len(t, err, 1)
|
||||
assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error())
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_MixedRef(t *testing.T) {
|
||||
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(mixedref, &rootNode)
|
||||
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(mixedref, &rootNode)
|
||||
|
||||
b := index.CreateOpenAPIIndexConfig()
|
||||
idx := index.NewSpecIndexWithConfig(&rootNode, b)
|
||||
b := index.CreateOpenAPIIndexConfig()
|
||||
idx := index.NewSpecIndexWithConfig(&rootNode, b)
|
||||
|
||||
resolver := NewResolver(idx)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(idx)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 0)
|
||||
assert.Equal(t, 5, resolver.GetIndexesVisited())
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 0)
|
||||
assert.Equal(t, 5, resolver.GetIndexesVisited())
|
||||
|
||||
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
|
||||
assert.Equal(t, 191, resolver.GetRelativesSeen())
|
||||
assert.Equal(t, 35, resolver.GetJourneysTaken())
|
||||
assert.Equal(t, 62, resolver.GetReferenceVisited())
|
||||
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
|
||||
assert.Equal(t, 191, resolver.GetRelativesSeen())
|
||||
assert.Equal(t, 35, resolver.GetJourneysTaken())
|
||||
assert.Equal(t, 62, resolver.GetReferenceVisited())
|
||||
}
|
||||
|
||||
func TestResolver_ResolveComponents_k8s(t *testing.T) {
|
||||
k8s, _ := ioutil.ReadFile("../test_specs/k8s.json")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(k8s, &rootNode)
|
||||
k8s, _ := ioutil.ReadFile("../test_specs/k8s.json")
|
||||
var rootNode yaml.Node
|
||||
yaml.Unmarshal(k8s, &rootNode)
|
||||
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
index := index.NewSpecIndex(&rootNode)
|
||||
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
resolver := NewResolver(index)
|
||||
assert.NotNil(t, resolver)
|
||||
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 0)
|
||||
circ := resolver.Resolve()
|
||||
assert.Len(t, circ, 0)
|
||||
}
|
||||
|
||||
// Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors
|
||||
func ExampleNewResolver() {
|
||||
// create a yaml.Node reference as a root node.
|
||||
var rootNode yaml.Node
|
||||
// create a yaml.Node reference as a root node.
|
||||
var rootNode yaml.Node
|
||||
|
||||
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
|
||||
stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
|
||||
stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||
|
||||
// unmarshal bytes into our rootNode.
|
||||
_ = yaml.Unmarshal(stripeBytes, &rootNode)
|
||||
// unmarshal bytes into our rootNode.
|
||||
_ = yaml.Unmarshal(stripeBytes, &rootNode)
|
||||
|
||||
// create a new spec index (resolver depends on it)
|
||||
indexConfig := index.CreateClosedAPIIndexConfig()
|
||||
index := index.NewSpecIndexWithConfig(&rootNode, indexConfig)
|
||||
// create a new spec index (resolver depends on it)
|
||||
indexConfig := index.CreateClosedAPIIndexConfig()
|
||||
index := index.NewSpecIndexWithConfig(&rootNode, indexConfig)
|
||||
|
||||
// create a new resolver using the index.
|
||||
resolver := NewResolver(index)
|
||||
// create a new resolver using the index.
|
||||
resolver := NewResolver(index)
|
||||
|
||||
// resolve the document, if there are circular reference errors, they are returned/
|
||||
// WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved
|
||||
circularErrors := resolver.Resolve()
|
||||
// resolve the document, if there are circular reference errors, they are returned/
|
||||
// WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved
|
||||
circularErrors := resolver.Resolve()
|
||||
|
||||
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
|
||||
// So let's print them out.
|
||||
//
|
||||
fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not",
|
||||
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()))
|
||||
// Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not
|
||||
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
|
||||
// So let's print them out.
|
||||
//
|
||||
fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not",
|
||||
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()))
|
||||
// Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not
|
||||
}
|
||||
|
||||
func ExampleResolvingError() {
|
||||
re := ResolvingError{
|
||||
ErrorRef: errors.New("Je suis une erreur"),
|
||||
Node: &yaml.Node{
|
||||
Line: 5,
|
||||
Column: 21,
|
||||
},
|
||||
Path: "#/definitions/JeSuisUneErreur",
|
||||
CircularReference: &index.CircularReferenceResult{},
|
||||
}
|
||||
re := ResolvingError{
|
||||
ErrorRef: errors.New("Je suis une erreur"),
|
||||
Node: &yaml.Node{
|
||||
Line: 5,
|
||||
Column: 21,
|
||||
},
|
||||
Path: "#/definitions/JeSuisUneErreur",
|
||||
CircularReference: &index.CircularReferenceResult{},
|
||||
}
|
||||
|
||||
fmt.Printf("%s", re.Error())
|
||||
// Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
|
||||
fmt.Printf("%s", re.Error())
|
||||
// Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
|
||||
}
|
||||
|
||||
@@ -6,63 +6,63 @@ package utils
|
||||
import "gopkg.in/yaml.v3"
|
||||
|
||||
func CreateRefNode(ref string) *yaml.Node {
|
||||
m := CreateEmptyMapNode()
|
||||
nodes := make([]*yaml.Node, 2)
|
||||
nodes[0] = CreateStringNode("$ref")
|
||||
nodes[1] = CreateStringNode(ref)
|
||||
nodes[1].Style = yaml.SingleQuotedStyle
|
||||
m.Content = nodes
|
||||
return m
|
||||
m := CreateEmptyMapNode()
|
||||
nodes := make([]*yaml.Node, 2)
|
||||
nodes[0] = CreateStringNode("$ref")
|
||||
nodes[1] = CreateStringNode(ref)
|
||||
nodes[1].Style = yaml.SingleQuotedStyle
|
||||
m.Content = nodes
|
||||
return m
|
||||
}
|
||||
|
||||
func CreateEmptyMapNode() *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Tag: "!!map",
|
||||
}
|
||||
return n
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Tag: "!!map",
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateEmptySequenceNode() *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.SequenceNode,
|
||||
Tag: "!!seq",
|
||||
}
|
||||
return n
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.SequenceNode,
|
||||
Tag: "!!seq",
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateStringNode(str string) *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateBoolNode(str string) *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!bool",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!bool",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateIntNode(str string) *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!int",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!int",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateFloatNode(str string) *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!float",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!float",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -4,46 +4,46 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateBoolNode(t *testing.T) {
|
||||
b := CreateBoolNode("true")
|
||||
assert.Equal(t, "!!bool", b.Tag)
|
||||
assert.Equal(t, "true", b.Value)
|
||||
b := CreateBoolNode("true")
|
||||
assert.Equal(t, "!!bool", b.Tag)
|
||||
assert.Equal(t, "true", b.Value)
|
||||
}
|
||||
|
||||
func TestCreateEmptyMapNode(t *testing.T) {
|
||||
m := CreateEmptyMapNode()
|
||||
assert.Equal(t, "!!map", m.Tag)
|
||||
assert.Len(t, m.Content, 0)
|
||||
m := CreateEmptyMapNode()
|
||||
assert.Equal(t, "!!map", m.Tag)
|
||||
assert.Len(t, m.Content, 0)
|
||||
}
|
||||
|
||||
func TestCreateEmptySequenceNode(t *testing.T) {
|
||||
s := CreateEmptySequenceNode()
|
||||
assert.Equal(t, "!!seq", s.Tag)
|
||||
assert.Len(t, s.Content, 0)
|
||||
s := CreateEmptySequenceNode()
|
||||
assert.Equal(t, "!!seq", s.Tag)
|
||||
assert.Len(t, s.Content, 0)
|
||||
}
|
||||
|
||||
func TestCreateFloatNode(t *testing.T) {
|
||||
f := CreateFloatNode("3.14")
|
||||
assert.Equal(t, "!!float", f.Tag)
|
||||
assert.Equal(t, "3.14", f.Value)
|
||||
f := CreateFloatNode("3.14")
|
||||
assert.Equal(t, "!!float", f.Tag)
|
||||
assert.Equal(t, "3.14", f.Value)
|
||||
}
|
||||
|
||||
func TestCreateIntNode(t *testing.T) {
|
||||
i := CreateIntNode("42")
|
||||
assert.Equal(t, "!!int", i.Tag)
|
||||
assert.Equal(t, "42", i.Value)
|
||||
i := CreateIntNode("42")
|
||||
assert.Equal(t, "!!int", i.Tag)
|
||||
assert.Equal(t, "42", i.Value)
|
||||
}
|
||||
|
||||
func TestCreateRefNode(t *testing.T) {
|
||||
r := CreateRefNode("#/components/schemas/MySchema")
|
||||
assert.Equal(t, "!!map", r.Tag)
|
||||
assert.Len(t, r.Content, 2)
|
||||
assert.Equal(t, "!!str", r.Content[0].Tag)
|
||||
assert.Equal(t, "$ref", r.Content[0].Value)
|
||||
assert.Equal(t, "!!str", r.Content[1].Tag)
|
||||
assert.Equal(t, "#/components/schemas/MySchema", r.Content[1].Value)
|
||||
r := CreateRefNode("#/components/schemas/MySchema")
|
||||
assert.Equal(t, "!!map", r.Tag)
|
||||
assert.Len(t, r.Content, 2)
|
||||
assert.Equal(t, "!!str", r.Content[0].Tag)
|
||||
assert.Equal(t, "$ref", r.Content[0].Value)
|
||||
assert.Equal(t, "!!str", r.Content[1].Tag)
|
||||
assert.Equal(t, "#/components/schemas/MySchema", r.Content[1].Value)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user