mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-07 12:37:48 +00:00
Added support for original format rendering #105
Original input document types are now rendered automatically as JSON or YAML (vs always being YAML). This only operates at the `Document` level for rendering out entire documents. Requested in #105 Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/index"
|
"github.com/pb33f/libopenapi/index"
|
||||||
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,6 +154,15 @@ func (d *Document) Render() ([]byte, error) {
|
|||||||
return yaml.Marshal(d)
|
return yaml.Marshal(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RenderJSON will return a JSON representation of the Document object as a byte slice.
|
||||||
|
func (d *Document) RenderJSON() ([]byte, error) {
|
||||||
|
yamlData, err := yaml.Marshal(d)
|
||||||
|
if err != nil {
|
||||||
|
return yamlData, err
|
||||||
|
}
|
||||||
|
return utils.ConvertYAMLtoJSONPretty(yamlData, "", " ")
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Document) RenderInline() ([]byte, error) {
|
func (d *Document) RenderInline() ([]byte, error) {
|
||||||
di, _ := d.MarshalYAMLInline()
|
di, _ := d.MarshalYAMLInline()
|
||||||
return yaml.Marshal(di)
|
return yaml.Marshal(di)
|
||||||
|
|||||||
@@ -108,18 +108,3 @@ func (d *Document) GetExternalDocs() *low.NodeReference[any] {
|
|||||||
Value: d.ExternalDocs.Value,
|
Value: d.ExternalDocs.Value,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this is early prototype mutation/modification code, keeping it around for later.
|
|
||||||
//func (d *Document) AddTag() *base.Tag {
|
|
||||||
// t := base.NewTag()
|
|
||||||
// //d.Tags.KeyNode
|
|
||||||
// t.Name.Value = "nice new tag"
|
|
||||||
//
|
|
||||||
// dat, _ := yaml.Marshal(t)
|
|
||||||
// var inject yaml.Node
|
|
||||||
// _ = yaml.Unmarshal(dat, &inject)
|
|
||||||
//
|
|
||||||
// d.Tags.ValueNode.Content = append(d.Tags.ValueNode.Content, inject.Content[0])
|
|
||||||
//
|
|
||||||
// return t
|
|
||||||
//}
|
|
||||||
|
|||||||
19
document.go
19
document.go
@@ -160,10 +160,23 @@ func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Do
|
|||||||
if d.highSwaggerModel != nil && d.highOpenAPI3Model == nil {
|
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, nil, nil, []error{errors.New("this method only supports OpenAPI 3 documents, not Swagger")}
|
||||||
}
|
}
|
||||||
newBytes, err := d.highOpenAPI3Model.Model.Render()
|
|
||||||
if err != nil {
|
var newBytes []byte
|
||||||
return newBytes, nil, nil, []error{err}
|
var renderError error
|
||||||
|
|
||||||
|
// render the model as the correct type based on the source.
|
||||||
|
// https://github.com/pb33f/libopenapi/issues/105
|
||||||
|
if d.info.SpecFileType == datamodel.JSONFileType {
|
||||||
|
newBytes, renderError = d.highOpenAPI3Model.Model.RenderJSON()
|
||||||
}
|
}
|
||||||
|
if d.info.SpecFileType == datamodel.YAMLFileType {
|
||||||
|
newBytes, renderError = d.highOpenAPI3Model.Model.Render()
|
||||||
|
}
|
||||||
|
|
||||||
|
if renderError != nil {
|
||||||
|
return newBytes, nil, nil, []error{renderError}
|
||||||
|
}
|
||||||
|
|
||||||
newDoc, err := NewDocumentWithConfiguration(newBytes, d.config)
|
newDoc, err := NewDocumentWithConfiguration(newBytes, d.config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newBytes, newDoc, nil, []error{err}
|
return newBytes, newDoc, nil, []error{err}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package libopenapi
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"io/ioutil"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -26,7 +25,7 @@ func ExampleNewDocument_fromOpenAPI3Document() {
|
|||||||
// How to read in an OpenAPI 3 Specification, into a Document.
|
// How to read in an OpenAPI 3 Specification, into a Document.
|
||||||
|
|
||||||
// load an OpenAPI 3 specification from bytes
|
// 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
|
// create a new document from specification bytes
|
||||||
document, err := NewDocument(petstore)
|
document, err := NewDocument(petstore)
|
||||||
@@ -62,7 +61,7 @@ func ExampleNewDocument_fromWithDocumentConfigurationFailure() {
|
|||||||
// from files or the network
|
// from files or the network
|
||||||
|
|
||||||
// load in the Digital Ocean OpenAPI specification
|
// load in the Digital Ocean OpenAPI specification
|
||||||
digitalOcean, _ := ioutil.ReadFile("test_specs/digitalocean.yaml")
|
digitalOcean, _ := os.ReadFile("test_specs/digitalocean.yaml")
|
||||||
|
|
||||||
// create a DocumentConfiguration that prevents loading file and remote references
|
// create a DocumentConfiguration that prevents loading file and remote references
|
||||||
config := datamodel.DocumentConfiguration{
|
config := datamodel.DocumentConfiguration{
|
||||||
@@ -94,7 +93,7 @@ func ExampleNewDocument_fromWithDocumentConfigurationSuccess() {
|
|||||||
// from files or the network
|
// from files or the network
|
||||||
|
|
||||||
// load in the Digital Ocean OpenAPI specification
|
// load in the Digital Ocean OpenAPI specification
|
||||||
digitalOcean, _ := ioutil.ReadFile("test_specs/digitalocean.yaml")
|
digitalOcean, _ := os.ReadFile("test_specs/digitalocean.yaml")
|
||||||
|
|
||||||
// Digital Ocean needs a baseURL to be set, so we can resolve relative references.
|
// Digital Ocean needs a baseURL to be set, so we can resolve relative references.
|
||||||
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
|
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
|
||||||
@@ -132,7 +131,7 @@ func ExampleNewDocument_fromSwaggerDocument() {
|
|||||||
// How to read in a Swagger / OpenAPI 2 Specification, into a Document.
|
// How to read in a Swagger / OpenAPI 2 Specification, into a Document.
|
||||||
|
|
||||||
// load a Swagger specification from bytes
|
// load a Swagger specification from bytes
|
||||||
petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json")
|
petstore, _ := os.ReadFile("test_specs/petstorev2.json")
|
||||||
|
|
||||||
// create a new document from specification bytes
|
// create a new document from specification bytes
|
||||||
document, err := NewDocument(petstore)
|
document, err := NewDocument(petstore)
|
||||||
@@ -165,7 +164,7 @@ func ExampleNewDocument_fromSwaggerDocument() {
|
|||||||
func ExampleNewDocument_fromUnknownVersion() {
|
func ExampleNewDocument_fromUnknownVersion() {
|
||||||
|
|
||||||
// load an unknown version of an OpenAPI spec
|
// load an unknown version of an OpenAPI spec
|
||||||
petstore, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml")
|
petstore, _ := os.ReadFile("test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// create a new document from specification bytes
|
// create a new document from specification bytes
|
||||||
document, err := NewDocument(petstore)
|
document, err := NewDocument(petstore)
|
||||||
@@ -226,7 +225,7 @@ info:
|
|||||||
name: Some Person
|
name: Some Person
|
||||||
email: some@emailaddress.com
|
email: some@emailaddress.com
|
||||||
license:
|
license:
|
||||||
url: http://some-place-on-the-internet.com/license
|
url: https://some-place-on-the-internet.com/license
|
||||||
`
|
`
|
||||||
// create a new document from specification bytes
|
// create a new document from specification bytes
|
||||||
document, err := NewDocument([]byte(spec))
|
document, err := NewDocument([]byte(spec))
|
||||||
@@ -284,10 +283,10 @@ func ExampleCompareDocuments_openAPI() {
|
|||||||
// How to compare two different OpenAPI specifications.
|
// How to compare two different OpenAPI specifications.
|
||||||
|
|
||||||
// load an original OpenAPI 3 specification from bytes
|
// load an original OpenAPI 3 specification from bytes
|
||||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml")
|
burgerShopOriginal, _ := os.ReadFile("test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// load an **updated** OpenAPI 3 specification from bytes
|
// load an **updated** OpenAPI 3 specification from bytes
|
||||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||||
|
|
||||||
// create a new document from original specification bytes
|
// create a new document from original specification bytes
|
||||||
originalDoc, err := NewDocument(burgerShopOriginal)
|
originalDoc, err := NewDocument(burgerShopOriginal)
|
||||||
@@ -331,10 +330,10 @@ func ExampleCompareDocuments_swagger() {
|
|||||||
// How to compare two different Swagger specifications.
|
// How to compare two different Swagger specifications.
|
||||||
|
|
||||||
// load an original OpenAPI 3 specification from bytes
|
// load an original OpenAPI 3 specification from bytes
|
||||||
petstoreOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-complete.yaml")
|
petstoreOriginal, _ := os.ReadFile("test_specs/petstorev2-complete.yaml")
|
||||||
|
|
||||||
// load an **updated** OpenAPI 3 specification from bytes
|
// load an **updated** OpenAPI 3 specification from bytes
|
||||||
petstoreUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-complete-modified.yaml")
|
petstoreUpdated, _ := os.ReadFile("test_specs/petstorev2-complete-modified.yaml")
|
||||||
|
|
||||||
// create a new document from original specification bytes
|
// create a new document from original specification bytes
|
||||||
originalDoc, err := NewDocument(petstoreOriginal)
|
originalDoc, err := NewDocument(petstoreOriginal)
|
||||||
@@ -647,9 +646,6 @@ func ExampleNewDocument_modifyAndReRender() {
|
|||||||
// add the path to the document
|
// add the path to the document
|
||||||
v3Model.Model.Paths.PathItems["/new/path"] = newPath
|
v3Model.Model.Paths.PathItems["/new/path"] = newPath
|
||||||
|
|
||||||
// render out the new path item to YAML
|
|
||||||
// renderedPathItem, _ := yaml.Marshal(newPath)
|
|
||||||
|
|
||||||
// render the document back to bytes and reload the model.
|
// render the document back to bytes and reload the model.
|
||||||
rawBytes, _, newModel, errs := doc.RenderAndReload()
|
rawBytes, _, newModel, errs := doc.RenderAndReload()
|
||||||
|
|
||||||
@@ -665,5 +661,5 @@ func ExampleNewDocument_modifyAndReRender() {
|
|||||||
fmt.Printf("There were %d original paths. There are now %d paths in the document\n", originalPaths, newPaths)
|
fmt.Printf("There were %d original paths. There are now %d paths in the document\n", originalPaths, newPaths)
|
||||||
fmt.Printf("The original spec had %d bytes, the new one has %d\n", len(petstore), len(rawBytes))
|
fmt.Printf("The original spec had %d bytes, the new one has %d\n", len(petstore), len(rawBytes))
|
||||||
// Output: There were 13 original paths. There are now 14 paths in the document
|
// Output: There were 13 original paths. There are now 14 paths in the document
|
||||||
//The original spec had 31143 bytes, the new one has 27841
|
//The original spec had 31143 bytes, the new one has 31027
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -573,6 +573,7 @@ summary: a test thing
|
|||||||
description: this is a test, that does a test.`
|
description: this is a test, that does a test.`
|
||||||
|
|
||||||
_ = os.WriteFile("test-operation.yaml", []byte(ae), 0644)
|
_ = os.WriteFile("test-operation.yaml", []byte(ae), 0644)
|
||||||
|
defer os.Remove("test-operation.yaml")
|
||||||
|
|
||||||
var d = `openapi: "3.1"
|
var d = `openapi: "3.1"
|
||||||
paths:
|
paths:
|
||||||
@@ -595,3 +596,29 @@ paths:
|
|||||||
|
|
||||||
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDocument_InputAsJSON(t *testing.T) {
|
||||||
|
|
||||||
|
var d = `{
|
||||||
|
"openapi": "3.1",
|
||||||
|
"paths": {
|
||||||
|
"/an/operation": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "thisIsAnOperationId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
doc, err := NewDocumentWithConfiguration([]byte(d), datamodel.NewOpenDocumentConfiguration())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = doc.BuildV3Model()
|
||||||
|
|
||||||
|
// render the document.
|
||||||
|
rend, _, _, _ := doc.RenderAndReload()
|
||||||
|
|
||||||
|
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||||||
|
}
|
||||||
|
|||||||
@@ -503,6 +503,19 @@ func ConvertYAMLtoJSON(yamlData []byte) ([]byte, error) {
|
|||||||
return jsonData, nil
|
return jsonData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConvertYAMLtoJSONPretty will do exactly what you think it will. It will deserialize YAML into serialized JSON.
|
||||||
|
// However, this version will a apply prefix/indentation to the JSON.
|
||||||
|
func ConvertYAMLtoJSONPretty(yamlData []byte, prefix string, indent string) ([]byte, error) {
|
||||||
|
var decodedYaml map[string]interface{}
|
||||||
|
err := yaml.Unmarshal(yamlData, &decodedYaml)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// if the data can be decoded, it can be encoded (that's my view anyway). no need for an error check.
|
||||||
|
jsonData, _ := json.MarshalIndent(decodedYaml, prefix, indent)
|
||||||
|
return jsonData, nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsHttpVerb will check if an operation is valid or not.
|
// IsHttpVerb will check if an operation is valid or not.
|
||||||
func IsHttpVerb(verb string) bool {
|
func IsHttpVerb(verb string) bool {
|
||||||
verbs := []string{"get", "post", "put", "patch", "delete", "options", "trace", "head"}
|
verbs := []string{"get", "post", "put", "patch", "delete", "options", "trace", "head"}
|
||||||
|
|||||||
@@ -637,6 +637,14 @@ func TestConvertYAMLtoJSON(t *testing.T) {
|
|||||||
assert.Nil(t, str)
|
assert.Nil(t, str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConvertYAMLtoJSONPretty(t *testing.T) {
|
||||||
|
str, err := ConvertYAMLtoJSONPretty([]byte("hello: there"), "", " ")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, str)
|
||||||
|
assert.Equal(t, "{\n \"hello\": \"there\"\n}", string(str))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestIsHttpVerb(t *testing.T) {
|
func TestIsHttpVerb(t *testing.T) {
|
||||||
assert.True(t, IsHttpVerb("get"))
|
assert.True(t, IsHttpVerb("get"))
|
||||||
assert.True(t, IsHttpVerb("post"))
|
assert.True(t, IsHttpVerb("post"))
|
||||||
|
|||||||
Reference in New Issue
Block a user