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:
Dave Shanley
2023-03-25 15:14:46 -04:00
parent 04eac2abe7
commit c549d9f62d
5 changed files with 115 additions and 11 deletions

View File

@@ -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/)

View File

@@ -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 {
if sp.refStr != "" {
return true
}
if sp.schema != nil {
return sp.schema.Value.IsSchemaReference() 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()
} }

View File

@@ -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())
}

View File

@@ -474,7 +474,7 @@ 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() {
@@ -486,6 +486,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod
} }
} }
} }
}
rawRender, _ := r.MarshalYAML() rawRender, _ := r.MarshalYAML()
if rawRender != nil { if rawRender != nil {
valueNode = rawRender.(*yaml.Node) valueNode = rawRender.(*yaml.Node)
@@ -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)
} }

View File

@@ -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
}