mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
Added new Render method to top level document.
Like `RenderAndReload` this method will render / print out the document, however it won’t read it back in and reload the model from the ground up. This is a non-destructive method that does not reload the model from the rendered document. If you don’t use the low model or you don’t care that the high level model and low-level models are out of sync? No problem. Otherwise keep this in mind that the low level model will only represent the original un-mutated document, as will the index. Signed-off-by: quobix <dave@quobix.com>
This commit is contained in:
51
document.go
51
document.go
@@ -72,6 +72,21 @@ type Document interface {
|
||||
// it's too old, so it should be motivation to upgrade to OpenAPI 3.
|
||||
RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], []error)
|
||||
|
||||
// Render will render the high level model as it currently exists (including any mutations, additions
|
||||
// and removals to and from any object in the tree). Unlike RenderAndReload, Render will simply print the state
|
||||
// of the model as it currently exists, and will not re-load the model into memory. It means that the low-level and
|
||||
// the high-level models will be out of sync, and the index will only be useful for the original document.
|
||||
//
|
||||
// Why use this instead of RenderAndReload?
|
||||
//
|
||||
// The simple answer is that RenderAndReload is a destructive operation, and will re-build the entire model from
|
||||
// scratch using the new bytes, which is desirable 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 the index is accurate.
|
||||
// However, if you don't care about the low-level model, and you're not using the index, and you just want to
|
||||
// print the state of the model as it currently exists, then Render() is the method to use.
|
||||
// **IMPORTANT** This method only supports OpenAPI Documents.
|
||||
Render() ([]byte, 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.
|
||||
//
|
||||
@@ -168,14 +183,32 @@ func (d *document) Serialize() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], []error) {
|
||||
|
||||
newBytes, rerr := d.Render()
|
||||
if rerr != nil {
|
||||
return nil, nil, nil, []error{rerr}
|
||||
}
|
||||
|
||||
newDoc, err := NewDocumentWithConfiguration(newBytes, d.config)
|
||||
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) Render() ([]byte, error) {
|
||||
if d.highSwaggerModel != nil && d.highOpenAPI3Model == nil {
|
||||
return nil, nil, nil, []error{errors.New("this method only supports OpenAPI 3 documents, not Swagger")}
|
||||
return nil, errors.New("this method only supports OpenAPI 3 documents, not Swagger")
|
||||
}
|
||||
|
||||
var newBytes []byte
|
||||
|
||||
// render the model as the correct type based on the source.
|
||||
// https://github.com/pb33f/libopenapi/issues/105
|
||||
if d.info.SpecFileType == datamodel.JSONFileType {
|
||||
jsonIndent := " "
|
||||
i := d.info.OriginalIndentation
|
||||
@@ -190,17 +223,7 @@ func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Do
|
||||
newBytes = d.highOpenAPI3Model.Model.RenderWithIndention(d.info.OriginalIndentation)
|
||||
}
|
||||
|
||||
newDoc, err := NewDocumentWithConfiguration(newBytes, d.config)
|
||||
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
|
||||
return newBytes, nil
|
||||
}
|
||||
|
||||
func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], []error) {
|
||||
|
||||
131
document_test.go
131
document_test.go
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||
"github.com/pb33f/libopenapi/what-changed/model"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -230,7 +229,7 @@ func TestDocument_RenderAndReload_ChangeCheck_Asana(t *testing.T) {
|
||||
func TestDocument_RenderAndReload(t *testing.T) {
|
||||
|
||||
// load an OpenAPI 3 specification from bytes
|
||||
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")
|
||||
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||||
|
||||
// create a new document from specification bytes
|
||||
doc, err := NewDocument(petstore)
|
||||
@@ -276,10 +275,101 @@ func TestDocument_RenderAndReload(t *testing.T) {
|
||||
|
||||
assert.Equal(t, "https://pb33f.io",
|
||||
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl)
|
||||
}
|
||||
|
||||
func TestDocument_Render(t *testing.T) {
|
||||
|
||||
// load an OpenAPI 3 specification from bytes
|
||||
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||||
|
||||
// create a new document from specification bytes
|
||||
doc, err := NewDocument(petstore)
|
||||
|
||||
// if anything went wrong, an error is thrown
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||||
}
|
||||
|
||||
// because we know this is a v3 spec, we can build a ready to go model from it.
|
||||
m, _ := doc.BuildV3Model()
|
||||
|
||||
// mutate the model
|
||||
h := m.Model
|
||||
h.Paths.PathItems["/pet/findByStatus"].Get.OperationId = "findACakeInABakery"
|
||||
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description = "a nice bucket of mice"
|
||||
h.Paths.PathItems["/pet/findByTags"].Get.Tags =
|
||||
append(h.Paths.PathItems["/pet/findByTags"].Get.Tags, "gurgle", "giggle")
|
||||
|
||||
h.Paths.PathItems["/pet/{petId}"].Delete.Security = append(h.Paths.PathItems["/pet/{petId}"].Delete.Security,
|
||||
&base.SecurityRequirement{Requirements: map[string][]string{
|
||||
"pizza-and-cake": {"read:abook", "write:asong"},
|
||||
}})
|
||||
|
||||
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example = "I am a teapot, filled with love."
|
||||
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
|
||||
|
||||
bytes, e := doc.Render()
|
||||
assert.NoError(t, e)
|
||||
assert.NotNil(t, bytes)
|
||||
|
||||
newDoc, docErr := NewDocument(bytes)
|
||||
|
||||
assert.NoError(t, docErr)
|
||||
|
||||
newDocModel, docErrs := newDoc.BuildV3Model()
|
||||
assert.Len(t, docErrs, 0)
|
||||
|
||||
h = newDocModel.Model
|
||||
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems["/pet/findByStatus"].Get.OperationId)
|
||||
assert.Equal(t, "a nice bucket of mice",
|
||||
h.Paths.PathItems["/pet/findByStatus"].Get.Responses.Codes["400"].Description)
|
||||
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3)
|
||||
|
||||
assert.Len(t, h.Paths.PathItems["/pet/findByTags"].Get.Tags, 3)
|
||||
yu := h.Paths.PathItems["/pet/{petId}"].Delete.Security
|
||||
assert.Equal(t, "read:abook", yu[len(yu)-1].Requirements["pizza-and-cake"][0])
|
||||
assert.Equal(t, "I am a teapot, filled with love.",
|
||||
h.Components.Schemas["Order"].Schema().Properties["status"].Schema().Example)
|
||||
|
||||
assert.Equal(t, "https://pb33f.io",
|
||||
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl)
|
||||
}
|
||||
|
||||
func TestDocument_RenderWithLargeIndention(t *testing.T) {
|
||||
|
||||
json := `{
|
||||
"openapi": "3.0"
|
||||
}`
|
||||
doc, _ := NewDocument([]byte(json))
|
||||
|
||||
doc.BuildV3Model()
|
||||
bytes, _ := doc.Render()
|
||||
assert.Equal(t, json, string(bytes))
|
||||
|
||||
}
|
||||
|
||||
func TestDocument_Render_ChangeCheck_Burgershop(t *testing.T) {
|
||||
|
||||
bs, _ := os.ReadFile("test_specs/burgershop.openapi.yaml")
|
||||
doc, _ := NewDocument(bs)
|
||||
doc.BuildV3Model()
|
||||
|
||||
rend, _ := doc.Render()
|
||||
|
||||
newDoc, _ := NewDocument(rend)
|
||||
|
||||
// compare documents
|
||||
compReport, errs := CompareDocuments(doc, newDoc)
|
||||
|
||||
// should noth be nil.
|
||||
assert.Nil(t, errs)
|
||||
assert.NotNil(t, rend)
|
||||
assert.Nil(t, compReport)
|
||||
|
||||
}
|
||||
|
||||
func TestDocument_RenderAndReload_Swagger(t *testing.T) {
|
||||
petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json")
|
||||
petstore, _ := os.ReadFile("test_specs/petstorev2.json")
|
||||
doc, _ := NewDocument(petstore)
|
||||
doc.BuildV2Model()
|
||||
doc.BuildV2Model()
|
||||
@@ -289,8 +379,19 @@ func TestDocument_RenderAndReload_Swagger(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestDocument_Render_Swagger(t *testing.T) {
|
||||
petstore, _ := os.ReadFile("test_specs/petstorev2.json")
|
||||
doc, _ := NewDocument(petstore)
|
||||
doc.BuildV2Model()
|
||||
doc.BuildV2Model()
|
||||
_, e := doc.Render()
|
||||
assert.Error(t, e)
|
||||
assert.Equal(t, "this method only supports OpenAPI 3 documents, not Swagger", e.Error())
|
||||
|
||||
}
|
||||
|
||||
func TestDocument_BuildModelPreBuild(t *testing.T) {
|
||||
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")
|
||||
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||||
doc, e := NewDocument(petstore)
|
||||
assert.NoError(t, e)
|
||||
doc.BuildV3Model()
|
||||
@@ -314,7 +415,7 @@ func TestDocument_AnyDocWithConfig(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDocument_BuildModelCircular(t *testing.T) {
|
||||
petstore, _ := ioutil.ReadFile("test_specs/circular-tests.yaml")
|
||||
petstore, _ := os.ReadFile("test_specs/circular-tests.yaml")
|
||||
doc, _ := NewDocument(petstore)
|
||||
m, e := doc.BuildV3Model()
|
||||
assert.NotNil(t, m)
|
||||
@@ -322,7 +423,7 @@ func TestDocument_BuildModelCircular(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDocument_BuildModelBad(t *testing.T) {
|
||||
petstore, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||
petstore, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||
doc, _ := NewDocument(petstore)
|
||||
m, e := doc.BuildV3Model()
|
||||
assert.Nil(t, m)
|
||||
@@ -387,8 +488,8 @@ paths:
|
||||
}
|
||||
|
||||
func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) {
|
||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||
burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||
changes, errors := CompareDocuments(originalDoc, updatedDoc)
|
||||
@@ -398,8 +499,8 @@ func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) {
|
||||
|
||||
func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) {
|
||||
|
||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||
burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||||
@@ -410,8 +511,8 @@ func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) {
|
||||
|
||||
func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) {
|
||||
|
||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-badref.json")
|
||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-badref.json")
|
||||
burgerShopOriginal, _ := os.ReadFile("test_specs/petstorev2-badref.json")
|
||||
burgerShopUpdated, _ := os.ReadFile("test_specs/petstorev2-badref.json")
|
||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||||
@@ -422,8 +523,8 @@ func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) {
|
||||
|
||||
func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) {
|
||||
|
||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/petstorev2.json")
|
||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/petstorev3.json")
|
||||
burgerShopOriginal, _ := os.ReadFile("test_specs/petstorev2.json")
|
||||
burgerShopUpdated, _ := os.ReadFile("test_specs/petstorev3.json")
|
||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||||
@@ -433,7 +534,7 @@ func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSchemaRefIsFollowed(t *testing.T) {
|
||||
petstore, _ := ioutil.ReadFile("test_specs/ref-followed.yaml")
|
||||
petstore, _ := os.ReadFile("test_specs/ref-followed.yaml")
|
||||
|
||||
// create a new document from specification bytes
|
||||
document, err := NewDocument(petstore)
|
||||
|
||||
Reference in New Issue
Block a user