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:
Dave Shanley
2023-06-15 08:58:20 -04:00
committed by quobix
parent 148822fa2a
commit c3cf5f1e38
142 changed files with 11751 additions and 11577 deletions

View File

@@ -32,14 +32,17 @@ const (
)
// OpenAPI3SchemaData is an embedded version of the OpenAPI 3 Schema
//
//go:embed schemas/oas3-schema.json
var OpenAPI3SchemaData string // embedded OAS3 schema
// OpenAPI31SchemaData is an embedded version of the OpenAPI 3.1 Schema
//
//go:embed schemas/oas31-schema.json
var OpenAPI31SchemaData string // embedded OAS31 schema
// OpenAPI2SchemaData is an embedded version of the OpenAPI 2 (Swagger) Schema
//
//go:embed schemas/swagger2-schema.json
var OpenAPI2SchemaData string // embedded OAS3 schema

View File

@@ -4,18 +4,18 @@
package datamodel
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
"testing"
)
func TestNewClosedDocumentConfiguration(t *testing.T) {
cfg := NewClosedDocumentConfiguration()
assert.False(t, cfg.AllowRemoteReferences)
assert.False(t, cfg.AllowFileReferences)
cfg := NewClosedDocumentConfiguration()
assert.False(t, cfg.AllowRemoteReferences)
assert.False(t, cfg.AllowFileReferences)
}
func TestNewOpenDocumentConfiguration(t *testing.T) {
cfg := NewOpenDocumentConfiguration()
assert.True(t, cfg.AllowRemoteReferences)
assert.True(t, cfg.AllowFileReferences)
cfg := NewOpenDocumentConfiguration()
assert.True(t, cfg.AllowRemoteReferences)
assert.True(t, cfg.AllowFileReferences)
}

View File

@@ -9,4 +9,3 @@
// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure
// that all the latest features are collected, without damaging backwards compatibility.
package base

View File

@@ -10,8 +10,9 @@ import (
)
// Contact represents a high-level representation of the Contact definitions found at
// v2 - https://swagger.io/specification/v2/#contactObject
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
//
// v2 - https://swagger.io/specification/v2/#contactObject
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
type Contact struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`
@@ -47,6 +48,3 @@ func (c *Contact) MarshalYAML() (interface{}, error) {
nb := low2.NewNodeBuilder(c, c.low)
return nb.Render(), nil
}

View File

@@ -16,7 +16,8 @@ import (
// which is used to inform the consumer of the document of an alternative schema based on the value associated with it.
//
// When using the discriminator, inline schemas will not be considered.
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
//
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
type Discriminator struct {
PropertyName string `json:"propertyName,omitempty" yaml:"propertyName,omitempty"`
Mapping map[string]string `json:"mapping,omitempty" yaml:"mapping,omitempty"`

View File

@@ -44,7 +44,6 @@ func (d *DynamicValue[A, B]) RenderInline() ([]byte, error) {
return yaml.Marshal(d)
}
// MarshalYAML will create a ready to render YAML representation of the DynamicValue object.
func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) {
// this is a custom renderer, we can't use the NodeBuilder out of the gate.

View File

@@ -11,7 +11,8 @@ import (
)
// Example represents a high-level Example object as defined by OpenAPI 3+
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
//
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
type Example struct {
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`

View File

@@ -12,8 +12,9 @@ import (
// ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3
//
// Allows referencing an external resource for extended documentation.
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
//
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
type ExternalDoc struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`

View File

@@ -200,5 +200,3 @@ x-cake:
bytes, _ := highInfo.Render()
assert.Len(t, bytes, 275)
}

View File

@@ -4,56 +4,56 @@
package base
import (
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestLicense_Render(t *testing.T) {
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
dat, _ := highL.Render()
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
dat, _ := highL.Render()
// unmarshal yaml into a *yaml.Node instance
var cNode yaml.Node
_ = yaml.Unmarshal(dat, &cNode)
// unmarshal yaml into a *yaml.Node instance
var cNode yaml.Node
_ = yaml.Unmarshal(dat, &cNode)
// build low
var lowLicense lowbase.License
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
// build low
var lowLicense lowbase.License
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
// build high
highLicense := NewLicense(&lowLicense)
// build high
highLicense := NewLicense(&lowLicense)
assert.Equal(t, "MIT", highLicense.Name)
assert.Equal(t, "https://pb33f.io", highLicense.URL)
assert.Equal(t, "MIT", highLicense.Name)
assert.Equal(t, "https://pb33f.io", highLicense.URL)
}
func TestLicense_RenderEqual(t *testing.T) {
yml := `name: MIT
yml := `name: MIT
url: https://pb33f.io/not-real
`
// unmarshal yaml into a *yaml.Node instance
var cNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &cNode)
// unmarshal yaml into a *yaml.Node instance
var cNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &cNode)
// build low
var lowLicense lowbase.License
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
_ = lowLicense.Build(cNode.Content[0], nil)
// build low
var lowLicense lowbase.License
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
_ = lowLicense.Build(cNode.Content[0], nil)
// build high
highLicense := NewLicense(&lowLicense)
// build high
highLicense := NewLicense(&lowLicense)
assert.Equal(t, "MIT", highLicense.Name)
assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL)
assert.Equal(t, "MIT", highLicense.Name)
assert.Equal(t, "https://pb33f.io/not-real", highLicense.URL)
// re-render and ensure everything is in the same order as before.
bytes, _ := highLicense.Render()
assert.Equal(t, yml, string(bytes))
// re-render and ensure everything is in the same order as before.
bytes, _ := highLicense.Render()
assert.Equal(t, yml, string(bytes))
}

View File

@@ -10,8 +10,9 @@ import (
)
// License is a high-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
// v2 - https://swagger.io/specification/v2/#licenseObject
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
//
// v2 - https://swagger.io/specification/v2/#licenseObject
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
type License struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
URL string `json:"url,omitempty" yaml:"url,omitempty"`

View File

@@ -4,12 +4,12 @@
package base
import (
"gopkg.in/yaml.v3"
"sync"
"gopkg.in/yaml.v3"
"sync"
"github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
)
// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1
@@ -22,8 +22,8 @@ import (
// - v3 schema: https://swagger.io/specification/#schema-object
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
type Schema struct {
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
// In version 3.1, ExclusiveMaximum is a number.
@@ -33,76 +33,79 @@ type Schema struct {
// In version 3.1, ExclusiveMinimum is a number.
ExclusiveMinimum *DynamicValue[bool, float64] `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
// in version 3.1, Type can be multiple values
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
// in version 3.1, Type can be multiple values
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
// Schemas are resolved on demand using a SchemaProxy
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
// Schemas are resolved on demand using a SchemaProxy
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
// Polymorphic Schemas are only available in version 3+
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
// Polymorphic Schemas are only available in version 3+
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
// in 3.1 examples can be an array (which is recommended)
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
// in 3.1 examples can be an array (which is recommended)
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
// in 3.1 prefixItems provides tuple validation support.
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
// in 3.1 prefixItems provides tuple validation support.
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
// 3.1 Specific properties
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
UnevaluatedProperties *SchemaProxy `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
// 3.1 Specific properties
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
// in 3.1 Items can be a Schema or a boolean
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
// in 3.1 UnevaluatedProperties can be a Schema or a boolean
// https://github.com/pb33f/libopenapi/issues/118
UnevaluatedProperties *DynamicValue[*SchemaProxy, *bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
// in 3.1 Items can be a Schema or a boolean
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
// Compatible with all versions
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"`
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *base.Schema
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
ParentProxy *SchemaProxy `json:"-" yaml:"-"`
// Compatible with all versions
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"`
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *base.Schema
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
ParentProxy *SchemaProxy `json:"-" yaml:"-"`
}
// NewSchema will create a new high-level schema from a low-level one.
@@ -164,302 +167,318 @@ func NewSchema(schema *base.Schema) *Schema {
s.MinProperties = &schema.MinProperties.Value
}
if !schema.MaxContains.IsEmpty() {
s.MaxContains = &schema.MaxContains.Value
}
if !schema.MinContains.IsEmpty() {
s.MinContains = &schema.MinContains.Value
}
if !schema.UniqueItems.IsEmpty() {
s.UniqueItems = &schema.UniqueItems.Value
}
if !schema.Contains.IsEmpty() {
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Contains.ValueNode,
Value: schema.Contains.Value,
}}
}
if !schema.If.IsEmpty() {
s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.If.ValueNode,
Value: schema.If.Value,
}}
}
if !schema.Else.IsEmpty() {
s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Else.ValueNode,
Value: schema.Else.Value,
}}
}
if !schema.Then.IsEmpty() {
s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Then.ValueNode,
Value: schema.Then.Value,
}}
}
if !schema.PropertyNames.IsEmpty() {
s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.PropertyNames.ValueNode,
Value: schema.PropertyNames.Value,
}}
}
if !schema.UnevaluatedItems.IsEmpty() {
s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.UnevaluatedItems.ValueNode,
Value: schema.UnevaluatedItems.Value,
}}
}
if !schema.UnevaluatedProperties.IsEmpty() {
s.UnevaluatedProperties = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.UnevaluatedProperties.ValueNode,
Value: schema.UnevaluatedProperties.Value,
}}
}
if !schema.MaxContains.IsEmpty() {
s.MaxContains = &schema.MaxContains.Value
}
if !schema.MinContains.IsEmpty() {
s.MinContains = &schema.MinContains.Value
}
if !schema.UniqueItems.IsEmpty() {
s.UniqueItems = &schema.UniqueItems.Value
}
if !schema.Contains.IsEmpty() {
s.Contains = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Contains.ValueNode,
Value: schema.Contains.Value,
}}
}
if !schema.If.IsEmpty() {
s.If = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.If.ValueNode,
Value: schema.If.Value,
}}
}
if !schema.Else.IsEmpty() {
s.Else = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Else.ValueNode,
Value: schema.Else.Value,
}}
}
if !schema.Then.IsEmpty() {
s.Then = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Then.ValueNode,
Value: schema.Then.Value,
}}
}
if !schema.PropertyNames.IsEmpty() {
s.PropertyNames = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.PropertyNames.ValueNode,
Value: schema.PropertyNames.Value,
}}
}
if !schema.UnevaluatedItems.IsEmpty() {
s.UnevaluatedItems = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.UnevaluatedItems.ValueNode,
Value: schema.UnevaluatedItems.Value,
}}
}
// check if unevaluated properties is a schema
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsA() {
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
A: &SchemaProxy{
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.UnevaluatedProperties.ValueNode,
Value: schema.UnevaluatedProperties.Value.A,
},
},
}
}
s.Pattern = schema.Pattern.Value
s.Format = schema.Format.Value
// check if unevaluated properties is a bool
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsB() {
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
B: schema.UnevaluatedProperties.Value.B,
}
}
// 3.0 spec is a single value
if !schema.Type.IsEmpty() && schema.Type.Value.IsA() {
s.Type = []string{schema.Type.Value.A}
}
// 3.1 spec may have multiple values
if !schema.Type.IsEmpty() && schema.Type.Value.IsB() {
for i := range schema.Type.Value.B {
s.Type = append(s.Type, schema.Type.Value.B[i].Value)
}
}
if schema.AdditionalProperties.Value != nil {
if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok {
s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
KeyNode: schema.AdditionalProperties.KeyNode,
ValueNode: schema.AdditionalProperties.ValueNode,
Value: addPropSchema,
})
} else {
// TODO: check for slice and map types and unpack correctly.
if !schema.UnevaluatedProperties.IsEmpty() {
s.AdditionalProperties = schema.AdditionalProperties.Value
}
}
s.Description = schema.Description.Value
s.Default = schema.Default.Value
if !schema.Nullable.IsEmpty() {
s.Nullable = &schema.Nullable.Value
}
if !schema.ReadOnly.IsEmpty() {
s.ReadOnly = schema.ReadOnly.Value
}
if !schema.WriteOnly.IsEmpty() {
s.WriteOnly = schema.WriteOnly.Value
}
if !schema.Deprecated.IsEmpty() {
s.Deprecated = &schema.Deprecated.Value
}
s.Example = schema.Example.Value
if len(schema.Examples.Value) > 0 {
examples := make([]any, len(schema.Examples.Value))
for i := 0; i < len(schema.Examples.Value); i++ {
examples[i] = schema.Examples.Value[i].Value
}
s.Examples = examples
}
s.Extensions = high.ExtractExtensions(schema.Extensions)
if !schema.Discriminator.IsEmpty() {
s.Discriminator = NewDiscriminator(schema.Discriminator.Value)
}
if !schema.XML.IsEmpty() {
s.XML = NewXML(schema.XML.Value)
}
if !schema.ExternalDocs.IsEmpty() {
s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value)
}
var req []string
for i := range schema.Required.Value {
req = append(req, schema.Required.Value[i].Value)
}
s.Required = req
}
var enum []any
if !schema.Anchor.IsEmpty() {
s.Anchor = schema.Anchor.Value
}
s.Pattern = schema.Pattern.Value
s.Format = schema.Format.Value
// TODO: check this behavior.
for i := range schema.Enum.Value {
enum = append(enum, schema.Enum.Value[i].Value)
}
s.Enum = enum
// 3.0 spec is a single value
if !schema.Type.IsEmpty() && schema.Type.Value.IsA() {
s.Type = []string{schema.Type.Value.A}
}
// 3.1 spec may have multiple values
if !schema.Type.IsEmpty() && schema.Type.Value.IsB() {
for i := range schema.Type.Value.B {
s.Type = append(s.Type, schema.Type.Value.B[i].Value)
}
}
if schema.AdditionalProperties.Value != nil {
if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok {
s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
KeyNode: schema.AdditionalProperties.KeyNode,
ValueNode: schema.AdditionalProperties.ValueNode,
Value: addPropSchema,
})
} else {
// TODO: check for slice and map types and unpack correctly.
// async work.
// any polymorphic properties need to be handled in their own threads
// any properties each need to be processed in their own thread.
// we go as fast as we can.
polyCompletedChan := make(chan bool)
propsChan := make(chan bool)
errChan := make(chan error)
s.AdditionalProperties = schema.AdditionalProperties.Value
}
}
s.Description = schema.Description.Value
s.Default = schema.Default.Value
if !schema.Nullable.IsEmpty() {
s.Nullable = &schema.Nullable.Value
}
if !schema.ReadOnly.IsEmpty() {
s.ReadOnly = schema.ReadOnly.Value
}
if !schema.WriteOnly.IsEmpty() {
s.WriteOnly = schema.WriteOnly.Value
}
if !schema.Deprecated.IsEmpty() {
s.Deprecated = &schema.Deprecated.Value
}
s.Example = schema.Example.Value
if len(schema.Examples.Value) > 0 {
examples := make([]any, len(schema.Examples.Value))
for i := 0; i < len(schema.Examples.Value); i++ {
examples[i] = schema.Examples.Value[i].Value
}
s.Examples = examples
}
s.Extensions = high.ExtractExtensions(schema.Extensions)
if !schema.Discriminator.IsEmpty() {
s.Discriminator = NewDiscriminator(schema.Discriminator.Value)
}
if !schema.XML.IsEmpty() {
s.XML = NewXML(schema.XML.Value)
}
if !schema.ExternalDocs.IsEmpty() {
s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value)
}
var req []string
for i := range schema.Required.Value {
req = append(req, schema.Required.Value[i].Value)
}
s.Required = req
type buildResult struct {
idx int
s *SchemaProxy
}
var enum []any
if !schema.Anchor.IsEmpty() {
s.Anchor = schema.Anchor.Value
}
// for every item, build schema async
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: sch.ValueNode,
Value: sch.Value,
Reference: sch.GetReference(),
}}
// TODO: check this behavior.
for i := range schema.Enum.Value {
enum = append(enum, schema.Enum.Value[i].Value)
}
s.Enum = enum
bChan <- buildResult{idx: idx, s: p}
}
// async work.
// any polymorphic properties need to be handled in their own threads
// any properties each need to be processed in their own thread.
// we go as fast as we can.
polyCompletedChan := make(chan bool)
propsChan := make(chan bool)
errChan := make(chan error)
// schema async
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
doneChan chan bool, e chan error,
) {
bChan := make(chan buildResult)
totalSchemas := len(schemas)
for i := range schemas {
go buildSchema(schemas[i], i, bChan)
}
j := 0
for j < totalSchemas {
select {
case r := <-bChan:
j++
(*items)[r.idx] = r.s
}
}
doneChan <- true
}
type buildResult struct {
idx int
s *SchemaProxy
}
// props async
var plock sync.Mutex
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
props map[string]*SchemaProxy, sw int,
) {
plock.Lock()
props[k.Value] = &SchemaProxy{
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
Value: v.Value,
KeyNode: k.KeyNode,
ValueNode: v.ValueNode,
},
}
plock.Unlock()
// for every item, build schema async
buildSchema := func(sch lowmodel.ValueReference[*base.SchemaProxy], idx int, bChan chan buildResult) {
p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: sch.ValueNode,
Value: sch.Value,
Reference: sch.GetReference(),
}}
switch sw {
case 0:
s.Properties = props
case 1:
s.DependentSchemas = props
case 2:
s.PatternProperties = props
}
c <- true
}
bChan <- buildResult{idx: idx, s: p}
}
props := make(map[string]*SchemaProxy)
for k, v := range schema.Properties.Value {
go buildProps(k, v, propsChan, props, 0)
}
// schema async
buildOutSchemas := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy,
doneChan chan bool, e chan error,
) {
bChan := make(chan buildResult)
totalSchemas := len(schemas)
for i := range schemas {
go buildSchema(schemas[i], i, bChan)
}
j := 0
for j < totalSchemas {
select {
case r := <-bChan:
j++
(*items)[r.idx] = r.s
}
}
doneChan <- true
}
dependents := make(map[string]*SchemaProxy)
for k, v := range schema.DependentSchemas.Value {
go buildProps(k, v, propsChan, dependents, 1)
}
patternProps := make(map[string]*SchemaProxy)
for k, v := range schema.PatternProperties.Value {
go buildProps(k, v, propsChan, patternProps, 2)
}
// props async
var plock sync.Mutex
buildProps := func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool,
props map[string]*SchemaProxy, sw int,
) {
plock.Lock()
props[k.Value] = &SchemaProxy{
schema: &lowmodel.NodeReference[*base.SchemaProxy]{
Value: v.Value,
KeyNode: k.KeyNode,
ValueNode: v.ValueNode,
},
}
plock.Unlock()
var allOf []*SchemaProxy
var oneOf []*SchemaProxy
var anyOf []*SchemaProxy
var not *SchemaProxy
var items *DynamicValue[*SchemaProxy, bool]
var prefixItems []*SchemaProxy
switch sw {
case 0:
s.Properties = props
case 1:
s.DependentSchemas = props
case 2:
s.PatternProperties = props
}
c <- true
}
children := 0
if !schema.AllOf.IsEmpty() {
children++
allOf = make([]*SchemaProxy, len(schema.AllOf.Value))
go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
}
if !schema.AnyOf.IsEmpty() {
children++
anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value))
go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
}
if !schema.OneOf.IsEmpty() {
children++
oneOf = make([]*SchemaProxy, len(schema.OneOf.Value))
go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan)
}
if !schema.Not.IsEmpty() {
not = NewSchemaProxy(&schema.Not)
}
if !schema.Items.IsEmpty() {
if schema.Items.Value.IsA() {
items = &DynamicValue[*SchemaProxy, bool]{A: &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Items.ValueNode,
Value: schema.Items.Value.A,
KeyNode: schema.Items.KeyNode,
}}}
} else {
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
}
}
if !schema.PrefixItems.IsEmpty() {
children++
prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value))
go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan)
}
props := make(map[string]*SchemaProxy)
for k, v := range schema.Properties.Value {
go buildProps(k, v, propsChan, props, 0)
}
completeChildren := 0
completedProps := 0
totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value)
if totalProps+children > 0 {
allDone:
for true {
select {
case <-polyCompletedChan:
completeChildren++
if totalProps == completedProps && children == completeChildren {
break allDone
}
case <-propsChan:
completedProps++
if totalProps == completedProps && children == completeChildren {
break allDone
}
}
}
}
s.OneOf = oneOf
s.AnyOf = anyOf
s.AllOf = allOf
s.Items = items
s.PrefixItems = prefixItems
s.Not = not
return s
dependents := make(map[string]*SchemaProxy)
for k, v := range schema.DependentSchemas.Value {
go buildProps(k, v, propsChan, dependents, 1)
}
patternProps := make(map[string]*SchemaProxy)
for k, v := range schema.PatternProperties.Value {
go buildProps(k, v, propsChan, patternProps, 2)
}
var allOf []*SchemaProxy
var oneOf []*SchemaProxy
var anyOf []*SchemaProxy
var not *SchemaProxy
var items *DynamicValue[*SchemaProxy, bool]
var prefixItems []*SchemaProxy
children := 0
if !schema.AllOf.IsEmpty() {
children++
allOf = make([]*SchemaProxy, len(schema.AllOf.Value))
go buildOutSchemas(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
}
if !schema.AnyOf.IsEmpty() {
children++
anyOf = make([]*SchemaProxy, len(schema.AnyOf.Value))
go buildOutSchemas(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
}
if !schema.OneOf.IsEmpty() {
children++
oneOf = make([]*SchemaProxy, len(schema.OneOf.Value))
go buildOutSchemas(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan)
}
if !schema.Not.IsEmpty() {
not = NewSchemaProxy(&schema.Not)
}
if !schema.Items.IsEmpty() {
if schema.Items.Value.IsA() {
items = &DynamicValue[*SchemaProxy, bool]{A: &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.Items.ValueNode,
Value: schema.Items.Value.A,
KeyNode: schema.Items.KeyNode,
}}}
} else {
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
}
}
if !schema.PrefixItems.IsEmpty() {
children++
prefixItems = make([]*SchemaProxy, len(schema.PrefixItems.Value))
go buildOutSchemas(schema.PrefixItems.Value, &prefixItems, polyCompletedChan, errChan)
}
completeChildren := 0
completedProps := 0
totalProps := len(schema.Properties.Value) + len(schema.DependentSchemas.Value) + len(schema.PatternProperties.Value)
if totalProps+children > 0 {
allDone:
for true {
select {
case <-polyCompletedChan:
completeChildren++
if totalProps == completedProps && children == completeChildren {
break allDone
}
case <-propsChan:
completedProps++
if totalProps == completedProps && children == completeChildren {
break allDone
}
}
}
}
s.OneOf = oneOf
s.AnyOf = anyOf
s.AllOf = allOf
s.Items = items
s.PrefixItems = prefixItems
s.Not = not
return s
}
// GoLow will return the low-level instance of Schema that was used to create the high level one.
func (s *Schema) GoLow() *base.Schema {
return s.low
return s.low
}
// GoLowUntyped will return the low-level Schema instance that was used to create the high-level one, with no type
func (s *Schema) GoLowUntyped() any {
return s.low
return s.low
}
// Render will return a YAML representation of the Schema object as a byte slice.
func (s *Schema) Render() ([]byte, error) {
return yaml.Marshal(s)
return yaml.Marshal(s)
}
// RenderInline will return a YAML representation of the Schema object as a byte slice. All of the
@@ -467,18 +486,18 @@ func (s *Schema) Render() ([]byte, error) {
//
// Make sure you don't have any circular references!
func (s *Schema) RenderInline() ([]byte, error) {
d, _ := s.MarshalYAMLInline()
return yaml.Marshal(d)
d, _ := s.MarshalYAMLInline()
return yaml.Marshal(d)
}
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
func (s *Schema) MarshalYAML() (interface{}, error) {
nb := high.NewNodeBuilder(s, s.low)
return nb.Render(), nil
nb := high.NewNodeBuilder(s, s.low)
return nb.Render(), nil
}
func (s *Schema) MarshalYAMLInline() (interface{}, error) {
nb := high.NewNodeBuilder(s, s.low)
nb.Resolve = true
return nb.Render(), nil
nb := high.NewNodeBuilder(s, s.low)
nb.Resolve = true
return nb.Render(), nil
}

View File

@@ -4,17 +4,17 @@
package base
import (
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestSchemaProxy_MarshalYAML(t *testing.T) {
const ymlComponents = `components:
const ymlComponents = `components:
schemas:
rice:
type: string
@@ -27,42 +27,41 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) {
rice:
$ref: '#/components/schemas/rice'`
idx := func() *index.SpecIndex {
var idxNode yaml.Node
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
assert.NoError(t, err)
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
}()
idx := func() *index.SpecIndex {
var idxNode yaml.Node
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
assert.NoError(t, err)
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
}()
const ref = "#/components/schemas/nice"
const ymlSchema = `$ref: '` + ref + `'`
var node yaml.Node
_ = yaml.Unmarshal([]byte(ymlSchema), &node)
const ref = "#/components/schemas/nice"
const ymlSchema = `$ref: '` + ref + `'`
var node yaml.Node
_ = yaml.Unmarshal([]byte(ymlSchema), &node)
lowProxy := new(lowbase.SchemaProxy)
err := lowProxy.Build(node.Content[0], idx)
assert.NoError(t, err)
lowProxy := new(lowbase.SchemaProxy)
err := lowProxy.Build(node.Content[0], idx)
assert.NoError(t, err)
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
Value: lowProxy,
}
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
Value: lowProxy,
}
sp := NewSchemaProxy(&lowRef)
sp := NewSchemaProxy(&lowRef)
rend, _ := sp.Render()
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
rend, _ := sp.Render()
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
}
func TestCreateSchemaProxy(t *testing.T) {
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
assert.Equal(t, "iAmASchema", sp.rendered.Description)
assert.False(t, sp.IsReference())
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
assert.Equal(t, "iAmASchema", sp.rendered.Description)
assert.False(t, sp.IsReference())
}
func TestCreateSchemaProxyRef(t *testing.T) {
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
assert.True(t, sp.IsReference())
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
assert.True(t, sp.IsReference())
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,7 +17,7 @@ import (
// security schemes declared in it which are all required (that is, there is a logical AND between the schemes).
//
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityRequirement struct {
Requirements map[string][]string `json:"-" yaml:"-"`
low *base.SecurityRequirement
@@ -108,7 +108,7 @@ func (s *SecurityRequirement) MarshalYAML() (interface{}, error) {
for t := range keys[k].val {
reqs[t] = &req{val: keys[k].val[t], line: 9999 + t}
if keys[k].lowVal != nil {
for _ = range keys[k].lowVal.Value[t].Value {
for range keys[k].lowVal.Value[t].Value {
fh := keys[k].val[t]
df := keys[k].lowVal.Value[t].Value
if fh == df {

View File

@@ -4,9 +4,9 @@
package base
import (
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/base"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/base"
"gopkg.in/yaml.v3"
)
// XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by
@@ -16,49 +16,49 @@ import (
//
// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be
// used to add that information. See examples for expected behavior.
// v2 - https://swagger.io/specification/v2/#xmlObject
// v3 - https://swagger.io/specification/#xml-object
//
// v2 - https://swagger.io/specification/v2/#xmlObject
// v3 - https://swagger.io/specification/#xml-object
type XML struct {
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
Extensions map[string]any
low *low.XML
Name string `json:"name,omitempty" yaml:"name,omitempty"`
Namespace string `json:"namespace,omitempty" yaml:"namespace,omitempty"`
Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"`
Attribute bool `json:"attribute,omitempty" yaml:"attribute,omitempty"`
Wrapped bool `json:"wrapped,omitempty" yaml:"wrapped,omitempty"`
Extensions map[string]any
low *low.XML
}
// NewXML creates a new high-level XML instance from a low-level one.
func NewXML(xml *low.XML) *XML {
x := new(XML)
x.low = xml
x.Name = xml.Name.Value
x.Namespace = xml.Namespace.Value
x.Prefix = xml.Prefix.Value
x.Attribute = xml.Attribute.Value
x.Wrapped = xml.Wrapped.Value
x.Extensions = high.ExtractExtensions(xml.Extensions)
return x
x := new(XML)
x.low = xml
x.Name = xml.Name.Value
x.Namespace = xml.Namespace.Value
x.Prefix = xml.Prefix.Value
x.Attribute = xml.Attribute.Value
x.Wrapped = xml.Wrapped.Value
x.Extensions = high.ExtractExtensions(xml.Extensions)
return x
}
// GoLow returns the low level XML reference used to create the high level one.
func (x *XML) GoLow() *low.XML {
return x.low
return x.low
}
// GoLowUntyped will return the low-level XML instance that was used to create the high-level one, with no type
func (x *XML) GoLowUntyped() any {
return x.low
return x.low
}
// Render will return a YAML representation of the XML object as a byte slice.
func (x *XML) Render() ([]byte, error) {
return yaml.Marshal(x)
return yaml.Marshal(x)
}
// MarshalYAML will create a ready to render YAML representation of the XML object.
func (x *XML) MarshalYAML() (interface{}, error) {
nb := high.NewNodeBuilder(x, x.low)
return nb.Render(), nil
nb := high.NewNodeBuilder(x, x.low)
return nb.Render(), nil
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -58,8 +58,9 @@ func ExtractExtensions(extensions map[low.KeyReference[string]]low.ValueReferenc
// `low` represents the HIGH type of the object that contains the extensions.
//
// to use:
// schema := schemaProxy.Schema() // any high-level object that has extensions
// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema)
//
// schema := schemaProxy.Schema() // any high-level object that has extensions
// extensions, err := UnpackExtensions[MyComplexType, low.Schema](schema)
func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string]*T, error) {
m := make(map[string]*T)
ext := low.GoLow().GetExtensions()
@@ -75,4 +76,3 @@ func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string
}
return m, nil
}

View File

@@ -4,127 +4,127 @@
package high
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestExtractExtensions(t *testing.T) {
n := make(map[low.KeyReference[string]]low.ValueReference[any])
n[low.KeyReference[string]{
Value: "pb33f",
}] = low.ValueReference[any]{
Value: "new cowboy in town",
}
ext := ExtractExtensions(n)
assert.Equal(t, "new cowboy in town", ext["pb33f"])
n := make(map[low.KeyReference[string]]low.ValueReference[any])
n[low.KeyReference[string]{
Value: "pb33f",
}] = low.ValueReference[any]{
Value: "new cowboy in town",
}
ext := ExtractExtensions(n)
assert.Equal(t, "new cowboy in town", ext["pb33f"])
}
type textExtension struct {
Cowboy string
Power int
Cowboy string
Power int
}
type parent struct {
low *child
low *child
}
func (p *parent) GoLow() *child {
return p.low
return p.low
}
type child struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
}
func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
return c.Extensions
return c.Extensions
}
func TestUnpackExtensions(t *testing.T) {
var resultA, resultB yaml.Node
var resultA, resultB yaml.Node
ymlA := `
ymlA := `
cowboy: buckaroo
power: 100`
ymlB := `
ymlB := `
cowboy: frogman
power: 2`
err := yaml.Unmarshal([]byte(ymlA), &resultA)
assert.NoError(t, err)
err = yaml.Unmarshal([]byte(ymlB), &resultB)
assert.NoError(t, err)
err := yaml.Unmarshal([]byte(ymlA), &resultA)
assert.NoError(t, err)
err = yaml.Unmarshal([]byte(ymlB), &resultB)
assert.NoError(t, err)
n := make(map[low.KeyReference[string]]low.ValueReference[any])
n[low.KeyReference[string]{
Value: "x-rancher-a",
}] = low.ValueReference[any]{
ValueNode: resultA.Content[0],
}
n := make(map[low.KeyReference[string]]low.ValueReference[any])
n[low.KeyReference[string]{
Value: "x-rancher-a",
}] = low.ValueReference[any]{
ValueNode: resultA.Content[0],
}
n[low.KeyReference[string]{
Value: "x-rancher-b",
}] = low.ValueReference[any]{
ValueNode: resultB.Content[0],
}
n[low.KeyReference[string]{
Value: "x-rancher-b",
}] = low.ValueReference[any]{
ValueNode: resultB.Content[0],
}
c := new(child)
c.Extensions = n
c := new(child)
c.Extensions = n
p := new(parent)
p.low = c
p := new(parent)
p.low = c
res, err := UnpackExtensions[textExtension, *child](p)
assert.NoError(t, err)
assert.NotEmpty(t, res)
assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy)
assert.Equal(t, 100, res["x-rancher-a"].Power)
assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy)
assert.Equal(t, 2, res["x-rancher-b"].Power)
res, err := UnpackExtensions[textExtension, *child](p)
assert.NoError(t, err)
assert.NotEmpty(t, res)
assert.Equal(t, "buckaroo", res["x-rancher-a"].Cowboy)
assert.Equal(t, 100, res["x-rancher-a"].Power)
assert.Equal(t, "frogman", res["x-rancher-b"].Cowboy)
assert.Equal(t, 2, res["x-rancher-b"].Power)
}
func TestUnpackExtensions_Fail(t *testing.T) {
var resultA, resultB yaml.Node
var resultA, resultB yaml.Node
ymlA := `
ymlA := `
cowboy: buckaroo
power: 100`
// this is incorrect types, unpacking will fail.
ymlB := `
// this is incorrect types, unpacking will fail.
ymlB := `
cowboy: 0
power: hello`
err := yaml.Unmarshal([]byte(ymlA), &resultA)
assert.NoError(t, err)
err = yaml.Unmarshal([]byte(ymlB), &resultB)
assert.NoError(t, err)
err := yaml.Unmarshal([]byte(ymlA), &resultA)
assert.NoError(t, err)
err = yaml.Unmarshal([]byte(ymlB), &resultB)
assert.NoError(t, err)
n := make(map[low.KeyReference[string]]low.ValueReference[any])
n[low.KeyReference[string]{
Value: "x-rancher-a",
}] = low.ValueReference[any]{
ValueNode: resultA.Content[0],
}
n := make(map[low.KeyReference[string]]low.ValueReference[any])
n[low.KeyReference[string]{
Value: "x-rancher-a",
}] = low.ValueReference[any]{
ValueNode: resultA.Content[0],
}
n[low.KeyReference[string]{
Value: "x-rancher-b",
}] = low.ValueReference[any]{
ValueNode: resultB.Content[0],
}
n[low.KeyReference[string]{
Value: "x-rancher-b",
}] = low.ValueReference[any]{
ValueNode: resultB.Content[0],
}
c := new(child)
c.Extensions = n
c := new(child)
c.Extensions = n
p := new(parent)
p.low = c
p := new(parent)
p.low = c
res, er := UnpackExtensions[textExtension, *child](p)
assert.Error(t, er)
assert.Empty(t, res)
res, er := UnpackExtensions[textExtension, *child](p)
assert.Error(t, er)
assert.Empty(t, res)
}

View File

@@ -14,7 +14,7 @@ import (
//
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
// arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject
// - https://swagger.io/specification/v2/#definitionsObject
type Definitions struct {
Definitions map[string]*highbase.SchemaProxy
low *low.Definitions

View File

@@ -7,7 +7,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
// Example represents a high-level Swagger / OpenAPI 2 Example object, backed by a low level one.
// Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject
// - https://swagger.io/specification/v2/#exampleObject
type Example struct {
Values map[string]any
low *low.Examples

View File

@@ -10,7 +10,7 @@ import (
// Header Represents a high-level Swagger / OpenAPI 2 Header object, backed by a low-level one.
// A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties.
// - https://swagger.io/specification/v2/#headerObject
// - https://swagger.io/specification/v2/#headerObject
type Header struct {
Type string
Format string

View File

@@ -10,7 +10,7 @@ import (
// Items is a high-level representation of a Swagger / OpenAPI 2 Items object, backed by a low level one.
// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not
// located in "body"
// - https://swagger.io/specification/v2/#itemsObject
// - https://swagger.io/specification/v2/#itemsObject
type Items struct {
Type string
Format string

View File

@@ -11,7 +11,7 @@ import (
// Operation represents a high-level Swagger / OpenAPI 2 Operation object, backed by a low-level one.
// It describes a single API operation on a path.
// - https://swagger.io/specification/v2/#operationObject
// - https://swagger.io/specification/v2/#operationObject
type Operation struct {
Tags []string
Summary string

View File

@@ -16,29 +16,39 @@ import (
// There are five possible parameter types.
//
// Path
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
//
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
//
// Query
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
//
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
//
// Header
// Custom headers that are expected as part of the request.
//
// Custom headers that are expected as part of the request.
//
// Body
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
//
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
//
// Form
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
// parameters have a different format based on the content-type used (for further details,
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
// being transferred.
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
// submit-name. This type of form parameters is more commonly used for file transfers
//
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
// parameters have a different format based on the content-type used (for further details,
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
// being transferred.
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
// submit-name. This type of form parameters is more commonly used for file transfers
//
// https://swagger.io/specification/v2/#parameterObject
type Parameter struct {
Name string

View File

@@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
//
// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be
// referenced to the ones defined here. It does not define global operation parameters
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
type ParameterDefinitions struct {
Definitions map[string]*Parameter
low *low.ParameterDefinitions

View File

@@ -4,8 +4,8 @@
package v2
import (
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/datamodel/high"
low "github.com/pb33f/libopenapi/datamodel/low/v2"
)
// PathItem represents a high-level Swagger / OpenAPI 2 PathItem object backed by a low-level one.
@@ -13,128 +13,128 @@ import (
// Describes the operations available on a single path. A Path Item may be empty, due to ACL constraints.
// The path itself is still exposed to the tooling, but will not know which operations and parameters
// are available.
// - https://swagger.io/specification/v2/#pathItemObject
// - https://swagger.io/specification/v2/#pathItemObject
type PathItem struct {
Ref string
Get *Operation
Put *Operation
Post *Operation
Delete *Operation
Options *Operation
Head *Operation
Patch *Operation
Parameters []*Parameter
Extensions map[string]any
low *low.PathItem
Ref string
Get *Operation
Put *Operation
Post *Operation
Delete *Operation
Options *Operation
Head *Operation
Patch *Operation
Parameters []*Parameter
Extensions map[string]any
low *low.PathItem
}
// NewPathItem will create a new high-level PathItem from a low-level one. All paths are built out asynchronously.
func NewPathItem(pathItem *low.PathItem) *PathItem {
p := new(PathItem)
p.low = pathItem
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
if !pathItem.Parameters.IsEmpty() {
var params []*Parameter
for k := range pathItem.Parameters.Value {
params = append(params, NewParameter(pathItem.Parameters.Value[k].Value))
}
p.Parameters = params
}
var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) {
resChan <- asyncResult[*Operation]{
key: method,
result: NewOperation(op),
}
}
totalOperations := 0
resChan := make(chan asyncResult[*Operation])
if !pathItem.Get.IsEmpty() {
totalOperations++
go buildOperation(low.GetLabel, pathItem.Get.Value, resChan)
}
if !pathItem.Put.IsEmpty() {
totalOperations++
go buildOperation(low.PutLabel, pathItem.Put.Value, resChan)
}
if !pathItem.Post.IsEmpty() {
totalOperations++
go buildOperation(low.PostLabel, pathItem.Post.Value, resChan)
}
if !pathItem.Patch.IsEmpty() {
totalOperations++
go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan)
}
if !pathItem.Delete.IsEmpty() {
totalOperations++
go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan)
}
if !pathItem.Head.IsEmpty() {
totalOperations++
go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan)
}
if !pathItem.Options.IsEmpty() {
totalOperations++
go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan)
}
completedOperations := 0
for completedOperations < totalOperations {
select {
case r := <-resChan:
switch r.key {
case low.GetLabel:
completedOperations++
p.Get = r.result
case low.PutLabel:
completedOperations++
p.Put = r.result
case low.PostLabel:
completedOperations++
p.Post = r.result
case low.PatchLabel:
completedOperations++
p.Patch = r.result
case low.DeleteLabel:
completedOperations++
p.Delete = r.result
case low.HeadLabel:
completedOperations++
p.Head = r.result
case low.OptionsLabel:
completedOperations++
p.Options = r.result
}
}
}
return p
p := new(PathItem)
p.low = pathItem
p.Extensions = high.ExtractExtensions(pathItem.Extensions)
if !pathItem.Parameters.IsEmpty() {
var params []*Parameter
for k := range pathItem.Parameters.Value {
params = append(params, NewParameter(pathItem.Parameters.Value[k].Value))
}
p.Parameters = params
}
var buildOperation = func(method string, op *low.Operation, resChan chan<- asyncResult[*Operation]) {
resChan <- asyncResult[*Operation]{
key: method,
result: NewOperation(op),
}
}
totalOperations := 0
resChan := make(chan asyncResult[*Operation])
if !pathItem.Get.IsEmpty() {
totalOperations++
go buildOperation(low.GetLabel, pathItem.Get.Value, resChan)
}
if !pathItem.Put.IsEmpty() {
totalOperations++
go buildOperation(low.PutLabel, pathItem.Put.Value, resChan)
}
if !pathItem.Post.IsEmpty() {
totalOperations++
go buildOperation(low.PostLabel, pathItem.Post.Value, resChan)
}
if !pathItem.Patch.IsEmpty() {
totalOperations++
go buildOperation(low.PatchLabel, pathItem.Patch.Value, resChan)
}
if !pathItem.Delete.IsEmpty() {
totalOperations++
go buildOperation(low.DeleteLabel, pathItem.Delete.Value, resChan)
}
if !pathItem.Head.IsEmpty() {
totalOperations++
go buildOperation(low.HeadLabel, pathItem.Head.Value, resChan)
}
if !pathItem.Options.IsEmpty() {
totalOperations++
go buildOperation(low.OptionsLabel, pathItem.Options.Value, resChan)
}
completedOperations := 0
for completedOperations < totalOperations {
select {
case r := <-resChan:
switch r.key {
case low.GetLabel:
completedOperations++
p.Get = r.result
case low.PutLabel:
completedOperations++
p.Put = r.result
case low.PostLabel:
completedOperations++
p.Post = r.result
case low.PatchLabel:
completedOperations++
p.Patch = r.result
case low.DeleteLabel:
completedOperations++
p.Delete = r.result
case low.HeadLabel:
completedOperations++
p.Head = r.result
case low.OptionsLabel:
completedOperations++
p.Options = r.result
}
}
}
return p
}
// GoLow returns the low-level PathItem used to create the high-level one.
func (p *PathItem) GoLow() *low.PathItem {
return p.low
return p.low
}
func (p *PathItem) GetOperations() map[string]*Operation {
o := make(map[string]*Operation)
if p.Get != nil {
o[low.GetLabel] = p.Get
}
if p.Put != nil {
o[low.PutLabel] = p.Put
}
if p.Post != nil {
o[low.PostLabel] = p.Post
}
if p.Delete != nil {
o[low.DeleteLabel] = p.Delete
}
if p.Options != nil {
o[low.OptionsLabel] = p.Options
}
if p.Head != nil {
o[low.HeadLabel] = p.Head
}
if p.Patch != nil {
o[low.PatchLabel] = p.Patch
}
return o
o := make(map[string]*Operation)
if p.Get != nil {
o[low.GetLabel] = p.Get
}
if p.Put != nil {
o[low.PutLabel] = p.Put
}
if p.Post != nil {
o[low.PostLabel] = p.Post
}
if p.Delete != nil {
o[low.DeleteLabel] = p.Delete
}
if p.Options != nil {
o[low.OptionsLabel] = p.Options
}
if p.Head != nil {
o[low.HeadLabel] = p.Head
}
if p.Patch != nil {
o[low.PatchLabel] = p.Patch
}
return o
}

View File

@@ -4,17 +4,17 @@
package v2
import (
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestPathItem_GetOperations(t *testing.T) {
yml := `get:
yml := `get:
description: get
put:
description: put
@@ -30,15 +30,15 @@ options:
description: options
`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n v2.PathItem
_ = low.BuildModel(&idxNode, &n)
_ = n.Build(idxNode.Content[0], idx)
var n v2.PathItem
_ = low.BuildModel(&idxNode, &n)
_ = n.Build(idxNode.Content[0], idx)
r := NewPathItem(&n)
r := NewPathItem(&n)
assert.Len(t, r.GetOperations(), 7)
assert.Len(t, r.GetOperations(), 7)
}

View File

@@ -11,7 +11,7 @@ import (
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
// Response describes a single response from an API Operation
// - https://swagger.io/specification/v2/#responseObject
// - https://swagger.io/specification/v2/#responseObject
type Response struct {
Description string
Schema *base.SchemaProxy

View File

@@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
//
// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be
// referenced to the ones defined here. It does not define global operation responses
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
type ResponsesDefinitions struct {
Definitions map[string]*Response
low *low.ResponsesDefinitions

View File

@@ -10,7 +10,7 @@ import (
// Scopes is a high-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object, that is backed by a low-level one.
//
// Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject
// - https://swagger.io/specification/v2/#scopesObject
type Scopes struct {
Values map[string]string
low *low.Scopes

View File

@@ -10,7 +10,7 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v2"
//
// A declaration of the security schemes available to be used in the specification. This does not enforce the security
// schemes on the operations and only serves to provide the relevant details for each scheme
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityDefinitions struct {
Definitions map[string]*SecurityScheme
low *low.SecurityDefinitions

View File

@@ -14,7 +14,7 @@ import (
// SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are
// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows
// (implicit, password, application and access code)
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityScheme struct {
Type string
Description string

View File

@@ -17,7 +17,7 @@ import (
// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected
// responses. The key value used to identify the path item object is an expression, evaluated at runtime,
// that identifies a URL to use for the callback operation.
// - https://spec.openapis.org/oas/v3.1.0#callback-object
// - https://spec.openapis.org/oas/v3.1.0#callback-object
type Callback struct {
Expression map[string]*PathItem `json:"-" yaml:"-"`
Extensions map[string]any `json:"-" yaml:"-"`

View File

@@ -4,49 +4,49 @@
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestCallback_MarshalYAML(t *testing.T) {
cb := &Callback{
Expression: map[string]*PathItem{
"https://pb33f.io": {
Get: &Operation{
OperationId: "oneTwoThree",
},
},
"https://pb33f.io/libopenapi": {
Get: &Operation{
OperationId: "openaypeeeye",
},
},
},
Extensions: map[string]any{
"x-burgers": "why not?",
},
}
cb := &Callback{
Expression: map[string]*PathItem{
"https://pb33f.io": {
Get: &Operation{
OperationId: "oneTwoThree",
},
},
"https://pb33f.io/libopenapi": {
Get: &Operation{
OperationId: "openaypeeeye",
},
},
},
Extensions: map[string]any{
"x-burgers": "why not?",
},
}
rend, _ := cb.Render()
rend, _ := cb.Render()
// there is no way to determine order in brand new maps, so we have to check length.
assert.Len(t, rend, 152)
// there is no way to determine order in brand new maps, so we have to check length.
assert.Len(t, rend, 152)
// mutate
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
// mutate
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
rend, _ = cb.Render()
// there is no way to determine order in brand new maps, so we have to check length.
assert.Len(t, rend, 153)
rend, _ = cb.Render()
// there is no way to determine order in brand new maps, so we have to check length.
assert.Len(t, rend, 153)
k := `x-break-everything: please
k := `x-break-everything: please
'{$request.query.queryUrl}':
post:
description: Callback payload
@@ -58,19 +58,19 @@ func TestCallback_MarshalYAML(t *testing.T) {
schema:
type: string`
var idxNode yaml.Node
err := yaml.Unmarshal([]byte(k), &idxNode)
assert.NoError(t, err)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
var idxNode yaml.Node
err := yaml.Unmarshal([]byte(k), &idxNode)
assert.NoError(t, err)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
var n v3.Callback
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n v3.Callback
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
r := NewCallback(&n)
r := NewCallback(&n)
assert.Equal(t, "please", r.Extensions["x-break-everything"])
assert.Equal(t, "please", r.Extensions["x-break-everything"])
rend, _ = r.Render()
assert.Equal(t, k, strings.TrimSpace(string(rend)))
rend, _ = r.Render()
assert.Equal(t, k, strings.TrimSpace(string(rend)))
}

View File

@@ -28,18 +28,18 @@ const (
//
// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
// - https://spec.openapis.org/oas/v3.1.0#components-object
// - https://spec.openapis.org/oas/v3.1.0#components-object
type Components struct {
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`
SecuritySchemes map[string]*SecurityScheme `json:"securitySchemes,omitempty" yaml:"securitySchemes,omitempty"`
Links map[string]*Link `json:"links,omitempty" yaml:"links,omitempty"`
Callbacks map[string]*Callback `json:"callbacks,omitempty" yaml:"callbacks,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"`
low *low.Components
}

View File

@@ -4,53 +4,53 @@
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestComponents_MarshalYAML(t *testing.T) {
comp := &Components{
Responses: map[string]*Response{
"200": {
Description: "OK",
},
},
Parameters: map[string]*Parameter{
"id": {
Name: "id",
In: "path",
},
},
RequestBodies: map[string]*RequestBody{
"body": {
Content: map[string]*MediaType{
"application/json": {
Example: "why?",
},
},
},
},
}
comp := &Components{
Responses: map[string]*Response{
"200": {
Description: "OK",
},
},
Parameters: map[string]*Parameter{
"id": {
Name: "id",
In: "path",
},
},
RequestBodies: map[string]*RequestBody{
"body": {
Content: map[string]*MediaType{
"application/json": {
Example: "why?",
},
},
},
},
}
dat, _ := comp.Render()
dat, _ := comp.Render()
var idxNode yaml.Node
_ = yaml.Unmarshal(dat, &idxNode)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
var idxNode yaml.Node
_ = yaml.Unmarshal(dat, &idxNode)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
var n v3.Components
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n v3.Components
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
r := NewComponents(&n)
r := NewComponents(&n)
desired := `responses:
desired := `responses:
"200":
description: OK
parameters:
@@ -63,6 +63,6 @@ requestBodies:
application/json:
example: why?`
dat, _ = r.Render()
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
dat, _ = r.Render()
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
}

View File

@@ -11,7 +11,7 @@ import (
)
// Encoding represents an OpenAPI 3+ Encoding object
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
type Encoding struct {
ContentType string `json:"contentType,omitempty" yaml:"contentType,omitempty"`
Headers map[string]*Header `json:"headers,omitempty" yaml:"headers,omitempty"`

View File

@@ -4,64 +4,64 @@
package v3
import (
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func TestEncoding_MarshalYAML(t *testing.T) {
explode := true
encoding := &Encoding{
ContentType: "application/json",
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}},
Style: "simple",
Explode: &explode,
}
explode := true
encoding := &Encoding{
ContentType: "application/json",
Headers: map[string]*Header{"x-pizza-time": {Description: "oh yes please"}},
Style: "simple",
Explode: &explode,
}
rend, _ := encoding.Render()
rend, _ := encoding.Render()
desired := `contentType: application/json
desired := `contentType: application/json
headers:
x-pizza-time:
description: oh yes please
style: simple
explode: true`
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
explode = false
encoding.Explode = &explode
rend, _ = encoding.Render()
explode = false
encoding.Explode = &explode
rend, _ = encoding.Render()
desired = `contentType: application/json
desired = `contentType: application/json
headers:
x-pizza-time:
description: oh yes please
style: simple`
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
encoding.Explode = nil
rend, _ = encoding.Render()
encoding.Explode = nil
rend, _ = encoding.Render()
desired = `contentType: application/json
desired = `contentType: application/json
headers:
x-pizza-time:
description: oh yes please
style: simple`
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
encoding.Explode = &explode
rend, _ = encoding.Render()
encoding.Explode = &explode
rend, _ = encoding.Render()
desired = `contentType: application/json
desired = `contentType: application/json
headers:
x-pizza-time:
description: oh yes please
style: simple`
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
}

View File

@@ -13,7 +13,7 @@ import (
)
// Header represents a high-level OpenAPI 3+ Header object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#header-object
// - https://spec.openapis.org/oas/v3.1.0#header-object
type Header struct {
Description string `json:"description,omitempty" yaml:"description,omitempty"`
Required bool `json:"required,omitempty" yaml:"required,omitempty"`

View File

@@ -4,30 +4,30 @@
package v3
import (
"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/high/base"
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func TestHeader_MarshalYAML(t *testing.T) {
header := &Header{
Description: "A header",
Required: true,
Deprecated: true,
AllowEmptyValue: true,
Style: "simple",
Explode: true,
AllowReserved: true,
Example: "example",
Examples: map[string]*base.Example{"example": {Value: "example"}},
Extensions: map[string]interface{}{"x-burgers": "why not?"},
}
header := &Header{
Description: "A header",
Required: true,
Deprecated: true,
AllowEmptyValue: true,
Style: "simple",
Explode: true,
AllowReserved: true,
Example: "example",
Examples: map[string]*base.Example{"example": {Value: "example"}},
Extensions: map[string]interface{}{"x-burgers": "why not?"},
}
rend, _ := header.Render()
rend, _ := header.Render()
desired := `description: A header
desired := `description: A header
required: true
deprecated: true
allowEmptyValue: true
@@ -40,6 +40,6 @@ examples:
value: example
x-burgers: why not?`
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
}

View File

@@ -4,27 +4,27 @@
package v3
import (
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func TestLink_MarshalYAML(t *testing.T) {
link := Link{
OperationRef: "somewhere",
OperationId: "somewhereOutThere",
Parameters: map[string]string{
"over": "theRainbow",
},
RequestBody: "hello?",
Description: "are you there?",
Server: &Server{
URL: "https://pb33f.io",
},
}
link := Link{
OperationRef: "somewhere",
OperationId: "somewhereOutThere",
Parameters: map[string]string{
"over": "theRainbow",
},
RequestBody: "hello?",
Description: "are you there?",
Server: &Server{
URL: "https://pb33f.io",
},
}
dat, _ := link.Render()
desired := `operationRef: somewhere
dat, _ := link.Render()
desired := `operationRef: somewhere
operationId: somewhereOutThere
parameters:
over: theRainbow
@@ -33,5 +33,5 @@ description: are you there?
server:
url: https://pb33f.io`
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
}

View File

@@ -10,7 +10,7 @@ import (
)
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
type OAuthFlow struct {
AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`

View File

@@ -4,42 +4,42 @@
package v3
import (
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func TestOAuthFlow_MarshalYAML(t *testing.T) {
oflow := &OAuthFlow{
AuthorizationUrl: "https://pb33f.io",
TokenUrl: "https://pb33f.io/token",
RefreshUrl: "https://pb33f.io/refresh",
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"},
}
oflow := &OAuthFlow{
AuthorizationUrl: "https://pb33f.io",
TokenUrl: "https://pb33f.io/token",
RefreshUrl: "https://pb33f.io/refresh",
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"},
}
rend, _ := oflow.Render()
rend, _ := oflow.Render()
desired := `authorizationUrl: https://pb33f.io
desired := `authorizationUrl: https://pb33f.io
tokenUrl: https://pb33f.io/token
refreshUrl: https://pb33f.io/refresh
scopes:
chicken: nuggets
beefy: soup`
// we can't check for equality, as the scopes map will be randomly ordered when created from scratch.
assert.Len(t, desired, 149)
// we can't check for equality, as the scopes map will be randomly ordered when created from scratch.
assert.Len(t, desired, 149)
// mutate
oflow.Scopes = nil
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
// mutate
oflow.Scopes = nil
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
desired = `authorizationUrl: https://pb33f.io
desired = `authorizationUrl: https://pb33f.io
tokenUrl: https://pb33f.io/token
refreshUrl: https://pb33f.io/refresh
x-burgers: why not?`
rend, _ = oflow.Render()
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
rend, _ = oflow.Render()
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
}

View File

@@ -10,7 +10,7 @@ import (
)
// OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
type OAuthFlows struct {
Implicit *OAuthFlow `json:"implicit,omitempty" yaml:"implicit,omitempty"`
Password *OAuthFlow `json:"password,omitempty" yaml:"password,omitempty"`
@@ -63,4 +63,3 @@ func (o *OAuthFlows) MarshalYAML() (interface{}, error) {
nb := high.NewNodeBuilder(o, o.low)
return nb.Render(), nil
}

View File

@@ -65,7 +65,7 @@ func TestOperation_MarshalYAML(t *testing.T) {
},
OperationId: "slice",
Parameters: []*Parameter{
&Parameter{
{
Name: "mice",
},
},
@@ -103,7 +103,7 @@ func TestOperation_MarshalYAMLInline(t *testing.T) {
},
OperationId: "slice",
Parameters: []*Parameter{
&Parameter{
{
Name: "mice",
},
},

View File

@@ -18,7 +18,7 @@ import (
// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery.
// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice.
// Recommended for most use case is Authorization Code Grant flow with PKCE.
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
type SecurityScheme struct {
Type string `json:"type,omitempty" yaml:"type,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"`

View File

@@ -4,44 +4,44 @@
package v3
import (
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"strings"
"testing"
)
func TestSecurityScheme_MarshalYAML(t *testing.T) {
ss := &SecurityScheme{
Type: "apiKey",
Description: "this is a description",
Name: "superSecret",
In: "header",
Scheme: "https",
}
ss := &SecurityScheme{
Type: "apiKey",
Description: "this is a description",
Name: "superSecret",
In: "header",
Scheme: "https",
}
dat, _ := ss.Render()
dat, _ := ss.Render()
var idxNode yaml.Node
_ = yaml.Unmarshal(dat, &idxNode)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
var idxNode yaml.Node
_ = yaml.Unmarshal(dat, &idxNode)
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
var n v3.SecurityScheme
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n v3.SecurityScheme
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
r := NewSecurityScheme(&n)
r := NewSecurityScheme(&n)
dat, _ = r.Render()
dat, _ = r.Render()
desired := `type: apiKey
desired := `type: apiKey
description: this is a description
name: superSecret
in: header
scheme: https`
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
}

View File

@@ -4,32 +4,32 @@
package v3
import (
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func TestServer_MarshalYAML(t *testing.T) {
server := &Server{
URL: "https://pb33f.io",
Description: "the b33f",
}
server := &Server{
URL: "https://pb33f.io",
Description: "the b33f",
}
desired := `url: https://pb33f.io
desired := `url: https://pb33f.io
description: the b33f`
rend, _ := server.Render()
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
rend, _ := server.Render()
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
// mutate
server.Variables = map[string]*ServerVariable{
"rainbow": {
Enum: []string{"one", "two", "three"},
},
}
// mutate
server.Variables = map[string]*ServerVariable{
"rainbow": {
Enum: []string{"one", "two", "three"},
},
}
desired = `url: https://pb33f.io
desired = `url: https://pb33f.io
description: the b33f
variables:
rainbow:
@@ -38,6 +38,6 @@ variables:
- two
- three`
rend, _ = server.Render()
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
rend, _ = server.Render()
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
}

View File

@@ -4,40 +4,40 @@
package v3
import (
"github.com/stretchr/testify/assert"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"strings"
"testing"
)
func TestServerVariable_MarshalYAML(t *testing.T) {
svar := &ServerVariable{
Enum: []string{"one", "two", "three"},
Description: "money day",
}
svar := &ServerVariable{
Enum: []string{"one", "two", "three"},
Description: "money day",
}
desired := `enum:
desired := `enum:
- one
- two
- three
description: money day`
svarRend, _ := svar.Render()
svarRend, _ := svar.Render()
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
// mutate
// mutate
svar.Default = "is moments away"
svar.Default = "is moments away"
desired = `enum:
desired = `enum:
- one
- two
- three
default: is moments away
description: money day`
svarRend, _ = svar.Render()
svarRend, _ = svar.Render()
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
}

View File

@@ -9,4 +9,3 @@
// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure
// that all the latest features are collected, without damaging backwards compatibility.
package base

View File

@@ -12,8 +12,9 @@ import (
)
// Contact represents a low-level representation of the Contact definitions found at
// v2 - https://swagger.io/specification/v2/#contactObject
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
//
// v2 - https://swagger.io/specification/v2/#contactObject
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
type Contact struct {
Name low.NodeReference[string]
URL low.NodeReference[string]
@@ -42,4 +43,3 @@ func (c *Contact) Hash() [32]byte {
}
return sha256.Sum256([]byte(strings.Join(f, "|")))
}

View File

@@ -17,7 +17,8 @@ import (
// which is used to inform the consumer of the document of an alternative schema based on the value associated with it.
//
// When using the discriminator, inline schemas will not be considered.
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
//
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
type Discriminator struct {
PropertyName low.NodeReference[string]
Mapping low.NodeReference[map[low.KeyReference[string]]low.ValueReference[string]]

View File

@@ -16,7 +16,8 @@ import (
)
// Example represents a low-level Example object as defined by OpenAPI 3+
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
//
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
type Example struct {
Summary low.NodeReference[string]
Description low.NodeReference[string]

View File

@@ -16,8 +16,9 @@ import (
// ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3
//
// Allows referencing an external resource for extended documentation.
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
//
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
type ExternalDoc struct {
Description low.NodeReference[string]
URL low.NodeReference[string]

View File

@@ -12,8 +12,9 @@ import (
)
// License is a low-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
// v2 - https://swagger.io/specification/v2/#licenseObject
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
//
// v2 - https://swagger.io/specification/v2/#licenseObject
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
type License struct {
Name low.NodeReference[string]
URL low.NodeReference[string]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -19,8 +19,8 @@ import (
// security schemes declared in it which are all required (that is, there is a logical AND between the schemes).
//
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/#security-requirement-object
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/#security-requirement-object
type SecurityRequirement struct {
Requirements low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
*low.Reference

View File

@@ -17,8 +17,8 @@ import (
//
// Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per
// tag defined in the Operation Object instances.
// - v2: https://swagger.io/specification/v2/#tagObject
// - v3: https://swagger.io/specification/#tag-object
// - v2: https://swagger.io/specification/v2/#tagObject
// - v3: https://swagger.io/specification/#tag-object
type Tag struct {
Name low.NodeReference[string]
Description low.NodeReference[string]

View File

@@ -16,8 +16,9 @@ import (
//
// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be
// used to add that information. See examples for expected behavior.
// v2 - https://swagger.io/specification/v2/#xmlObject
// v3 - https://swagger.io/specification/#xml-object
//
// v2 - https://swagger.io/specification/v2/#xmlObject
// v3 - https://swagger.io/specification/#xml-object
type XML struct {
Name low.NodeReference[string]
Namespace low.NodeReference[string]

View File

@@ -1024,7 +1024,7 @@ one:
assert.NoError(t, err)
assert.Len(t, things, 1)
for k, _ := range things {
for k := range things {
assert.Equal(t, "one", k.Value)
}
}

View File

@@ -4,14 +4,14 @@
package low
import (
"fmt"
"reflect"
"strconv"
"strings"
"sync"
"fmt"
"reflect"
"strconv"
"strings"
"sync"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
// BuildModel accepts a yaml.Node pointer and a model, which can be any struct. Using reflection, the model is
@@ -20,198 +20,198 @@ import (
//
// BuildModel is non-recursive and will only build out a single layer of the node tree.
func BuildModel(node *yaml.Node, model interface{}) error {
if node == nil {
return nil
}
if node == nil {
return nil
}
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
}
v := reflect.ValueOf(model).Elem()
num := v.NumField()
for i := 0; i < num; i++ {
if reflect.ValueOf(model).Type().Kind() != reflect.Pointer {
return fmt.Errorf("cannot build model on non-pointer: %v", reflect.ValueOf(model).Type().Kind())
}
v := reflect.ValueOf(model).Elem()
num := v.NumField()
for i := 0; i < num; i++ {
fName := v.Type().Field(i).Name
fName := v.Type().Field(i).Name
if fName == "Extensions" {
continue // internal construct
}
if fName == "Extensions" {
continue // internal construct
}
if fName == "PathItems" {
continue // internal construct
}
if fName == "PathItems" {
continue // internal construct
}
kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content)
if vn == nil {
// no point in going on.
continue
}
kn, vn := utils.FindKeyNodeTop(strings.ToLower(fName), node.Content)
if vn == nil {
// no point in going on.
continue
}
field := v.FieldByName(fName)
kind := field.Kind()
switch kind {
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
err := SetField(&field, vn, kn)
if err != nil {
return err
}
default:
return fmt.Errorf("unable to parse unsupported type: %v", kind)
}
field := v.FieldByName(fName)
kind := field.Kind()
switch kind {
case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
err := SetField(&field, vn, kn)
if err != nil {
return err
}
default:
return fmt.Errorf("unable to parse unsupported type: %v", kind)
}
}
}
return nil
return nil
}
// SetField accepts a field reflection value, a yaml.Node valueNode and a yaml.Node keyNode. Using reflection, the
// function will attempt to set the value of the field based on the key and value nodes. This method is only useful
// for low-level models, it has no value to high-level ones.
func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) error {
if valueNode == nil {
return nil
}
if valueNode == nil {
return nil
}
switch field.Type() {
switch field.Type() {
case reflect.TypeOf(map[string]NodeReference[any]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[string]NodeReference[any])
var currentLabel string
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
currentLabel = sliceItem.Value
continue
}
var decoded map[string]interface{}
// I cannot think of a way to make this error out by this point.
_ = sliceItem.Decode(&decoded)
items[currentLabel] = NodeReference[any]{
Value: decoded,
ValueNode: sliceItem,
KeyNode: valueNode,
}
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf(map[string]NodeReference[any]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[string]NodeReference[any])
var currentLabel string
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
currentLabel = sliceItem.Value
continue
}
var decoded map[string]interface{}
// I cannot think of a way to make this error out by this point.
_ = sliceItem.Decode(&decoded)
items[currentLabel] = NodeReference[any]{
Value: decoded,
ValueNode: sliceItem,
KeyNode: valueNode,
}
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf(map[string]NodeReference[string]{}):
case reflect.TypeOf(map[string]NodeReference[string]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[string]NodeReference[string])
var currentLabel string
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
currentLabel = sliceItem.Value
continue
}
items[currentLabel] = NodeReference[string]{
Value: fmt.Sprintf("%v", sliceItem.Value),
ValueNode: sliceItem,
KeyNode: valueNode,
}
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[string]NodeReference[string])
var currentLabel string
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
currentLabel = sliceItem.Value
continue
}
items[currentLabel] = NodeReference[string]{
Value: fmt.Sprintf("%v", sliceItem.Value),
ValueNode: sliceItem,
KeyNode: valueNode,
}
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf(NodeReference[any]{}):
case reflect.TypeOf(NodeReference[any]{}):
var decoded interface{}
_ = valueNode.Decode(&decoded)
if field.CanSet() {
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
field.Set(reflect.ValueOf(or))
}
var decoded interface{}
_ = valueNode.Decode(&decoded)
if field.CanSet() {
or := NodeReference[any]{Value: decoded, ValueNode: valueNode, KeyNode: keyNode}
field.Set(reflect.ValueOf(or))
}
case reflect.TypeOf([]NodeReference[any]{}):
case reflect.TypeOf([]NodeReference[any]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[any]
for _, sliceItem := range valueNode.Content {
var decoded map[string]interface{}
err := sliceItem.Decode(&decoded)
if err != nil {
return err
}
items = append(items, NodeReference[any]{
Value: decoded,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[any]
for _, sliceItem := range valueNode.Content {
var decoded map[string]interface{}
err := sliceItem.Decode(&decoded)
if err != nil {
return err
}
items = append(items, NodeReference[any]{
Value: decoded,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf(NodeReference[string]{}):
case reflect.TypeOf(NodeReference[string]{}):
if field.CanSet() {
nr := NodeReference[string]{
Value: fmt.Sprintf("%v", valueNode.Value),
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
if field.CanSet() {
nr := NodeReference[string]{
Value: fmt.Sprintf("%v", valueNode.Value),
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
case reflect.TypeOf(ValueReference[string]{}):
case reflect.TypeOf(ValueReference[string]{}):
if field.CanSet() {
nr := ValueReference[string]{
Value: fmt.Sprintf("%v", valueNode.Value),
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(nr))
}
if field.CanSet() {
nr := ValueReference[string]{
Value: fmt.Sprintf("%v", valueNode.Value),
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(nr))
}
case reflect.TypeOf(NodeReference[bool]{}):
case reflect.TypeOf(NodeReference[bool]{}):
if utils.IsNodeBoolValue(valueNode) {
if field.CanSet() {
bv, _ := strconv.ParseBool(valueNode.Value)
nr := NodeReference[bool]{
Value: bv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
}
if utils.IsNodeBoolValue(valueNode) {
if field.CanSet() {
bv, _ := strconv.ParseBool(valueNode.Value)
nr := NodeReference[bool]{
Value: bv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
}
case reflect.TypeOf(NodeReference[int]{}):
case reflect.TypeOf(NodeReference[int]{}):
if utils.IsNodeIntValue(valueNode) {
if field.CanSet() {
fv, _ := strconv.Atoi(valueNode.Value)
nr := NodeReference[int]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
}
if utils.IsNodeIntValue(valueNode) {
if field.CanSet() {
fv, _ := strconv.Atoi(valueNode.Value)
nr := NodeReference[int]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
}
case reflect.TypeOf(NodeReference[int64]{}):
case reflect.TypeOf(NodeReference[int64]{}):
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) {
if field.CanSet() {
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
nr := NodeReference[int64]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
}
if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) {
if field.CanSet() {
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
nr := NodeReference[int64]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr))
}
}
case reflect.TypeOf(NodeReference[float32]{}):
case reflect.TypeOf(NodeReference[float32]{}):
if utils.IsNodeNumberValue(valueNode) {
if field.CanSet() {
@@ -225,7 +225,7 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
}
}
case reflect.TypeOf(NodeReference[float64]{}):
case reflect.TypeOf(NodeReference[float64]{}):
if utils.IsNodeNumberValue(valueNode) {
if field.CanSet() {
@@ -239,249 +239,249 @@ func SetField(field *reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) er
}
}
case reflect.TypeOf([]NodeReference[string]{}):
case reflect.TypeOf([]NodeReference[string]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[string]
for _, sliceItem := range valueNode.Content {
items = append(items, NodeReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[string]
for _, sliceItem := range valueNode.Content {
items = append(items, NodeReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf([]NodeReference[float32]{}):
case reflect.TypeOf([]NodeReference[float32]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[float32]
for _, sliceItem := range valueNode.Content {
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
items = append(items, NodeReference[float32]{
Value: float32(fv),
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[float32]
for _, sliceItem := range valueNode.Content {
fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
items = append(items, NodeReference[float32]{
Value: float32(fv),
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf([]NodeReference[float64]{}):
case reflect.TypeOf([]NodeReference[float64]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[float64]
for _, sliceItem := range valueNode.Content {
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[float64]
for _, sliceItem := range valueNode.Content {
fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
items = append(items, NodeReference[float64]{Value: fv, ValueNode: sliceItem})
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf([]NodeReference[int]{}):
case reflect.TypeOf([]NodeReference[int]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[int]
for _, sliceItem := range valueNode.Content {
iv, _ := strconv.Atoi(sliceItem.Value)
items = append(items, NodeReference[int]{
Value: iv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[int]
for _, sliceItem := range valueNode.Content {
iv, _ := strconv.Atoi(sliceItem.Value)
items = append(items, NodeReference[int]{
Value: iv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf([]NodeReference[int64]{}):
case reflect.TypeOf([]NodeReference[int64]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[int64]
for _, sliceItem := range valueNode.Content {
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
items = append(items, NodeReference[int64]{
Value: iv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[int64]
for _, sliceItem := range valueNode.Content {
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
items = append(items, NodeReference[int64]{
Value: iv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf([]NodeReference[bool]{}):
case reflect.TypeOf([]NodeReference[bool]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[bool]
for _, sliceItem := range valueNode.Content {
bv, _ := strconv.ParseBool(sliceItem.Value)
items = append(items, NodeReference[bool]{
Value: bv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []NodeReference[bool]
for _, sliceItem := range valueNode.Content {
bv, _ := strconv.ParseBool(sliceItem.Value)
items = append(items, NodeReference[bool]{
Value: bv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
}
field.Set(reflect.ValueOf(items))
}
}
// helper for unpacking string maps.
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
// helper for unpacking string maps.
case reflect.TypeOf(map[KeyReference[string]]ValueReference[string]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[KeyReference[string]]ValueReference[string])
var cf *yaml.Node
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
cf = sliceItem
continue
}
items[KeyReference[string]{
Value: cf.Value,
KeyNode: cf,
}] = ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
}
}
field.Set(reflect.ValueOf(items))
}
}
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[KeyReference[string]]ValueReference[string])
var cf *yaml.Node
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
cf = sliceItem
continue
}
items[KeyReference[string]{
Value: cf.Value,
KeyNode: cf,
}] = ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
}
}
field.Set(reflect.ValueOf(items))
}
}
case reflect.TypeOf(KeyReference[map[KeyReference[string]]ValueReference[string]]{}):
case reflect.TypeOf(KeyReference[map[KeyReference[string]]ValueReference[string]]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[KeyReference[string]]ValueReference[string])
var cf *yaml.Node
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
cf = sliceItem
continue
}
items[KeyReference[string]{
Value: cf.Value,
KeyNode: cf,
}] = ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
}
}
ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{
Value: items,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(ref))
}
}
case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[KeyReference[string]]ValueReference[string])
var cf *yaml.Node
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
cf = sliceItem
continue
}
items[KeyReference[string]{
Value: cf.Value,
KeyNode: cf,
}] = ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
}
}
ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{
Value: items,
KeyNode: keyNode,
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(ref))
}
}
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[KeyReference[string]]ValueReference[string])
var cf *yaml.Node
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
cf = sliceItem
continue
}
items[KeyReference[string]{
Value: cf.Value,
KeyNode: cf,
}] = ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
}
}
ref := KeyReference[map[KeyReference[string]]ValueReference[string]]{
Value: items,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(ref))
}
}
case reflect.TypeOf(NodeReference[map[KeyReference[string]]ValueReference[string]]{}):
if utils.IsNodeMap(valueNode) {
if field.CanSet() {
items := make(map[KeyReference[string]]ValueReference[string])
var cf *yaml.Node
for i, sliceItem := range valueNode.Content {
if i%2 == 0 {
cf = sliceItem
continue
}
items[KeyReference[string]{
Value: cf.Value,
KeyNode: cf,
}] = ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
}
}
ref := NodeReference[map[KeyReference[string]]ValueReference[string]]{
Value: items,
KeyNode: keyNode,
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(ref))
}
}
case reflect.TypeOf(NodeReference[[]ValueReference[string]]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []ValueReference[string]
for _, sliceItem := range valueNode.Content {
items = append(items, ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
})
}
n := NodeReference[[]ValueReference[string]]{
Value: items,
KeyNode: keyNode,
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(n))
}
}
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []ValueReference[string]
for _, sliceItem := range valueNode.Content {
items = append(items, ValueReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
})
}
n := NodeReference[[]ValueReference[string]]{
Value: items,
KeyNode: keyNode,
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(n))
}
}
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
case reflect.TypeOf(NodeReference[[]ValueReference[any]]{}):
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []ValueReference[any]
for _, sliceItem := range valueNode.Content {
if utils.IsNodeArray(valueNode) {
if field.CanSet() {
var items []ValueReference[any]
for _, sliceItem := range valueNode.Content {
var val any
if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) {
if utils.IsNodeIntValue(sliceItem) {
val, _ = strconv.ParseInt(sliceItem.Value, 10, 64)
} else {
val, _ = strconv.ParseFloat(sliceItem.Value, 64)
}
}
if utils.IsNodeBoolValue(sliceItem) {
val, _ = strconv.ParseBool(sliceItem.Value)
}
if utils.IsNodeStringValue(sliceItem) {
val = sliceItem.Value
}
var val any
if utils.IsNodeIntValue(sliceItem) || utils.IsNodeFloatValue(sliceItem) {
if utils.IsNodeIntValue(sliceItem) {
val, _ = strconv.ParseInt(sliceItem.Value, 10, 64)
} else {
val, _ = strconv.ParseFloat(sliceItem.Value, 64)
}
}
if utils.IsNodeBoolValue(sliceItem) {
val, _ = strconv.ParseBool(sliceItem.Value)
}
if utils.IsNodeStringValue(sliceItem) {
val = sliceItem.Value
}
items = append(items, ValueReference[any]{
Value: val,
ValueNode: sliceItem,
})
}
n := NodeReference[[]ValueReference[any]]{
Value: items,
KeyNode: keyNode,
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(n))
}
}
items = append(items, ValueReference[any]{
Value: val,
ValueNode: sliceItem,
})
}
n := NodeReference[[]ValueReference[any]]{
Value: items,
KeyNode: keyNode,
ValueNode: valueNode,
}
field.Set(reflect.ValueOf(n))
}
}
default:
// we want to ignore everything else, each model handles its own complex types.
break
}
return nil
default:
// we want to ignore everything else, each model handles its own complex types.
break
}
return nil
}
// BuildModelAsync is a convenience function for calling BuildModel from a goroutine, requires a sync.WaitGroup
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
if n != nil {
err := BuildModel(n, model)
if err != nil {
*errors = append(*errors, err)
}
}
lwg.Done()
if n != nil {
err := BuildModel(n, model)
if err != nil {
*errors = append(*errors, err)
}
}
lwg.Done()
}

View File

@@ -1,53 +1,53 @@
package low
import (
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"sync"
"testing"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"sync"
"testing"
)
type hotdog struct {
Name NodeReference[string]
ValueName ValueReference[string]
Fat NodeReference[int]
Ketchup NodeReference[float32]
Mustard NodeReference[float64]
Grilled NodeReference[bool]
MaxTemp NodeReference[int]
MaxTempHigh NodeReference[int64]
MaxTempAlt []NodeReference[int]
Drinks []NodeReference[string]
Sides []NodeReference[float32]
BigSides []NodeReference[float64]
Temps []NodeReference[int]
HighTemps []NodeReference[int64]
Buns []NodeReference[bool]
UnknownElements NodeReference[any]
LotsOfUnknowns []NodeReference[any]
Where map[string]NodeReference[any]
There map[string]NodeReference[string]
AllTheThings NodeReference[map[KeyReference[string]]ValueReference[string]]
Name NodeReference[string]
ValueName ValueReference[string]
Fat NodeReference[int]
Ketchup NodeReference[float32]
Mustard NodeReference[float64]
Grilled NodeReference[bool]
MaxTemp NodeReference[int]
MaxTempHigh NodeReference[int64]
MaxTempAlt []NodeReference[int]
Drinks []NodeReference[string]
Sides []NodeReference[float32]
BigSides []NodeReference[float64]
Temps []NodeReference[int]
HighTemps []NodeReference[int64]
Buns []NodeReference[bool]
UnknownElements NodeReference[any]
LotsOfUnknowns []NodeReference[any]
Where map[string]NodeReference[any]
There map[string]NodeReference[string]
AllTheThings NodeReference[map[KeyReference[string]]ValueReference[string]]
}
func TestBuildModel_Mismatch(t *testing.T) {
yml := `crisps: are tasty`
yml := `crisps: are tasty`
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
hd := hotdog{}
cErr := BuildModel(&rootNode, &hd)
assert.NoError(t, cErr)
assert.Empty(t, hd.Name)
hd := hotdog{}
cErr := BuildModel(&rootNode, &hd)
assert.NoError(t, cErr)
assert.Empty(t, hd.Name)
}
func TestBuildModel(t *testing.T) {
yml := `name: yummy
yml := `name: yummy
valueName: yammy
beef: true
fat: 200
@@ -105,347 +105,347 @@ allTheThings:
beer: isGood
cake: isNice`
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
hd := hotdog{}
cErr := BuildModel(rootNode.Content[0], &hd)
assert.Equal(t, 200, hd.Fat.Value)
assert.Equal(t, 4, hd.Fat.ValueNode.Line)
assert.Equal(t, true, hd.Grilled.Value)
assert.Equal(t, "yummy", hd.Name.Value)
assert.Equal(t, "yammy", hd.ValueName.Value)
assert.Equal(t, float32(200.45), hd.Ketchup.Value)
assert.Len(t, hd.Drinks, 3)
assert.Len(t, hd.Sides, 4)
assert.Len(t, hd.BigSides, 4)
assert.Len(t, hd.Temps, 2)
assert.Len(t, hd.HighTemps, 2)
assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value)
assert.Len(t, hd.MaxTempAlt, 5)
assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value)
assert.Equal(t, 2, hd.Temps[1].Value)
assert.Equal(t, 27, hd.Temps[1].ValueNode.Line)
assert.Len(t, hd.UnknownElements.Value, 2)
assert.Len(t, hd.LotsOfUnknowns, 3)
assert.Len(t, hd.Where, 2)
assert.Len(t, hd.There, 2)
assert.Equal(t, "bear", hd.There["care"].Value)
assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
hd := hotdog{}
cErr := BuildModel(rootNode.Content[0], &hd)
assert.Equal(t, 200, hd.Fat.Value)
assert.Equal(t, 4, hd.Fat.ValueNode.Line)
assert.Equal(t, true, hd.Grilled.Value)
assert.Equal(t, "yummy", hd.Name.Value)
assert.Equal(t, "yammy", hd.ValueName.Value)
assert.Equal(t, float32(200.45), hd.Ketchup.Value)
assert.Len(t, hd.Drinks, 3)
assert.Len(t, hd.Sides, 4)
assert.Len(t, hd.BigSides, 4)
assert.Len(t, hd.Temps, 2)
assert.Len(t, hd.HighTemps, 2)
assert.Equal(t, int64(11732849090192923), hd.HighTemps[1].Value)
assert.Len(t, hd.MaxTempAlt, 5)
assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value)
assert.Equal(t, 2, hd.Temps[1].Value)
assert.Equal(t, 27, hd.Temps[1].ValueNode.Line)
assert.Len(t, hd.UnknownElements.Value, 2)
assert.Len(t, hd.LotsOfUnknowns, 3)
assert.Len(t, hd.Where, 2)
assert.Len(t, hd.There, 2)
assert.Equal(t, "bear", hd.There["care"].Value)
assert.Equal(t, 324938249028.98234892374892374923874823974, hd.Mustard.Value)
allTheThings := hd.AllTheThings.Value
for i := range allTheThings {
if i.Value == "beer" {
assert.Equal(t, "isGood", allTheThings[i].Value)
}
if i.Value == "cake" {
assert.Equal(t, "isNice", allTheThings[i].Value)
}
}
assert.NoError(t, cErr)
allTheThings := hd.AllTheThings.Value
for i := range allTheThings {
if i.Value == "beer" {
assert.Equal(t, "isGood", allTheThings[i].Value)
}
if i.Value == "cake" {
assert.Equal(t, "isNice", allTheThings[i].Value)
}
}
assert.NoError(t, cErr)
}
func TestBuildModel_UseCopyNotRef(t *testing.T) {
yml := `cake: -99999`
yml := `cake: -99999`
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
hd := hotdog{}
cErr := BuildModel(&rootNode, hd)
assert.Error(t, cErr)
assert.Empty(t, hd.Name)
hd := hotdog{}
cErr := BuildModel(&rootNode, hd)
assert.Error(t, cErr)
assert.Empty(t, hd.Name)
}
func TestBuildModel_UseUnsupportedPrimitive(t *testing.T) {
type notSupported struct {
cake string
}
ns := notSupported{}
yml := `cake: party`
type notSupported struct {
cake string
}
ns := notSupported{}
yml := `cake: party`
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
cErr := BuildModel(rootNode.Content[0], &ns)
assert.Error(t, cErr)
assert.Empty(t, ns.cake)
cErr := BuildModel(rootNode.Content[0], &ns)
assert.Error(t, cErr)
assert.Empty(t, ns.cake)
}
func TestBuildModel_UsingInternalConstructs(t *testing.T) {
type internal struct {
Extensions NodeReference[string]
PathItems NodeReference[string]
Thing NodeReference[string]
}
type internal struct {
Extensions NodeReference[string]
PathItems NodeReference[string]
Thing NodeReference[string]
}
yml := `extensions: one
yml := `extensions: one
pathItems: two
thing: yeah`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
// try a null build
try := BuildModel(nil, ins)
assert.NoError(t, try)
// try a null build
try := BuildModel(nil, ins)
assert.NoError(t, try)
cErr := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, cErr)
assert.Empty(t, ins.PathItems.Value)
assert.Empty(t, ins.Extensions.Value)
assert.Equal(t, "yeah", ins.Thing.Value)
cErr := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, cErr)
assert.Empty(t, ins.PathItems.Value)
assert.Empty(t, ins.Extensions.Value)
assert.Equal(t, "yeah", ins.Thing.Value)
}
func TestSetField_NodeRefAny_Error(t *testing.T) {
type internal struct {
Thing []NodeReference[any]
}
type internal struct {
Thing []NodeReference[any]
}
yml := `thing:
yml := `thing:
- 999
- false`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.Error(t, try)
try := BuildModel(rootNode.Content[0], ins)
assert.Error(t, try)
}
func TestSetField_MapHelperWrapped(t *testing.T) {
type internal struct {
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
}
type internal struct {
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
}
yml := `thing:
yml := `thing:
what: not
chip: chop
lip: lop`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing.Value, 3)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing.Value, 3)
}
func TestSetField_MapHelper(t *testing.T) {
type internal struct {
Thing map[KeyReference[string]]ValueReference[string]
}
type internal struct {
Thing map[KeyReference[string]]ValueReference[string]
}
yml := `thing:
yml := `thing:
what: not
chip: chop
lip: lop`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing, 3)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing, 3)
}
func TestSetField_ArrayHelper(t *testing.T) {
type internal struct {
Thing NodeReference[[]ValueReference[string]]
}
type internal struct {
Thing NodeReference[[]ValueReference[string]]
}
yml := `thing:
yml := `thing:
- nice
- rice
- slice`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing.Value, 3)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing.Value, 3)
}
func TestSetField_Enum_Helper(t *testing.T) {
type internal struct {
Thing NodeReference[[]ValueReference[any]]
}
type internal struct {
Thing NodeReference[[]ValueReference[any]]
}
yml := `thing:
yml := `thing:
- nice
- rice
- slice`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing.Value, 3)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Len(t, ins.Thing.Value, 3)
}
func TestSetField_Default_Helper(t *testing.T) {
type cake struct {
thing int
}
type cake struct {
thing int
}
// this should be ignored, no custom objects in here my friend.
type internal struct {
Thing cake
}
// this should be ignored, no custom objects in here my friend.
type internal struct {
Thing cake
}
yml := `thing:
yml := `thing:
type: cake`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Equal(t, 0, ins.Thing.thing)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Equal(t, 0, ins.Thing.thing)
}
func TestHandleSlicesOfInts(t *testing.T) {
type internal struct {
Thing NodeReference[[]ValueReference[any]]
}
type internal struct {
Thing NodeReference[[]ValueReference[any]]
}
yml := `thing:
yml := `thing:
- 5
- 1.234`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Equal(t, int64(5), ins.Thing.Value[0].Value)
assert.Equal(t, 1.234, ins.Thing.Value[1].Value)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Equal(t, int64(5), ins.Thing.Value[0].Value)
assert.Equal(t, 1.234, ins.Thing.Value[1].Value)
}
func TestHandleSlicesOfBools(t *testing.T) {
type internal struct {
Thing NodeReference[[]ValueReference[any]]
}
type internal struct {
Thing NodeReference[[]ValueReference[any]]
}
yml := `thing:
yml := `thing:
- true
- false`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Equal(t, true, ins.Thing.Value[0].Value)
assert.Equal(t, false, ins.Thing.Value[1].Value)
try := BuildModel(rootNode.Content[0], ins)
assert.NoError(t, try)
assert.Equal(t, true, ins.Thing.Value[0].Value)
assert.Equal(t, false, ins.Thing.Value[1].Value)
}
func TestSetField_Ignore(t *testing.T) {
type Complex struct {
name string
}
type internal struct {
Thing *Complex
}
type Complex struct {
name string
}
type internal struct {
Thing *Complex
}
yml := `thing:
yml := `thing:
- nice
- rice
- slice`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
try := BuildModel(&rootNode, ins)
assert.NoError(t, try)
assert.Nil(t, ins.Thing)
try := BuildModel(&rootNode, ins)
assert.NoError(t, try)
assert.Nil(t, ins.Thing)
}
func TestBuildModelAsync(t *testing.T) {
type internal struct {
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
}
type internal struct {
Thing KeyReference[map[KeyReference[string]]ValueReference[string]]
}
yml := `thing:
yml := `thing:
what: not
chip: chop
lip: lop`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var wg sync.WaitGroup
var errors []error
wg.Add(1)
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
wg.Wait()
assert.Len(t, ins.Thing.Value, 3)
var wg sync.WaitGroup
var errors []error
wg.Add(1)
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
wg.Wait()
assert.Len(t, ins.Thing.Value, 3)
}
func TestBuildModelAsync_Error(t *testing.T) {
type internal struct {
Thing []NodeReference[any]
}
type internal struct {
Thing []NodeReference[any]
}
yml := `thing:
yml := `thing:
- 999
- false`
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
ins := new(internal)
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
var wg sync.WaitGroup
var errors []error
wg.Add(1)
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
wg.Wait()
assert.Len(t, errors, 1)
assert.Len(t, ins.Thing, 0)
var wg sync.WaitGroup
var errors []error
wg.Add(1)
BuildModelAsync(rootNode.Content[0], ins, &wg, &errors)
wg.Wait()
assert.Len(t, errors, 1)
assert.Len(t, ins.Thing, 0)
}

View File

@@ -51,7 +51,7 @@ func TestNodeReference_Mutate(t *testing.T) {
func TestNodeReference_RefNode(t *testing.T) {
nr := new(NodeReference[string])
nr.KeyNode = &yaml.Node{
Content: []*yaml.Node{&yaml.Node{
Content: []*yaml.Node{{
Value: "$ref",
}},
}
@@ -749,8 +749,3 @@ func TestKeyReference_GetKeyNode(t *testing.T) {
assert.Equal(t, 3, nr.GetKeyNode().Line)
assert.Equal(t, "pizza", nr.GetKeyNode().Value)
}

View File

@@ -17,7 +17,7 @@ import (
//
// ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be
// referenced to the ones defined here. It does not define global operation parameters
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
type ParameterDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter]
}
@@ -26,7 +26,7 @@ type ParameterDefinitions struct {
//
// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be
// referenced to the ones defined here. It does not define global operation responses
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
type ResponsesDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*Response]
}
@@ -35,7 +35,7 @@ type ResponsesDefinitions struct {
//
// A declaration of the security schemes available to be used in the specification. This does not enforce the security
// schemes on the operations and only serves to provide the relevant details for each scheme
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]
}
@@ -44,7 +44,7 @@ type SecurityDefinitions struct {
//
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
// arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject
// - https://swagger.io/specification/v2/#definitionsObject
type Definitions struct {
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
}

View File

@@ -15,7 +15,7 @@ import (
// Examples represents a low-level Swagger / OpenAPI 2 Example object.
// Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject
// - https://swagger.io/specification/v2/#exampleObject
type Examples struct {
Values map[low.KeyReference[string]]low.ValueReference[any]
}

View File

@@ -17,7 +17,7 @@ import (
// Header Represents a low-level Swagger / OpenAPI 2 Header object.
//
// A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties.
// - https://swagger.io/specification/v2/#headerObject
// - https://swagger.io/specification/v2/#headerObject
type Header struct {
Type low.NodeReference[string]
Format low.NodeReference[string]

View File

@@ -4,90 +4,90 @@
package v2
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"testing"
)
func TestHeader_Build(t *testing.T) {
yml := `items:
yml := `items:
$ref: break`
var idxNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&idxNode)
var idxNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &idxNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&idxNode)
var n Header
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
var n Header
err := low.BuildModel(idxNode.Content[0], &n)
assert.NoError(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
err = n.Build(idxNode.Content[0], idx)
assert.Error(t, err)
}
func TestHeader_DefaultAsSlice(t *testing.T) {
yml := `x-ext: thing
yml := `x-ext: thing
default:
- why
- so many
- variations`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
assert.NotNil(t, n.Default.Value)
assert.Len(t, n.Default.Value, 3)
assert.Len(t, n.GetExtensions(), 1)
assert.NotNil(t, n.Default.Value)
assert.Len(t, n.Default.Value, 3)
assert.Len(t, n.GetExtensions(), 1)
}
func TestHeader_DefaultAsObject(t *testing.T) {
yml := `default:
yml := `default:
lets:
create:
a:
thing: ok?`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
assert.NotNil(t, n.Default.Value)
assert.NotNil(t, n.Default.Value)
}
func TestHeader_NoDefault(t *testing.T) {
yml := `minimum: 12`
yml := `minimum: 12`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
assert.Equal(t, 12, n.Minimum.Value)
assert.Equal(t, 12, n.Minimum.Value)
}
func TestHeader_Hash_n_Grab(t *testing.T) {
yml := `description: head
yml := `description: head
type: string
format: left
collectionFormat: nice
@@ -110,15 +110,15 @@ minItems: 1
uniqueItems: true
multipleOf: 12`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
idx := index.NewSpecIndex(&idxNode)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
var n Header
_ = low.BuildModel(idxNode.Content[0], &n)
_ = n.Build(idxNode.Content[0], idx)
yml2 := `description: head
yml2 := `description: head
items:
type: int
format: left
@@ -142,37 +142,37 @@ x-belly: large
pattern: wow
`
var idxNode2 yaml.Node
_ = yaml.Unmarshal([]byte(yml2), &idxNode2)
idx2 := index.NewSpecIndex(&idxNode2)
var idxNode2 yaml.Node
_ = yaml.Unmarshal([]byte(yml2), &idxNode2)
idx2 := index.NewSpecIndex(&idxNode2)
var n2 Header
_ = low.BuildModel(idxNode2.Content[0], &n2)
_ = n2.Build(idxNode2.Content[0], idx2)
var n2 Header
_ = low.BuildModel(idxNode2.Content[0], &n2)
_ = n2.Build(idxNode2.Content[0], idx2)
// hash
assert.Equal(t, n.Hash(), n2.Hash())
// hash
assert.Equal(t, n.Hash(), n2.Hash())
// and grab
assert.Equal(t, "string", n.GetType().Value)
assert.Equal(t, "head", n.GetDescription().Value)
assert.Equal(t, "left", n.GetFormat().Value)
assert.Equal(t, "left", n.GetFormat().Value)
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
assert.Equal(t, "shut that door!", n.GetDefault().Value)
assert.Equal(t, 10, n.GetMaximum().Value)
assert.Equal(t, 1, n.GetMinimum().Value)
assert.True(t, n.GetExclusiveMinimum().Value)
assert.True(t, n.GetExclusiveMaximum().Value)
assert.Equal(t, 10, n.GetMaxLength().Value)
assert.Equal(t, 1, n.GetMinLength().Value)
assert.Equal(t, 10, n.GetMaxItems().Value)
assert.Equal(t, 1, n.GetMinItems().Value)
assert.True(t, n.GetUniqueItems().Value)
assert.Equal(t, 12, n.GetMultipleOf().Value)
assert.Equal(t, "wow", n.GetPattern().Value)
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
assert.Len(t, n.GetEnum().Value, 2)
assert.Equal(t, "large", n.FindExtension("x-belly").Value)
// and grab
assert.Equal(t, "string", n.GetType().Value)
assert.Equal(t, "head", n.GetDescription().Value)
assert.Equal(t, "left", n.GetFormat().Value)
assert.Equal(t, "left", n.GetFormat().Value)
assert.Equal(t, "nice", n.GetCollectionFormat().Value)
assert.Equal(t, "shut that door!", n.GetDefault().Value)
assert.Equal(t, 10, n.GetMaximum().Value)
assert.Equal(t, 1, n.GetMinimum().Value)
assert.True(t, n.GetExclusiveMinimum().Value)
assert.True(t, n.GetExclusiveMaximum().Value)
assert.Equal(t, 10, n.GetMaxLength().Value)
assert.Equal(t, 1, n.GetMinLength().Value)
assert.Equal(t, 10, n.GetMaxItems().Value)
assert.Equal(t, 1, n.GetMinItems().Value)
assert.True(t, n.GetUniqueItems().Value)
assert.Equal(t, 12, n.GetMultipleOf().Value)
assert.Equal(t, "wow", n.GetPattern().Value)
assert.Equal(t, "int", n.GetItems().Value.(*Items).Type.Value)
assert.Len(t, n.GetEnum().Value, 2)
assert.Equal(t, "large", n.FindExtension("x-belly").Value)
}

View File

@@ -18,7 +18,7 @@ import (
//
// Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not
// located in "body". Items, is actually identical to a Header, except it does not have description.
// - https://swagger.io/specification/v2/#itemsObject
// - https://swagger.io/specification/v2/#itemsObject
type Items struct {
Type low.NodeReference[string]
Format low.NodeReference[string]

View File

@@ -17,7 +17,7 @@ import (
// Operation represents a low-level Swagger / OpenAPI 2 Operation object.
//
// It describes a single API operation on a path.
// - https://swagger.io/specification/v2/#operationObject
// - https://swagger.io/specification/v2/#operationObject
type Operation struct {
Tags low.NodeReference[[]low.ValueReference[string]]
Summary low.NodeReference[string]

View File

@@ -22,29 +22,39 @@ import (
// There are five possible parameter types.
//
// Path
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
//
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
//
// Query
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
//
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
//
// Header
// Custom headers that are expected as part of the request.
//
// Custom headers that are expected as part of the request.
//
// Body
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
//
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
//
// Form
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
// parameters have a different format based on the content-type used (for further details,
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
// being transferred.
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
// submit-name. This type of form parameters is more commonly used for file transfers
//
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
// parameters have a different format based on the content-type used (for further details,
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
// being transferred.
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
// submit-name. This type of form parameters is more commonly used for file transfers
//
// https://swagger.io/specification/v2/#parameterObject
type Parameter struct {
Name low.NodeReference[string]

View File

@@ -20,7 +20,7 @@ import (
// The path itself is still exposed to the tooling, but will not know which operations and parameters
// are available.
//
// - https://swagger.io/specification/v2/#pathItemObject
// - https://swagger.io/specification/v2/#pathItemObject
type PathItem struct {
Ref low.NodeReference[string]
Get low.NodeReference[*Operation]

View File

@@ -17,7 +17,7 @@ import (
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
//
// Response describes a single response from an API Operation
// - https://swagger.io/specification/v2/#responseObject
// - https://swagger.io/specification/v2/#responseObject
type Response struct {
Description low.NodeReference[string]
Schema low.NodeReference[*base.SchemaProxy]

View File

@@ -17,7 +17,7 @@ import (
// Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object.
//
// Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject
// - https://swagger.io/specification/v2/#scopesObject
type Scopes struct {
Values map[low.KeyReference[string]]low.ValueReference[string]
Extensions map[low.KeyReference[string]]low.ValueReference[any]

View File

@@ -18,7 +18,7 @@ import (
// SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are
// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows
// (implicit, password, application and access code)
// - https://swagger.io/specification/v2/#securityDefinitionsObject
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityScheme struct {
Type low.NodeReference[string]
Description low.NodeReference[string]

View File

@@ -12,12 +12,12 @@
package v2
import (
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/resolver"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/index"
"github.com/pb33f/libopenapi/resolver"
"gopkg.in/yaml.v3"
)
// processes a property of a Swagger document asynchronously using bool and error channels for signals.
@@ -26,272 +26,272 @@ type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex,
// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification.
type Swagger struct {
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
Swagger low.ValueReference[string]
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
Swagger low.ValueReference[string]
// Info represents a specification Info definition.
// Provides metadata about the API. The metadata can be used by the clients if needed.
// - https://swagger.io/specification/v2/#infoObject
Info low.NodeReference[*base.Info]
// Info represents a specification Info definition.
// Provides metadata about the API. The metadata can be used by the clients if needed.
// - https://swagger.io/specification/v2/#infoObject
Info low.NodeReference[*base.Info]
// Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor
// sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used
// (including the port). The host does not support path templating.
Host low.NodeReference[string]
// Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor
// sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used
// (including the port). The host does not support path templating.
Host low.NodeReference[string]
// BasePath is The base path on which the API is served, which is relative to the host. If it is not included,
// the API is served directly under the host. The value MUST start with a leading slash (/).
// The basePath does not support path templating.
BasePath low.NodeReference[string]
// BasePath is The base path on which the API is served, which is relative to the host. If it is not included,
// the API is served directly under the host. The value MUST start with a leading slash (/).
// The basePath does not support path templating.
BasePath low.NodeReference[string]
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
// If the schemes is not included, the default scheme to be used is the one used to access
// the Swagger definition itself.
Schemes low.NodeReference[[]low.ValueReference[string]]
// Schemes represents the transfer protocol of the API. Requirements MUST be from the list: "http", "https", "ws", "wss".
// If the schemes is not included, the default scheme to be used is the one used to access
// the Swagger definition itself.
Schemes low.NodeReference[[]low.ValueReference[string]]
// Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on
// specific API calls. Value MUST be as described under Mime Types.
Consumes low.NodeReference[[]low.ValueReference[string]]
// Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on
// specific API calls. Value MUST be as described under Mime Types.
Consumes low.NodeReference[[]low.ValueReference[string]]
// Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on
// specific API calls. Value MUST be as described under Mime Types.
Produces low.NodeReference[[]low.ValueReference[string]]
// Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on
// specific API calls. Value MUST be as described under Mime Types.
Produces low.NodeReference[[]low.ValueReference[string]]
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
// - https://swagger.io/specification/v2/#pathsObject
Paths low.NodeReference[*Paths]
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
// - https://swagger.io/specification/v2/#pathsObject
Paths low.NodeReference[*Paths]
// Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances
// - https://swagger.io/specification/v2/#definitionsObject
Definitions low.NodeReference[*Definitions]
// Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances
// - https://swagger.io/specification/v2/#definitionsObject
Definitions low.NodeReference[*Definitions]
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
// - https://swagger.io/specification/v2/#securityDefinitionsObject
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
// - https://swagger.io/specification/v2/#securityDefinitionsObject
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
// Parameters is an object to hold parameters that can be used across operations.
// This property does not define global parameters for all operations.
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
Parameters low.NodeReference[*ParameterDefinitions]
// Parameters is an object to hold parameters that can be used across operations.
// This property does not define global parameters for all operations.
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
Parameters low.NodeReference[*ParameterDefinitions]
// Responses is an object to hold responses that can be used across operations.
// This property does not define global responses for all operations.
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
Responses low.NodeReference[*ResponsesDefinitions]
// Responses is an object to hold responses that can be used across operations.
// This property does not define global responses for all operations.
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
Responses low.NodeReference[*ResponsesDefinitions]
// Security is a declaration of which security schemes are applied for the API as a whole. The list of values
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
// requirements). Individual operations can override this definition.
// - https://swagger.io/specification/v2/#securityRequirementObject
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
// Security is a declaration of which security schemes are applied for the API as a whole. The list of values
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
// requirements). Individual operations can override this definition.
// - https://swagger.io/specification/v2/#securityRequirementObject
Security low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]
// Tags are A list of tags used by the specification with additional metadata.
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
// by the Operation Object must be declared. The tags that are not declared may be organized randomly or based
// on the tools' logic. Each tag name in the list MUST be unique.
// - https://swagger.io/specification/v2/#tagObject
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
// Tags are A list of tags used by the specification with additional metadata.
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
// by the Operation Object must be declared. The tags that are not declared may be organized randomly or based
// on the tools' logic. Each tag name in the list MUST be unique.
// - https://swagger.io/specification/v2/#tagObject
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
ExternalDocs low.NodeReference[*base.ExternalDoc]
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
ExternalDocs low.NodeReference[*base.ExternalDoc]
// Extensions contains all custom extensions defined for the top-level document.
Extensions map[low.KeyReference[string]]low.ValueReference[any]
// Extensions contains all custom extensions defined for the top-level document.
Extensions map[low.KeyReference[string]]low.ValueReference[any]
// Index is a reference to the index.SpecIndex that was created for the document and used
// as a guide when building out the Document. Ideal if further processing is required on the model and
// the original details are required to continue the work.
//
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
Index *index.SpecIndex
// Index is a reference to the index.SpecIndex that was created for the document and used
// as a guide when building out the Document. Ideal if further processing is required on the model and
// the original details are required to continue the work.
//
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
Index *index.SpecIndex
// SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read.
//
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
SpecInfo *datamodel.SpecInfo
// SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read.
//
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
SpecInfo *datamodel.SpecInfo
}
// FindExtension locates an extension from the root of the Swagger document.
func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, s.Extensions)
return low.FindItemInMap[any](ext, s.Extensions)
}
// GetExtensions returns all Swagger/Top level extensions and satisfies the low.HasExtensions interface.
func (s *Swagger) GetExtensions() map[low.KeyReference[string]]low.ValueReference[any] {
return s.Extensions
return s.Extensions
}
// CreateDocumentFromConfig will create a new Swagger document from the provided SpecInfo and DocumentConfiguration.
func CreateDocumentFromConfig(info *datamodel.SpecInfo,
configuration *datamodel.DocumentConfiguration) (*Swagger, []error) {
return createDocument(info, configuration)
configuration *datamodel.DocumentConfiguration) (*Swagger, []error) {
return createDocument(info, configuration)
}
// CreateDocument will create a new Swagger document from the provided SpecInfo.
//
// Deprecated: Use CreateDocumentFromConfig instead.
func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) {
return createDocument(info, &datamodel.DocumentConfiguration{
AllowRemoteReferences: true,
AllowFileReferences: true,
})
return createDocument(info, &datamodel.DocumentConfiguration{
AllowRemoteReferences: true,
AllowFileReferences: true,
})
}
func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, []error) {
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}}
doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0])
// build an index
idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{
BaseURL: config.BaseURL,
AllowRemoteLookup: config.AllowRemoteReferences,
AllowFileLookup: config.AllowFileReferences,
})
doc.Index = idx
doc.SpecInfo = info
// build an index
idx := index.NewSpecIndexWithConfig(info.RootNode, &index.SpecIndexConfig{
BaseURL: config.BaseURL,
AllowRemoteLookup: config.AllowRemoteReferences,
AllowFileLookup: config.AllowFileReferences,
})
doc.Index = idx
doc.SpecInfo = info
var errors []error
var errors []error
// build out swagger scalar variables.
_ = low.BuildModel(info.RootNode.Content[0], &doc)
// build out swagger scalar variables.
_ = low.BuildModel(info.RootNode.Content[0], &doc)
// extract externalDocs
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
if err != nil {
errors = append(errors, err)
}
// extract externalDocs
extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx)
if err != nil {
errors = append(errors, err)
}
doc.ExternalDocs = extDocs
doc.ExternalDocs = extDocs
// create resolver and check for circular references.
resolve := resolver.NewResolver(idx)
resolvingErrors := resolve.CheckForCircularReferences()
// create resolver and check for circular references.
resolve := resolver.NewResolver(idx)
resolvingErrors := resolve.CheckForCircularReferences()
if len(resolvingErrors) > 0 {
for r := range resolvingErrors {
errors = append(errors, resolvingErrors[r])
}
}
if len(resolvingErrors) > 0 {
for r := range resolvingErrors {
errors = append(errors, resolvingErrors[r])
}
}
extractionFuncs := []documentFunction{
extractInfo,
extractPaths,
extractDefinitions,
extractParamDefinitions,
extractResponsesDefinitions,
extractSecurityDefinitions,
extractTags,
extractSecurity,
}
doneChan := make(chan bool)
errChan := make(chan error)
for i := range extractionFuncs {
go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan)
}
completedExtractions := 0
for completedExtractions < len(extractionFuncs) {
select {
case <-doneChan:
completedExtractions++
case e := <-errChan:
completedExtractions++
errors = append(errors, e)
}
}
extractionFuncs := []documentFunction{
extractInfo,
extractPaths,
extractDefinitions,
extractParamDefinitions,
extractResponsesDefinitions,
extractSecurityDefinitions,
extractTags,
extractSecurity,
}
doneChan := make(chan bool)
errChan := make(chan error)
for i := range extractionFuncs {
go extractionFuncs[i](info.RootNode.Content[0], &doc, idx, doneChan, errChan)
}
completedExtractions := 0
for completedExtractions < len(extractionFuncs) {
select {
case <-doneChan:
completedExtractions++
case e := <-errChan:
completedExtractions++
errors = append(errors, e)
}
}
return &doc, errors
return &doc, errors
}
func (s *Swagger) GetExternalDocs() *low.NodeReference[any] {
return &low.NodeReference[any]{
KeyNode: s.ExternalDocs.KeyNode,
ValueNode: s.ExternalDocs.ValueNode,
Value: s.ExternalDocs.Value,
}
return &low.NodeReference[any]{
KeyNode: s.ExternalDocs.KeyNode,
ValueNode: s.ExternalDocs.ValueNode,
Value: s.ExternalDocs.Value,
}
}
func extractInfo(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Info = info
c <- true
info, err := low.ExtractObject[*base.Info](base.InfoLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Info = info
c <- true
}
func extractPaths(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Paths = paths
c <- true
paths, err := low.ExtractObject[*Paths](PathsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Paths = paths
c <- true
}
func extractDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Definitions = def
c <- true
def, err := low.ExtractObject[*Definitions](DefinitionsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Definitions = def
c <- true
}
func extractParamDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Parameters = param
c <- true
param, err := low.ExtractObject[*ParameterDefinitions](ParametersLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Parameters = param
c <- true
}
func extractResponsesDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Responses = resp
c <- true
resp, err := low.ExtractObject[*ResponsesDefinitions](ResponsesLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Responses = resp
c <- true
}
func extractSecurityDefinitions(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.SecurityDefinitions = sec
c <- true
sec, err := low.ExtractObject[*SecurityDefinitions](SecurityDefinitionsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.SecurityDefinitions = sec
c <- true
}
func extractTags(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
Value: tags,
KeyNode: ln,
ValueNode: vn,
}
c <- true
tags, ln, vn, err := low.ExtractArray[*base.Tag](base.TagsLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Tags = low.NodeReference[[]low.ValueReference[*base.Tag]]{
Value: tags,
KeyNode: ln,
ValueNode: vn,
}
c <- true
}
func extractSecurity(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) {
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
Value: sec,
KeyNode: ln,
ValueNode: vn,
}
c <- true
sec, ln, vn, err := low.ExtractArray[*base.SecurityRequirement](SecurityLabel, root, idx)
if err != nil {
e <- err
return
}
doc.Security = low.NodeReference[[]low.ValueReference[*base.SecurityRequirement]]{
Value: sec,
KeyNode: ln,
ValueNode: vn,
}
c <- true
}

View File

@@ -4,350 +4,350 @@
package v2
import (
"fmt"
"github.com/pb33f/libopenapi/datamodel"
"github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
"fmt"
"github.com/pb33f/libopenapi/datamodel"
"github.com/stretchr/testify/assert"
"io/ioutil"
"testing"
)
var doc *Swagger
func initTest() {
if doc != nil {
return
}
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
var err []error
doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
AllowFileReferences: false,
AllowRemoteReferences: false,
})
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
if err != nil {
fmt.Print(err)
panic(err)
}
if doc != nil {
return
}
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
var err []error
doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
AllowFileReferences: false,
AllowRemoteReferences: false,
})
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
if err != nil {
fmt.Print(err)
panic(err)
}
}
func BenchmarkCreateDocument(b *testing.B) {
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
for i := 0; i < b.N; i++ {
doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
AllowFileReferences: false,
AllowRemoteReferences: false,
})
}
data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
for i := 0; i < b.N; i++ {
doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
AllowFileReferences: false,
AllowRemoteReferences: false,
})
}
}
func TestCreateDocument(t *testing.T) {
initTest()
doc := doc
assert.Equal(t, "2.0", doc.SpecInfo.Version)
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
assert.Equal(t, "/v2", doc.BasePath.Value)
assert.Len(t, doc.Parameters.Value.Definitions, 1)
assert.Len(t, doc.Tags.Value, 3)
assert.Len(t, doc.Schemes.Value, 2)
assert.Len(t, doc.Definitions.Value.Schemas, 6)
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3)
assert.Len(t, doc.Paths.Value.PathItems, 15)
assert.Len(t, doc.Responses.Value.Definitions, 2)
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
assert.NotNil(t, doc.GetExternalDocs())
assert.Len(t, doc.GetExtensions(), 1)
initTest()
doc := doc
assert.Equal(t, "2.0", doc.SpecInfo.Version)
assert.Equal(t, "1.0.6", doc.Info.Value.Version.Value)
assert.Equal(t, "petstore.swagger.io", doc.Host.Value)
assert.Equal(t, "/v2", doc.BasePath.Value)
assert.Len(t, doc.Parameters.Value.Definitions, 1)
assert.Len(t, doc.Tags.Value, 3)
assert.Len(t, doc.Schemes.Value, 2)
assert.Len(t, doc.Definitions.Value.Schemas, 6)
assert.Len(t, doc.SecurityDefinitions.Value.Definitions, 3)
assert.Len(t, doc.Paths.Value.PathItems, 15)
assert.Len(t, doc.Responses.Value.Definitions, 2)
assert.Equal(t, "http://swagger.io", doc.ExternalDocs.Value.URL.Value)
assert.Equal(t, true, doc.FindExtension("x-pet").Value)
assert.Equal(t, true, doc.FindExtension("X-Pet").Value)
assert.NotNil(t, doc.GetExternalDocs())
assert.Len(t, doc.GetExtensions(), 1)
}
func TestCreateDocument_Info(t *testing.T) {
initTest()
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
initTest()
assert.Equal(t, "Swagger Petstore", doc.Info.Value.Title.Value)
assert.Equal(t, "apiteam@swagger.io", doc.Info.Value.Contact.Value.Email.Value)
assert.Equal(t, "Apache 2.0", doc.Info.Value.License.Value.Name.Value)
}
func TestCreateDocument_Parameters(t *testing.T) {
initTest()
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
assert.NotNil(t, simpleParam)
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
initTest()
simpleParam := doc.Parameters.Value.FindParameter("simpleParam")
assert.NotNil(t, simpleParam)
assert.Equal(t, "simple", simpleParam.Value.Name.Value)
assert.Equal(t, "nuggets", simpleParam.Value.FindExtension("x-chicken").Value)
}
func TestCreateDocument_Tags(t *testing.T) {
initTest()
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value)
assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value)
assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value)
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
initTest()
assert.Equal(t, "pet", doc.Tags.Value[0].Value.Name.Value)
assert.Equal(t, "http://swagger.io", doc.Tags.Value[0].Value.ExternalDocs.Value.URL.Value)
assert.Equal(t, "store", doc.Tags.Value[1].Value.Name.Value)
assert.Equal(t, "user", doc.Tags.Value[2].Value.Name.Value)
assert.Equal(t, "http://swagger.io", doc.Tags.Value[2].Value.ExternalDocs.Value.URL.Value)
}
func TestCreateDocument_SecurityDefinitions(t *testing.T) {
initTest()
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
initTest()
apiKey := doc.SecurityDefinitions.Value.FindSecurityDefinition("api_key")
assert.Equal(t, "apiKey", apiKey.Value.Type.Value)
petStoreAuth := doc.SecurityDefinitions.Value.FindSecurityDefinition("petstore_auth")
assert.Equal(t, "oauth2", petStoreAuth.Value.Type.Value)
assert.Equal(t, "implicit", petStoreAuth.Value.Flow.Value)
assert.Len(t, petStoreAuth.Value.Scopes.Value.Values, 2)
assert.Equal(t, "read your pets", petStoreAuth.Value.Scopes.Value.FindScope("read:pets").Value)
}
func TestCreateDocument_Definitions(t *testing.T) {
initTest()
apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema()
assert.NotNil(t, apiResp)
assert.Len(t, apiResp.Properties.Value, 3)
assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A)
initTest()
apiResp := doc.Definitions.Value.FindSchema("ApiResponse").Value.Schema()
assert.NotNil(t, apiResp)
assert.Len(t, apiResp.Properties.Value, 3)
assert.Equal(t, "integer", apiResp.FindProperty("code").Value.Schema().Type.Value.A)
pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema()
assert.NotNil(t, pet)
assert.Len(t, pet.Required.Value, 2)
pet := doc.Definitions.Value.FindSchema("Pet").Value.Schema()
assert.NotNil(t, pet)
assert.Len(t, pet.Required.Value, 2)
// perform a deep inline lookup on a schema to ensure chains work
assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value)
// perform a deep inline lookup on a schema to ensure chains work
assert.Equal(t, "Category", pet.FindProperty("category").Value.Schema().XML.Value.Name.Value)
// check enums
assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3)
// check enums
assert.Len(t, pet.FindProperty("status").Value.Schema().Enum.Value, 3)
}
func TestCreateDocument_ResponseDefinitions(t *testing.T) {
initTest()
apiResp := doc.Responses.Value.FindResponse("200")
assert.NotNil(t, apiResp)
assert.Equal(t, "OK", apiResp.Value.Description.Value)
assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value)
initTest()
apiResp := doc.Responses.Value.FindResponse("200")
assert.NotNil(t, apiResp)
assert.Equal(t, "OK", apiResp.Value.Description.Value)
assert.Equal(t, "morning", apiResp.Value.FindExtension("x-coffee").Value)
header := apiResp.Value.FindHeader("noHeader")
assert.NotNil(t, header)
assert.True(t, header.Value.FindExtension("x-empty").Value.(bool))
header := apiResp.Value.FindHeader("noHeader")
assert.NotNil(t, header)
assert.True(t, header.Value.FindExtension("x-empty").Value.(bool))
header = apiResp.Value.FindHeader("myHeader")
if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok {
assert.Equal(t, "here", k["something"])
} else {
panic("should not fail.")
}
if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok {
assert.Len(t, k, 2)
assert.Equal(t, "two", k[1])
} else {
panic("should not fail.")
}
header = apiResp.Value.FindHeader("myHeader")
if k, ok := header.Value.Items.Value.Default.Value.(map[string]interface{}); ok {
assert.Equal(t, "here", k["something"])
} else {
panic("should not fail.")
}
if k, ok := header.Value.Items.Value.Items.Value.Default.Value.([]interface{}); ok {
assert.Len(t, k, 2)
assert.Equal(t, "two", k[1])
} else {
panic("should not fail.")
}
header = apiResp.Value.FindHeader("yourHeader")
assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value)
header = apiResp.Value.FindHeader("yourHeader")
assert.Equal(t, "somethingSimple", header.Value.Items.Value.Default.Value)
assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value)
assert.NotNil(t, apiResp.Value.Examples.Value.FindExample("application/json").Value)
}
func TestCreateDocument_Paths(t *testing.T) {
initTest()
uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value
assert.NotNil(t, uploadImage)
assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow"))
assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value)
assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value)
assert.Equal(t, "successful operation",
uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value)
initTest()
uploadImage := doc.Paths.Value.FindPath("/pet/{petId}/uploadImage").Value
assert.NotNil(t, uploadImage)
assert.Nil(t, doc.Paths.Value.FindPath("/nothing-nowhere-nohow"))
assert.Equal(t, "man", uploadImage.FindExtension("x-potato").Value)
assert.Equal(t, "fresh", doc.Paths.Value.FindExtension("x-minty").Value)
assert.Equal(t, "successful operation",
uploadImage.Post.Value.Responses.Value.FindResponseByCode("200").Value.Description.Value)
}
func TestCreateDocument_Bad(t *testing.T) {
yml := `swagger:
yml := `swagger:
$ref: bork`
info, err := datamodel.ExtractSpecInfo([]byte(yml))
assert.Nil(t, info)
assert.Error(t, err)
info, err := datamodel.ExtractSpecInfo([]byte(yml))
assert.Nil(t, info)
assert.Error(t, err)
}
func TestCreateDocument_ExternalDocsBad(t *testing.T) {
yml := `externalDocs:
yml := `externalDocs:
$ref: bork`
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_TagsBad(t *testing.T) {
yml := `tags:
yml := `tags:
$ref: bork`
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_PathsBad(t *testing.T) {
yml := `paths:
yml := `paths:
"/hey":
post:
responses:
"200":
$ref: bork`
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_SecurityBad(t *testing.T) {
yml := `security:
yml := `security:
$ref: `
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) {
yml := `securityDefinitions:
yml := `securityDefinitions:
$ref: `
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_ResponsesBad(t *testing.T) {
yml := `responses:
yml := `responses:
$ref: `
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_ParametersBad(t *testing.T) {
yml := `parameters:
yml := `parameters:
$ref: `
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_DefinitionsBad(t *testing.T) {
yml := `definitions:
yml := `definitions:
$ref: `
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCreateDocument_InfoBad(t *testing.T) {
yml := `info:
yml := `info:
$ref: `
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
var err []error
doc, err = CreateDocument(info)
wait := true
for wait {
select {
case <-info.JsonParsingChannel:
wait = false
}
}
assert.Len(t, err, 1)
}
func TestCircularReferenceError(t *testing.T) {
data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
circDoc, err := CreateDocument(info)
assert.NotNil(t, circDoc)
assert.Len(t, err, 3)
data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml")
info, _ := datamodel.ExtractSpecInfo(data)
circDoc, err := CreateDocument(info)
assert.NotNil(t, circDoc)
assert.Len(t, err, 3)
}

View File

@@ -19,7 +19,7 @@ import (
// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected
// responses. The key value used to identify the path item object is an expression, evaluated at runtime,
// that identifies a URL to use for the callback operation.
// - https://spec.openapis.org/oas/v3.1.0#callback-object
// - https://spec.openapis.org/oas/v3.1.0#callback-object
type Callback struct {
Expression low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
@@ -61,9 +61,9 @@ func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error {
Value: currentCB.Value,
KeyNode: currentCB,
}] = low.ValueReference[*PathItem]{
Value: callback,
ValueNode: callbackNode,
Reference: rv,
Value: callback,
ValueNode: callbackNode,
Reference: rv,
}
}
if len(callbacks) > 0 {

View File

@@ -19,7 +19,7 @@ import (
//
// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
// - https://spec.openapis.org/oas/v3.1.0#components-object
// - https://spec.openapis.org/oas/v3.1.0#components-object
type Components struct {
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]]
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]

View File

@@ -13,7 +13,7 @@ import (
)
// Encoding represents a low-level OpenAPI 3+ Encoding object
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
// - https://spec.openapis.org/oas/v3.1.0#encoding-object
type Encoding struct {
ContentType low.NodeReference[string]
Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]]

View File

@@ -16,7 +16,7 @@ import (
)
// Header represents a low-level OpenAPI 3+ Header object.
// - https://spec.openapis.org/oas/v3.1.0#header-object
// - https://spec.openapis.org/oas/v3.1.0#header-object
type Header struct {
Description low.NodeReference[string]
Required low.NodeReference[bool]

View File

@@ -24,7 +24,7 @@ import (
//
// For computing links, and providing instructions to execute them, a runtime expression is used for accessing values
// in an operation and using them as parameters while invoking the linked operation.
// - https://spec.openapis.org/oas/v3.1.0#link-object
// - https://spec.openapis.org/oas/v3.1.0#link-object
type Link struct {
OperationRef low.NodeReference[string]
OperationId low.NodeReference[string]

View File

@@ -18,7 +18,7 @@ import (
// MediaType represents a low-level OpenAPI MediaType object.
//
// Each Media Type Object provides schema and examples for the media type identified by its key.
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
type MediaType struct {
Schema low.NodeReference[*base.SchemaProxy]
Example low.NodeReference[any]

View File

@@ -14,7 +14,7 @@ import (
)
// OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
type OAuthFlows struct {
Implicit low.NodeReference[*OAuthFlow]
Password low.NodeReference[*OAuthFlow]
@@ -87,7 +87,7 @@ func (o *OAuthFlows) Hash() [32]byte {
}
// OAuthFlow represents a low-level OpenAPI 3+ OAuthFlow object.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
type OAuthFlow struct {
AuthorizationUrl low.NodeReference[string]
TokenUrl low.NodeReference[string]

View File

@@ -18,7 +18,7 @@ import (
//
// An Operation is perhaps the most important object of the entire specification. Everything of value
// happens here. The entire being for existence of this library and the specification, is this Operation.
// - https://spec.openapis.org/oas/v3.1.0#operation-object
// - https://spec.openapis.org/oas/v3.1.0#operation-object
type Operation struct {
Tags low.NodeReference[[]low.ValueReference[string]]
Summary low.NodeReference[string]

View File

@@ -18,7 +18,7 @@ import (
// Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one.
//
// A unique parameter is defined by a combination of a name and location.
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
type Parameter struct {
Name low.NodeReference[string]
In low.NodeReference[string]

View File

@@ -14,7 +14,7 @@ import (
)
// RequestBody represents a low-level OpenAPI 3+ RequestBody object.
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
// - https://spec.openapis.org/oas/v3.1.0#request-body-object
type RequestBody struct {
Description low.NodeReference[string]
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]

View File

@@ -22,7 +22,7 @@ import (
// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery.
// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice.
// Recommended for most use case is Authorization Code Grant flow with PKCE.
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
type SecurityScheme struct {
Type low.NodeReference[string]
Description low.NodeReference[string]

View File

@@ -14,7 +14,7 @@ import (
)
// Server represents a low-level OpenAPI 3+ Server object.
// - https://spec.openapis.org/oas/v3.1.0#server-object
// - https://spec.openapis.org/oas/v3.1.0#server-object
type Server struct {
URL low.NodeReference[string]
Description low.NodeReference[string]

View File

@@ -4,31 +4,31 @@
package datamodel
import (
"fmt"
"io/ioutil"
"testing"
"fmt"
"io/ioutil"
"testing"
"github.com/pb33f/libopenapi/utils"
"github.com/stretchr/testify/assert"
"github.com/pb33f/libopenapi/utils"
"github.com/stretchr/testify/assert"
)
const (
// OpenApi3 is used by all OpenAPI 3+ docs
OpenApi3 = "openapi"
// OpenApi3 is used by all OpenAPI 3+ docs
OpenApi3 = "openapi"
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
OpenApi2 = "swagger"
// OpenApi2 is used by all OpenAPI 2 docs, formerly known as swagger.
OpenApi2 = "swagger"
// AsyncApi is used by akk AsyncAPI docs, all versions.
AsyncApi = "asyncapi"
// AsyncApi is used by akk AsyncAPI docs, all versions.
AsyncApi = "asyncapi"
)
func TestSpecInfo_GetJSONParsingChannel(t *testing.T) {
// dumb, but we need to ensure coverage is as high as we can make it.
bchan := make(chan bool)
si := &SpecInfo{JsonParsingChannel: bchan}
assert.Equal(t, si.GetJSONParsingChannel(), bchan)
// dumb, but we need to ensure coverage is as high as we can make it.
bchan := make(chan bool)
si := &SpecInfo{JsonParsingChannel: bchan}
assert.Equal(t, si.GetJSONParsingChannel(), bchan)
}
@@ -115,147 +115,147 @@ info:
version: '0.1.0'`
func TestExtractSpecInfo_ValidJSON(t *testing.T) {
r, e := ExtractSpecInfo([]byte(goodJSON))
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Error(t, e)
r, e := ExtractSpecInfo([]byte(goodJSON))
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Error(t, e)
}
func TestExtractSpecInfo_InvalidJSON(t *testing.T) {
_, e := ExtractSpecInfo([]byte(badJSON))
assert.Error(t, e)
_, e := ExtractSpecInfo([]byte(badJSON))
assert.Error(t, e)
}
func TestExtractSpecInfo_Nothing(t *testing.T) {
_, e := ExtractSpecInfo([]byte(""))
assert.Error(t, e)
_, e := ExtractSpecInfo([]byte(""))
assert.Error(t, e)
}
func TestExtractSpecInfo_ValidYAML(t *testing.T) {
r, e := ExtractSpecInfo([]byte(goodYAML))
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Error(t, e)
r, e := ExtractSpecInfo([]byte(goodYAML))
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Error(t, e)
}
func TestExtractSpecInfo_InvalidYAML(t *testing.T) {
_, e := ExtractSpecInfo([]byte(badYAML))
assert.Error(t, e)
_, e := ExtractSpecInfo([]byte(badYAML))
assert.Error(t, e)
}
func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) {
_, e := ExtractSpecInfo([]byte(OpenApiOne))
assert.Error(t, e)
_, e := ExtractSpecInfo([]byte(OpenApiOne))
assert.Error(t, e)
}
func TestExtractSpecInfo_OpenAPI3(t *testing.T) {
r, e := ExtractSpecInfo([]byte(OpenApi3Spec))
assert.Nil(t, e)
assert.Equal(t, utils.OpenApi3, r.SpecType)
assert.Equal(t, "3.0.1", r.Version)
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28")
r, e := ExtractSpecInfo([]byte(OpenApi3Spec))
assert.Nil(t, e)
assert.Equal(t, utils.OpenApi3, r.SpecType)
assert.Equal(t, "3.0.1", r.Version)
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.0/schema/2021-09-28")
}
func TestExtractSpecInfo_OpenAPIWat(t *testing.T) {
r, e := ExtractSpecInfo([]byte(OpenApiWat))
assert.Nil(t, e)
assert.Equal(t, OpenApi3, r.SpecType)
assert.Equal(t, "3.2", r.Version)
r, e := ExtractSpecInfo([]byte(OpenApiWat))
assert.Nil(t, e)
assert.Equal(t, OpenApi3, r.SpecType)
assert.Equal(t, "3.2", r.Version)
}
func TestExtractSpecInfo_OpenAPI31(t *testing.T) {
r, e := ExtractSpecInfo([]byte(OpenApi31))
assert.Nil(t, e)
assert.Equal(t, OpenApi3, r.SpecType)
assert.Equal(t, "3.1", r.Version)
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07")
r, e := ExtractSpecInfo([]byte(OpenApi31))
assert.Nil(t, e)
assert.Equal(t, OpenApi3, r.SpecType)
assert.Equal(t, "3.1", r.Version)
assert.Contains(t, r.APISchema, "https://spec.openapis.org/oas/3.1/schema/2022-10-07")
}
func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) {
spec, e := ExtractSpecInfo([]byte(OpenApiFalse))
assert.NoError(t, e)
assert.Equal(t, "false", spec.Version)
spec, e := ExtractSpecInfo([]byte(OpenApiFalse))
assert.NoError(t, e)
assert.Equal(t, "false", spec.Version)
}
func TestExtractSpecInfo_OpenAPI2(t *testing.T) {
r, e := ExtractSpecInfo([]byte(OpenApi2Spec))
assert.Nil(t, e)
assert.Equal(t, OpenApi2, r.SpecType)
assert.Equal(t, "2.0.1", r.Version)
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#")
r, e := ExtractSpecInfo([]byte(OpenApi2Spec))
assert.Nil(t, e)
assert.Equal(t, OpenApi2, r.SpecType)
assert.Equal(t, "2.0.1", r.Version)
assert.Greater(t, len(*r.SpecJSONBytes), 0)
assert.Contains(t, r.APISchema, "http://swagger.io/v2/schema.json#")
}
func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) {
_, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd))
assert.NotNil(t, e)
assert.Equal(t,
"spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error())
_, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd))
assert.NotNil(t, e)
assert.Equal(t,
"spec is defined as a swagger (openapi 2.0) spec, but is an openapi 3 or unknown version", e.Error())
}
func TestExtractSpecInfo_AsyncAPI(t *testing.T) {
r, e := ExtractSpecInfo([]byte(AsyncAPISpec))
assert.Nil(t, e)
assert.Equal(t, AsyncApi, r.SpecType)
assert.Equal(t, "2.0.0", r.Version)
assert.Greater(t, len(*r.SpecJSONBytes), 0)
r, e := ExtractSpecInfo([]byte(AsyncAPISpec))
assert.Nil(t, e)
assert.Equal(t, AsyncApi, r.SpecType)
assert.Equal(t, "2.0.0", r.Version)
assert.Greater(t, len(*r.SpecJSONBytes), 0)
}
func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) {
_, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd))
assert.NotNil(t, e)
assert.Equal(t,
"spec is defined as asyncapi, but has a major version that is invalid", e.Error())
_, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd))
assert.NotNil(t, e)
assert.Equal(t,
"spec is defined as asyncapi, but has a major version that is invalid", e.Error())
}
func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) {
yml := `openapi:
yml := `openapi:
should: fail`
_, err := ExtractSpecInfo([]byte(yml))
assert.Error(t, err)
_, err := ExtractSpecInfo([]byte(yml))
assert.Error(t, err)
}
func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) {
yml := `swagger:
yml := `swagger:
should: fail`
_, err := ExtractSpecInfo([]byte(yml))
assert.Error(t, err)
_, err := ExtractSpecInfo([]byte(yml))
assert.Error(t, err)
}
func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) {
yml := `asyncapi:
yml := `asyncapi:
should: fail`
_, err := ExtractSpecInfo([]byte(yml))
assert.Error(t, err)
_, err := ExtractSpecInfo([]byte(yml))
assert.Error(t, err)
}
func ExampleExtractSpecInfo() {
// load bytes from openapi spec file.
bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json")
// load bytes from openapi spec file.
bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json")
// create a new *SpecInfo instance from loaded bytes
specInfo, err := ExtractSpecInfo(bytes)
if err != nil {
panic(fmt.Sprintf("cannot extract spec info: %e", err))
}
// create a new *SpecInfo instance from loaded bytes
specInfo, err := ExtractSpecInfo(bytes)
if err != nil {
panic(fmt.Sprintf("cannot extract spec info: %e", err))
}
// print out the version, format and filetype
fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s",
specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType)
// print out the version, format and filetype
fmt.Printf("the version of the spec is %s, the format is %s and the file type is %s",
specInfo.Version, specInfo.SpecFormat, specInfo.SpecFileType)
// Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json
// Output: the version of the spec is 3.0.2, the format is oas3 and the file type is json
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,22 +4,22 @@
package index
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSpecIndex_Children(t *testing.T) {
idx1 := new(SpecIndex)
idx2 := new(SpecIndex)
idx3 := new(SpecIndex)
idx4 := new(SpecIndex)
idx5 := new(SpecIndex)
idx1.AddChild(idx2)
idx1.AddChild(idx3)
idx3.AddChild(idx4)
idx4.AddChild(idx5)
assert.Equal(t, 2, len(idx1.GetChildren()))
assert.Equal(t, 1, len(idx3.GetChildren()))
assert.Equal(t, 1, len(idx4.GetChildren()))
assert.Equal(t, 0, len(idx5.GetChildren()))
idx1 := new(SpecIndex)
idx2 := new(SpecIndex)
idx3 := new(SpecIndex)
idx4 := new(SpecIndex)
idx5 := new(SpecIndex)
idx1.AddChild(idx2)
idx1.AddChild(idx3)
idx3.AddChild(idx4)
idx4.AddChild(idx5)
assert.Equal(t, 2, len(idx1.GetChildren()))
assert.Equal(t, 1, len(idx3.GetChildren()))
assert.Equal(t, 1, len(idx4.GetChildren()))
assert.Equal(t, 0, len(idx5.GetChildren()))
}

View File

@@ -4,88 +4,88 @@
package index
import (
"gopkg.in/yaml.v3"
"net/http"
"strings"
"time"
"gopkg.in/yaml.v3"
"net/http"
"strings"
"time"
)
func isHttpMethod(val string) bool {
switch strings.ToLower(val) {
case methodTypes[0]:
return true
case methodTypes[1]:
return true
case methodTypes[2]:
return true
case methodTypes[3]:
return true
case methodTypes[4]:
return true
case methodTypes[5]:
return true
case methodTypes[6]:
return true
}
return false
switch strings.ToLower(val) {
case methodTypes[0]:
return true
case methodTypes[1]:
return true
case methodTypes[2]:
return true
case methodTypes[3]:
return true
case methodTypes[4]:
return true
case methodTypes[5]:
return true
case methodTypes[6]:
return true
}
return false
}
func DetermineReferenceResolveType(ref string) int {
if ref != "" && ref[0] == '#' {
return LocalResolve
}
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
return HttpResolve
}
if strings.Contains(ref, ".json") ||
strings.Contains(ref, ".yaml") ||
strings.Contains(ref, ".yml") {
return FileResolve
}
return -1
if ref != "" && ref[0] == '#' {
return LocalResolve
}
if ref != "" && len(ref) >= 5 && (ref[:5] == "https" || ref[:5] == "http:") {
return HttpResolve
}
if strings.Contains(ref, ".json") ||
strings.Contains(ref, ".yaml") ||
strings.Contains(ref, ".yml") {
return FileResolve
}
return -1
}
func boostrapIndexCollections(rootNode *yaml.Node, index *SpecIndex) {
index.root = rootNode
index.allRefs = make(map[string]*Reference)
index.allMappedRefs = make(map[string]*Reference)
index.refsByLine = make(map[string]map[int]bool)
index.linesWithRefs = make(map[int]bool)
index.pathRefs = make(map[string]map[string]*Reference)
index.paramOpRefs = make(map[string]map[string]map[string][]*Reference)
index.operationTagsRefs = make(map[string]map[string][]*Reference)
index.operationDescriptionRefs = make(map[string]map[string]*Reference)
index.operationSummaryRefs = make(map[string]map[string]*Reference)
index.paramCompRefs = make(map[string]*Reference)
index.paramAllRefs = make(map[string]*Reference)
index.paramInlineDuplicateNames = make(map[string][]*Reference)
index.globalTagRefs = make(map[string]*Reference)
index.securitySchemeRefs = make(map[string]*Reference)
index.requestBodiesRefs = make(map[string]*Reference)
index.responsesRefs = make(map[string]*Reference)
index.headersRefs = make(map[string]*Reference)
index.examplesRefs = make(map[string]*Reference)
index.callbacksRefs = make(map[string]map[string][]*Reference)
index.linksRefs = make(map[string]map[string][]*Reference)
index.callbackRefs = make(map[string]*Reference)
index.externalSpecIndex = make(map[string]*SpecIndex)
index.allComponentSchemaDefinitions = make(map[string]*Reference)
index.allParameters = make(map[string]*Reference)
index.allSecuritySchemes = make(map[string]*Reference)
index.allRequestBodies = make(map[string]*Reference)
index.allResponses = make(map[string]*Reference)
index.allHeaders = make(map[string]*Reference)
index.allExamples = make(map[string]*Reference)
index.allLinks = make(map[string]*Reference)
index.allCallbacks = make(map[string]*Reference)
index.allExternalDocuments = make(map[string]*Reference)
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
index.polymorphicRefs = make(map[string]*Reference)
index.refsWithSiblings = make(map[string]Reference)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenLocalSources = make(map[string]*yaml.Node)
index.opServersRefs = make(map[string]map[string][]*Reference)
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
index.componentIndexChan = make(chan bool)
index.polyComponentIndexChan = make(chan bool)
index.root = rootNode
index.allRefs = make(map[string]*Reference)
index.allMappedRefs = make(map[string]*Reference)
index.refsByLine = make(map[string]map[int]bool)
index.linesWithRefs = make(map[int]bool)
index.pathRefs = make(map[string]map[string]*Reference)
index.paramOpRefs = make(map[string]map[string]map[string][]*Reference)
index.operationTagsRefs = make(map[string]map[string][]*Reference)
index.operationDescriptionRefs = make(map[string]map[string]*Reference)
index.operationSummaryRefs = make(map[string]map[string]*Reference)
index.paramCompRefs = make(map[string]*Reference)
index.paramAllRefs = make(map[string]*Reference)
index.paramInlineDuplicateNames = make(map[string][]*Reference)
index.globalTagRefs = make(map[string]*Reference)
index.securitySchemeRefs = make(map[string]*Reference)
index.requestBodiesRefs = make(map[string]*Reference)
index.responsesRefs = make(map[string]*Reference)
index.headersRefs = make(map[string]*Reference)
index.examplesRefs = make(map[string]*Reference)
index.callbacksRefs = make(map[string]map[string][]*Reference)
index.linksRefs = make(map[string]map[string][]*Reference)
index.callbackRefs = make(map[string]*Reference)
index.externalSpecIndex = make(map[string]*SpecIndex)
index.allComponentSchemaDefinitions = make(map[string]*Reference)
index.allParameters = make(map[string]*Reference)
index.allSecuritySchemes = make(map[string]*Reference)
index.allRequestBodies = make(map[string]*Reference)
index.allResponses = make(map[string]*Reference)
index.allHeaders = make(map[string]*Reference)
index.allExamples = make(map[string]*Reference)
index.allLinks = make(map[string]*Reference)
index.allCallbacks = make(map[string]*Reference)
index.allExternalDocuments = make(map[string]*Reference)
index.securityRequirementRefs = make(map[string]map[string][]*Reference)
index.polymorphicRefs = make(map[string]*Reference)
index.refsWithSiblings = make(map[string]Reference)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenLocalSources = make(map[string]*yaml.Node)
index.opServersRefs = make(map[string]map[string][]*Reference)
index.httpClient = &http.Client{Timeout: time.Duration(5) * time.Second}
index.componentIndexChan = make(chan bool)
index.polyComponentIndexChan = make(chan bool)
}

View File

@@ -4,447 +4,445 @@
package index
import (
"fmt"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"net/url"
"strings"
"sync"
"fmt"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
"net/url"
"strings"
"sync"
)
func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pathPrefix string) {
var name string
for i, schema := range schemasNode.Content {
if i%2 == 0 {
name = schema.Value
continue
}
var name string
for i, schema := range schemasNode.Content {
if i%2 == 0 {
name = schema.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: schema,
Path: fmt.Sprintf("$.components.schemas.%s", name),
ParentNode: schemasNode,
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}),
}
index.allComponentSchemaDefinitions[def] = ref
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: schema,
Path: fmt.Sprintf("$.components.schemas.%s", name),
ParentNode: schemasNode,
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}),
}
index.allComponentSchemaDefinitions[def] = ref
}
}
// extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it
func (index *SpecIndex) extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string) map[string][]string {
if schemaNode == nil {
return reqRefProps
}
if schemaNode == nil {
return reqRefProps
}
// If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties
isRef, _, defPath := utils.IsNodeRefValue(schemaNode)
if isRef {
if _, ok := reqRefProps[defPath]; !ok {
reqRefProps[defPath] = []string{}
}
}
// If the node we're looking at is a direct ref to another model without any properties, mark it as required, but still continue to look for required properties
isRef, _, defPath := utils.IsNodeRefValue(schemaNode)
if isRef {
if _, ok := reqRefProps[defPath]; !ok {
reqRefProps[defPath] = []string{}
}
}
// Check for a required parameters list, and return if none exists, as any properties will be optional
_, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content)
if requiredSeqNode == nil {
return reqRefProps
}
// Check for a required parameters list, and return if none exists, as any properties will be optional
_, requiredSeqNode := utils.FindKeyNodeTop("required", schemaNode.Content)
if requiredSeqNode == nil {
return reqRefProps
}
_, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content)
if propertiesMapNode == nil {
// TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong
return reqRefProps
}
_, propertiesMapNode := utils.FindKeyNodeTop("properties", schemaNode.Content)
if propertiesMapNode == nil {
// TODO: Log a warning on the resolver, because if you have required properties, but no actual properties, something is wrong
return reqRefProps
}
name := ""
for i, param := range propertiesMapNode.Content {
if i%2 == 0 {
name = param.Value
continue
}
name := ""
for i, param := range propertiesMapNode.Content {
if i%2 == 0 {
name = param.Value
continue
}
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
if paramPropertiesMapNode != nil {
reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps)
}
// Check to see if the current property is directly embedded within the current schema, and handle its properties if so
_, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content)
if paramPropertiesMapNode != nil {
reqRefProps = index.extractDefinitionRequiredRefProperties(param, reqRefProps)
}
// Check to see if the current property is polymorphic, and dive into that model if so
for _, key := range []string{"allOf", "oneOf", "anyOf"} {
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
if ofNode != nil {
for _, ofNodeItem := range ofNode.Content {
reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps)
}
}
}
}
// Check to see if the current property is polymorphic, and dive into that model if so
for _, key := range []string{"allOf", "oneOf", "anyOf"} {
_, ofNode := utils.FindKeyNodeTop(key, param.Content)
if ofNode != nil {
for _, ofNodeItem := range ofNode.Content {
reqRefProps = index.extractRequiredReferenceProperties(ofNodeItem, name, reqRefProps)
}
}
}
}
// Run through each of the required properties and extract _their_ required references
for _, requiredPropertyNode := range requiredSeqNode.Content {
_, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content)
if requiredPropDefNode == nil {
continue
}
// Run through each of the required properties and extract _their_ required references
for _, requiredPropertyNode := range requiredSeqNode.Content {
_, requiredPropDefNode := utils.FindKeyNodeTop(requiredPropertyNode.Value, propertiesMapNode.Content)
if requiredPropDefNode == nil {
continue
}
reqRefProps = index.extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
}
reqRefProps = index.extractRequiredReferenceProperties(requiredPropDefNode, requiredPropertyNode.Value, reqRefProps)
}
return reqRefProps
return reqRefProps
}
// extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node
func (index *SpecIndex) extractRequiredReferenceProperties(requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string {
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
if !isRef {
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
if defItems != nil {
isRef, _, defPath = utils.IsNodeRefValue(defItems)
}
}
isRef, _, defPath := utils.IsNodeRefValue(requiredPropDefNode)
if !isRef {
_, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content)
if defItems != nil {
isRef, _, defPath = utils.IsNodeRefValue(defItems)
}
}
if /* still */ !isRef {
return reqRefProps
}
if /* still */ !isRef {
return reqRefProps
}
if _, ok := reqRefProps[defPath]; !ok {
reqRefProps[defPath] = []string{}
}
reqRefProps[defPath] = append(reqRefProps[defPath], propName)
if _, ok := reqRefProps[defPath]; !ok {
reqRefProps[defPath] = []string{}
}
reqRefProps[defPath] = append(reqRefProps[defPath], propName)
return reqRefProps
return reqRefProps
}
func (index *SpecIndex) extractComponentParameters(paramsNode *yaml.Node, pathPrefix string) {
var name string
for i, param := range paramsNode.Content {
if i%2 == 0 {
name = param.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: param,
}
index.allParameters[def] = ref
}
var name string
for i, param := range paramsNode.Content {
if i%2 == 0 {
name = param.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: param,
}
index.allParameters[def] = ref
}
}
func (index *SpecIndex) extractComponentRequestBodies(requestBodiesNode *yaml.Node, pathPrefix string) {
var name string
for i, reqBod := range requestBodiesNode.Content {
if i%2 == 0 {
name = reqBod.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: reqBod,
}
index.allRequestBodies[def] = ref
}
var name string
for i, reqBod := range requestBodiesNode.Content {
if i%2 == 0 {
name = reqBod.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: reqBod,
}
index.allRequestBodies[def] = ref
}
}
func (index *SpecIndex) extractComponentResponses(responsesNode *yaml.Node, pathPrefix string) {
var name string
for i, response := range responsesNode.Content {
if i%2 == 0 {
name = response.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: response,
}
index.allResponses[def] = ref
}
var name string
for i, response := range responsesNode.Content {
if i%2 == 0 {
name = response.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: response,
}
index.allResponses[def] = ref
}
}
func (index *SpecIndex) extractComponentHeaders(headersNode *yaml.Node, pathPrefix string) {
var name string
for i, header := range headersNode.Content {
if i%2 == 0 {
name = header.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: header,
}
index.allHeaders[def] = ref
}
var name string
for i, header := range headersNode.Content {
if i%2 == 0 {
name = header.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: header,
}
index.allHeaders[def] = ref
}
}
func (index *SpecIndex) extractComponentCallbacks(callbacksNode *yaml.Node, pathPrefix string) {
var name string
for i, callback := range callbacksNode.Content {
if i%2 == 0 {
name = callback.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: callback,
}
index.allCallbacks[def] = ref
}
var name string
for i, callback := range callbacksNode.Content {
if i%2 == 0 {
name = callback.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: callback,
}
index.allCallbacks[def] = ref
}
}
func (index *SpecIndex) extractComponentLinks(linksNode *yaml.Node, pathPrefix string) {
var name string
for i, link := range linksNode.Content {
if i%2 == 0 {
name = link.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: link,
}
index.allLinks[def] = ref
}
var name string
for i, link := range linksNode.Content {
if i%2 == 0 {
name = link.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: link,
}
index.allLinks[def] = ref
}
}
func (index *SpecIndex) extractComponentExamples(examplesNode *yaml.Node, pathPrefix string) {
var name string
for i, example := range examplesNode.Content {
if i%2 == 0 {
name = example.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: example,
}
index.allExamples[def] = ref
}
var name string
for i, example := range examplesNode.Content {
if i%2 == 0 {
name = example.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: example,
}
index.allExamples[def] = ref
}
}
func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yaml.Node, pathPrefix string) {
var name string
for i, secScheme := range securitySchemesNode.Content {
if i%2 == 0 {
name = secScheme.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: secScheme,
ParentNode: securitySchemesNode,
Path: fmt.Sprintf("$.components.securitySchemes.%s", name),
}
index.allSecuritySchemes[def] = ref
}
var name string
for i, secScheme := range securitySchemesNode.Content {
if i%2 == 0 {
name = secScheme.Value
continue
}
def := fmt.Sprintf("%s%s", pathPrefix, name)
ref := &Reference{
Definition: def,
Name: name,
Node: secScheme,
ParentNode: securitySchemesNode,
Path: fmt.Sprintf("$.components.securitySchemes.%s", name),
}
index.allSecuritySchemes[def] = ref
}
}
func (index *SpecIndex) countUniqueInlineDuplicates() int {
if index.componentsInlineParamUniqueCount > 0 {
return index.componentsInlineParamUniqueCount
}
unique := 0
for _, p := range index.paramInlineDuplicateNames {
if len(p) == 1 {
unique++
}
}
index.componentsInlineParamUniqueCount = unique
return unique
if index.componentsInlineParamUniqueCount > 0 {
return index.componentsInlineParamUniqueCount
}
unique := 0
for _, p := range index.paramInlineDuplicateNames {
if len(p) == 1 {
unique++
}
}
index.componentsInlineParamUniqueCount = unique
return unique
}
func (index *SpecIndex) scanOperationParams(params []*yaml.Node, pathItemNode *yaml.Node, method string) {
for i, param := range params {
// param is ref
if len(param.Content) > 0 && param.Content[0].Value == "$ref" {
for i, param := range params {
// param is ref
if len(param.Content) > 0 && param.Content[0].Value == "$ref" {
paramRefName := param.Content[1].Value
paramRef := index.allMappedRefs[paramRefName]
paramRefName := param.Content[1].Value
paramRef := index.allMappedRefs[paramRefName]
if index.paramOpRefs[pathItemNode.Value] == nil {
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
if index.paramOpRefs[pathItemNode.Value] == nil {
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
// if we know the path, but it's a new method
if index.paramOpRefs[pathItemNode.Value][method] == nil {
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
}
// if we know the path, but it's a new method
if index.paramOpRefs[pathItemNode.Value][method] == nil {
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
// if this is a duplicate, add an error and ignore it
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
}
// if this is a duplicate, add an error and ignore it
if index.paramOpRefs[pathItemNode.Value][method][paramRefName] != nil {
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
}
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
"index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName),
Node: param,
Path: path,
})
} else {
if paramRef != nil {
index.paramOpRefs[pathItemNode.Value][method][paramRefName] =
append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef)
}
}
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
"index %d has a duplicate ref `%s`", method, pathItemNode.Value, i, paramRefName),
Node: param,
Path: path,
})
} else {
if paramRef != nil {
index.paramOpRefs[pathItemNode.Value][method][paramRefName] =
append(index.paramOpRefs[pathItemNode.Value][method][paramRefName], paramRef)
}
}
continue
continue
} else {
} else {
// param is inline.
_, vn := utils.FindKeyNode("name", param.Content)
// param is inline.
_, vn := utils.FindKeyNode("name", param.Content)
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
}
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
}
if vn == nil {
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value",
method, pathItemNode.Value, i),
Node: param,
Path: path,
})
continue
}
if vn == nil {
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
Err: fmt.Errorf("the '%s' operation parameter at path '%s', index %d has no 'name' value",
method, pathItemNode.Value, i),
Node: param,
Path: path,
})
continue
}
ref := &Reference{
Definition: vn.Value,
Name: vn.Value,
Node: param,
Path: path,
}
if index.paramOpRefs[pathItemNode.Value] == nil {
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
ref := &Reference{
Definition: vn.Value,
Name: vn.Value,
Node: param,
Path: path,
}
if index.paramOpRefs[pathItemNode.Value] == nil {
index.paramOpRefs[pathItemNode.Value] = make(map[string]map[string][]*Reference)
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
// if we know the path but this is a new method.
if index.paramOpRefs[pathItemNode.Value][method] == nil {
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
// if we know the path but this is a new method.
if index.paramOpRefs[pathItemNode.Value][method] == nil {
index.paramOpRefs[pathItemNode.Value][method] = make(map[string][]*Reference)
}
// if this is a duplicate name, check if the `in` type is also the same, if so, it's a duplicate.
if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 {
// if this is a duplicate name, check if the `in` type is also the same, if so, it's a duplicate.
if len(index.paramOpRefs[pathItemNode.Value][method][ref.Name]) > 0 {
currentNode := ref.Node
checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name]
_, currentIn := utils.FindKeyNodeTop("in", currentNode.Content)
currentNode := ref.Node
checkNodes := index.paramOpRefs[pathItemNode.Value][method][ref.Name]
_, currentIn := utils.FindKeyNodeTop("in", currentNode.Content)
for _, checkNode := range checkNodes {
for _, checkNode := range checkNodes {
_, checkIn := utils.FindKeyNodeTop("in", checkNode.Node.Content)
_, checkIn := utils.FindKeyNodeTop("in", checkNode.Node.Content)
if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value {
if currentIn != nil && checkIn != nil && currentIn.Value == checkIn.Value {
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
}
path := fmt.Sprintf("$.paths.%s.%s.parameters[%d]", pathItemNode.Value, method, i)
if method == "top" {
path = fmt.Sprintf("$.paths.%s.parameters[%d]", pathItemNode.Value, i)
}
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
"index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value),
Node: param,
Path: path,
})
} else {
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
}
}
} else {
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
}
continue
}
}
index.operationParamErrors = append(index.operationParamErrors, &IndexingError{
Err: fmt.Errorf("the `%s` operation parameter at path `%s`, "+
"index %d has a duplicate name `%s` and `in` type", method, pathItemNode.Value, i, vn.Value),
Node: param,
Path: path,
})
} else {
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
}
}
} else {
index.paramOpRefs[pathItemNode.Value][method][ref.Name] =
append(index.paramOpRefs[pathItemNode.Value][method][ref.Name], ref)
}
continue
}
}
}
func runIndexFunction(funcs []func() int, wg *sync.WaitGroup) {
for _, cFunc := range funcs {
go func(wg *sync.WaitGroup, cf func() int) {
cf()
wg.Done()
}(wg, cFunc)
}
for _, cFunc := range funcs {
go func(wg *sync.WaitGroup, cf func() int) {
cf()
wg.Done()
}(wg, cFunc)
}
}
func GenerateCleanSpecConfigBaseURL(baseURL *url.URL, dir string, includeFile bool) string {
cleanedPath := baseURL.Path // not cleaned yet!
cleanedPath := baseURL.Path // not cleaned yet!
// create a slice of path segments from existing path
pathSegs := strings.Split(cleanedPath, "/")
dirSegs := strings.Split(dir, "/")
// create a slice of path segments from existing path
pathSegs := strings.Split(cleanedPath, "/")
dirSegs := strings.Split(dir, "/")
var cleanedSegs []string
if !includeFile {
dirSegs = dirSegs[:len(dirSegs)-1]
}
var cleanedSegs []string
if !includeFile {
dirSegs = dirSegs[:len(dirSegs)-1]
}
// relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them
// down into services, please don't blast apart specs into a billion shards.
if strings.Contains(dir, "../") {
for s := range dirSegs {
if dirSegs[s] == ".." {
// chop off the last segment of the base path.
if len(pathSegs) > 0 {
pathSegs = pathSegs[:len(pathSegs)-1]
}
} else {
cleanedSegs = append(cleanedSegs, dirSegs[s])
}
}
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/"))
} else {
if !strings.HasPrefix(dir, "http") {
if len(pathSegs) > 1 || len(dirSegs) > 1 {
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/"))
}
} else {
cleanedPath = strings.Join(dirSegs, "/")
}
}
var p string
if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") {
p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath)
} else {
if !strings.Contains(cleanedPath, "/") {
p = ""
} else {
p = cleanedPath
}
// relative paths are a pain in the ass, damn you digital ocean, use a single spec, and break them
// down into services, please don't blast apart specs into a billion shards.
if strings.Contains(dir, "../") {
for s := range dirSegs {
if dirSegs[s] == ".." {
// chop off the last segment of the base path.
if len(pathSegs) > 0 {
pathSegs = pathSegs[:len(pathSegs)-1]
}
} else {
cleanedSegs = append(cleanedSegs, dirSegs[s])
}
}
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(cleanedSegs, "/"))
} else {
if !strings.HasPrefix(dir, "http") {
if len(pathSegs) > 1 || len(dirSegs) > 1 {
cleanedPath = fmt.Sprintf("%s/%s", strings.Join(pathSegs, "/"), strings.Join(dirSegs, "/"))
}
} else {
cleanedPath = strings.Join(dirSegs, "/")
}
}
var p string
if baseURL.Scheme != "" && !strings.HasPrefix(dir, "http") {
p = fmt.Sprintf("%s://%s%s", baseURL.Scheme, baseURL.Host, cleanedPath)
} else {
if !strings.Contains(cleanedPath, "/") {
p = ""
} else {
p = cleanedPath
}
}
if strings.HasSuffix(p, "/") {
p = p[:len(p)-1]
}
return p
}
if strings.HasSuffix(p, "/") {
p = p[:len(p)-1]
}
return p
}

