v2 low-level docs are now in also.

That is now 6/6! Time to tune up the README again next.
This commit is contained in:
Dave Shanley
2022-09-21 09:24:40 -04:00
parent f9016b8414
commit 6214babaec
19 changed files with 479 additions and 215 deletions

View File

@@ -10,8 +10,8 @@ import (
"io/ioutil" "io/ioutil"
) )
// Creating a new high-level OpenAPI 3+ document from an OpenAPI specification. // An example of how to create a new high-level OpenAPI 3+ document from an OpenAPI specification.
func Example() { func Example_createHighLevelOpenAPIDocument() {
// Load in an OpenAPI 3+ specification as a byte slice. // Load in an OpenAPI 3+ specification as a byte slice.
data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json")

View File

@@ -0,0 +1,25 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v2
const (
DefinitionsLabel = "definitions"
SecurityDefinitionsLabel = "securityDefinitions"
ExamplesLabel = "examples"
HeadersLabel = "headers"
DefaultLabel = "default"
ItemsLabel = "items"
ParametersLabel = "parameters"
PathsLabel = "paths"
GetLabel = "get"
PostLabel = "post"
PatchLabel = "patch"
PutLabel = "put"
DeleteLabel = "delete"
OptionsLabel = "options"
HeadLabel = "head"
SecurityLabel = "security"
ScopesLabel = "scopes"
ResponsesLabel = "responses"
)

View File

@@ -10,43 +10,63 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // ParameterDefinitions is a low-level representation of a Swagger / OpenAPI 2 Parameters Definitions object.
DefinitionsLabel = "definitions" //
SecurityDefinitionsLabel = "securityDefinitions" // ParameterDefinitions holds parameters to be reused across operations. Parameter definitions can be
) // referenced to the ones defined here. It does not define global operation parameters
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
type ParameterDefinitions struct { type ParameterDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter] Definitions map[low.KeyReference[string]]low.ValueReference[*Parameter]
} }
// ResponsesDefinitions is a low-level representation of a Swagger / OpenAPI 2 Responses Definitions object.
//
// ResponsesDefinitions is an object to hold responses to be reused across operations. Response definitions can be
// referenced to the ones defined here. It does not define global operation responses
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
type ResponsesDefinitions struct { type ResponsesDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*Response] Definitions map[low.KeyReference[string]]low.ValueReference[*Response]
} }
// SecurityDefinitions is a low-level representation of a Swagger / OpenAPI 2 Security Definitions object.
//
// A declaration of the security schemes available to be used in the specification. This does not enforce the security
// schemes on the operations and only serves to provide the relevant details for each scheme
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityDefinitions struct { type SecurityDefinitions struct {
Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme] Definitions map[low.KeyReference[string]]low.ValueReference[*SecurityScheme]
} }
// Definitions is a low-level representation of a Swagger / OpenAPI 2 Definitions object
//
// An object to hold data types that can be consumed and produced by operations. These data types can be primitives,
// arrays or models.
// - https://swagger.io/specification/v2/#definitionsObject
type Definitions struct { type Definitions struct {
Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy] Schemas map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]
} }
// FindSchema will attempt to locate a base.SchemaProxy instance using a name.
func (d *Definitions) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] { func (d *Definitions) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] {
return low.FindItemInMap[*base.SchemaProxy](schema, d.Schemas) return low.FindItemInMap[*base.SchemaProxy](schema, d.Schemas)
} }
func (pd *ParameterDefinitions) FindParameter(schema string) *low.ValueReference[*Parameter] { // FindParameter will attempt to locate a Parameter instance using a name.
return low.FindItemInMap[*Parameter](schema, pd.Definitions) func (pd *ParameterDefinitions) FindParameter(parameter string) *low.ValueReference[*Parameter] {
return low.FindItemInMap[*Parameter](parameter, pd.Definitions)
} }
func (r *ResponsesDefinitions) FindResponse(schema string) *low.ValueReference[*Response] { // FindResponse will attempt to locate a Response instance using a name.
return low.FindItemInMap[*Response](schema, r.Definitions) func (r *ResponsesDefinitions) FindResponse(response string) *low.ValueReference[*Response] {
return low.FindItemInMap[*Response](response, r.Definitions)
} }
func (s *SecurityDefinitions) FindSecurityDefinition(schema string) *low.ValueReference[*SecurityScheme] { // FindSecurityDefinition will attempt to locate a SecurityScheme using a name.
return low.FindItemInMap[*SecurityScheme](schema, s.Definitions) func (s *SecurityDefinitions) FindSecurityDefinition(securityDef string) *low.ValueReference[*SecurityScheme] {
return low.FindItemInMap[*SecurityScheme](securityDef, s.Definitions)
} }
// Build will extract all definitions into SchemaProxy instances.
func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error { func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error) errorChan := make(chan error)
resultChan := make(chan definitionResult[*base.SchemaProxy]) resultChan := make(chan definitionResult[*base.SchemaProxy])
@@ -58,12 +78,16 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
continue continue
} }
totalDefinitions++ totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*base.SchemaProxy], e chan error) { var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
r chan definitionResult[*base.SchemaProxy], e chan error) {
obj, err := low.ExtractObjectRaw[*base.SchemaProxy](value, idx) obj, err := low.ExtractObjectRaw[*base.SchemaProxy](value, idx)
if err != nil { if err != nil {
e <- err e <- err
} }
r <- definitionResult[*base.SchemaProxy]{k: label, v: low.ValueReference[*base.SchemaProxy]{Value: obj, ValueNode: value}} r <- definitionResult[*base.SchemaProxy]{k: label, v: low.ValueReference[*base.SchemaProxy]{
Value: obj, ValueNode: value,
}}
} }
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan) go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
} }
@@ -86,6 +110,7 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
return nil return nil
} }
// Build will extract all ParameterDefinitions into Parameter instances.
func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error { func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error) errorChan := make(chan error)
resultChan := make(chan definitionResult[*Parameter]) resultChan := make(chan definitionResult[*Parameter])
@@ -97,7 +122,9 @@ func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) err
continue continue
} }
totalDefinitions++ totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*Parameter], e chan error) { var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
r chan definitionResult[*Parameter], e chan error) {
obj, err := low.ExtractObjectRaw[*Parameter](value, idx) obj, err := low.ExtractObjectRaw[*Parameter](value, idx)
if err != nil { if err != nil {
e <- err e <- err
@@ -125,11 +152,13 @@ func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) err
return nil return nil
} }
// re-usable struct for holding results as k/v pairs.
type definitionResult[T any] struct { type definitionResult[T any] struct {
k *yaml.Node k *yaml.Node
v low.ValueReference[T] v low.ValueReference[T]
} }
// Build will extract all ResponsesDefinitions into Response instances.
func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error { func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error) errorChan := make(chan error)
resultChan := make(chan definitionResult[*Response]) resultChan := make(chan definitionResult[*Response])
@@ -141,7 +170,9 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
continue continue
} }
totalDefinitions++ totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*Response], e chan error) { var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
r chan definitionResult[*Response], e chan error) {
obj, err := low.ExtractObjectRaw[*Response](value, idx) obj, err := low.ExtractObjectRaw[*Response](value, idx)
if err != nil { if err != nil {
e <- err e <- err
@@ -169,6 +200,7 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
return nil return nil
} }
// Build will extract all SecurityDefinitions into SecurityScheme instances.
func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error { func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error) errorChan := make(chan error)
resultChan := make(chan definitionResult[*SecurityScheme]) resultChan := make(chan definitionResult[*SecurityScheme])
@@ -180,12 +212,16 @@ func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error
continue continue
} }
totalDefinitions++ totalDefinitions++
var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex, r chan definitionResult[*SecurityScheme], e chan error) { var buildFunc = func(label *yaml.Node, value *yaml.Node, idx *index.SpecIndex,
r chan definitionResult[*SecurityScheme], e chan error) {
obj, err := low.ExtractObjectRaw[*SecurityScheme](value, idx) obj, err := low.ExtractObjectRaw[*SecurityScheme](value, idx)
if err != nil { if err != nil {
e <- err e <- err
} }
r <- definitionResult[*SecurityScheme]{k: label, v: low.ValueReference[*SecurityScheme]{Value: obj, ValueNode: value}} r <- definitionResult[*SecurityScheme]{k: label, v: low.ValueReference[*SecurityScheme]{
Value: obj, ValueNode: value,
}}
} }
go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan) go buildFunc(defLabel, root.Content[i], idx, resultChan, errorChan)
} }

