More v2 high-level docs

started including content from the schema to make it easier to understand when using the code as a complete guide, without pivoting to the spec.
This commit is contained in:
Dave Shanley
2022-09-17 09:57:29 -04:00
parent c17cc4a7e6
commit 649a93b008
17 changed files with 123 additions and 7 deletions

View File

@@ -14,7 +14,7 @@ There is already a great OpenAPI library for golang, it's called [kin-openapi](h
[kin-openapi](https://github.com/getkin/kin-openapi) is great, and you should use it.
**However, it's missing one critical feature**
> **_However, kin-openapi missing one critical feature_**... It's so important, this library exists because of it.
When building tooling that needs to analyze OpenAPI specifications at a *low* level, [kin-openapi](https://github.com/getkin/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.
@@ -23,6 +23,8 @@ All that data is **lost** when the spec is loaded in by [kin-openapi](https://gi
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.
Want to build a linter? Analysis tool? Renderer that retains original positions?
## libopenapi retains _everything_.
libopenapi has been designed to retain all of that really low-level detail about the AST, line numbers, column numbers, comments,
@@ -32,11 +34,11 @@ libopenapi has a **porcelain** (high-level) and a **plumbing** (low-level) API.
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](https://vmware.com), we built our own internal
version of libopenapi, which isn't something that can be released as it's bespoke.
This library exists because this very need existed inside [VMware](https://vmware.com). The company built an internal
version of libopenapi, which isn't something that can be released as it's customized for VMware (and it's incomplete).
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.
libopenapi is the result of years of learning and battle testing OpenAPI in golang. This library represents what would
have been 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**
@@ -153,7 +155,7 @@ document, _ := NewDocument(petstore)
v3Model, _ := document.BuildV3Model()
// extract the RequestBody from the 'put' operation under the /pet path
reqBody := h.Paths.PathItems["/pet"].Put.RequestBody
reqBody := document.Paths.PathItems["/pet"].Put.RequestBody
// dropdown to the low-level API for RequestBody
lowReqBody := reqBody.GoLow()

View File

@@ -8,6 +8,10 @@ import (
)
// Info represents an Info object as defined by both OpenAPI 2 and OpenAPI 3.
//
// The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented
// in editing or documentation generation tools for convenience.
//
// v2 - https://swagger.io/specification/v2/#infoObject
// v3 - https://spec.openapis.org/oas/v3.1.0#info-object
type Info struct {

View File

@@ -6,6 +6,11 @@ package v3
import low "github.com/pb33f/libopenapi/datamodel/low/v3"
// Callback represents a high-level Callback object for OpenAPI 3+.
//
// A map of possible out-of band callbacks related to the parent operation. Each value in the map is a
// PathItem Object that describes a set of requests that may be initiated by the API provider and the expected
// responses. The key value used to identify the path item object is an expression, evaluated at runtime,
// that identifies a URL to use for the callback operation.
// - https://spec.openapis.org/oas/v3.1.0#callback-object
type Callback struct {
Expression map[string]*PathItem

View File

@@ -11,6 +11,7 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// used for internal channel co-ordination for building out different component types.
const (
responses = iota
parameters
@@ -22,6 +23,11 @@ const (
callbacks
)
// Components represents a high-level OpenAPI 3+ Components Object, that is backed by a low-level one.
//
// Holds a set of reusable objects for different aspects of the OAS. All objects defined within the components object
// will have no effect on the API unless they are explicitly referenced from properties outside the components object.
// - https://spec.openapis.org/oas/v3.1.0#components-object
type Components struct {
Schemas map[string]*highbase.SchemaProxy
Responses map[string]*Response
@@ -36,6 +42,9 @@ type Components struct {
low *low.Components
}
// NewComponents will create new high-level instance of Components from a low-level one. Components can be considerable
// in scope, with a lot of different properties across different categories. All components are built asynchronously
// in order to keep things fast.
func NewComponents(comp *low.Components) *Components {
c := new(Components)
c.low = comp
@@ -140,16 +149,19 @@ func NewComponents(comp *low.Components) *Components {
return c
}
// contains a component build result.
type componentResult[T any] struct {
res T
key string
comp int
}
// build out a component.
func buildComponent[N any, O any](comp int, key string, orig O, c chan componentResult[N], f func(O) N) {
c <- componentResult[N]{comp: comp, res: f(orig), key: key}
}
// build out a schema
func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference[*base.SchemaProxy], c chan componentResult[*highbase.SchemaProxy]) {
var sch *highbase.SchemaProxy
sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
@@ -159,6 +171,7 @@ func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference
c <- componentResult[*highbase.SchemaProxy]{res: sch, key: key.Value}
}
// GoLow returns the low-level Components instance used to create the high-level one.
func (c *Components) GoLow() *low.Components {
return c.low
}

View File

@@ -36,7 +36,7 @@ func (e *Encoding) GoLow() *low.Encoding {
return e.low
}
// ExtractEncoding converts hard to navigate low-level plumbing Encoding definitions, into high-level simple map
// ExtractEncoding converts hard to navigate low-level plumbing Encoding definitions, into a high-level simple map
func ExtractEncoding(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.Encoding]) map[string]*Encoding {
extracted := make(map[string]*Encoding)
for k, v := range elements {

View File

@@ -9,6 +9,16 @@ import (
)
// Link represents an OpenAPI 3+ Link object that is backed by a low-level one.
//
// The Link object represents a possible design-time link for a response. The presence of a link does not guarantee the
// callers ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between
// responses and other operations.
//
// Unlike dynamic links (i.e. links provided in the response payload), the OAS linking mechanism does not require
// link information in the runtime response.
//
// For computing links, and providing instructions to execute them, a runtime expression is used for accessing values
// in an operation and using them as parameters while invoking the linked operation.
// - https://spec.openapis.org/oas/v3.1.0#link-object
type Link struct {
OperationRef string

View File

@@ -12,6 +12,8 @@ import (
)
// MediaType represents a high-level OpenAPI MediaType object that is backed by a low-level one.
//
// Each Media Type Object provides schema and examples for the media type identified by its key.
// - https://spec.openapis.org/oas/v3.1.0#media-type-object
type MediaType struct {
Schema *base.SchemaProxy
@@ -22,6 +24,7 @@ type MediaType struct {
low *low.MediaType
}
// NewMediaType will create a new high-level MediaType instance from a low-level one.
func NewMediaType(mediaType *low.MediaType) *MediaType {
m := new(MediaType)
m.low = mediaType
@@ -35,10 +38,13 @@ func NewMediaType(mediaType *low.MediaType) *MediaType {
return m
}
// GoLow will return the low-level instance of MediaType used to create the high-level one.
func (m *MediaType) GoLow() *low.MediaType {
return m.low
}
// ExtractContent takes in a complex and hard to navigate low-level content map, and converts it in to a much simpler
// and easier to navigate high-level one.
func ExtractContent(elements map[lowmodel.KeyReference[string]]lowmodel.ValueReference[*low.MediaType]) map[string]*MediaType {
// extract everything async
doneChan := make(chan bool)

View File

@@ -8,6 +8,8 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// OAuthFlow represents a high-level OpenAPI 3+ OAuthFlow object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object
type OAuthFlow struct {
AuthorizationUrl string
TokenUrl string
@@ -17,6 +19,7 @@ type OAuthFlow struct {
low *low.OAuthFlow
}
// NewOAuthFlow creates a new high-level OAuthFlow instance from a low-level one.
func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
o := new(OAuthFlow)
o.low = flow
@@ -32,6 +35,7 @@ func NewOAuthFlow(flow *low.OAuthFlow) *OAuthFlow {
return o
}
// GoLow returns the low-level OAuthFlow instance used to create the high-level one.
func (o *OAuthFlow) GoLow() *low.OAuthFlow {
return o.low
}

View File

@@ -8,6 +8,8 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// OAuthFlows represents a high-level OpenAPI 3+ OAuthFlows object that is backed by a low-level one.
// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object
type OAuthFlows struct {
Implicit *OAuthFlow
Password *OAuthFlow
@@ -17,6 +19,7 @@ type OAuthFlows struct {
low *low.OAuthFlows
}
// NewOAuthFlows creates a new high-level OAuthFlows instance from a low-level one.
func NewOAuthFlows(flows *low.OAuthFlows) *OAuthFlows {
o := new(OAuthFlows)
o.low = flows
@@ -39,6 +42,7 @@ func NewOAuthFlows(flows *low.OAuthFlows) *OAuthFlows {
return o
}
// GoLow returns the low-level OAuthFlows instance used to create the high-level one.
func (o *OAuthFlows) GoLow() *low.OAuthFlows {
return o.low
}

View File

@@ -8,6 +8,10 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// Operation is a high-level representation of an OpenAPI 3+ Operation object, backed by a low-level one.
// An Operation is perhaps the most important object of the entire specification. Everything of value
// happens here. The entire being for existence of this library and the specification, is this Operation.
// - https://spec.openapis.org/oas/v3.1.0#operation-object
type Operation struct {
Tags []string
Summary string
@@ -25,6 +29,7 @@ type Operation struct {
low *low.Operation
}
// NewOperation will create a new Operation instance from a low-level one.
func NewOperation(operation *low.Operation) *Operation {
o := new(Operation)
o.low = operation
@@ -65,6 +70,7 @@ func NewOperation(operation *low.Operation) *Operation {
return o
}
// GoLow will return the low-level Operation instance that was used to create the high-level one.
func (o *Operation) GoLow() *low.Operation {
return o.low
}

View File

@@ -9,6 +9,10 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// Parameter represents a high-level OpenAPI 3+ Parameter object, that is backed by a low-level one.
//
// A unique parameter is defined by a combination of a name and location.
// - https://spec.openapis.org/oas/v3.1.0#parameter-object
type Parameter struct {
Name string
In string
@@ -27,6 +31,7 @@ type Parameter struct {
low *low.Parameter
}
// NewParameter will create a new high-level instance of a Parameter, using a low-level one.
func NewParameter(param *low.Parameter) *Parameter {
p := new(Parameter)
p.low = param
@@ -49,6 +54,7 @@ func NewParameter(param *low.Parameter) *Parameter {
return p
}
// GoLow returns the low-level Parameter used to create the high-level one.
func (p *Parameter) GoLow() *low.Parameter {
return p.low
}

View File

@@ -19,6 +19,12 @@ const (
trace
)
// PathItem represents a high-level OpenAPI 3+ PathItem object backed by a low-level one.
//
// Describes the operations available on a single path. A Path Item MAY be empty, due to ACL constraints.
// The path itself is still exposed to the documentation viewer but they will not know which operations and parameters
// are available.
// - https://spec.openapis.org/oas/v3.1.0#path-item-object
type PathItem struct {
Description string
Summary string
@@ -36,6 +42,7 @@ type PathItem struct {
low *low.PathItem
}
// NewPathItem creates a new high-level PathItem instance from a low-level one.
func NewPathItem(pathItem *low.PathItem) *PathItem {
pi := new(PathItem)
pi.low = pathItem
@@ -111,6 +118,7 @@ func NewPathItem(pathItem *low.PathItem) *PathItem {
return pi
}
// GoLow returns the low level instance of PathItem, used to build the high-level one.
func (p *PathItem) GoLow() *low.PathItem {
return p.low
}

View File

@@ -8,12 +8,19 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// Paths represents a high-level OpenAPI 3+ Paths object, that is backed by a low-level one.
//
// Holds the relative paths to the individual endpoints and their operations. The path is appended to the URL from the
// Server Object in order to construct the full URL. The Paths MAY be empty, due to Access Control List (ACL)
// constraints.
// - https://spec.openapis.org/oas/v3.1.0#paths-object
type Paths struct {
PathItems map[string]*PathItem
Extensions map[string]any
low *low.Paths
}
// NewPaths creates a new high-level instance of Paths from a low-level one.
func NewPaths(paths *low.Paths) *Paths {
p := new(Paths)
p.low = paths
@@ -44,6 +51,7 @@ func NewPaths(paths *low.Paths) *Paths {
return p
}
// GoLow returns the low-level Paths instance used to create the high-level one.
func (p *Paths) GoLow() *low.Paths {
return p.low
}

View File

@@ -8,6 +8,7 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// RequestBody represents a high-level OpenAPI 3+ RequestBody object,
type RequestBody struct {
Description string
Content map[string]*MediaType

View File

@@ -8,6 +8,11 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// Response represents a high-level OpenAPI 3+ Response object that is backed by a low-level one.
//
// Describes a single response from an API Operation, including design-time, static links to
// operations based on the response.
// - https://spec.openapis.org/oas/v3.1.0#response-object
type Response struct {
Description string
Headers map[string]*Header
@@ -17,6 +22,7 @@ type Response struct {
low *low.Response
}
// NewResponse creates a new high-level Response object that is backed by a low-level one.
func NewResponse(response *low.Response) *Response {
r := new(Response)
r.low = response
@@ -38,6 +44,7 @@ func NewResponse(response *low.Response) *Response {
return r
}
// GoLow returns the low-level Response object that was used to create the high-level one.
func (r *Response) GoLow() *low.Response {
return r.low
}

View File

@@ -8,12 +8,28 @@ import (
low "github.com/pb33f/libopenapi/datamodel/low/v3"
)
// Responses represents a high-level OpenAPI 3+ Responses object that is backed by a low-level one.
//
// It's a container for the expected responses of an operation. The container maps a HTTP response code to the
// expected response.
//
// The specification is not necessarily expected to cover all possible HTTP response codes because they may not be
// known in advance. However, documentation is expected to cover a successful operation response and any known errors.
//
// The default MAY be used as a default response object for all HTTP codes that are not covered individually by
// the Responses Object.
//
// The Responses Object MUST contain at least one response code, and if only one response code is provided it SHOULD
// be the response for a successful operation call.
// - https://spec.openapis.org/oas/v3.1.0#responses-object
type Responses struct {
Codes map[string]*Response
Default *Response
low *low.Responses
}
// NewResponses will create a new high-level Responses instance from a low-level one. It operates asynchronously
// internally, as each response may be considerable in complexity.
func NewResponses(response *low.Responses) *Responses {
r := new(Responses)
r.low = response
@@ -49,10 +65,12 @@ func NewResponses(response *low.Responses) *Responses {
return r
}
// FindResponseByCode is a shortcut for looking up code by an integer vs. a string
func (r *Responses) FindResponseByCode(code int) *Response {
return r.Codes[fmt.Sprintf("%d", code)]
}
// GoLow returns the low-level Response object used to create the high-level one.
func (r *Responses) GoLow() *low.Responses {
return r.low
}

View File

@@ -5,11 +5,24 @@ package v3
import low "github.com/pb33f/libopenapi/datamodel/low/v3"
// SecurityRequirement is a high-level representation of an OpenAPI 3+ SecurityRequirement object that is backed
// by a low-level one.
//
// It lists the required security schemes to execute this operation. The name used for each property MUST correspond
// to a security scheme declared in the Security Schemes under the Components Object.
//
// Security Requirement Objects that contain multiple schemes require that all schemes MUST be satisfied for a
// request to be authorized. This enables support for scenarios where multiple query parameters or HTTP headers are required to convey security information.
//
// When a list of Security Requirement Objects is defined on the OpenAPI Object or Operation Object, only one of the
// Security Requirement Objects in the list needs to be satisfied to authorize the request.
// - https://spec.openapis.org/oas/v3.1.0#security-requirement-object
type SecurityRequirement struct {
ValueRequirements []map[string][]string
low *low.SecurityRequirement
}
// NewSecurityRequirement will create a new high-level SecurityRequirement instance, from a low-level one.
func NewSecurityRequirement(req *low.SecurityRequirement) *SecurityRequirement {
r := new(SecurityRequirement)
r.low = req
@@ -29,6 +42,7 @@ func NewSecurityRequirement(req *low.SecurityRequirement) *SecurityRequirement {
return r
}
// GoLow returns the low-level SecurityRequirement instance used to create the high-level one.
func (s *SecurityRequirement) GoLow() *low.SecurityRequirement {
return s.low
}