libopenapi

package module
v0.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2022 License: MIT Imports: 8 Imported by: 153

README

libopenapi - enterprise grade OpenAPI tools for golang.

Pipeline GoReportCard codecov

libopenapi has full support for Swagger (OpenAPI 2), OpenAPI 3, and OpenAPI 3.1.

Introduction - Why?

There is already a great OpenAPI library for golang, it's called kin-openapi.

So why does this exist?

kin-openapi is great, and you should use it.

However, it's missing one critical feature

When building tooling that needs to analyze OpenAPI specifications at a low level, kin-openapi runs out of power when you need to know the original line numbers and columns, or comments within all keys and values in the spec.

All that data is lost when the spec is loaded in by kin-openapi. It's mainly because the library will unmarshal data directly into structs, which works great - if you don't need access to the original specification low level details.

libopenapi retains everything.

libopenapi has been designed to retain all of that really low-level detail about the AST, line numbers, column numbers, comments, original AST structure - everything you could need.

libopenapi has a porcelain (high-level) and a plumbing (low-level) API. Every high level struct, has the ability to 'GoLow' and dive from the high-level model, down to the low-level model and look-up any detail about the underlying raw data backing that model.

This library exists because this very need existed inside VMware, we built our own internal version of libopenapi, which isn't something that can be released as it's bespoke.

libopenapi is the result of years of learning and battle testing OpenAPI in golang. This library represents what we would have created, if we knew then - what we know now.

If you need to know which line, or column a key or value for something is? libopenapi has you covered

Comes with an Index and a Resolver

Want a lightning fast way to look up any element in an OpenAPI swagger spec? libopenapi has you covered

Need a way to 'resolve' OpenAPI documents that are exploded out across multiple files, remotely or locally? libopenapi has you covered


Some examples to get you started.

Load an OpenAPI 3+ spec into a model
// load an OpenAPI 3 specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")

// create a new document from specification bytes
document, 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 := 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:

There are 13 paths and 8 schemas in the document
Load a Swagger (OpenAPI 2) spec into a model
// load a Swagger specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json")

// create a new document from specification bytes
document, 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 v2 spec, we can build a ready to go model from it.
v2Model, errors := document.BuildV2Model()

// 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(v2Model.Model.Paths.PathItems)
schemas := len(v2Model.Model.Definitions.Definitions)

// 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:

There are 14 paths and 6 schemas in the document
Dropping down from the high-level API to the low-level one

This example shows how after loading an OpenAPI spec into a document, navigating to an Operation is pretty simple. It then shows how to drop-down to the low-level API and query the line and start column of the RequestBody description.

// load an OpenAPI 3 specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")

// create a new document from specification bytes (ignore errors for the same of the example)
document, _ := NewDocument(petstore)

// because we know this is a v3 spec, we can build a ready to go model from it (ignoring errors for the example)
v3Model, _ := document.BuildV3Model()

// extract the RequestBody from the 'put' operation under the /pet path
reqBody := h.Paths.PathItems["/pet"].Put.RequestBody

// dropdown to the low-level API for RequestBody
lowReqBody := reqBody.GoLow() 

// print out the value, the line it appears on and the start columns for the key and value of the nodes.
fmt.Printf("value is %s, the value is on line %d, starting column %d, the key is on line %d, column %d", 
    reqBody.Description, lowReqBody.Description.ValueNode.Line, lowReqBody.Description.ValueNode.Column,
    lowReqBody.Description.KeyNode.Line, lowReqBody.KeyNode.Column)

Documentation

Overview

Package libopenapi is a library containing tools for reading and in and manipulating Swagger (OpenAPI 2) and OpenAPI 3+ specifications into strongly typed documents. These documents have two APIs, a high level (porcelain) and a low level (plumbing).

Every single type has a 'GoLow()' method that drops down from the high API to the low API. Once in the low API, the entire original document data is available, including all comments, line and column numbers for keys and values.

There are two steps to creating a using Document. First, create a new Document using the NewDocument() method and pass in a specification []byte array that contains the OpenAPI Specification. It doesn't matter if YAML or JSON are used.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Document

