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
|
// OpenAPI3SchemaData is an embedded version of the OpenAPI 3 Schema
|
||||||
|
//
|
||||||
//go:embed schemas/oas3-schema.json
|
//go:embed schemas/oas3-schema.json
|
||||||
var OpenAPI3SchemaData string // embedded OAS3 schema
|
var OpenAPI3SchemaData string // embedded OAS3 schema
|
||||||
|
|
||||||
// OpenAPI31SchemaData is an embedded version of the OpenAPI 3.1 Schema
|
// OpenAPI31SchemaData is an embedded version of the OpenAPI 3.1 Schema
|
||||||
|
//
|
||||||
//go:embed schemas/oas31-schema.json
|
//go:embed schemas/oas31-schema.json
|
||||||
var OpenAPI31SchemaData string // embedded OAS31 schema
|
var OpenAPI31SchemaData string // embedded OAS31 schema
|
||||||
|
|
||||||
// OpenAPI2SchemaData is an embedded version of the OpenAPI 2 (Swagger) Schema
|
// OpenAPI2SchemaData is an embedded version of the OpenAPI 2 (Swagger) Schema
|
||||||
|
//
|
||||||
//go:embed schemas/swagger2-schema.json
|
//go:embed schemas/swagger2-schema.json
|
||||||
var OpenAPI2SchemaData string // embedded OAS3 schema
|
var OpenAPI2SchemaData string // embedded OAS3 schema
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
package datamodel
|
package datamodel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewClosedDocumentConfiguration(t *testing.T) {
|
func TestNewClosedDocumentConfiguration(t *testing.T) {
|
||||||
cfg := NewClosedDocumentConfiguration()
|
cfg := NewClosedDocumentConfiguration()
|
||||||
assert.False(t, cfg.AllowRemoteReferences)
|
assert.False(t, cfg.AllowRemoteReferences)
|
||||||
assert.False(t, cfg.AllowFileReferences)
|
assert.False(t, cfg.AllowFileReferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewOpenDocumentConfiguration(t *testing.T) {
|
func TestNewOpenDocumentConfiguration(t *testing.T) {
|
||||||
cfg := NewOpenDocumentConfiguration()
|
cfg := NewOpenDocumentConfiguration()
|
||||||
assert.True(t, cfg.AllowRemoteReferences)
|
assert.True(t, cfg.AllowRemoteReferences)
|
||||||
assert.True(t, cfg.AllowFileReferences)
|
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
|
// 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.
|
// that all the latest features are collected, without damaging backwards compatibility.
|
||||||
package base
|
package base
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Contact represents a high-level representation of the Contact definitions found at
|
// 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 {
|
type Contact struct {
|
||||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||||
URL string `json:"url,omitempty" yaml:"url,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)
|
nb := low2.NewNodeBuilder(c, c.low)
|
||||||
return nb.Render(), nil
|
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.
|
// 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.
|
// 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 {
|
type Discriminator struct {
|
||||||
PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"`
|
PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"`
|
||||||
Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,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)
|
return yaml.Marshal(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// MarshalYAML will create a ready to render YAML representation of the DynamicValue object.
|
// MarshalYAML will create a ready to render YAML representation of the DynamicValue object.
|
||||||
func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) {
|
func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) {
|
||||||
// this is a custom renderer, we can't use the NodeBuilder out of the gate.
|
// 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+
|
// 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 {
|
type Example struct {
|
||||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import (
|
|||||||
// ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3
|
// ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3
|
||||||
//
|
//
|
||||||
// Allows referencing an external resource for extended documentation.
|
// 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 {
|
type ExternalDoc struct {
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||||
|
|||||||
@@ -200,5 +200,3 @@ x-cake:
|
|||||||
bytes, _ := highInfo.Render()
|
bytes, _ := highInfo.Render()
|
||||||
assert.Len(t, bytes, 275)
|
assert.Len(t, bytes, 275)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,56 +4,56 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLicense_Render(t *testing.T) {
|
func TestLicense_Render(t *testing.T) {
|
||||||
|
|
||||||
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
|
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
|
||||||
dat, _ := highL.Render()
|
dat, _ := highL.Render()
|
||||||
|
|
||||||
// unmarshal yaml into a *yaml.Node instance
|
// unmarshal yaml into a *yaml.Node instance
|
||||||
var cNode yaml.Node
|
var cNode yaml.Node
|
||||||
_ = yaml.Unmarshal(dat, &cNode)
|
_ = yaml.Unmarshal(dat, &cNode)
|
||||||
|
|
||||||
// build low
|
// build low
|
||||||
var lowLicense lowbase.License
|
var lowLicense lowbase.License
|
||||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||||
|
|
||||||
// build high
|
// build high
|
||||||
highLicense := NewLicense(&lowLicense)
|
highLicense := NewLicense(&lowLicense)
|
||||||
|
|
||||||
assert.Equal(t, "MIT", highLicense.Name)
|
assert.Equal(t, "MIT", highLicense.Name)
|
||||||
assert.Equal(t, "https://pb33f.io", highLicense.URL)
|
assert.Equal(t, "https://pb33f.io", highLicense.URL)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLicense_RenderEqual(t *testing.T) {
|
func TestLicense_RenderEqual(t *testing.T) {
|
||||||
|
|
||||||
yml := `name: MIT
|
yml := `name: MIT
|
||||||
url: https://pb33f.io/not-real
|
url: https://pb33f.io/not-real
|
||||||
`
|
`
|
||||||
// unmarshal yaml into a *yaml.Node instance
|
// unmarshal yaml into a *yaml.Node instance
|
||||||
var cNode yaml.Node
|
var cNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
_ = yaml.Unmarshal([]byte(yml), &cNode)
|
||||||
|
|
||||||
// build low
|
// build low
|
||||||
var lowLicense lowbase.License
|
var lowLicense lowbase.License
|
||||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||||
_ = lowLicense.Build(cNode.Content[0], nil)
|
_ = lowLicense.Build(cNode.Content[0], nil)
|
||||||
|
|
||||||
// build high
|
// build high
|
||||||
highLicense := NewLicense(&lowLicense)
|
highLicense := NewLicense(&lowLicense)
|
||||||
|
|
||||||
assert.Equal(t, "MIT", highLicense.Name)
|
assert.Equal(t, "MIT", highLicense.Name)
|
||||||
assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL)
|
assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL)
|
||||||
|
|
||||||
// re-render and ensure everything is in the same order as before.
|
// re-render and ensure everything is in the same order as before.
|
||||||
bytes, _ := highLicense.Render()
|
bytes, _ := highLicense.Render()
|
||||||
assert.Equal(t, yml, string(bytes))
|
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
|
// 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 {
|
type License struct {
|
||||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1
|
// 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 schema: https://swagger.io/specification/#schema-object
|
||||||
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
|
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
|
||||||
type Schema struct {
|
type Schema struct {
|
||||||
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
|
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
|
||||||
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
||||||
|
|
||||||
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
||||||
// In version 3.1, ExclusiveMaximum is a number.
|
// In version 3.1, ExclusiveMaximum is a number.
|
||||||
@@ -33,76 +33,79 @@ type Schema struct {
|
|||||||
// In version 3.1, ExclusiveMinimum is a number.
|
// In version 3.1, ExclusiveMinimum is a number.
|
||||||
ExclusiveMinimum *DynamicValue[bool, float64] `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
|
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 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
|
// in version 3.1, Type can be multiple values
|
||||||
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
|
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
|
|
||||||
// Schemas are resolved on demand using a SchemaProxy
|
// Schemas are resolved on demand using a SchemaProxy
|
||||||
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
|
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
|
||||||
|
|
||||||
// Polymorphic Schemas are only available in version 3+
|
// Polymorphic Schemas are only available in version 3+
|
||||||
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
|
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
|
||||||
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
|
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
|
||||||
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
|
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
|
||||||
|
|
||||||
// in 3.1 examples can be an array (which is recommended)
|
// in 3.1 examples can be an array (which is recommended)
|
||||||
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
|
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||||
|
|
||||||
// in 3.1 prefixItems provides tuple validation support.
|
// in 3.1 prefixItems provides tuple validation support.
|
||||||
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
|
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
|
||||||
|
|
||||||
// 3.1 Specific properties
|
// 3.1 Specific properties
|
||||||
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
|
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
|
||||||
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
|
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
|
||||||
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
|
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
|
||||||
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
|
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
|
||||||
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
|
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
|
||||||
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
|
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
|
||||||
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
|
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
|
||||||
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
|
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
|
||||||
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
|
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
|
||||||
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
|
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
|
||||||
UnevaluatedProperties *SchemaProxy `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
|
|
||||||
|
|
||||||
// in 3.1 Items can be a Schema or a boolean
|
// in 3.1 UnevaluatedProperties can be a Schema or a boolean
|
||||||
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
// 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
|
// in 3.1 Items can be a Schema or a boolean
|
||||||
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
|
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
||||||
|
|
||||||
// Compatible with all versions
|
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
|
||||||
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
|
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,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.
|
// Compatible with all versions
|
||||||
ParentProxy *SchemaProxy `json:"-" yaml:"-"`
|
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.
|
// 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
|
s.MinProperties = &schema.MinProperties.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
if !schema.MaxContains.IsEmpty() {
|
if !schema.MaxContains.IsEmpty() {
|
||||||
s.MaxContains = &schema.MaxContains.Value
|
s.MaxContains = &schema.MaxContains.Value
|
||||||
}
|
}
|
||||||
if !schema.MinContains.IsEmpty() {
|
if !schema.MinContains.IsEmpty() {
|
||||||
s.MinContains = &schema.MinContains.Value
|
s.MinContains = &schema.MinContains.Value
|
||||||
}
|
}
|
||||||
if !schema.UniqueItems.IsEmpty() {
|
if !schema.UniqueItems.IsEmpty() {
|
||||||
s.UniqueItems = &schema.UniqueItems.Value
|
s.UniqueItems = &schema.UniqueItems.Value
|
||||||
}
|
}
|
||||||
if !schema.Contains.IsEmpty() {
|
if !schema.Contains.IsEmpty() {
|
||||||
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.Contains.ValueNode,
|
ValueNode: schema.Contains.ValueNode,
|
||||||
Value: schema.Contains.Value,
|
Value: schema.Contains.Value,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if !schema.If.IsEmpty() {
|
if !schema.If.IsEmpty() {
|
||||||
s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.If.ValueNode,
|
ValueNode: schema.If.ValueNode,
|
||||||
Value: schema.If.Value,
|
Value: schema.If.Value,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if !schema.Else.IsEmpty() {
|
if !schema.Else.IsEmpty() {
|
||||||
s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.Else.ValueNode,
|
ValueNode: schema.Else.ValueNode,
|
||||||
Value: schema.Else.Value,
|
Value: schema.Else.Value,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if !schema.Then.IsEmpty() {
|
if !schema.Then.IsEmpty() {
|
||||||
s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.Then.ValueNode,
|
ValueNode: schema.Then.ValueNode,
|
||||||
Value: schema.Then.Value,
|
Value: schema.Then.Value,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if !schema.PropertyNames.IsEmpty() {
|
if !schema.PropertyNames.IsEmpty() {
|
||||||
s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.PropertyNames.ValueNode,
|
ValueNode: schema.PropertyNames.ValueNode,
|
||||||
Value: schema.PropertyNames.Value,
|
Value: schema.PropertyNames.Value,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if !schema.UnevaluatedItems.IsEmpty() {
|
if !schema.UnevaluatedItems.IsEmpty() {
|
||||||
s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
ValueNode: schema.UnevaluatedItems.ValueNode,
|
ValueNode: schema.UnevaluatedItems.ValueNode,
|
||||||
Value: schema.UnevaluatedItems.Value,
|
Value: schema.UnevaluatedItems.Value,
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
if !schema.UnevaluatedProperties.IsEmpty() {
|
// check if unevaluated properties is a schema
|
||||||
s.UnevaluatedProperties = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsA() {
|
||||||
ValueNode: schema.UnevaluatedProperties.ValueNode,
|
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
|
||||||
Value: schema.UnevaluatedProperties.Value,
|
A: &SchemaProxy{
|
||||||
}}
|
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
}
|
ValueNode: schema.UnevaluatedProperties.ValueNode,
|
||||||
|
Value: schema.UnevaluatedProperties.Value.A,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.Pattern = schema.Pattern.Value
|
// check if unevaluated properties is a bool
|
||||||
s.Format = schema.Format.Value
|
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.UnevaluatedProperties.IsEmpty() {
|
||||||
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.
|
|
||||||
|
|
||||||
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
|
s.Pattern = schema.Pattern.Value
|
||||||
if !schema.Anchor.IsEmpty() {
|
s.Format = schema.Format.Value
|
||||||
s.Anchor = schema.Anchor.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check this behavior.
|
// 3.0 spec is a single value
|
||||||
for i := range schema.Enum.Value {
|
if !schema.Type.IsEmpty() && schema.Type.Value.IsA() {
|
||||||
enum = append(enum, schema.Enum.Value[i].Value)
|
s.Type = []string{schema.Type.Value.A}
|
||||||
}
|
}
|
||||||
s.Enum = enum
|
// 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.
|
s.AdditionalProperties = schema.AdditionalProperties.Value
|
||||||
// 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.
|
s.Description = schema.Description.Value
|
||||||
polyCompletedChan := make(chan bool)
|
s.Default = schema.Default.Value
|
||||||
propsChan := make(chan bool)
|
if !schema.Nullable.IsEmpty() {
|
||||||
errChan := make(chan error)
|
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 {
|
var enum []any
|
||||||
idx int
|
if !schema.Anchor.IsEmpty() {
|
||||||
s *SchemaProxy
|
s.Anchor = schema.Anchor.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
// for every item, build schema async
|
// TODO: check this behavior.
|
||||||
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
|
for i := range schema.Enum.Value {
|
||||||
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
enum = append(enum, schema.Enum.Value[i].Value)
|
||||||
ValueNode: sch.ValueNode,
|
}
|
||||||
Value: sch.Value,
|
s.Enum = enum
|
||||||
Reference: sch.GetReference(),
|
|
||||||
}}
|
|
||||||
|
|
||||||
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
|
type buildResult struct {
|
||||||
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
|
idx int
|
||||||
doneChan chan bool, e chan error,
|
s *SchemaProxy
|
||||||
) {
|
}
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// props async
|
// for every item, build schema async
|
||||||
var plock sync.Mutex
|
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
|
||||||
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
|
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
props map[string]*SchemaProxy, sw int,
|
ValueNode: sch.ValueNode,
|
||||||
) {
|
Value: sch.Value,
|
||||||
plock.Lock()
|
Reference: sch.GetReference(),
|
||||||
props[k.Value] = &SchemaProxy{
|
}}
|
||||||
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
|
||||||
Value: v.Value,
|
|
||||||
KeyNode: k.KeyNode,
|
|
||||||
ValueNode: v.ValueNode,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
plock.Unlock()
|
|
||||||
|
|
||||||
switch sw {
|
bChan <- buildResult{idx: idx, s: p}
|
||||||
case 0:
|
}
|
||||||
s.Properties = props
|
|
||||||
case 1:
|
|
||||||
s.DependentSchemas = props
|
|
||||||
case 2:
|
|
||||||
s.PatternProperties = props
|
|
||||||
}
|
|
||||||
c <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
props := make(map[string]*SchemaProxy)
|
// schema async
|
||||||
for k, v := range schema.Properties.Value {
|
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
|
||||||
go buildProps(k, v, propsChan, props, 0)
|
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)
|
// props async
|
||||||
for k, v := range schema.DependentSchemas.Value {
|
var plock sync.Mutex
|
||||||
go buildProps(k, v, propsChan, dependents, 1)
|
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
|
||||||
}
|
props map[string]*SchemaProxy, sw int,
|
||||||
patternProps := make(map[string]*SchemaProxy)
|
) {
|
||||||
for k, v := range schema.PatternProperties.Value {
|
plock.Lock()
|
||||||
go buildProps(k, v, propsChan, patternProps, 2)
|
props[k.Value] = &SchemaProxy{
|
||||||
}
|
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
|
Value: v.Value,
|
||||||
|
KeyNode: k.KeyNode,
|
||||||
|
ValueNode: v.ValueNode,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
plock.Unlock()
|
||||||
|
|
||||||
var allOf []*SchemaProxy
|
switch sw {
|
||||||
var oneOf []*SchemaProxy
|
case 0:
|
||||||
var anyOf []*SchemaProxy
|
s.Properties = props
|
||||||
var not *SchemaProxy
|
case 1:
|
||||||
var items *DynamicValue[*SchemaProxy, bool]
|
s.DependentSchemas = props
|
||||||
var prefixItems []*SchemaProxy
|
case 2:
|
||||||
|
s.PatternProperties = props
|
||||||
|
}
|
||||||
|
c <- true
|
||||||
|
}
|
||||||
|
|
||||||
children := 0
|
props := make(map[string]*SchemaProxy)
|
||||||
if !schema.AllOf.IsEmpty() {
|
for k, v := range schema.Properties.Value {
|
||||||
children++
|
go buildProps(k, v, propsChan, props, 0)
|
||||||
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
|
dependents := make(map[string]*SchemaProxy)
|
||||||
completedProps := 0
|
for k, v := range schema.DependentSchemas.Value {
|
||||||
totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value)
|
go buildProps(k, v, propsChan, dependents, 1)
|
||||||
if totalProps+children > 0 {
|
}
|
||||||
allDone:
|
patternProps := make(map[string]*SchemaProxy)
|
||||||
for true {
|
for k, v := range schema.PatternProperties.Value {
|
||||||
select {
|
go buildProps(k, v, propsChan, patternProps, 2)
|
||||||
case <-polyCompletedChan:
|
}
|
||||||
completeChildren++
|
|
||||||
if totalProps == completedProps && children == completeChildren {
|
var allOf []*SchemaProxy
|
||||||
break allDone
|
var oneOf []*SchemaProxy
|
||||||
}
|
var anyOf []*SchemaProxy
|
||||||
case <-propsChan:
|
var not *SchemaProxy
|
||||||
completedProps++
|
var items *DynamicValue[*SchemaProxy, bool]
|
||||||
if totalProps == completedProps && children == completeChildren {
|
var prefixItems []*SchemaProxy
|
||||||
break allDone
|
|
||||||
}
|
children := 0
|
||||||
}
|
if !schema.AllOf.IsEmpty() {
|
||||||
}
|
children++
|
||||||
}
|
allOf = make([]*SchemaProxy, len(schema.AllOf.Value))
|
||||||
s.OneOf = oneOf
|
go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
|
||||||
s.AnyOf = anyOf
|
}
|
||||||
s.AllOf = allOf
|
if !schema.AnyOf.IsEmpty() {
|
||||||
s.Items = items
|
children++
|
||||||
s.PrefixItems = prefixItems
|
anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value))
|
||||||
s.Not = not
|
go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
|
||||||
return s
|
}
|
||||||
|
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.
|
// GoLow will return the low-level instance of Schema that was used to create the high level one.
|
||||||
func (s *Schema) GoLow() *base.Schema {
|
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
|
// 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 {
|
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.
|
// Render will return a YAML representation of the Schema object as a byte slice.
|
||||||
func (s *Schema) Render() ([]byte, error) {
|
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
|
// 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!
|
// Make sure you don't have any circular references!
|
||||||
func (s *Schema) RenderInline() ([]byte, error) {
|
func (s *Schema) RenderInline() ([]byte, error) {
|
||||||
d, _ := s.MarshalYAMLInline()
|
d, _ := s.MarshalYAMLInline()
|
||||||
return yaml.Marshal(d)
|
return yaml.Marshal(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
|
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
|
||||||
func (s *Schema) MarshalYAML() (interface{}, error) {
|
func (s *Schema) MarshalYAML() (interface{}, error) {
|
||||||
nb := high.NewNodeBuilder(s, s.low)
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
return nb.Render(), nil
|
return nb.Render(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Schema) MarshalYAMLInline() (interface{}, error) {
|
func (s *Schema) MarshalYAMLInline() (interface{}, error) {
|
||||||
nb := high.NewNodeBuilder(s, s.low)
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
nb.Resolve = true
|
nb.Resolve = true
|
||||||
return nb.Render(), nil
|
return nb.Render(), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
||||||
const ymlComponents = `components:
|
const ymlComponents = `components:
|
||||||
schemas:
|
schemas:
|
||||||
rice:
|
rice:
|
||||||
type: string
|
type: string
|
||||||
@@ -27,42 +27,41 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
|||||||
rice:
|
rice:
|
||||||
$ref: '#/components/schemas/rice'`
|
$ref: '#/components/schemas/rice'`
|
||||||
|
|
||||||
idx := func() *index.SpecIndex {
|
idx := func() *index.SpecIndex {
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
const ref = "#/components/schemas/nice"
|
const ref = "#/components/schemas/nice"
|
||||||
const ymlSchema = `$ref: '` + ref + `'`
|
const ymlSchema = `$ref: '` + ref + `'`
|
||||||
var node yaml.Node
|
var node yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(ymlSchema), &node)
|
_ = yaml.Unmarshal([]byte(ymlSchema), &node)
|
||||||
|
|
||||||
lowProxy := new(lowbase.SchemaProxy)
|
lowProxy := new(lowbase.SchemaProxy)
|
||||||
err := lowProxy.Build(node.Content[0], idx)
|
err := lowProxy.Build(node.Content[0], idx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||||
Value: lowProxy,
|
Value: lowProxy,
|
||||||
}
|
}
|
||||||
|
|
||||||
sp := NewSchemaProxy(&lowRef)
|
sp := NewSchemaProxy(&lowRef)
|
||||||
|
|
||||||
rend, _ := sp.Render()
|
rend, _ := sp.Render()
|
||||||
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
|
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateSchemaProxy(t *testing.T) {
|
func TestCreateSchemaProxy(t *testing.T) {
|
||||||
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
|
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
|
||||||
assert.Equal(t, "iAmASchema", sp.rendered.Description)
|
assert.Equal(t, "iAmASchema", sp.rendered.Description)
|
||||||
assert.False(t, sp.IsReference())
|
assert.False(t, sp.IsReference())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateSchemaProxyRef(t *testing.T) {
|
func TestCreateSchemaProxyRef(t *testing.T) {
|
||||||
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
|
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
|
||||||
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
|
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
|
||||||
assert.True(t, sp.IsReference())
|
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).
|
// 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
|
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
|
||||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||||
type SecurityRequirement struct {
|
type SecurityRequirement struct {
|
||||||
Requirements map[string][]string `json:"-" yaml:"-"`
|
Requirements map[string][]string `json:"-" yaml:"-"`
|
||||||
low *base.SecurityRequirement
|
low *base.SecurityRequirement
|
||||||
@@ -108,7 +108,7 @@ func (s *SecurityRequirement) MarshalYAML() (interface{}, error) {
|
|||||||
for t := range keys[k].val {
|
for t := range keys[k].val {
|
||||||
reqs[t] = &req{val: keys[k].val[t], line: 9999 + t}
|
reqs[t] = &req{val: keys[k].val[t], line: 9999 + t}
|
||||||
if keys[k].lowVal != nil {
|
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]
|
fh := keys[k].val[t]
|
||||||
df := keys[k].lowVal.Value[t].Value
|
df := keys[k].lowVal.Value[t].Value
|
||||||
if fh == df {
|
if fh == df {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by
|
// 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
|
// 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.
|
// 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 {
|
type XML struct {
|
||||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||||
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
|
||||||
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
|
||||||
Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
|
Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
|
||||||
Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
|
Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any
|
||||||
low *low.XML
|
low *low.XML
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewXML creates a new high-level XML instance from a low-level one.
|
// NewXML creates a new high-level XML instance from a low-level one.
|
||||||
func NewXML(xml *low.XML) *XML {
|
func NewXML(xml *low.XML) *XML {
|
||||||
x := new(XML)
|
x := new(XML)
|
||||||
x.low = xml
|
x.low = xml
|
||||||
x.Name = xml.Name.Value
|
x.Name = xml.Name.Value
|
||||||
x.Namespace = xml.Namespace.Value
|
x.Namespace = xml.Namespace.Value
|
||||||
x.Prefix = xml.Prefix.Value
|
x.Prefix = xml.Prefix.Value
|
||||||
x.Attribute = xml.Attribute.Value
|
x.Attribute = xml.Attribute.Value
|
||||||
x.Wrapped = xml.Wrapped.Value
|
x.Wrapped = xml.Wrapped.Value
|
||||||
x.Extensions = high.ExtractExtensions(xml.Extensions)
|
x.Extensions = high.ExtractExtensions(xml.Extensions)
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoLow returns the low level XML reference used to create the high level one.
|
// GoLow returns the low level XML reference used to create the high level one.
|
||||||
func (x *XML) GoLow() *low.XML {
|
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
|
// 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 {
|
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.
|
// Render will return a YAML representation of the XML object as a byte slice.
|
||||||
func (x *XML) Render() ([]byte, error) {
|
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.
|
// MarshalYAML will create a ready to render YAML representation of the XML object.
|
||||||
func (x *XML) MarshalYAML() (interface{}, error) {
|
func (x *XML) MarshalYAML() (interface{}, error) {
|
||||||
nb := high.NewNodeBuilder(x, x.low)
|
nb := high.NewNodeBuilder(x, x.low)
|
||||||
return nb.Render(), nil
|
return nb.Render(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,4 +71,4 @@ wrapped: true`
|
|||||||
highXMLBytes, _ = highXML.Render()
|
highXMLBytes, _ = highXML.Render()
|
||||||
assert.NotEqual(t, yml, strings.TrimSpace(string(highXMLBytes)))
|
assert.NotEqual(t, yml, strings.TrimSpace(string(highXMLBytes)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
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.
|
// `low` represents the HIGH type of the object that contains the extensions.
|
||||||
//
|
//
|
||||||
// to use:
|
// 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) {
|
func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string]*T, error) {
|
||||||
m := make(map[string]*T)
|
m := make(map[string]*T)
|
||||||
ext := low.GoLow().GetExtensions()
|
ext := low.GoLow().GetExtensions()
|
||||||
@@ -75,4 +76,3 @@ func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string
|
|||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,127 +4,127 @@
|
|||||||
package high
|
package high
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExtractExtensions(t *testing.T) {
|
func TestExtractExtensions(t *testing.T) {
|
||||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
n[low.KeyReference[string]{
|
n[low.KeyReference[string]{
|
||||||
Value: "pb33f",
|
Value: "pb33f",
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
Value: "new cowboy in town",
|
Value: "new cowboy in town",
|
||||||
}
|
}
|
||||||
ext := ExtractExtensions(n)
|
ext := ExtractExtensions(n)
|
||||||
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
assert.Equal(t, "new cowboy in town", ext["pb33f"])
|
||||||
}
|
}
|
||||||
|
|
||||||
type textExtension struct {
|
type textExtension struct {
|
||||||
Cowboy string
|
Cowboy string
|
||||||
Power int
|
Power int
|
||||||
}
|
}
|
||||||
|
|
||||||
type parent struct {
|
type parent struct {
|
||||||
low *child
|
low *child
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *parent) GoLow() *child {
|
func (p *parent) GoLow() *child {
|
||||||
return p.low
|
return p.low
|
||||||
}
|
}
|
||||||
|
|
||||||
type child struct {
|
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] {
|
func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
||||||
return c.Extensions
|
return c.Extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackExtensions(t *testing.T) {
|
func TestUnpackExtensions(t *testing.T) {
|
||||||
|
|
||||||
var resultA, resultB yaml.Node
|
var resultA, resultB yaml.Node
|
||||||
|
|
||||||
ymlA := `
|
ymlA := `
|
||||||
cowboy: buckaroo
|
cowboy: buckaroo
|
||||||
power: 100`
|
power: 100`
|
||||||
|
|
||||||
ymlB := `
|
ymlB := `
|
||||||
cowboy: frogman
|
cowboy: frogman
|
||||||
power: 2`
|
power: 2`
|
||||||
|
|
||||||
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
n[low.KeyReference[string]{
|
n[low.KeyReference[string]{
|
||||||
Value: "x-rancher-a",
|
Value: "x-rancher-a",
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
ValueNode: resultA.Content[0],
|
ValueNode: resultA.Content[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
n[low.KeyReference[string]{
|
n[low.KeyReference[string]{
|
||||||
Value: "x-rancher-b",
|
Value: "x-rancher-b",
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
ValueNode: resultB.Content[0],
|
ValueNode: resultB.Content[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
c := new(child)
|
c := new(child)
|
||||||
c.Extensions = n
|
c.Extensions = n
|
||||||
|
|
||||||
p := new(parent)
|
p := new(parent)
|
||||||
p.low = c
|
p.low = c
|
||||||
|
|
||||||
res, err := UnpackExtensions[textExtension, *child](p)
|
res, err := UnpackExtensions[textExtension, *child](p)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, res)
|
assert.NotEmpty(t, res)
|
||||||
assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy)
|
assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy)
|
||||||
assert.Equal(t, 100, res["x-rancher-a"].Power)
|
assert.Equal(t, 100, res["x-rancher-a"].Power)
|
||||||
assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy)
|
assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy)
|
||||||
assert.Equal(t, 2, res["x-rancher-b"].Power)
|
assert.Equal(t, 2, res["x-rancher-b"].Power)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnpackExtensions_Fail(t *testing.T) {
|
func TestUnpackExtensions_Fail(t *testing.T) {
|
||||||
|
|
||||||
var resultA, resultB yaml.Node
|
var resultA, resultB yaml.Node
|
||||||
|
|
||||||
ymlA := `
|
ymlA := `
|
||||||
cowboy: buckaroo
|
cowboy: buckaroo
|
||||||
power: 100`
|
power: 100`
|
||||||
|
|
||||||
// this is incorrect types, unpacking will fail.
|
// this is incorrect types, unpacking will fail.
|
||||||
ymlB := `
|
ymlB := `
|
||||||
cowboy: 0
|
cowboy: 0
|
||||||
power: hello`
|
power: hello`
|
||||||
|
|
||||||
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
err := yaml.Unmarshal([]byte(ymlA), &resultA)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
err = yaml.Unmarshal([]byte(ymlB), &resultB)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
n := make(map[low.KeyReference[string]]low.ValueReference[any])
|
||||||
n[low.KeyReference[string]{
|
n[low.KeyReference[string]{
|
||||||
Value: "x-rancher-a",
|
Value: "x-rancher-a",
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
ValueNode: resultA.Content[0],
|
ValueNode: resultA.Content[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
n[low.KeyReference[string]{
|
n[low.KeyReference[string]{
|
||||||
Value: "x-rancher-b",
|
Value: "x-rancher-b",
|
||||||
}] = low.ValueReference[any]{
|
}] = low.ValueReference[any]{
|
||||||
ValueNode: resultB.Content[0],
|
ValueNode: resultB.Content[0],
|
||||||
}
|
}
|
||||||
|
|
||||||
c := new(child)
|
c := new(child)
|
||||||
c.Extensions = n
|
c.Extensions = n
|
||||||
|
|
||||||
p := new(parent)
|
p := new(parent)
|
||||||
p.low = c
|
p.low = c
|
||||||
|
|
||||||
res, er := UnpackExtensions[textExtension, *child](p)
|
res, er := UnpackExtensions[textExtension, *child](p)
|
||||||
assert.Error(t, er)
|
assert.Error(t, er)
|
||||||
assert.Empty(t, res)
|
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,
|
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
|
||||||
// arrays or models.
|
// arrays or models.
|
||||||
// - https://swagger.io/specification/v2/#definitionsObject
|
// - https://swagger.io/specification/v2/#definitionsObject
|
||||||
type Definitions struct {
|
type Definitions struct {
|
||||||
Definitions map[string]*highbase.SchemaProxy
|
Definitions map[string]*highbase.SchemaProxy
|
||||||
low *low.Definitions
|
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.
|
// Example represents a high-level Swagger / OpenAPI 2 Example object, backed by a low level one.
|
||||||
// Allows sharing examples for operation responses
|
// Allows sharing examples for operation responses
|
||||||
// - https://swagger.io/specification/v2/#exampleObject
|
// - https://swagger.io/specification/v2/#exampleObject
|
||||||
type Example struct {
|
type Example struct {
|
||||||
Values map[string]any
|
Values map[string]any
|
||||||
low *low.Examples
|
low *low.Examples
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
// Header Represents a high-level Swagger / OpenAPI 2 Header object, backed by a low-level one.
|
// 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.
|
// 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 Header struct {
|
||||||
Type string
|
Type string
|
||||||
Format 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 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
|
// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not
|
||||||
// located in "body"
|
// located in "body"
|
||||||
// - https://swagger.io/specification/v2/#itemsObject
|
// - https://swagger.io/specification/v2/#itemsObject
|
||||||
type Items struct {
|
type Items struct {
|
||||||
Type string
|
Type string
|
||||||
Format string
|
Format string
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
|
|
||||||
// Operation represents a high-level Swagger / OpenAPI 2 Operation object, backed by a low-level one.
|
// 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.
|
// It describes a single API operation on a path.
|
||||||
// - https://swagger.io/specification/v2/#operationObject
|
// - https://swagger.io/specification/v2/#operationObject
|
||||||
type Operation struct {
|
type Operation struct {
|
||||||
Tags []string
|
Tags []string
|
||||||
Summary string
|
Summary string
|
||||||
|
|||||||
@@ -16,29 +16,39 @@ import (
|
|||||||
// There are five possible parameter types.
|
// There are five possible parameter types.
|
||||||
//
|
//
|
||||||
// Path
|
// 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
|
// 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
|
// Header
|
||||||
// Custom headers that are expected as part of the request.
|
//
|
||||||
|
// Custom headers that are expected as part of the request.
|
||||||
|
//
|
||||||
// Body
|
// 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.
|
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
|
||||||
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
|
// 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
|
// 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).
|
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
|
||||||
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
|
||||||
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
||||||
// parameters have a different format based on the content-type used (for further details,
|
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
||||||
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
// parameters have a different format based on the content-type used (for further details,
|
||||||
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
||||||
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
||||||
// being transferred.
|
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
||||||
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
// being transferred.
|
||||||
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
|
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
||||||
// submit-name. This type of form parameters is more commonly used for file transfers
|
// 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
|
// https://swagger.io/specification/v2/#parameterObject
|
||||||
type Parameter struct {
|
type Parameter struct {
|
||||||
Name string
|
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
|
// 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
|
// referenced to the ones defined here. It does not define global operation parameters
|
||||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||||
type ParameterDefinitions struct {
|
type ParameterDefinitions struct {
|
||||||
Definitions map[string]*Parameter
|
Definitions map[string]*Parameter
|
||||||
low *low.ParameterDefinitions
|
low *low.ParameterDefinitions
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PathItem represents a high-level Swagger / OpenAPI 2 PathItem object backed by a low-level one.
|
// 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.
|
// 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
|
// The path itself is still exposed to the tooling, but will not know which operations and parameters
|
||||||
// are available.
|
// are available.
|
||||||
// - https://swagger.io/specification/v2/#pathItemObject
|
// - https://swagger.io/specification/v2/#pathItemObject
|
||||||
type PathItem struct {
|
type PathItem struct {
|
||||||
Ref string
|
Ref string
|
||||||
Get *Operation
|
Get *Operation
|
||||||
Put *Operation
|
Put *Operation
|
||||||
Post *Operation
|
Post *Operation
|
||||||
Delete *Operation
|
Delete *Operation
|
||||||
Options *Operation
|
Options *Operation
|
||||||
Head *Operation
|
Head *Operation
|
||||||
Patch *Operation
|
Patch *Operation
|
||||||
Parameters []*Parameter
|
Parameters []*Parameter
|
||||||
Extensions map[string]any
|
Extensions map[string]any
|
||||||
low *low.PathItem
|
low *low.PathItem
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPathItem will create a new high-level PathItem from a low-level one. All paths are built out asynchronously.
|
// NewPathItem will create a new high-level PathItem from a low-level one. All paths are built out asynchronously.
|
||||||
func NewPathItem(pathItem *low.PathItem) *PathItem {
|
func NewPathItem(pathItem *low.PathItem) *PathItem {
|
||||||
p := new(PathItem)
|
p := new(PathItem)
|
||||||
p.low = pathItem
|
p.low = pathItem
|
||||||
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
|
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
|
||||||
if !pathItem.Parameters.IsEmpty() {
|
if !pathItem.Parameters.IsEmpty() {
|
||||||
var params []*Parameter
|
var params []*Parameter
|
||||||
for k := range pathItem.Parameters.Value {
|
for k := range pathItem.Parameters.Value {
|
||||||
params = append(params, NewParameter(pathItem.Parameters.Value[k].Value))
|
params = append(params, NewParameter(pathItem.Parameters.Value[k].Value))
|
||||||
}
|
}
|
||||||
p.Parameters = params
|
p.Parameters = params
|
||||||
}
|
}
|
||||||
var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) {
|
var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) {
|
||||||
resChan <- asyncResult[*Operation]{
|
resChan <- asyncResult[*Operation]{
|
||||||
key: method,
|
key: method,
|
||||||
result: NewOperation(op),
|
result: NewOperation(op),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
totalOperations := 0
|
totalOperations := 0
|
||||||
resChan := make(chan asyncResult[*Operation])
|
resChan := make(chan asyncResult[*Operation])
|
||||||
if !pathItem.Get.IsEmpty() {
|
if !pathItem.Get.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.GetLabel, pathItem.Get.Value, resChan)
|
go buildOperation(low.GetLabel, pathItem.Get.Value, resChan)
|
||||||
}
|
}
|
||||||
if !pathItem.Put.IsEmpty() {
|
if !pathItem.Put.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.PutLabel, pathItem.Put.Value, resChan)
|
go buildOperation(low.PutLabel, pathItem.Put.Value, resChan)
|
||||||
}
|
}
|
||||||
if !pathItem.Post.IsEmpty() {
|
if !pathItem.Post.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.PostLabel, pathItem.Post.Value, resChan)
|
go buildOperation(low.PostLabel, pathItem.Post.Value, resChan)
|
||||||
}
|
}
|
||||||
if !pathItem.Patch.IsEmpty() {
|
if !pathItem.Patch.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan)
|
go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan)
|
||||||
}
|
}
|
||||||
if !pathItem.Delete.IsEmpty() {
|
if !pathItem.Delete.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan)
|
go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan)
|
||||||
}
|
}
|
||||||
if !pathItem.Head.IsEmpty() {
|
if !pathItem.Head.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan)
|
go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan)
|
||||||
}
|
}
|
||||||
if !pathItem.Options.IsEmpty() {
|
if !pathItem.Options.IsEmpty() {
|
||||||
totalOperations++
|
totalOperations++
|
||||||
go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan)
|
go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan)
|
||||||
}
|
}
|
||||||
completedOperations := 0
|
completedOperations := 0
|
||||||
for completedOperations < totalOperations {
|
for completedOperations < totalOperations {
|
||||||
select {
|
select {
|
||||||
case r := <-resChan:
|
case r := <-resChan:
|
||||||
switch r.key {
|
switch r.key {
|
||||||
case low.GetLabel:
|
case low.GetLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Get = r.result
|
p.Get = r.result
|
||||||
case low.PutLabel:
|
case low.PutLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Put = r.result
|
p.Put = r.result
|
||||||
case low.PostLabel:
|
case low.PostLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Post = r.result
|
p.Post = r.result
|
||||||
case low.PatchLabel:
|
case low.PatchLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Patch = r.result
|
p.Patch = r.result
|
||||||
case low.DeleteLabel:
|
case low.DeleteLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Delete = r.result
|
p.Delete = r.result
|
||||||
case low.HeadLabel:
|
case low.HeadLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Head = r.result
|
p.Head = r.result
|
||||||
case low.OptionsLabel:
|
case low.OptionsLabel:
|
||||||
completedOperations++
|
completedOperations++
|
||||||
p.Options = r.result
|
p.Options = r.result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoLow returns the low-level PathItem used to create the high-level one.
|
// GoLow returns the low-level PathItem used to create the high-level one.
|
||||||
func (p *PathItem) GoLow() *low.PathItem {
|
func (p *PathItem) GoLow() *low.PathItem {
|
||||||
return p.low
|
return p.low
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PathItem) GetOperations() map[string]*Operation {
|
func (p *PathItem) GetOperations() map[string]*Operation {
|
||||||
o := make(map[string]*Operation)
|
o := make(map[string]*Operation)
|
||||||
if p.Get != nil {
|
if p.Get != nil {
|
||||||
o[low.GetLabel] = p.Get
|
o[low.GetLabel] = p.Get
|
||||||
}
|
}
|
||||||
if p.Put != nil {
|
if p.Put != nil {
|
||||||
o[low.PutLabel] = p.Put
|
o[low.PutLabel] = p.Put
|
||||||
}
|
}
|
||||||
if p.Post != nil {
|
if p.Post != nil {
|
||||||
o[low.PostLabel] = p.Post
|
o[low.PostLabel] = p.Post
|
||||||
}
|
}
|
||||||
if p.Delete != nil {
|
if p.Delete != nil {
|
||||||
o[low.DeleteLabel] = p.Delete
|
o[low.DeleteLabel] = p.Delete
|
||||||
}
|
}
|
||||||
if p.Options != nil {
|
if p.Options != nil {
|
||||||
o[low.OptionsLabel] = p.Options
|
o[low.OptionsLabel] = p.Options
|
||||||
}
|
}
|
||||||
if p.Head != nil {
|
if p.Head != nil {
|
||||||
o[low.HeadLabel] = p.Head
|
o[low.HeadLabel] = p.Head
|
||||||
}
|
}
|
||||||
if p.Patch != nil {
|
if p.Patch != nil {
|
||||||
o[low.PatchLabel] = p.Patch
|
o[low.PatchLabel] = p.Patch
|
||||||
}
|
}
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,17 +4,17 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPathItem_GetOperations(t *testing.T) {
|
func TestPathItem_GetOperations(t *testing.T) {
|
||||||
|
|
||||||
yml := `get:
|
yml := `get:
|
||||||
description: get
|
description: get
|
||||||
put:
|
put:
|
||||||
description: put
|
description: put
|
||||||
@@ -30,15 +30,15 @@ options:
|
|||||||
description: options
|
description: options
|
||||||
`
|
`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
var n v2.PathItem
|
var n v2.PathItem
|
||||||
_ = low.BuildModel(&idxNode, &n)
|
_ = low.BuildModel(&idxNode, &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = 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 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
|
// Response describes a single response from an API Operation
|
||||||
// - https://swagger.io/specification/v2/#responseObject
|
// - https://swagger.io/specification/v2/#responseObject
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Description string
|
Description string
|
||||||
Schema *base.SchemaProxy
|
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
|
// 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
|
// referenced to the ones defined here. It does not define global operation responses
|
||||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||||
type ResponsesDefinitions struct {
|
type ResponsesDefinitions struct {
|
||||||
Definitions map[string]*Response
|
Definitions map[string]*Response
|
||||||
low *low.ResponsesDefinitions
|
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 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.
|
// Scopes lists the available scopes for an OAuth2 security scheme.
|
||||||
// - https://swagger.io/specification/v2/#scopesObject
|
// - https://swagger.io/specification/v2/#scopesObject
|
||||||
type Scopes struct {
|
type Scopes struct {
|
||||||
Values map[string]string
|
Values map[string]string
|
||||||
low *low.Scopes
|
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
|
// 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
|
// schemes on the operations and only serves to provide the relevant details for each scheme
|
||||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||||
type SecurityDefinitions struct {
|
type SecurityDefinitions struct {
|
||||||
Definitions map[string]*SecurityScheme
|
Definitions map[string]*SecurityScheme
|
||||||
low *low.SecurityDefinitions
|
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
|
// 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
|
// 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)
|
// (implicit, password, application and access code)
|
||||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||||
type SecurityScheme struct {
|
type SecurityScheme struct {
|
||||||
Type string
|
Type string
|
||||||
Description 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
|
// 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,
|
// 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.
|
// that identifies a URL to use for the callback operation.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||||
type Callback struct {
|
type Callback struct {
|
||||||
Expression map[string]*PathItem `json:"-" yaml:"-"`
|
Expression map[string]*PathItem `json:"-" yaml:"-"`
|
||||||
Extensions map[string]any `json:"-" yaml:"-"`
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
|
|||||||
@@ -4,49 +4,49 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCallback_MarshalYAML(t *testing.T) {
|
func TestCallback_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
cb := &Callback{
|
cb := &Callback{
|
||||||
Expression: map[string]*PathItem{
|
Expression: map[string]*PathItem{
|
||||||
"https://pb33f.io": {
|
"https://pb33f.io": {
|
||||||
Get: &Operation{
|
Get: &Operation{
|
||||||
OperationId: "oneTwoThree",
|
OperationId: "oneTwoThree",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"https://pb33f.io/libopenapi": {
|
"https://pb33f.io/libopenapi": {
|
||||||
Get: &Operation{
|
Get: &Operation{
|
||||||
OperationId: "openaypeeeye",
|
OperationId: "openaypeeeye",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Extensions: map[string]any{
|
Extensions: map[string]any{
|
||||||
"x-burgers": "why not?",
|
"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.
|
// there is no way to determine order in brand new maps, so we have to check length.
|
||||||
assert.Len(t, rend, 152)
|
assert.Len(t, rend, 152)
|
||||||
|
|
||||||
// mutate
|
// mutate
|
||||||
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
|
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
|
||||||
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
|
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
|
||||||
|
|
||||||
rend, _ = cb.Render()
|
rend, _ = cb.Render()
|
||||||
// there is no way to determine order in brand new maps, so we have to check length.
|
// there is no way to determine order in brand new maps, so we have to check length.
|
||||||
assert.Len(t, rend, 153)
|
assert.Len(t, rend, 153)
|
||||||
|
|
||||||
k := `x-break-everything: please
|
k := `x-break-everything: please
|
||||||
'{$request.query.queryUrl}':
|
'{$request.query.queryUrl}':
|
||||||
post:
|
post:
|
||||||
description: Callback payload
|
description: Callback payload
|
||||||
@@ -58,19 +58,19 @@ func TestCallback_MarshalYAML(t *testing.T) {
|
|||||||
schema:
|
schema:
|
||||||
type: string`
|
type: string`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
err := yaml.Unmarshal([]byte(k), &idxNode)
|
err := yaml.Unmarshal([]byte(k), &idxNode)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
|
|
||||||
var n v3.Callback
|
var n v3.Callback
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = 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()
|
rend, _ = r.Render()
|
||||||
assert.Equal(t, k, strings.TrimSpace(string(rend)))
|
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
|
// 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.
|
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||||
type Components struct {
|
type Components struct {
|
||||||
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
|
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
|
||||||
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
|
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
|
||||||
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||||
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||||
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
|
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
|
||||||
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||||
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
|
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
|
||||||
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
|
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
|
||||||
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
|
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
|
||||||
Extensions map[string]any `json:"-" yaml:"-"`
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.Components
|
low *low.Components
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,53 +4,53 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestComponents_MarshalYAML(t *testing.T) {
|
func TestComponents_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
comp := &Components{
|
comp := &Components{
|
||||||
Responses: map[string]*Response{
|
Responses: map[string]*Response{
|
||||||
"200": {
|
"200": {
|
||||||
Description: "OK",
|
Description: "OK",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Parameters: map[string]*Parameter{
|
Parameters: map[string]*Parameter{
|
||||||
"id": {
|
"id": {
|
||||||
Name: "id",
|
Name: "id",
|
||||||
In: "path",
|
In: "path",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RequestBodies: map[string]*RequestBody{
|
RequestBodies: map[string]*RequestBody{
|
||||||
"body": {
|
"body": {
|
||||||
Content: map[string]*MediaType{
|
Content: map[string]*MediaType{
|
||||||
"application/json": {
|
"application/json": {
|
||||||
Example: "why?",
|
Example: "why?",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dat, _ := comp.Render()
|
dat, _ := comp.Render()
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal(dat, &idxNode)
|
_ = yaml.Unmarshal(dat, &idxNode)
|
||||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
|
|
||||||
var n v3.Components
|
var n v3.Components
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
r := NewComponents(&n)
|
r := NewComponents(&n)
|
||||||
|
|
||||||
desired := `responses:
|
desired := `responses:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
parameters:
|
parameters:
|
||||||
@@ -63,6 +63,6 @@ requestBodies:
|
|||||||
application/json:
|
application/json:
|
||||||
example: why?`
|
example: why?`
|
||||||
|
|
||||||
dat, _ = r.Render()
|
dat, _ = r.Render()
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Encoding represents an OpenAPI 3+ Encoding object
|
// Encoding represents an OpenAPI 3+ Encoding object
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||||
type Encoding struct {
|
type Encoding struct {
|
||||||
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
|
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
|
||||||
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
|
||||||
|
|||||||
@@ -4,64 +4,64 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEncoding_MarshalYAML(t *testing.T) {
|
func TestEncoding_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
explode := true
|
explode := true
|
||||||
encoding := &Encoding{
|
encoding := &Encoding{
|
||||||
ContentType: "application/json",
|
ContentType: "application/json",
|
||||||
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}},
|
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}},
|
||||||
Style: "simple",
|
Style: "simple",
|
||||||
Explode: &explode,
|
Explode: &explode,
|
||||||
}
|
}
|
||||||
|
|
||||||
rend, _ := encoding.Render()
|
rend, _ := encoding.Render()
|
||||||
|
|
||||||
desired := `contentType: application/json
|
desired := `contentType: application/json
|
||||||
headers:
|
headers:
|
||||||
x-pizza-time:
|
x-pizza-time:
|
||||||
description: oh yes please
|
description: oh yes please
|
||||||
style: simple
|
style: simple
|
||||||
explode: true`
|
explode: true`
|
||||||
|
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
explode = false
|
explode = false
|
||||||
encoding.Explode = &explode
|
encoding.Explode = &explode
|
||||||
rend, _ = encoding.Render()
|
rend, _ = encoding.Render()
|
||||||
|
|
||||||
desired = `contentType: application/json
|
desired = `contentType: application/json
|
||||||
headers:
|
headers:
|
||||||
x-pizza-time:
|
x-pizza-time:
|
||||||
description: oh yes please
|
description: oh yes please
|
||||||
style: simple`
|
style: simple`
|
||||||
|
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
encoding.Explode = nil
|
encoding.Explode = nil
|
||||||
rend, _ = encoding.Render()
|
rend, _ = encoding.Render()
|
||||||
|
|
||||||
desired = `contentType: application/json
|
desired = `contentType: application/json
|
||||||
headers:
|
headers:
|
||||||
x-pizza-time:
|
x-pizza-time:
|
||||||
description: oh yes please
|
description: oh yes please
|
||||||
style: simple`
|
style: simple`
|
||||||
|
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
encoding.Explode = &explode
|
encoding.Explode = &explode
|
||||||
rend, _ = encoding.Render()
|
rend, _ = encoding.Render()
|
||||||
|
|
||||||
desired = `contentType: application/json
|
desired = `contentType: application/json
|
||||||
headers:
|
headers:
|
||||||
x-pizza-time:
|
x-pizza-time:
|
||||||
description: oh yes please
|
description: oh yes please
|
||||||
style: simple`
|
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.
|
// Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
// - https://spec.openapis.org/oas/v3.1.0#header-object
|
||||||
type Header struct {
|
type Header struct {
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
|
|||||||
@@ -4,30 +4,30 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHeader_MarshalYAML(t *testing.T) {
|
func TestHeader_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
header := &Header{
|
header := &Header{
|
||||||
Description: "A header",
|
Description: "A header",
|
||||||
Required: true,
|
Required: true,
|
||||||
Deprecated: true,
|
Deprecated: true,
|
||||||
AllowEmptyValue: true,
|
AllowEmptyValue: true,
|
||||||
Style: "simple",
|
Style: "simple",
|
||||||
Explode: true,
|
Explode: true,
|
||||||
AllowReserved: true,
|
AllowReserved: true,
|
||||||
Example: "example",
|
Example: "example",
|
||||||
Examples: map[string]*base.Example{"example": {Value: "example"}},
|
Examples: map[string]*base.Example{"example": {Value: "example"}},
|
||||||
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
Extensions: map[string]interface{}{"x-burgers": "why not?"},
|
||||||
}
|
}
|
||||||
|
|
||||||
rend, _ := header.Render()
|
rend, _ := header.Render()
|
||||||
|
|
||||||
desired := `description: A header
|
desired := `description: A header
|
||||||
required: true
|
required: true
|
||||||
deprecated: true
|
deprecated: true
|
||||||
allowEmptyValue: true
|
allowEmptyValue: true
|
||||||
@@ -40,6 +40,6 @@ examples:
|
|||||||
value: example
|
value: example
|
||||||
x-burgers: why not?`
|
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
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLink_MarshalYAML(t *testing.T) {
|
func TestLink_MarshalYAML(t *testing.T) {
|
||||||
link := Link{
|
link := Link{
|
||||||
OperationRef: "somewhere",
|
OperationRef: "somewhere",
|
||||||
OperationId: "somewhereOutThere",
|
OperationId: "somewhereOutThere",
|
||||||
Parameters: map[string]string{
|
Parameters: map[string]string{
|
||||||
"over": "theRainbow",
|
"over": "theRainbow",
|
||||||
},
|
},
|
||||||
RequestBody: "hello?",
|
RequestBody: "hello?",
|
||||||
Description: "are you there?",
|
Description: "are you there?",
|
||||||
Server: &Server{
|
Server: &Server{
|
||||||
URL: "https://pb33f.io",
|
URL: "https://pb33f.io",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
dat, _ := link.Render()
|
dat, _ := link.Render()
|
||||||
desired := `operationRef: somewhere
|
desired := `operationRef: somewhere
|
||||||
operationId: somewhereOutThere
|
operationId: somewhereOutThere
|
||||||
parameters:
|
parameters:
|
||||||
over: theRainbow
|
over: theRainbow
|
||||||
@@ -33,5 +33,5 @@ description: are you there?
|
|||||||
server:
|
server:
|
||||||
url: https://pb33f.io`
|
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.
|
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
||||||
type OAuthFlow struct {
|
type OAuthFlow struct {
|
||||||
AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
|
AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
|
||||||
TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
|
TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
|
||||||
|
|||||||
@@ -4,42 +4,42 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOAuthFlow_MarshalYAML(t *testing.T) {
|
func TestOAuthFlow_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
oflow := &OAuthFlow{
|
oflow := &OAuthFlow{
|
||||||
AuthorizationUrl: "https://pb33f.io",
|
AuthorizationUrl: "https://pb33f.io",
|
||||||
TokenUrl: "https://pb33f.io/token",
|
TokenUrl: "https://pb33f.io/token",
|
||||||
RefreshUrl: "https://pb33f.io/refresh",
|
RefreshUrl: "https://pb33f.io/refresh",
|
||||||
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"},
|
Scopes: 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
|
tokenUrl: https://pb33f.io/token
|
||||||
refreshUrl: https://pb33f.io/refresh
|
refreshUrl: https://pb33f.io/refresh
|
||||||
scopes:
|
scopes:
|
||||||
chicken: nuggets
|
chicken: nuggets
|
||||||
beefy: soup`
|
beefy: soup`
|
||||||
|
|
||||||
// we can't check for equality, as the scopes map will be randomly ordered when created from scratch.
|
// we can't check for equality, as the scopes map will be randomly ordered when created from scratch.
|
||||||
assert.Len(t, desired, 149)
|
assert.Len(t, desired, 149)
|
||||||
|
|
||||||
// mutate
|
// mutate
|
||||||
oflow.Scopes = nil
|
oflow.Scopes = nil
|
||||||
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
|
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
|
||||||
|
|
||||||
desired = `authorizationUrl: https://pb33f.io
|
desired = `authorizationUrl: https://pb33f.io
|
||||||
tokenUrl: https://pb33f.io/token
|
tokenUrl: https://pb33f.io/token
|
||||||
refreshUrl: https://pb33f.io/refresh
|
refreshUrl: https://pb33f.io/refresh
|
||||||
x-burgers: why not?`
|
x-burgers: why not?`
|
||||||
|
|
||||||
rend, _ = oflow.Render()
|
rend, _ = oflow.Render()
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
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.
|
// 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 {
|
type OAuthFlows struct {
|
||||||
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
|
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
|
||||||
Password *OAuthFlow `json:"password,omitempty" yaml:"password,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)
|
nb := high.NewNodeBuilder(o, o.low)
|
||||||
return nb.Render(), nil
|
return nb.Render(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func TestOperation_MarshalYAML(t *testing.T) {
|
|||||||
},
|
},
|
||||||
OperationId: "slice",
|
OperationId: "slice",
|
||||||
Parameters: []*Parameter{
|
Parameters: []*Parameter{
|
||||||
&Parameter{
|
{
|
||||||
Name: "mice",
|
Name: "mice",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -103,7 +103,7 @@ func TestOperation_MarshalYAMLInline(t *testing.T) {
|
|||||||
},
|
},
|
||||||
OperationId: "slice",
|
OperationId: "slice",
|
||||||
Parameters: []*Parameter{
|
Parameters: []*Parameter{
|
||||||
&Parameter{
|
{
|
||||||
Name: "mice",
|
Name: "mice",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery.
|
// 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.
|
// 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.
|
// 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 SecurityScheme struct {
|
||||||
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
|
|||||||
@@ -4,44 +4,44 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSecurityScheme_MarshalYAML(t *testing.T) {
|
func TestSecurityScheme_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
ss := &SecurityScheme{
|
ss := &SecurityScheme{
|
||||||
Type: "apiKey",
|
Type: "apiKey",
|
||||||
Description: "this is a description",
|
Description: "this is a description",
|
||||||
Name: "superSecret",
|
Name: "superSecret",
|
||||||
In: "header",
|
In: "header",
|
||||||
Scheme: "https",
|
Scheme: "https",
|
||||||
}
|
}
|
||||||
|
|
||||||
dat, _ := ss.Render()
|
dat, _ := ss.Render()
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal(dat, &idxNode)
|
_ = yaml.Unmarshal(dat, &idxNode)
|
||||||
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
|
|
||||||
var n v3.SecurityScheme
|
var n v3.SecurityScheme
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = 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
|
description: this is a description
|
||||||
name: superSecret
|
name: superSecret
|
||||||
in: header
|
in: header
|
||||||
scheme: https`
|
scheme: https`
|
||||||
|
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,32 +4,32 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServer_MarshalYAML(t *testing.T) {
|
func TestServer_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
server := &Server{
|
server := &Server{
|
||||||
URL: "https://pb33f.io",
|
URL: "https://pb33f.io",
|
||||||
Description: "the b33f",
|
Description: "the b33f",
|
||||||
}
|
}
|
||||||
|
|
||||||
desired := `url: https://pb33f.io
|
desired := `url: https://pb33f.io
|
||||||
description: the b33f`
|
description: the b33f`
|
||||||
|
|
||||||
rend, _ := server.Render()
|
rend, _ := server.Render()
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
// mutate
|
// mutate
|
||||||
server.Variables = map[string]*ServerVariable{
|
server.Variables = map[string]*ServerVariable{
|
||||||
"rainbow": {
|
"rainbow": {
|
||||||
Enum: []string{"one", "two", "three"},
|
Enum: []string{"one", "two", "three"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
desired = `url: https://pb33f.io
|
desired = `url: https://pb33f.io
|
||||||
description: the b33f
|
description: the b33f
|
||||||
variables:
|
variables:
|
||||||
rainbow:
|
rainbow:
|
||||||
@@ -38,6 +38,6 @@ variables:
|
|||||||
- two
|
- two
|
||||||
- three`
|
- three`
|
||||||
|
|
||||||
rend, _ = server.Render()
|
rend, _ = server.Render()
|
||||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,40 +4,40 @@
|
|||||||
package v3
|
package v3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServerVariable_MarshalYAML(t *testing.T) {
|
func TestServerVariable_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
svar := &ServerVariable{
|
svar := &ServerVariable{
|
||||||
Enum: []string{"one", "two", "three"},
|
Enum: []string{"one", "two", "three"},
|
||||||
Description: "money day",
|
Description: "money day",
|
||||||
}
|
}
|
||||||
|
|
||||||
desired := `enum:
|
desired := `enum:
|
||||||
- one
|
- one
|
||||||
- two
|
- two
|
||||||
- three
|
- three
|
||||||
description: money day`
|
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
|
- one
|
||||||
- two
|
- two
|
||||||
- three
|
- three
|
||||||
default: is moments away
|
default: is moments away
|
||||||
description: money day`
|
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
|
// 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.
|
// that all the latest features are collected, without damaging backwards compatibility.
|
||||||
package base
|
package base
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Contact represents a low-level representation of the Contact definitions found at
|
// 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 {
|
type Contact struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
URL low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
@@ -42,4 +43,3 @@ func (c *Contact) Hash() [32]byte {
|
|||||||
}
|
}
|
||||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
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.
|
// 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.
|
// 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 {
|
type Discriminator struct {
|
||||||
PropertyName low.NodeReference[string]
|
PropertyName low.NodeReference[string]
|
||||||
Mapping low.NodeReference[map[low.KeyReference[string]]low.ValueReference[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+
|
// 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 {
|
type Example struct {
|
||||||
Summary low.NodeReference[string]
|
Summary low.NodeReference[string]
|
||||||
Description 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
|
// ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3
|
||||||
//
|
//
|
||||||
// Allows referencing an external resource for extended documentation.
|
// 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 {
|
type ExternalDoc struct {
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
URL 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
|
// 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 {
|
type License struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
URL 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).
|
// 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
|
// 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
|
||||||
// - https://swagger.io/specification/#security-requirement-object
|
// - https://swagger.io/specification/#security-requirement-object
|
||||||
type SecurityRequirement struct {
|
type SecurityRequirement struct {
|
||||||
Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
|
Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
|
||||||
*low.Reference
|
*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
|
// 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.
|
// tag defined in the Operation Object instances.
|
||||||
// - v2: https://swagger.io/specification/v2/#tagObject
|
// - v2: https://swagger.io/specification/v2/#tagObject
|
||||||
// - v3: https://swagger.io/specification/#tag-object
|
// - v3: https://swagger.io/specification/#tag-object
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
|
|||||||
@@ -84,4 +84,4 @@ x-b33f: princess`
|
|||||||
|
|
||||||
assert.Equal(t, lDoc.Hash(), rDoc.Hash())
|
assert.Equal(t, lDoc.Hash(), rDoc.Hash())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,9 @@ import (
|
|||||||
//
|
//
|
||||||
// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be
|
// 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.
|
// 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 {
|
type XML struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
Namespace low.NodeReference[string]
|
Namespace low.NodeReference[string]
|
||||||
|
|||||||
@@ -1024,7 +1024,7 @@ one:
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Len(t, things, 1)
|
assert.Len(t, things, 1)
|
||||||
|
|
||||||
for k, _ := range things {
|
for k := range things {
|
||||||
assert.Equal(t, "one", k.Value)
|
assert.Equal(t, "one", k.Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
package low
|
package low
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BuildModel accepts a yaml.Node pointer and a model, which can be any struct. Using reflection, the model is
|
// 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.
|
// BuildModel is non-recursive and will only build out a single layer of the node tree.
|
||||||
func BuildModel(node *yaml.Node, model interface{}) error {
|
func BuildModel(node *yaml.Node, model interface{}) error {
|
||||||
if node == nil {
|
if node == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
|
||||||
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
|
||||||
}
|
}
|
||||||
v := reflect.ValueOf(model).Elem()
|
v := reflect.ValueOf(model).Elem()
|
||||||
num := v.NumField()
|
num := v.NumField()
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
|
|
||||||
fName := v.Type().Field(i).Name
|
fName := v.Type().Field(i).Name
|
||||||
|
|
||||||
if fName == "Extensions" {
|
if fName == "Extensions" {
|
||||||
continue // internal construct
|
continue // internal construct
|
||||||
}
|
}
|
||||||
|
|
||||||
if fName == "PathItems" {
|
if fName == "PathItems" {
|
||||||
continue // internal construct
|
continue // internal construct
|
||||||
}
|
}
|
||||||
|
|
||||||
kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content)
|
kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content)
|
||||||
if vn == nil {
|
if vn == nil {
|
||||||
// no point in going on.
|
// no point in going on.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
field := v.FieldByName(fName)
|
field := v.FieldByName(fName)
|
||||||
kind := field.Kind()
|
kind := field.Kind()
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
|
||||||
err := SetField(&field, vn, kn)
|
err := SetField(&field, vn, kn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unable to parse unsupported type: %v", kind)
|
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
|
// 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
|
// 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.
|
// for low-level models, it has no value to high-level ones.
|
||||||
func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
|
func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
|
||||||
if valueNode == nil {
|
if valueNode == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch field.Type() {
|
switch field.Type() {
|
||||||
|
|
||||||
case reflect.TypeOf(map[string]NodeReference[any]{}):
|
case reflect.TypeOf(map[string]NodeReference[any]{}):
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[string]NodeReference[any])
|
items := make(map[string]NodeReference[any])
|
||||||
var currentLabel string
|
var currentLabel string
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
currentLabel = sliceItem.Value
|
currentLabel = sliceItem.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var decoded map[string]interface{}
|
var decoded map[string]interface{}
|
||||||
// I cannot think of a way to make this error out by this point.
|
// I cannot think of a way to make this error out by this point.
|
||||||
_ = sliceItem.Decode(&decoded)
|
_ = sliceItem.Decode(&decoded)
|
||||||
items[currentLabel] = NodeReference[any]{
|
items[currentLabel] = NodeReference[any]{
|
||||||
Value: decoded,
|
Value: decoded,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(map[string]NodeReference[string]{}):
|
case reflect.TypeOf(map[string]NodeReference[string]{}):
|
||||||
|
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[string]NodeReference[string])
|
items := make(map[string]NodeReference[string])
|
||||||
var currentLabel string
|
var currentLabel string
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
currentLabel = sliceItem.Value
|
currentLabel = sliceItem.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items[currentLabel] = NodeReference[string]{
|
items[currentLabel] = NodeReference[string]{
|
||||||
Value: fmt.Sprintf("%v", sliceItem.Value),
|
Value: fmt.Sprintf("%v", sliceItem.Value),
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[any]{}):
|
case reflect.TypeOf(NodeReference[any]{}):
|
||||||
|
|
||||||
var decoded interface{}
|
var decoded interface{}
|
||||||
_ = valueNode.Decode(&decoded)
|
_ = valueNode.Decode(&decoded)
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
|
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
|
||||||
field.Set(reflect.ValueOf(or))
|
field.Set(reflect.ValueOf(or))
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[any]{}):
|
case reflect.TypeOf([]NodeReference[any]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[any]
|
var items []NodeReference[any]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
var decoded map[string]interface{}
|
var decoded map[string]interface{}
|
||||||
err := sliceItem.Decode(&decoded)
|
err := sliceItem.Decode(&decoded)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
items = append(items, NodeReference[any]{
|
items = append(items, NodeReference[any]{
|
||||||
Value: decoded,
|
Value: decoded,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[string]{}):
|
case reflect.TypeOf(NodeReference[string]{}):
|
||||||
|
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
nr := NodeReference[string]{
|
nr := NodeReference[string]{
|
||||||
Value: fmt.Sprintf("%v", valueNode.Value),
|
Value: fmt.Sprintf("%v", valueNode.Value),
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(ValueReference[string]{}):
|
case reflect.TypeOf(ValueReference[string]{}):
|
||||||
|
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
nr := ValueReference[string]{
|
nr := ValueReference[string]{
|
||||||
Value: fmt.Sprintf("%v", valueNode.Value),
|
Value: fmt.Sprintf("%v", valueNode.Value),
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[bool]{}):
|
case reflect.TypeOf(NodeReference[bool]{}):
|
||||||
|
|
||||||
if utils.IsNodeBoolValue(valueNode) {
|
if utils.IsNodeBoolValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
bv, _ := strconv.ParseBool(valueNode.Value)
|
bv, _ := strconv.ParseBool(valueNode.Value)
|
||||||
nr := NodeReference[bool]{
|
nr := NodeReference[bool]{
|
||||||
Value: bv,
|
Value: bv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[int]{}):
|
case reflect.TypeOf(NodeReference[int]{}):
|
||||||
|
|
||||||
if utils.IsNodeIntValue(valueNode) {
|
if utils.IsNodeIntValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.Atoi(valueNode.Value)
|
fv, _ := strconv.Atoi(valueNode.Value)
|
||||||
nr := NodeReference[int]{
|
nr := NodeReference[int]{
|
||||||
Value: fv,
|
Value: fv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[int64]{}):
|
case reflect.TypeOf(NodeReference[int64]{}):
|
||||||
|
|
||||||
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) {
|
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
|
||||||
nr := NodeReference[int64]{
|
nr := NodeReference[int64]{
|
||||||
Value: fv,
|
Value: fv,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(nr))
|
field.Set(reflect.ValueOf(nr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[float32]{}):
|
case reflect.TypeOf(NodeReference[float32]{}):
|
||||||
|
|
||||||
if utils.IsNodeNumberValue(valueNode) {
|
if utils.IsNodeNumberValue(valueNode) {
|
||||||
if field.CanSet() {
|
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 utils.IsNodeNumberValue(valueNode) {
|
||||||
if field.CanSet() {
|
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 utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[string]
|
var items []NodeReference[string]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
items = append(items, NodeReference[string]{
|
items = append(items, NodeReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[float32]{}):
|
case reflect.TypeOf([]NodeReference[float32]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[float32]
|
var items []NodeReference[float32]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
|
||||||
items = append(items, NodeReference[float32]{
|
items = append(items, NodeReference[float32]{
|
||||||
Value: float32(fv),
|
Value: float32(fv),
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[float64]{}):
|
case reflect.TypeOf([]NodeReference[float64]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[float64]
|
var items []NodeReference[float64]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
|
||||||
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[int]{}):
|
case reflect.TypeOf([]NodeReference[int]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[int]
|
var items []NodeReference[int]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
iv, _ := strconv.Atoi(sliceItem.Value)
|
iv, _ := strconv.Atoi(sliceItem.Value)
|
||||||
items = append(items, NodeReference[int]{
|
items = append(items, NodeReference[int]{
|
||||||
Value: iv,
|
Value: iv,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[int64]{}):
|
case reflect.TypeOf([]NodeReference[int64]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[int64]
|
var items []NodeReference[int64]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||||
items = append(items, NodeReference[int64]{
|
items = append(items, NodeReference[int64]{
|
||||||
Value: iv,
|
Value: iv,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf([]NodeReference[bool]{}):
|
case reflect.TypeOf([]NodeReference[bool]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []NodeReference[bool]
|
var items []NodeReference[bool]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
bv, _ := strconv.ParseBool(sliceItem.Value)
|
bv, _ := strconv.ParseBool(sliceItem.Value)
|
||||||
items = append(items, NodeReference[bool]{
|
items = append(items, NodeReference[bool]{
|
||||||
Value: bv,
|
Value: bv,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
KeyNode: valueNode,
|
KeyNode: valueNode,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
field.Set(reflect.ValueOf(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// helper for unpacking string maps.
|
// helper for unpacking string maps.
|
||||||
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
|
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
|
||||||
|
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[KeyReference[string]]ValueReference[string])
|
items := make(map[KeyReference[string]]ValueReference[string])
|
||||||
var cf *yaml.Node
|
var cf *yaml.Node
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
cf = sliceItem
|
cf = sliceItem
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items[KeyReference[string]{
|
items[KeyReference[string]{
|
||||||
Value: cf.Value,
|
Value: cf.Value,
|
||||||
KeyNode: cf,
|
KeyNode: cf,
|
||||||
}] = ValueReference[string]{
|
}] = ValueReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(items))
|
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 utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[KeyReference[string]]ValueReference[string])
|
items := make(map[KeyReference[string]]ValueReference[string])
|
||||||
var cf *yaml.Node
|
var cf *yaml.Node
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
cf = sliceItem
|
cf = sliceItem
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items[KeyReference[string]{
|
items[KeyReference[string]{
|
||||||
Value: cf.Value,
|
Value: cf.Value,
|
||||||
KeyNode: cf,
|
KeyNode: cf,
|
||||||
}] = ValueReference[string]{
|
}] = ValueReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{
|
ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{
|
||||||
Value: items,
|
Value: items,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(ref))
|
field.Set(reflect.ValueOf(ref))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}):
|
case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}):
|
||||||
if utils.IsNodeMap(valueNode) {
|
if utils.IsNodeMap(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
items := make(map[KeyReference[string]]ValueReference[string])
|
items := make(map[KeyReference[string]]ValueReference[string])
|
||||||
var cf *yaml.Node
|
var cf *yaml.Node
|
||||||
for i, sliceItem := range valueNode.Content {
|
for i, sliceItem := range valueNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
cf = sliceItem
|
cf = sliceItem
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items[KeyReference[string]{
|
items[KeyReference[string]{
|
||||||
Value: cf.Value,
|
Value: cf.Value,
|
||||||
KeyNode: cf,
|
KeyNode: cf,
|
||||||
}] = ValueReference[string]{
|
}] = ValueReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{
|
ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{
|
||||||
Value: items,
|
Value: items,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(ref))
|
field.Set(reflect.ValueOf(ref))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
|
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []ValueReference[string]
|
var items []ValueReference[string]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
items = append(items, ValueReference[string]{
|
items = append(items, ValueReference[string]{
|
||||||
Value: sliceItem.Value,
|
Value: sliceItem.Value,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
n := NodeReference[[]ValueReference[string]]{
|
n := NodeReference[[]ValueReference[string]]{
|
||||||
Value: items,
|
Value: items,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(n))
|
field.Set(reflect.ValueOf(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
|
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
|
||||||
|
|
||||||
if utils.IsNodeArray(valueNode) {
|
if utils.IsNodeArray(valueNode) {
|
||||||
if field.CanSet() {
|
if field.CanSet() {
|
||||||
var items []ValueReference[any]
|
var items []ValueReference[any]
|
||||||
for _, sliceItem := range valueNode.Content {
|
for _, sliceItem := range valueNode.Content {
|
||||||
|
|
||||||
var val any
|
var val any
|
||||||
if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) {
|
if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) {
|
||||||
if utils.IsNodeIntValue(sliceItem) {
|
if utils.IsNodeIntValue(sliceItem) {
|
||||||
val, _ = strconv.ParseInt(sliceItem.Value, 10, 64)
|
val, _ = strconv.ParseInt(sliceItem.Value, 10, 64)
|
||||||
} else {
|
} else {
|
||||||
val, _ = strconv.ParseFloat(sliceItem.Value, 64)
|
val, _ = strconv.ParseFloat(sliceItem.Value, 64)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if utils.IsNodeBoolValue(sliceItem) {
|
if utils.IsNodeBoolValue(sliceItem) {
|
||||||
val, _ = strconv.ParseBool(sliceItem.Value)
|
val, _ = strconv.ParseBool(sliceItem.Value)
|
||||||
}
|
}
|
||||||
if utils.IsNodeStringValue(sliceItem) {
|
if utils.IsNodeStringValue(sliceItem) {
|
||||||
val = sliceItem.Value
|
val = sliceItem.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
items = append(items, ValueReference[any]{
|
items = append(items, ValueReference[any]{
|
||||||
Value: val,
|
Value: val,
|
||||||
ValueNode: sliceItem,
|
ValueNode: sliceItem,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
n := NodeReference[[]ValueReference[any]]{
|
n := NodeReference[[]ValueReference[any]]{
|
||||||
Value: items,
|
Value: items,
|
||||||
KeyNode: keyNode,
|
KeyNode: keyNode,
|
||||||
ValueNode: valueNode,
|
ValueNode: valueNode,
|
||||||
}
|
}
|
||||||
field.Set(reflect.ValueOf(n))
|
field.Set(reflect.ValueOf(n))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// we want to ignore everything else, each model handles its own complex types.
|
// we want to ignore everything else, each model handles its own complex types.
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildModelAsync is a convenience function for calling BuildModel from a goroutine, requires a sync.WaitGroup
|
// 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) {
|
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
|
||||||
if n != nil {
|
if n != nil {
|
||||||
err := BuildModel(n, model)
|
err := BuildModel(n, model)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
*errors = append(*errors, err)
|
*errors = append(*errors, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lwg.Done()
|
lwg.Done()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,53 +1,53 @@
|
|||||||
package low
|
package low
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type hotdog struct {
|
type hotdog struct {
|
||||||
Name NodeReference[string]
|
Name NodeReference[string]
|
||||||
ValueName ValueReference[string]
|
ValueName ValueReference[string]
|
||||||
Fat NodeReference[int]
|
Fat NodeReference[int]
|
||||||
Ketchup NodeReference[float32]
|
Ketchup NodeReference[float32]
|
||||||
Mustard NodeReference[float64]
|
Mustard NodeReference[float64]
|
||||||
Grilled NodeReference[bool]
|
Grilled NodeReference[bool]
|
||||||
MaxTemp NodeReference[int]
|
MaxTemp NodeReference[int]
|
||||||
MaxTempHigh NodeReference[int64]
|
MaxTempHigh NodeReference[int64]
|
||||||
MaxTempAlt []NodeReference[int]
|
MaxTempAlt []NodeReference[int]
|
||||||
Drinks []NodeReference[string]
|
Drinks []NodeReference[string]
|
||||||
Sides []NodeReference[float32]
|
Sides []NodeReference[float32]
|
||||||
BigSides []NodeReference[float64]
|
BigSides []NodeReference[float64]
|
||||||
Temps []NodeReference[int]
|
Temps []NodeReference[int]
|
||||||
HighTemps []NodeReference[int64]
|
HighTemps []NodeReference[int64]
|
||||||
Buns []NodeReference[bool]
|
Buns []NodeReference[bool]
|
||||||
UnknownElements NodeReference[any]
|
UnknownElements NodeReference[any]
|
||||||
LotsOfUnknowns []NodeReference[any]
|
LotsOfUnknowns []NodeReference[any]
|
||||||
Where map[string]NodeReference[any]
|
Where map[string]NodeReference[any]
|
||||||
There map[string]NodeReference[string]
|
There map[string]NodeReference[string]
|
||||||
AllTheThings NodeReference[map[KeyReference[string]]ValueReference[string]]
|
AllTheThings NodeReference[map[KeyReference[string]]ValueReference[string]]
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModel_Mismatch(t *testing.T) {
|
func TestBuildModel_Mismatch(t *testing.T) {
|
||||||
|
|
||||||
yml := `crisps: are tasty`
|
yml := `crisps: are tasty`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
hd := hotdog{}
|
hd := hotdog{}
|
||||||
cErr := BuildModel(&rootNode, &hd)
|
cErr := BuildModel(&rootNode, &hd)
|
||||||
assert.NoError(t, cErr)
|
assert.NoError(t, cErr)
|
||||||
assert.Empty(t, hd.Name)
|
assert.Empty(t, hd.Name)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModel(t *testing.T) {
|
func TestBuildModel(t *testing.T) {
|
||||||
|
|
||||||
yml := `name: yummy
|
yml := `name: yummy
|
||||||
valueName: yammy
|
valueName: yammy
|
||||||
beef: true
|
beef: true
|
||||||
fat: 200
|
fat: 200
|
||||||
@@ -105,347 +105,347 @@ allTheThings:
|
|||||||
beer: isGood
|
beer: isGood
|
||||||
cake: isNice`
|
cake: isNice`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
hd := hotdog{}
|
hd := hotdog{}
|
||||||
cErr := BuildModel(rootNode.Content[0], &hd)
|
cErr := BuildModel(rootNode.Content[0], &hd)
|
||||||
assert.Equal(t, 200, hd.Fat.Value)
|
assert.Equal(t, 200, hd.Fat.Value)
|
||||||
assert.Equal(t, 4, hd.Fat.ValueNode.Line)
|
assert.Equal(t, 4, hd.Fat.ValueNode.Line)
|
||||||
assert.Equal(t, true, hd.Grilled.Value)
|
assert.Equal(t, true, hd.Grilled.Value)
|
||||||
assert.Equal(t, "yummy", hd.Name.Value)
|
assert.Equal(t, "yummy", hd.Name.Value)
|
||||||
assert.Equal(t, "yammy", hd.ValueName.Value)
|
assert.Equal(t, "yammy", hd.ValueName.Value)
|
||||||
assert.Equal(t, float32(200.45), hd.Ketchup.Value)
|
assert.Equal(t, float32(200.45), hd.Ketchup.Value)
|
||||||
assert.Len(t, hd.Drinks, 3)
|
assert.Len(t, hd.Drinks, 3)
|
||||||
assert.Len(t, hd.Sides, 4)
|
assert.Len(t, hd.Sides, 4)
|
||||||
assert.Len(t, hd.BigSides, 4)
|
assert.Len(t, hd.BigSides, 4)
|
||||||
assert.Len(t, hd.Temps, 2)
|
assert.Len(t, hd.Temps, 2)
|
||||||
assert.Len(t, hd.HighTemps, 2)
|
assert.Len(t, hd.HighTemps, 2)
|
||||||
assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value)
|
assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value)
|
||||||
assert.Len(t, hd.MaxTempAlt, 5)
|
assert.Len(t, hd.MaxTempAlt, 5)
|
||||||
assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value)
|
assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value)
|
||||||
assert.Equal(t, 2, hd.Temps[1].Value)
|
assert.Equal(t, 2, hd.Temps[1].Value)
|
||||||
assert.Equal(t, 27, hd.Temps[1].ValueNode.Line)
|
assert.Equal(t, 27, hd.Temps[1].ValueNode.Line)
|
||||||
assert.Len(t, hd.UnknownElements.Value, 2)
|
assert.Len(t, hd.UnknownElements.Value, 2)
|
||||||
assert.Len(t, hd.LotsOfUnknowns, 3)
|
assert.Len(t, hd.LotsOfUnknowns, 3)
|
||||||
assert.Len(t, hd.Where, 2)
|
assert.Len(t, hd.Where, 2)
|
||||||
assert.Len(t, hd.There, 2)
|
assert.Len(t, hd.There, 2)
|
||||||
assert.Equal(t, "bear", hd.There["care"].Value)
|
assert.Equal(t, "bear", hd.There["care"].Value)
|
||||||
assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
|
assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
|
||||||
|
|
||||||
allTheThings := hd.AllTheThings.Value
|
allTheThings := hd.AllTheThings.Value
|
||||||
for i := range allTheThings {
|
for i := range allTheThings {
|
||||||
if i.Value == "beer" {
|
if i.Value == "beer" {
|
||||||
assert.Equal(t, "isGood", allTheThings[i].Value)
|
assert.Equal(t, "isGood", allTheThings[i].Value)
|
||||||
}
|
}
|
||||||
if i.Value == "cake" {
|
if i.Value == "cake" {
|
||||||
assert.Equal(t, "isNice", allTheThings[i].Value)
|
assert.Equal(t, "isNice", allTheThings[i].Value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.NoError(t, cErr)
|
assert.NoError(t, cErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModel_UseCopyNotRef(t *testing.T) {
|
func TestBuildModel_UseCopyNotRef(t *testing.T) {
|
||||||
|
|
||||||
yml := `cake: -99999`
|
yml := `cake: -99999`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
hd := hotdog{}
|
hd := hotdog{}
|
||||||
cErr := BuildModel(&rootNode, hd)
|
cErr := BuildModel(&rootNode, hd)
|
||||||
assert.Error(t, cErr)
|
assert.Error(t, cErr)
|
||||||
assert.Empty(t, hd.Name)
|
assert.Empty(t, hd.Name)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModel_UseUnsupportedPrimitive(t *testing.T) {
|
func TestBuildModel_UseUnsupportedPrimitive(t *testing.T) {
|
||||||
|
|
||||||
type notSupported struct {
|
type notSupported struct {
|
||||||
cake string
|
cake string
|
||||||
}
|
}
|
||||||
ns := notSupported{}
|
ns := notSupported{}
|
||||||
yml := `cake: party`
|
yml := `cake: party`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
cErr := BuildModel(rootNode.Content[0], &ns)
|
cErr := BuildModel(rootNode.Content[0], &ns)
|
||||||
assert.Error(t, cErr)
|
assert.Error(t, cErr)
|
||||||
assert.Empty(t, ns.cake)
|
assert.Empty(t, ns.cake)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModel_UsingInternalConstructs(t *testing.T) {
|
func TestBuildModel_UsingInternalConstructs(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Extensions NodeReference[string]
|
Extensions NodeReference[string]
|
||||||
PathItems NodeReference[string]
|
PathItems NodeReference[string]
|
||||||
Thing NodeReference[string]
|
Thing NodeReference[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `extensions: one
|
yml := `extensions: one
|
||||||
pathItems: two
|
pathItems: two
|
||||||
thing: yeah`
|
thing: yeah`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
// try a null build
|
// try a null build
|
||||||
try := BuildModel(nil, ins)
|
try := BuildModel(nil, ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
|
|
||||||
cErr := BuildModel(rootNode.Content[0], ins)
|
cErr := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, cErr)
|
assert.NoError(t, cErr)
|
||||||
assert.Empty(t, ins.PathItems.Value)
|
assert.Empty(t, ins.PathItems.Value)
|
||||||
assert.Empty(t, ins.Extensions.Value)
|
assert.Empty(t, ins.Extensions.Value)
|
||||||
assert.Equal(t, "yeah", ins.Thing.Value)
|
assert.Equal(t, "yeah", ins.Thing.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_NodeRefAny_Error(t *testing.T) {
|
func TestSetField_NodeRefAny_Error(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing []NodeReference[any]
|
Thing []NodeReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- 999
|
- 999
|
||||||
- false`
|
- false`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.Error(t, try)
|
assert.Error(t, try)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_MapHelperWrapped(t *testing.T) {
|
func TestSetField_MapHelperWrapped(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
what: not
|
what: not
|
||||||
chip: chop
|
chip: chop
|
||||||
lip: lop`
|
lip: lop`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Len(t, ins.Thing.Value, 3)
|
assert.Len(t, ins.Thing.Value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_MapHelper(t *testing.T) {
|
func TestSetField_MapHelper(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing map[KeyReference[string]]ValueReference[string]
|
Thing map[KeyReference[string]]ValueReference[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
what: not
|
what: not
|
||||||
chip: chop
|
chip: chop
|
||||||
lip: lop`
|
lip: lop`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Len(t, ins.Thing, 3)
|
assert.Len(t, ins.Thing, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_ArrayHelper(t *testing.T) {
|
func TestSetField_ArrayHelper(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing NodeReference[[]ValueReference[string]]
|
Thing NodeReference[[]ValueReference[string]]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- nice
|
- nice
|
||||||
- rice
|
- rice
|
||||||
- slice`
|
- slice`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Len(t, ins.Thing.Value, 3)
|
assert.Len(t, ins.Thing.Value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_Enum_Helper(t *testing.T) {
|
func TestSetField_Enum_Helper(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing NodeReference[[]ValueReference[any]]
|
Thing NodeReference[[]ValueReference[any]]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- nice
|
- nice
|
||||||
- rice
|
- rice
|
||||||
- slice`
|
- slice`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Len(t, ins.Thing.Value, 3)
|
assert.Len(t, ins.Thing.Value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_Default_Helper(t *testing.T) {
|
func TestSetField_Default_Helper(t *testing.T) {
|
||||||
|
|
||||||
type cake struct {
|
type cake struct {
|
||||||
thing int
|
thing int
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be ignored, no custom objects in here my friend.
|
// this should be ignored, no custom objects in here my friend.
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing cake
|
Thing cake
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
type: cake`
|
type: cake`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Equal(t, 0, ins.Thing.thing)
|
assert.Equal(t, 0, ins.Thing.thing)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleSlicesOfInts(t *testing.T) {
|
func TestHandleSlicesOfInts(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing NodeReference[[]ValueReference[any]]
|
Thing NodeReference[[]ValueReference[any]]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- 5
|
- 5
|
||||||
- 1.234`
|
- 1.234`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Equal(t, int64(5), ins.Thing.Value[0].Value)
|
assert.Equal(t, int64(5), ins.Thing.Value[0].Value)
|
||||||
assert.Equal(t, 1.234, ins.Thing.Value[1].Value)
|
assert.Equal(t, 1.234, ins.Thing.Value[1].Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleSlicesOfBools(t *testing.T) {
|
func TestHandleSlicesOfBools(t *testing.T) {
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing NodeReference[[]ValueReference[any]]
|
Thing NodeReference[[]ValueReference[any]]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- true
|
- true
|
||||||
- false`
|
- false`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(rootNode.Content[0], ins)
|
try := BuildModel(rootNode.Content[0], ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Equal(t, true, ins.Thing.Value[0].Value)
|
assert.Equal(t, true, ins.Thing.Value[0].Value)
|
||||||
assert.Equal(t, false, ins.Thing.Value[1].Value)
|
assert.Equal(t, false, ins.Thing.Value[1].Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSetField_Ignore(t *testing.T) {
|
func TestSetField_Ignore(t *testing.T) {
|
||||||
|
|
||||||
type Complex struct {
|
type Complex struct {
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing *Complex
|
Thing *Complex
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- nice
|
- nice
|
||||||
- rice
|
- rice
|
||||||
- slice`
|
- slice`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
try := BuildModel(&rootNode, ins)
|
try := BuildModel(&rootNode, ins)
|
||||||
assert.NoError(t, try)
|
assert.NoError(t, try)
|
||||||
assert.Nil(t, ins.Thing)
|
assert.Nil(t, ins.Thing)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModelAsync(t *testing.T) {
|
func TestBuildModelAsync(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
what: not
|
what: not
|
||||||
chip: chop
|
chip: chop
|
||||||
lip: lop`
|
lip: lop`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var errors []error
|
var errors []error
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
|
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
assert.Len(t, ins.Thing.Value, 3)
|
assert.Len(t, ins.Thing.Value, 3)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuildModelAsync_Error(t *testing.T) {
|
func TestBuildModelAsync_Error(t *testing.T) {
|
||||||
|
|
||||||
type internal struct {
|
type internal struct {
|
||||||
Thing []NodeReference[any]
|
Thing []NodeReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
yml := `thing:
|
yml := `thing:
|
||||||
- 999
|
- 999
|
||||||
- false`
|
- false`
|
||||||
|
|
||||||
ins := new(internal)
|
ins := new(internal)
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var errors []error
|
var errors []error
|
||||||
wg.Add(1)
|
wg.Add(1)
|
||||||
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
|
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
assert.Len(t, errors, 1)
|
assert.Len(t, errors, 1)
|
||||||
assert.Len(t, ins.Thing, 0)
|
assert.Len(t, ins.Thing, 0)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ func TestNodeReference_Mutate(t *testing.T) {
|
|||||||
func TestNodeReference_RefNode(t *testing.T) {
|
func TestNodeReference_RefNode(t *testing.T) {
|
||||||
nr := new(NodeReference[string])
|
nr := new(NodeReference[string])
|
||||||
nr.KeyNode = &yaml.Node{
|
nr.KeyNode = &yaml.Node{
|
||||||
Content: []*yaml.Node{&yaml.Node{
|
Content: []*yaml.Node{{
|
||||||
Value: "$ref",
|
Value: "$ref",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
@@ -749,8 +749,3 @@ func TestKeyReference_GetKeyNode(t *testing.T) {
|
|||||||
assert.Equal(t, 3, nr.GetKeyNode().Line)
|
assert.Equal(t, 3, nr.GetKeyNode().Line)
|
||||||
assert.Equal(t, "pizza", nr.GetKeyNode().Value)
|
assert.Equal(t, "pizza", nr.GetKeyNode().Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
//
|
//
|
||||||
// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be
|
// 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
|
// referenced to the ones defined here. It does not define global operation parameters
|
||||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||||
type ParameterDefinitions struct {
|
type ParameterDefinitions struct {
|
||||||
Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter]
|
Definitions 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
|
// 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
|
// referenced to the ones defined here. It does not define global operation responses
|
||||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||||
type ResponsesDefinitions struct {
|
type ResponsesDefinitions struct {
|
||||||
Definitions map[low.KeyReference[string]]low.ValueReference[*Response]
|
Definitions 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
|
// 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
|
// schemes on the operations and only serves to provide the relevant details for each scheme
|
||||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||||
type SecurityDefinitions struct {
|
type SecurityDefinitions struct {
|
||||||
Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]
|
Definitions 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,
|
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
|
||||||
// arrays or models.
|
// arrays or models.
|
||||||
// - https://swagger.io/specification/v2/#definitionsObject
|
// - https://swagger.io/specification/v2/#definitionsObject
|
||||||
type Definitions struct {
|
type Definitions struct {
|
||||||
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
|
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
// Examples represents a low-level Swagger / OpenAPI 2 Example object.
|
// Examples represents a low-level Swagger / OpenAPI 2 Example object.
|
||||||
// Allows sharing examples for operation responses
|
// Allows sharing examples for operation responses
|
||||||
// - https://swagger.io/specification/v2/#exampleObject
|
// - https://swagger.io/specification/v2/#exampleObject
|
||||||
type Examples struct {
|
type Examples struct {
|
||||||
Values map[low.KeyReference[string]]low.ValueReference[any]
|
Values map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
// Header Represents a low-level Swagger / OpenAPI 2 Header object.
|
// 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.
|
// 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 Header struct {
|
||||||
Type low.NodeReference[string]
|
Type low.NodeReference[string]
|
||||||
Format low.NodeReference[string]
|
Format low.NodeReference[string]
|
||||||
|
|||||||
@@ -4,90 +4,90 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHeader_Build(t *testing.T) {
|
func TestHeader_Build(t *testing.T) {
|
||||||
|
|
||||||
yml := `items:
|
yml := `items:
|
||||||
$ref: break`
|
$ref: break`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
assert.NoError(t, mErr)
|
assert.NoError(t, mErr)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
var n Header
|
var n Header
|
||||||
err := low.BuildModel(idxNode.Content[0], &n)
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = n.Build(idxNode.Content[0], idx)
|
err = n.Build(idxNode.Content[0], idx)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeader_DefaultAsSlice(t *testing.T) {
|
func TestHeader_DefaultAsSlice(t *testing.T) {
|
||||||
|
|
||||||
yml := `x-ext: thing
|
yml := `x-ext: thing
|
||||||
default:
|
default:
|
||||||
- why
|
- why
|
||||||
- so many
|
- so many
|
||||||
- variations`
|
- variations`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
var n Header
|
var n Header
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
assert.NotNil(t, n.Default.Value)
|
assert.NotNil(t, n.Default.Value)
|
||||||
assert.Len(t, n.Default.Value, 3)
|
assert.Len(t, n.Default.Value, 3)
|
||||||
assert.Len(t, n.GetExtensions(), 1)
|
assert.Len(t, n.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeader_DefaultAsObject(t *testing.T) {
|
func TestHeader_DefaultAsObject(t *testing.T) {
|
||||||
|
|
||||||
yml := `default:
|
yml := `default:
|
||||||
lets:
|
lets:
|
||||||
create:
|
create:
|
||||||
a:
|
a:
|
||||||
thing: ok?`
|
thing: ok?`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
var n Header
|
var n Header
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
assert.NotNil(t, n.Default.Value)
|
assert.NotNil(t, n.Default.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHeader_NoDefault(t *testing.T) {
|
func TestHeader_NoDefault(t *testing.T) {
|
||||||
|
|
||||||
yml := `minimum: 12`
|
yml := `minimum: 12`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
var n Header
|
var n Header
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = 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) {
|
func TestHeader_Hash_n_Grab(t *testing.T) {
|
||||||
|
|
||||||
yml := `description: head
|
yml := `description: head
|
||||||
type: string
|
type: string
|
||||||
format: left
|
format: left
|
||||||
collectionFormat: nice
|
collectionFormat: nice
|
||||||
@@ -110,15 +110,15 @@ minItems: 1
|
|||||||
uniqueItems: true
|
uniqueItems: true
|
||||||
multipleOf: 12`
|
multipleOf: 12`
|
||||||
|
|
||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
idx := index.NewSpecIndex(&idxNode)
|
idx := index.NewSpecIndex(&idxNode)
|
||||||
|
|
||||||
var n Header
|
var n Header
|
||||||
_ = low.BuildModel(idxNode.Content[0], &n)
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
_ = n.Build(idxNode.Content[0], idx)
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
yml2 := `description: head
|
yml2 := `description: head
|
||||||
items:
|
items:
|
||||||
type: int
|
type: int
|
||||||
format: left
|
format: left
|
||||||
@@ -142,37 +142,37 @@ x-belly: large
|
|||||||
pattern: wow
|
pattern: wow
|
||||||
`
|
`
|
||||||
|
|
||||||
var idxNode2 yaml.Node
|
var idxNode2 yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(yml2), &idxNode2)
|
_ = yaml.Unmarshal([]byte(yml2), &idxNode2)
|
||||||
idx2 := index.NewSpecIndex(&idxNode2)
|
idx2 := index.NewSpecIndex(&idxNode2)
|
||||||
|
|
||||||
var n2 Header
|
var n2 Header
|
||||||
_ = low.BuildModel(idxNode2.Content[0], &n2)
|
_ = low.BuildModel(idxNode2.Content[0], &n2)
|
||||||
_ = n2.Build(idxNode2.Content[0], idx2)
|
_ = n2.Build(idxNode2.Content[0], idx2)
|
||||||
|
|
||||||
// hash
|
// hash
|
||||||
assert.Equal(t, n.Hash(), n2.Hash())
|
assert.Equal(t, n.Hash(), n2.Hash())
|
||||||
|
|
||||||
// and grab
|
// and grab
|
||||||
assert.Equal(t, "string", n.GetType().Value)
|
assert.Equal(t, "string", n.GetType().Value)
|
||||||
assert.Equal(t, "head", n.GetDescription().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, "left", n.GetFormat().Value)
|
assert.Equal(t, "left", n.GetFormat().Value)
|
||||||
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
|
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
|
||||||
assert.Equal(t, "shut that door!", n.GetDefault().Value)
|
assert.Equal(t, "shut that door!", n.GetDefault().Value)
|
||||||
assert.Equal(t, 10, n.GetMaximum().Value)
|
assert.Equal(t, 10, n.GetMaximum().Value)
|
||||||
assert.Equal(t, 1, n.GetMinimum().Value)
|
assert.Equal(t, 1, n.GetMinimum().Value)
|
||||||
assert.True(t, n.GetExclusiveMinimum().Value)
|
assert.True(t, n.GetExclusiveMinimum().Value)
|
||||||
assert.True(t, n.GetExclusiveMaximum().Value)
|
assert.True(t, n.GetExclusiveMaximum().Value)
|
||||||
assert.Equal(t, 10, n.GetMaxLength().Value)
|
assert.Equal(t, 10, n.GetMaxLength().Value)
|
||||||
assert.Equal(t, 1, n.GetMinLength().Value)
|
assert.Equal(t, 1, n.GetMinLength().Value)
|
||||||
assert.Equal(t, 10, n.GetMaxItems().Value)
|
assert.Equal(t, 10, n.GetMaxItems().Value)
|
||||||
assert.Equal(t, 1, n.GetMinItems().Value)
|
assert.Equal(t, 1, n.GetMinItems().Value)
|
||||||
assert.True(t, n.GetUniqueItems().Value)
|
assert.True(t, n.GetUniqueItems().Value)
|
||||||
assert.Equal(t, 12, n.GetMultipleOf().Value)
|
assert.Equal(t, 12, n.GetMultipleOf().Value)
|
||||||
assert.Equal(t, "wow", n.GetPattern().Value)
|
assert.Equal(t, "wow", n.GetPattern().Value)
|
||||||
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
|
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
|
||||||
assert.Len(t, n.GetEnum().Value, 2)
|
assert.Len(t, n.GetEnum().Value, 2)
|
||||||
assert.Equal(t, "large", n.FindExtension("x-belly").Value)
|
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
|
// 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.
|
// 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 Items struct {
|
||||||
Type low.NodeReference[string]
|
Type low.NodeReference[string]
|
||||||
Format low.NodeReference[string]
|
Format low.NodeReference[string]
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import (
|
|||||||
// Operation represents a low-level Swagger / OpenAPI 2 Operation object.
|
// Operation represents a low-level Swagger / OpenAPI 2 Operation object.
|
||||||
//
|
//
|
||||||
// It describes a single API operation on a path.
|
// It describes a single API operation on a path.
|
||||||
// - https://swagger.io/specification/v2/#operationObject
|
// - https://swagger.io/specification/v2/#operationObject
|
||||||
type Operation struct {
|
type Operation struct {
|
||||||
Tags low.NodeReference[[]low.ValueReference[string]]
|
Tags low.NodeReference[[]low.ValueReference[string]]
|
||||||
Summary low.NodeReference[string]
|
Summary low.NodeReference[string]
|
||||||
|
|||||||
@@ -22,29 +22,39 @@ import (
|
|||||||
// There are five possible parameter types.
|
// There are five possible parameter types.
|
||||||
//
|
//
|
||||||
// Path
|
// 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
|
// 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
|
// Header
|
||||||
// Custom headers that are expected as part of the request.
|
//
|
||||||
|
// Custom headers that are expected as part of the request.
|
||||||
|
//
|
||||||
// Body
|
// 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.
|
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
|
||||||
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
|
// 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
|
// 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).
|
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
|
||||||
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
|
||||||
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
|
||||||
// parameters have a different format based on the content-type used (for further details,
|
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
|
||||||
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
// parameters have a different format based on the content-type used (for further details,
|
||||||
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
|
||||||
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
|
||||||
// being transferred.
|
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
|
||||||
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
// being transferred.
|
||||||
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
|
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
|
||||||
// submit-name. This type of form parameters is more commonly used for file transfers
|
// 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
|
// https://swagger.io/specification/v2/#parameterObject
|
||||||
type Parameter struct {
|
type Parameter struct {
|
||||||
Name low.NodeReference[string]
|
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
|
// The path itself is still exposed to the tooling, but will not know which operations and parameters
|
||||||
// are available.
|
// are available.
|
||||||
//
|
//
|
||||||
// - https://swagger.io/specification/v2/#pathItemObject
|
// - https://swagger.io/specification/v2/#pathItemObject
|
||||||
type PathItem struct {
|
type PathItem struct {
|
||||||
Ref low.NodeReference[string]
|
Ref low.NodeReference[string]
|
||||||
Get low.NodeReference[*Operation]
|
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 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
|
// Response describes a single response from an API Operation
|
||||||
// - https://swagger.io/specification/v2/#responseObject
|
// - https://swagger.io/specification/v2/#responseObject
|
||||||
type Response struct {
|
type Response struct {
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
Schema low.NodeReference[*base.SchemaProxy]
|
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 is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object.
|
||||||
//
|
//
|
||||||
// Scopes lists the available scopes for an OAuth2 security scheme.
|
// Scopes lists the available scopes for an OAuth2 security scheme.
|
||||||
// - https://swagger.io/specification/v2/#scopesObject
|
// - https://swagger.io/specification/v2/#scopesObject
|
||||||
type Scopes struct {
|
type Scopes struct {
|
||||||
Values map[low.KeyReference[string]]low.ValueReference[string]
|
Values map[low.KeyReference[string]]low.ValueReference[string]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
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
|
// 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
|
// 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)
|
// (implicit, password, application and access code)
|
||||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||||
type SecurityScheme struct {
|
type SecurityScheme struct {
|
||||||
Type low.NodeReference[string]
|
Type low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
|
|||||||
@@ -12,12 +12,12 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/pb33f/libopenapi/resolver"
|
"github.com/pb33f/libopenapi/resolver"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// processes a property of a Swagger document asynchronously using bool and error channels for signals.
|
// 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.
|
// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification.
|
||||||
type Swagger struct {
|
type Swagger struct {
|
||||||
|
|
||||||
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
|
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
|
||||||
Swagger low.ValueReference[string]
|
Swagger low.ValueReference[string]
|
||||||
|
|
||||||
// Info represents a specification Info definition.
|
// Info represents a specification Info definition.
|
||||||
// Provides metadata about the API. The metadata can be used by the clients if needed.
|
// Provides metadata about the API. The metadata can be used by the clients if needed.
|
||||||
// - https://swagger.io/specification/v2/#infoObject
|
// - https://swagger.io/specification/v2/#infoObject
|
||||||
Info low.NodeReference[*base.Info]
|
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
|
// 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
|
// 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.
|
// (including the port). The host does not support path templating.
|
||||||
Host low.NodeReference[string]
|
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,
|
// 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 API is served directly under the host. The value MUST start with a leading slash (/).
|
||||||
// The basePath does not support path templating.
|
// The basePath does not support path templating.
|
||||||
BasePath low.NodeReference[string]
|
BasePath low.NodeReference[string]
|
||||||
|
|
||||||
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
|
// 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
|
// If the schemes is not included, the default scheme to be used is the one used to access
|
||||||
// the Swagger definition itself.
|
// the Swagger definition itself.
|
||||||
Schemes low.NodeReference[[]low.ValueReference[string]]
|
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
|
// 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.
|
// specific API calls. Value MUST be as described under Mime Types.
|
||||||
Consumes low.NodeReference[[]low.ValueReference[string]]
|
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
|
// 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.
|
// specific API calls. Value MUST be as described under Mime Types.
|
||||||
Produces low.NodeReference[[]low.ValueReference[string]]
|
Produces low.NodeReference[[]low.ValueReference[string]]
|
||||||
|
|
||||||
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
|
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
|
||||||
// - https://swagger.io/specification/v2/#pathsObject
|
// - https://swagger.io/specification/v2/#pathsObject
|
||||||
Paths low.NodeReference[*Paths]
|
Paths low.NodeReference[*Paths]
|
||||||
|
|
||||||
// Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances
|
// 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
|
// - https://swagger.io/specification/v2/#definitionsObject
|
||||||
Definitions low.NodeReference[*Definitions]
|
Definitions low.NodeReference[*Definitions]
|
||||||
|
|
||||||
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
|
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
|
||||||
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
// - https://swagger.io/specification/v2/#securityDefinitionsObject
|
||||||
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
|
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
|
||||||
|
|
||||||
// Parameters is an object to hold parameters that can be used across operations.
|
// Parameters is an object to hold parameters that can be used across operations.
|
||||||
// This property does not define global parameters for all operations.
|
// This property does not define global parameters for all operations.
|
||||||
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
|
||||||
Parameters low.NodeReference[*ParameterDefinitions]
|
Parameters low.NodeReference[*ParameterDefinitions]
|
||||||
|
|
||||||
// Responses is an object to hold responses that can be used across operations.
|
// Responses is an object to hold responses that can be used across operations.
|
||||||
// This property does not define global responses for all operations.
|
// This property does not define global responses for all operations.
|
||||||
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
|
||||||
Responses low.NodeReference[*ResponsesDefinitions]
|
Responses low.NodeReference[*ResponsesDefinitions]
|
||||||
|
|
||||||
// Security is a declaration of which security schemes are applied for the API as a whole. The list of values
|
// 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
|
// 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.
|
// requirements). Individual operations can override this definition.
|
||||||
// - https://swagger.io/specification/v2/#securityRequirementObject
|
// - https://swagger.io/specification/v2/#securityRequirementObject
|
||||||
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
|
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
|
||||||
|
|
||||||
// Tags are A list of tags used by the specification with additional metadata.
|
// 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
|
// 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
|
// 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.
|
// on the tools' logic. Each tag name in the list MUST be unique.
|
||||||
// - https://swagger.io/specification/v2/#tagObject
|
// - https://swagger.io/specification/v2/#tagObject
|
||||||
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
|
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
|
||||||
|
|
||||||
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
|
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
|
||||||
ExternalDocs low.NodeReference[*base.ExternalDoc]
|
ExternalDocs low.NodeReference[*base.ExternalDoc]
|
||||||
|
|
||||||
// Extensions contains all custom extensions defined for the top-level document.
|
// Extensions contains all custom extensions defined for the top-level document.
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
|
|
||||||
// Index is a reference to the index.SpecIndex that was created for the document and used
|
// Index is a reference to the index.SpecIndex that was created for the document and used
|
||||||
// as a guide when building out the Document. Ideal if further processing is required on the model and
|
// as a guide when building out the Document. Ideal if further processing is required on the model and
|
||||||
// the original details are required to continue the work.
|
// the original details are required to continue the work.
|
||||||
//
|
//
|
||||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||||
Index *index.SpecIndex
|
Index *index.SpecIndex
|
||||||
|
|
||||||
// SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read.
|
// 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.
|
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||||
SpecInfo *datamodel.SpecInfo
|
SpecInfo *datamodel.SpecInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindExtension locates an extension from the root of the Swagger document.
|
// FindExtension locates an extension from the root of the Swagger document.
|
||||||
func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
|
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.
|
// GetExtensions returns all Swagger/Top level extensions and satisfies the low.HasExtensions interface.
|
||||||
func (s *Swagger) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
|
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.
|
// CreateDocumentFromConfig will create a new Swagger document from the provided SpecInfo and DocumentConfiguration.
|
||||||
func CreateDocumentFromConfig(info *datamodel.SpecInfo,
|
func CreateDocumentFromConfig(info *datamodel.SpecInfo,
|
||||||
configuration *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
configuration *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
||||||
return createDocument(info, configuration)
|
return createDocument(info, configuration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateDocument will create a new Swagger document from the provided SpecInfo.
|
// CreateDocument will create a new Swagger document from the provided SpecInfo.
|
||||||
//
|
//
|
||||||
// Deprecated: Use CreateDocumentFromConfig instead.
|
// Deprecated: Use CreateDocumentFromConfig instead.
|
||||||
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
|
||||||
return createDocument(info, &datamodel.DocumentConfiguration{
|
return createDocument(info, &datamodel.DocumentConfiguration{
|
||||||
AllowRemoteReferences: true,
|
AllowRemoteReferences: true,
|
||||||
AllowFileReferences: true,
|
AllowFileReferences: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, []error) {
|
||||||
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
|
||||||
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
|
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
|
||||||
|
|
||||||
// build an index
|
// build an index
|
||||||
idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{
|
idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{
|
||||||
BaseURL: config.BaseURL,
|
BaseURL: config.BaseURL,
|
||||||
AllowRemoteLookup: config.AllowRemoteReferences,
|
AllowRemoteLookup: config.AllowRemoteReferences,
|
||||||
AllowFileLookup: config.AllowFileReferences,
|
AllowFileLookup: config.AllowFileReferences,
|
||||||
})
|
})
|
||||||
doc.Index = idx
|
doc.Index = idx
|
||||||
doc.SpecInfo = info
|
doc.SpecInfo = info
|
||||||
|
|
||||||
var errors []error
|
var errors []error
|
||||||
|
|
||||||
// build out swagger scalar variables.
|
// build out swagger scalar variables.
|
||||||
_ = low.BuildModel(info.RootNode.Content[0], &doc)
|
_ = low.BuildModel(info.RootNode.Content[0], &doc)
|
||||||
|
|
||||||
// extract externalDocs
|
// extract externalDocs
|
||||||
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
|
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors = append(errors, err)
|
errors = append(errors, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.ExternalDocs = extDocs
|
doc.ExternalDocs = extDocs
|
||||||
|
|
||||||
// create resolver and check for circular references.
|
// create resolver and check for circular references.
|
||||||
resolve := resolver.NewResolver(idx)
|
resolve := resolver.NewResolver(idx)
|
||||||
resolvingErrors := resolve.CheckForCircularReferences()
|
resolvingErrors := resolve.CheckForCircularReferences()
|
||||||
|
|
||||||
if len(resolvingErrors) > 0 {
|
if len(resolvingErrors) > 0 {
|
||||||
for r := range resolvingErrors {
|
for r := range resolvingErrors {
|
||||||
errors = append(errors, resolvingErrors[r])
|
errors = append(errors, resolvingErrors[r])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extractionFuncs := []documentFunction{
|
extractionFuncs := []documentFunction{
|
||||||
extractInfo,
|
extractInfo,
|
||||||
extractPaths,
|
extractPaths,
|
||||||
extractDefinitions,
|
extractDefinitions,
|
||||||
extractParamDefinitions,
|
extractParamDefinitions,
|
||||||
extractResponsesDefinitions,
|
extractResponsesDefinitions,
|
||||||
extractSecurityDefinitions,
|
extractSecurityDefinitions,
|
||||||
extractTags,
|
extractTags,
|
||||||
extractSecurity,
|
extractSecurity,
|
||||||
}
|
}
|
||||||
doneChan := make(chan bool)
|
doneChan := make(chan bool)
|
||||||
errChan := make(chan error)
|
errChan := make(chan error)
|
||||||
for i := range extractionFuncs {
|
for i := range extractionFuncs {
|
||||||
go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan)
|
go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan)
|
||||||
}
|
}
|
||||||
completedExtractions := 0
|
completedExtractions := 0
|
||||||
for completedExtractions < len(extractionFuncs) {
|
for completedExtractions < len(extractionFuncs) {
|
||||||
select {
|
select {
|
||||||
case <-doneChan:
|
case <-doneChan:
|
||||||
completedExtractions++
|
completedExtractions++
|
||||||
case e := <-errChan:
|
case e := <-errChan:
|
||||||
completedExtractions++
|
completedExtractions++
|
||||||
errors = append(errors, e)
|
errors = append(errors, e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &doc, errors
|
return &doc, errors
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Swagger) GetExternalDocs() *low.NodeReference[any] {
|
func (s *Swagger) GetExternalDocs() *low.NodeReference[any] {
|
||||||
return &low.NodeReference[any]{
|
return &low.NodeReference[any]{
|
||||||
KeyNode: s.ExternalDocs.KeyNode,
|
KeyNode: s.ExternalDocs.KeyNode,
|
||||||
ValueNode: s.ExternalDocs.ValueNode,
|
ValueNode: s.ExternalDocs.ValueNode,
|
||||||
Value: s.ExternalDocs.Value,
|
Value: s.ExternalDocs.Value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractInfo(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
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)
|
info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Info = info
|
doc.Info = info
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||||
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
|
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Paths = paths
|
doc.Paths = paths
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||||
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
|
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Definitions = def
|
doc.Definitions = def
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||||
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
|
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Parameters = param
|
doc.Parameters = param
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||||
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
|
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Responses = resp
|
doc.Responses = resp
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
||||||
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
|
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.SecurityDefinitions = sec
|
doc.SecurityDefinitions = sec
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
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)
|
tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
|
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
|
||||||
Value: tags,
|
Value: tags,
|
||||||
KeyNode: ln,
|
KeyNode: ln,
|
||||||
ValueNode: vn,
|
ValueNode: vn,
|
||||||
}
|
}
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
|
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)
|
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
|
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
|
||||||
Value: sec,
|
Value: sec,
|
||||||
KeyNode: ln,
|
KeyNode: ln,
|
||||||
ValueNode: vn,
|
ValueNode: vn,
|
||||||
}
|
}
|
||||||
c <- true
|
c <- true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,350 +4,350 @@
|
|||||||
package v2
|
package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var doc *Swagger
|
var doc *Swagger
|
||||||
|
|
||||||
func initTest() {
|
func initTest() {
|
||||||
if doc != nil {
|
if doc != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||||
AllowFileReferences: false,
|
AllowFileReferences: false,
|
||||||
AllowRemoteReferences: false,
|
AllowRemoteReferences: false,
|
||||||
})
|
})
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Print(err)
|
fmt.Print(err)
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCreateDocument(b *testing.B) {
|
func BenchmarkCreateDocument(b *testing.B) {
|
||||||
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||||
AllowFileReferences: false,
|
AllowFileReferences: false,
|
||||||
AllowRemoteReferences: false,
|
AllowRemoteReferences: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument(t *testing.T) {
|
func TestCreateDocument(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
doc := doc
|
doc := doc
|
||||||
assert.Equal(t, "2.0", doc.SpecInfo.Version)
|
assert.Equal(t, "2.0", doc.SpecInfo.Version)
|
||||||
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
|
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
|
||||||
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
|
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
|
||||||
assert.Equal(t, "/v2", doc.BasePath.Value)
|
assert.Equal(t, "/v2", doc.BasePath.Value)
|
||||||
assert.Len(t, doc.Parameters.Value.Definitions, 1)
|
assert.Len(t, doc.Parameters.Value.Definitions, 1)
|
||||||
assert.Len(t, doc.Tags.Value, 3)
|
assert.Len(t, doc.Tags.Value, 3)
|
||||||
assert.Len(t, doc.Schemes.Value, 2)
|
assert.Len(t, doc.Schemes.Value, 2)
|
||||||
assert.Len(t, doc.Definitions.Value.Schemas, 6)
|
assert.Len(t, doc.Definitions.Value.Schemas, 6)
|
||||||
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3)
|
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3)
|
||||||
assert.Len(t, doc.Paths.Value.PathItems, 15)
|
assert.Len(t, doc.Paths.Value.PathItems, 15)
|
||||||
assert.Len(t, doc.Responses.Value.Definitions, 2)
|
assert.Len(t, doc.Responses.Value.Definitions, 2)
|
||||||
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
|
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.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
|
||||||
assert.NotNil(t, doc.GetExternalDocs())
|
assert.NotNil(t, doc.GetExternalDocs())
|
||||||
assert.Len(t, doc.GetExtensions(), 1)
|
assert.Len(t, doc.GetExtensions(), 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Info(t *testing.T) {
|
func TestCreateDocument_Info(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
|
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, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
|
||||||
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
|
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Parameters(t *testing.T) {
|
func TestCreateDocument_Parameters(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
|
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
|
||||||
assert.NotNil(t, simpleParam)
|
assert.NotNil(t, simpleParam)
|
||||||
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
|
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
|
||||||
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
|
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Tags(t *testing.T) {
|
func TestCreateDocument_Tags(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
|
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, "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, "store", doc.Tags.Value[1].Value.Name.Value)
|
||||||
assert.Equal(t, "user", doc.Tags.Value[2].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)
|
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
|
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
|
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
|
||||||
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
|
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
|
||||||
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
|
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
|
||||||
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
|
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
|
||||||
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
|
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
|
||||||
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
|
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
|
||||||
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
|
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Definitions(t *testing.T) {
|
func TestCreateDocument_Definitions(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema()
|
apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema()
|
||||||
assert.NotNil(t, apiResp)
|
assert.NotNil(t, apiResp)
|
||||||
assert.Len(t, apiResp.Properties.Value, 3)
|
assert.Len(t, apiResp.Properties.Value, 3)
|
||||||
assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A)
|
assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A)
|
||||||
|
|
||||||
pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema()
|
pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema()
|
||||||
assert.NotNil(t, pet)
|
assert.NotNil(t, pet)
|
||||||
assert.Len(t, pet.Required.Value, 2)
|
assert.Len(t, pet.Required.Value, 2)
|
||||||
|
|
||||||
// perform a deep inline lookup on a schema to ensure chains work
|
// 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)
|
assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value)
|
||||||
|
|
||||||
// check enums
|
// check enums
|
||||||
assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3)
|
assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_ResponseDefinitions(t *testing.T) {
|
func TestCreateDocument_ResponseDefinitions(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
apiResp := doc.Responses.Value.FindResponse("200")
|
apiResp := doc.Responses.Value.FindResponse("200")
|
||||||
assert.NotNil(t, apiResp)
|
assert.NotNil(t, apiResp)
|
||||||
assert.Equal(t, "OK", apiResp.Value.Description.Value)
|
assert.Equal(t, "OK", apiResp.Value.Description.Value)
|
||||||
assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value)
|
assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value)
|
||||||
|
|
||||||
header := apiResp.Value.FindHeader("noHeader")
|
header := apiResp.Value.FindHeader("noHeader")
|
||||||
assert.NotNil(t, header)
|
assert.NotNil(t, header)
|
||||||
assert.True(t, header.Value.FindExtension("x-empty").Value.(bool))
|
assert.True(t, header.Value.FindExtension("x-empty").Value.(bool))
|
||||||
|
|
||||||
header = apiResp.Value.FindHeader("myHeader")
|
header = apiResp.Value.FindHeader("myHeader")
|
||||||
if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok {
|
if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok {
|
||||||
assert.Equal(t, "here", k["something"])
|
assert.Equal(t, "here", k["something"])
|
||||||
} else {
|
} else {
|
||||||
panic("should not fail.")
|
panic("should not fail.")
|
||||||
}
|
}
|
||||||
if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok {
|
if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok {
|
||||||
assert.Len(t, k, 2)
|
assert.Len(t, k, 2)
|
||||||
assert.Equal(t, "two", k[1])
|
assert.Equal(t, "two", k[1])
|
||||||
} else {
|
} else {
|
||||||
panic("should not fail.")
|
panic("should not fail.")
|
||||||
}
|
}
|
||||||
|
|
||||||
header = apiResp.Value.FindHeader("yourHeader")
|
header = apiResp.Value.FindHeader("yourHeader")
|
||||||
assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value)
|
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) {
|
func TestCreateDocument_Paths(t *testing.T) {
|
||||||
initTest()
|
initTest()
|
||||||
uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value
|
uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value
|
||||||
assert.NotNil(t, uploadImage)
|
assert.NotNil(t, uploadImage)
|
||||||
assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow"))
|
assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow"))
|
||||||
assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value)
|
assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value)
|
||||||
assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value)
|
assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value)
|
||||||
assert.Equal(t, "successful operation",
|
assert.Equal(t, "successful operation",
|
||||||
uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value)
|
uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_Bad(t *testing.T) {
|
func TestCreateDocument_Bad(t *testing.T) {
|
||||||
|
|
||||||
yml := `swagger:
|
yml := `swagger:
|
||||||
$ref: bork`
|
$ref: bork`
|
||||||
|
|
||||||
info, err := datamodel.ExtractSpecInfo([]byte(yml))
|
info, err := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
assert.Nil(t, info)
|
assert.Nil(t, info)
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_ExternalDocsBad(t *testing.T) {
|
func TestCreateDocument_ExternalDocsBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `externalDocs:
|
yml := `externalDocs:
|
||||||
$ref: bork`
|
$ref: bork`
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_TagsBad(t *testing.T) {
|
func TestCreateDocument_TagsBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `tags:
|
yml := `tags:
|
||||||
$ref: bork`
|
$ref: bork`
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_PathsBad(t *testing.T) {
|
func TestCreateDocument_PathsBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `paths:
|
yml := `paths:
|
||||||
"/hey":
|
"/hey":
|
||||||
post:
|
post:
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
$ref: bork`
|
$ref: bork`
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_SecurityBad(t *testing.T) {
|
func TestCreateDocument_SecurityBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `security:
|
yml := `security:
|
||||||
$ref: `
|
$ref: `
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) {
|
func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `securityDefinitions:
|
yml := `securityDefinitions:
|
||||||
$ref: `
|
$ref: `
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_ResponsesBad(t *testing.T) {
|
func TestCreateDocument_ResponsesBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `responses:
|
yml := `responses:
|
||||||
$ref: `
|
$ref: `
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_ParametersBad(t *testing.T) {
|
func TestCreateDocument_ParametersBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `parameters:
|
yml := `parameters:
|
||||||
$ref: `
|
$ref: `
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_DefinitionsBad(t *testing.T) {
|
func TestCreateDocument_DefinitionsBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `definitions:
|
yml := `definitions:
|
||||||
$ref: `
|
$ref: `
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDocument_InfoBad(t *testing.T) {
|
func TestCreateDocument_InfoBad(t *testing.T) {
|
||||||
|
|
||||||
yml := `info:
|
yml := `info:
|
||||||
$ref: `
|
$ref: `
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||||
var err []error
|
var err []error
|
||||||
doc, err = CreateDocument(info)
|
doc, err = CreateDocument(info)
|
||||||
wait := true
|
wait := true
|
||||||
for wait {
|
for wait {
|
||||||
select {
|
select {
|
||||||
case <-info.JsonParsingChannel:
|
case <-info.JsonParsingChannel:
|
||||||
wait = false
|
wait = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert.Len(t, err, 1)
|
assert.Len(t, err, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCircularReferenceError(t *testing.T) {
|
func TestCircularReferenceError(t *testing.T) {
|
||||||
|
|
||||||
data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml")
|
data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
circDoc, err := CreateDocument(info)
|
circDoc, err := CreateDocument(info)
|
||||||
assert.NotNil(t, circDoc)
|
assert.NotNil(t, circDoc)
|
||||||
assert.Len(t, err, 3)
|
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
|
// 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,
|
// 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.
|
// that identifies a URL to use for the callback operation.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||||
type Callback struct {
|
type Callback struct {
|
||||||
Expression low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]
|
Expression low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
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,
|
Value: currentCB.Value,
|
||||||
KeyNode: currentCB,
|
KeyNode: currentCB,
|
||||||
}] = low.ValueReference[*PathItem]{
|
}] = low.ValueReference[*PathItem]{
|
||||||
Value: callback,
|
Value: callback,
|
||||||
ValueNode: callbackNode,
|
ValueNode: callbackNode,
|
||||||
Reference: rv,
|
Reference: rv,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(callbacks) > 0 {
|
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
|
// 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.
|
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||||
type Components struct {
|
type Components struct {
|
||||||
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
|
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
|
||||||
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
|
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Encoding represents a low-level OpenAPI 3+ Encoding object
|
// Encoding represents a low-level OpenAPI 3+ Encoding object
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
|
||||||
type Encoding struct {
|
type Encoding struct {
|
||||||
ContentType low.NodeReference[string]
|
ContentType low.NodeReference[string]
|
||||||
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
|
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Header represents a low-level OpenAPI 3+ Header object.
|
// 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 {
|
type Header struct {
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
Required low.NodeReference[bool]
|
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
|
// 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.
|
// in an operation and using them as parameters while invoking the linked operation.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
||||||
type Link struct {
|
type Link struct {
|
||||||
OperationRef low.NodeReference[string]
|
OperationRef low.NodeReference[string]
|
||||||
OperationId low.NodeReference[string]
|
OperationId low.NodeReference[string]
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import (
|
|||||||
// MediaType represents a low-level OpenAPI MediaType object.
|
// MediaType represents a low-level OpenAPI MediaType object.
|
||||||
//
|
//
|
||||||
// Each Media Type Object provides schema and examples for the media type identified by its key.
|
// Each Media Type Object provides schema and examples for the media type identified by its key.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
||||||
type MediaType struct {
|
type MediaType struct {
|
||||||
Schema low.NodeReference[*base.SchemaProxy]
|
Schema low.NodeReference[*base.SchemaProxy]
|
||||||
Example low.NodeReference[any]
|
Example low.NodeReference[any]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object.
|
// 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 {
|
type OAuthFlows struct {
|
||||||
Implicit low.NodeReference[*OAuthFlow]
|
Implicit low.NodeReference[*OAuthFlow]
|
||||||
Password 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.
|
// 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 {
|
type OAuthFlow struct {
|
||||||
AuthorizationUrl low.NodeReference[string]
|
AuthorizationUrl low.NodeReference[string]
|
||||||
TokenUrl 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
|
// 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.
|
// happens here. The entire being for existence of this library and the specification, is this Operation.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#operation-object
|
// - https://spec.openapis.org/oas/v3.1.0#operation-object
|
||||||
type Operation struct {
|
type Operation struct {
|
||||||
Tags low.NodeReference[[]low.ValueReference[string]]
|
Tags low.NodeReference[[]low.ValueReference[string]]
|
||||||
Summary low.NodeReference[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.
|
// 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.
|
// A unique parameter is defined by a combination of a name and location.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
|
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
|
||||||
type Parameter struct {
|
type Parameter struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
In low.NodeReference[string]
|
In low.NodeReference[string]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// RequestBody represents a low-level OpenAPI 3+ RequestBody object.
|
// RequestBody represents a low-level OpenAPI 3+ RequestBody object.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
|
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
|
||||||
type RequestBody struct {
|
type RequestBody struct {
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
|
Content low.NodeReference[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.
|
// 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.
|
// 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.
|
// 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 SecurityScheme struct {
|
||||||
Type low.NodeReference[string]
|
Type low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Server represents a low-level OpenAPI 3+ Server object.
|
// 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 {
|
type Server struct {
|
||||||
URL low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
|
|||||||
@@ -4,31 +4,31 @@
|
|||||||
package datamodel
|
package datamodel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// OpenApi3 is used by all OpenAPI 3+ docs
|
// OpenApi3 is used by all OpenAPI 3+ docs
|
||||||
OpenApi3 = "openapi"
|
OpenApi3 = "openapi"
|
||||||
|
|
||||||
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
|
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
|
||||||
OpenApi2 = "swagger"
|
OpenApi2 = "swagger"
|
||||||
|
|
||||||
// AsyncApi is used by akk AsyncAPI docs, all versions.
|
// AsyncApi is used by akk AsyncAPI docs, all versions.
|
||||||
AsyncApi = "asyncapi"
|
AsyncApi = "asyncapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpecInfo_GetJSONParsingChannel(t *testing.T) {
|
func TestSpecInfo_GetJSONParsingChannel(t *testing.T) {
|
||||||
|
|
||||||
// dumb, but we need to ensure coverage is as high as we can make it.
|
// dumb, but we need to ensure coverage is as high as we can make it.
|
||||||
bchan := make(chan bool)
|
bchan := make(chan bool)
|
||||||
si := &SpecInfo{JsonParsingChannel: bchan}
|
si := &SpecInfo{JsonParsingChannel: bchan}
|
||||||
assert.Equal(t, si.GetJSONParsingChannel(), bchan)
|
assert.Equal(t, si.GetJSONParsingChannel(), bchan)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,147 +115,147 @@ info:
|
|||||||
version: '0.1.0'`
|
version: '0.1.0'`
|
||||||
|
|
||||||
func TestExtractSpecInfo_ValidJSON(t *testing.T) {
|
func TestExtractSpecInfo_ValidJSON(t *testing.T) {
|
||||||
r, e := ExtractSpecInfo([]byte(goodJSON))
|
r, e := ExtractSpecInfo([]byte(goodJSON))
|
||||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||||
assert.Error(t, e)
|
assert.Error(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_InvalidJSON(t *testing.T) {
|
func TestExtractSpecInfo_InvalidJSON(t *testing.T) {
|
||||||
_, e := ExtractSpecInfo([]byte(badJSON))
|
_, e := ExtractSpecInfo([]byte(badJSON))
|
||||||
assert.Error(t, e)
|
assert.Error(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_Nothing(t *testing.T) {
|
func TestExtractSpecInfo_Nothing(t *testing.T) {
|
||||||
_, e := ExtractSpecInfo([]byte(""))
|
_, e := ExtractSpecInfo([]byte(""))
|
||||||
assert.Error(t, e)
|
assert.Error(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_ValidYAML(t *testing.T) {
|
func TestExtractSpecInfo_ValidYAML(t *testing.T) {
|
||||||
r, e := ExtractSpecInfo([]byte(goodYAML))
|
r, e := ExtractSpecInfo([]byte(goodYAML))
|
||||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||||
assert.Error(t, e)
|
assert.Error(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_InvalidYAML(t *testing.T) {
|
func TestExtractSpecInfo_InvalidYAML(t *testing.T) {
|
||||||
_, e := ExtractSpecInfo([]byte(badYAML))
|
_, e := ExtractSpecInfo([]byte(badYAML))
|
||||||
assert.Error(t, e)
|
assert.Error(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) {
|
func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) {
|
||||||
_, e := ExtractSpecInfo([]byte(OpenApiOne))
|
_, e := ExtractSpecInfo([]byte(OpenApiOne))
|
||||||
assert.Error(t, e)
|
assert.Error(t, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_OpenAPI3(t *testing.T) {
|
func TestExtractSpecInfo_OpenAPI3(t *testing.T) {
|
||||||
|
|
||||||
r, e := ExtractSpecInfo([]byte(OpenApi3Spec))
|
r, e := ExtractSpecInfo([]byte(OpenApi3Spec))
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
assert.Equal(t, utils.OpenApi3, r.SpecType)
|
assert.Equal(t, utils.OpenApi3, r.SpecType)
|
||||||
assert.Equal(t, "3.0.1", r.Version)
|
assert.Equal(t, "3.0.1", r.Version)
|
||||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||||
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28")
|
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_OpenAPIWat(t *testing.T) {
|
func TestExtractSpecInfo_OpenAPIWat(t *testing.T) {
|
||||||
|
|
||||||
r, e := ExtractSpecInfo([]byte(OpenApiWat))
|
r, e := ExtractSpecInfo([]byte(OpenApiWat))
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
assert.Equal(t, OpenApi3, r.SpecType)
|
assert.Equal(t, OpenApi3, r.SpecType)
|
||||||
assert.Equal(t, "3.2", r.Version)
|
assert.Equal(t, "3.2", r.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_OpenAPI31(t *testing.T) {
|
func TestExtractSpecInfo_OpenAPI31(t *testing.T) {
|
||||||
|
|
||||||
r, e := ExtractSpecInfo([]byte(OpenApi31))
|
r, e := ExtractSpecInfo([]byte(OpenApi31))
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
assert.Equal(t, OpenApi3, r.SpecType)
|
assert.Equal(t, OpenApi3, r.SpecType)
|
||||||
assert.Equal(t, "3.1", r.Version)
|
assert.Equal(t, "3.1", r.Version)
|
||||||
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07")
|
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) {
|
func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) {
|
||||||
|
|
||||||
spec, e := ExtractSpecInfo([]byte(OpenApiFalse))
|
spec, e := ExtractSpecInfo([]byte(OpenApiFalse))
|
||||||
assert.NoError(t, e)
|
assert.NoError(t, e)
|
||||||
assert.Equal(t, "false", spec.Version)
|
assert.Equal(t, "false", spec.Version)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_OpenAPI2(t *testing.T) {
|
func TestExtractSpecInfo_OpenAPI2(t *testing.T) {
|
||||||
|
|
||||||
r, e := ExtractSpecInfo([]byte(OpenApi2Spec))
|
r, e := ExtractSpecInfo([]byte(OpenApi2Spec))
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
assert.Equal(t, OpenApi2, r.SpecType)
|
assert.Equal(t, OpenApi2, r.SpecType)
|
||||||
assert.Equal(t, "2.0.1", r.Version)
|
assert.Equal(t, "2.0.1", r.Version)
|
||||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||||
assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#")
|
assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) {
|
func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) {
|
||||||
|
|
||||||
_, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd))
|
_, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd))
|
||||||
assert.NotNil(t, e)
|
assert.NotNil(t, e)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
"spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error())
|
"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) {
|
func TestExtractSpecInfo_AsyncAPI(t *testing.T) {
|
||||||
|
|
||||||
r, e := ExtractSpecInfo([]byte(AsyncAPISpec))
|
r, e := ExtractSpecInfo([]byte(AsyncAPISpec))
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
assert.Equal(t, AsyncApi, r.SpecType)
|
assert.Equal(t, AsyncApi, r.SpecType)
|
||||||
assert.Equal(t, "2.0.0", r.Version)
|
assert.Equal(t, "2.0.0", r.Version)
|
||||||
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
assert.Greater(t, len(*r.SpecJSONBytes), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) {
|
func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) {
|
||||||
|
|
||||||
_, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd))
|
_, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd))
|
||||||
assert.NotNil(t, e)
|
assert.NotNil(t, e)
|
||||||
assert.Equal(t,
|
assert.Equal(t,
|
||||||
"spec is defined as asyncapi, but has a major version that is invalid", e.Error())
|
"spec is defined as asyncapi, but has a major version that is invalid", e.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) {
|
func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) {
|
||||||
|
|
||||||
yml := `openapi:
|
yml := `openapi:
|
||||||
should: fail`
|
should: fail`
|
||||||
|
|
||||||
_, err := ExtractSpecInfo([]byte(yml))
|
_, err := ExtractSpecInfo([]byte(yml))
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) {
|
func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) {
|
||||||
|
|
||||||
yml := `swagger:
|
yml := `swagger:
|
||||||
should: fail`
|
should: fail`
|
||||||
|
|
||||||
_, err := ExtractSpecInfo([]byte(yml))
|
_, err := ExtractSpecInfo([]byte(yml))
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) {
|
func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) {
|
||||||
|
|
||||||
yml := `asyncapi:
|
yml := `asyncapi:
|
||||||
should: fail`
|
should: fail`
|
||||||
|
|
||||||
_, err := ExtractSpecInfo([]byte(yml))
|
_, err := ExtractSpecInfo([]byte(yml))
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleExtractSpecInfo() {
|
func ExampleExtractSpecInfo() {
|
||||||
|
|
||||||
// load bytes from openapi spec file.
|
// load bytes from openapi spec file.
|
||||||
bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json")
|
bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json")
|
||||||
|
|
||||||
// create a new *SpecInfo instance from loaded bytes
|
// create a new *SpecInfo instance from loaded bytes
|
||||||
specInfo, err := ExtractSpecInfo(bytes)
|
specInfo, err := ExtractSpecInfo(bytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("cannot extract spec info: %e", err))
|
panic(fmt.Sprintf("cannot extract spec info: %e", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// print out the version, format and filetype
|
// 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",
|
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)
|
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
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSpecIndex_Children(t *testing.T) {
|
func TestSpecIndex_Children(t *testing.T) {
|
||||||
idx1 := new(SpecIndex)
|
idx1 := new(SpecIndex)
|
||||||
idx2 := new(SpecIndex)
|
idx2 := new(SpecIndex)
|
||||||
idx3 := new(SpecIndex)
|
idx3 := new(SpecIndex)
|
||||||
idx4 := new(SpecIndex)
|
idx4 := new(SpecIndex)
|
||||||
idx5 := new(SpecIndex)
|
idx5 := new(SpecIndex)
|
||||||
idx1.AddChild(idx2)
|
idx1.AddChild(idx2)
|
||||||
idx1.AddChild(idx3)
|
idx1.AddChild(idx3)
|
||||||
idx3.AddChild(idx4)
|
idx3.AddChild(idx4)
|
||||||
idx4.AddChild(idx5)
|
idx4.AddChild(idx5)
|
||||||
assert.Equal(t, 2, len(idx1.GetChildren()))
|
assert.Equal(t, 2, len(idx1.GetChildren()))
|
||||||
assert.Equal(t, 1, len(idx3.GetChildren()))
|
assert.Equal(t, 1, len(idx3.GetChildren()))
|
||||||
assert.Equal(t, 1, len(idx4.GetChildren()))
|
assert.Equal(t, 1, len(idx4.GetChildren()))
|
||||||
assert.Equal(t, 0, len(idx5.GetChildren()))
|
assert.Equal(t, 0, len(idx5.GetChildren()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,88 +4,88 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func isHttpMethod(val string) bool {
|
func isHttpMethod(val string) bool {
|
||||||
switch strings.ToLower(val) {
|
switch strings.ToLower(val) {
|
||||||
case methodTypes[0]:
|
case methodTypes[0]:
|
||||||
return true
|
return true
|
||||||
case methodTypes[1]:
|
case methodTypes[1]:
|
||||||
return true
|
return true
|
||||||
case methodTypes[2]:
|
case methodTypes[2]:
|
||||||
return true
|
return true
|
||||||
case methodTypes[3]:
|
case methodTypes[3]:
|
||||||
return true
|
return true
|
||||||
case methodTypes[4]:
|
case methodTypes[4]:
|
||||||
return true
|
return true
|
||||||
case methodTypes[5]:
|
case methodTypes[5]:
|
||||||
return true
|
return true
|
||||||
case methodTypes[6]:
|
case methodTypes[6]:
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func DetermineReferenceResolveType(ref string) int {
|
func DetermineReferenceResolveType(ref string) int {
|
||||||
if ref != "" && ref[0] == '#' {
|
if ref != "" && ref[0] == '#' {
|
||||||
return LocalResolve
|
return LocalResolve
|
||||||
}
|
}
|
||||||
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
|
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
|
||||||
return HttpResolve
|
return HttpResolve
|
||||||
}
|
}
|
||||||
if strings.Contains(ref, ".json") ||
|
if strings.Contains(ref, ".json") ||
|
||||||
strings.Contains(ref, ".yaml") ||
|
strings.Contains(ref, ".yaml") ||
|
||||||
strings.Contains(ref, ".yml") {
|
strings.Contains(ref, ".yml") {
|
||||||
return FileResolve
|
return FileResolve
|
||||||
}
|
}
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
func boostrapIndexCollections(rootNode *yaml.Node, index *SpecIndex) {
|
func boostrapIndexCollections(rootNode *yaml.Node, index *SpecIndex) {
|
||||||
index.root = rootNode
|
index.root = rootNode
|
||||||
index.allRefs = make(map[string]*Reference)
|
index.allRefs = make(map[string]*Reference)
|
||||||
index.allMappedRefs = make(map[string]*Reference)
|
index.allMappedRefs = make(map[string]*Reference)
|
||||||
index.refsByLine = make(map[string]map[int]bool)
|
index.refsByLine = make(map[string]map[int]bool)
|
||||||
index.linesWithRefs = make(map[int]bool)
|
index.linesWithRefs = make(map[int]bool)
|
||||||
index.pathRefs = make(map[string]map[string]*Reference)
|
index.pathRefs = make(map[string]map[string]*Reference)
|
||||||
index.paramOpRefs = make(map[string]map[string]map[string][]*Reference)
|
index.paramOpRefs = make(map[string]map[string]map[string][]*Reference)
|
||||||
index.operationTagsRefs = make(map[string]map[string][]*Reference)
|
index.operationTagsRefs = make(map[string]map[string][]*Reference)
|
||||||
index.operationDescriptionRefs = make(map[string]map[string]*Reference)
|
index.operationDescriptionRefs = make(map[string]map[string]*Reference)
|
||||||
index.operationSummaryRefs = make(map[string]map[string]*Reference)
|
index.operationSummaryRefs = make(map[string]map[string]*Reference)
|
||||||
index.paramCompRefs = make(map[string]*Reference)
|
index.paramCompRefs = make(map[string]*Reference)
|
||||||
index.paramAllRefs = make(map[string]*Reference)
|
index.paramAllRefs = make(map[string]*Reference)
|
||||||
index.paramInlineDuplicateNames = make(map[string][]*Reference)
|
index.paramInlineDuplicateNames = make(map[string][]*Reference)
|
||||||
index.globalTagRefs = make(map[string]*Reference)
|
index.globalTagRefs = make(map[string]*Reference)
|
||||||
index.securitySchemeRefs = make(map[string]*Reference)
|
index.securitySchemeRefs = make(map[string]*Reference)
|
||||||
index.requestBodiesRefs = make(map[string]*Reference)
|
index.requestBodiesRefs = make(map[string]*Reference)
|
||||||
index.responsesRefs = make(map[string]*Reference)
|
index.responsesRefs = make(map[string]*Reference)
|
||||||
index.headersRefs = make(map[string]*Reference)
|
index.headersRefs = make(map[string]*Reference)
|
||||||
index.examplesRefs = make(map[string]*Reference)
|
index.examplesRefs = make(map[string]*Reference)
|
||||||
index.callbacksRefs = make(map[string]map[string][]*Reference)
|
index.callbacksRefs = make(map[string]map[string][]*Reference)
|
||||||
index.linksRefs = make(map[string]map[string][]*Reference)
|
index.linksRefs = make(map[string]map[string][]*Reference)
|
||||||
index.callbackRefs = make(map[string]*Reference)
|
index.callbackRefs = make(map[string]*Reference)
|
||||||
index.externalSpecIndex = make(map[string]*SpecIndex)
|
index.externalSpecIndex = make(map[string]*SpecIndex)
|
||||||
index.allComponentSchemaDefinitions = make(map[string]*Reference)
|
index.allComponentSchemaDefinitions = make(map[string]*Reference)
|
||||||
index.allParameters = make(map[string]*Reference)
|
index.allParameters = make(map[string]*Reference)
|
||||||
index.allSecuritySchemes = make(map[string]*Reference)
|
index.allSecuritySchemes = make(map[string]*Reference)
|
||||||
index.allRequestBodies = make(map[string]*Reference)
|
index.allRequestBodies = make(map[string]*Reference)
|
||||||
index.allResponses = make(map[string]*Reference)
|
index.allResponses = make(map[string]*Reference)
|
||||||
index.allHeaders = make(map[string]*Reference)
|
index.allHeaders = make(map[string]*Reference)
|
||||||
index.allExamples = make(map[string]*Reference)
|
index.allExamples = make(map[string]*Reference)
|
||||||
index.allLinks = make(map[string]*Reference)
|
index.allLinks = make(map[string]*Reference)
|
||||||
index.allCallbacks = make(map[string]*Reference)
|
index.allCallbacks = make(map[string]*Reference)
|
||||||
index.allExternalDocuments = make(map[string]*Reference)
|
index.allExternalDocuments = make(map[string]*Reference)
|
||||||
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
|
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
|
||||||
index.polymorphicRefs = make(map[string]*Reference)
|
index.polymorphicRefs = make(map[string]*Reference)
|
||||||
index.refsWithSiblings = make(map[string]Reference)
|
index.refsWithSiblings = make(map[string]Reference)
|
||||||
index.seenRemoteSources = make(map[string]*yaml.Node)
|
index.seenRemoteSources = make(map[string]*yaml.Node)
|
||||||
index.seenLocalSources = make(map[string]*yaml.Node)
|
index.seenLocalSources = make(map[string]*yaml.Node)
|
||||||
index.opServersRefs = make(map[string]map[string][]*Reference)
|
index.opServersRefs = make(map[string]map[string][]*Reference)
|
||||||
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
|
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
|
||||||
index.componentIndexChan = make(chan bool)
|
index.componentIndexChan = make(chan bool)
|
||||||
index.polyComponentIndexChan = make(chan bool)
|
index.polyComponentIndexChan = make(chan bool)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,447 +4,445 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, schema := range schemasNode.Content {
|
for i, schema := range schemasNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = schema.Value
|
name = schema.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: schema,
|
Node: schema,
|
||||||
Path: fmt.Sprintf("$.components.schemas.%s", name),
|
Path: fmt.Sprintf("$.components.schemas.%s", name),
|
||||||
ParentNode: schemasNode,
|
ParentNode: schemasNode,
|
||||||
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}),
|
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}),
|
||||||
}
|
}
|
||||||
index.allComponentSchemaDefinitions[def] = ref
|
index.allComponentSchemaDefinitions[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it
|
// 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 {
|
func (index *SpecIndex) extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string) map[string][]string {
|
||||||
if schemaNode == nil {
|
if schemaNode == nil {
|
||||||
return reqRefProps
|
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
|
// 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)
|
isRef, _, defPath := utils.IsNodeRefValue(schemaNode)
|
||||||
if isRef {
|
if isRef {
|
||||||
if _, ok := reqRefProps[defPath]; !ok {
|
if _, ok := reqRefProps[defPath]; !ok {
|
||||||
reqRefProps[defPath] = []string{}
|
reqRefProps[defPath] = []string{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a required parameters list, and return if none exists, as any properties will be optional
|
// Check for a required parameters list, and return if none exists, as any properties will be optional
|
||||||
_, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content)
|
_, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content)
|
||||||
if requiredSeqNode == nil {
|
if requiredSeqNode == nil {
|
||||||
return reqRefProps
|
return reqRefProps
|
||||||
}
|
}
|
||||||
|
|
||||||
_, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content)
|
_, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content)
|
||||||
if propertiesMapNode == nil {
|
if propertiesMapNode == nil {
|
||||||
// TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong
|
// TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong
|
||||||
return reqRefProps
|
return reqRefProps
|
||||||
}
|
}
|
||||||
|
|
||||||
name := ""
|
name := ""
|
||||||
for i, param := range propertiesMapNode.Content {
|
for i, param := range propertiesMapNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = param.Value
|
name = param.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
|
// 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)
|
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
|
||||||
if paramPropertiesMapNode != nil {
|
if paramPropertiesMapNode != nil {
|
||||||
reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps)
|
reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if the current property is polymorphic, and dive into that model if so
|
// Check to see if the current property is polymorphic, and dive into that model if so
|
||||||
for _, key := range []string{"allOf", "oneOf", "anyOf"} {
|
for _, key := range []string{"allOf", "oneOf", "anyOf"} {
|
||||||
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
|
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
|
||||||
if ofNode != nil {
|
if ofNode != nil {
|
||||||
for _, ofNodeItem := range ofNode.Content {
|
for _, ofNodeItem := range ofNode.Content {
|
||||||
reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps)
|
reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run through each of the required properties and extract _their_ required references
|
// Run through each of the required properties and extract _their_ required references
|
||||||
for _, requiredPropertyNode := range requiredSeqNode.Content {
|
for _, requiredPropertyNode := range requiredSeqNode.Content {
|
||||||
_, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content)
|
_, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content)
|
||||||
if requiredPropDefNode == nil {
|
if requiredPropDefNode == nil {
|
||||||
continue
|
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
|
// 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 {
|
func (index *SpecIndex) extractRequiredReferenceProperties(requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string {
|
||||||
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
|
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
|
||||||
if !isRef {
|
if !isRef {
|
||||||
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
|
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
|
||||||
if defItems != nil {
|
if defItems != nil {
|
||||||
isRef, _, defPath = utils.IsNodeRefValue(defItems)
|
isRef, _, defPath = utils.IsNodeRefValue(defItems)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if /* still */ !isRef {
|
if /* still */ !isRef {
|
||||||
return reqRefProps
|
return reqRefProps
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := reqRefProps[defPath]; !ok {
|
if _, ok := reqRefProps[defPath]; !ok {
|
||||||
reqRefProps[defPath] = []string{}
|
reqRefProps[defPath] = []string{}
|
||||||
}
|
}
|
||||||
reqRefProps[defPath] = append(reqRefProps[defPath], propName)
|
reqRefProps[defPath] = append(reqRefProps[defPath], propName)
|
||||||
|
|
||||||
return reqRefProps
|
return reqRefProps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentParameters(paramsNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentParameters(paramsNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, param := range paramsNode.Content {
|
for i, param := range paramsNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = param.Value
|
name = param.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: param,
|
Node: param,
|
||||||
}
|
}
|
||||||
index.allParameters[def] = ref
|
index.allParameters[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentRequestBodies(requestBodiesNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentRequestBodies(requestBodiesNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, reqBod := range requestBodiesNode.Content {
|
for i, reqBod := range requestBodiesNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = reqBod.Value
|
name = reqBod.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: reqBod,
|
Node: reqBod,
|
||||||
}
|
}
|
||||||
index.allRequestBodies[def] = ref
|
index.allRequestBodies[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentResponses(responsesNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentResponses(responsesNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, response := range responsesNode.Content {
|
for i, response := range responsesNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = response.Value
|
name = response.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: response,
|
Node: response,
|
||||||
}
|
}
|
||||||
index.allResponses[def] = ref
|
index.allResponses[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentHeaders(headersNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentHeaders(headersNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, header := range headersNode.Content {
|
for i, header := range headersNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = header.Value
|
name = header.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: header,
|
Node: header,
|
||||||
}
|
}
|
||||||
index.allHeaders[def] = ref
|
index.allHeaders[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentCallbacks(callbacksNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentCallbacks(callbacksNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, callback := range callbacksNode.Content {
|
for i, callback := range callbacksNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = callback.Value
|
name = callback.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: callback,
|
Node: callback,
|
||||||
}
|
}
|
||||||
index.allCallbacks[def] = ref
|
index.allCallbacks[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentLinks(linksNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentLinks(linksNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, link := range linksNode.Content {
|
for i, link := range linksNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = link.Value
|
name = link.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: link,
|
Node: link,
|
||||||
}
|
}
|
||||||
index.allLinks[def] = ref
|
index.allLinks[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentExamples(examplesNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentExamples(examplesNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, example := range examplesNode.Content {
|
for i, example := range examplesNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = example.Value
|
name = example.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: example,
|
Node: example,
|
||||||
}
|
}
|
||||||
index.allExamples[def] = ref
|
index.allExamples[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yaml.Node, pathPrefix string) {
|
func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yaml.Node, pathPrefix string) {
|
||||||
var name string
|
var name string
|
||||||
for i, secScheme := range securitySchemesNode.Content {
|
for i, secScheme := range securitySchemesNode.Content {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
name = secScheme.Value
|
name = secScheme.Value
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
def := fmt.Sprintf("%s%s", pathPrefix, name)
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: def,
|
Definition: def,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: secScheme,
|
Node: secScheme,
|
||||||
ParentNode: securitySchemesNode,
|
ParentNode: securitySchemesNode,
|
||||||
Path: fmt.Sprintf("$.components.securitySchemes.%s", name),
|
Path: fmt.Sprintf("$.components.securitySchemes.%s", name),
|
||||||
}
|
}
|
||||||
index.allSecuritySchemes[def] = ref
|
index.allSecuritySchemes[def] = ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) countUniqueInlineDuplicates() int {
|
func (index *SpecIndex) countUniqueInlineDuplicates() int {
|
||||||
if index.componentsInlineParamUniqueCount > 0 {
|
if index.componentsInlineParamUniqueCount > 0 {
|
||||||
return index.componentsInlineParamUniqueCount
|
return index.componentsInlineParamUniqueCount
|
||||||
}
|
}
|
||||||
unique := 0
|
unique := 0
|
||||||
for _, p := range index.paramInlineDuplicateNames {
|
for _, p := range index.paramInlineDuplicateNames {
|
||||||
if len(p) == 1 {
|
if len(p) == 1 {
|
||||||
unique++
|
unique++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
index.componentsInlineParamUniqueCount = unique
|
index.componentsInlineParamUniqueCount = unique
|
||||||
return unique
|
return unique
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *yaml.Node, method string) {
|
func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *yaml.Node, method string) {
|
||||||
for i, param := range params {
|
for i, param := range params {
|
||||||
// param is ref
|
// param is ref
|
||||||
if len(param.Content) > 0 && param.Content[0].Value == "$ref" {
|
if len(param.Content) > 0 && param.Content[0].Value == "$ref" {
|
||||||
|
|
||||||
paramRefName := param.Content[1].Value
|
paramRefName := param.Content[1].Value
|
||||||
paramRef := index.allMappedRefs[paramRefName]
|
paramRef := index.allMappedRefs[paramRefName]
|
||||||
|
|
||||||
if index.paramOpRefs[pathItemNode.Value] == nil {
|
if index.paramOpRefs[pathItemNode.Value] == nil {
|
||||||
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
||||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||||
|
|
||||||
}
|
}
|
||||||
// if we know the path, but it's a new method
|
// if we know the path, but it's a new method
|
||||||
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
||||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is a duplicate, add an error and ignore it
|
// if this is a duplicate, add an error and ignore it
|
||||||
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
|
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
|
||||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||||
if method == "top" {
|
if method == "top" {
|
||||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||||
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
||||||
"index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName),
|
"index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName),
|
||||||
Node: param,
|
Node: param,
|
||||||
Path: path,
|
Path: path,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
if paramRef != nil {
|
if paramRef != nil {
|
||||||
index.paramOpRefs[pathItemNode.Value][method][paramRefName] =
|
index.paramOpRefs[pathItemNode.Value][method][paramRefName] =
|
||||||
append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef)
|
append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// param is inline.
|
// param is inline.
|
||||||
_, vn := utils.FindKeyNode("name", param.Content)
|
_, vn := utils.FindKeyNode("name", param.Content)
|
||||||
|
|
||||||
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||||
if method == "top" {
|
if method == "top" {
|
||||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vn == nil {
|
if vn == nil {
|
||||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||||
Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value",
|
Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value",
|
||||||
method, pathItemNode.Value, i),
|
method, pathItemNode.Value, i),
|
||||||
Node: param,
|
Node: param,
|
||||||
Path: path,
|
Path: path,
|
||||||
})
|
})
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: vn.Value,
|
Definition: vn.Value,
|
||||||
Name: vn.Value,
|
Name: vn.Value,
|
||||||
Node: param,
|
Node: param,
|
||||||
Path: path,
|
Path: path,
|
||||||
}
|
}
|
||||||
if index.paramOpRefs[pathItemNode.Value] == nil {
|
if index.paramOpRefs[pathItemNode.Value] == nil {
|
||||||
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
|
||||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we know the path but this is a new method.
|
// if we know the path but this is a new method.
|
||||||
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
if index.paramOpRefs[pathItemNode.Value][method] == nil {
|
||||||
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
|
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 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 len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 {
|
||||||
|
|
||||||
currentNode := ref.Node
|
currentNode := ref.Node
|
||||||
checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name]
|
checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name]
|
||||||
_, currentIn := utils.FindKeyNodeTop("in", currentNode.Content)
|
_, 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)
|
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
|
||||||
if method == "top" {
|
if method == "top" {
|
||||||
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
|
||||||
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
|
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),
|
"index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value),
|
||||||
Node: param,
|
Node: param,
|
||||||
Path: path,
|
Path: path,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
||||||
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
|
||||||
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func runIndexFunction(funcs []func() int, wg *sync.WaitGroup) {
|
func runIndexFunction(funcs []func() int, wg *sync.WaitGroup) {
|
||||||
for _, cFunc := range funcs {
|
for _, cFunc := range funcs {
|
||||||
go func(wg *sync.WaitGroup, cf func() int) {
|
go func(wg *sync.WaitGroup, cf func() int) {
|
||||||
cf()
|
cf()
|
||||||
wg.Done()
|
wg.Done()
|
||||||
}(wg, cFunc)
|
}(wg, cFunc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateCleanSpecConfigBaseURL(baseURL *url.URL, dir string, includeFile bool) string {
|
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
|
// create a slice of path segments from existing path
|
||||||
pathSegs := strings.Split(cleanedPath, "/")
|
pathSegs := strings.Split(cleanedPath, "/")
|
||||||
dirSegs := strings.Split(dir, "/")
|
dirSegs := strings.Split(dir, "/")
|
||||||
|
|
||||||
var cleanedSegs []string
|
var cleanedSegs []string
|
||||||
if !includeFile {
|
if !includeFile {
|
||||||
dirSegs = dirSegs[:len(dirSegs)-1]
|
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
|
||||||
if strings.HasSuffix(p, "/") {
|
// down into services, please don't blast apart specs into a billion shards.
|
||||||
p = p[:len(p)-1]
|
if strings.Contains(dir, "../") {
|
||||||
}
|
for s := range dirSegs {
|
||||||
return p
|
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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,52 +4,52 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGenerateCleanSpecConfigBaseURL(t *testing.T) {
|
func TestGenerateCleanSpecConfigBaseURL(t *testing.T) {
|
||||||
u, _ := url.Parse("https://pb33f.io/things/stuff")
|
u, _ := url.Parse("https://pb33f.io/things/stuff")
|
||||||
path := "."
|
path := "."
|
||||||
assert.Equal(t, "https://pb33f.io/things/stuff",
|
assert.Equal(t, "https://pb33f.io/things/stuff",
|
||||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateCleanSpecConfigBaseURL_RelativeDeep(t *testing.T) {
|
func TestGenerateCleanSpecConfigBaseURL_RelativeDeep(t *testing.T) {
|
||||||
u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil")
|
u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil")
|
||||||
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
||||||
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz",
|
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz",
|
||||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||||
|
|
||||||
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang",
|
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang",
|
||||||
GenerateCleanSpecConfigBaseURL(u, path, true))
|
GenerateCleanSpecConfigBaseURL(u, path, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateCleanSpecConfigBaseURL_NoBaseURL(t *testing.T) {
|
func TestGenerateCleanSpecConfigBaseURL_NoBaseURL(t *testing.T) {
|
||||||
|
|
||||||
u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil")
|
u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil")
|
||||||
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
path := "../../../../foo/bar/baz/crap.yaml#thang"
|
||||||
assert.Equal(t, "/things/stuff/foo/bar/baz",
|
assert.Equal(t, "/things/stuff/foo/bar/baz",
|
||||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||||
|
|
||||||
assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang",
|
assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang",
|
||||||
GenerateCleanSpecConfigBaseURL(u, path, true))
|
GenerateCleanSpecConfigBaseURL(u, path, true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateCleanSpecConfigBaseURL_HttpStrip(t *testing.T) {
|
func TestGenerateCleanSpecConfigBaseURL_HttpStrip(t *testing.T) {
|
||||||
|
|
||||||
u, _ := url.Parse(".")
|
u, _ := url.Parse(".")
|
||||||
path := "http://thing.com/crap.yaml#thang"
|
path := "http://thing.com/crap.yaml#thang"
|
||||||
assert.Equal(t, "http://thing.com",
|
assert.Equal(t, "http://thing.com",
|
||||||
GenerateCleanSpecConfigBaseURL(u, path, false))
|
GenerateCleanSpecConfigBaseURL(u, path, false))
|
||||||
|
|
||||||
assert.Equal(t, "",
|
assert.Equal(t, "",
|
||||||
GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true))
|
GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) {
|
func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) {
|
||||||
c := CreateOpenAPIIndexConfig()
|
c := CreateOpenAPIIndexConfig()
|
||||||
idx := NewSpecIndexWithConfig(nil, c)
|
idx := NewSpecIndexWithConfig(nil, c)
|
||||||
assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil))
|
assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,170 +1,170 @@
|
|||||||
package resolver
|
package resolver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNewResolver(t *testing.T) {
|
func TestNewResolver(t *testing.T) {
|
||||||
assert.Nil(t, NewResolver(nil))
|
assert.Nil(t, NewResolver(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Benchmark_ResolveDocumentStripe(b *testing.B) {
|
func Benchmark_ResolveDocumentStripe(b *testing.B) {
|
||||||
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||||
for n := 0; n < b.N; n++ {
|
for n := 0; n < b.N; n++ {
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(stripe, &rootNode)
|
yaml.Unmarshal(stripe, &rootNode)
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
resolver.Resolve()
|
resolver.Resolve()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_CircularSpec(t *testing.T) {
|
func TestResolver_ResolveComponents_CircularSpec(t *testing.T) {
|
||||||
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(circular, &rootNode)
|
yaml.Unmarshal(circular, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.Resolve()
|
circ := resolver.Resolve()
|
||||||
assert.Len(t, circ, 3)
|
assert.Len(t, circ, 3)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_CheckForCircularReferences(t *testing.T) {
|
func TestResolver_CheckForCircularReferences(t *testing.T) {
|
||||||
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(circular, &rootNode)
|
yaml.Unmarshal(circular, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 3)
|
assert.Len(t, circ, 3)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 3)
|
assert.Len(t, resolver.GetResolvingErrors(), 3)
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 3)
|
assert.Len(t, resolver.GetCircularErrors(), 3)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) {
|
func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) {
|
||||||
circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml")
|
circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(circular, &rootNode)
|
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{
|
index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{
|
||||||
AllowRemoteLookup: true,
|
AllowRemoteLookup: true,
|
||||||
AllowFileLookup: true,
|
AllowFileLookup: true,
|
||||||
BaseURL: baseURL,
|
BaseURL: baseURL,
|
||||||
})
|
})
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
assert.Len(t, resolver.GetResolvingErrors(), 0)
|
||||||
assert.Len(t, resolver.GetCircularErrors(), 0)
|
assert.Len(t, resolver.GetCircularErrors(), 0)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_CircularReferencesRequiredValid(t *testing.T) {
|
func TestResolver_CircularReferencesRequiredValid(t *testing.T) {
|
||||||
circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
|
circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(circular, &rootNode)
|
yaml.Unmarshal(circular, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) {
|
func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) {
|
||||||
circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
|
circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(circular, &rootNode)
|
yaml.Unmarshal(circular, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 2)
|
assert.Len(t, circ, 2)
|
||||||
|
|
||||||
_, err := yaml.Marshal(resolver.resolvedRoot)
|
_, err := yaml.Marshal(resolver.resolvedRoot)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_DeepJourney(t *testing.T) {
|
func TestResolver_DeepJourney(t *testing.T) {
|
||||||
var journey []*index.Reference
|
var journey []*index.Reference
|
||||||
for f := 0; f < 200; f++ {
|
for f := 0; f < 200; f++ {
|
||||||
journey = append(journey, nil)
|
journey = append(journey, nil)
|
||||||
}
|
}
|
||||||
index := index.NewSpecIndex(nil)
|
index := index.NewSpecIndex(nil)
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
|
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_Stripe(t *testing.T) {
|
func TestResolver_ResolveComponents_Stripe(t *testing.T) {
|
||||||
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(stripe, &rootNode)
|
yaml.Unmarshal(stripe, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.Resolve()
|
circ := resolver.Resolve()
|
||||||
assert.Len(t, circ, 3)
|
assert.Len(t, circ, 3)
|
||||||
|
|
||||||
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
|
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
|
||||||
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0)
|
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_BurgerShop(t *testing.T) {
|
func TestResolver_ResolveComponents_BurgerShop(t *testing.T) {
|
||||||
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(mixedref, &rootNode)
|
yaml.Unmarshal(mixedref, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.Resolve()
|
circ := resolver.Resolve()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) {
|
func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) {
|
||||||
yml := `paths:
|
yml := `paths:
|
||||||
/hey:
|
/hey:
|
||||||
get:
|
get:
|
||||||
responses:
|
responses:
|
||||||
@@ -184,20 +184,20 @@ components:
|
|||||||
tea:
|
tea:
|
||||||
description: tea`
|
description: tea`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.CheckForCircularReferences()
|
circ := resolver.CheckForCircularReferences()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_PolyCircRef(t *testing.T) {
|
func TestResolver_ResolveComponents_PolyCircRef(t *testing.T) {
|
||||||
yml := `openapi: 3.1.0
|
yml := `openapi: 3.1.0
|
||||||
components:
|
components:
|
||||||
schemas:
|
schemas:
|
||||||
cheese:
|
cheese:
|
||||||
@@ -211,24 +211,24 @@ components:
|
|||||||
tea:
|
tea:
|
||||||
description: tea`
|
description: tea`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
_ = resolver.CheckForCircularReferences()
|
_ = resolver.CheckForCircularReferences()
|
||||||
resolver.circularReferences[0].IsInfiniteLoop = true // override
|
resolver.circularReferences[0].IsInfiniteLoop = true // override
|
||||||
assert.Len(t, index.GetCircularReferences(), 1)
|
assert.Len(t, index.GetCircularReferences(), 1)
|
||||||
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
|
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
|
||||||
assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex)
|
assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_Missing(t *testing.T) {
|
func TestResolver_ResolveComponents_Missing(t *testing.T) {
|
||||||
yml := `paths:
|
yml := `paths:
|
||||||
/hey:
|
/hey:
|
||||||
get:
|
get:
|
||||||
responses:
|
responses:
|
||||||
@@ -247,95 +247,95 @@ components:
|
|||||||
butter:
|
butter:
|
||||||
$ref: 'go home, I am drunk'`
|
$ref: 'go home, I am drunk'`
|
||||||
|
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal([]byte(yml), &rootNode)
|
yaml.Unmarshal([]byte(yml), &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
err := resolver.Resolve()
|
err := resolver.Resolve()
|
||||||
assert.Len(t, err, 1)
|
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())
|
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) {
|
func TestResolver_ResolveComponents_MixedRef(t *testing.T) {
|
||||||
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
|
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(mixedref, &rootNode)
|
yaml.Unmarshal(mixedref, &rootNode)
|
||||||
|
|
||||||
b := index.CreateOpenAPIIndexConfig()
|
b := index.CreateOpenAPIIndexConfig()
|
||||||
idx := index.NewSpecIndexWithConfig(&rootNode, b)
|
idx := index.NewSpecIndexWithConfig(&rootNode, b)
|
||||||
|
|
||||||
resolver := NewResolver(idx)
|
resolver := NewResolver(idx)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.Resolve()
|
circ := resolver.Resolve()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
assert.Equal(t, 5, resolver.GetIndexesVisited())
|
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.
|
// 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, 191, resolver.GetRelativesSeen())
|
||||||
assert.Equal(t, 35, resolver.GetJourneysTaken())
|
assert.Equal(t, 35, resolver.GetJourneysTaken())
|
||||||
assert.Equal(t, 62, resolver.GetReferenceVisited())
|
assert.Equal(t, 62, resolver.GetReferenceVisited())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolver_ResolveComponents_k8s(t *testing.T) {
|
func TestResolver_ResolveComponents_k8s(t *testing.T) {
|
||||||
k8s, _ := ioutil.ReadFile("../test_specs/k8s.json")
|
k8s, _ := ioutil.ReadFile("../test_specs/k8s.json")
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
yaml.Unmarshal(k8s, &rootNode)
|
yaml.Unmarshal(k8s, &rootNode)
|
||||||
|
|
||||||
index := index.NewSpecIndex(&rootNode)
|
index := index.NewSpecIndex(&rootNode)
|
||||||
|
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
assert.NotNil(t, resolver)
|
assert.NotNil(t, resolver)
|
||||||
|
|
||||||
circ := resolver.Resolve()
|
circ := resolver.Resolve()
|
||||||
assert.Len(t, circ, 0)
|
assert.Len(t, circ, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors
|
// Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors
|
||||||
func ExampleNewResolver() {
|
func ExampleNewResolver() {
|
||||||
// create a yaml.Node reference as a root node.
|
// create a yaml.Node reference as a root node.
|
||||||
var rootNode yaml.Node
|
var rootNode yaml.Node
|
||||||
|
|
||||||
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
|
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
|
||||||
stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
|
||||||
|
|
||||||
// unmarshal bytes into our rootNode.
|
// unmarshal bytes into our rootNode.
|
||||||
_ = yaml.Unmarshal(stripeBytes, &rootNode)
|
_ = yaml.Unmarshal(stripeBytes, &rootNode)
|
||||||
|
|
||||||
// create a new spec index (resolver depends on it)
|
// create a new spec index (resolver depends on it)
|
||||||
indexConfig := index.CreateClosedAPIIndexConfig()
|
indexConfig := index.CreateClosedAPIIndexConfig()
|
||||||
index := index.NewSpecIndexWithConfig(&rootNode, indexConfig)
|
index := index.NewSpecIndexWithConfig(&rootNode, indexConfig)
|
||||||
|
|
||||||
// create a new resolver using the index.
|
// create a new resolver using the index.
|
||||||
resolver := NewResolver(index)
|
resolver := NewResolver(index)
|
||||||
|
|
||||||
// resolve the document, if there are circular reference errors, they are returned/
|
// 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
|
// WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved
|
||||||
circularErrors := resolver.Resolve()
|
circularErrors := resolver.Resolve()
|
||||||
|
|
||||||
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
|
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
|
||||||
// So let's print them out.
|
// So let's print them out.
|
||||||
//
|
//
|
||||||
fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not",
|
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()))
|
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()))
|
||||||
// Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not
|
// Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleResolvingError() {
|
func ExampleResolvingError() {
|
||||||
re := ResolvingError{
|
re := ResolvingError{
|
||||||
ErrorRef: errors.New("Je suis une erreur"),
|
ErrorRef: errors.New("Je suis une erreur"),
|
||||||
Node: &yaml.Node{
|
Node: &yaml.Node{
|
||||||
Line: 5,
|
Line: 5,
|
||||||
Column: 21,
|
Column: 21,
|
||||||
},
|
},
|
||||||
Path: "#/definitions/JeSuisUneErreur",
|
Path: "#/definitions/JeSuisUneErreur",
|
||||||
CircularReference: &index.CircularReferenceResult{},
|
CircularReference: &index.CircularReferenceResult{},
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s", re.Error())
|
fmt.Printf("%s", re.Error())
|
||||||
// Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
|
// Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user