mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 12:37:49 +00:00
3.0 and 2.0 do not work, there are multiple versions and anything with a period in it sucks from my point of view, v2 and v3 feel much better from a DX perspective.
147 lines
5.8 KiB
Go
147 lines
5.8 KiB
Go
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
// 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.
|
|
package libopenapi
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/pb33f/libopenapi/datamodel"
|
|
v2high "github.com/pb33f/libopenapi/datamodel/high/v2"
|
|
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
|
v2low "github.com/pb33f/libopenapi/datamodel/low/v2"
|
|
v3low "github.com/pb33f/libopenapi/datamodel/low/v3"
|
|
"github.com/pb33f/libopenapi/utils"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// Document Represents an OpenAPI specification that can then be rendered into a model or serialized back into
|
|
// a string document after being manipulated.
|
|
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)
|
|
}
|
|
|
|
type document struct {
|
|
version string
|
|
info *datamodel.SpecInfo
|
|
}
|
|
|
|
// DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is
|
|
// built from a parent Document.
|
|
type DocumentModel[T v2high.Swagger | v3high.Document] struct {
|
|
Model T
|
|
}
|
|
|
|
// 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.
|
|
func NewDocument(specByteArray []byte) (Document, error) {
|
|
info, err := datamodel.ExtractSpecInfo(specByteArray)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
d := new(document)
|
|
d.version = info.Version
|
|
d.info = info
|
|
return d, nil
|
|
}
|
|
|
|
func (d *document) GetVersion() string {
|
|
return d.version
|
|
}
|
|
|
|
func (d *document) GetSpecInfo() *datamodel.SpecInfo {
|
|
return d.info
|
|
}
|
|
|
|
func (d *document) Serialize() ([]byte, error) {
|
|
if d.info == nil {
|
|
return nil, fmt.Errorf("unable to serialize, document has not yet been initialized")
|
|
}
|
|
if d.info.SpecFileType == datamodel.YAMLFileType {
|
|
return yaml.Marshal(d.info.RootNode)
|
|
} else {
|
|
yamlData, _ := yaml.Marshal(d.info.RootNode)
|
|
return utils.ConvertYAMLtoJSON(yamlData)
|
|
}
|
|
}
|
|
|
|
func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], []error) {
|
|
var errors []error
|
|
if d.info == nil {
|
|
errors = append(errors, fmt.Errorf("unable to build swagger document, no specification has been loaded"))
|
|
return nil, errors
|
|
}
|
|
if d.info.SpecFormat != datamodel.OAS2 {
|
|
errors = append(errors, fmt.Errorf("unable to build swagger document, "+
|
|
"supplied spec is a different version (%v). Try 'BuildV3Model()'", d.info.SpecFormat))
|
|
return nil, errors
|
|
}
|
|
lowDoc, err := v2low.CreateDocument(d.info)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
highDoc := v2high.NewSwaggerDocument(lowDoc)
|
|
return &DocumentModel[v2high.Swagger]{
|
|
Model: *highDoc,
|
|
}, nil
|
|
}
|
|
|
|
func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) {
|
|
var errors []error
|
|
if d.info == nil {
|
|
errors = append(errors, fmt.Errorf("unable to build document, no specification has been loaded"))
|
|
return nil, errors
|
|
}
|
|
if d.info.SpecFormat != datamodel.OAS3 {
|
|
errors = append(errors, fmt.Errorf("unable to build openapi document, "+
|
|
"supplied spec is a different version (%v). Try 'BuildV2Model()'", d.info.SpecFormat))
|
|
return nil, errors
|
|
}
|
|
lowDoc, err := v3low.CreateDocument(d.info)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
highDoc := v3high.NewDocument(lowDoc)
|
|
return &DocumentModel[v3high.Document]{
|
|
Model: *highDoc,
|
|
}, nil
|
|
}
|