mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
Tackled the boss, schemas! now mutable.
A tricky one, because of the complexity, still working through edge cases and variations, but so far, operating correctly!
This commit is contained in:
69
datamodel/high/base/dynamic_value.go
Normal file
69
datamodel/high/base/dynamic_value.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DynamicValue is used to hold multiple possible values for a schema property. There are two values, a left
|
||||||
|
// value (A) and a right value (B). The left value (A) is a 3.0 schema property value, the right value (B) is a 3.1
|
||||||
|
// schema value.
|
||||||
|
//
|
||||||
|
// OpenAPI 3.1 treats a Schema as a real JSON schema, which means some properties become incompatible, or others
|
||||||
|
// now support more than one primitive type or structure.
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsA will return true if the 'A' or left value is set. (OpenAPI 3)
|
||||||
|
func (d *DynamicValue[A, B]) IsA() bool {
|
||||||
|
return d.N == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsB will return true if the 'B' or right value is set (OpenAPI 3.1)
|
||||||
|
func (d *DynamicValue[A, B]) IsB() bool {
|
||||||
|
return d.N == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DynamicValue[A, B]) Render() ([]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) {
|
||||||
|
if d == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// 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.Renderable); ok {
|
||||||
|
return r.MarshalYAML()
|
||||||
|
}
|
||||||
|
case reflect.Bool:
|
||||||
|
_ = n.Encode(value.(bool))
|
||||||
|
case reflect.Int64:
|
||||||
|
_ = n.Encode(value.(int64))
|
||||||
|
}
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ package base
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
@@ -12,29 +13,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DynamicValue is used to hold multiple possible values for a schema property. There are two values, a left
|
|
||||||
// value (A) and a right value (B). The left value (A) is a 3.0 schema property value, the right value (B) is a 3.1
|
|
||||||
// schema value.
|
|
||||||
//
|
|
||||||
// OpenAPI 3.1 treats a Schema as a real JSON schema, which means some properties become incompatible, or others
|
|
||||||
// now support more than one primitive type or structure.
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsA will return true if the 'A' or left value is set. (OpenAPI 3)
|
|
||||||
func (s *DynamicValue[A, B]) IsA() bool {
|
|
||||||
return s.N == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsB will return true if the 'B' or right value is set (OpenAPI 3.1)
|
|
||||||
func (s *DynamicValue[A, B]) IsB() bool {
|
|
||||||
return s.N == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1
|
// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1
|
||||||
//
|
//
|
||||||
@@ -47,83 +26,83 @@ func (s *DynamicValue[A, B]) IsB() bool {
|
|||||||
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
|
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
|
||||||
type Schema struct {
|
type Schema struct {
|
||||||
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
|
// 3.1 only, used to define a dialect for this schema, label is '$schema'.
|
||||||
SchemaTypeRef string
|
SchemaTypeRef string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
||||||
|
|
||||||
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
||||||
// In version 3.1, ExclusiveMaximum is an integer.
|
// In version 3.1, ExclusiveMaximum is an integer.
|
||||||
ExclusiveMaximum *DynamicValue[bool, int64]
|
ExclusiveMaximum *DynamicValue[bool, int64] `json:"exclusiveMaximum,omitempty" yaml:"exclusiveMaximum,omitempty"`
|
||||||
|
|
||||||
// In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean.
|
// In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean.
|
||||||
// In version 3.1, ExclusiveMinimum is an integer.
|
// In version 3.1, ExclusiveMinimum is an integer.
|
||||||
ExclusiveMinimum *DynamicValue[bool, int64]
|
ExclusiveMinimum *DynamicValue[bool, int64] `json:"exclusiveMinimum,omitempty" yaml:"exclusiveMinimum,omitempty"`
|
||||||
|
|
||||||
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
||||||
// in version 3.1, Type can be multiple values
|
// in version 3.1, Type can be multiple values
|
||||||
Type []string
|
Type []string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
|
|
||||||
// Schemas are resolved on demand using a SchemaProxy
|
// Schemas are resolved on demand using a SchemaProxy
|
||||||
AllOf []*SchemaProxy
|
AllOf []*SchemaProxy `json:"allOf,omitempty" yaml:"allOf,omitempty"`
|
||||||
|
|
||||||
// Polymorphic Schemas are only available in version 3+
|
// Polymorphic Schemas are only available in version 3+
|
||||||
OneOf []*SchemaProxy
|
OneOf []*SchemaProxy `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
|
||||||
AnyOf []*SchemaProxy
|
AnyOf []*SchemaProxy `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
|
||||||
Discriminator *Discriminator
|
Discriminator *Discriminator `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
|
||||||
|
|
||||||
// in 3.1 examples can be an array (which is recommended)
|
// in 3.1 examples can be an array (which is recommended)
|
||||||
Examples []any
|
Examples []any `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||||
|
|
||||||
// in 3.1 prefixItems provides tuple validation support.
|
// in 3.1 prefixItems provides tuple validation support.
|
||||||
PrefixItems []*SchemaProxy
|
PrefixItems []*SchemaProxy `json:"prefixItems,omitempty" yaml:"prefixItems,omitempty"`
|
||||||
|
|
||||||
// 3.1 Specific properties
|
// 3.1 Specific properties
|
||||||
Contains *SchemaProxy
|
Contains *SchemaProxy `json:"contains,omitempty" yaml:"contains,omitempty"`
|
||||||
MinContains *int64
|
MinContains *int64 `json:"minContains,omitempty" yaml:"minContains,omitempty"`
|
||||||
MaxContains *int64
|
MaxContains *int64 `json:"maxContains,omitempty" yaml:"maxContains,omitempty"`
|
||||||
If *SchemaProxy
|
If *SchemaProxy `json:"if,omitempty" yaml:"if,omitempty"`
|
||||||
Else *SchemaProxy
|
Else *SchemaProxy `json:"else,omitempty" yaml:"else,omitempty"`
|
||||||
Then *SchemaProxy
|
Then *SchemaProxy `json:"then,omitempty" yaml:"then,omitempty"`
|
||||||
DependentSchemas map[string]*SchemaProxy
|
DependentSchemas map[string]*SchemaProxy `json:"dependentSchemas,omitempty" yaml:"dependentSchemas,omitempty"`
|
||||||
PatternProperties map[string]*SchemaProxy
|
PatternProperties map[string]*SchemaProxy `json:"patternProperties,omitempty" yaml:"patternProperties,omitempty"`
|
||||||
PropertyNames *SchemaProxy
|
PropertyNames *SchemaProxy `json:"propertyNames,omitempty" yaml:"propertyNames,omitempty"`
|
||||||
UnevaluatedItems *SchemaProxy
|
UnevaluatedItems *SchemaProxy `json:"unevaluatedItems,omitempty" yaml:"unevaluatedItems,omitempty"`
|
||||||
UnevaluatedProperties *SchemaProxy
|
UnevaluatedProperties *SchemaProxy `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
|
||||||
|
|
||||||
// in 3.1 Items can be a Schema or a boolean
|
// in 3.1 Items can be a Schema or a boolean
|
||||||
Items *DynamicValue[*SchemaProxy, bool]
|
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
|
||||||
|
|
||||||
// Compatible with all versions
|
// Compatible with all versions
|
||||||
Not *SchemaProxy
|
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
|
||||||
Properties map[string]*SchemaProxy
|
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
|
||||||
Title string
|
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||||
MultipleOf *int64
|
MultipleOf *int64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
|
||||||
Maximum *int64
|
Maximum *int64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
|
||||||
Minimum *int64
|
Minimum *int64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
|
||||||
MaxLength *int64
|
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
|
||||||
MinLength *int64
|
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
|
||||||
Pattern string
|
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
|
||||||
Format string
|
Format string `json:"format,omitempty" yaml:"format,omitempty"`
|
||||||
MaxItems *int64
|
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
|
||||||
MinItems *int64
|
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
|
||||||
UniqueItems *int64
|
UniqueItems *int64 `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
|
||||||
MaxProperties *int64
|
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
|
||||||
MinProperties *int64
|
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
|
||||||
Required []string
|
Required []string `json:"required,omitempty" yaml:"required,omitempty"`
|
||||||
Enum []any
|
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||||
AdditionalProperties any
|
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
|
||||||
Description string
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Default any
|
Default any `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
Nullable *bool
|
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
|
||||||
ReadOnly bool // https://github.com/pb33f/libopenapi/issues/30
|
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
|
||||||
WriteOnly bool // https://github.com/pb33f/libopenapi/issues/30
|
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
|
||||||
XML *XML
|
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
|
||||||
ExternalDocs *ExternalDoc
|
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||||
Example any
|
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||||
Deprecated *bool
|
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *base.Schema
|
low *base.Schema
|
||||||
|
|
||||||
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
|
||||||
ParentProxy *SchemaProxy
|
ParentProxy *SchemaProxy `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSchema will create a new high-level schema from a low-level one.
|
// NewSchema will create a new high-level schema from a low-level one.
|
||||||
@@ -462,3 +441,17 @@ func NewSchema(schema *base.Schema) *Schema {
|
|||||||
func (s *Schema) GoLow() *base.Schema {
|
func (s *Schema) GoLow() *base.Schema {
|
||||||
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
|
||||||
|
func (s *Schema) MarshalYAML() (interface{}, error) {
|
||||||
|
if s == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@
|
|||||||
package base
|
package base
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An
|
// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An
|
||||||
@@ -83,3 +85,23 @@ func (sp *SchemaProxy) GoLow() *base.SchemaProxy {
|
|||||||
}
|
}
|
||||||
return sp.schema.Value
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the ExternalDoc object.
|
||||||
|
func (sp *SchemaProxy) MarshalYAML() (interface{}, error) {
|
||||||
|
if sp == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
s, err := sp.BuildSchema()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -257,6 +257,11 @@ unevaluatedProperties:
|
|||||||
|
|
||||||
wentLow := compiled.GoLow()
|
wentLow := compiled.GoLow()
|
||||||
assert.Equal(t, 114, wentLow.AdditionalProperties.ValueNode.Line)
|
assert.Equal(t, 114, wentLow.AdditionalProperties.ValueNode.Line)
|
||||||
|
|
||||||
|
// now render it out!
|
||||||
|
schemaBytes, _ := compiled.Render()
|
||||||
|
assert.Equal(t, testSpec, string(schemaBytes))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {
|
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {
|
||||||
@@ -701,3 +706,114 @@ properties:
|
|||||||
type: number
|
type: number
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNewSchemaProxy_RenderSchema(t *testing.T) {
|
||||||
|
testSpec := `type: object
|
||||||
|
description: something object
|
||||||
|
discriminator:
|
||||||
|
propertyName: athing
|
||||||
|
mapping:
|
||||||
|
log: cat
|
||||||
|
pizza: party
|
||||||
|
allOf:
|
||||||
|
- type: object
|
||||||
|
description: an allof thing
|
||||||
|
properties:
|
||||||
|
allOfA:
|
||||||
|
type: string
|
||||||
|
description: allOfA description
|
||||||
|
example: allOfAExp
|
||||||
|
allOfB:
|
||||||
|
type: string
|
||||||
|
description: allOfB description
|
||||||
|
example: allOfBExp
|
||||||
|
`
|
||||||
|
|
||||||
|
var compNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
|
||||||
|
|
||||||
|
sp := new(lowbase.SchemaProxy)
|
||||||
|
err := sp.Build(compNode.Content[0], nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
|
||||||
|
Value: sp,
|
||||||
|
ValueNode: compNode.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaProxy := NewSchemaProxy(&lowproxy)
|
||||||
|
compiled := schemaProxy.Schema()
|
||||||
|
|
||||||
|
assert.Equal(t, schemaProxy, compiled.ParentProxy)
|
||||||
|
|
||||||
|
assert.NotNil(t, compiled)
|
||||||
|
assert.Nil(t, schemaProxy.GetBuildError())
|
||||||
|
|
||||||
|
// now render it out, it should be identical.
|
||||||
|
schemaBytes, _ := compiled.Render()
|
||||||
|
assert.Equal(t, testSpec, string(schemaBytes))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) {
|
||||||
|
testSpec := `type: object
|
||||||
|
description: something object
|
||||||
|
oneOf:
|
||||||
|
- type: object
|
||||||
|
description: a oneof thing
|
||||||
|
properties:
|
||||||
|
oneOfA:
|
||||||
|
type: string
|
||||||
|
example: oneOfAExp
|
||||||
|
anyOf:
|
||||||
|
- type: object
|
||||||
|
description: an anyOf thing
|
||||||
|
properties:
|
||||||
|
anyOfA:
|
||||||
|
type: string
|
||||||
|
example: anyOfAExp
|
||||||
|
not:
|
||||||
|
type: object
|
||||||
|
description: a not thing
|
||||||
|
properties:
|
||||||
|
notA:
|
||||||
|
type: string
|
||||||
|
example: notAExp
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
description: an items thing
|
||||||
|
properties:
|
||||||
|
itemsA:
|
||||||
|
type: string
|
||||||
|
description: itemsA description
|
||||||
|
example: itemsAExp
|
||||||
|
itemsB:
|
||||||
|
type: string
|
||||||
|
description: itemsB description
|
||||||
|
example: itemsBExp
|
||||||
|
`
|
||||||
|
|
||||||
|
var compNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
|
||||||
|
|
||||||
|
sp := new(lowbase.SchemaProxy)
|
||||||
|
err := sp.Build(compNode.Content[0], nil)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
|
||||||
|
Value: sp,
|
||||||
|
ValueNode: compNode.Content[0],
|
||||||
|
}
|
||||||
|
|
||||||
|
schemaProxy := NewSchemaProxy(&lowproxy)
|
||||||
|
compiled := schemaProxy.Schema()
|
||||||
|
|
||||||
|
assert.Equal(t, schemaProxy, compiled.ParentProxy)
|
||||||
|
|
||||||
|
assert.NotNil(t, compiled)
|
||||||
|
assert.Nil(t, schemaProxy.GetBuildError())
|
||||||
|
|
||||||
|
// now render it out, it should be identical.
|
||||||
|
schemaBytes, _ := compiled.Render()
|
||||||
|
assert.Equal(t, testSpec, string(schemaBytes))
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
package high
|
package high
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
@@ -153,12 +155,23 @@ func (n *NodeBuilder) add(key string) {
|
|||||||
field, _ := reflect.TypeOf(n.High).Elem().FieldByName(key)
|
field, _ := reflect.TypeOf(n.High).Elem().FieldByName(key)
|
||||||
tag := string(field.Tag.Get("yaml"))
|
tag := string(field.Tag.Get("yaml"))
|
||||||
tagName := strings.Split(tag, ",")[0]
|
tagName := strings.Split(tag, ",")[0]
|
||||||
|
if tag == "-" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// extract the value of the field
|
// extract the value of the field
|
||||||
fieldValue := reflect.ValueOf(n.High).Elem().FieldByName(key)
|
fieldValue := reflect.ValueOf(n.High).Elem().FieldByName(key)
|
||||||
f := fieldValue.Interface()
|
f := fieldValue.Interface()
|
||||||
value := reflect.ValueOf(f)
|
value := reflect.ValueOf(f)
|
||||||
|
|
||||||
|
if tag == "additionalProperties" {
|
||||||
|
fmt.Printf("woo")
|
||||||
|
}
|
||||||
|
|
||||||
|
if f == nil || value.IsZero() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// create a new node entry
|
// create a new node entry
|
||||||
nodeEntry := &NodeEntry{Key: tagName}
|
nodeEntry := &NodeEntry{Key: tagName}
|
||||||
|
|
||||||
@@ -169,12 +182,22 @@ func (n *NodeBuilder) add(key string) {
|
|||||||
nodeEntry.Value = value.String()
|
nodeEntry.Value = value.String()
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
nodeEntry.Value = value.Bool()
|
nodeEntry.Value = value.Bool()
|
||||||
|
case reflect.Slice:
|
||||||
|
if tagName == v3.TypeLabel {
|
||||||
|
if value.Len() == 1 {
|
||||||
|
nodeEntry.Value = value.Index(0).String()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !value.IsNil() {
|
||||||
|
nodeEntry.Value = f
|
||||||
|
}
|
||||||
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
|
if !value.IsNil() {
|
||||||
nodeEntry.Value = f
|
nodeEntry.Value = f
|
||||||
case reflect.Map:
|
}
|
||||||
nodeEntry.Value = f
|
|
||||||
default:
|
default:
|
||||||
panic("not supported yet")
|
nodeEntry.Value = f
|
||||||
}
|
}
|
||||||
|
|
||||||
// if there is no low level object, then we cannot extract line numbers,
|
// if there is no low level object, then we cannot extract line numbers,
|
||||||
@@ -196,7 +219,9 @@ func (n *NodeBuilder) add(key string) {
|
|||||||
nodeEntry.Line = 9999
|
nodeEntry.Line = 9999
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if nodeEntry.Value != nil {
|
||||||
n.Nodes = append(n.Nodes, nodeEntry)
|
n.Nodes = append(n.Nodes, nodeEntry)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *NodeBuilder) Render() *yaml.Node {
|
func (n *NodeBuilder) Render() *yaml.Node {
|
||||||
@@ -225,17 +250,75 @@ func AddYAMLNode(parent *yaml.Node, key string, value any) *yaml.Node {
|
|||||||
l = CreateStringNode(key)
|
l = CreateStringNode(key)
|
||||||
}
|
}
|
||||||
var valueNode *yaml.Node
|
var valueNode *yaml.Node
|
||||||
|
vo := reflect.ValueOf(value)
|
||||||
switch t.Kind() {
|
switch t.Kind() {
|
||||||
|
|
||||||
|
case reflect.String:
|
||||||
|
val := value.(string)
|
||||||
|
if val == "" {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
valueNode = CreateStringNode(val)
|
||||||
|
break
|
||||||
|
|
||||||
|
case reflect.Bool:
|
||||||
|
val := value.(bool)
|
||||||
|
if !val {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
valueNode = CreateBoolNode("true")
|
||||||
|
break
|
||||||
|
|
||||||
|
case reflect.Slice:
|
||||||
|
if vo.IsNil() {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// type is a case where it can be a single value, or a slice.
|
||||||
|
// so, if the key is 'type', then check if the slice contains a sigle value
|
||||||
|
// and if so, render it as a string, otherwise, proceed as normal.
|
||||||
|
skip := false
|
||||||
|
if key == v3.TypeLabel {
|
||||||
|
//if vo.Len() == 1 {
|
||||||
|
// valueNode = CreateStringNode(value.([]string)[0])
|
||||||
|
// skip = true
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !skip {
|
||||||
|
var rawNode yaml.Node
|
||||||
|
err := rawNode.Encode(value)
|
||||||
|
if err != nil {
|
||||||
|
return parent
|
||||||
|
} else {
|
||||||
|
valueNode = &rawNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
panic("no way dude, why?")
|
panic("no way dude, why?")
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
rawRender, _ := value.(Renderable).MarshalYAML()
|
if r, ok := value.(Renderable); ok {
|
||||||
|
rawRender, _ := r.MarshalYAML()
|
||||||
if rawRender != nil {
|
if rawRender != nil {
|
||||||
valueNode = rawRender.(*yaml.Node)
|
valueNode = rawRender.(*yaml.Node)
|
||||||
} else {
|
} else {
|
||||||
return parent
|
return parent
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var rawNode yaml.Node
|
||||||
|
err := rawNode.Encode(value)
|
||||||
|
if err != nil {
|
||||||
|
return parent
|
||||||
|
} else {
|
||||||
|
valueNode = &rawNode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
if vo.IsNil() {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
var rawNode yaml.Node
|
var rawNode yaml.Node
|
||||||
err := rawNode.Encode(value)
|
err := rawNode.Encode(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -270,6 +353,15 @@ func CreateStringNode(str string) *yaml.Node {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateBoolNode(str string) *yaml.Node {
|
||||||
|
n := &yaml.Node{
|
||||||
|
Kind: yaml.ScalarNode,
|
||||||
|
Tag: "!!bool",
|
||||||
|
Value: str,
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func CreateIntNode(val int) *yaml.Node {
|
func CreateIntNode(val int) *yaml.Node {
|
||||||
i := strconv.Itoa(val)
|
i := strconv.Itoa(val)
|
||||||
n := &yaml.Node{
|
n := &yaml.Node{
|
||||||
|
|||||||
Reference in New Issue
Block a user