View File

@@ -4,52 +4,52 @@
package index
import (
"github.com/stretchr/testify/assert"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"net/url"
"testing"
)
func TestGenerateCleanSpecConfigBaseURL(t *testing.T) {
u, _ := url.Parse("https://pb33f.io/things/stuff")
path := "."
assert.Equal(t, "https://pb33f.io/things/stuff",
GenerateCleanSpecConfigBaseURL(u, path, false))
u, _ := url.Parse("https://pb33f.io/things/stuff")
path := "."
assert.Equal(t, "https://pb33f.io/things/stuff",
GenerateCleanSpecConfigBaseURL(u, path, false))
}
func TestGenerateCleanSpecConfigBaseURL_RelativeDeep(t *testing.T) {
u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil")
path := "../../../../foo/bar/baz/crap.yaml#thang"
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz",
GenerateCleanSpecConfigBaseURL(u, path, false))
u, _ := url.Parse("https://pb33f.io/things/stuff/jazz/cakes/winter/oil")
path := "../../../../foo/bar/baz/crap.yaml#thang"
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz",
GenerateCleanSpecConfigBaseURL(u, path, false))
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang",
GenerateCleanSpecConfigBaseURL(u, path, true))
assert.Equal(t, "https://pb33f.io/things/stuff/foo/bar/baz/crap.yaml#thang",
GenerateCleanSpecConfigBaseURL(u, path, true))
}
func TestGenerateCleanSpecConfigBaseURL_NoBaseURL(t *testing.T) {
u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil")
path := "../../../../foo/bar/baz/crap.yaml#thang"
assert.Equal(t, "/things/stuff/foo/bar/baz",
GenerateCleanSpecConfigBaseURL(u, path, false))
u, _ := url.Parse("/things/stuff/jazz/cakes/winter/oil")
path := "../../../../foo/bar/baz/crap.yaml#thang"
assert.Equal(t, "/things/stuff/foo/bar/baz",
GenerateCleanSpecConfigBaseURL(u, path, false))
assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang",
GenerateCleanSpecConfigBaseURL(u, path, true))
assert.Equal(t, "/things/stuff/foo/bar/baz/crap.yaml#thang",
GenerateCleanSpecConfigBaseURL(u, path, true))
}
func TestGenerateCleanSpecConfigBaseURL_HttpStrip(t *testing.T) {
u, _ := url.Parse(".")
path := "http://thing.com/crap.yaml#thang"
assert.Equal(t, "http://thing.com",
GenerateCleanSpecConfigBaseURL(u, path, false))
u, _ := url.Parse(".")
path := "http://thing.com/crap.yaml#thang"
assert.Equal(t, "http://thing.com",
GenerateCleanSpecConfigBaseURL(u, path, false))
assert.Equal(t, "",
GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true))
assert.Equal(t, "",
GenerateCleanSpecConfigBaseURL(u, "crap.yaml#thing", true))
}
func TestSpecIndex_extractDefinitionRequiredRefProperties(t *testing.T) {
c := CreateOpenAPIIndexConfig()
idx := NewSpecIndexWithConfig(nil, c)
assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil))
c := CreateOpenAPIIndexConfig()
idx := NewSpecIndexWithConfig(nil, c)
assert.Nil(t, idx.extractDefinitionRequiredRefProperties(nil, nil))
}