type Document interface {

	// GetVersion will return the exact version of the OpenAPI specification set for the document.
	GetVersion() string

	// GetSpecInfo will return the *datamodel.SpecInfo instance that contains all specification information.
	GetSpecInfo() *datamodel.SpecInfo

	// BuildV2Model will build out a Swagger (version 2) model from the specification used to create the document
	// If there are any issues, then no model will be returned, instead a slice of errors will explain all the
	// problems that occurred. This method will only support version 2 specifications and will throw an error for
	// any other types.
	BuildV2Model() (*DocumentModel[v2high.Swagger], []error)

	// BuildV3Model will build out an OpenAPI (version 3+) model from the specification used to create the document
	// If there are any issues, then no model will be returned, instead a slice of errors will explain all the
	// problems that occurred. This method will only support version 3 specifications and will throw an error for
	// any other types.
	BuildV3Model() (*DocumentModel[v3high.Document], []error)

	// 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.
	//
	// It's important to know that this should not be used if the resolver has been used on a specification to
	// for anything other than checking for circular references. If the resolver is used to resolve the spec, then this
	// method may spin out forever if the specification backing the model has circular references.
	Serialize() ([]byte, error)
}

Document Represents an OpenAPI specification that can then be rendered into a model or serialized back into a string document after being manipulated.

func NewDocument

func NewDocument(specByteArray []byte) (Document, error)

NewDocument will create a new OpenAPI instance from an OpenAPI specification []byte array. If anything goes wrong when parsing, reading or processing the OpenAPI specification, there will be no document returned, instead a slice of errors will be returned that explain everything that failed.

After creating a Document, the option to build a model becomes available, in either V2 or V3 flavors. The models are about 70% different between Swagger and OpenAPI 3, which is why two different models are available.

Example (FromOpenAPI3Document)
// load an OpenAPI 3 specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json")

// create a new document from specification bytes
document, 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 := 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)
Output:

There are 13 paths and 8 schemas in the document
Example (FromSwaggerDocument)
// load a Swagger specification from bytes
petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json")

// create a new document from specification bytes
document, 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 v2 spec, we can build a ready to go model from it.
v2Model, errors := document.BuildV2Model()

// 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(v2Model.Model.Paths.PathItems)
schemas := len(v2Model.Model.Definitions.Definitions)

// print the number of paths and schemas in the document
fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas)
Output:

There are 14 paths and 6 schemas in the document
Example (FromUnknownVersion)
// load an unknown version of an OpenAPI spec
petstore, _ := ioutil.ReadFile("test_specs/burgershop.openapi.yaml")

// create a new document from specification bytes
document, err := NewDocument(petstore)

// if anything went wrong, an error is thrown
if err != nil {
	panic(fmt.Sprintf("cannot create new document: %e", err))
}

var paths, schemas int
var errors []error

// We don't know which type of document this is, so we can use the spec info to inform us
if document.GetSpecInfo().SpecType == utils.OpenApi3 {
	v3Model, errs := document.BuildV3Model()
	if len(errs) > 0 {
		errors = errs
	}
	if len(errors) <= 0 {
		paths = len(v3Model.Model.Paths.PathItems)
		schemas = len(v3Model.Model.Components.Schemas)
	}
}
if document.GetSpecInfo().SpecType == utils.OpenApi2 {
	v2Model, errs := document.BuildV2Model()
	if len(errs) > 0 {
		errors = errs
	}
	if len(errors) <= 0 {
		paths = len(v2Model.Model.Paths.PathItems)
		schemas = len(v2Model.Model.Definitions.Definitions)
	}
}

// if anything went wrong when building the model, report errors.
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)))
}

// print the number of paths and schemas in the document
fmt.Printf("There are %d paths and %d schemas in the document", paths, schemas)
Output:

There are 5 paths and 6 schemas in the document

type DocumentModel

type DocumentModel[T v2high.Swagger | v3high.Document] struct {
	Model T
}

DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is built from a parent Document.

Directories

Path Synopsis
Package datamodel contains two sets of models, high and low.
Package datamodel contains two sets of models, high and low.
high
Package high contains a set of high-level models that represent OpenAPI 2 and 3 documents.
Package high contains a set of high-level models that represent OpenAPI 2 and 3 documents.
high/base
Package base contains shared high-level models that are used between both versions 2 and 3 of OpenAPI.
Package base contains shared high-level models that are used between both versions 2 and 3 of OpenAPI.
high/v3
Package v3 represents all OpenAPI 3+ high-level models.
Package v3 represents all OpenAPI 3+ high-level models.
low

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL