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"
)
// Creating a new high-level OpenAPI 3+ document from an OpenAPI specification.
func Example() {
// An example of how to create a new high-level OpenAPI 3+ document from an OpenAPI specification.
func Example_createHighLevelOpenAPIDocument() {
// Load in an OpenAPI 3+ specification as a byte slice.
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"
)
const (
DefinitionsLabel = "definitions"
SecurityDefinitionsLabel = "securityDefinitions"
)
// ParameterDefinitions is a low-level representation of a Swagger / OpenAPI 2 Parameters Definitions object.
//
// 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 {
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 {
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 {
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 {
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] {
return low.FindItemInMap[*base.SchemaProxy](schema, d.Schemas)
}
func (pd *ParameterDefinitions) FindParameter(schema string) *low.ValueReference[*Parameter] {
return low.FindItemInMap[*Parameter](schema, pd.Definitions)
// FindParameter will attempt to locate a Parameter instance using a name.
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] {
return low.FindItemInMap[*Response](schema, r.Definitions)
// FindResponse will attempt to locate a Response instance using a name.
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] {
return low.FindItemInMap[*SecurityScheme](schema, s.Definitions)
// FindSecurityDefinition will attempt to locate a SecurityScheme using a name.
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 {
errorChan := make(chan error)
resultChan := make(chan definitionResult[*base.SchemaProxy])
@@ -58,12 +78,16 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
continue
}
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)
if err != nil {
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)
}
@@ -86,6 +110,7 @@ func (d *Definitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
return nil
}
// Build will extract all ParameterDefinitions into Parameter instances.
func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult[*Parameter])
@@ -97,7 +122,9 @@ func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) err
continue
}
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)
if err != nil {
e <- err
@@ -125,11 +152,13 @@ func (pd *ParameterDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) err
return nil
}
// re-usable struct for holding results as k/v pairs.
type definitionResult[T any] struct {
k *yaml.Node
v low.ValueReference[T]
}
// Build will extract all ResponsesDefinitions into Response instances.
func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult[*Response])
@@ -141,7 +170,9 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
continue
}
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)
if err != nil {
e <- err
@@ -169,6 +200,7 @@ func (r *ResponsesDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) erro
return nil
}
// Build will extract all SecurityDefinitions into SecurityScheme instances.
func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error {
errorChan := make(chan error)
resultChan := make(chan definitionResult[*SecurityScheme])
@@ -180,12 +212,16 @@ func (s *SecurityDefinitions) Build(root *yaml.Node, idx *index.SpecIndex) error
continue
}
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)
if err != nil {
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)
}

View File

@@ -9,18 +9,19 @@ import (
"gopkg.in/yaml.v3"
)
const (
ExamplesLabel = "examples"
)
// Examples represents a low-level Swagger / OpenAPI 2 Example object.
// Allows sharing examples for operation responses
// - https://swagger.io/specification/v2/#exampleObject
type Examples struct {
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] {
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 {
var keyNode, currNode *yaml.Node
var err error

View File

@@ -10,10 +10,10 @@ import (
"gopkg.in/yaml.v3"
)
const (
HeadersLabel = "headers"
)
// Header Represents a low-level Swagger / OpenAPI 2 Header object.
//
// 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 low.NodeReference[string]
Format low.NodeReference[string]
@@ -36,10 +36,12 @@ type Header struct {
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] {
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 {
h.Extensions = low.ExtractExtensions(root)
items, err := low.ExtractObject[*Items](ItemsLabel, root, idx)

View File

@@ -10,11 +10,11 @@ import (
"gopkg.in/yaml.v3"
)
const (
DefaultLabel = "default"
ItemsLabel = "items"
)
// Items is a low-level representation of a Swagger / OpenAPI 2 Items object.
//
// 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 low.NodeReference[string]
Format low.NodeReference[string]
@@ -35,6 +35,7 @@ type Items struct {
MultipleOf low.NodeReference[int]
}
// Build will build out items and default value.
func (i *Items) Build(root *yaml.Node, idx *index.SpecIndex) error {
items, iErr := low.ExtractObject[*Items](ItemsLabel, root, idx)
if iErr != nil {

View File

@@ -10,6 +10,10 @@ import (
"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 {
Tags low.NodeReference[[]low.ValueReference[string]]
Summary low.NodeReference[string]
@@ -26,6 +30,7 @@ type Operation struct {
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 {
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"
)
const (
ParametersLabel = "parameters"
)
// Parameter represents a low-level Swagger / OpenAPI 2 Parameter object.
//
// 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 {
Name low.NodeReference[string]
In low.NodeReference[string]
@@ -42,10 +69,12 @@ type Parameter struct {
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] {
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 {
p.Extensions = low.ExtractExtensions(root)
sch, sErr := base.ExtractSchema(root, idx)

View File

@@ -4,168 +4,177 @@
package v2
import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
"strings"
"sync"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/index"
"gopkg.in/yaml.v3"
"strings"
"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 {
Ref low.NodeReference[string]
Get low.NodeReference[*Operation]
Put low.NodeReference[*Operation]
Post low.NodeReference[*Operation]
Delete low.NodeReference[*Operation]
Options low.NodeReference[*Operation]
Head low.NodeReference[*Operation]
Patch low.NodeReference[*Operation]
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
Ref low.NodeReference[string]
Get low.NodeReference[*Operation]
Put low.NodeReference[*Operation]
Post low.NodeReference[*Operation]
Delete low.NodeReference[*Operation]
Options low.NodeReference[*Operation]
Head low.NodeReference[*Operation]
Patch low.NodeReference[*Operation]
Parameters low.NodeReference[[]low.ValueReference[*Parameter]]
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] {
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 {
p.Extensions = low.ExtractExtensions(root)
skip := false
var currentNode *yaml.Node
p.Extensions = low.ExtractExtensions(root)
skip := false
var currentNode *yaml.Node
var wg sync.WaitGroup
var errors []error
var wg sync.WaitGroup
var errors []error
var ops []low.NodeReference[*Operation]
var ops []low.NodeReference[*Operation]
// extract parameters
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
if pErr != nil {
return pErr
}
if params != nil {
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
Value: params,
KeyNode: ln,
ValueNode: vn,
}
}
// extract parameters
params, ln, vn, pErr := low.ExtractArray[*Parameter](ParametersLabel, root, idx)
if pErr != nil {
return pErr
}
if params != nil {
p.Parameters = low.NodeReference[[]low.ValueReference[*Parameter]]{
Value: params,
KeyNode: ln,
ValueNode: vn,
}
}
for i, pathNode := range root.Content {
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
skip = true
continue
}
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
// in swagger. Why this choice was made, I do not know.
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
rn := root.Content[i+1]
p.Ref = low.NodeReference[string]{
Value: rn.Value,
ValueNode: rn,
KeyNode: pathNode,
}
skip = true
continue
}
if skip {
skip = false
continue
}
if i%2 == 0 {
currentNode = pathNode
continue
}
for i, pathNode := range root.Content {
if strings.HasPrefix(strings.ToLower(pathNode.Value), "x-") {
skip = true
continue
}
// because (for some reason) the spec for swagger docs allows for a '$ref' property for path items.
// this is kinda nuts, because '$ref' is a reserved keyword for JSON references, which is ALSO used
// in swagger. Why this choice was made, I do not know.
if strings.Contains(strings.ToLower(pathNode.Value), "$ref") {
rn := root.Content[i+1]
p.Ref = low.NodeReference[string]{
Value: rn.Value,
ValueNode: rn,
KeyNode: pathNode,
}
skip = true
continue
}
if skip {
skip = false
continue
}
if i%2 == 0 {
currentNode = pathNode
continue
}
// the only thing we now care about is handling operations, filter out anything that's not a verb.
switch currentNode.Value {
case GetLabel:
break
case PostLabel:
break
case PutLabel:
break
case PatchLabel:
break
case DeleteLabel:
break
case HeadLabel:
break
case OptionsLabel:
break
default:
continue // ignore everything else.
}
// the only thing we now care about is handling operations, filter out anything that's not a verb.
switch currentNode.Value {
case GetLabel:
break
case PostLabel:
break
case PutLabel:
break
case PatchLabel:
break
case DeleteLabel:
break
case HeadLabel:
break
case OptionsLabel:
break
default:
continue // ignore everything else.
}
var op Operation
var op Operation
wg.Add(1)
wg.Add(1)
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
go low.BuildModelAsync(pathNode, &op, &wg, &errors)
opRef := low.NodeReference[*Operation]{
Value: &op,
KeyNode: currentNode,
ValueNode: pathNode,
}
opRef := low.NodeReference[*Operation]{
Value: &op,
KeyNode: currentNode,
ValueNode: pathNode,
}
ops = append(ops, opRef)
ops = append(ops, opRef)
switch currentNode.Value {
case GetLabel:
p.Get = opRef
case PostLabel:
p.Post = opRef
case PutLabel:
p.Put = opRef
case PatchLabel:
p.Patch = opRef
case DeleteLabel:
p.Delete = opRef
case HeadLabel:
p.Head = opRef
case OptionsLabel:
p.Options = opRef
}
}
switch currentNode.Value {
case GetLabel:
p.Get = opRef
case PostLabel:
p.Post = opRef
case PutLabel:
p.Put = opRef
case PatchLabel:
p.Patch = opRef
case DeleteLabel:
p.Delete = opRef
case HeadLabel:
p.Head = opRef
case OptionsLabel:
p.Options = opRef
}
}
//all operations have been superficially built,
//now we need to build out the operation, we will do this asynchronously for speed.
opBuildChan := make(chan bool)
opErrorChan := make(chan error)
//all operations have been superficially built,
//now we need to build out the operation, we will do this asynchronously for speed.
opBuildChan := make(chan bool)
opErrorChan := make(chan error)
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
er := op.Value.Build(op.ValueNode, idx)
if er != nil {
errCh <- er
}
ch <- true
}
var buildOpFunc = func(op low.NodeReference[*Operation], ch chan<- bool, errCh chan<- error) {
er := op.Value.Build(op.ValueNode, idx)
if er != nil {
errCh <- er
}
ch <- true
}
if len(ops) <= 0 {
return nil // nothing to do.
}
if len(ops) <= 0 {
return nil // nothing to do.
}
for _, op := range ops {
go buildOpFunc(op, opBuildChan, opErrorChan)
}
for _, op := range ops {
go buildOpFunc(op, opBuildChan, opErrorChan)
}
n := 0
total := len(ops)
for n < total {
select {
case buildError := <-opErrorChan:
return buildError
case <-opBuildChan:
n++
}
}
n := 0
total := len(ops)
for n < total {
select {
case buildError := <-opErrorChan:
return buildError
case <-opBuildChan:
n++
}
}
// make sure we don't exit before the path is finished building.
if len(ops) > 0 {
wg.Wait()
}
// make sure we don't exit before the path is finished building.
if len(ops) > 0 {
wg.Wait()
}
return nil
return nil
}

View File

@@ -10,22 +10,13 @@ import (
"strings"
)
const (
PathsLabel = "paths"
GetLabel = "get"
PostLabel = "post"
PatchLabel = "patch"
PutLabel = "put"
DeleteLabel = "delete"
OptionsLabel = "options"
HeadLabel = "head"
)
// Paths represents a low-level Swagger / OpenAPI Paths object.
type Paths struct {
PathItems map[low.KeyReference[string]]low.ValueReference[*PathItem]
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] {
for k, j := range p.PathItems {
if k.Value == path {
@@ -35,10 +26,12 @@ func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
return nil
}
// FindExtension will attempt to locate an extension value given a name.
func (p *Paths) FindExtension(ext string) *low.ValueReference[any] {
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 {
p.Extensions = low.ExtractExtensions(root)
skip := false

View File

@@ -10,10 +10,10 @@ import (
"gopkg.in/yaml.v3"
)
const (
ResponsesLabel = "responses"
)
// Response is a representation of a high-level Swagger / OpenAPI 2 Response object, backed by a low-level one.
//
// Response describes a single response from an API Operation
// - https://swagger.io/specification/v2/#responseObject
type Response struct {
Description low.NodeReference[string]
Schema low.NodeReference[*base.SchemaProxy]
@@ -22,14 +22,17 @@ type Response struct {
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] {
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] {
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 {
r.Extensions = low.ExtractExtensions(root)
s, err := base.ExtractSchema(root, idx)

View File

@@ -11,12 +11,14 @@ import (
"gopkg.in/yaml.v3"
)
// Responses is a low-level representation of a Swagger / OpenAPI 2 Responses object.
type Responses struct {
Codes map[low.KeyReference[string]]low.ValueReference[*Response]
Default low.NodeReference[*Response]
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 {
r.Extensions = low.ExtractExtensions(root)
@@ -42,6 +44,7 @@ func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
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] {
return low.FindItemInMap[*Response](code, r.Codes)
}

View File

@@ -10,15 +10,21 @@ import (
"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 {
Values map[low.KeyReference[string]]low.ValueReference[string]
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] {
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 {
s.Extensions = low.ExtractExtensions(root)
valueMap := make(map[low.KeyReference[string]]low.ValueReference[string])

View File

@@ -9,14 +9,18 @@ import (
"gopkg.in/yaml.v3"
)
const (
SecurityLabel = "security"
)
// SecurityRequirement is a low-level representation of a Swagger / OpenAPI 2 SecurityRequirement object.
//
// 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 {
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 {
var labelNode *yaml.Node
var arr []low.ValueReference[string]

View File

@@ -9,10 +9,12 @@ import (
"gopkg.in/yaml.v3"
)
const (
ScopesLabel = "scopes"
)
// SecurityScheme is a low-level representation of a Swagger / OpenAPI 2 SecurityScheme object.
//
// 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 low.NodeReference[string]
Description low.NodeReference[string]
@@ -25,6 +27,7 @@ type SecurityScheme struct {
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 {
ss.Extensions = low.ExtractExtensions(root)

View File

@@ -1,6 +1,14 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// 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
import (
@@ -12,29 +20,98 @@ import (
"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)
// Swagger represents a high-level Swagger / OpenAPI 2 document. An instance of Swagger is the root of the specification.
type Swagger struct {
Swagger low.ValueReference[string]
Info low.NodeReference[*base.Info]
Host low.NodeReference[string]
BasePath low.NodeReference[string]
Schemes low.NodeReference[[]low.ValueReference[string]]
Consumes low.NodeReference[[]low.ValueReference[string]]
Produces low.NodeReference[[]low.ValueReference[string]]
Paths low.NodeReference[*Paths]
Definitions low.NodeReference[*Definitions]
// Swagger is the version of Swagger / OpenAPI being used, extracted from the 'swagger: 2.x' definition.
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]
// 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]
// 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]
// 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]]
// 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]]
// 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]]
// 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]
// 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]
// SecurityDefinitions represents security scheme definitions that can be used across the specification.
// - https://swagger.io/specification/v2/#securityDefinitionsObject
SecurityDefinitions low.NodeReference[*SecurityDefinitions]
Parameters low.NodeReference[*ParameterDefinitions]
Responses low.NodeReference[*ResponsesDefinitions]
Security low.NodeReference[[]low.ValueReference[*SecurityRequirement]]
Tags low.NodeReference[[]low.ValueReference[*base.Tag]]
ExternalDocs low.NodeReference[*base.ExternalDoc]
Extensions map[low.KeyReference[string]]low.ValueReference[any]
Index *index.SpecIndex
SpecInfo *datamodel.SpecInfo
// 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]
// 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]
// 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]]
// 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]]
// ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit mate?
ExternalDocs low.NodeReference[*base.ExternalDoc]
// Extensions contains all custom extensions defined for the top-level document.
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
// 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
}
// FindExte
func (s *Swagger) FindExtension(ext string) *low.ValueReference[any] {
return low.FindItemInMap[any](ext, s.Extensions)
}

View File

@@ -1,7 +1,7 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// 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
// any kind of analysis on the underlying data.
//

View File

@@ -9,7 +9,8 @@ import (
"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
// load petstore into bytes