How to parse an OpenAPI 3 specification
Using OpenAPI 3.0 and 3.1Loading a model from a specification
There are two steps to creating a model, creating a document and then building a model from that document.
Creating a new document
First we need to create a reference to a Document
that we create using NewDocument()
and pass in a []byte
slice that contains the specification.
For example:
// load an OpenAPI 3 specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")
// create a new document from specification bytes
document, err := libopenapi.NewDocument(petstore)
Documents with relative or remote references
If the specification has lots of relative or remote references, (just like the
DigitalOcean OpenAPI Specification), then a datamodel.DocumentConfigutation
is required to set a BaseURL
or a BasePath
and allow the library to resolve the references by making HTTP requests or loading
files from the local file system.
The NewDocumentWithConfiguration()
method can be used to create a document with a configuration.
// Digital Ocean needs a baseURL to be set, so we can resolve relative references.
baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/main/specification")
// create a DocumentConfiguration that allows loading file and remote references, and sets the baseURL
// to somewhere that can resolve the relative references.
config := datamodel.DocumentConfiguration{
AllowFileReferences: true,
AllowRemoteReferences: true,
BaseURL: baseURL,
}
// create a new document from specification bytes
doc, err := NewDocumentWithConfiguration(digitalOceanBytes, &config)
Or you can use a BasePath
instead of a BaseURL
if all the files are local, but they are in a different
working directory.
// create a DocumentConfiguration that allows loading file and remote references, and sets the BasePath
// to somewhere other than the local working directory.
config := datamodel.DocumentConfiguration{
AllowFileReferences: true,
AllowRemoteReferences: true,
BasePath: "../../some/path/to/files",
}
// create a new document from specification bytes
doc, err := NewDocumentWithConfiguration(digitalOceanBytes, &config)
Building a model from the document
Once we have the Document
pointer, we can then build a model from the document.
Document
first, is because the library supports both Swagger and OpenAPI models. The
model is generated from the Document, depending on what version of specification you have read in.Here is an example of doing that, if we know we’re looking at an OpenAPI 3 document.
import (
"fmt"
"github.com/pb33f/libopenapi"
"io/ioutil"
)
func main() {
// load an OpenAPI 3 specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")
// create a new document from specification bytes
document, err := libopenapi.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 := document.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)))
}
// get a count of the number of paths and schemas.
paths := len(v3Model.Model.Paths.PathItems)
schemas := len(v3Model.Model.Components.Schemas)
// print the number of paths and schemas in the document
fmt.Printf("There are %d paths and %d schemas " +
"in the document", paths, schemas)
}
This will print the following to the console: