mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 04:20:24 +00:00
Churning through v3 models now.
mutations everywhere, for everyone!
This commit is contained in:
@@ -67,6 +67,16 @@ func (sp *SchemaProxy) Schema() *Schema {
|
|||||||
return sch
|
return sch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsReference returns true if the SchemaProxy is a reference to another Schema.
|
||||||
|
func (sp *SchemaProxy) IsReference() bool {
|
||||||
|
return sp.schema.Value.IsSchemaReference()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReference returns the location of the $ref if this SchemaProxy is a reference to another Schema.
|
||||||
|
func (sp *SchemaProxy) GetReference() string {
|
||||||
|
return sp.schema.Value.GetSchemaReference()
|
||||||
|
}
|
||||||
|
|
||||||
// BuildSchema operates the same way as Schema, except it will return any error along with the *Schema
|
// BuildSchema operates the same way as Schema, except it will return any error along with the *Schema
|
||||||
func (sp *SchemaProxy) BuildSchema() (*Schema, error) {
|
func (sp *SchemaProxy) BuildSchema() (*Schema, error) {
|
||||||
schema := sp.Schema()
|
schema := sp.Schema()
|
||||||
@@ -96,12 +106,23 @@ func (sp *SchemaProxy) MarshalYAML() (interface{}, error) {
|
|||||||
if sp == nil {
|
if sp == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
s, err := sp.BuildSchema()
|
var s *Schema
|
||||||
if err != nil {
|
var err error
|
||||||
return nil, err
|
// 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 := high.CreateEmptyMapNode()
|
||||||
|
mp.Content = append(mp.Content,
|
||||||
|
high.CreateStringNode("$ref"),
|
||||||
|
high.CreateStringNode(sp.GetReference()))
|
||||||
|
return mp, nil
|
||||||
}
|
}
|
||||||
nb := high.NewNodeBuilder(s, s.low)
|
|
||||||
return nb.Render(), nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,6 +211,33 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
valueNode = CreateBoolNode("true")
|
valueNode = CreateBoolNode("true")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case reflect.Int:
|
||||||
|
if value != nil {
|
||||||
|
val := strconv.Itoa(value.(int))
|
||||||
|
valueNode = CreateIntNode(val)
|
||||||
|
} else {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case reflect.Int64:
|
||||||
|
if value != nil {
|
||||||
|
val := strconv.FormatInt(value.(int64), 10)
|
||||||
|
valueNode = CreateIntNode(val)
|
||||||
|
} else {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
|
case reflect.Float64:
|
||||||
|
if value != nil {
|
||||||
|
val := strconv.FormatFloat(value.(float64), 'f', 2, 64)
|
||||||
|
valueNode = CreateFloatNode(val)
|
||||||
|
} else {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
break
|
||||||
|
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
|
|
||||||
// the keys will be rendered randomly, if we don't find out the original line
|
// the keys will be rendered randomly, if we don't find out the original line
|
||||||
@@ -239,12 +266,22 @@ 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())
|
||||||
for _, ky := range fg.MapKeys() {
|
for _, ky := range fg.MapKeys() {
|
||||||
er := ky.Interface().(low.HasKeyNode).GetKeyNode().Value
|
if we, wok := ky.Interface().(low.HasKeyNode); wok {
|
||||||
if er == x {
|
er := we.GetKeyNode().Value
|
||||||
|
if er == x {
|
||||||
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
|
Tag: x,
|
||||||
|
Key: x,
|
||||||
|
Line: we.GetKeyNode().Line,
|
||||||
|
Value: m.MapIndex(k).Interface(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// this is a map, without any low level details available
|
||||||
orderedCollection = append(orderedCollection, &NodeEntry{
|
orderedCollection = append(orderedCollection, &NodeEntry{
|
||||||
Tag: x,
|
Tag: x,
|
||||||
Key: x,
|
Key: x,
|
||||||
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
|
Line: 9999,
|
||||||
Value: m.MapIndex(k).Interface(),
|
Value: m.MapIndex(k).Interface(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -296,7 +333,11 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any)
|
|||||||
for _, cv := range orderedCollection {
|
for _, cv := range orderedCollection {
|
||||||
n.AddYAMLNode(p, cv.Tag, cv.Key, cv.Value)
|
n.AddYAMLNode(p, cv.Tag, cv.Key, cv.Value)
|
||||||
}
|
}
|
||||||
valueNode = p
|
if len(p.Content) > 0 {
|
||||||
|
valueNode = p
|
||||||
|
} else {
|
||||||
|
return parent
|
||||||
|
}
|
||||||
|
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
if vo.IsNil() {
|
if vo.IsNil() {
|
||||||
@@ -383,6 +424,24 @@ func CreateBoolNode(str string) *yaml.Node {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CreateIntNode(str string) *yaml.Node {
|
||||||
|
n := &yaml.Node{
|
||||||
|
Kind: yaml.ScalarNode,
|
||||||
|
Tag: "!!int",
|
||||||
|
Value: str,
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateFloatNode(str string) *yaml.Node {
|
||||||
|
n := &yaml.Node{
|
||||||
|
Kind: yaml.ScalarNode,
|
||||||
|
Tag: "!!float",
|
||||||
|
Value: str,
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
type Renderable interface {
|
type Renderable interface {
|
||||||
MarshalYAML() (interface{}, error)
|
MarshalYAML() (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,26 +22,26 @@ type Document struct {
|
|||||||
|
|
||||||
// Version is the version of OpenAPI being used, extracted from the 'openapi: x.x.x' definition.
|
// Version is the version of OpenAPI being used, extracted from the 'openapi: x.x.x' definition.
|
||||||
// This is not a standard property of the OpenAPI model, it's a convenience mechanism only.
|
// This is not a standard property of the OpenAPI model, it's a convenience mechanism only.
|
||||||
Version string
|
Version string `json:"openapi,omitempty" yaml:"openapi,omitempty"`
|
||||||
|
|
||||||
// Info represents a specification Info definitions
|
// Info represents a specification Info definitions
|
||||||
// Provides metadata about the API. The metadata MAY be used by tooling as required.
|
// Provides metadata about the API. The metadata MAY be used by tooling as required.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#info-object
|
// - https://spec.openapis.org/oas/v3.1.0#info-object
|
||||||
Info *base.Info
|
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 a 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
|
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
|
Paths *Paths `json:"-" yaml:"-"`
|
||||||
|
|
||||||
// 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
|
Components *Components `json:"-" yaml:"-"`
|
||||||
|
|
||||||
// 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
|
||||||
@@ -49,7 +49,7 @@ type Document struct {
|
|||||||
// to authorize a request. Individual operations can override this definition. To make security optional,
|
// to authorize a request. Individual operations can override this definition. To make security optional,
|
||||||
// an empty security requirement ({}) can be included in the array.
|
// an empty security requirement ({}) can be included in the array.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
|
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
|
||||||
Security []*base.SecurityRequirement
|
Security []*base.SecurityRequirement `json:"security,omitempty" yaml:"security,omitempty"`
|
||||||
|
|
||||||
// Tags is a slice of base.Tag instances defined by the specification
|
// Tags is a slice of base.Tag instances defined by the specification
|
||||||
// A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on
|
// A list of tags used by the document with additional metadata. The order of the tags can be used to reflect on
|
||||||
@@ -57,20 +57,20 @@ type Document struct {
|
|||||||
// The tags that are not declared MAY be organized randomly or based on the tools’ logic.
|
// The tags that are not declared MAY be organized randomly or based on the tools’ logic.
|
||||||
// Each tag name in the list MUST be unique.
|
// Each tag name in the list MUST be unique.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#tag-object
|
// - https://spec.openapis.org/oas/v3.1.0#tag-object
|
||||||
Tags []*base.Tag
|
Tags []*base.Tag `json:"tags,omitempty" yaml:"tags,omitempty"`
|
||||||
|
|
||||||
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit.
|
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
// - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
||||||
ExternalDocs *base.ExternalDoc
|
ExternalDocs *base.ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
|
||||||
|
|
||||||
// Extensions contains all custom extensions defined for the top-level document.
|
// Extensions contains all custom extensions defined for the top-level document.
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
|
|
||||||
// JsonSchemaDialect is a 3.1+ property that sets the dialect to use for validating *base.Schema definitions
|
// JsonSchemaDialect is a 3.1+ property that sets the dialect to use for validating *base.Schema definitions
|
||||||
// The default value for the $schema keyword within Schema Objects contained within this OAS document.
|
// The default value for the $schema keyword within Schema Objects contained within this OAS document.
|
||||||
// This MUST be in the form of a URI.
|
// This MUST be in the form of a URI.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#schema-object
|
// - https://spec.openapis.org/oas/v3.1.0#schema-object
|
||||||
JsonSchemaDialect string
|
JsonSchemaDialect string `json:"$schema,omitempty" yaml:"$schema,omitempty"`
|
||||||
|
|
||||||
// Webhooks is a 3.1+ property that is similar to callbacks, except, this defines incoming webhooks.
|
// Webhooks is a 3.1+ property that is similar to callbacks, except, this defines incoming webhooks.
|
||||||
// The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement.
|
// The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement.
|
||||||
@@ -78,15 +78,15 @@ 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
|
Webhooks map[string]*PathItem `json:"-" yaml:"-"`
|
||||||
|
|
||||||
// Index is a reference to the *index.SpecIndex that was created for the document and used
|
// Index is a reference to the *index.SpecIndex that was created for the document and used
|
||||||
// as a guide when building out the Document. Ideal if further processing is required on the model and
|
// as a guide when building out the Document. Ideal if further processing is required on the model and
|
||||||
// the original details are required to continue the work.
|
// the original details are required to continue the work.
|
||||||
//
|
//
|
||||||
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
|
||||||
Index *index.SpecIndex
|
Index *index.SpecIndex `json:"-" yaml:"-"`
|
||||||
low *low.Document
|
low *low.Document `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -154,18 +154,9 @@ func (d *Document) Render() ([]byte, error) {
|
|||||||
|
|
||||||
// MarshalYAML will create a ready to render YAML representation of the Document object.
|
// MarshalYAML will create a ready to render YAML representation of the Document object.
|
||||||
func (d *Document) MarshalYAML() (interface{}, error) {
|
func (d *Document) MarshalYAML() (interface{}, error) {
|
||||||
n := high.CreateEmptyMapNode()
|
if d == nil {
|
||||||
//high.AddYAMLNode(n, low.SchemaDialectLabel, d.JsonSchemaDialect)
|
return nil, nil
|
||||||
//high.AddYAMLNode(n, low.OpenAPILabel, d.Version)
|
}
|
||||||
//high.AddYAMLNode(n, low.InfoLabel, d.Info)
|
nb := high.NewNodeBuilder(d, d.low)
|
||||||
//high.AddYAMLNode(n, low.TagsLabel, d.Tags)
|
return nb.Render(), nil
|
||||||
//high.AddYAMLNode(n, low.ServersLabel, d.Servers)
|
|
||||||
//high.AddYAMLNode(n, low.SecurityLabel, d.Security)
|
|
||||||
//high.AddYAMLNode(n, low.ServersLabel, d.Servers)
|
|
||||||
//high.AddYAMLNode(n, low.ExternalDocsLabel, d.ExternalDocs)
|
|
||||||
//high.AddYAMLNode(n, low.PathsLabel, d.Paths)
|
|
||||||
//high.AddYAMLNode(n, low.ComponentsLabel, d.Components)
|
|
||||||
//high.AddYAMLNode(n, low.WebhooksLabel, d.Webhooks)
|
|
||||||
//high.MarshalExtensions(n, d.Extensions)
|
|
||||||
return n, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -485,9 +485,10 @@ func TestDocument_MarshalYAML(t *testing.T) {
|
|||||||
r, _ := h.Render()
|
r, _ := h.Render()
|
||||||
|
|
||||||
info, _ := datamodel.ExtractSpecInfo(r)
|
info, _ := datamodel.ExtractSpecInfo(r)
|
||||||
lowDoc, _ = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration())
|
lDoc, e := lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration())
|
||||||
highDoc := NewDocument(lowDoc)
|
assert.Nil(t, e)
|
||||||
|
|
||||||
|
highDoc := NewDocument(lDoc)
|
||||||
assert.Equal(t, "3.1.0", highDoc.Version)
|
assert.Equal(t, "3.1.0", highDoc.Version)
|
||||||
|
|
||||||
// TODO: COMPLETE THIS
|
// TODO: COMPLETE THIS
|
||||||
|
|||||||
@@ -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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Link represents a high-level OpenAPI 3+ Link object that is backed by a low-level one.
|
// Link represents a high-level OpenAPI 3+ Link object that is backed by a low-level one.
|
||||||
@@ -21,13 +22,13 @@ import (
|
|||||||
// in an operation and using them as parameters while invoking the linked operation.
|
// in an operation and using them as parameters while invoking the linked operation.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
// - https://spec.openapis.org/oas/v3.1.0#link-object
|
||||||
type Link struct {
|
type Link struct {
|
||||||
OperationRef string
|
OperationRef string `json:"operationRef,omitempty" yaml:"operationRef,omitempty"`
|
||||||
OperationId string
|
OperationId string `json:"operationId,omitempty" yaml:"operationId,omitempty"`
|
||||||
Parameters map[string]string
|
Parameters map[string]string `json:"parameters,omitempty" yaml:"parameters,omitempty"`
|
||||||
RequestBody string
|
RequestBody string `json:"requestBody,omitempty" yaml:"requestBody,omitempty"`
|
||||||
Description string
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Server *Server
|
Server *Server `json:"server,omitempty" yaml:"server,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.Link
|
low *low.Link
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,3 +56,17 @@ func NewLink(link *low.Link) *Link {
|
|||||||
func (l *Link) GoLow() *low.Link {
|
func (l *Link) GoLow() *low.Link {
|
||||||
return l.low
|
return l.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the Link object as a byte slice.
|
||||||
|
func (l *Link) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the Link object.
|
||||||
|
func (l *Link) MarshalYAML() (interface{}, error) {
|
||||||
|
if l == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(l, l.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
37
datamodel/high/v3/link_test.go
Normal file
37
datamodel/high/v3/link_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLink_MarshalYAML(t *testing.T) {
|
||||||
|
link := Link{
|
||||||
|
OperationRef: "somewhere",
|
||||||
|
OperationId: "somewhereOutThere",
|
||||||
|
Parameters: map[string]string{
|
||||||
|
"over": "theRainbow",
|
||||||
|
},
|
||||||
|
RequestBody: "hello?",
|
||||||
|
Description: "are you there?",
|
||||||
|
Server: &Server{
|
||||||
|
URL: "https://pb33f.io",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
dat, _ := link.Render()
|
||||||
|
desired := `operationRef: somewhere
|
||||||
|
operationId: somewhereOutThere
|
||||||
|
parameters:
|
||||||
|
over: theRainbow
|
||||||
|
requestBody: hello?
|
||||||
|
description: are you there?
|
||||||
|
server:
|
||||||
|
url: https://pb33f.io`
|
||||||
|
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(dat)))
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"gopkg.in/yaml.v3"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -16,11 +17,11 @@ import (
|
|||||||
// Each Media Type Object provides schema and examples for the media type identified by its key.
|
// Each Media Type Object provides schema and examples for the media type identified by its key.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
|
||||||
type MediaType struct {
|
type MediaType struct {
|
||||||
Schema *base.SchemaProxy
|
Schema *base.SchemaProxy `json:"schema,omitempty" yaml:"schema,omitempty"`
|
||||||
Example any
|
Example any `json:"example,omitempty" yaml:"example,omitempty"`
|
||||||
Examples map[string]*base.Example
|
Examples map[string]*base.Example `json:"examples,omitempty" yaml:"examples,omitempty"`
|
||||||
Encoding map[string]*Encoding
|
Encoding map[string]*Encoding `json:"encoding,omitempty" yaml:"encoding,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.MediaType
|
low *low.MediaType
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +44,20 @@ func (m *MediaType) GoLow() *low.MediaType {
|
|||||||
return m.low
|
return m.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the MediaType object as a byte slice.
|
||||||
|
func (m *MediaType) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the MediaType object.
|
||||||
|
func (m *MediaType) MarshalYAML() (interface{}, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(m, m.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ExtractContent takes in a complex and hard to navigate low-level content map, and converts it in to a much simpler
|
// ExtractContent takes in a complex and hard to navigate low-level content map, and converts it in to a much simpler
|
||||||
// and easier to navigate high-level one.
|
// and easier to navigate high-level one.
|
||||||
func ExtractContent(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.MediaType]) map[string]*MediaType {
|
func ExtractContent(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.MediaType]) map[string]*MediaType {
|
||||||
|
|||||||
49
datamodel/high/v3/media_type_test.go
Normal file
49
datamodel/high/v3/media_type_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
|
"github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMediaType_MarshalYAML(t *testing.T) {
|
||||||
|
// load the petstore spec
|
||||||
|
data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json")
|
||||||
|
info, _ := datamodel.ExtractSpecInfo(data)
|
||||||
|
var err []error
|
||||||
|
lowDoc, err = v3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{})
|
||||||
|
if err != nil {
|
||||||
|
panic("broken something")
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new document and extract a media type object from it.
|
||||||
|
d := NewDocument(lowDoc)
|
||||||
|
mt := d.Paths.PathItems["/pet"].Put.RequestBody.Content["application/json"]
|
||||||
|
|
||||||
|
// render out the media type
|
||||||
|
yml, _ := mt.Render()
|
||||||
|
|
||||||
|
// the rendered output should be a ref to the media type.
|
||||||
|
op := `schema:
|
||||||
|
$ref: '#/components/schemas/Pet'`
|
||||||
|
|
||||||
|
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||||
|
|
||||||
|
// modify the media type to have an example
|
||||||
|
mt.Example = "testing a nice mutation"
|
||||||
|
|
||||||
|
op = `example: testing a nice mutation
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/Pet'`
|
||||||
|
|
||||||
|
yml, _ = mt.Render()
|
||||||
|
|
||||||
|
assert.Equal(t, op, strings.TrimSpace(string(yml)))
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,16 +6,17 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
|
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
|
||||||
type OAuthFlow struct {
|
type OAuthFlow struct {
|
||||||
AuthorizationUrl string
|
AuthorizationUrl string `json:"authorizationUrl,omitempty" yaml:"authorizationUrl,omitempty"`
|
||||||
TokenUrl string
|
TokenUrl string `json:"tokenUrl,omitempty" yaml:"tokenUrl,omitempty"`
|
||||||
RefreshUrl string
|
RefreshUrl string `json:"refreshUrl,omitempty" yaml:"refreshUrl,omitempty"`
|
||||||
Scopes map[string]string
|
Scopes map[string]string `json:"scopes,omitempty" yaml:"scopes,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.OAuthFlow
|
low *low.OAuthFlow
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,3 +40,17 @@ func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
|
|||||||
func (o *OAuthFlow) GoLow() *low.OAuthFlow {
|
func (o *OAuthFlow) GoLow() *low.OAuthFlow {
|
||||||
return o.low
|
return o.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the OAuthFlow object as a byte slice.
|
||||||
|
func (o *OAuthFlow) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(o)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the OAuthFlow object.
|
||||||
|
func (o *OAuthFlow) MarshalYAML() (interface{}, error) {
|
||||||
|
if o == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(o, o.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
|
|||||||
44
datamodel/high/v3/oauth_flow_test.go
Normal file
44
datamodel/high/v3/oauth_flow_test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOAuthFlow_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
|
oflow := &OAuthFlow{
|
||||||
|
AuthorizationUrl: "https://pb33f.io",
|
||||||
|
TokenUrl: "https://pb33f.io/token",
|
||||||
|
RefreshUrl: "https://pb33f.io/refresh",
|
||||||
|
Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"},
|
||||||
|
}
|
||||||
|
|
||||||
|
rend, _ := oflow.Render()
|
||||||
|
|
||||||
|
desired := `authorizationUrl: https://pb33f.io
|
||||||
|
tokenUrl: https://pb33f.io/token
|
||||||
|
refreshUrl: https://pb33f.io/refresh
|
||||||
|
scopes:
|
||||||
|
chicken: nuggets
|
||||||
|
beefy: soup`
|
||||||
|
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
|
// mutate
|
||||||
|
oflow.Scopes = nil
|
||||||
|
oflow.Extensions = map[string]interface{}{"x-burgers": "why not?"}
|
||||||
|
|
||||||
|
desired = `authorizationUrl: https://pb33f.io
|
||||||
|
tokenUrl: https://pb33f.io/token
|
||||||
|
refreshUrl: https://pb33f.io/refresh
|
||||||
|
x-burgers: why not?`
|
||||||
|
|
||||||
|
rend, _ = oflow.Render()
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
|
}
|
||||||
@@ -6,15 +6,16 @@ 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"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server represents a high-level OpenAPI 3+ Server object, that is backed by a low level one.
|
// 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 {
|
type Server struct {
|
||||||
URL string
|
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||||
Description string
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
Variables map[string]*ServerVariable
|
Variables map[string]*ServerVariable `json:"variables,omitempty" yaml:"variables,omitempty"`
|
||||||
Extensions map[string]any
|
Extensions map[string]any `json:"-" yaml:"-"`
|
||||||
low *low.Server
|
low *low.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,3 +38,17 @@ func NewServer(server *low.Server) *Server {
|
|||||||
func (s *Server) GoLow() *low.Server {
|
func (s *Server) GoLow() *low.Server {
|
||||||
return s.low
|
return s.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the Server object as a byte slice.
|
||||||
|
func (s *Server) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the Server object.
|
||||||
|
func (s *Server) MarshalYAML() (interface{}, error) {
|
||||||
|
if s == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
43
datamodel/high/v3/server_test.go
Normal file
43
datamodel/high/v3/server_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServer_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
|
server := &Server{
|
||||||
|
URL: "https://pb33f.io",
|
||||||
|
Description: "the b33f",
|
||||||
|
}
|
||||||
|
|
||||||
|
desired := `url: https://pb33f.io
|
||||||
|
description: the b33f`
|
||||||
|
|
||||||
|
rend, _ := server.Render()
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
|
// mutate
|
||||||
|
server.Variables = map[string]*ServerVariable{
|
||||||
|
"rainbow": {
|
||||||
|
Enum: []string{"one", "two", "three"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
desired = `url: https://pb33f.io
|
||||||
|
description: the b33f
|
||||||
|
variables:
|
||||||
|
rainbow:
|
||||||
|
enum:
|
||||||
|
- one
|
||||||
|
- two
|
||||||
|
- three`
|
||||||
|
|
||||||
|
rend, _ = server.Render()
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(rend)))
|
||||||
|
}
|
||||||
@@ -3,16 +3,20 @@
|
|||||||
|
|
||||||
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"
|
||||||
|
)
|
||||||
|
|
||||||
// ServerVariable represents a high-level OpenAPI 3+ ServerVariable object, that is backed by a low-level one.
|
// ServerVariable represents a high-level OpenAPI 3+ ServerVariable object, that is backed by a low-level one.
|
||||||
//
|
//
|
||||||
// ServerVariable is an object representing a Server Variable for server URL template substitution.
|
// ServerVariable is an object representing a Server Variable for server URL template substitution.
|
||||||
// - https://spec.openapis.org/oas/v3.1.0#server-variable-object
|
// - https://spec.openapis.org/oas/v3.1.0#server-variable-object
|
||||||
type ServerVariable struct {
|
type ServerVariable struct {
|
||||||
Enum []string
|
Enum []string `json:"enum,omitempty" yaml:"enum,omitempty"`
|
||||||
Default string
|
Default string `json:"default,omitempty" yaml:"default,omitempty"`
|
||||||
Description string
|
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||||
low *low.ServerVariable
|
low *low.ServerVariable
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +36,21 @@ func NewServerVariable(variable *low.ServerVariable) *ServerVariable {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoLow returns the low-level ServerVariable used to to create the high\-level one.
|
// GoLow returns the low-level ServerVariable used to create the high\-level one.
|
||||||
func (s *ServerVariable) GoLow() *low.ServerVariable {
|
func (s *ServerVariable) GoLow() *low.ServerVariable {
|
||||||
return s.low
|
return s.low
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Render will return a YAML representation of the ServerVariable object as a byte slice.
|
||||||
|
func (s *ServerVariable) Render() ([]byte, error) {
|
||||||
|
return yaml.Marshal(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalYAML will create a ready to render YAML representation of the ServerVariable object.
|
||||||
|
func (s *ServerVariable) MarshalYAML() (interface{}, error) {
|
||||||
|
if s == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
nb := high.NewNodeBuilder(s, s.low)
|
||||||
|
return nb.Render(), nil
|
||||||
|
}
|
||||||
43
datamodel/high/v3/server_variable_test.go
Normal file
43
datamodel/high/v3/server_variable_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package v3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerVariable_MarshalYAML(t *testing.T) {
|
||||||
|
|
||||||
|
svar := &ServerVariable{
|
||||||
|
Enum: []string{"one", "two", "three"},
|
||||||
|
Description: "money day",
|
||||||
|
}
|
||||||
|
|
||||||
|
desired := `enum:
|
||||||
|
- one
|
||||||
|
- two
|
||||||
|
- three
|
||||||
|
description: money day`
|
||||||
|
|
||||||
|
svarRend, _ := svar.Render()
|
||||||
|
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
|
||||||
|
|
||||||
|
// mutate
|
||||||
|
|
||||||
|
svar.Default = "is moments away"
|
||||||
|
|
||||||
|
desired = `enum:
|
||||||
|
- one
|
||||||
|
- two
|
||||||
|
- three
|
||||||
|
default: is moments away
|
||||||
|
description: money day`
|
||||||
|
|
||||||
|
svarRend, _ = svar.Render()
|
||||||
|
|
||||||
|
assert.Equal(t, desired, strings.TrimSpace(string(svarRend)))
|
||||||
|
}
|
||||||
@@ -424,8 +424,6 @@ func (index *SpecIndex) checkPolymorphicNode(name string) (bool, string) {
|
|||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GetPathCount will return the number of paths found in the spec
|
// GetPathCount will return the number of paths found in the spec
|
||||||
func (index *SpecIndex) GetPathCount() int {
|
func (index *SpecIndex) GetPathCount() int {
|
||||||
if index.root == nil {
|
if index.root == nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user