mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +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 Swagger](https://pb33f.io/libopenapi/swagger/)
|
||||
- [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/)
|
||||
- [The Index](https://pb33f.io/libopenapi/index/)
|
||||
- [The Resolver](https://pb33f.io/libopenapi/resolver/)
|
||||
|
||||
@@ -48,6 +48,7 @@ type SchemaProxy struct {
|
||||
schema *low.NodeReference[*base.SchemaProxy]
|
||||
buildError error
|
||||
rendered *Schema
|
||||
refStr string
|
||||
}
|
||||
|
||||
// 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}
|
||||
}
|
||||
|
||||
// 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.
|
||||
// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access
|
||||
// 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.
|
||||
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.
|
||||
func (sp *SchemaProxy) GetReference() string {
|
||||
if sp.refStr != "" {
|
||||
return sp.refStr
|
||||
}
|
||||
return sp.schema.Value.GetSchemaReference()
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) {
|
||||
var idxNode yaml.Node
|
||||
err := yaml.Unmarshal([]byte(ymlComponents), &idxNode)
|
||||
assert.NoError(t, err)
|
||||
return index.NewSpecIndex(&idxNode)
|
||||
return index.NewSpecIndexWithConfig(&idxNode, index.CreateOpenAPIIndexConfig())
|
||||
}()
|
||||
|
||||
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)))
|
||||
|
||||
}
|
||||
|
||||
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:
|
||||
if r, ok := value.(Renderable); ok {
|
||||
if gl, lg := value.(GoesLowUntyped); lg {
|
||||
|
||||
ut := reflect.ValueOf(gl.GoLowUntyped())
|
||||
if !ut.IsNil() {
|
||||
if gl.GoLowUntyped().(low.IsReferenced).IsReference() {
|
||||
rvn := utils.CreateEmptyMapNode()
|
||||
rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref"))
|
||||
rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference()))
|
||||
valueNode = rvn
|
||||
break
|
||||
if gl.GoLowUntyped() != nil {
|
||||
ut := reflect.ValueOf(gl.GoLowUntyped())
|
||||
if !ut.IsNil() {
|
||||
if gl.GoLowUntyped().(low.IsReferenced).IsReference() {
|
||||
rvn := utils.CreateEmptyMapNode()
|
||||
rvn.Content = append(rvn.Content, utils.CreateStringNode("$ref"))
|
||||
rvn.Content = append(rvn.Content, utils.CreateStringNode(gl.GoLowUntyped().(low.IsReferenced).GetReference()))
|
||||
valueNode = rvn
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -600,6 +601,7 @@ func (n *NodeBuilder) extractLowMapKeys(fg reflect.Value, x string, found bool,
|
||||
return found, orderedCollection
|
||||
}
|
||||
|
||||
// Renderable is an interface that can be implemented by types that provide a custom MarshaYAML method.
|
||||
type Renderable interface {
|
||||
MarshalYAML() (interface{}, error)
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import (
|
||||
"github.com/pb33f/libopenapi/datamodel"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/pb33f/libopenapi/datamodel/high"
|
||||
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
|
||||
"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
|
||||
//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