mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 12:37:48 +00:00
Working through mutation designs.
trying out some sketches to get the APIs correct.
This commit is contained in:
@@ -25,3 +25,17 @@ type DocumentConfiguration struct {
|
||||
// AllowRemoteReferences will allow the index to lookup remote references. This is disabled by default.
|
||||
AllowRemoteReferences bool
|
||||
}
|
||||
|
||||
func NewOpenDocumentConfiguration() *DocumentConfiguration {
|
||||
return &DocumentConfiguration{
|
||||
AllowFileReferences: true,
|
||||
AllowRemoteReferences: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewClosedDocumentConfiguration() *DocumentConfiguration {
|
||||
return &DocumentConfiguration{
|
||||
AllowFileReferences: false,
|
||||
AllowRemoteReferences: false,
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,19 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Contact represents a high-level representation of the Contact definitions found at
|
||||
// v2 - https://swagger.io/specification/v2/#contactObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
|
||||
type Contact struct {
|
||||
Name string
|
||||
URL string
|
||||
Email string
|
||||
low *low.Contact
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
Email string `json:"email,omitempty" yaml:"email,omitempty"`
|
||||
low *low.Contact `json:"-" yaml:"-"` // low-level representation
|
||||
}
|
||||
|
||||
// NewContact will create a new Contact instance using a low-level Contact
|
||||
@@ -31,3 +33,19 @@ func NewContact(contact *low.Contact) *Contact {
|
||||
func (c *Contact) GoLow() *low.Contact {
|
||||
return c.low
|
||||
}
|
||||
|
||||
func (c *Contact) Render() ([]byte, error) {
|
||||
return yaml.Marshal(c)
|
||||
}
|
||||
|
||||
func (c *Contact) MarshalYAML() (interface{}, error) {
|
||||
if c == nil {
|
||||
return nil, nil
|
||||
}
|
||||
n := high.CreateEmptyMapNode()
|
||||
high.AddYAMLNode(n, low.NameLabel, c.Name)
|
||||
high.AddYAMLNode(n, low.URLLabel, c.URL)
|
||||
high.AddYAMLNode(n, low.EmailLabel, c.Email)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -56,3 +56,25 @@ email: buckaroo@pb33f.io`
|
||||
fmt.Print(highContact.Name)
|
||||
// Output: Buckaroo
|
||||
}
|
||||
|
||||
func TestContact_MarshalYAML(t *testing.T) {
|
||||
|
||||
highC := &Contact{Name: "dave", URL: "https://pb33f.io", Email: "dave@pb33f.io"}
|
||||
dat, _ := highC.Render()
|
||||
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &cNode)
|
||||
|
||||
// build low
|
||||
var lowContact lowbase.Contact
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowContact)
|
||||
|
||||
// build high
|
||||
highContact := NewContact(&lowContact)
|
||||
|
||||
assert.Equal(t, "dave", highContact.Name)
|
||||
assert.Equal(t, "dave@pb33f.io", highContact.Email)
|
||||
assert.Equal(t, "https://pb33f.io", highContact.URL)
|
||||
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package base
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Info represents a high-level Info object as defined by both OpenAPI 2 and OpenAPI 3.
|
||||
@@ -16,13 +17,13 @@ import (
|
||||
// v2 - https://swagger.io/specification/v2/#infoObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#info-object
|
||||
type Info struct {
|
||||
Title string
|
||||
Summary string
|
||||
Description string
|
||||
TermsOfService string
|
||||
Contact *Contact
|
||||
License *License
|
||||
Version string
|
||||
Summary string `json:"summary,omitempty" yaml:"summary,omitempty"`
|
||||
Title string `json:"title,omitempty" yaml:"title,omitempty"`
|
||||
Description string `json:"description,omitempty" yaml:"description,omitempty"`
|
||||
TermsOfService string `json:"termsOfService,omitempty" yaml:"termsOfService,omitempty"`
|
||||
Contact *Contact `json:"contact,omitempty" yaml:"contact,omitempty"`
|
||||
License *License `json:"license,omitempty" yaml:"license,omitempty"`
|
||||
Version string `json:"version,omitempty" yaml:"version,omitempty"`
|
||||
Extensions map[string]any
|
||||
low *low.Info
|
||||
}
|
||||
@@ -62,3 +63,24 @@ func NewInfo(info *low.Info) *Info {
|
||||
func (i *Info) GoLow() *low.Info {
|
||||
return i.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Info object as a byte slice.
|
||||
func (i *Info) Render() ([]byte, error) {
|
||||
return yaml.Marshal(i)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Info object.
|
||||
func (i *Info) MarshalYAML() (interface{}, error) {
|
||||
if i == nil {
|
||||
return nil, nil
|
||||
}
|
||||
n := high.CreateEmptyMapNode()
|
||||
high.AddYAMLNode(n, low.TitleLabel, i.Title)
|
||||
high.AddYAMLNode(n, low.DescriptionLabel, i.Description)
|
||||
high.AddYAMLNode(n, low.TermsOfServiceLabel, i.TermsOfService)
|
||||
high.AddYAMLNode(n, low.ContactLabel, i.Contact)
|
||||
high.AddYAMLNode(n, low.LicenseLabel, i.License)
|
||||
high.AddYAMLNode(n, low.VersionLabel, i.Version)
|
||||
high.MarshalExtensions(n, i.Extensions)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
@@ -106,3 +106,50 @@ url: https://opensource.org/licenses/MIT`
|
||||
fmt.Print(highLicense.Name)
|
||||
// Output: MIT
|
||||
}
|
||||
|
||||
func TestInfo_Render(t *testing.T) {
|
||||
|
||||
ext := make(map[string]any)
|
||||
|
||||
ext["x-pizza"] = "pepperoni"
|
||||
|
||||
highI := &Info{
|
||||
Title: "hey",
|
||||
Description: "there you",
|
||||
TermsOfService: "have you got any money",
|
||||
Contact: &Contact{
|
||||
Name: "buckaroo",
|
||||
Email: "buckaroo@pb33f.io",
|
||||
},
|
||||
License: &License{
|
||||
Name: "MIT",
|
||||
URL: "https://opensource.org/licenses/MIT",
|
||||
},
|
||||
Version: "1.2.3",
|
||||
Extensions: ext,
|
||||
}
|
||||
|
||||
dat, _ := highI.Render()
|
||||
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &cNode)
|
||||
|
||||
// build low
|
||||
var lowInfo lowbase.Info
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowInfo)
|
||||
_ = lowInfo.Build(cNode.Content[0], nil)
|
||||
|
||||
// build high
|
||||
highInfo := NewInfo(&lowInfo)
|
||||
|
||||
assert.Equal(t, "hey", highInfo.Title)
|
||||
assert.Equal(t, "there you", highInfo.Description)
|
||||
assert.Equal(t, "have you got any money", highInfo.TermsOfService)
|
||||
assert.Equal(t, "buckaroo", highInfo.Contact.Name)
|
||||
assert.Equal(t, "buckaroo@pb33f.io", highInfo.Contact.Email)
|
||||
assert.Equal(t, "MIT", highInfo.License.Name)
|
||||
assert.Equal(t, "https://opensource.org/licenses/MIT", highInfo.License.URL)
|
||||
assert.Equal(t, "1.2.3", highInfo.Version)
|
||||
assert.Equal(t, "pepperoni", highInfo.Extensions["x-pizza"])
|
||||
}
|
||||
33
datamodel/high/base/licence_test.go
Normal file
33
datamodel/high/base/licence_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package base
|
||||
|
||||
import (
|
||||
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
|
||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/yaml.v3"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLicense_Render(t *testing.T) {
|
||||
|
||||
highL := &License{Name: "MIT", URL: "https://pb33f.io"}
|
||||
dat, _ := highL.Render()
|
||||
|
||||
// unmarshal yaml into a *yaml.Node instance
|
||||
var cNode yaml.Node
|
||||
_ = yaml.Unmarshal(dat, &cNode)
|
||||
|
||||
// build low
|
||||
var lowLicense lowbase.License
|
||||
_ = lowmodel.BuildModel(cNode.Content[0], &lowLicense)
|
||||
|
||||
// build high
|
||||
highLicense := NewLicense(&lowLicense)
|
||||
|
||||
assert.Equal(t, "MIT", highLicense.Name)
|
||||
assert.Equal(t, "https://pb33f.io", highLicense.URL)
|
||||
|
||||
}
|
||||
@@ -4,15 +4,17 @@
|
||||
package base
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// License is a high-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
|
||||
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||
type License struct {
|
||||
Name string
|
||||
URL string
|
||||
Name string `json:"name,omitempty" yaml:"name,omitempty"`
|
||||
URL string `json:"url,omitempty" yaml:"url,omitempty"`
|
||||
low *low.License
|
||||
}
|
||||
|
||||
@@ -33,3 +35,19 @@ func NewLicense(license *low.License) *License {
|
||||
func (l *License) GoLow() *low.License {
|
||||
return l.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the License object as a byte slice.
|
||||
func (l *License) Render() ([]byte, error) {
|
||||
return yaml.Marshal(l)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the License object.
|
||||
func (l *License) MarshalYAML() (interface{}, error) {
|
||||
if l == nil {
|
||||
return nil, nil
|
||||
}
|
||||
n := high.CreateEmptyMapNode()
|
||||
high.AddYAMLNode(n, low.NameLabel, l.Name)
|
||||
high.AddYAMLNode(n, low.URLLabel, l.URL)
|
||||
return n, nil
|
||||
}
|
||||
|
||||
@@ -13,7 +13,12 @@
|
||||
// those models, things like key/value breakdown of each value, lines, column, source comments etc.
|
||||
package high
|
||||
|
||||
import "github.com/pb33f/libopenapi/datamodel/low"
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"gopkg.in/yaml.v3"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// GoesLow is used to represent any high-level model. All high level models meet this interface and can be used to
|
||||
// extract low-level models from any high-level model.
|
||||
@@ -63,4 +68,75 @@ func UnpackExtensions[T any, R low.HasExtensions[T]](low GoesLow[R]) (map[string
|
||||
m[key] = g
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalExtensions is a convenience function that makes it easy and simple to marshal an objects extensions into a
|
||||
// map that can then correctly rendered back down in to YAML.
|
||||
func MarshalExtensions(parent *yaml.Node, extensions map[string]any) {
|
||||
for k := range extensions {
|
||||
AddYAMLNode(parent, k, extensions[k])
|
||||
}
|
||||
}
|
||||
|
||||
func AddYAMLNode(parent *yaml.Node, key string, value any) *yaml.Node {
|
||||
|
||||
if value == nil {
|
||||
return parent
|
||||
}
|
||||
|
||||
// check the type
|
||||
t := reflect.TypeOf(value)
|
||||
l := CreateStringNode(key)
|
||||
var valueNode *yaml.Node
|
||||
switch t.Kind() {
|
||||
case reflect.String:
|
||||
if value.(string) == "" {
|
||||
return parent
|
||||
}
|
||||
valueNode = CreateStringNode(value.(string))
|
||||
case reflect.Int:
|
||||
valueNode = CreateIntNode(value.(int))
|
||||
case reflect.Struct:
|
||||
panic("no way dude")
|
||||
case reflect.Ptr:
|
||||
rawRender, _ := value.(Renderable).MarshalYAML()
|
||||
if rawRender != nil {
|
||||
valueNode = rawRender.(*yaml.Node)
|
||||
} else {
|
||||
return parent
|
||||
}
|
||||
}
|
||||
parent.Content = append(parent.Content, l, valueNode)
|
||||
return parent
|
||||
}
|
||||
|
||||
func CreateEmptyMapNode() *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.MappingNode,
|
||||
Tag: "!!map",
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateStringNode(str string) *yaml.Node {
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!str",
|
||||
Value: str,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func CreateIntNode(val int) *yaml.Node {
|
||||
i := strconv.Itoa(val)
|
||||
n := &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Tag: "!!int",
|
||||
Value: i,
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
type Renderable interface {
|
||||
MarshalYAML() (interface{}, error)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Document represents a high-level OpenAPI 3 document (both 3.0 & 3.1). A Document is the root of the specification.
|
||||
@@ -121,27 +122,50 @@ func NewDocument(document *low.Document) *Document {
|
||||
if !document.Paths.IsEmpty() {
|
||||
d.Paths = NewPaths(document.Paths.Value)
|
||||
}
|
||||
if !document.JsonSchemaDialect.IsEmpty() {
|
||||
d.JsonSchemaDialect = document.JsonSchemaDialect.Value
|
||||
}
|
||||
if !document.Webhooks.IsEmpty() {
|
||||
hooks := make(map[string]*PathItem)
|
||||
for h := range document.Webhooks.Value {
|
||||
hooks[h.Value] = NewPathItem(document.Webhooks.Value[h].Value)
|
||||
}
|
||||
d.Webhooks = hooks
|
||||
}
|
||||
if !document.Security.IsEmpty() {
|
||||
var security []*base.SecurityRequirement
|
||||
for s := range document.Security.Value {
|
||||
security = append(security, base.NewSecurityRequirement(document.Security.Value[s].Value))
|
||||
}
|
||||
d.Security = security
|
||||
}
|
||||
return d
|
||||
if !document.JsonSchemaDialect.IsEmpty() {
|
||||
d.JsonSchemaDialect = document.JsonSchemaDialect.Value
|
||||
}
|
||||
if !document.Webhooks.IsEmpty() {
|
||||
hooks := make(map[string]*PathItem)
|
||||
for h := range document.Webhooks.Value {
|
||||
hooks[h.Value] = NewPathItem(document.Webhooks.Value[h].Value)
|
||||
}
|
||||
d.Webhooks = hooks
|
||||
}
|
||||
if !document.Security.IsEmpty() {
|
||||
var security []*base.SecurityRequirement
|
||||
for s := range document.Security.Value {
|
||||
security = append(security, base.NewSecurityRequirement(document.Security.Value[s].Value))
|
||||
}
|
||||
d.Security = security
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// GoLow returns the low-level Document that was used to create the high level one.
|
||||
func (d *Document) GoLow() *low.Document {
|
||||
return d.low
|
||||
}
|
||||
|
||||
// Render will return a YAML representation of the Document object as a byte slice.
|
||||
func (d *Document) Render() ([]byte, error) {
|
||||
return yaml.Marshal(d)
|
||||
}
|
||||
|
||||
// MarshalYAML will create a ready to render YAML representation of the Document object.
|
||||
func (d *Document) MarshalYAML() (interface{}, error) {
|
||||
n := high.CreateEmptyMapNode()
|
||||
high.AddYAMLNode(n, low.SchemaDialectLabel, d.JsonSchemaDialect)
|
||||
high.AddYAMLNode(n, low.OpenAPILabel, d.Version)
|
||||
high.AddYAMLNode(n, low.InfoLabel, d.Info)
|
||||
//high.AddYAMLNode(n, low.TagsLabel, d.Tags)
|
||||
//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
|
||||
}
|
||||
|
||||
@@ -473,3 +473,25 @@ func TestCircularReferencesDoc(t *testing.T) {
|
||||
assert.Len(t, d.Components.Schemas, 9)
|
||||
assert.Len(t, d.Index.GetCircularReferences(), 3)
|
||||
}
|
||||
|
||||
func TestDocument_MarshalYAML(t *testing.T) {
|
||||
|
||||
// create a new document
|
||||
initTest()
|
||||
h := NewDocument(lowDoc)
|
||||
|
||||
// render the document to YAML
|
||||
|
||||
r, _ := h.Render()
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo(r)
|
||||
lowDoc, _ = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration())
|
||||
highDoc := NewDocument(lowDoc)
|
||||
|
||||
assert.Equal(t, "3.1.0", highDoc.Version)
|
||||
|
||||
// TODO: COMPLETE THIS
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,6 +5,13 @@ package base
|
||||
|
||||
// Constants for labels used to look up values within OpenAPI specifications.
|
||||
const (
|
||||
VersionLabel = "version"
|
||||
TermsOfServiceLabel = "termsOfService"
|
||||
DescriptionLabel = "description"
|
||||
TitleLabel = "title"
|
||||
EmailLabel = "email"
|
||||
NameLabel = "name"
|
||||
URLLabel = "url"
|
||||
TagsLabel = "tags"
|
||||
ExternalDocsLabel = "externalDocs"
|
||||
ExamplesLabel = "examples"
|
||||
|
||||
@@ -40,3 +40,4 @@ func (c *Contact) Hash() [32]byte {
|
||||
}
|
||||
return sha256.Sum256([]byte(strings.Join(f, "|")))
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ type HasExtensions[T any] interface {
|
||||
type HasValue[T any] interface {
|
||||
GetValue() T
|
||||
GetValueNode() *yaml.Node
|
||||
IsEmpty() bool
|
||||
*T
|
||||
}
|
||||
|
||||
|
||||
4
datamodel/low/serializing.go
Normal file
4
datamodel/low/serializing.go
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright 2023 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package low
|
||||
64
document.go
64
document.go
@@ -14,6 +14,7 @@
|
||||
package libopenapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
@@ -55,19 +56,39 @@ type Document interface {
|
||||
// any other types.
|
||||
BuildV3Model() (*DocumentModel[v3high.Document], []error)
|
||||
|
||||
// RenderAndReload will render the high level model as it currently exists (including any mutations, additions
|
||||
// and removals to and from any object in the tree). It will then reload the low level model with the new bytes
|
||||
// extracted from the model that was re-rendered. This is useful if you want to make changes to the high level model
|
||||
// and then 'reload' the model into memory, so that line numbers and column numbers are correct and all update
|
||||
// according to the changes made.
|
||||
//
|
||||
// The method returns the raw YAML bytes that were rendered, and any errors that occurred during rebuilding of the model.
|
||||
// This is a destructive operation, and will re-build the entire model from scratch using the new bytes, so any
|
||||
// references to the old model will be lost. The second return is the new Document that was created, and the third
|
||||
// return is any errors hit trying to re-render.
|
||||
//
|
||||
// **IMPORTANT** This method only supports OpenAPI Documents. The Swagger model will not support mutations correctly
|
||||
// and will not update when called. This choice has been made because we don't want to continue supporting Swagger,
|
||||
// it's too old, so it should be motivation to upgrade to OpenAPI 3.
|
||||
RenderAndReload() ([]byte, *Document, *DocumentModel[v3high.Document], []error)
|
||||
|
||||
// Serialize will re-render a Document back into a []byte slice. If any modifications have been made to the
|
||||
// underlying data model using low level APIs, then those changes will be reflected in the serialized output.
|
||||
//
|
||||
// It's important to know that this should not be used if the resolver has been used on a specification to
|
||||
// for anything other than checking for circular references. If the resolver is used to resolve the spec, then this
|
||||
// method may spin out forever if the specification backing the model has circular references.
|
||||
// Deprecated: This method is deprecated and will be removed in a future release. Use RenderAndReload() instead.
|
||||
// This method does not support mutations correctly.
|
||||
Serialize() ([]byte, error)
|
||||
}
|
||||
|
||||
type document struct {
|
||||
version string
|
||||
info *datamodel.SpecInfo
|
||||
config *datamodel.DocumentConfiguration
|
||||
version string
|
||||
info *datamodel.SpecInfo
|
||||
config *datamodel.DocumentConfiguration
|
||||
highOpenAPI3Model *DocumentModel[v3high.Document]
|
||||
highSwaggerModel *DocumentModel[v2high.Swagger]
|
||||
}
|
||||
|
||||
// DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is
|
||||
@@ -135,7 +156,31 @@ func (d *document) Serialize() ([]byte, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (d *document) RenderAndReload() ([]byte, *Document, *DocumentModel[v3high.Document], []error) {
|
||||
if d.highSwaggerModel != nil && d.highOpenAPI3Model == nil {
|
||||
return nil, nil, nil, []error{errors.New("this method only supports OpenAPI 3 documents, not Swagger")}
|
||||
}
|
||||
newBytes, err := d.highOpenAPI3Model.Model.Render()
|
||||
if err != nil {
|
||||
return newBytes, nil, nil, []error{err}
|
||||
}
|
||||
newDoc, err := NewDocument(newBytes)
|
||||
if err != nil {
|
||||
return newBytes, &newDoc, nil, []error{err}
|
||||
}
|
||||
// build the model.
|
||||
model, errs := newDoc.BuildV3Model()
|
||||
if errs != nil {
|
||||
return newBytes, &newDoc, model, errs
|
||||
}
|
||||
// this document is now dead, long live the new document!
|
||||
return newBytes, &newDoc, model, nil
|
||||
}
|
||||
|
||||
func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], []error) {
|
||||
if d.highSwaggerModel != nil {
|
||||
return d.highSwaggerModel, nil
|
||||
}
|
||||
var errors []error
|
||||
if d.info == nil {
|
||||
errors = append(errors, fmt.Errorf("unable to build swagger document, no specification has been loaded"))
|
||||
@@ -168,13 +213,17 @@ func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], []error) {
|
||||
}
|
||||
}
|
||||
highDoc := v2high.NewSwaggerDocument(lowDoc)
|
||||
return &DocumentModel[v2high.Swagger]{
|
||||
d.highSwaggerModel = &DocumentModel[v2high.Swagger]{
|
||||
Model: *highDoc,
|
||||
Index: lowDoc.Index,
|
||||
}, errors
|
||||
}
|
||||
return d.highSwaggerModel, errors
|
||||
}
|
||||
|
||||
func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) {
|
||||
if d.highOpenAPI3Model != nil {
|
||||
return d.highOpenAPI3Model, nil
|
||||
}
|
||||
var errors []error
|
||||
if d.info == nil {
|
||||
errors = append(errors, fmt.Errorf("unable to build document, no specification has been loaded"))
|
||||
@@ -207,10 +256,11 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) {
|
||||
}
|
||||
}
|
||||
highDoc := v3high.NewDocument(lowDoc)
|
||||
return &DocumentModel[v3high.Document]{
|
||||
d.highOpenAPI3Model = &DocumentModel[v3high.Document]{
|
||||
Model: *highDoc,
|
||||
Index: lowDoc.Index,
|
||||
}, errors
|
||||
}
|
||||
return d.highOpenAPI3Model, errors
|
||||
}
|
||||
|
||||
// CompareDocuments will accept a left and right Document implementing struct, build a model for the correct
|
||||
|
||||
@@ -157,6 +157,23 @@ info:
|
||||
assert.Equal(t, ymlModified, string(serial))
|
||||
}
|
||||
|
||||
func TestDocument_RenderAndReload(t *testing.T) {
|
||||
|
||||
yml := `openapi: 3.0
|
||||
info:
|
||||
title: The magic API
|
||||
`
|
||||
doc, _ := NewDocument([]byte(yml))
|
||||
v3Doc, _ := doc.BuildV3Model()
|
||||
|
||||
v3Doc.Model.Info.Title = "The magic API - but now, altered!"
|
||||
bytes, _, newDocModel, err := doc.RenderAndReload()
|
||||
assert.Nil(t, err)
|
||||
assert.NotNil(t, bytes)
|
||||
assert.Equal(t, "The magic API - but now, altered!",
|
||||
newDocModel.Model.Info.Title)
|
||||
}
|
||||
|
||||
func TestDocument_Serialize_JSON_Modified(t *testing.T) {
|
||||
|
||||
json := `{ 'openapi': '3.0',
|
||||
|
||||
Reference in New Issue
Block a user