mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
Updated readme with new docs link
Added in some new helper methods for building out new models. More details in the docs. Added another example.
This commit is contained in:
@@ -47,6 +47,7 @@ See all the documentation at https://pb33f.io/libopenapi/
|
|||||||
- [Using OpenAPI](https://pb33f.io/libopenapi/openapi/)
|
- [Using OpenAPI](https://pb33f.io/libopenapi/openapi/)
|
||||||
- [Using Swagger](https://pb33f.io/libopenapi/swagger/)
|
- [Using Swagger](https://pb33f.io/libopenapi/swagger/)
|
||||||
- [The Data Model](https://pb33f.io/libopenapi/model/)
|
- [The Data Model](https://pb33f.io/libopenapi/model/)
|
||||||
|
- [Modifying / Mutating the OpenAPI Model](https://pb33f.io/libopenapi/modifying/)
|
||||||
- [Using Vendor Extensions](https://pb33f.io/libopenapi/extensions/)
|
- [Using Vendor Extensions](https://pb33f.io/libopenapi/extensions/)
|
||||||
- [The Index](https://pb33f.io/libopenapi/index/)
|
- [The Index](https://pb33f.io/libopenapi/index/)
|
||||||
- [The Resolver](https://pb33f.io/libopenapi/resolver/)
|
- [The Resolver](https://pb33f.io/libopenapi/resolver/)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type SchemaProxy struct {
|
|||||||
schema *low.NodeReference[*base.SchemaProxy]
|
schema *low.NodeReference[*base.SchemaProxy]
|
||||||
buildError error
|
buildError error
|
||||||
rendered *Schema
|
rendered *Schema
|
||||||
|
refStr string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSchemaProxy creates a new high-level SchemaProxy from a low-level one.
|
// NewSchemaProxy creates a new high-level SchemaProxy from a low-level one.
|
||||||
@@ -55,6 +56,18 @@ func NewSchemaProxy(schema *low.NodeReference[*base.SchemaProxy]) *SchemaProxy {
|
|||||||
return &SchemaProxy{schema: schema}
|
return &SchemaProxy{schema: schema}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateSchemaProxy will create a new high-level SchemaProxy from a high-level Schema, this acts the same
|
||||||
|
// as if the SchemaProxy is pre-rendered.
|
||||||
|
func CreateSchemaProxy(schema *Schema) *SchemaProxy {
|
||||||
|
return &SchemaProxy{rendered: schema}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateSchemaProxyRef will create a new high-level SchemaProxy from a reference string, this is used only when
|
||||||
|
// building out new models from scratch that require a reference rather than a schema implementation.
|
||||||
|
func CreateSchemaProxyRef(ref string) *SchemaProxy {
|
||||||
|
return &SchemaProxy{refStr: ref}
|
||||||
|
}
|
||||||
|
|
||||||
// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one.
|
// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one.
|
||||||
// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access
|
// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access
|
||||||
// to that building error.
|
// to that building error.
|
||||||
@@ -76,11 +89,20 @@ func (sp *SchemaProxy) Schema() *Schema {
|
|||||||
|
|
||||||
// IsReference returns true if the SchemaProxy is a reference to another Schema.
|
// IsReference returns true if the SchemaProxy is a reference to another Schema.
|
||||||
func (sp *SchemaProxy) IsReference() bool {
|
func (sp *SchemaProxy) IsReference() bool {
|
||||||
return sp.schema.Value.IsSchemaReference()
|
if sp.refStr != "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if sp.schema != nil {
|
||||||
|
return sp.schema.Value.IsSchemaReference()
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetReference returns the location of the $ref if this SchemaProxy is a reference to another Schema.
|
// GetReference returns the location of the $ref if this SchemaProxy is a reference to another Schema.
|
||||||
func (sp *SchemaProxy) GetReference() string {
|
func (sp *SchemaProxy) GetReference() string {
|
||||||
|
if sp.refStr != "" {
|
||||||
|
return sp.refStr
|
||||||
|
}
|
||||||
return sp.schema.Value.GetSchemaReference()
|
return sp.schema.Value.GetSchemaReference()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
|||||||
var idxNode yaml.Node
|
var idxNode yaml.Node
|
||||||
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
return index.NewSpecIndex(&idxNode)
|
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||||
}()
|
}()
|
||||||
|
|
||||||
const ref = "#/components/schemas/nice"
|
const ref = "#/components/schemas/nice"
|
||||||
@@ -53,3 +53,14 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
|||||||
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
|
assert.Equal(t, "$ref: '#/components/schemas/nice'", strings.TrimSpace(string(rend)))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCreateSchemaProxy(t *testing.T) {
|
||||||
|
sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"})
|
||||||
|
assert.Equal(t, "iAmASchema", sp.rendered.Description)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSchemaProxyRef(t *testing.T) {
|
||||||
|
sp := CreateSchemaProxyRef("#/components/schemas/MySchema")
|
||||||
|
assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference())
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -474,15 +474,16 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
|
|||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
if r, ok := value.(Renderable); ok {
|
if r, ok := value.(Renderable); ok {
|
||||||
if gl, lg := value.(GoesLowUntyped); lg {
|
if gl, lg := value.(GoesLowUntyped); lg {
|
||||||
|
if gl.GoLowUntyped() != nil {
|
||||||
ut := reflect.ValueOf(gl.GoLowUntyped())
|
ut := reflect.ValueOf(gl.GoLowUntyped())
|
||||||
if !ut.IsNil() {
|
if !ut.IsNil() {
|
||||||
if gl.GoLowUntyped().(low.IsReferenced).IsReference() {
|
if gl.GoLowUntyped().(low.IsReferenced).IsReference() {
|
||||||
rvn := utils.CreateEmptyMapNode()
|
rvn := utils.CreateEmptyMapNode()
|
||||||
rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref"))
|
rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref"))
|
||||||
rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference()))
|
rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference()))
|
||||||
valueNode = rvn
|
valueNode = rvn
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -600,6 +601,7 @@ func (n *NodeBuilder) extractLowMapKeys(fg reflect.Value, x string, found bool,
|
|||||||
return found, orderedCollection
|
return found, orderedCollection
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Renderable is an interface that can be implemented by types that provide a custom MarshaYAML method.
|
||||||
type Renderable interface {
|
type Renderable interface {
|
||||||
MarshalYAML() (interface{}, error)
|
MarshalYAML() (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,12 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel"
|
"github.com/pb33f/libopenapi/datamodel"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/pb33f/libopenapi/datamodel/high"
|
"github.com/pb33f/libopenapi/datamodel/high"
|
||||||
|
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
||||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||||
"github.com/pb33f/libopenapi/resolver"
|
"github.com/pb33f/libopenapi/resolver"
|
||||||
@@ -599,3 +601,69 @@ components:
|
|||||||
// Output: schemaOne 'x-custom-cakes' (some cakes) has 2 cakes, 'someCake' has 10 candles and blue frosting
|
// Output: schemaOne 'x-custom-cakes' (some cakes) has 2 cakes, 'someCake' has 10 candles and blue frosting
|
||||||
//parameterOne 'x-custom-burgers' (some burgers) has 2 burgers, 'anotherBurger' has mayo sauce and a lamb patty
|
//parameterOne 'x-custom-burgers' (some burgers) has 2 burgers, 'anotherBurger' has mayo sauce and a lamb patty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleNewDocument_modifyAndReRender() {
|
||||||
|
|
||||||
|
// How to read in an OpenAPI 3 Specification, into a Document,
|
||||||
|
// modify the document and then re-render it back to YAML bytes.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
v3Model, errors := doc.BuildV3Model()
|
||||||
|
|
||||||
|
// if anything went wrong when building the v3 model, a slice of errors will be returned
|
||||||
|
if len(errors) > 0 {
|
||||||
|
for i := range errors {
|
||||||
|
fmt.Printf("error: %e\n", errors[i])
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new path item and operation.
|
||||||
|
newPath := &v3high.PathItem{
|
||||||
|
Description: "this is a new path item",
|
||||||
|
Get: &v3high.Operation{
|
||||||
|
Description: "this is a get operation",
|
||||||
|
OperationId: "getNewThing",
|
||||||
|
RequestBody: &v3high.RequestBody{
|
||||||
|
Description: "this is a new request body",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture original number of paths
|
||||||
|
originalPaths := len(v3Model.Model.Paths.PathItems)
|
||||||
|
|
||||||
|
// add the path to the document
|
||||||
|
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.
|
||||||
|
rawBytes, _, newModel, errs := doc.RenderAndReload()
|
||||||
|
|
||||||
|
// if anything went wrong when re-rendering the v3 model, a slice of errors will be returned
|
||||||
|
if len(errors) > 0 {
|
||||||
|
panic(fmt.Sprintf("cannot re-render document: %d errors reported", len(errs)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// capture new number of paths after re-rendering
|
||||||
|
newPaths := len(newModel.Model.Paths.PathItems)
|
||||||
|
|
||||||
|
// print the number of paths and schemas in the document
|
||||||
|
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))
|
||||||
|
// Output: There were 13 original paths. There are now 14 paths in the document
|
||||||
|
//The original spec had 31143 bytes, the new one has 27857
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user