mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
Added support for libopenapi-validator and bumped coverage.
Non breaking changes add support for the new `libopenapi-validator` module. Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
@@ -16,7 +16,7 @@ type DocumentConfiguration struct {
|
||||
BaseURL *url.URL
|
||||
|
||||
// If resolving locally, the BasePath will be the root from which relative references will be resolved from.
|
||||
// It's usually the location of the root specification.
|
||||
// It's usually the location of the root specification.
|
||||
BasePath string // set the Base Path for resolving relative references if the spec is exploded.
|
||||
|
||||
// AllowFileReferences will allow the index to locate relative file references. This is disabled by default.
|
||||
@@ -27,15 +27,15 @@ type DocumentConfiguration struct {
|
||||
}
|
||||
|
||||
func NewOpenDocumentConfiguration() *DocumentConfiguration {
|
||||
return &DocumentConfiguration{
|
||||
AllowFileReferences: true,
|
||||
AllowRemoteReferences: true,
|
||||
}
|
||||
return &DocumentConfiguration{
|
||||
AllowFileReferences: true,
|
||||
AllowRemoteReferences: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewClosedDocumentConfiguration() *DocumentConfiguration {
|
||||
return &DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
}
|
||||
}
|
||||
return &DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,10 @@ import (
|
||||
// The N value is a bit to make it each to know which value (A or B) is used, this prevents having to
|
||||
// if/else on the value to determine which one is set.
|
||||
type DynamicValue[A any, B any] struct {
|
||||
N int // 0 == A, 1 == B
|
||||
A A
|
||||
B B
|
||||
N int // 0 == A, 1 == B
|
||||
A A
|
||||
B B
|
||||
inline bool
|
||||
}
|
||||
|
||||
// IsA will return true if the 'A' or left value is set. (OpenAPI 3)
|
||||
@@ -34,9 +35,16 @@ func (d *DynamicValue[A, B]) IsB() bool {
|
||||
}
|
||||
|
||||
func (d *DynamicValue[A, B]) Render() ([]byte, error) {
|
||||
d.inline = false
|
||||
return yaml.Marshal(d)
|
||||
}
|
||||
|
||||
func (d *DynamicValue[A, B]) RenderInline() ([]byte, error) {
|
||||
d.inline = true
|
||||
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.
|
||||
@@ -53,10 +61,18 @@ func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) {
|
||||
to := reflect.TypeOf(value)
|
||||
switch to.Kind() {
|
||||
case reflect.Ptr:
|
||||
if r, ok := value.(high.Renderable); ok {
|
||||
return r.MarshalYAML()
|
||||
if d.inline {
|
||||
if r, ok := value.(high.RenderableInline); ok {
|
||||
return r.MarshalYAMLInline()
|
||||
} else {
|
||||
_ = n.Encode(value)
|
||||
}
|
||||
} else {
|
||||
_ = n.Encode(value)
|
||||
if r, ok := value.(high.Renderable); ok {
|
||||
return r.MarshalYAML()
|
||||
} else {
|
||||
_ = n.Encode(value)
|
||||
}
|
||||
}
|
||||
case reflect.Bool:
|
||||
_ = n.Encode(value.(bool))
|
||||
@@ -77,42 +93,9 @@ func (d *DynamicValue[A, B]) MarshalYAML() (interface{}, error) {
|
||||
return &n, err
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the DynamicValue object.
|
||||
// MarshalYAMLInline will create a ready to render YAML representation of the DynamicValue object. The
|
||||
// references will be inlined instead of kept as references.
|
||||
func (d *DynamicValue[A, B]) MarshalYAMLInline() (interface{}, error) {
|
||||
// this is a custom renderer, we can't use the NodeBuilder out of the gate.
|
||||
var n yaml.Node
|
||||
var err error
|
||||
var value any
|
||||
|
||||
if d.IsA() {
|
||||
value = d.A
|
||||
}
|
||||
if d.IsB() {
|
||||
value = d.B
|
||||
}
|
||||
to := reflect.TypeOf(value)
|
||||
switch to.Kind() {
|
||||
case reflect.Ptr:
|
||||
if r, ok := value.(high.RenderableInline); ok {
|
||||
return r.MarshalYAMLInline()
|
||||
} else {
|
||||
_ = n.Encode(value)
|
||||
}
|
||||
case reflect.Bool:
|
||||
_ = n.Encode(value.(bool))
|
||||
case reflect.Int:
|
||||
_ = n.Encode(value.(int))
|
||||
case reflect.String:
|
||||
_ = n.Encode(value.(string))
|
||||
case reflect.Int64:
|
||||
_ = n.Encode(value.(int64))
|
||||
case reflect.Float64:
|
||||
_ = n.Encode(value.(float64))
|
||||
case reflect.Float32:
|
||||
_ = n.Encode(value.(float32))
|
||||
case reflect.Int32:
|
||||
_ = n.Encode(value.(int32))
|
||||
|
||||
}
|
||||
return &n, err
|
||||
d.inline = true
|
||||
return d.MarshalYAML()
|
||||
}
|
||||
|
||||
@@ -4,71 +4,172 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"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 TestDynamicValue_Render_A(t *testing.T) {
|
||||
dv := &DynamicValue[string, int]{N: 0, A: "hello"}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "hello", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, int]{N: 0, A: "hello"}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "hello", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_B(t *testing.T) {
|
||||
dv := &DynamicValue[string, int]{N: 1, B: 12345}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "12345", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, int]{N: 1, B: 12345}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "12345", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_Bool(t *testing.T) {
|
||||
dv := &DynamicValue[string, bool]{N: 1, B: true}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "true", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, bool]{N: 1, B: true}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "true", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_Int64(t *testing.T) {
|
||||
dv := &DynamicValue[string, int64]{N: 1, B: 12345567810}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "12345567810", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, int64]{N: 1, B: 12345567810}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "12345567810", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_Int32(t *testing.T) {
|
||||
dv := &DynamicValue[string, int32]{N: 1, B: 1234567891}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "1234567891", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, int32]{N: 1, B: 1234567891}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "1234567891", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_Float32(t *testing.T) {
|
||||
dv := &DynamicValue[string, float32]{N: 1, B: 23456.123}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "23456.123", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, float32]{N: 1, B: 23456.123}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "23456.123", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_Float64(t *testing.T) {
|
||||
dv := &DynamicValue[string, float64]{N: 1, B: 23456.1233456778}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "23456.1233456778", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, float64]{N: 1, B: 23456.1233456778}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "23456.1233456778", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_Ptr(t *testing.T) {
|
||||
|
||||
type cake struct {
|
||||
Cake string
|
||||
}
|
||||
type cake struct {
|
||||
Cake string
|
||||
}
|
||||
|
||||
dv := &DynamicValue[string, *cake]{N: 1, B: &cake{Cake: "vanilla"}}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "cake: vanilla", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, *cake]{N: 1, B: &cake{Cake: "vanilla"}}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "cake: vanilla", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_Render_PtrRenderable(t *testing.T) {
|
||||
|
||||
tag := &Tag{
|
||||
Name: "cake",
|
||||
}
|
||||
tag := &Tag{
|
||||
Name: "cake",
|
||||
}
|
||||
|
||||
dv := &DynamicValue[string, *Tag]{N: 1, B: tag}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "name: cake", strings.TrimSpace(string(dvb)))
|
||||
dv := &DynamicValue[string, *Tag]{N: 1, B: tag}
|
||||
dvb, _ := dv.Render()
|
||||
assert.Equal(t, "name: cake", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_RenderInline(t *testing.T) {
|
||||
|
||||
tag := &Tag{
|
||||
Name: "cake",
|
||||
}
|
||||
|
||||
dv := &DynamicValue[string, *Tag]{N: 1, B: tag}
|
||||
dvb, _ := dv.RenderInline()
|
||||
assert.Equal(t, "name: cake", strings.TrimSpace(string(dvb)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_MarshalYAMLInline(t *testing.T) {
|
||||
|
||||
const ymlComponents = `components:
|
||||
schemas:
|
||||
rice:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ice'
|
||||
nice:
|
||||
properties:
|
||||
rice:
|
||||
$ref: '#/components/schemas/rice'
|
||||
ice:
|
||||
type: string`
|
||||
|
||||
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)
|
||||
|
||||
lowProxy := new(lowbase.SchemaProxy)
|
||||
err := lowProxy.Build(node.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||
Value: lowProxy,
|
||||
}
|
||||
|
||||
sp := NewSchemaProxy(&lowRef)
|
||||
|
||||
rend, _ := sp.MarshalYAMLInline()
|
||||
|
||||
// convert node into yaml
|
||||
bits, _ := yaml.Marshal(rend)
|
||||
assert.Equal(t, "properties:\n rice:\n type: array\n items:\n type: string", strings.TrimSpace(string(bits)))
|
||||
}
|
||||
|
||||
func TestDynamicValue_MarshalYAMLInline_Error(t *testing.T) {
|
||||
|
||||
const ymlComponents = `components:
|
||||
schemas:
|
||||
rice:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/bork'
|
||||
nice:
|
||||
properties:
|
||||
rice:
|
||||
$ref: '#/components/schemas/berk'
|
||||
ice:
|
||||
type: string`
|
||||
|
||||
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)
|
||||
|
||||
lowProxy := new(lowbase.SchemaProxy)
|
||||
err := lowProxy.Build(node.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||
Value: lowProxy,
|
||||
}
|
||||
|
||||
sp := NewSchemaProxy(&lowRef)
|
||||
|
||||
rend, er := sp.MarshalYAMLInline()
|
||||
assert.Nil(t, rend)
|
||||
assert.Error(t, er)
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ package base
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"sync"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
@@ -70,7 +68,10 @@ type Schema struct {
|
||||
// in 3.1 Items can be a Schema or a boolean
|
||||
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
||||
|
||||
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
|
||||
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
|
||||
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,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"`
|
||||
@@ -97,42 +98,10 @@ type Schema struct {
|
||||
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
|
||||
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
|
||||
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *base.Schema
|
||||
|
||||
// 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 *int64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
|
||||
Maximum *int64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
|
||||
Minimum *int64 `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 *int64 `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:"-"`
|
||||
}
|
||||
@@ -311,9 +280,11 @@ func NewSchema(schema *base.Schema) *Schema {
|
||||
s.Required = req
|
||||
|
||||
var enum []any
|
||||
if !schema.Anchor.IsEmpty() {
|
||||
s.Anchor = schema.Anchor.Value
|
||||
}
|
||||
if !schema.Anchor.IsEmpty() {
|
||||
s.Anchor = schema.Anchor.Value
|
||||
}
|
||||
|
||||
// TODO: check this behavior.
|
||||
for i := range schema.Enum.Value {
|
||||
enum = append(enum, fmt.Sprint(schema.Enum.Value[i].Value))
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An
|
||||
@@ -36,8 +36,8 @@ import (
|
||||
// and slow things down when building. By preventing recursion through every polymorphic item, building models is kept
|
||||
// fast and snappy, which is desired for realtime processing of specs.
|
||||
//
|
||||
// - Q: Yeah, but, why not just use state to avoiding re-visiting seen polymorphic nodes?
|
||||
// - A: It's slow, takes up memory and still has runaway potential in very, very long chains.
|
||||
// - Q: Yeah, but, why not just use state to avoiding re-visiting seen polymorphic nodes?
|
||||
// - A: It's slow, takes up memory and still has runaway potential in very, very long chains.
|
||||
//
|
||||
// 3. Short Circuit Errors.
|
||||
//
|
||||
@@ -45,133 +45,133 @@ import (
|
||||
// it's not actually JSONSchema until 3.1, so lots of times a bad schema will break parsing. Errors are only found
|
||||
// when a schema is needed, so the rest of the document is parsed and ready to use.
|
||||
type SchemaProxy struct {
|
||||
schema *low.NodeReference[*base.SchemaProxy]
|
||||
buildError error
|
||||
rendered *Schema
|
||||
refStr string
|
||||
schema *low.NodeReference[*base.SchemaProxy]
|
||||
buildError error
|
||||
rendered *Schema
|
||||
refStr string
|
||||
}
|
||||
|
||||
// NewSchemaProxy creates a new high-level SchemaProxy from a low-level one.
|
||||
func NewSchemaProxy(schema *low.NodeReference[*base.SchemaProxy]) *SchemaProxy {
|
||||
return &SchemaProxy{schema: schema}
|
||||
return &SchemaProxy{schema: schema}
|
||||
}
|
||||
|
||||
// CreateSchemaProxy will create a new high-level SchemaProxy from a high-level Schema, this acts the same
|
||||
// as if the SchemaProxy is pre-rendered.
|
||||
func CreateSchemaProxy(schema *Schema) *SchemaProxy {
|
||||
return &SchemaProxy{rendered: schema}
|
||||
return &SchemaProxy{rendered: schema}
|
||||
}
|
||||
|
||||
// CreateSchemaProxyRef will create a new high-level SchemaProxy from a reference string, this is used only when
|
||||
// building out new models from scratch that require a reference rather than a schema implementation.
|
||||
func CreateSchemaProxyRef(ref string) *SchemaProxy {
|
||||
return &SchemaProxy{refStr: ref}
|
||||
return &SchemaProxy{refStr: ref}
|
||||
}
|
||||
|
||||
// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one.
|
||||
// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access
|
||||
// to that building error.
|
||||
func (sp *SchemaProxy) Schema() *Schema {
|
||||
if sp.rendered == nil {
|
||||
s := sp.schema.Value.Schema()
|
||||
if s == nil {
|
||||
sp.buildError = sp.schema.Value.GetBuildError()
|
||||
return nil
|
||||
}
|
||||
sch := NewSchema(s)
|
||||
sch.ParentProxy = sp
|
||||
sp.rendered = sch
|
||||
return sch
|
||||
} else {
|
||||
return sp.rendered
|
||||
}
|
||||
if sp.rendered == nil {
|
||||
s := sp.schema.Value.Schema()
|
||||
if s == nil {
|
||||
sp.buildError = sp.schema.Value.GetBuildError()
|
||||
return nil
|
||||
}
|
||||
sch := NewSchema(s)
|
||||
sch.ParentProxy = sp
|
||||
sp.rendered = sch
|
||||
return sch
|
||||
} else {
|
||||
return sp.rendered
|
||||
}
|
||||
}
|
||||
|
||||
// IsReference returns true if the SchemaProxy is a reference to another Schema.
|
||||
func (sp *SchemaProxy) IsReference() bool {
|
||||
if sp.refStr != "" {
|
||||
return true
|
||||
}
|
||||
if sp.schema != nil {
|
||||
return sp.schema.Value.IsSchemaReference()
|
||||
}
|
||||
return false
|
||||
if sp.refStr != "" {
|
||||
return true
|
||||
}
|
||||
if sp.schema != nil {
|
||||
return sp.schema.Value.IsSchemaReference()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetReference returns the location of the $ref if this SchemaProxy is a reference to another Schema.
|
||||
func (sp *SchemaProxy) GetReference() string {
|
||||
if sp.refStr != "" {
|
||||
return sp.refStr
|
||||
}
|
||||
return sp.schema.Value.GetSchemaReference()
|
||||
if sp.refStr != "" {
|
||||
return sp.refStr
|
||||
}
|
||||
return sp.schema.Value.GetSchemaReference()
|
||||
}
|
||||
|
||||
// BuildSchema operates the same way as Schema, except it will return any error along with the *Schema
|
||||
func (sp *SchemaProxy) BuildSchema() (*Schema, error) {
|
||||
if sp.rendered != nil {
|
||||
return sp.rendered, sp.buildError
|
||||
}
|
||||
schema := sp.Schema()
|
||||
er := sp.buildError
|
||||
return schema, er
|
||||
if sp.rendered != nil {
|
||||
return sp.rendered, sp.buildError
|
||||
}
|
||||
schema := sp.Schema()
|
||||
er := sp.buildError
|
||||
return schema, er
|
||||
}
|
||||
|
||||
// GetBuildError returns any error that was thrown when calling Schema()
|
||||
func (sp *SchemaProxy) GetBuildError() error {
|
||||
return sp.buildError
|
||||
return sp.buildError
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) GoLow() *base.SchemaProxy {
|
||||
if sp.schema == nil {
|
||||
return nil
|
||||
}
|
||||
return sp.schema.Value
|
||||
if sp.schema == nil {
|
||||
return nil
|
||||
}
|
||||
return sp.schema.Value
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) GoLowUntyped() any {
|
||||
if sp.schema == nil {
|
||||
return nil
|
||||
}
|
||||
return sp.schema.Value
|
||||
if sp.schema == nil {
|
||||
return nil
|
||||
}
|
||||
return sp.schema.Value
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Schema object as a byte slice.
|
||||
func (sp *SchemaProxy) Render() ([]byte, error) {
|
||||
return yaml.Marshal(sp)
|
||||
return yaml.Marshal(sp)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
|
||||
func (sp *SchemaProxy) MarshalYAML() (interface{}, error) {
|
||||
var s *Schema
|
||||
var err error
|
||||
// if this schema isn't a reference, then build it out.
|
||||
if !sp.IsReference() {
|
||||
s, err = sp.BuildSchema()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
} else {
|
||||
// do not build out a reference, just marshal the reference.
|
||||
mp := utils.CreateEmptyMapNode()
|
||||
mp.Content = append(mp.Content,
|
||||
utils.CreateStringNode("$ref"),
|
||||
utils.CreateStringNode(sp.GetReference()))
|
||||
return mp, nil
|
||||
}
|
||||
var s *Schema
|
||||
var err error
|
||||
// if this schema isn't a reference, then build it out.
|
||||
if !sp.IsReference() {
|
||||
s, err = sp.BuildSchema()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
} else {
|
||||
// do not build out a reference, just marshal the reference.
|
||||
mp := utils.CreateEmptyMapNode()
|
||||
mp.Content = append(mp.Content,
|
||||
utils.CreateStringNode("$ref"),
|
||||
utils.CreateStringNode(sp.GetReference()))
|
||||
return mp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalYAMLInline will create a ready to render YAML representation of the ExternalDoc object. All of the
|
||||
// MarshalYAMLInline will create a ready to render YAML representation of the ExternalDoc object. The
|
||||
// $ref values will be inlined instead of kept as is.
|
||||
func (sp *SchemaProxy) MarshalYAMLInline() (interface{}, error) {
|
||||
var s *Schema
|
||||
var err error
|
||||
s, err = sp.BuildSchema()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
nb.Resolve = true
|
||||
return nb.Render(), nil
|
||||
var s *Schema
|
||||
var err error
|
||||
s, err = sp.BuildSchema()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
nb.Resolve = true
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,59 +4,59 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// Tag represents a high-level Tag instance that is backed by a low-level one.
|
||||
//
|
||||
// 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 string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
Extensions map[string]any
|
||||
low *low.Tag
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||
Extensions map[string]any
|
||||
low *low.Tag
|
||||
}
|
||||
|
||||
// NewTag creates a new high-level Tag instance that is backed by a low-level one.
|
||||
func NewTag(tag *low.Tag) *Tag {
|
||||
t := new(Tag)
|
||||
t.low = tag
|
||||
if !tag.Name.IsEmpty() {
|
||||
t.Name = tag.Name.Value
|
||||
}
|
||||
if !tag.Description.IsEmpty() {
|
||||
t.Description = tag.Description.Value
|
||||
}
|
||||
if !tag.ExternalDocs.IsEmpty() {
|
||||
t.ExternalDocs = NewExternalDoc(tag.ExternalDocs.Value)
|
||||
}
|
||||
t.Extensions = high.ExtractExtensions(tag.Extensions)
|
||||
return t
|
||||
t := new(Tag)
|
||||
t.low = tag
|
||||
if !tag.Name.IsEmpty() {
|
||||
t.Name = tag.Name.Value
|
||||
}
|
||||
if !tag.Description.IsEmpty() {
|
||||
t.Description = tag.Description.Value
|
||||
}
|
||||
if !tag.ExternalDocs.IsEmpty() {
|
||||
t.ExternalDocs = NewExternalDoc(tag.ExternalDocs.Value)
|
||||
}
|
||||
t.Extensions = high.ExtractExtensions(tag.Extensions)
|
||||
return t
|
||||
}
|
||||
|
||||
// GoLow returns the low-level Tag instance used to create the high-level one.
|
||||
func (t *Tag) GoLow() *low.Tag {
|
||||
return t.low
|
||||
return t.low
|
||||
}
|
||||
|
||||
// GoLowUntyped will return the low-level Tag instance that was used to create the high-level one, with no type
|
||||
func (t *Tag) GoLowUntyped() any {
|
||||
return t.low
|
||||
return t.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Info object as a byte slice.
|
||||
func (t *Tag) Render() ([]byte, error) {
|
||||
return yaml.Marshal(t)
|
||||
return yaml.Marshal(t)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Info object.
|
||||
func (t *Tag) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(t, t.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
nb := high.NewNodeBuilder(t, t.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
@@ -20,7 +20,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 string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
|
||||
OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
|
||||
@@ -71,4 +71,4 @@ func (l *Link) Render() ([]byte, error) {
|
||||
func (l *Link) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(l, l.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,114 +4,114 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// 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 string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
|
||||
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
||||
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
|
||||
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
|
||||
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *low.Parameter
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
Required bool `json:"required,omitempty" yaml:"required,omitempty"`
|
||||
Deprecated bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||
AllowEmptyValue bool `json:"allowEmptyValue,omitempty" yaml:"allowEmptyValue,omitempty"`
|
||||
Style string `json:"style,omitempty" yaml:"style,omitempty"`
|
||||
Explode *bool `json:"explode,omitempty" yaml:"explode,omitempty"`
|
||||
AllowReserved bool `json:"allowReserved,omitempty" yaml:"allowReserved,omitempty"`
|
||||
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||
Content map[string]*MediaType `json:"content,omitempty" yaml:"content,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *low.Parameter
|
||||
}
|
||||
|
||||
// NewParameter will create a new high-level instance of a Parameter, using a low-level one.
|
||||
func NewParameter(param *low.Parameter) *Parameter {
|
||||
p := new(Parameter)
|
||||
p.low = param
|
||||
p.Name = param.Name.Value
|
||||
p.In = param.In.Value
|
||||
p.Description = param.Description.Value
|
||||
p.Deprecated = param.Deprecated.Value
|
||||
p.AllowEmptyValue = param.AllowEmptyValue.Value
|
||||
p.Style = param.Style.Value
|
||||
if !param.Explode.IsEmpty() {
|
||||
p.Explode = ¶m.Explode.Value
|
||||
}
|
||||
p.AllowReserved = param.AllowReserved.Value
|
||||
if !param.Schema.IsEmpty() {
|
||||
p.Schema = base.NewSchemaProxy(¶m.Schema)
|
||||
}
|
||||
p.Required = param.Required.Value
|
||||
p.Example = param.Example.Value
|
||||
p.Examples = base.ExtractExamples(param.Examples.Value)
|
||||
p.Content = ExtractContent(param.Content.Value)
|
||||
p.Extensions = high.ExtractExtensions(param.Extensions)
|
||||
return p
|
||||
p := new(Parameter)
|
||||
p.low = param
|
||||
p.Name = param.Name.Value
|
||||
p.In = param.In.Value
|
||||
p.Description = param.Description.Value
|
||||
p.Deprecated = param.Deprecated.Value
|
||||
p.AllowEmptyValue = param.AllowEmptyValue.Value
|
||||
p.Style = param.Style.Value
|
||||
if !param.Explode.IsEmpty() {
|
||||
p.Explode = ¶m.Explode.Value
|
||||
}
|
||||
p.AllowReserved = param.AllowReserved.Value
|
||||
if !param.Schema.IsEmpty() {
|
||||
p.Schema = base.NewSchemaProxy(¶m.Schema)
|
||||
}
|
||||
p.Required = param.Required.Value
|
||||
p.Example = param.Example.Value
|
||||
p.Examples = base.ExtractExamples(param.Examples.Value)
|
||||
p.Content = ExtractContent(param.Content.Value)
|
||||
p.Extensions = high.ExtractExtensions(param.Extensions)
|
||||
return p
|
||||
}
|
||||
|
||||
// GoLow returns the low-level Parameter used to create the high-level one.
|
||||
func (p *Parameter) GoLow() *low.Parameter {
|
||||
return p.low
|
||||
return p.low
|
||||
}
|
||||
|
||||
// GoLowUntyped will return the low-level Discriminator instance that was used to create the high-level one, with no type
|
||||
func (p *Parameter) GoLowUntyped() any {
|
||||
return p.low
|
||||
return p.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Encoding object as a byte slice.
|
||||
func (p *Parameter) Render() ([]byte, error) {
|
||||
return yaml.Marshal(p)
|
||||
return yaml.Marshal(p)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Encoding object.
|
||||
func (p *Parameter) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(p, p.low)
|
||||
return nb.Render(), nil
|
||||
nb := high.NewNodeBuilder(p, p.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
|
||||
// IsExploded will return true if the parameter is exploded, false otherwise.
|
||||
func (p *Parameter) IsExploded() bool {
|
||||
if p.Explode == nil {
|
||||
return false
|
||||
}
|
||||
return *p.Explode
|
||||
if p.Explode == nil {
|
||||
return false
|
||||
}
|
||||
return *p.Explode
|
||||
}
|
||||
|
||||
// IsDefaultFormEncoding will return true if the paramter has no exploded value, or has exploded set to true, and no style
|
||||
// IsDefaultFormEncoding will return true if the parameter has no exploded value, or has exploded set to true, and no style
|
||||
// or a style set to form. This combination is the default encoding/serialization style for parameters for OpenAPI 3+
|
||||
func (p *Parameter) IsDefaultFormEncoding() bool {
|
||||
if p.Explode == nil && (p.Style == "" || p.Style == "form") {
|
||||
return true
|
||||
}
|
||||
if p.Explode != nil && *p.Explode && (p.Style == "" || p.Style == "form") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
if p.Explode == nil && (p.Style == "" || p.Style == "form") {
|
||||
return true
|
||||
}
|
||||
if p.Explode != nil && *p.Explode && (p.Style == "" || p.Style == "form") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDefaultHeaderEncoding will return true if the paramter has no exploded value, or has exploded set to false, and no style
|
||||
// IsDefaultHeaderEncoding will return true if the parameter has no exploded value, or has exploded set to false, and no style
|
||||
// or a style set to simple. This combination is the default encoding/serialization style for header parameters for OpenAPI 3+
|
||||
func (p *Parameter) IsDefaultHeaderEncoding() bool {
|
||||
if p.Explode == nil && (p.Style == "" || p.Style == "simple") {
|
||||
return true
|
||||
}
|
||||
if p.Explode != nil && !*p.Explode && (p.Style == "" || p.Style == "simple") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
if p.Explode == nil && (p.Style == "" || p.Style == "simple") {
|
||||
return true
|
||||
}
|
||||
if p.Explode != nil && !*p.Explode && (p.Style == "" || p.Style == "simple") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsDefaultPathEncoding will return true if the paramter has no exploded value, or has exploded set to false, and no style
|
||||
// IsDefaultPathEncoding will return true if the parameter has no exploded value, or has exploded set to false, and no style
|
||||
// or a style set to simple. This combination is the default encoding/serialization style for path parameters for OpenAPI 3+
|
||||
func (p *Parameter) IsDefaultPathEncoding() bool {
|
||||
return p.IsDefaultHeaderEncoding() // header default encoding and path default encoding are the same
|
||||
return p.IsDefaultHeaderEncoding() // header default encoding and path default encoding are the same
|
||||
}
|
||||
|
||||
@@ -43,3 +43,88 @@ x-burgers: why not?`
|
||||
|
||||
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||
}
|
||||
|
||||
func TestParameter_IsExploded(t *testing.T) {
|
||||
|
||||
explode := true
|
||||
param := Parameter{
|
||||
Explode: &explode,
|
||||
}
|
||||
|
||||
assert.True(t, param.IsExploded())
|
||||
|
||||
explode = false
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
}
|
||||
|
||||
assert.False(t, param.IsExploded())
|
||||
|
||||
param = Parameter{}
|
||||
|
||||
assert.False(t, param.IsExploded())
|
||||
}
|
||||
|
||||
func TestParameter_IsDefaultFormEncoding(t *testing.T) {
|
||||
|
||||
param := Parameter{}
|
||||
assert.True(t, param.IsDefaultFormEncoding())
|
||||
|
||||
param = Parameter{Style: "form"}
|
||||
assert.True(t, param.IsDefaultFormEncoding())
|
||||
|
||||
explode := false
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
}
|
||||
assert.False(t, param.IsDefaultFormEncoding())
|
||||
|
||||
explode = true
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
}
|
||||
assert.True(t, param.IsDefaultFormEncoding())
|
||||
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
Style: "simple",
|
||||
}
|
||||
assert.False(t, param.IsDefaultFormEncoding())
|
||||
}
|
||||
|
||||
func TestParameter_IsDefaultHeaderEncoding(t *testing.T) {
|
||||
|
||||
param := Parameter{}
|
||||
assert.True(t, param.IsDefaultHeaderEncoding())
|
||||
|
||||
param = Parameter{Style: "simple"}
|
||||
assert.True(t, param.IsDefaultHeaderEncoding())
|
||||
|
||||
explode := false
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
Style: "simple",
|
||||
}
|
||||
assert.True(t, param.IsDefaultHeaderEncoding())
|
||||
|
||||
explode = true
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
Style: "simple",
|
||||
}
|
||||
assert.False(t, param.IsDefaultHeaderEncoding())
|
||||
|
||||
explode = false
|
||||
param = Parameter{
|
||||
Explode: &explode,
|
||||
Style: "form",
|
||||
}
|
||||
assert.False(t, param.IsDefaultHeaderEncoding())
|
||||
}
|
||||
|
||||
func TestParameter_IsDefaultPathEncoding(t *testing.T) {
|
||||
|
||||
param := Parameter{}
|
||||
assert.True(t, param.IsDefaultPathEncoding())
|
||||
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sort"
|
||||
"fmt"
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Responses represents a high-level OpenAPI 3+ Responses object that is backed by a low-level one.
|
||||
@@ -25,130 +25,125 @@ import (
|
||||
//
|
||||
// The Responses Object MUST contain at least one response code, and if only one response code is provided it SHOULD
|
||||
// be the response for a successful operation call.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#responses-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#responses-object
|
||||
type Responses struct {
|
||||
Codes map[string]*Response `json:"-" yaml:"-"`
|
||||
Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *low.Responses
|
||||
Codes map[string]*Response `json:"-" yaml:"-"`
|
||||
Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
|
||||
Extensions map[string]any `json:"-" yaml:"-"`
|
||||
low *low.Responses
|
||||
}
|
||||
|
||||
// NewResponses will create a new high-level Responses instance from a low-level one. It operates asynchronously
|
||||
// internally, as each response may be considerable in complexity.
|
||||
func NewResponses(responses *low.Responses) *Responses {
|
||||
r := new(Responses)
|
||||
r.low = responses
|
||||
r.Extensions = high.ExtractExtensions(responses.Extensions)
|
||||
if !responses.Default.IsEmpty() {
|
||||
r.Default = NewResponse(responses.Default.Value)
|
||||
}
|
||||
codes := make(map[string]*Response)
|
||||
r := new(Responses)
|
||||
r.low = responses
|
||||
r.Extensions = high.ExtractExtensions(responses.Extensions)
|
||||
if !responses.Default.IsEmpty() {
|
||||
r.Default = NewResponse(responses.Default.Value)
|
||||
}
|
||||
codes := make(map[string]*Response)
|
||||
|
||||
// struct to hold response and code sent over chan.
|
||||
type respRes struct {
|
||||
code string
|
||||
resp *Response
|
||||
}
|
||||
// struct to hold response and code sent over chan.
|
||||
type respRes struct {
|
||||
code string
|
||||
resp *Response
|
||||
}
|
||||
|
||||
// build each response async for speed
|
||||
rChan := make(chan respRes)
|
||||
var buildResponse = func(code string, resp *low.Response, c chan respRes) {
|
||||
c <- respRes{code: code, resp: NewResponse(resp)}
|
||||
}
|
||||
for k, v := range responses.Codes {
|
||||
go buildResponse(k.Value, v.Value, rChan)
|
||||
}
|
||||
totalCodes := len(responses.Codes)
|
||||
codesParsed := 0
|
||||
for codesParsed < totalCodes {
|
||||
select {
|
||||
case re := <-rChan:
|
||||
codesParsed++
|
||||
codes[re.code] = re.resp
|
||||
}
|
||||
}
|
||||
r.Codes = codes
|
||||
return r
|
||||
// build each response async for speed
|
||||
rChan := make(chan respRes)
|
||||
var buildResponse = func(code string, resp *low.Response, c chan respRes) {
|
||||
c <- respRes{code: code, resp: NewResponse(resp)}
|
||||
}
|
||||
for k, v := range responses.Codes {
|
||||
go buildResponse(k.Value, v.Value, rChan)
|
||||
}
|
||||
totalCodes := len(responses.Codes)
|
||||
codesParsed := 0
|
||||
for codesParsed < totalCodes {
|
||||
select {
|
||||
case re := <-rChan:
|
||||
codesParsed++
|
||||
codes[re.code] = re.resp
|
||||
}
|
||||
}
|
||||
r.Codes = codes
|
||||
return r
|
||||
}
|
||||
|
||||
// FindResponseByCode is a shortcut for looking up code by an integer vs. a string
|
||||
func (r *Responses) FindResponseByCode(code int) *Response {
|
||||
return r.Codes[fmt.Sprintf("%d", code)]
|
||||
}
|
||||
|
||||
// GetDefaultResponse will
|
||||
func (r *Responses) GetDefaultResponse(code int) *Response {
|
||||
return r.Codes[fmt.Sprintf("%d", code)]
|
||||
return r.Codes[fmt.Sprintf("%d", code)]
|
||||
}
|
||||
|
||||
// GoLow returns the low-level Response object used to create the high-level one.
|
||||
func (r *Responses) GoLow() *low.Responses {
|
||||
return r.low
|
||||
return r.low
|
||||
}
|
||||
|
||||
// GoLowUntyped will return the low-level Responses instance that was used to create the high-level one, with no type
|
||||
func (r *Responses) GoLowUntyped() any {
|
||||
return r.low
|
||||
return r.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Responses object as a byte slice.
|
||||
func (r *Responses) Render() ([]byte, error) {
|
||||
return yaml.Marshal(r)
|
||||
return yaml.Marshal(r)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Responses object.
|
||||
func (r *Responses) MarshalYAML() (interface{}, error) {
|
||||
// map keys correctly.
|
||||
m := utils.CreateEmptyMapNode()
|
||||
type responseItem struct {
|
||||
resp *Response
|
||||
code string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
}
|
||||
var mapped []*responseItem
|
||||
// map keys correctly.
|
||||
m := utils.CreateEmptyMapNode()
|
||||
type responseItem struct {
|
||||
resp *Response
|
||||
code string
|
||||
line int
|
||||
ext *yaml.Node
|
||||
}
|
||||
var mapped []*responseItem
|
||||
|
||||
for k, re := range r.Codes {
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
if r.low != nil {
|
||||
for lKey := range r.low.Codes {
|
||||
if lKey.Value == k {
|
||||
ln = lKey.KeyNode.Line
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &responseItem{re, k, ln, nil})
|
||||
}
|
||||
for k, re := range r.Codes {
|
||||
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||
if r.low != nil {
|
||||
for lKey := range r.low.Codes {
|
||||
if lKey.Value == k {
|
||||
ln = lKey.KeyNode.Line
|
||||
}
|
||||
}
|
||||
}
|
||||
mapped = append(mapped, &responseItem{re, k, ln, nil})
|
||||
}
|
||||
|
||||
// extract extensions
|
||||
nb := high.NewNodeBuilder(r, r.low)
|
||||
extNode := nb.Render()
|
||||
if extNode != nil && extNode.Content != nil {
|
||||
var label string
|
||||
for u := range extNode.Content {
|
||||
if u%2 == 0 {
|
||||
label = extNode.Content[u].Value
|
||||
continue
|
||||
}
|
||||
mapped = append(mapped, &responseItem{nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u]})
|
||||
}
|
||||
}
|
||||
// extract extensions
|
||||
nb := high.NewNodeBuilder(r, r.low)
|
||||
extNode := nb.Render()
|
||||
if extNode != nil && extNode.Content != nil {
|
||||
var label string
|
||||
for u := range extNode.Content {
|
||||
if u%2 == 0 {
|
||||
label = extNode.Content[u].Value
|
||||
continue
|
||||
}
|
||||
mapped = append(mapped, &responseItem{nil, label,
|
||||
extNode.Content[u].Line, extNode.Content[u]})
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].resp != nil {
|
||||
rendered, _ := mapped[j].resp.MarshalYAML()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mapped[j].ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
m.Content = append(m.Content, mapped[j].ext)
|
||||
}
|
||||
sort.Slice(mapped, func(i, j int) bool {
|
||||
return mapped[i].line < mapped[j].line
|
||||
})
|
||||
for j := range mapped {
|
||||
if mapped[j].resp != nil {
|
||||
rendered, _ := mapped[j].resp.MarshalYAML()
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||
}
|
||||
if mapped[j].ext != nil {
|
||||
m.Content = append(m.Content, utils.CreateStringNode(mapped[j].code))
|
||||
m.Content = append(m.Content, mapped[j].ext)
|
||||
}
|
||||
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
// Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one.
|
||||
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
||||
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
||||
type Server struct {
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
@@ -53,4 +53,4 @@ func (s *Server) Render() ([]byte, error) {
|
||||
func (s *Server) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,4 +55,4 @@ func (s *ServerVariable) Render() ([]byte, error) {
|
||||
func (s *ServerVariable) MarshalYAML() (interface{}, error) {
|
||||
nb := high.NewNodeBuilder(s, s.low)
|
||||
return nb.Render(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ func TestLicense_Build(t *testing.T) {
|
||||
|
||||
func TestInfo_Hash(t *testing.T) {
|
||||
left := `title: princess b33f
|
||||
summary: a thing
|
||||
description: a thing
|
||||
termsOfService: https://pb33f.io
|
||||
x-princess: b33f
|
||||
@@ -85,6 +86,7 @@ version: 1.2.3
|
||||
x-b33f: princess`
|
||||
|
||||
right := `title: princess b33f
|
||||
summary: a thing
|
||||
description: a thing
|
||||
termsOfService: https://pb33f.io
|
||||
x-princess: b33f
|
||||
|
||||
@@ -33,4 +33,4 @@ func CompareOpenAPIDocuments(original, updated *v3.Document) *model.DocumentChan
|
||||
// or removed and which of those changes were breaking.
|
||||
func CompareSwaggerDocuments(original, updated *v2.Swagger) *model.DocumentChanges {
|
||||
return model.CompareDocuments(original, updated)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user