mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 12:37:48 +00:00
more mutable objects, more tightening up
This commit is contained in:
@@ -45,9 +45,6 @@ func (e *Example) Render() ([]byte, error) {
|
|||||||
|
|
||||||
// MarshalYAML will create a ready to render YAML representation of the Example object.
|
// MarshalYAML will create a ready to render YAML representation of the Example object.
|
||||||
func (e *Example) MarshalYAML() (interface{}, error) {
|
func (e *Example) MarshalYAML() (interface{}, error) {
|
||||||
if e == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
nb := high.NewNodeBuilder(e, e.low)
|
nb := high.NewNodeBuilder(e, e.low)
|
||||||
return nb.Render(), nil
|
return nb.Render(), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -922,161 +922,3 @@ func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSlice(t *testing.T)
|
|||||||
schemaBytes, _ := compiled.Render()
|
schemaBytes, _ := compiled.Render()
|
||||||
assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes)))
|
assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
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'
|
|
||||||
oneOf:
|
|
||||||
type: object
|
|
||||||
description: a oneof thing
|
|
||||||
properties:
|
|
||||||
oneOfA:
|
|
||||||
type: string
|
|
||||||
description: oneOfA description
|
|
||||||
example: 'oneOfAExp'
|
|
||||||
oneOfB:
|
|
||||||
type: string
|
|
||||||
description: oneOfB description
|
|
||||||
example: 'oneOfBExp'
|
|
||||||
anyOf:
|
|
||||||
type: object
|
|
||||||
description: an anyOf thing
|
|
||||||
properties:
|
|
||||||
anyOfA:
|
|
||||||
type: string
|
|
||||||
description: anyOfA description
|
|
||||||
example: 'anyOfAExp'
|
|
||||||
anyOfB:
|
|
||||||
type: string
|
|
||||||
description: anyOfB description
|
|
||||||
example: 'anyOfBExp'
|
|
||||||
not:
|
|
||||||
type: object
|
|
||||||
description: a not thing
|
|
||||||
properties:
|
|
||||||
notA:
|
|
||||||
type: string
|
|
||||||
description: notA description
|
|
||||||
example: 'notAExp'
|
|
||||||
notB:
|
|
||||||
type: string
|
|
||||||
description: notB description
|
|
||||||
example: 'notBExp'
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
description: an items thing
|
|
||||||
properties:
|
|
||||||
itemsA:
|
|
||||||
type: string
|
|
||||||
description: itemsA description
|
|
||||||
example: 'itemsAExp'
|
|
||||||
itemsB:
|
|
||||||
type: string
|
|
||||||
description: itemsB description
|
|
||||||
example: 'itemsBExp'
|
|
||||||
prefixItems:
|
|
||||||
type: object
|
|
||||||
description: an items thing
|
|
||||||
properties:
|
|
||||||
itemsA:
|
|
||||||
type: string
|
|
||||||
description: itemsA description
|
|
||||||
example: 'itemsAExp'
|
|
||||||
itemsB:
|
|
||||||
type: string
|
|
||||||
description: itemsB description
|
|
||||||
example: 'itemsBExp'
|
|
||||||
properties:
|
|
||||||
somethingBee:
|
|
||||||
type: number
|
|
||||||
somethingThree:
|
|
||||||
type: number
|
|
||||||
somethingTwo:
|
|
||||||
type: number
|
|
||||||
somethingOne:
|
|
||||||
type: number
|
|
||||||
somethingA:
|
|
||||||
type: number
|
|
||||||
description: a number
|
|
||||||
example: 2
|
|
||||||
somethingB:
|
|
||||||
type: object
|
|
||||||
description: an object
|
|
||||||
externalDocs:
|
|
||||||
description: the best docs
|
|
||||||
url: https://pb33f.io
|
|
||||||
properties:
|
|
||||||
somethingBProp:
|
|
||||||
type: string
|
|
||||||
description: something b subprop
|
|
||||||
example: picnics are nice.
|
|
||||||
xml:
|
|
||||||
name: an xml thing
|
|
||||||
namespace: an xml namespace
|
|
||||||
prefix: a prefix
|
|
||||||
attribute: true
|
|
||||||
wrapped: false
|
|
||||||
x-pizza: love
|
|
||||||
additionalProperties:
|
|
||||||
why: yes
|
|
||||||
thatIs: true
|
|
||||||
additionalProperties: true
|
|
||||||
xml:
|
|
||||||
name: XML Thing
|
|
||||||
externalDocs:
|
|
||||||
url: https://pb33f.io/docs
|
|
||||||
enum: [fish, cake]
|
|
||||||
required: [cake, fish]
|
|
||||||
maxLength: 10
|
|
||||||
minLength: 1
|
|
||||||
maxItems: 10
|
|
||||||
minItems: 1
|
|
||||||
maxProperties: 10
|
|
||||||
minProperties: 1
|
|
||||||
nullable: true
|
|
||||||
readOnly: true
|
|
||||||
writeOnly: false
|
|
||||||
deprecated: true
|
|
||||||
contains:
|
|
||||||
type: int
|
|
||||||
minContains: 1
|
|
||||||
maxContains: 10
|
|
||||||
if:
|
|
||||||
type: string
|
|
||||||
else:
|
|
||||||
type: integer
|
|
||||||
then:
|
|
||||||
type: boolean
|
|
||||||
dependentSchemas:
|
|
||||||
schemaOne:
|
|
||||||
type: string
|
|
||||||
patternProperties:
|
|
||||||
patternOne:
|
|
||||||
type: string
|
|
||||||
propertyNames:
|
|
||||||
type: string
|
|
||||||
unevaluatedItems:
|
|
||||||
type: boolean
|
|
||||||
unevaluatedProperties:
|
|
||||||
type: integer
|
|
||||||
*/
|
|
||||||
@@ -253,7 +253,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
// number of the tag.
|
// number of the tag.
|
||||||
var orderedCollection []*NodeEntry
|
var orderedCollection []*NodeEntry
|
||||||
m := reflect.ValueOf(value)
|
m := reflect.ValueOf(value)
|
||||||
for _, k := range m.MapKeys() {
|
for g, k := range m.MapKeys() {
|
||||||
|
|
||||||
var x string
|
var x string
|
||||||
// extract key
|
// extract key
|
||||||
@@ -275,7 +275,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
if pr, ok := gh.(low.HasValueUnTyped); ok {
|
if pr, ok := gh.(low.HasValueUnTyped); ok {
|
||||||
fg := reflect.ValueOf(pr.GetValueUntyped())
|
fg := reflect.ValueOf(pr.GetValueUntyped())
|
||||||
found := false
|
found := false
|
||||||
for _, ky := range fg.MapKeys() {
|
for j, ky := range fg.MapKeys() {
|
||||||
if we, wok := ky.Interface().(low.HasKeyNode); wok {
|
if we, wok := ky.Interface().(low.HasKeyNode); wok {
|
||||||
er := we.GetKeyNode().Value
|
er := we.GetKeyNode().Value
|
||||||
if er == x {
|
if er == x {
|
||||||
@@ -288,14 +288,17 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// this is a map, without any low level details available
|
uu := ky.Interface()
|
||||||
found = true
|
if uu == x {
|
||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
// this is a map, without any low level details available
|
||||||
Tag: x,
|
found = true
|
||||||
Key: x,
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Line: 9999,
|
Tag: uu.(string),
|
||||||
Value: m.MapIndex(k).Interface(),
|
Key: uu.(string),
|
||||||
})
|
Line: 9999 + j,
|
||||||
|
Value: fg.MapIndex(ky).Interface(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if found != true {
|
if found != true {
|
||||||
@@ -303,7 +306,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
Line: 9999,
|
Line: 9999 + g,
|
||||||
Value: m.MapIndex(k).Interface(),
|
Value: m.MapIndex(k).Interface(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -319,7 +322,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
|
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
|
||||||
Value: m.MapIndex(k).Interface(),
|
Value: iu.MapIndex(ky).Interface(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +339,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
Line: 9999,
|
Line: 9999 + g,
|
||||||
Value: m.MapIndex(k).Interface(),
|
Value: m.MapIndex(k).Interface(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -345,7 +348,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
Line: 9999,
|
Line: 9999 + g,
|
||||||
Value: m.MapIndex(k).Interface(),
|
Value: m.MapIndex(k).Interface(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -357,6 +360,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
})
|
})
|
||||||
|
|
||||||
// create an empty map.
|
// create an empty map.
|
||||||
|
|
||||||
p := CreateEmptyMapNode()
|
p := CreateEmptyMapNode()
|
||||||
|
|
||||||
// build out each map node in original order.
|
// build out each map node in original order.
|
||||||
@@ -385,9 +389,13 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if r, ok := value.(low.ValueReference[any]); ok {
|
if r, ok := value.(low.ValueReference[any]); ok {
|
||||||
valueNode = r.GetValueNode()
|
valueNode = r.GetValueNode()
|
||||||
} else {
|
break
|
||||||
panic("not supported yet")
|
|
||||||
}
|
}
|
||||||
|
if r, ok := value.(low.ValueReference[string]); ok {
|
||||||
|
valueNode = r.GetValueNode()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
panic("not supported yet")
|
||||||
|
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if r, ok := value.(Renderable); ok {
|
if r, ok := value.(Renderable); ok {
|
||||||
|
|||||||
@@ -3,7 +3,12 @@
|
|||||||
|
|
||||||
package v3
|
package v3
|
||||||
|
|
||||||
import low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
import (
|
||||||
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
// Callback represents a high-level Callback object for OpenAPI 3+.
|
// Callback represents a high-level Callback object for OpenAPI 3+.
|
||||||
//
|
//
|
||||||
@@ -13,8 +18,8 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
|||||||
// that identifies a URL to use for the callback operation.
|
// that identifies a URL to use for the callback operation.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
// - https://spec.openapis.org/oas/v3.1.0#callback-object
|
||||||
type Callback struct {
|
type Callback struct {
|
||||||
Expression map[string]*PathItem
|
Expression map[string]*PathItem `json:"-" yaml:"-"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.Callback
|
low *low.Callback
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,3 +42,46 @@ func NewCallback(lowCallback *low.Callback) *Callback {
|
|||||||
func (c *Callback) GoLow() *low.Callback {
|
func (c *Callback) GoLow() *low.Callback {
|
||||||
return c.low
|
return c.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the Callback object as a byte slice.
|
||||||
|
func (c *Callback) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the Callback object.
|
||||||
|
func (c *Callback) MarshalYAML() (interface{}, error) {
|
||||||
|
// map keys correctly.
|
||||||
|
m := high.CreateEmptyMapNode()
|
||||||
|
type cbItem struct {
|
||||||
|
cb *PathItem
|
||||||
|
exp string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
var mapped []*cbItem
|
||||||
|
|
||||||
|
for k, ex := range c.Expression {
|
||||||
|
ln := 9999 // default to a high value to weight new content to the bottom.
|
||||||
|
if c.low != nil {
|
||||||
|
lcb := c.low.FindExpression(k)
|
||||||
|
if lcb != nil {
|
||||||
|
ln = lcb.ValueNode.Line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapped = append(mapped, &cbItem{ex, k, ln})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(mapped, func(i, j int) bool {
|
||||||
|
return mapped[i].line < mapped[j].line
|
||||||
|
})
|
||||||
|
for j := range mapped {
|
||||||
|
rendered, _ := mapped[j].cb.MarshalYAML()
|
||||||
|
m.Content = append(m.Content, high.CreateStringNode(mapped[j].exp))
|
||||||
|
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(c, c.low)
|
||||||
|
extNode := nb.Render()
|
||||||
|
if extNode.Content != nil {
|
||||||
|
m.Content = append(m.Content, extNode.Content...)
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|||||||
92
datamodel/high/v3/callback_test.go
Normal file
92
datamodel/high/v3/callback_test.go
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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?",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
rend, _ := cb.Render()
|
||||||
|
|
||||||
|
desired := `https://pb33f.io:
|
||||||
|
get:
|
||||||
|
operationId: oneTwoThree
|
||||||
|
https://pb33f.io/libopenapi:
|
||||||
|
get:
|
||||||
|
operationId: openaypeeeye
|
||||||
|
x-burgers: why not?`
|
||||||
|
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
|
// mutate
|
||||||
|
cb.Expression["https://pb33f.io"].Get.OperationId = "blim-blam"
|
||||||
|
cb.Extensions = map[string]interface{}{"x-burgers": "yes please!"}
|
||||||
|
|
||||||
|
desired = `https://pb33f.io:
|
||||||
|
get:
|
||||||
|
operationId: blim-blam
|
||||||
|
https://pb33f.io/libopenapi:
|
||||||
|
get:
|
||||||
|
operationId: openaypeeeye
|
||||||
|
x-burgers: yes please!`
|
||||||
|
|
||||||
|
rend, _ = cb.Render()
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
|
k := `x-break-everything: please
|
||||||
|
"{$request.query.queryUrl}":
|
||||||
|
post:
|
||||||
|
requestBody: null
|
||||||
|
description: Callback payload
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: callback successfully processes
|
||||||
|
`
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
r := NewCallback(&n)
|
||||||
|
|
||||||
|
assert.Equal(t, "please", r.Extensions["x-break-everything"])
|
||||||
|
|
||||||
|
rend, _ = r.Render()
|
||||||
|
assert.Equal(t, k, strings.TrimSpace(string(rend)))
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/base"
|
"github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// used for internal channel co-ordination for building out different component types.
|
// used for internal channel co-ordination for building out different component types.
|
||||||
@@ -29,16 +30,20 @@ const (
|
|||||||
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
|
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||||
type Components struct {
|
type Components struct {
|
||||||
Schemas map[string]*highbase.SchemaProxy
|
//Schemas map[string]*highbase.SchemaProxy `json:"schemas,omitempty" yaml:"schemas,omitempty"`
|
||||||
Responses map[string]*Response
|
Schemas map[string]*highbase.SchemaProxy `json:"-" yaml:"-"`
|
||||||
Parameters map[string]*Parameter
|
Responses map[string]*Response `json:"-" yaml:"-"`
|
||||||
Examples map[string]*highbase.Example
|
//Responses map[string]*Response `json:"responses,omitempty" yaml:"responses,omitempty"`
|
||||||
RequestBodies map[string]*RequestBody
|
Parameters map[string]*Parameter `json:"-" yaml:"-"`
|
||||||
Headers map[string]*Header
|
//Parameters map[string]*Parameter `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||||
SecuritySchemes map[string]*SecurityScheme
|
//Examples map[string]*highbase.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||||
Links map[string]*Link
|
Examples map[string]*highbase.Example `json:"-" yaml:"-"`
|
||||||
Callbacks map[string]*Callback
|
RequestBodies map[string]*RequestBody `json:"requestBodies,omitempty" yaml:"requestBodies,omitempty"`
|
||||||
Extensions map[string]any
|
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
|
low *low.Components
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +89,7 @@ func NewComponents(comp *low.Components) *Components {
|
|||||||
go buildComponent[*Parameter, *low.Parameter](parameters, k.Value, v.Value, paramChan, NewParameter)
|
go buildComponent[*Parameter, *low.Parameter](parameters, k.Value, v.Value, paramChan, NewParameter)
|
||||||
}
|
}
|
||||||
for k, v := range comp.Examples.Value {
|
for k, v := range comp.Examples.Value {
|
||||||
go buildComponent[*highbase.Example, *base.Example](parameters, k.Value, v.Value, exampleChan, highbase.NewExample)
|
go buildComponent[*highbase.Example, *base.Example](examples, k.Value, v.Value, exampleChan, highbase.NewExample)
|
||||||
}
|
}
|
||||||
for k, v := range comp.RequestBodies.Value {
|
for k, v := range comp.RequestBodies.Value {
|
||||||
go buildComponent[*RequestBody, *low.RequestBody](requestBodies, k.Value, v.Value,
|
go buildComponent[*RequestBody, *low.RequestBody](requestBodies, k.Value, v.Value,
|
||||||
@@ -162,7 +167,8 @@ func buildComponent[N any, O any](comp int, key string, orig O, c chan component
|
|||||||
}
|
}
|
||||||
|
|
||||||
// build out a schema
|
// build out a schema
|
||||||
func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference[*base.SchemaProxy], c chan componentResult[*highbase.SchemaProxy]) {
|
func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference[*base.SchemaProxy],
|
||||||
|
c chan componentResult[*highbase.SchemaProxy]) {
|
||||||
var sch *highbase.SchemaProxy
|
var sch *highbase.SchemaProxy
|
||||||
sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
|
||||||
Value: orig.Value,
|
Value: orig.Value,
|
||||||
@@ -175,3 +181,14 @@ func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference
|
|||||||
func (c *Components) GoLow() *low.Components {
|
func (c *Components) GoLow() *low.Components {
|
||||||
return c.low
|
return c.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the Components object as a byte slice.
|
||||||
|
func (c *Components) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the Response object.
|
||||||
|
func (c *Components) MarshalYAML() (interface{}, error) {
|
||||||
|
nb := high.NewNodeBuilder(c, c.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
|
|||||||
68
datamodel/high/v3/components_test.go
Normal file
68
datamodel/high/v3/components_test.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
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?",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dat, _ := comp.Render()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
r := NewComponents(&n)
|
||||||
|
|
||||||
|
desired := `responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
parameters:
|
||||||
|
id:
|
||||||
|
name: id
|
||||||
|
in: path
|
||||||
|
requestBodies:
|
||||||
|
body:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
example: why?`
|
||||||
|
|
||||||
|
dat, _ = r.Render()
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
// Copyright 2022-2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
// Package v3 represents all OpenAPI 3+ high-level models. High-level models are easy to navigate
|
// Package v3 represents all OpenAPI 3+ high-level models. High-level models are easy to navigate
|
||||||
@@ -30,18 +30,18 @@ type Document struct {
|
|||||||
Info *base.Info `json:"info,omitempty" yaml:"info,omitempty"`
|
Info *base.Info `json:"info,omitempty" yaml:"info,omitempty"`
|
||||||
|
|
||||||
// Servers is a slice of Server instances which provide connectivity information to a target server. If the servers
|
// Servers is a slice of Server instances which provide connectivity information to a target server. If the servers
|
||||||
// property is not provided, or is an empty array, the default value would be a Server Object with a url value of /.
|
// property is not provided, or is an empty array, the default value would be a Server Object with an url value of /.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
// - https://spec.openapis.org/oas/v3.1.0#server-object
|
||||||
Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"`
|
Servers []*Server `json:"servers,omitempty" yaml:"servers,omitempty"`
|
||||||
|
|
||||||
// Paths contains all the PathItem definitions for the specification.
|
// Paths contains all the PathItem definitions for the specification.
|
||||||
// The available paths and operations for the API, The most important part of ths spec.
|
// The available paths and operations for the API, The most important part of ths spec.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#paths-object
|
// - https://spec.openapis.org/oas/v3.1.0#paths-object
|
||||||
Paths *Paths `json:"-" yaml:"-"`
|
Paths *Paths `json:"paths,omitempty" yaml:"paths,omitempty"`
|
||||||
|
|
||||||
// Components is an element to hold various schemas for the document.
|
// Components is an element to hold various schemas for the document.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
// - https://spec.openapis.org/oas/v3.1.0#components-object
|
||||||
Components *Components `json:"-" yaml:"-"`
|
Components *Components `json:"components,omitempty" yaml:"components,omitempty"`
|
||||||
|
|
||||||
// Security contains global security requirements/roles for the specification
|
// Security contains global security requirements/roles for the specification
|
||||||
// A declaration of which security mechanisms can be used across the API. The list of values includes alternative
|
// A declaration of which security mechanisms can be used across the API. The list of values includes alternative
|
||||||
@@ -78,7 +78,7 @@ type Document struct {
|
|||||||
// for example by an out-of-band registration. The key name is a unique string to refer to each webhook,
|
// for example by an out-of-band registration. The key name is a unique string to refer to each webhook,
|
||||||
// while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider
|
// while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider
|
||||||
// and the expected responses. An example is available.
|
// and the expected responses. An example is available.
|
||||||
Webhooks map[string]*PathItem `json:"-" yaml:"-"`
|
Webhooks map[string]*PathItem `json:"webhooks,omitempty" yaml:"webhooks,omitempty"`
|
||||||
|
|
||||||
// Index is a reference to the *index.SpecIndex that was created for the document and used
|
// Index is a reference to the *index.SpecIndex that was created for the document and used
|
||||||
// as a guide when building out the Document. Ideal if further processing is required on the model and
|
// as a guide when building out the Document. Ideal if further processing is required on the model and
|
||||||
@@ -86,7 +86,7 @@ type Document struct {
|
|||||||
//
|
//
|
||||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||||
Index *index.SpecIndex `json:"-" yaml:"-"`
|
Index *index.SpecIndex `json:"-" yaml:"-"`
|
||||||
low *low.Document `json:"-" yaml:"-"`
|
low *low.Document
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDocument will create a new high-level Document from a low-level one.
|
// NewDocument will create a new high-level Document from a low-level one.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
@@ -22,7 +23,10 @@ func initTest() {
|
|||||||
data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml")
|
data, _ := ioutil.ReadFile("../../../test_specs/burgershop.openapi.yaml")
|
||||||
info, _ := datamodel.ExtractSpecInfo(data)
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
var err []error
|
var err []error
|
||||||
lowDoc, err = lowv3.CreateDocument(info)
|
lowDoc, err = lowv3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
|
||||||
|
AllowFileReferences: true,
|
||||||
|
AllowRemoteReferences: true,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("broken something")
|
panic("broken something")
|
||||||
}
|
}
|
||||||
@@ -481,17 +485,18 @@ func TestDocument_MarshalYAML(t *testing.T) {
|
|||||||
h := NewDocument(lowDoc)
|
h := NewDocument(lowDoc)
|
||||||
|
|
||||||
// render the document to YAML
|
// render the document to YAML
|
||||||
|
|
||||||
r, _ := h.Render()
|
r, _ := h.Render()
|
||||||
|
|
||||||
|
os.WriteFile("rendered.yaml", r, 0644)
|
||||||
|
|
||||||
|
// re-parse the document
|
||||||
info, _ := datamodel.ExtractSpecInfo(r)
|
info, _ := datamodel.ExtractSpecInfo(r)
|
||||||
lDoc, e := lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration())
|
lDoc, e := lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration())
|
||||||
assert.Nil(t, e)
|
assert.Nil(t, e)
|
||||||
|
|
||||||
highDoc := NewDocument(lDoc)
|
highDoc := NewDocument(lDoc)
|
||||||
assert.Equal(t, "3.1.0", highDoc.Version)
|
assert.Equal(t, "3.1.0", highDoc.Version)
|
||||||
|
assert.Len(t, highDoc.Paths.PathItems, 1)
|
||||||
// TODO: COMPLETE THIS
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,11 @@ package v3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low/v3"
|
"github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -47,3 +50,31 @@ example: testing a nice mutation`
|
|||||||
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMediaType_Examples(t *testing.T) {
|
||||||
|
yml := `examples:
|
||||||
|
pbjBurger:
|
||||||
|
summary: A horrible, nutty, sticky mess.
|
||||||
|
value:
|
||||||
|
name: Peanut And Jelly
|
||||||
|
numPatties: 3
|
||||||
|
cakeBurger:
|
||||||
|
summary: A sickly, sweet, atrocity
|
||||||
|
value:
|
||||||
|
name: Chocolate Cake Burger
|
||||||
|
numPatties: 5`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
|
|
||||||
|
var n v3.MediaType
|
||||||
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
|
r := NewMediaType(&n)
|
||||||
|
|
||||||
|
rend, _ := r.Render()
|
||||||
|
assert.Equal(t, yml, strings.TrimSpace(string(rend)))
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,16 +65,13 @@ func (p *Paths) Render() ([]byte, error) {
|
|||||||
|
|
||||||
// MarshalYAML will create a ready to render YAML representation of the Paths object.
|
// MarshalYAML will create a ready to render YAML representation of the Paths object.
|
||||||
func (p *Paths) MarshalYAML() (interface{}, error) {
|
func (p *Paths) MarshalYAML() (interface{}, error) {
|
||||||
if p == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// map keys correctly.
|
// map keys correctly.
|
||||||
m := high.CreateEmptyMapNode()
|
m := high.CreateEmptyMapNode()
|
||||||
type pathItem struct {
|
type pathItem struct {
|
||||||
pi *PathItem
|
pi *PathItem
|
||||||
path string
|
path string
|
||||||
line int
|
line int
|
||||||
|
rendered *yaml.Node
|
||||||
}
|
}
|
||||||
var mapped []*pathItem
|
var mapped []*pathItem
|
||||||
|
|
||||||
@@ -86,17 +83,33 @@ func (p *Paths) MarshalYAML() (interface{}, error) {
|
|||||||
ln = lpi.ValueNode.Line
|
ln = lpi.ValueNode.Line
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mapped = append(mapped, &pathItem{pi, k, ln})
|
mapped = append(mapped, &pathItem{pi, k, ln, nil})
|
||||||
|
}
|
||||||
|
|
||||||
|
nb := high.NewNodeBuilder(p, p.low)
|
||||||
|
extNode := nb.Render()
|
||||||
|
if extNode.Content != nil {
|
||||||
|
for u := range extNode.Content {
|
||||||
|
mapped = append(mapped, &pathItem{nil, extNode.Content[u].Value,
|
||||||
|
extNode.Content[u].Line, extNode.Content[u]})
|
||||||
|
}
|
||||||
|
//m.Content = append(m.Content, extNode.Content...)
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Slice(mapped, func(i, j int) bool {
|
sort.Slice(mapped, func(i, j int) bool {
|
||||||
return mapped[i].line < mapped[j].line
|
return mapped[i].line < mapped[j].line
|
||||||
})
|
})
|
||||||
for j := range mapped {
|
for j := range mapped {
|
||||||
rendered, _ := mapped[j].pi.MarshalYAML()
|
if mapped[j].pi != nil {
|
||||||
m.Content = append(m.Content, high.CreateStringNode(mapped[j].path))
|
rendered, _ := mapped[j].pi.MarshalYAML()
|
||||||
m.Content = append(m.Content, rendered.(*yaml.Node))
|
m.Content = append(m.Content, high.CreateStringNode(mapped[j].path))
|
||||||
|
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||||
|
}
|
||||||
|
if mapped[j].rendered != nil {
|
||||||
|
m.Content = append(m.Content, mapped[j].rendered)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,4 +60,3 @@ func (r *Response) MarshalYAML() (interface{}, error) {
|
|||||||
nb := high.NewNodeBuilder(r, r.low)
|
nb := high.NewNodeBuilder(r, r.low)
|
||||||
return nb.Render(), nil
|
return nb.Render(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Responses represents a high-level OpenAPI 3+ Responses object that is backed by a low-level one.
|
// Responses represents a high-level OpenAPI 3+ Responses object that is backed by a low-level one.
|
||||||
@@ -24,9 +26,9 @@ import (
|
|||||||
// be the response for a successful operation call.
|
// 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 {
|
type Responses struct {
|
||||||
Codes map[string]*Response
|
Codes map[string]*Response `json:"-" yaml:"-"`
|
||||||
Default *Response
|
Default *Response `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.Responses
|
low *low.Responses
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,3 +79,41 @@ func (r *Responses) FindResponseByCode(code int) *Response {
|
|||||||
func (r *Responses) GoLow() *low.Responses {
|
func (r *Responses) GoLow() *low.Responses {
|
||||||
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the Responses object.
|
||||||
|
func (r *Responses) MarshalYAML() (interface{}, error) {
|
||||||
|
// map keys correctly.
|
||||||
|
m := high.CreateEmptyMapNode()
|
||||||
|
type responseItem struct {
|
||||||
|
resp *Response
|
||||||
|
code string
|
||||||
|
line int
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
lpi := r.low.FindResponseByCode(k)
|
||||||
|
if lpi != nil {
|
||||||
|
ln = lpi.ValueNode.Line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mapped = append(mapped, &responseItem{re, k, ln})
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(mapped, func(i, j int) bool {
|
||||||
|
return mapped[i].line < mapped[j].line
|
||||||
|
})
|
||||||
|
for j := range mapped {
|
||||||
|
rendered, _ := mapped[j].resp.MarshalYAML()
|
||||||
|
m.Content = append(m.Content, high.CreateStringNode(mapped[j].code))
|
||||||
|
m.Content = append(m.Content, rendered.(*yaml.Node))
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,3 +37,33 @@ func TestNewResponses(t *testing.T) {
|
|||||||
assert.Equal(t, 1, r.GoLow().Default.KeyNode.Line)
|
assert.Equal(t, 1, r.GoLow().Default.KeyNode.Line)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResponses_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
|
yml := `"201":
|
||||||
|
description: this is a response
|
||||||
|
content:
|
||||||
|
something/thing:
|
||||||
|
example: cake
|
||||||
|
"404":
|
||||||
|
description: this is a 404
|
||||||
|
content:
|
||||||
|
something/thing:
|
||||||
|
example: why do you need an example?
|
||||||
|
"200":
|
||||||
|
description: OK! not bad.`
|
||||||
|
|
||||||
|
var idxNode yaml.Node
|
||||||
|
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||||
|
idx := index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
|
|
||||||
|
var n v3.Responses
|
||||||
|
_ = low.BuildModel(idxNode.Content[0], &n)
|
||||||
|
_ = n.Build(idxNode.Content[0], idx)
|
||||||
|
|
||||||
|
r := NewResponses(&n)
|
||||||
|
|
||||||
|
rend, _ := r.Render()
|
||||||
|
assert.Equal(t, yml, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package v3
|
|||||||
import (
|
import (
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecurityScheme represents a high-level OpenAPI 3+ SecurityScheme object that is backed by a low-level one.
|
// SecurityScheme represents a high-level OpenAPI 3+ SecurityScheme object that is backed by a low-level one.
|
||||||
@@ -19,15 +20,15 @@ import (
|
|||||||
// Recommended for most use case is Authorization Code Grant flow with PKCE.
|
// Recommended for most use case is Authorization Code Grant flow with PKCE.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
||||||
type SecurityScheme struct {
|
type SecurityScheme struct {
|
||||||
Type string
|
Type string `json:"type,omitempty" yaml:"type,omitempty"`
|
||||||
Description string
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Name string
|
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||||
In string
|
In string `json:"in,omitempty" yaml:"in,omitempty"`
|
||||||
Scheme string
|
Scheme string `json:"scheme,omitempty" yaml:"scheme,omitempty"`
|
||||||
BearerFormat string
|
BearerFormat string `json:"bearerFormat,omitempty" yaml:"bearerFormat,omitempty"`
|
||||||
Flows *OAuthFlows
|
Flows *OAuthFlows `json:"flows,omitempty" yaml:"flows,omitempty"`
|
||||||
OpenIdConnectUrl string
|
OpenIdConnectUrl string `json:"openIdConnectUrl,omitempty" yaml:"openIdConnectUrl,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.SecurityScheme
|
low *low.SecurityScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,3 +54,14 @@ func NewSecurityScheme(ss *low.SecurityScheme) *SecurityScheme {
|
|||||||
func (s *SecurityScheme) GoLow() *low.SecurityScheme {
|
func (s *SecurityScheme) GoLow() *low.SecurityScheme {
|
||||||
return s.low
|
return s.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the SecurityScheme object as a byte slice.
|
||||||
|
func (s *SecurityScheme) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the Response object.
|
||||||
|
func (s *SecurityScheme) MarshalYAML() (interface{}, error) {
|
||||||
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
|
|||||||
47
datamodel/high/v3/security_scheme_test.go
Normal file
47
datamodel/high/v3/security_scheme_test.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSecurityScheme_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
|
ss := &SecurityScheme{
|
||||||
|
Type: "apiKey",
|
||||||
|
Description: "this is a description",
|
||||||
|
Name: "superSecret",
|
||||||
|
In: "header",
|
||||||
|
Scheme: "https",
|
||||||
|
}
|
||||||
|
|
||||||
|
dat, _ := ss.Render()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
r := NewSecurityScheme(&n)
|
||||||
|
|
||||||
|
dat, _ = r.Render()
|
||||||
|
|
||||||
|
desired := `type: apiKey
|
||||||
|
description: this is a description
|
||||||
|
name: superSecret
|
||||||
|
in: header
|
||||||
|
scheme: https`
|
||||||
|
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||||
|
}
|
||||||
@@ -48,6 +48,9 @@ func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
currentCB = callbackNode
|
currentCB = callbackNode
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if strings.HasPrefix(currentCB.Value, "x-") {
|
||||||
|
continue // ignore extension.
|
||||||
|
}
|
||||||
callback, eErr, isRef, rv := low.ExtractObjectRaw[*PathItem](callbackNode, idx)
|
callback, eErr, isRef, rv := low.ExtractObjectRaw[*PathItem](callbackNode, idx)
|
||||||
if eErr != nil {
|
if eErr != nil {
|
||||||
return eErr
|
return eErr
|
||||||
|
|||||||
Reference in New Issue
Block a user