mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +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:
@@ -34,7 +34,7 @@ x-cli-name: pizza cli`
|
|||||||
err := low.BuildModel(idxNode.Content[0], &n)
|
err := low.BuildModel(idxNode.Content[0], &n)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
err = n.Build(nil,idxNode.Content[0], idx)
|
err = n.Build(nil, idxNode.Content[0], idx)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Equal(t, "pizza", n.Title.Value)
|
assert.Equal(t, "pizza", n.Title.Value)
|
||||||
|
|||||||
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.
|
// it's too old, so it should be motivation to upgrade to OpenAPI 3.
|
||||||
RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], []error)
|
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
|
// 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.
|
// 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) {
|
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 {
|
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
|
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 {
|
if d.info.SpecFileType == datamodel.JSONFileType {
|
||||||
jsonIndent := " "
|
jsonIndent := " "
|
||||||
i := d.info.OriginalIndentation
|
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)
|
newBytes = d.highOpenAPI3Model.Model.RenderWithIndention(d.info.OriginalIndentation)
|
||||||
}
|
}
|
||||||
|
|
||||||
newDoc, err := NewDocumentWithConfiguration(newBytes, d.config)
|
return newBytes, nil
|
||||||
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) {
|
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/datamodel/high/base"
|
||||||
"github.com/pb33f/libopenapi/what-changed/model"
|
"github.com/pb33f/libopenapi/what-changed/model"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -230,7 +229,7 @@ func TestDocument_RenderAndReload_ChangeCheck_Asana(t *testing.T) {
|
|||||||
func TestDocument_RenderAndReload(t *testing.T) {
|
func TestDocument_RenderAndReload(t *testing.T) {
|
||||||
|
|
||||||
// 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
|
||||||
doc, err := NewDocument(petstore)
|
doc, err := NewDocument(petstore)
|
||||||
@@ -276,10 +275,101 @@ func TestDocument_RenderAndReload(t *testing.T) {
|
|||||||
|
|
||||||
assert.Equal(t, "https://pb33f.io",
|
assert.Equal(t, "https://pb33f.io",
|
||||||
h.Components.SecuritySchemes["petstore_auth"].Flows.Implicit.AuthorizationUrl)
|
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) {
|
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, _ := NewDocument(petstore)
|
||||||
doc.BuildV2Model()
|
doc.BuildV2Model()
|
||||||
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) {
|
func TestDocument_BuildModelPreBuild(t *testing.T) {
|
||||||
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")
|
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||||||
doc, e := NewDocument(petstore)
|
doc, e := NewDocument(petstore)
|
||||||
assert.NoError(t, e)
|
assert.NoError(t, e)
|
||||||
doc.BuildV3Model()
|
doc.BuildV3Model()
|
||||||
@@ -314,7 +415,7 @@ func TestDocument_AnyDocWithConfig(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_BuildModelCircular(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)
|
doc, _ := NewDocument(petstore)
|
||||||
m, e := doc.BuildV3Model()
|
m, e := doc.BuildV3Model()
|
||||||
assert.NotNil(t, m)
|
assert.NotNil(t, m)
|
||||||
@@ -322,7 +423,7 @@ func TestDocument_BuildModelCircular(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_BuildModelBad(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)
|
doc, _ := NewDocument(petstore)
|
||||||
m, e := doc.BuildV3Model()
|
m, e := doc.BuildV3Model()
|
||||||
assert.Nil(t, m)
|
assert.Nil(t, m)
|
||||||
@@ -387,8 +488,8 @@ paths:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) {
|
func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) {
|
||||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||||
changes, errors := CompareDocuments(originalDoc, updatedDoc)
|
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) {
|
func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) {
|
||||||
|
|
||||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
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) {
|
func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) {
|
||||||
|
|
||||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-badref.json")
|
burgerShopOriginal, _ := os.ReadFile("test_specs/petstorev2-badref.json")
|
||||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-badref.json")
|
burgerShopUpdated, _ := os.ReadFile("test_specs/petstorev2-badref.json")
|
||||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
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) {
|
func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) {
|
||||||
|
|
||||||
burgerShopOriginal, _ := ioutil.ReadFile("test_specs/petstorev2.json")
|
burgerShopOriginal, _ := os.ReadFile("test_specs/petstorev2.json")
|
||||||
burgerShopUpdated, _ := ioutil.ReadFile("test_specs/petstorev3.json")
|
burgerShopUpdated, _ := os.ReadFile("test_specs/petstorev3.json")
|
||||||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||||||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||||||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||||||
@@ -433,7 +534,7 @@ func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSchemaRefIsFollowed(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
|
// create a new document from specification bytes
|
||||||
document, err := NewDocument(petstore)
|
document, err := NewDocument(petstore)
|
||||||
|
|||||||
@@ -4,86 +4,86 @@
|
|||||||
package index
|
package index
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/utils"
|
"github.com/pb33f/libopenapi/utils"
|
||||||
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
"github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FindComponent will locate a component by its reference, returns nil if nothing is found.
|
// FindComponent will locate a component by its reference, returns nil if nothing is found.
|
||||||
// This method will recurse through remote, local and file references. For each new external reference
|
// This method will recurse through remote, local and file references. For each new external reference
|
||||||
// a new index will be created. These indexes can then be traversed recursively.
|
// a new index will be created. These indexes can then be traversed recursively.
|
||||||
func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference {
|
func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference {
|
||||||
if index.root == nil {
|
if index.root == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
||||||
if index.config.AllowRemoteLookup {
|
if index.config.AllowRemoteLookup {
|
||||||
return index.lookupRemoteReference(id)
|
return index.lookupRemoteReference(id)
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, fmt.Errorf("remote lookups are not permitted, " +
|
return nil, nil, fmt.Errorf("remote lookups are not permitted, " +
|
||||||
"please set AllowRemoteLookup to true in the configuration")
|
"please set AllowRemoteLookup to true in the configuration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) {
|
||||||
if index.config.AllowFileLookup {
|
if index.config.AllowFileLookup {
|
||||||
return index.lookupFileReference(id)
|
return index.lookupFileReference(id)
|
||||||
} else {
|
} else {
|
||||||
return nil, nil, fmt.Errorf("local lookups are not permitted, " +
|
return nil, nil, fmt.Errorf("local lookups are not permitted, " +
|
||||||
"please set AllowFileLookup to true in the configuration")
|
"please set AllowFileLookup to true in the configuration")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch DetermineReferenceResolveType(componentId) {
|
switch DetermineReferenceResolveType(componentId) {
|
||||||
case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
|
case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case.
|
||||||
return index.FindComponentInRoot(componentId)
|
return index.FindComponentInRoot(componentId)
|
||||||
|
|
||||||
case HttpResolve:
|
case HttpResolve:
|
||||||
uri := strings.Split(componentId, "#")
|
uri := strings.Split(componentId, "#")
|
||||||
if len(uri) >= 2 {
|
if len(uri) >= 2 {
|
||||||
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
||||||
}
|
}
|
||||||
if len(uri) == 1 {
|
if len(uri) == 1 {
|
||||||
// if there is no reference, second segment is empty / has no name
|
// if there is no reference, second segment is empty / has no name
|
||||||
// this means there is no component to look-up and the entire file should be pulled in.
|
// this means there is no component to look-up and the entire file should be pulled in.
|
||||||
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
||||||
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
||||||
// described in https://github.com/pb33f/libopenapi/issues/37
|
// described in https://github.com/pb33f/libopenapi/issues/37
|
||||||
componentId = fmt.Sprintf("%s#", componentId)
|
componentId = fmt.Sprintf("%s#", componentId)
|
||||||
uri = append(uri, "")
|
uri = append(uri, "")
|
||||||
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
return index.performExternalLookup(uri, componentId, remoteLookup, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
case FileResolve:
|
case FileResolve:
|
||||||
uri := strings.Split(componentId, "#")
|
uri := strings.Split(componentId, "#")
|
||||||
if len(uri) == 2 {
|
if len(uri) == 2 {
|
||||||
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
||||||
}
|
}
|
||||||
if len(uri) == 1 {
|
if len(uri) == 1 {
|
||||||
// if there is no reference, second segment is empty / has no name
|
// if there is no reference, second segment is empty / has no name
|
||||||
// this means there is no component to look-up and the entire file should be pulled in.
|
// this means there is no component to look-up and the entire file should be pulled in.
|
||||||
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
// to stop all the other code from breaking (that is expecting a component), let's just post-pend
|
||||||
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
// a hash to the end of the componentId and ensure the uri slice is as expected.
|
||||||
// described in https://github.com/pb33f/libopenapi/issues/37
|
// described in https://github.com/pb33f/libopenapi/issues/37
|
||||||
//
|
//
|
||||||
// ^^ this same issue was re-reported in file based lookups in vacuum.
|
// ^^ this same issue was re-reported in file based lookups in vacuum.
|
||||||
// more info here: https://github.com/daveshanley/vacuum/issues/225
|
// more info here: https://github.com/daveshanley/vacuum/issues/225
|
||||||
componentId = fmt.Sprintf("%s#", componentId)
|
componentId = fmt.Sprintf("%s#", componentId)
|
||||||
uri = append(uri, "")
|
uri = append(uri, "")
|
||||||
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
return index.performExternalLookup(uri, componentId, fileLookup, parent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
||||||
@@ -91,367 +91,367 @@ var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second}
|
|||||||
type RemoteURLHandler = func(url string) (*http.Response, error)
|
type RemoteURLHandler = func(url string) (*http.Response, error)
|
||||||
|
|
||||||
func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) {
|
func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) {
|
||||||
resp, err := g(u)
|
resp, err := g(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e <- err
|
e <- err
|
||||||
close(e)
|
close(e)
|
||||||
close(d)
|
close(d)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var body []byte
|
var body []byte
|
||||||
body, _ = io.ReadAll(resp.Body)
|
body, _ = io.ReadAll(resp.Body)
|
||||||
d <- body
|
d <- body
|
||||||
close(e)
|
close(e)
|
||||||
close(d)
|
close(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
||||||
// split string to remove file reference
|
// split string to remove file reference
|
||||||
uri := strings.Split(ref, "#")
|
uri := strings.Split(ref, "#")
|
||||||
|
|
||||||
// have we already seen this remote source?
|
// have we already seen this remote source?
|
||||||
var parsedRemoteDocument *yaml.Node
|
var parsedRemoteDocument *yaml.Node
|
||||||
alreadySeen, foundDocument := index.CheckForSeenRemoteSource(uri[0])
|
alreadySeen, foundDocument := index.CheckForSeenRemoteSource(uri[0])
|
||||||
|
|
||||||
if alreadySeen {
|
if alreadySeen {
|
||||||
parsedRemoteDocument = foundDocument
|
parsedRemoteDocument = foundDocument
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
d := make(chan bool)
|
d := make(chan bool)
|
||||||
var body []byte
|
var body []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
go func(uri string) {
|
go func(uri string) {
|
||||||
bc := make(chan []byte)
|
bc := make(chan []byte)
|
||||||
ec := make(chan error)
|
ec := make(chan error)
|
||||||
var getter RemoteURLHandler = httpClient.Get
|
var getter RemoteURLHandler = httpClient.Get
|
||||||
if index.config != nil && index.config.RemoteURLHandler != nil {
|
if index.config != nil && index.config.RemoteURLHandler != nil {
|
||||||
getter = index.config.RemoteURLHandler
|
getter = index.config.RemoteURLHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we have a remote handler, use it instead of the default.
|
// if we have a remote handler, use it instead of the default.
|
||||||
if index.config != nil && index.config.FSHandler != nil {
|
if index.config != nil && index.config.FSHandler != nil {
|
||||||
go func() {
|
go func() {
|
||||||
remoteFS := index.config.FSHandler
|
remoteFS := index.config.FSHandler
|
||||||
remoteFile, rErr := remoteFS.Open(uri)
|
remoteFile, rErr := remoteFS.Open(uri)
|
||||||
if rErr != nil {
|
if rErr != nil {
|
||||||
e := fmt.Errorf("unable to open remote file: %s", rErr)
|
e := fmt.Errorf("unable to open remote file: %s", rErr)
|
||||||
ec <- e
|
ec <- e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
b, ioErr := io.ReadAll(remoteFile)
|
b, ioErr := io.ReadAll(remoteFile)
|
||||||
if ioErr != nil {
|
if ioErr != nil {
|
||||||
e := fmt.Errorf("unable to read remote file bytes: %s", ioErr)
|
e := fmt.Errorf("unable to read remote file bytes: %s", ioErr)
|
||||||
ec <- e
|
ec <- e
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
bc <- b
|
bc <- b
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
go getRemoteDoc(getter, uri, bc, ec)
|
go getRemoteDoc(getter, uri, bc, ec)
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case v := <-bc:
|
case v := <-bc:
|
||||||
body = v
|
body = v
|
||||||
break
|
break
|
||||||
case er := <-ec:
|
case er := <-ec:
|
||||||
err = er
|
err = er
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
if len(body) > 0 {
|
if len(body) > 0 {
|
||||||
var remoteDoc yaml.Node
|
var remoteDoc yaml.Node
|
||||||
er := yaml.Unmarshal(body, &remoteDoc)
|
er := yaml.Unmarshal(body, &remoteDoc)
|
||||||
if er != nil {
|
if er != nil {
|
||||||
err = er
|
err = er
|
||||||
d <- true
|
d <- true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parsedRemoteDocument = &remoteDoc
|
parsedRemoteDocument = &remoteDoc
|
||||||
if index.config != nil {
|
if index.config != nil {
|
||||||
index.config.seenRemoteSources.Store(uri, &remoteDoc)
|
index.config.seenRemoteSources.Store(uri, &remoteDoc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d <- true
|
d <- true
|
||||||
}(uri[0])
|
}(uri[0])
|
||||||
|
|
||||||
// wait for double go fun.
|
// wait for double go fun.
|
||||||
<-d
|
<-d
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// no bueno.
|
// no bueno.
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup item from reference by using a path query.
|
// lookup item from reference by using a path query.
|
||||||
var query string
|
var query string
|
||||||
if len(uri) >= 2 {
|
if len(uri) >= 2 {
|
||||||
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
||||||
} else {
|
} else {
|
||||||
query = "$"
|
query = "$"
|
||||||
}
|
}
|
||||||
|
|
||||||
query, err := url.PathUnescape(query)
|
query, err := url.PathUnescape(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any URL encoding
|
// remove any URL encoding
|
||||||
query = strings.Replace(query, "~1", "./", 1)
|
query = strings.Replace(query, "~1", "./", 1)
|
||||||
query = strings.ReplaceAll(query, "~1", "/")
|
query = strings.ReplaceAll(query, "~1", "/")
|
||||||
|
|
||||||
path, err := yamlpath.NewPath(query)
|
path, err := yamlpath.NewPath(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
result, _ := path.Find(parsedRemoteDocument)
|
result, _ := path.Find(parsedRemoteDocument)
|
||||||
if len(result) == 1 {
|
if len(result) == 1 {
|
||||||
return result[0], parsedRemoteDocument, nil
|
return result[0], parsedRemoteDocument, nil
|
||||||
}
|
}
|
||||||
return nil, nil, nil
|
return nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) lookupFileReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
func (index *SpecIndex) lookupFileReference(ref string) (*yaml.Node, *yaml.Node, error) {
|
||||||
// split string to remove file reference
|
// split string to remove file reference
|
||||||
uri := strings.Split(ref, "#")
|
uri := strings.Split(ref, "#")
|
||||||
file := strings.ReplaceAll(uri[0], "file:", "")
|
file := strings.ReplaceAll(uri[0], "file:", "")
|
||||||
filePath := filepath.Dir(file)
|
filePath := filepath.Dir(file)
|
||||||
fileName := filepath.Base(file)
|
fileName := filepath.Base(file)
|
||||||
|
|
||||||
var parsedRemoteDocument *yaml.Node
|
var parsedRemoteDocument *yaml.Node
|
||||||
|
|
||||||
if index.seenRemoteSources[file] != nil {
|
if index.seenRemoteSources[file] != nil {
|
||||||
parsedRemoteDocument = index.seenRemoteSources[file]
|
parsedRemoteDocument = index.seenRemoteSources[file]
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
base := index.config.BasePath
|
base := index.config.BasePath
|
||||||
fileToRead := filepath.Join(base, filePath, fileName)
|
fileToRead := filepath.Join(base, filePath, fileName)
|
||||||
var body []byte
|
var body []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
// if we have an FS handler, use it instead of the default behavior
|
// if we have an FS handler, use it instead of the default behavior
|
||||||
if index.config != nil && index.config.FSHandler != nil {
|
if index.config != nil && index.config.FSHandler != nil {
|
||||||
remoteFS := index.config.FSHandler
|
remoteFS := index.config.FSHandler
|
||||||
remoteFile, rErr := remoteFS.Open(fileToRead)
|
remoteFile, rErr := remoteFS.Open(fileToRead)
|
||||||
if rErr != nil {
|
if rErr != nil {
|
||||||
e := fmt.Errorf("unable to open file: %s", rErr)
|
e := fmt.Errorf("unable to open file: %s", rErr)
|
||||||
return nil, nil, e
|
return nil, nil, e
|
||||||
}
|
}
|
||||||
body, err = io.ReadAll(remoteFile)
|
body, err = io.ReadAll(remoteFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
e := fmt.Errorf("unable to read file bytes: %s", err)
|
e := fmt.Errorf("unable to read file bytes: %s", err)
|
||||||
return nil, nil, e
|
return nil, nil, e
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// try and read the file off the local file system, if it fails
|
// try and read the file off the local file system, if it fails
|
||||||
// check for a baseURL and then ask our remote lookup function to go try and get it.
|
// check for a baseURL and then ask our remote lookup function to go try and get it.
|
||||||
body, err = os.ReadFile(fileToRead)
|
body, err = os.ReadFile(fileToRead)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
// if we have a baseURL, then we can try and get the file from there.
|
// if we have a baseURL, then we can try and get the file from there.
|
||||||
if index.config != nil && index.config.BaseURL != nil {
|
if index.config != nil && index.config.BaseURL != nil {
|
||||||
|
|
||||||
u := index.config.BaseURL
|
u := index.config.BaseURL
|
||||||
remoteRef := GenerateCleanSpecConfigBaseURL(u, ref, true)
|
remoteRef := GenerateCleanSpecConfigBaseURL(u, ref, true)
|
||||||
a, b, e := index.lookupRemoteReference(remoteRef)
|
a, b, e := index.lookupRemoteReference(remoteRef)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
// give up, we can't find the file, not locally, not remotely. It's toast.
|
// give up, we can't find the file, not locally, not remotely. It's toast.
|
||||||
return nil, nil, e
|
return nil, nil, e
|
||||||
}
|
}
|
||||||
return a, b, nil
|
return a, b, nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// no baseURL? then we can't do anything, give up.
|
// no baseURL? then we can't do anything, give up.
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var remoteDoc yaml.Node
|
var remoteDoc yaml.Node
|
||||||
err = yaml.Unmarshal(body, &remoteDoc)
|
err = yaml.Unmarshal(body, &remoteDoc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
parsedRemoteDocument = &remoteDoc
|
parsedRemoteDocument = &remoteDoc
|
||||||
if index.seenLocalSources != nil {
|
if index.seenLocalSources != nil {
|
||||||
index.sourceLock.Lock()
|
index.sourceLock.Lock()
|
||||||
index.seenLocalSources[file] = &remoteDoc
|
index.seenLocalSources[file] = &remoteDoc
|
||||||
index.sourceLock.Unlock()
|
index.sourceLock.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lookup item from reference by using a path query.
|
// lookup item from reference by using a path query.
|
||||||
var query string
|
var query string
|
||||||
if len(uri) >= 2 {
|
if len(uri) >= 2 {
|
||||||
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
|
||||||
} else {
|
} else {
|
||||||
query = "$"
|
query = "$"
|
||||||
}
|
}
|
||||||
|
|
||||||
query, err := url.PathUnescape(query)
|
query, err := url.PathUnescape(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove any URL encoding
|
// remove any URL encoding
|
||||||
query = strings.Replace(query, "~1", "./", 1)
|
query = strings.Replace(query, "~1", "./", 1)
|
||||||
query = strings.ReplaceAll(query, "~1", "/")
|
query = strings.ReplaceAll(query, "~1", "/")
|
||||||
|
|
||||||
path, err := yamlpath.NewPath(query)
|
path, err := yamlpath.NewPath(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
result, _ := path.Find(parsedRemoteDocument)
|
result, _ := path.Find(parsedRemoteDocument)
|
||||||
if len(result) == 1 {
|
if len(result) == 1 {
|
||||||
return result[0], parsedRemoteDocument, nil
|
return result[0], parsedRemoteDocument, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, parsedRemoteDocument, nil
|
return nil, parsedRemoteDocument, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
|
func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference {
|
||||||
if index.root != nil {
|
if index.root != nil {
|
||||||
|
|
||||||
// check component for url encoding.
|
// check component for url encoding.
|
||||||
if strings.Contains(componentId, "%") {
|
if strings.Contains(componentId, "%") {
|
||||||
// decode the url.
|
// decode the url.
|
||||||
componentId, _ = url.QueryUnescape(componentId)
|
componentId, _ = url.QueryUnescape(componentId)
|
||||||
}
|
}
|
||||||
|
|
||||||
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
|
name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId)
|
||||||
path, err := yamlpath.NewPath(friendlySearch)
|
path, err := yamlpath.NewPath(friendlySearch)
|
||||||
if path == nil || err != nil {
|
if path == nil || err != nil {
|
||||||
return nil // no component found
|
return nil // no component found
|
||||||
}
|
}
|
||||||
res, _ := path.Find(index.root)
|
res, _ := path.Find(index.root)
|
||||||
|
|
||||||
if len(res) == 1 {
|
if len(res) == 1 {
|
||||||
resNode := res[0]
|
resNode := res[0]
|
||||||
if res[0].Kind == yaml.DocumentNode {
|
if res[0].Kind == yaml.DocumentNode {
|
||||||
resNode = res[0].Content[0]
|
resNode = res[0].Content[0]
|
||||||
}
|
}
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: componentId,
|
Definition: componentId,
|
||||||
Name: name,
|
Name: name,
|
||||||
Node: resNode,
|
Node: resNode,
|
||||||
Path: friendlySearch,
|
Path: friendlySearch,
|
||||||
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(res[0], map[string][]string{}),
|
RequiredRefProperties: index.extractDefinitionRequiredRefProperties(res[0], map[string][]string{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
|
func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
|
||||||
lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference {
|
lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference {
|
||||||
if len(uri) > 0 {
|
if len(uri) > 0 {
|
||||||
index.externalLock.RLock()
|
index.externalLock.RLock()
|
||||||
externalSpecIndex := index.externalSpecIndex[uri[0]]
|
externalSpecIndex := index.externalSpecIndex[uri[0]]
|
||||||
index.externalLock.RUnlock()
|
index.externalLock.RUnlock()
|
||||||
|
|
||||||
if externalSpecIndex == nil {
|
if externalSpecIndex == nil {
|
||||||
_, newRoot, err := lookupFunction(componentId)
|
_, newRoot, err := lookupFunction(componentId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
indexError := &IndexingError{
|
indexError := &IndexingError{
|
||||||
Err: err,
|
Err: err,
|
||||||
Node: parent,
|
Node: parent,
|
||||||
Path: componentId,
|
Path: componentId,
|
||||||
}
|
}
|
||||||
index.errorLock.Lock()
|
index.errorLock.Lock()
|
||||||
index.refErrors = append(index.refErrors, indexError)
|
index.refErrors = append(index.refErrors, indexError)
|
||||||
index.errorLock.Unlock()
|
index.errorLock.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// cool, cool, lets index this spec also. This is a recursive action and will keep going
|
// cool, cool, lets index this spec also. This is a recursive action and will keep going
|
||||||
// until all remote references have been found.
|
// until all remote references have been found.
|
||||||
var bp *url.URL
|
var bp *url.URL
|
||||||
var bd string
|
var bd string
|
||||||
|
|
||||||
if index.config.BaseURL != nil {
|
if index.config.BaseURL != nil {
|
||||||
bp = index.config.BaseURL
|
bp = index.config.BaseURL
|
||||||
}
|
}
|
||||||
if index.config.BasePath != "" {
|
if index.config.BasePath != "" {
|
||||||
bd = index.config.BasePath
|
bd = index.config.BasePath
|
||||||
}
|
}
|
||||||
|
|
||||||
var path, newBasePath string
|
var path, newBasePath string
|
||||||
var newUrl *url.URL
|
var newUrl *url.URL
|
||||||
|
|
||||||
if bp != nil {
|
if bp != nil {
|
||||||
path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false)
|
path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false)
|
||||||
newUrl, _ = url.Parse(path)
|
newUrl, _ = url.Parse(path)
|
||||||
newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path)))
|
newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path)))
|
||||||
}
|
}
|
||||||
if bd != "" {
|
if bd != "" {
|
||||||
if len(uri[0]) > 0 {
|
if len(uri[0]) > 0 {
|
||||||
// if there is no base url defined, but we can know we have been requested remotely,
|
// if there is no base url defined, but we can know we have been requested remotely,
|
||||||
// set the base url to the remote url base path.
|
// set the base url to the remote url base path.
|
||||||
// first check if the first param is actually a URL
|
// first check if the first param is actually a URL
|
||||||
io, er := url.ParseRequestURI(uri[0])
|
io, er := url.ParseRequestURI(uri[0])
|
||||||
if er != nil {
|
if er != nil {
|
||||||
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
||||||
} else {
|
} else {
|
||||||
if newUrl == nil || newUrl.String() != io.String() {
|
if newUrl == nil || newUrl.String() != io.String() {
|
||||||
newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path)))
|
newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path)))
|
||||||
}
|
}
|
||||||
newBasePath = filepath.Dir(filepath.Join(bd, uri[1]))
|
newBasePath = filepath.Dir(filepath.Join(bd, uri[1]))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
newBasePath = filepath.Dir(filepath.Join(bd, uri[0]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if newUrl != nil || newBasePath != "" {
|
if newUrl != nil || newBasePath != "" {
|
||||||
newConfig := &SpecIndexConfig{
|
newConfig := &SpecIndexConfig{
|
||||||
BaseURL: newUrl,
|
BaseURL: newUrl,
|
||||||
BasePath: newBasePath,
|
BasePath: newBasePath,
|
||||||
AllowRemoteLookup: index.config.AllowRemoteLookup,
|
AllowRemoteLookup: index.config.AllowRemoteLookup,
|
||||||
AllowFileLookup: index.config.AllowFileLookup,
|
AllowFileLookup: index.config.AllowFileLookup,
|
||||||
ParentIndex: index,
|
ParentIndex: index,
|
||||||
seenRemoteSources: index.config.seenRemoteSources,
|
seenRemoteSources: index.config.seenRemoteSources,
|
||||||
remoteLock: index.config.remoteLock,
|
remoteLock: index.config.remoteLock,
|
||||||
uri: uri,
|
uri: uri,
|
||||||
}
|
}
|
||||||
|
|
||||||
var newIndex *SpecIndex
|
var newIndex *SpecIndex
|
||||||
seen := index.SearchAncestryForSeenURI(uri[0])
|
seen := index.SearchAncestryForSeenURI(uri[0])
|
||||||
if seen == nil {
|
if seen == nil {
|
||||||
|
|
||||||
newIndex = NewSpecIndexWithConfig(newRoot, newConfig)
|
newIndex = NewSpecIndexWithConfig(newRoot, newConfig)
|
||||||
index.refLock.Lock()
|
index.refLock.Lock()
|
||||||
index.externalLock.Lock()
|
index.externalLock.Lock()
|
||||||
index.externalSpecIndex[uri[0]] = newIndex
|
index.externalSpecIndex[uri[0]] = newIndex
|
||||||
index.externalLock.Unlock()
|
index.externalLock.Unlock()
|
||||||
newIndex.relativePath = path
|
newIndex.relativePath = path
|
||||||
newIndex.parentIndex = index
|
newIndex.parentIndex = index
|
||||||
index.AddChild(newIndex)
|
index.AddChild(newIndex)
|
||||||
index.refLock.Unlock()
|
index.refLock.Unlock()
|
||||||
externalSpecIndex = newIndex
|
externalSpecIndex = newIndex
|
||||||
} else {
|
} else {
|
||||||
externalSpecIndex = seen
|
externalSpecIndex = seen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if externalSpecIndex != nil {
|
if externalSpecIndex != nil {
|
||||||
foundRef := externalSpecIndex.FindComponentInRoot(uri[1])
|
foundRef := externalSpecIndex.FindComponentInRoot(uri[1])
|
||||||
if foundRef != nil {
|
if foundRef != nil {
|
||||||
nameSegs := strings.Split(uri[1], "/")
|
nameSegs := strings.Split(uri[1], "/")
|
||||||
ref := &Reference{
|
ref := &Reference{
|
||||||
Definition: componentId,
|
Definition: componentId,
|
||||||
Name: nameSegs[len(nameSegs)-1],
|
Name: nameSegs[len(nameSegs)-1],
|
||||||
Node: foundRef.Node,
|
Node: foundRef.Node,
|
||||||
IsRemote: true,
|
IsRemote: true,
|
||||||
RemoteLocation: componentId,
|
RemoteLocation: componentId,
|
||||||
Path: foundRef.Path,
|
Path: foundRef.Path,
|
||||||
}
|
}
|
||||||
return ref
|
return ref
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,125 +4,125 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/pb33f/libopenapi"
|
"github.com/pb33f/libopenapi"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleMockGenerator_generateBurgerMock_yaml() {
|
func ExampleMockGenerator_generateBurgerMock_yaml() {
|
||||||
|
|
||||||
// create a new YAML mock generator
|
// create a new YAML mock generator
|
||||||
mg := NewMockGenerator(YAML)
|
mg := NewMockGenerator(YAML)
|
||||||
|
|
||||||
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// create a new document from specification and build a v3 model.
|
// create a new document from specification and build a v3 model.
|
||||||
document, _ := libopenapi.NewDocument(burgerShop)
|
document, _ := libopenapi.NewDocument(burgerShop)
|
||||||
v3Model, _ := document.BuildV3Model()
|
v3Model, _ := document.BuildV3Model()
|
||||||
|
|
||||||
// create a mock of the Burger model
|
// create a mock of the Burger model
|
||||||
burgerModel := v3Model.Model.Components.Schemas["Burger"]
|
burgerModel := v3Model.Model.Components.Schemas["Burger"]
|
||||||
burger := burgerModel.Schema()
|
burger := burgerModel.Schema()
|
||||||
mock, err := mg.GenerateMock(burger, "")
|
mock, err := mg.GenerateMock(burger, "")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(mock))
|
fmt.Println(string(mock))
|
||||||
// Output: name: Big Mac
|
// Output: name: Big Mac
|
||||||
//numPatties: 2
|
//numPatties: 2
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMockGenerator_generateFriesMock_json() {
|
func ExampleMockGenerator_generateFriesMock_json() {
|
||||||
|
|
||||||
// create a new YAML mock generator
|
// create a new YAML mock generator
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
|
|
||||||
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// create a new document from specification and build a v3 model.
|
// create a new document from specification and build a v3 model.
|
||||||
document, _ := libopenapi.NewDocument(burgerShop)
|
document, _ := libopenapi.NewDocument(burgerShop)
|
||||||
v3Model, _ := document.BuildV3Model()
|
v3Model, _ := document.BuildV3Model()
|
||||||
|
|
||||||
// create a mock of the Fries model
|
// create a mock of the Fries model
|
||||||
friesModel := v3Model.Model.Components.Schemas["Fries"]
|
friesModel := v3Model.Model.Components.Schemas["Fries"]
|
||||||
fries := friesModel.Schema()
|
fries := friesModel.Schema()
|
||||||
mock, err := mg.GenerateMock(fries, "")
|
mock, err := mg.GenerateMock(fries, "")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(mock))
|
fmt.Println(string(mock))
|
||||||
// Output: {"favoriteDrink":{"drinkType":"coke","size":"M"},"potatoShape":"Crispy Shoestring"}
|
// Output: {"favoriteDrink":{"drinkType":"coke","size":"M"},"potatoShape":"Crispy Shoestring"}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMockGenerator_generateRequestMock_json() {
|
func ExampleMockGenerator_generateRequestMock_json() {
|
||||||
|
|
||||||
// create a new YAML mock generator
|
// create a new YAML mock generator
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
|
|
||||||
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// create a new document from specification and build a v3 model.
|
// create a new document from specification and build a v3 model.
|
||||||
document, _ := libopenapi.NewDocument(burgerShop)
|
document, _ := libopenapi.NewDocument(burgerShop)
|
||||||
v3Model, _ := document.BuildV3Model()
|
v3Model, _ := document.BuildV3Model()
|
||||||
|
|
||||||
// create a mock of the burger request model, extracted from the operation directly.
|
// create a mock of the burger request model, extracted from the operation directly.
|
||||||
burgerRequestModel := v3Model.Model.Paths.PathItems["/burgers"].Post.RequestBody.Content["application/json"]
|
burgerRequestModel := v3Model.Model.Paths.PathItems["/burgers"].Post.RequestBody.Content["application/json"]
|
||||||
|
|
||||||
// use the 'cakeBurger' example to generate a mock
|
// use the 'cakeBurger' example to generate a mock
|
||||||
mock, err := mg.GenerateMock(burgerRequestModel, "cakeBurger")
|
mock, err := mg.GenerateMock(burgerRequestModel, "cakeBurger")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(mock))
|
fmt.Println(string(mock))
|
||||||
// Output: {"name":"Chocolate Cake Burger","numPatties":5}
|
// Output: {"name":"Chocolate Cake Burger","numPatties":5}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMockGenerator_generateResponseMock_json() {
|
func ExampleMockGenerator_generateResponseMock_json() {
|
||||||
|
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
// create a new YAML mock generator
|
// create a new YAML mock generator
|
||||||
|
|
||||||
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// create a new document from specification and build a v3 model.
|
// create a new document from specification and build a v3 model.
|
||||||
document, _ := libopenapi.NewDocument(burgerShop)
|
document, _ := libopenapi.NewDocument(burgerShop)
|
||||||
v3Model, _ := document.BuildV3Model()
|
v3Model, _ := document.BuildV3Model()
|
||||||
|
|
||||||
// create a mock of the burger response model, extracted from the operation directly.
|
// create a mock of the burger response model, extracted from the operation directly.
|
||||||
burgerResponseModel := v3Model.Model.Paths.PathItems["/burgers"].Post.Responses.Codes["200"].Content["application/json"]
|
burgerResponseModel := v3Model.Model.Paths.PathItems["/burgers"].Post.Responses.Codes["200"].Content["application/json"]
|
||||||
|
|
||||||
// use the 'filetOFish' example to generate a mock
|
// use the 'filetOFish' example to generate a mock
|
||||||
mock, err := mg.GenerateMock(burgerResponseModel, "filetOFish")
|
mock, err := mg.GenerateMock(burgerResponseModel, "filetOFish")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(mock))
|
fmt.Println(string(mock))
|
||||||
// Output: {"name":"Filet-O-Fish","numPatties":1}
|
// Output: {"name":"Filet-O-Fish","numPatties":1}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleMockGenerator_generatePolymorphicMock_json() {
|
func ExampleMockGenerator_generatePolymorphicMock_json() {
|
||||||
|
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
// create a new YAML mock generator
|
// create a new YAML mock generator
|
||||||
|
|
||||||
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
burgerShop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml")
|
||||||
|
|
||||||
// create a new document from specification and build a v3 model.
|
// create a new document from specification and build a v3 model.
|
||||||
document, _ := libopenapi.NewDocument(burgerShop)
|
document, _ := libopenapi.NewDocument(burgerShop)
|
||||||
v3Model, _ := document.BuildV3Model()
|
v3Model, _ := document.BuildV3Model()
|
||||||
|
|
||||||
// create a mock of the SomePayload component, which uses polymorphism (incorrectly)
|
// create a mock of the SomePayload component, which uses polymorphism (incorrectly)
|
||||||
payloadModel := v3Model.Model.Components.Schemas["SomePayload"]
|
payloadModel := v3Model.Model.Components.Schemas["SomePayload"]
|
||||||
payload := payloadModel.Schema()
|
payload := payloadModel.Schema()
|
||||||
mock, err := mg.GenerateMock(payload, "")
|
mock, err := mg.GenerateMock(payload, "")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(mock))
|
fmt.Println(string(mock))
|
||||||
// Output: {"drinkType":"coke","size":"M"}
|
// Output: {"drinkType":"coke","size":"M"}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,26 +4,26 @@
|
|||||||
package renderer
|
package renderer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
|
highbase "github.com/pb33f/libopenapi/datamodel/high/base"
|
||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeMockable struct {
|
type fakeMockable struct {
|
||||||
Schema *highbase.SchemaProxy
|
Schema *highbase.SchemaProxy
|
||||||
Example any
|
Example any
|
||||||
Examples map[string]*highbase.Example
|
Examples map[string]*highbase.Example
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakeMockableButWithASchemaNotAProxy struct {
|
type fakeMockableButWithASchemaNotAProxy struct {
|
||||||
Schema *highbase.Schema
|
Schema *highbase.Schema
|
||||||
Example any
|
Example any
|
||||||
Examples map[string]*highbase.Example
|
Examples map[string]*highbase.Example
|
||||||
}
|
}
|
||||||
|
|
||||||
var simpleFakeMockSchema = `type: string
|
var simpleFakeMockSchema = `type: string
|
||||||
@@ -40,240 +40,240 @@ properties:
|
|||||||
maximum: 400`
|
maximum: 400`
|
||||||
|
|
||||||
func createFakeMock(mock string, values map[string]any, example any) *fakeMockable {
|
func createFakeMock(mock string, values map[string]any, example any) *fakeMockable {
|
||||||
var root yaml.Node
|
var root yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(mock), &root)
|
_ = yaml.Unmarshal([]byte(mock), &root)
|
||||||
var lowProxy lowbase.SchemaProxy
|
var lowProxy lowbase.SchemaProxy
|
||||||
_ = lowProxy.Build(&root, root.Content[0], nil)
|
_ = lowProxy.Build(&root, root.Content[0], nil)
|
||||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||||
Value: &lowProxy,
|
Value: &lowProxy,
|
||||||
}
|
}
|
||||||
highSchema := highbase.NewSchemaProxy(&lowRef)
|
highSchema := highbase.NewSchemaProxy(&lowRef)
|
||||||
examples := make(map[string]*highbase.Example)
|
examples := make(map[string]*highbase.Example)
|
||||||
|
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
examples[k] = &highbase.Example{
|
examples[k] = &highbase.Example{
|
||||||
Value: v,
|
Value: v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &fakeMockable{
|
return &fakeMockable{
|
||||||
Schema: highSchema,
|
Schema: highSchema,
|
||||||
Example: example,
|
Example: example,
|
||||||
Examples: examples,
|
Examples: examples,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createFakeMockWithoutProxy(mock string, values map[string]any, example any) *fakeMockableButWithASchemaNotAProxy {
|
func createFakeMockWithoutProxy(mock string, values map[string]any, example any) *fakeMockableButWithASchemaNotAProxy {
|
||||||
var root yaml.Node
|
var root yaml.Node
|
||||||
_ = yaml.Unmarshal([]byte(mock), &root)
|
_ = yaml.Unmarshal([]byte(mock), &root)
|
||||||
var lowProxy lowbase.SchemaProxy
|
var lowProxy lowbase.SchemaProxy
|
||||||
_ = lowProxy.Build(&root, root.Content[0], nil)
|
_ = lowProxy.Build(&root, root.Content[0], nil)
|
||||||
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
lowRef := low.NodeReference[*lowbase.SchemaProxy]{
|
||||||
Value: &lowProxy,
|
Value: &lowProxy,
|
||||||
}
|
}
|
||||||
highSchema := highbase.NewSchemaProxy(&lowRef)
|
highSchema := highbase.NewSchemaProxy(&lowRef)
|
||||||
examples := make(map[string]*highbase.Example)
|
examples := make(map[string]*highbase.Example)
|
||||||
|
|
||||||
for k, v := range values {
|
for k, v := range values {
|
||||||
examples[k] = &highbase.Example{
|
examples[k] = &highbase.Example{
|
||||||
Value: v,
|
Value: v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &fakeMockableButWithASchemaNotAProxy{
|
return &fakeMockableButWithASchemaNotAProxy{
|
||||||
Schema: highSchema.Schema(),
|
Schema: highSchema.Schema(),
|
||||||
Example: example,
|
Example: example,
|
||||||
Examples: examples,
|
Examples: examples,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewMockGenerator(t *testing.T) {
|
func TestNewMockGenerator(t *testing.T) {
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
assert.NotNil(t, mg)
|
assert.NotNil(t, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewMockGeneratorWithDictionary(t *testing.T) {
|
func TestNewMockGeneratorWithDictionary(t *testing.T) {
|
||||||
mg := NewMockGeneratorWithDictionary("", JSON)
|
mg := NewMockGeneratorWithDictionary("", JSON)
|
||||||
assert.NotNil(t, mg)
|
assert.NotNil(t, mg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_BadObject(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_BadObject(t *testing.T) {
|
||||||
type NotMockable struct {
|
type NotMockable struct {
|
||||||
pizza string
|
pizza string
|
||||||
}
|
}
|
||||||
|
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(&NotMockable{}, "")
|
mock, err := mg.GenerateMock(&NotMockable{}, "")
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.Nil(t, mock)
|
assert.Nil(t, mock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_EmptyObject(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_EmptyObject(t *testing.T) {
|
||||||
|
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(&fakeMockable{}, "")
|
mock, err := mg.GenerateMock(&fakeMockable{}, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Nil(t, mock)
|
assert.Nil(t, mock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_SuppliedExample_JSON(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_SuppliedExample_JSON(t *testing.T) {
|
||||||
|
|
||||||
fakeExample := map[string]any{
|
fakeExample := map[string]any{
|
||||||
"fish-and-chips": "cod-and-chips-twice",
|
"fish-and-chips": "cod-and-chips-twice",
|
||||||
}
|
}
|
||||||
fake := createFakeMock(simpleFakeMockSchema, nil, fakeExample)
|
fake := createFakeMock(simpleFakeMockSchema, nil, fakeExample)
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "{\"fish-and-chips\":\"cod-and-chips-twice\"}", string(mock))
|
assert.Equal(t, "{\"fish-and-chips\":\"cod-and-chips-twice\"}", string(mock))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_SuppliedExample_YAML(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_SuppliedExample_YAML(t *testing.T) {
|
||||||
|
|
||||||
fakeExample := map[string]any{
|
fakeExample := map[string]any{
|
||||||
"fish-and-chips": "cod-and-chips-twice",
|
"fish-and-chips": "cod-and-chips-twice",
|
||||||
}
|
}
|
||||||
fake := createFakeMock(simpleFakeMockSchema, nil, fakeExample)
|
fake := createFakeMock(simpleFakeMockSchema, nil, fakeExample)
|
||||||
mg := NewMockGenerator(YAML)
|
mg := NewMockGenerator(YAML)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "fish-and-chips: cod-and-chips-twice", strings.TrimSpace(string(mock)))
|
assert.Equal(t, "fish-and-chips: cod-and-chips-twice", strings.TrimSpace(string(mock)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_MultiExamples_NoName_JSON(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_MultiExamples_NoName_JSON(t *testing.T) {
|
||||||
fakeExample := map[string]any{
|
fakeExample := map[string]any{
|
||||||
"exampleOne": map[string]any{
|
"exampleOne": map[string]any{
|
||||||
"fish-and-chips": "cod-and-chips-twice",
|
"fish-and-chips": "cod-and-chips-twice",
|
||||||
},
|
},
|
||||||
"exampleTwo": map[string]any{
|
"exampleTwo": map[string]any{
|
||||||
"rice-and-peas": "brown-or-white-rice",
|
"rice-and-peas": "brown-or-white-rice",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(fake, "JimmyJammyJimJams") // does not exist
|
mock, err := mg.GenerateMock(fake, "JimmyJammyJimJams") // does not exist
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.NotEmpty(t, string(mock))
|
assert.NotEmpty(t, string(mock))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_MultiExamples_JSON(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_MultiExamples_JSON(t *testing.T) {
|
||||||
fakeExample := map[string]any{
|
fakeExample := map[string]any{
|
||||||
"exampleOne": map[string]any{
|
"exampleOne": map[string]any{
|
||||||
"fish-and-chips": "cod-and-chips-twice",
|
"fish-and-chips": "cod-and-chips-twice",
|
||||||
},
|
},
|
||||||
"exampleTwo": map[string]any{
|
"exampleTwo": map[string]any{
|
||||||
"rice-and-peas": "brown-or-white-rice",
|
"rice-and-peas": "brown-or-white-rice",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(fake, "exampleTwo")
|
mock, err := mg.GenerateMock(fake, "exampleTwo")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "{\"rice-and-peas\":\"brown-or-white-rice\"}", string(mock))
|
assert.Equal(t, "{\"rice-and-peas\":\"brown-or-white-rice\"}", string(mock))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_MultiExamples_PrettyJSON(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_MultiExamples_PrettyJSON(t *testing.T) {
|
||||||
fakeExample := map[string]any{
|
fakeExample := map[string]any{
|
||||||
"exampleOne": map[string]any{
|
"exampleOne": map[string]any{
|
||||||
"fish-and-chips": "cod-and-chips-twice",
|
"fish-and-chips": "cod-and-chips-twice",
|
||||||
},
|
},
|
||||||
"exampleTwo": map[string]any{
|
"exampleTwo": map[string]any{
|
||||||
"rice-and-peas": "brown-or-white-rice",
|
"rice-and-peas": "brown-or-white-rice",
|
||||||
"peas": "buttery",
|
"peas": "buttery",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mg.SetPretty()
|
mg.SetPretty()
|
||||||
mock, err := mg.GenerateMock(fake, "exampleTwo")
|
mock, err := mg.GenerateMock(fake, "exampleTwo")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "{\n \"peas\": \"buttery\",\n \"rice-and-peas\": \"brown-or-white-rice\"\n}", string(mock))
|
assert.Equal(t, "{\n \"peas\": \"buttery\",\n \"rice-and-peas\": \"brown-or-white-rice\"\n}", string(mock))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_MultiExamples_YAML(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_MultiExamples_YAML(t *testing.T) {
|
||||||
fakeExample := map[string]any{
|
fakeExample := map[string]any{
|
||||||
"exampleOne": map[string]any{
|
"exampleOne": map[string]any{
|
||||||
"fish-and-chips": "cod-and-chips-twice",
|
"fish-and-chips": "cod-and-chips-twice",
|
||||||
},
|
},
|
||||||
"exampleTwo": map[string]any{
|
"exampleTwo": map[string]any{
|
||||||
"rice-and-peas": "brown-or-white-rice",
|
"rice-and-peas": "brown-or-white-rice",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
fake := createFakeMock(simpleFakeMockSchema, fakeExample, nil)
|
||||||
mg := NewMockGenerator(YAML)
|
mg := NewMockGenerator(YAML)
|
||||||
mock, err := mg.GenerateMock(fake, "exampleTwo")
|
mock, err := mg.GenerateMock(fake, "exampleTwo")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "rice-and-peas: brown-or-white-rice", strings.TrimSpace(string(mock)))
|
assert.Equal(t, "rice-and-peas: brown-or-white-rice", strings.TrimSpace(string(mock)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_NoExamples_JSON(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_NoExamples_JSON(t *testing.T) {
|
||||||
|
|
||||||
fake := createFakeMock(simpleFakeMockSchema, nil, nil)
|
fake := createFakeMock(simpleFakeMockSchema, nil, nil)
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "magic-herbs", string(mock))
|
assert.Equal(t, "magic-herbs", string(mock))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_NoExamples_YAML(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_NoExamples_YAML(t *testing.T) {
|
||||||
|
|
||||||
fake := createFakeMock(simpleFakeMockSchema, nil, nil)
|
fake := createFakeMock(simpleFakeMockSchema, nil, nil)
|
||||||
mg := NewMockGenerator(YAML)
|
mg := NewMockGenerator(YAML)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, "magic-herbs", string(mock))
|
assert.Equal(t, "magic-herbs", string(mock))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_Object_NoExamples_JSON(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_Object_NoExamples_JSON(t *testing.T) {
|
||||||
|
|
||||||
fake := createFakeMock(objectFakeMockSchema, nil, nil)
|
fake := createFakeMock(objectFakeMockSchema, nil, nil)
|
||||||
mg := NewMockGenerator(JSON)
|
mg := NewMockGenerator(JSON)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// re-serialize back into a map and check the values
|
// re-serialize back into a map and check the values
|
||||||
var m map[string]any
|
var m map[string]any
|
||||||
err = json.Unmarshal(mock, &m)
|
err = json.Unmarshal(mock, &m)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, m, 2)
|
assert.Len(t, m, 2)
|
||||||
assert.GreaterOrEqual(t, len(m["coffee"].(string)), 6)
|
assert.GreaterOrEqual(t, len(m["coffee"].(string)), 6)
|
||||||
assert.GreaterOrEqual(t, m["herbs"].(float64), float64(350))
|
assert.GreaterOrEqual(t, m["herbs"].(float64), float64(350))
|
||||||
assert.LessOrEqual(t, m["herbs"].(float64), float64(400))
|
assert.LessOrEqual(t, m["herbs"].(float64), float64(400))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMockGenerator_GenerateJSONMock_Object_NoExamples_YAML(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_Object_NoExamples_YAML(t *testing.T) {
|
||||||
|
|
||||||
fake := createFakeMock(objectFakeMockSchema, nil, nil)
|
fake := createFakeMock(objectFakeMockSchema, nil, nil)
|
||||||
mg := NewMockGenerator(YAML)
|
mg := NewMockGenerator(YAML)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// re-serialize back into a map and check the values
|
// re-serialize back into a map and check the values
|
||||||
var m map[string]any
|
var m map[string]any
|
||||||
err = yaml.Unmarshal(mock, &m)
|
err = yaml.Unmarshal(mock, &m)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, m, 2)
|
assert.Len(t, m, 2)
|
||||||
assert.GreaterOrEqual(t, len(m["coffee"].(string)), 6)
|
assert.GreaterOrEqual(t, len(m["coffee"].(string)), 6)
|
||||||
assert.GreaterOrEqual(t, m["herbs"].(int), 350)
|
assert.GreaterOrEqual(t, m["herbs"].(int), 350)
|
||||||
assert.LessOrEqual(t, m["herbs"].(int), 400)
|
assert.LessOrEqual(t, m["herbs"].(int), 400)
|
||||||
}
|
}
|
||||||
|
|
||||||
// should result in the exact same output as the above test
|
// should result in the exact same output as the above test
|
||||||
func TestMockGenerator_GenerateJSONMock_Object_RawSchema(t *testing.T) {
|
func TestMockGenerator_GenerateJSONMock_Object_RawSchema(t *testing.T) {
|
||||||
|
|
||||||
fake := createFakeMockWithoutProxy(objectFakeMockSchema, nil, nil)
|
fake := createFakeMockWithoutProxy(objectFakeMockSchema, nil, nil)
|
||||||
|
|
||||||
mg := NewMockGenerator(YAML)
|
mg := NewMockGenerator(YAML)
|
||||||
mock, err := mg.GenerateMock(fake, "")
|
mock, err := mg.GenerateMock(fake, "")
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// re-serialize back into a map and check the values
|
// re-serialize back into a map and check the values
|
||||||
var m map[string]any
|
var m map[string]any
|
||||||
err = yaml.Unmarshal(mock, &m)
|
err = yaml.Unmarshal(mock, &m)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
assert.Len(t, m, 2)
|
assert.Len(t, m, 2)
|
||||||
assert.GreaterOrEqual(t, len(m["coffee"].(string)), 6)
|
assert.GreaterOrEqual(t, len(m["coffee"].(string)), 6)
|
||||||
assert.GreaterOrEqual(t, m["herbs"].(int), 350)
|
assert.GreaterOrEqual(t, m["herbs"].(int), 350)
|
||||||
assert.LessOrEqual(t, m["herbs"].(int), 400)
|
assert.LessOrEqual(t, m["herbs"].(int), 400)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user