View File

@@ -1,170 +1,170 @@
package resolver
import (
"errors"
"fmt"
"io/ioutil"
"net/url"
"testing"
"errors"
"fmt"
"io/ioutil"
"net/url"
"testing"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/index"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
func TestNewResolver(t *testing.T) {
assert.Nil(t, NewResolver(nil))
assert.Nil(t, NewResolver(nil))
}
func Benchmark_ResolveDocumentStripe(b *testing.B) {
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
for n := 0; n < b.N; n++ {
var rootNode yaml.Node
yaml.Unmarshal(stripe, &rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
resolver.Resolve()
}
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
for n := 0; n < b.N; n++ {
var rootNode yaml.Node
yaml.Unmarshal(stripe, &rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
resolver.Resolve()
}
}
func TestResolver_ResolveComponents_CircularSpec(t *testing.T) {
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 3)
circ := resolver.Resolve()
assert.Len(t, circ, 3)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
}
func TestResolver_CheckForCircularReferences(t *testing.T) {
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 3)
assert.Len(t, resolver.GetResolvingErrors(), 3)
assert.Len(t, resolver.GetCircularErrors(), 3)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 3)
assert.Len(t, resolver.GetResolvingErrors(), 3)
assert.Len(t, resolver.GetCircularErrors(), 3)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
}
func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) {
circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{
AllowRemoteLookup: true,
AllowFileLookup: true,
BaseURL: baseURL,
})
index := index.NewSpecIndexWithConfig(&rootNode, &index.SpecIndexConfig{
AllowRemoteLookup: true,
AllowFileLookup: true,
BaseURL: baseURL,
})
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
assert.Len(t, resolver.GetResolvingErrors(), 0)
assert.Len(t, resolver.GetCircularErrors(), 0)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
assert.Len(t, resolver.GetResolvingErrors(), 0)
assert.Len(t, resolver.GetCircularErrors(), 0)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
}
func TestResolver_CircularReferencesRequiredValid(t *testing.T) {
circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
}
func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) {
circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 2)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 2)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
}
func TestResolver_DeepJourney(t *testing.T) {
var journey []*index.Reference
for f := 0; f < 200; f++ {
journey = append(journey, nil)
}
index := index.NewSpecIndex(nil)
resolver := NewResolver(index)
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
var journey []*index.Reference
for f := 0; f < 200; f++ {
journey = append(journey, nil)
}
index := index.NewSpecIndex(nil)
resolver := NewResolver(index)
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
}
func TestResolver_ResolveComponents_Stripe(t *testing.T) {
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
var rootNode yaml.Node
yaml.Unmarshal(stripe, &rootNode)
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
var rootNode yaml.Node
yaml.Unmarshal(stripe, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 3)
circ := resolver.Resolve()
assert.Len(t, circ, 3)
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0)
assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3)
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 0)
}
func TestResolver_ResolveComponents_BurgerShop(t *testing.T) {
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
var rootNode yaml.Node
yaml.Unmarshal(mixedref, &rootNode)
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
var rootNode yaml.Node
yaml.Unmarshal(mixedref, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 0)
circ := resolver.Resolve()
assert.Len(t, circ, 0)
}
func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) {
yml := `paths:
yml := `paths:
/hey:
get:
responses:
@@ -184,20 +184,20 @@ components:
tea:
description: tea`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
}
func TestResolver_ResolveComponents_PolyCircRef(t *testing.T) {
yml := `openapi: 3.1.0
yml := `openapi: 3.1.0
components:
schemas:
cheese:
@@ -211,24 +211,24 @@ components:
tea:
description: tea`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
_ = resolver.CheckForCircularReferences()
resolver.circularReferences[0].IsInfiniteLoop = true // override
assert.Len(t, index.GetCircularReferences(), 1)
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex)
_ = resolver.CheckForCircularReferences()
resolver.circularReferences[0].IsInfiniteLoop = true // override
assert.Len(t, index.GetCircularReferences(), 1)
assert.Len(t, resolver.GetPolymorphicCircularErrors(), 1)
assert.Equal(t, 2, index.GetCircularReferences()[0].LoopIndex)
}
func TestResolver_ResolveComponents_Missing(t *testing.T) {
yml := `paths:
yml := `paths:
/hey:
get:
responses:
@@ -247,95 +247,95 @@ components:
butter:
$ref: 'go home, I am drunk'`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
err := resolver.Resolve()
assert.Len(t, err, 1)
assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error())
err := resolver.Resolve()
assert.Len(t, err, 1)
assert.Equal(t, "cannot resolve reference `go home, I am drunk`, it's missing: $go home, I am drunk [18:11]", err[0].Error())
}
func TestResolver_ResolveComponents_MixedRef(t *testing.T) {
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
var rootNode yaml.Node
yaml.Unmarshal(mixedref, &rootNode)
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")
var rootNode yaml.Node
yaml.Unmarshal(mixedref, &rootNode)
b := index.CreateOpenAPIIndexConfig()
idx := index.NewSpecIndexWithConfig(&rootNode, b)
b := index.CreateOpenAPIIndexConfig()
idx := index.NewSpecIndexWithConfig(&rootNode, b)
resolver := NewResolver(idx)
assert.NotNil(t, resolver)
resolver := NewResolver(idx)
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 0)
assert.Equal(t, 5, resolver.GetIndexesVisited())
circ := resolver.Resolve()
assert.Len(t, circ, 0)
assert.Equal(t, 5, resolver.GetIndexesVisited())
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
assert.Equal(t, 191, resolver.GetRelativesSeen())
assert.Equal(t, 35, resolver.GetJourneysTaken())
assert.Equal(t, 62, resolver.GetReferenceVisited())
// in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times.
assert.Equal(t, 191, resolver.GetRelativesSeen())
assert.Equal(t, 35, resolver.GetJourneysTaken())
assert.Equal(t, 62, resolver.GetReferenceVisited())
}
func TestResolver_ResolveComponents_k8s(t *testing.T) {
k8s, _ := ioutil.ReadFile("../test_specs/k8s.json")
var rootNode yaml.Node
yaml.Unmarshal(k8s, &rootNode)
k8s, _ := ioutil.ReadFile("../test_specs/k8s.json")
var rootNode yaml.Node
yaml.Unmarshal(k8s, &rootNode)
index := index.NewSpecIndex(&rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 0)
circ := resolver.Resolve()
assert.Len(t, circ, 0)
}
// Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors
func ExampleNewResolver() {
// create a yaml.Node reference as a root node.
var rootNode yaml.Node
// create a yaml.Node reference as a root node.
var rootNode yaml.Node
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
// load in the Stripe OpenAPI spec (lots of polymorphic complexity in here)
stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
// unmarshal bytes into our rootNode.
_ = yaml.Unmarshal(stripeBytes, &rootNode)
// unmarshal bytes into our rootNode.
_ = yaml.Unmarshal(stripeBytes, &rootNode)
// create a new spec index (resolver depends on it)
indexConfig := index.CreateClosedAPIIndexConfig()
index := index.NewSpecIndexWithConfig(&rootNode, indexConfig)
// create a new spec index (resolver depends on it)
indexConfig := index.CreateClosedAPIIndexConfig()
index := index.NewSpecIndexWithConfig(&rootNode, indexConfig)
// create a new resolver using the index.
resolver := NewResolver(index)
// create a new resolver using the index.
resolver := NewResolver(index)
// resolve the document, if there are circular reference errors, they are returned/
// WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved
circularErrors := resolver.Resolve()
// resolve the document, if there are circular reference errors, they are returned/
// WARNING: this is a destructive action and the rootNode will be PERMANENTLY altered and cannot be unresolved
circularErrors := resolver.Resolve()
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
// So let's print them out.
//
fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not",
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()))
// Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not
// The Stripe API has a bunch of circular reference problems, mainly from polymorphism.
// So let's print them out.
//
fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not",
len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors()))
// Output: There are 3 circular reference errors, 0 of them are polymorphic errors, 3 are not
}
func ExampleResolvingError() {
re := ResolvingError{
ErrorRef: errors.New("Je suis une erreur"),
Node: &yaml.Node{
Line: 5,
Column: 21,
},
Path: "#/definitions/JeSuisUneErreur",
CircularReference: &index.CircularReferenceResult{},
}
re := ResolvingError{
ErrorRef: errors.New("Je suis une erreur"),
Node: &yaml.Node{
Line: 5,
Column: 21,
},
Path: "#/definitions/JeSuisUneErreur",
CircularReference: &index.CircularReferenceResult{},
}
fmt.Printf("%s", re.Error())
// Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
fmt.Printf("%s", re.Error())
// Output: Je suis une erreur: #/definitions/JeSuisUneErreur [5:21]
}

View File

@@ -6,63 +6,63 @@ package utils
import "gopkg.in/yaml.v3"
func CreateRefNode(ref string) *yaml.Node {
m := CreateEmptyMapNode()
nodes := make([]*yaml.Node, 2)
nodes[0] = CreateStringNode("$ref")
nodes[1] = CreateStringNode(ref)
nodes[1].Style = yaml.SingleQuotedStyle
m.Content = nodes
return m
m := CreateEmptyMapNode()
nodes := make([]*yaml.Node, 2)
nodes[0] = CreateStringNode("$ref")
nodes[1] = CreateStringNode(ref)
nodes[1].Style = yaml.SingleQuotedStyle
m.Content = nodes
return m
}
func CreateEmptyMapNode() *yaml.Node {
n := &yaml.Node{
Kind: yaml.MappingNode,
Tag: "!!map",
}
return n
n := &yaml.Node{
Kind: yaml.MappingNode,
Tag: "!!map",
}
return n
}
func CreateEmptySequenceNode() *yaml.Node {
n := &yaml.Node{
Kind: yaml.SequenceNode,
Tag: "!!seq",
}
return n
n := &yaml.Node{
Kind: yaml.SequenceNode,
Tag: "!!seq",
}
return n
}
func CreateStringNode(str string) *yaml.Node {
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: str,
}
return n
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!str",
Value: str,
}
return n
}
func CreateBoolNode(str string) *yaml.Node {
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!bool",
Value: str,
}
return n
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!bool",
Value: str,
}
return n
}
func CreateIntNode(str string) *yaml.Node {
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!int",
Value: str,
}
return n
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!int",
Value: str,
}
return n
}
func CreateFloatNode(str string) *yaml.Node {
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!float",
Value: str,
}
return n
n := &yaml.Node{
Kind: yaml.ScalarNode,
Tag: "!!float",
Value: str,
}
return n
}

View File

@@ -4,46 +4,46 @@
package utils
import (
"github.com/stretchr/testify/assert"
"testing"
"github.com/stretchr/testify/assert"
"testing"
)
func TestCreateBoolNode(t *testing.T) {
b := CreateBoolNode("true")
assert.Equal(t, "!!bool", b.Tag)
assert.Equal(t, "true", b.Value)
b := CreateBoolNode("true")
assert.Equal(t, "!!bool", b.Tag)
assert.Equal(t, "true", b.Value)
}
func TestCreateEmptyMapNode(t *testing.T) {
m := CreateEmptyMapNode()
assert.Equal(t, "!!map", m.Tag)
assert.Len(t, m.Content, 0)
m := CreateEmptyMapNode()
assert.Equal(t, "!!map", m.Tag)
assert.Len(t, m.Content, 0)
}
func TestCreateEmptySequenceNode(t *testing.T) {
s := CreateEmptySequenceNode()
assert.Equal(t, "!!seq", s.Tag)
assert.Len(t, s.Content, 0)
s := CreateEmptySequenceNode()
assert.Equal(t, "!!seq", s.Tag)
assert.Len(t, s.Content, 0)
}
func TestCreateFloatNode(t *testing.T) {
f := CreateFloatNode("3.14")
assert.Equal(t, "!!float", f.Tag)
assert.Equal(t, "3.14", f.Value)
f := CreateFloatNode("3.14")
assert.Equal(t, "!!float", f.Tag)
assert.Equal(t, "3.14", f.Value)
}
func TestCreateIntNode(t *testing.T) {
i := CreateIntNode("42")
assert.Equal(t, "!!int", i.Tag)
assert.Equal(t, "42", i.Value)
i := CreateIntNode("42")
assert.Equal(t, "!!int", i.Tag)
assert.Equal(t, "42", i.Value)
}
func TestCreateRefNode(t *testing.T) {
r := CreateRefNode("#/components/schemas/MySchema")
assert.Equal(t, "!!map", r.Tag)
assert.Len(t, r.Content, 2)
assert.Equal(t, "!!str", r.Content[0].Tag)
assert.Equal(t, "$ref", r.Content[0].Value)
assert.Equal(t, "!!str", r.Content[1].Tag)
assert.Equal(t, "#/components/schemas/MySchema", r.Content[1].Value)
r := CreateRefNode("#/components/schemas/MySchema")
assert.Equal(t, "!!map", r.Tag)
assert.Len(t, r.Content, 2)
assert.Equal(t, "!!str", r.Content[0].Tag)
assert.Equal(t, "$ref", r.Content[0].Value)
assert.Equal(t, "!!str", r.Content[1].Tag)
assert.Equal(t, "#/components/schemas/MySchema", r.Content[1].Value)
}

Some files were not shown because too many files have changed in this diff Show More