View File

@@ -9,18 +9,19 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // Examples represents a low-level Swagger / OpenAPI 2 Example object.
ExamplesLabel = "examples" // Allows sharing examples for operation responses
) // - https://swagger.io/specification/v2/#exampleObject
type Examples struct { type Examples struct {
Values map[low.KeyReference[string]]low.ValueReference[any] Values map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindExample attempts to locate an example value, using a key label.
func (e *Examples) FindExample(name string) *low.ValueReference[any] { func (e *Examples) FindExample(name string) *low.ValueReference[any] {
return low.FindItemInMap[any](name, e.Values) return low.FindItemInMap[any](name, e.Values)
} }
// Build will extract all examples and will attempt to unmarshal content into a map or slice based on type.
func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error { func (e *Examples) Build(root *yaml.Node, _ *index.SpecIndex) error {
var keyNode, currNode *yaml.Node var keyNode, currNode *yaml.Node
var err error var err error

View File

@@ -10,10 +10,10 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // Header Represents a low-level Swagger / OpenAPI 2 Header object.
HeadersLabel = "headers" //
) // A Header is essentially identical to a Parameter, except it does not contain 'name' or 'in' properties.
// - https://swagger.io/specification/v2/#headerObject
type Header struct { type Header struct {
Type low.NodeReference[string] Type low.NodeReference[string]
Format low.NodeReference[string] Format low.NodeReference[string]
@@ -36,10 +36,12 @@ type Header struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindExtension will attempt to locate an extension value using a name lookup.
func (h *Header) FindExtension(ext string) *low.ValueReference[any] { func (h *Header) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, h.Extensions) return low.FindItemInMap[any](ext, h.Extensions)
} }
// Build will build out items, extensions and default value from the supplied node.
func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error { func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error {
h.Extensions = low.ExtractExtensions(root) h.Extensions = low.ExtractExtensions(root)
items, err := low.ExtractObject[*Items](ItemsLabel, root, idx) items, err := low.ExtractObject[*Items](ItemsLabel, root, idx)

View File

@@ -10,11 +10,11 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // Items is a low-level representation of a Swagger / OpenAPI 2 Items object.
DefaultLabel = "default" //
ItemsLabel = "items" // Items is a limited subset of JSON-Schema's items object. It is used by parameter definitions that are not
) // located in "body"
// - https://swagger.io/specification/v2/#itemsObject
type Items struct { type Items struct {
Type low.NodeReference[string] Type low.NodeReference[string]
Format low.NodeReference[string] Format low.NodeReference[string]
@@ -35,6 +35,7 @@ type Items struct {
MultipleOf low.NodeReference[int] MultipleOf low.NodeReference[int]
} }
// Build will build out items and default value.
func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error { func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
items, iErr := low.ExtractObject[*Items](ItemsLabel, root, idx) items, iErr := low.ExtractObject[*Items](ItemsLabel, root, idx)
if iErr != nil { if iErr != nil {

View File

@@ -10,6 +10,10 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Operation represents a low-level Swagger / OpenAPI 2 Operation object.
//
// It describes a single API operation on a path.
// - https://swagger.io/specification/v2/#operationObject
type Operation struct { type Operation struct {
Tags low.NodeReference[[]low.ValueReference[string]] Tags low.NodeReference[[]low.ValueReference[string]]
Summary low.NodeReference[string] Summary low.NodeReference[string]
@@ -26,6 +30,7 @@ type Operation struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// Build will extract external docs, extensions, parameters, responses and security requirements.
func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error { func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error {
o.Extensions = low.ExtractExtensions(root) o.Extensions = low.ExtractExtensions(root)

View File

@@ -0,0 +1,66 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package v2
import (
"fmt"
"github.com/pb33f/libopenapi/datamodel"
"io/ioutil"
)
// How to create a low-level Swagger / OpenAPI 2 Document from a specification
func Example_createLowLevelSwaggerDocument() {
// How to create a low-level OpenAPI 2 Document
// load petstore into bytes
petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev2.json")
// read in specification
info, _ := datamodel.ExtractSpecInfo(petstoreBytes)
// build low-level document model
document, errors := CreateDocument(info)
// if something went wrong, a slice of errors is returned
if len(errors) > 0 {
for i := range errors {
fmt.Printf("error: %s\n", errors[i].Error())
}
panic("cannot build document")
}
// print out email address from the info > contact object.
fmt.Print(document.Info.Value.Contact.Value.Email.Value)
// Output: apiteam@swagger.io
}
// How to create a low-level Swagger / OpenAPI 2 Document from a specification
func ExampleCreateDocument() {
// How to create a low-level OpenAPI 2 Document
// load petstore into bytes
petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev2.json")
// read in specification
info, _ := datamodel.ExtractSpecInfo(petstoreBytes)
// build low-level document model
document, errors := CreateDocument(info)
// if something went wrong, a slice of errors is returned
if len(errors) > 0 {
for i := range errors {
fmt.Printf("error: %s\n", errors[i].Error())
}
panic("cannot build document")
}
// print out email address from the info > contact object.
fmt.Print(document.Info.Value.Contact.Value.Email.Value)
// Output: apiteam@swagger.io
}

View File

@@ -11,10 +11,37 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // Parameter represents a low-level Swagger / OpenAPI 2 Parameter object.
ParametersLabel = "parameters" //
) // A unique parameter is defined by a combination of a name and location.
//
// There are five possible parameter types.
//
// Path
// Used together with Path Templating, where the parameter value is actually part of the operation's URL.
// This does not include the host or base path of the API. For example, in /items/{itemId}, the path parameter is itemId.
// Query
// Parameters that are appended to the URL. For example, in /items?id=###, the query parameter is id.
// Header
// Custom headers that are expected as part of the request.
// Body
// The payload that's appended to the HTTP request. Since there can only be one payload, there can only be one body parameter.
// The name of the body parameter has no effect on the parameter itself and is used for documentation purposes only.
// Since Form parameters are also in the payload, body and form parameters cannot exist together for the same operation.
// Form
// Used to describe the payload of an HTTP request when either application/x-www-form-urlencoded, multipart/form-data
// or both are used as the content type of the request (in Swagger's definition, the consumes property of an operation).
// This is the only parameter type that can be used to send files, thus supporting the file type. Since form parameters
// are sent in the payload, they cannot be declared together with a body parameter for the same operation. Form
// parameters have a different format based on the content-type used (for further details,
// consult http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4):
// application/x-www-form-urlencoded - Similar to the format of Query parameters but as a payload. For example,
// foo=1&bar=swagger - both foo and bar are form parameters. This is normally used for simple parameters that are
// being transferred.
// multipart/form-data - each parameter takes a section in the payload with an internal header. For example, for
// the header Content-Disposition: form-data; name="submit-name" the name of the parameter is
// submit-name. This type of form parameters is more commonly used for file transfers
// https://swagger.io/specification/v2/#parameterObject
type Parameter struct { type Parameter struct {
Name low.NodeReference[string] Name low.NodeReference[string]
In low.NodeReference[string] In low.NodeReference[string]
@@ -42,10 +69,12 @@ type Parameter struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindExtension attempts to locate a extension value given a name.
func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] { func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, p.Extensions) return low.FindItemInMap[any](ext, p.Extensions)
} }
// Build will extract out extensions, schema, items and default value
func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error { func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = low.ExtractExtensions(root) p.Extensions = low.ExtractExtensions(root)
sch, sErr := base.ExtractSchema(root, idx) sch, sErr := base.ExtractSchema(root, idx)

View File

@@ -11,6 +11,12 @@ import (
"sync" "sync"
) )
// PathItem represents a low-level Swagger / OpenAPI 2 PathItem object.
//
// 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 tooling, but will not know which operations and parameters
// are available.
// - https://swagger.io/specification/v2/#pathItemObject
type PathItem struct { type PathItem struct {
Ref low.NodeReference[string] Ref low.NodeReference[string]
Get low.NodeReference[*Operation] Get low.NodeReference[*Operation]
@@ -24,10 +30,13 @@ type PathItem struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindExtension will attempt to locate an extension given a name.
func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] { func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, p.Extensions) return low.FindItemInMap[any](ext, p.Extensions)
} }
// Build will extract extensions, parameters and operations for all methods. Every method is handled
// asynchronously, in order to keep things moving quickly for complex operations.
func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error { func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = low.ExtractExtensions(root) p.Extensions = low.ExtractExtensions(root)
skip := false skip := false

View File

@@ -10,22 +10,13 @@ import (
"strings" "strings"
) )
const ( // Paths represents a low-level Swagger / OpenAPI Paths object.
PathsLabel = "paths"
GetLabel = "get"
PostLabel = "post"
PatchLabel = "patch"
PutLabel = "put"
DeleteLabel = "delete"
OptionsLabel = "options"
HeadLabel = "head"
)
type Paths struct { type Paths struct {
PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem] PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindPath attempts to locate a PathItem instance, given a path key.
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] { func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
for k, j := range p.PathItems { for k, j := range p.PathItems {
if k.Value == path { if k.Value == path {
@@ -35,10 +26,12 @@ func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
return nil return nil
} }
// FindExtension will attempt to locate an extension value given a name.
func (p *Paths) FindExtension(ext string) *low.ValueReference[any] { func (p *Paths) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, p.Extensions) return low.FindItemInMap[any](ext, p.Extensions)
} }
// Build will extract extensions and paths from node.
func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error {
p.Extensions = low.ExtractExtensions(root) p.Extensions = low.ExtractExtensions(root)
skip := false skip := false

View File

@@ -10,10 +10,10 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
ResponsesLabel = "responses" //
) // Response describes a single response from an API Operation
// - https://swagger.io/specification/v2/#responseObject
type Response struct { type Response struct {
Description low.NodeReference[string] Description low.NodeReference[string]
Schema low.NodeReference[*base.SchemaProxy] Schema low.NodeReference[*base.SchemaProxy]
@@ -22,14 +22,17 @@ type Response struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindExtension will attempt to locate an extension value given a key to lookup.
func (r *Response) FindExtension(ext string) *low.ValueReference[any] { func (r *Response) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, r.Extensions) return low.FindItemInMap[any](ext, r.Extensions)
} }
// FindHeader will attempt to locate a Header value, given a key
func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] { func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] {
return low.FindItemInMap[*Header](hType, r.Headers.Value) return low.FindItemInMap[*Header](hType, r.Headers.Value)
} }
// Build will extract schema, extensions, examples and headers from node
func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error { func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error {
r.Extensions = low.ExtractExtensions(root) r.Extensions = low.ExtractExtensions(root)
s, err := base.ExtractSchema(root, idx) s, err := base.ExtractSchema(root, idx)

View File

@@ -11,12 +11,14 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Responses is a low-level representation of a Swagger / OpenAPI 2 Responses object.
type Responses struct { type Responses struct {
Codes map[low.KeyReference[string]]low.ValueReference[*Response] Codes map[low.KeyReference[string]]low.ValueReference[*Response]
Default low.NodeReference[*Response] Default low.NodeReference[*Response]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// Build will extract default value and extensions from node.
func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error { func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
r.Extensions = low.ExtractExtensions(root) r.Extensions = low.ExtractExtensions(root)
@@ -42,6 +44,7 @@ func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
return nil return nil
} }
// FindResponseByCode will attempt to locate a Response instance using an HTTP response code string.
func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] { func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] {
return low.FindItemInMap[*Response](code, r.Codes) return low.FindItemInMap[*Response](code, r.Codes)
} }

View File

@@ -10,15 +10,21 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// Scopes is a low-level representation of a Swagger / OpenAPI 2 OAuth2 Scopes object.
//
// Scopes lists the available scopes for an OAuth2 security scheme.
// - https://swagger.io/specification/v2/#scopesObject
type Scopes struct { type Scopes struct {
Values map[low.KeyReference[string]]low.ValueReference[string] Values map[low.KeyReference[string]]low.ValueReference[string]
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// FindScope will attempt to locate a scope string using a key.
func (s *Scopes) FindScope(scope string) *low.ValueReference[string] { func (s *Scopes) FindScope(scope string) *low.ValueReference[string] {
return low.FindItemInMap[string](scope, s.Values) return low.FindItemInMap[string](scope, s.Values)
} }
// Build will extract scope values and extensions from node.
func (s *Scopes) Build(root *yaml.Node, idx *index.SpecIndex) error { func (s *Scopes) Build(root *yaml.Node, idx *index.SpecIndex) error {
s.Extensions = low.ExtractExtensions(root) s.Extensions = low.ExtractExtensions(root)
valueMap := make(map[low.KeyReference[string]]low.ValueReference[string]) valueMap := make(map[low.KeyReference[string]]low.ValueReference[string])

View File

@@ -9,14 +9,18 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // SecurityRequirement is a low-level representation of a Swagger / OpenAPI 2 SecurityRequirement object.
SecurityLabel = "security" //
) // SecurityRequirement lists the required security schemes to execute this operation. The object can have multiple
// security schemes declared in it which are all required (that is, there is a logical AND between the schemes).
//
// The name used for each property MUST correspond to a security scheme declared in the Security Definitions
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityRequirement struct { type SecurityRequirement struct {
Values low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]] Values low.ValueReference[map[low.KeyReference[string]]low.ValueReference[[]low.ValueReference[string]]]
} }
// Build will extract security requirements from the node (the structure is odd, to be honest)
func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error { func (s *SecurityRequirement) Build(root *yaml.Node, _ *index.SpecIndex) error {
var labelNode *yaml.Node var labelNode *yaml.Node
var arr []low.ValueReference[string] var arr []low.ValueReference[string]

View File

@@ -9,10 +9,12 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const ( // SecurityScheme is a low-level representation of a Swagger / OpenAPI 2 SecurityScheme object.
ScopesLabel = "scopes" //
) // SecurityScheme allows the definition of a security scheme that can be used by the operations. Supported schemes are
// basic authentication, an API key (either as a header or as a query parameter) and OAuth2's common flows
// (implicit, password, application and access code)
// - https://swagger.io/specification/v2/#securityDefinitionsObject
type SecurityScheme struct { type SecurityScheme struct {
Type low.NodeReference[string] Type low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
@@ -25,6 +27,7 @@ type SecurityScheme struct {
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
} }
// Build will extract extensions and scopes from the node.
func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error { func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error {
ss.Extensions = low.ExtractExtensions(root) ss.Extensions = low.ExtractExtensions(root)

View File

@@ -1,6 +1,14 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley // Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Package v2 represents all Swagger / OpenAPI 2 low-level models.
//
// Low-level models are more difficult to navigate than higher-level models, however they are packed with all the
// raw AST and node data required to perform any kind of analysis on the underlying data.
//
// Every property is wrapped in a NodeReference or a KeyReference or a ValueReference.
//
// IMPORTANT: As a general rule, Swagger / OpenAPI 2 should be avoided for new projects.
package v2 package v2
import ( import (
@@ -12,29 +20,98 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
// processes a property of a Swagger document asynchronously using bool and error channels for signals.
type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error) type documentFunction func(root *yaml.Node, doc *Swagger, idx *index.SpecIndex, c chan<- bool, e chan<- error)
// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification.
type Swagger struct { type Swagger struct {
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
Swagger low.ValueReference[string] Swagger low.ValueReference[string]
// Info represents a specification Info definition.
// Provides metadata about the API. The metadata can be used by the clients if needed.
// - https://swagger.io/specification/v2/#infoObject
Info low.NodeReference[*base.Info] Info low.NodeReference[*base.Info]
// Host is The host (name or ip) serving the API. This MUST be the host only and does not include the scheme nor
// sub-paths. It MAY include a port. If the host is not included, the host serving the documentation is to be used
// (including the port). The host does not support path templating.
Host low.NodeReference[string] Host low.NodeReference[string]
// BasePath is The base path on which the API is served, which is relative to the host. If it is not included,
// the API is served directly under the host. The value MUST start with a leading slash (/).
// The basePath does not support path templating.
BasePath low.NodeReference[string] BasePath low.NodeReference[string]
// Schemes represents the transfer protocol of the API. Values MUST be from the list: "http", "https", "ws", "wss".
// If the schemes is not included, the default scheme to be used is the one used to access
// the Swagger definition itself.
Schemes low.NodeReference[[]low.ValueReference[string]] Schemes low.NodeReference[[]low.ValueReference[string]]
// Consumes is a list of MIME types the APIs can consume. This is global to all APIs but can be overridden on
// specific API calls. Value MUST be as described under Mime Types.
Consumes low.NodeReference[[]low.ValueReference[string]] Consumes low.NodeReference[[]low.ValueReference[string]]
// Produces is a list of MIME types the APIs can produce. This is global to all APIs but can be overridden on
// specific API calls. Value MUST be as described under Mime Types.
Produces low.NodeReference[[]low.ValueReference[string]] Produces low.NodeReference[[]low.ValueReference[string]]
// Paths are the paths and operations for the API. Perhaps the most important part of the specification.
// - https://swagger.io/specification/v2/#pathsObject
Paths low.NodeReference[*Paths] Paths low.NodeReference[*Paths]
// Definitions is an object to hold data types produced and consumed by operations. It's composed of Schema instances
// - https://swagger.io/specification/v2/#definitionsObject
Definitions low.NodeReference[*Definitions] Definitions low.NodeReference[*Definitions]
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
// - https://swagger.io/specification/v2/#securityDefinitionsObject
SecurityDefinitions low.NodeReference[*SecurityDefinitions] SecurityDefinitions low.NodeReference[*SecurityDefinitions]
// Parameters is an object to hold parameters that can be used across operations.
// This property does not define global parameters for all operations.
// - https://swagger.io/specification/v2/#parametersDefinitionsObject
Parameters low.NodeReference[*ParameterDefinitions] Parameters low.NodeReference[*ParameterDefinitions]
// Responses is an object to hold responses that can be used across operations.
// This property does not define global responses for all operations.
// - https://swagger.io/specification/v2/#responsesDefinitionsObject
Responses low.NodeReference[*ResponsesDefinitions] Responses low.NodeReference[*ResponsesDefinitions]
// Security is a declaration of which security schemes are applied for the API as a whole. The list of values
// describes alternative security schemes that can be used (that is, there is a logical OR between the security
// requirements). Individual operations can override this definition.
// - https://swagger.io/specification/v2/#securityRequirementObject
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]] Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
// Tags are A list of tags used by the specification with additional metadata.
// The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used
// by the Operation Object must be declared. The tags that are not declared may be organized randomly or based
// on the tools' logic. Each tag name in the list MUST be unique.
// - https://swagger.io/specification/v2/#tagObject
Tags low.NodeReference[[]low.ValueReference[*base.Tag]] Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
ExternalDocs low.NodeReference[*base.ExternalDoc] ExternalDocs low.NodeReference[*base.ExternalDoc]
// Extensions contains all custom extensions defined for the top-level document.
Extensions map[low.KeyReference[string]]low.ValueReference[any] Extensions map[low.KeyReference[string]]low.ValueReference[any]
// Index is a reference to the index.SpecIndex that was created for the document and used
// as a guide when building out the Document. Ideal if further processing is required on the model and
// the original details are required to continue the work.
//
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
Index *index.SpecIndex Index *index.SpecIndex
// SpecInfo is a reference to the datamodel.SpecInfo instance created when the specification was read.
//
// This property is not a part of the OpenAPI schema, this is custom to libopenapi.
SpecInfo *datamodel.SpecInfo SpecInfo *datamodel.SpecInfo
} }
// FindExte
func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] { func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, s.Extensions) return low.FindItemInMap[any](ext, s.Extensions)
} }

View File

@@ -1,7 +1,7 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley // Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Package v3 represents all OpenAPI 3+ low-level models. High-level models are more difficult to navigate // Package v3 represents all OpenAPI 3+ low-level models. Low-level models are more difficult to navigate
// than higher-level models, however they are packed with all the raw AST and node data required to perform // than higher-level models, however they are packed with all the raw AST and node data required to perform
// any kind of analysis on the underlying data. // any kind of analysis on the underlying data.
// //

View File

@@ -9,7 +9,8 @@ import (
"io/ioutil" "io/ioutil"
) )
func Example_createLowDocument() { // How to create a low-level OpenAPI 3+ Document from an OpenAPI specification
func Example_createLowLevelOpenAPIDocument() {
// How to create a low-level OpenAPI 3 Document // How to create a low-level OpenAPI 3 Document
// load petstore into bytes // load petstore into bytes