diff --git a/datamodel/high/v3/link.go b/datamodel/high/v3/link.go index 662e2ef..d9f62b5 100644 --- a/datamodel/high/v3/link.go +++ b/datamodel/high/v3/link.go @@ -8,7 +8,7 @@ import ( low "github.com/pb33f/libopenapi/datamodel/low/v3" ) -// Link represents an OpenAPI 3+ Link object that is backed by a low-level one. +// Link represents a high-level 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 // caller’s ability to successfully invoke it, rather it provides a known relationship and traversal mechanism between diff --git a/datamodel/high/v3/operation.go b/datamodel/high/v3/operation.go index 521232e..011035c 100644 --- a/datamodel/high/v3/operation.go +++ b/datamodel/high/v3/operation.go @@ -9,6 +9,7 @@ import ( ) // 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 diff --git a/datamodel/high/v3/request_body.go b/datamodel/high/v3/request_body.go index 476f270..e2af21c 100644 --- a/datamodel/high/v3/request_body.go +++ b/datamodel/high/v3/request_body.go @@ -8,7 +8,8 @@ import ( low "github.com/pb33f/libopenapi/datamodel/low/v3" ) -// RequestBody represents a high-level OpenAPI 3+ RequestBody object, +// RequestBody represents a high-level OpenAPI 3+ RequestBody object, backed by a low-level one. +// - https://spec.openapis.org/oas/v3.1.0#request-body-object type RequestBody struct { Description string Content map[string]*MediaType @@ -17,6 +18,7 @@ type RequestBody struct { low *low.RequestBody } +// NewRequestBody will create a new high-level RequestBody instance, from a low-level one. func NewRequestBody(rb *low.RequestBody) *RequestBody { r := new(RequestBody) r.low = rb @@ -27,6 +29,7 @@ func NewRequestBody(rb *low.RequestBody) *RequestBody { return r } +// GoLow returns the low-level RequestBody instance used to create the high-level one. func (r *RequestBody) GoLow() *low.RequestBody { return r.low } diff --git a/datamodel/high/v3/security_requirement.go b/datamodel/high/v3/security_requirement.go index cb98edd..43dcc19 100644 --- a/datamodel/high/v3/security_requirement.go +++ b/datamodel/high/v3/security_requirement.go @@ -12,7 +12,8 @@ import low "github.com/pb33f/libopenapi/datamodel/low/v3" // 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. +// 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. diff --git a/datamodel/low/v3/callback.go b/datamodel/low/v3/callback.go index eaad768..1b76ea6 100644 --- a/datamodel/low/v3/callback.go +++ b/datamodel/low/v3/callback.go @@ -9,15 +9,24 @@ import ( "gopkg.in/yaml.v3" ) +// Callback represents a low-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 low.ValueReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]] Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExpression will locate a string expression and return a ValueReference containing the located PathItem func (cb *Callback) FindExpression(exp string) *low.ValueReference[*PathItem] { return low.FindItemInMap[*PathItem](exp, cb.Expression.Value) } +// Build will extract extensions, expressions and PathItem objects for Callback func (cb *Callback) Build(root *yaml.Node, idx *index.SpecIndex) error { cb.Extensions = low.ExtractExtensions(root) diff --git a/datamodel/low/v3/components.go b/datamodel/low/v3/components.go index cbbd0cd..a53534e 100644 --- a/datamodel/low/v3/components.go +++ b/datamodel/low/v3/components.go @@ -13,11 +13,11 @@ import ( "strings" ) -const ( - ComponentsLabel = "components" - SchemasLabel = "schemas" -) - +// Components represents a low-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 low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*base.SchemaProxy]] Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]] @@ -31,26 +31,32 @@ type Components struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExtension attempts to locate an extension with the supplied key func (co *Components) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, co.Extensions) } +// FindSchema attempts to locate a SchemaProxy from 'schemas' with a specific name func (co *Components) FindSchema(schema string) *low.ValueReference[*base.SchemaProxy] { return low.FindItemInMap[*base.SchemaProxy](schema, co.Schemas.Value) } +// FindResponse attempts to locate a Response from 'responses' with a specific name func (co *Components) FindResponse(response string) *low.ValueReference[*Response] { return low.FindItemInMap[*Response](response, co.Responses.Value) } +// FindParameter attempts to locate a Parameter from 'parameters' with a specific name func (co *Components) FindParameter(response string) *low.ValueReference[*Parameter] { return low.FindItemInMap[*Parameter](response, co.Parameters.Value) } +// FindSecurityScheme attempts to locate a SecurityScheme from 'securitySchemes' with a specific name func (co *Components) FindSecurityScheme(sScheme string) *low.ValueReference[*SecurityScheme] { return low.FindItemInMap[*SecurityScheme](sScheme, co.SecuritySchemes.Value) } +// FindExample attempts tp func (co *Components) FindExample(example string) *low.ValueReference[*base.Example] { return low.FindItemInMap[*base.Example](example, co.Examples.Value) } diff --git a/datamodel/low/v3/constants.go b/datamodel/low/v3/constants.go new file mode 100644 index 0000000..f963532 --- /dev/null +++ b/datamodel/low/v3/constants.go @@ -0,0 +1,41 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +// Label definitions used to look up vales in yaml.Node tree. +const ( + ComponentsLabel = "components" + SchemasLabel = "schemas" + EncodingLabel = "encoding" + HeadersLabel = "headers" + ParametersLabel = "parameters" + RequestBodyLabel = "requestBody" + RequestBodiesLabel = "requestBodies" + ResponsesLabel = "responses" + CallbacksLabel = "callbacks" + ContentLabel = "content" + PathsLabel = "paths" + WebhooksLabel = "webhooks" + JSONSchemaDialectLabel = "jsonSchemaDialect" + GetLabel = "get" + PostLabel = "post" + PatchLabel = "patch" + PutLabel = "put" + DeleteLabel = "delete" + OptionsLabel = "options" + HeadLabel = "head" + TraceLabel = "trace" + LinksLabel = "links" + DefaultLabel = "default" + SecurityLabel = "security" + SecuritySchemesLabel = "securitySchemes" + OAuthFlowsLabel = "flows" + VariablesLabel = "variables" + ServersLabel = "servers" + ServerLabel = "server" + ImplicitLabel = "implicit" + PasswordLabel = "password" + ClientCredentialsLabel = "clientCredentials" + AuthorizationCodeLabel = "authorizationCode" +) diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 5607f64..b71c736 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -1,6 +1,7 @@ package v3 import ( + "fmt" "github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" @@ -577,3 +578,28 @@ func TestCreateDocument_ExternalDoc_Error(t *testing.T) { _, err = CreateDocument(info) assert.Len(t, err, 1) } + +func ExampleCreateDocument() { + // How to create a low-level OpenAPI 3 Document + + // load petstore into bytes + petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev3.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 +} \ No newline at end of file diff --git a/datamodel/low/v3/document.go b/datamodel/low/v3/document.go index bca0465..d155522 100644 --- a/datamodel/low/v3/document.go +++ b/datamodel/low/v3/document.go @@ -1,6 +1,11 @@ // 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 +// 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. package v3 import ( @@ -10,21 +15,76 @@ import ( ) type Document struct { - Version low.ValueReference[string] - Info low.NodeReference[*base.Info] - JsonSchemaDialect low.NodeReference[string] // 3.1 - Webhooks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]] // 3.1 - Servers low.NodeReference[[]low.ValueReference[*Server]] - Paths low.NodeReference[*Paths] - Components low.NodeReference[*Components] - Security low.NodeReference[*SecurityRequirement] - Tags low.NodeReference[[]low.ValueReference[*base.Tag]] - ExternalDocs low.NodeReference[*base.ExternalDoc] - Extensions map[low.KeyReference[string]]low.ValueReference[any] - Index *index.SpecIndex + + // Version is the version of OpenAPI being used, extracted from the 'openapi: x.x.x' definition. + // This is not a standard property of the OpenAPI model, it's a convenience mechanism only. + Version low.ValueReference[string] + + // Info represents a specification Info definitions + // Provides metadata about the API. The metadata MAY be used by tooling as required. + // - https://spec.openapis.org/oas/v3.1.0#info-object + Info low.NodeReference[*base.Info] + + // JsonSchemaDialect is a 3.1+ property that sets the dialect to use for validating *base.Schema definitions + // The default value for the $schema keyword within Schema Objects contained within this OAS document. + // This MUST be in the form of a URI. + // - https://spec.openapis.org/oas/v3.1.0#schema-object + JsonSchemaDialect low.NodeReference[string] // 3.1 + + // Webhooks is a 3.1+ property that is similar to callbacks, except, this defines incoming webhooks. + // The incoming webhooks that MAY be received as part of this API and that the API consumer MAY choose to implement. + // Closely related to the callbacks feature, this section describes requests initiated other than by an API call, + // for example by an out-of-band registration. The key name is a unique string to refer to each webhook, + // while the (optionally referenced) Path Item Object describes a request that may be initiated by the API provider + // and the expected responses. An example is available. + Webhooks low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*PathItem]] // 3.1 + + // Servers is a slice of Server instances which provide connectivity information to a target server. If the servers + // property is not provided, or is an empty array, the default value would be a Server Object with a url value of /. + // - https://spec.openapis.org/oas/v3.1.0#server-object + Servers low.NodeReference[[]low.ValueReference[*Server]] + + // Paths contains all the PathItem definitions for the specification. + // The available paths and operations for the API, The most important part of ths spec. + // - https://spec.openapis.org/oas/v3.1.0#paths-object + Paths low.NodeReference[*Paths] + + // Components is an element to hold various schemas for the document. + // - https://spec.openapis.org/oas/v3.1.0#components-object + Components low.NodeReference[*Components] + + // Security contains global security requirements/roles for the specification + // A declaration of which security mechanisms can be used across the API. The list of values includes alternative + // security requirement objects that can be used. Only one of the security requirement objects need to be satisfied + // to authorize a request. Individual operations can override this definition. To make security optional, + // an empty security requirement ({}) can be included in the array. + // - https://spec.openapis.org/oas/v3.1.0#security-requirement-object + Security low.NodeReference[*SecurityRequirement] + + // Tags is a slice of base.Tag instances defined by the specification + // A list of tags used by the document 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://spec.openapis.org/oas/v3.1.0#tag-object + Tags low.NodeReference[[]low.ValueReference[*base.Tag]] + + // ExternalDocs is an instance of base.ExternalDoc for.. well, obvious really, innit. + // - https://spec.openapis.org/oas/v3.1.0#external-documentation-object + 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 } -// +// TODO: this is early prototype mutation/modification code, keeping it around for later. //func (d *Document) AddTag() *base.Tag { // t := base.NewTag() // //d.Tags.KeyNode diff --git a/datamodel/low/v3/encoding.go b/datamodel/low/v3/encoding.go index c163df1..eb95dbc 100644 --- a/datamodel/low/v3/encoding.go +++ b/datamodel/low/v3/encoding.go @@ -9,10 +9,8 @@ import ( "gopkg.in/yaml.v3" ) -const ( - EncodingLabel = "encoding" -) - +// Encoding represents a low-level OpenAPI 3+ Encoding object +// - https://spec.openapis.org/oas/v3.1.0#encoding-object type Encoding struct { ContentType low.NodeReference[string] Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] @@ -21,12 +19,13 @@ type Encoding struct { AllowReserved low.NodeReference[bool] } +// FindHeader attempts to locate a Header with the supplied name func (en *Encoding) FindHeader(hType string) *low.ValueReference[*Header] { return low.FindItemInMap[*Header](hType, en.Headers.Value) } +// Build will extract all Header objects from supplied node. func (en *Encoding) Build(root *yaml.Node, idx *index.SpecIndex) error { - headers, hL, hN, err := low.ExtractMap[*Header](HeadersLabel, root, idx) if err != nil { return err @@ -38,6 +37,5 @@ func (en *Encoding) Build(root *yaml.Node, idx *index.SpecIndex) error { ValueNode: hN, } } - return nil } diff --git a/datamodel/low/v3/examples_test.go b/datamodel/low/v3/examples_test.go new file mode 100644 index 0000000..6e7017f --- /dev/null +++ b/datamodel/low/v3/examples_test.go @@ -0,0 +1,35 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package v3 + +import ( + "fmt" + "github.com/pb33f/libopenapi/datamodel" + "io/ioutil" +) + +func Example_createLowDocument() { + // How to create a low-level OpenAPI 3 Document + + // load petstore into bytes + petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev3.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 +} diff --git a/datamodel/low/v3/header.go b/datamodel/low/v3/header.go index 3eee088..2a8702e 100644 --- a/datamodel/low/v3/header.go +++ b/datamodel/low/v3/header.go @@ -11,10 +11,8 @@ import ( "gopkg.in/yaml.v3" ) -const ( - HeadersLabel = "headers" -) - +// Header represents a low-level OpenAPI 3+ Header object. +// - https://spec.openapis.org/oas/v3.1.0#header-object type Header struct { Description low.NodeReference[string] Required low.NodeReference[bool] @@ -30,18 +28,22 @@ type Header struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExtension will attempt to locate an extension with the supplied name func (h *Header) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, h.Extensions) } +// FindExample will attempt to locate an Example with a specified name func (h *Header) FindExample(eType string) *low.ValueReference[*base.Example] { return low.FindItemInMap[*base.Example](eType, h.Examples.Value) } +// FindContent will attempt to locate a MediaType definition, with a specified name func (h *Header) FindContent(ext string) *low.ValueReference[*MediaType] { return low.FindItemInMap[*MediaType](ext, h.Content.Value) } +// Build will extract extensions, examples, schema and content/media types from node. func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error { h.Extensions = low.ExtractExtensions(root) @@ -83,6 +85,5 @@ func (h *Header) Build(root *yaml.Node, idx *index.SpecIndex) error { KeyNode: cL, ValueNode: cN, } - return nil } diff --git a/datamodel/low/v3/link.go b/datamodel/low/v3/link.go index 4cba774..af01057 100644 --- a/datamodel/low/v3/link.go +++ b/datamodel/low/v3/link.go @@ -9,6 +9,18 @@ import ( "gopkg.in/yaml.v3" ) +// Link represents a low-level OpenAPI 3+ Link object. +// +// The Link object represents a possible design-time link for a response. The presence of a link does not guarantee the +// caller’s 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 low.NodeReference[string] OperationId low.NodeReference[string] @@ -19,14 +31,17 @@ type Link struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindParameter will attempt to locate a parameter string value, using a parameter name input. func (l *Link) FindParameter(pName string) *low.ValueReference[string] { return low.FindItemInMap[string](pName, l.Parameters.Value) } +// FindExtension will attempt to locate an extension with a specific key func (l *Link) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, l.Extensions) } +// Build will extract extensions and servers from the node. func (l *Link) Build(root *yaml.Node, idx *index.SpecIndex) error { l.Extensions = low.ExtractExtensions(root) @@ -36,6 +51,5 @@ func (l *Link) Build(root *yaml.Node, idx *index.SpecIndex) error { return sErr } l.Server = ser - return nil } diff --git a/datamodel/low/v3/media_type.go b/datamodel/low/v3/media_type.go index 5fc52a1..71cfd87 100644 --- a/datamodel/low/v3/media_type.go +++ b/datamodel/low/v3/media_type.go @@ -11,6 +11,10 @@ import ( "gopkg.in/yaml.v3" ) +// MediaType represents a low-level OpenAPI MediaType object. +// +// 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 low.NodeReference[*base.SchemaProxy] Example low.NodeReference[any] @@ -19,22 +23,27 @@ type MediaType struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExtension will attempt to locate an extension with the supplied name. func (mt *MediaType) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, mt.Extensions) } +// FindPropertyEncoding will attempt to locate an Encoding value with a specific name. func (mt *MediaType) FindPropertyEncoding(eType string) *low.ValueReference[*Encoding] { return low.FindItemInMap[*Encoding](eType, mt.Encoding.Value) } +// FindExample will attempt to locate an Example with a specific name. func (mt *MediaType) FindExample(eType string) *low.ValueReference[*base.Example] { return low.FindItemInMap[*base.Example](eType, mt.Examples.Value) } +// GetAllExamples will extract all examples from the MediaType instance. func (mt *MediaType) GetAllExamples() map[low.KeyReference[string]]low.ValueReference[*base.Example] { return mt.Examples.Value } +// Build will extract examples, extensions, schema and encoding from node. func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error { mt.Extensions = low.ExtractExtensions(root) diff --git a/datamodel/low/v3/oauth_flows.go b/datamodel/low/v3/oauth_flows.go index 2f52da5..000c4df 100644 --- a/datamodel/low/v3/oauth_flows.go +++ b/datamodel/low/v3/oauth_flows.go @@ -9,13 +9,8 @@ import ( "gopkg.in/yaml.v3" ) -const ( - ImplicitLabel = "implicit" - PasswordLabel = "password" - ClientCredentialsLabel = "clientCredentials" - AuthorizationCodeLabel = "authorizationCode" -) - +// OAuthFlows represents a low-level OpenAPI 3+ OAuthFlows object. +// - https://spec.openapis.org/oas/v3.1.0#oauth-flows-object type OAuthFlows struct { Implicit low.NodeReference[*OAuthFlow] Password low.NodeReference[*OAuthFlow] @@ -24,10 +19,12 @@ type OAuthFlows struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExtension will attempt to locate an extension with the supplied name. func (o *OAuthFlows) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, o.Extensions) } +// Build will extract extensions and all OAuthFlow types from the supplied node. func (o *OAuthFlows) Build(root *yaml.Node, idx *index.SpecIndex) error { o.Extensions = low.ExtractExtensions(root) @@ -58,6 +55,8 @@ func (o *OAuthFlows) Build(root *yaml.Node, idx *index.SpecIndex) error { } +// OAuthFlow represents a low-level OpenAPI 3+ OAuthFlow object. +// - https://spec.openapis.org/oas/v3.1.0#oauth-flow-object type OAuthFlow struct { AuthorizationUrl low.NodeReference[string] TokenUrl low.NodeReference[string] @@ -66,14 +65,17 @@ type OAuthFlow struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindScope attempts to locate a scope using a specified name. func (o *OAuthFlow) FindScope(scope string) *low.ValueReference[string] { return low.FindItemInMap[string](scope, o.Scopes.Value) } +// FindExtension attempts to locate an extension with a specified key func (o *OAuthFlow) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, o.Extensions) } +// Build will extract extensions from the node. func (o *OAuthFlow) Build(root *yaml.Node, idx *index.SpecIndex) error { o.Extensions = low.ExtractExtensions(root) return nil diff --git a/datamodel/low/v3/operation.go b/datamodel/low/v3/operation.go index 7930a12..61bdf17 100644 --- a/datamodel/low/v3/operation.go +++ b/datamodel/low/v3/operation.go @@ -10,14 +10,11 @@ import ( "gopkg.in/yaml.v3" ) -const ( - ParametersLabel = "parameters" - RequestBodyLabel = "requestBody" - RequestBodiesLabel = "requestBodies" - ResponsesLabel = "responses" - CallbacksLabel = "callbacks" -) - +// Operation is a low-level representation of an OpenAPI 3+ Operation object. +// +// 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 low.NodeReference[[]low.ValueReference[string]] Summary low.NodeReference[string] @@ -34,10 +31,12 @@ type Operation struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindCallback will attempt to locate a Callback instance by the supplied name. func (o *Operation) FindCallback(callback string) *low.ValueReference[*Callback] { return low.FindItemInMap[*Callback](callback, o.Callbacks.Value) } +// Build will extract external docs, parameters, request body, responses, callbacks, security and servers. func (o *Operation) Build(root *yaml.Node, idx *index.SpecIndex) error { o.Extensions = low.ExtractExtensions(root) diff --git a/datamodel/low/v3/parameter.go b/datamodel/low/v3/parameter.go index cb43bff..71f6758 100644 --- a/datamodel/low/v3/parameter.go +++ b/datamodel/low/v3/parameter.go @@ -11,10 +11,10 @@ import ( "gopkg.in/yaml.v3" ) -const ( - ContentLabel = "content" -) - +// 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 low.NodeReference[string] In low.NodeReference[string] @@ -32,18 +32,22 @@ type Parameter struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindContent will attempt to locate a MediaType instance using the specified name. func (p *Parameter) FindContent(cType string) *low.ValueReference[*MediaType] { return low.FindItemInMap[*MediaType](cType, p.Content.Value) } +// FindExample will attempt to locate a base.Example instance using the specified name. func (p *Parameter) FindExample(eType string) *low.ValueReference[*base.Example] { return low.FindItemInMap[*base.Example](eType, p.Examples.Value) } +// FindExtension attempts to locate an extension using the specified name. func (p *Parameter) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, p.Extensions) } +// Build will extract examples, extensions and content/media types. func (p *Parameter) Build(root *yaml.Node, idx *index.SpecIndex) error { p.Extensions = low.ExtractExtensions(root) diff --git a/datamodel/low/v3/path_item.go b/datamodel/low/v3/path_item.go index fe3994d..4a70b53 100644 --- a/datamodel/low/v3/path_item.go +++ b/datamodel/low/v3/path_item.go @@ -13,6 +13,12 @@ import ( "sync" ) +// PathItem represents a low-level OpenAPI 3+ 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 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 low.NodeReference[string] Summary low.NodeReference[string] @@ -29,10 +35,13 @@ type PathItem struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExtension attempts to find an extension func (p *PathItem) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, p.Extensions) } +// Build extracts extensions, parameters, servers and each http method defined. +// everything is extracted asynchronously for speed. func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error { p.Extensions = low.ExtractExtensions(root) skip := false @@ -204,6 +213,5 @@ func (p *PathItem) Build(root *yaml.Node, idx *index.SpecIndex) error { if len(ops) > 0 { wg.Wait() } - return nil } diff --git a/datamodel/low/v3/path.go b/datamodel/low/v3/paths.go similarity index 80% rename from datamodel/low/v3/path.go rename to datamodel/low/v3/paths.go index a2f163b..28bb254 100644 --- a/datamodel/low/v3/path.go +++ b/datamodel/low/v3/paths.go @@ -12,25 +12,18 @@ import ( "strings" ) -const ( - PathsLabel = "paths" - WebhooksLabel = "webhooks" - JSONSchemaDialectLabel = "jsonSchemaDialect" - GetLabel = "get" - PostLabel = "post" - PatchLabel = "patch" - PutLabel = "put" - DeleteLabel = "delete" - OptionsLabel = "options" - HeadLabel = "head" - TraceLabel = "trace" -) - +// 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[low.KeyReference[string]]low.ValueReference[*PathItem] Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindPath will attempt to locate a PathItem using the provided path string. func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] { for k, j := range p.PathItems { if k.Value == path { @@ -40,10 +33,12 @@ func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] { return nil } +// FindExtension will attempt to locate an extension using the specified string. func (p *Paths) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, p.Extensions) } +// Build will extract extensions and all PathItems. This happens asynchronously for speed. func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { p.Extensions = low.ExtractExtensions(root) skip := false @@ -126,5 +121,4 @@ func (p *Paths) Build(root *yaml.Node, idx *index.SpecIndex) error { } p.PathItems = pathsMap return nil - } diff --git a/datamodel/low/v3/path_test.go b/datamodel/low/v3/paths_test.go similarity index 100% rename from datamodel/low/v3/path_test.go rename to datamodel/low/v3/paths_test.go diff --git a/datamodel/low/v3/request_body.go b/datamodel/low/v3/request_body.go index 0166b5f..9601d5d 100644 --- a/datamodel/low/v3/request_body.go +++ b/datamodel/low/v3/request_body.go @@ -9,6 +9,8 @@ import ( "gopkg.in/yaml.v3" ) +// RequestBody represents a low-level OpenAPI 3+ RequestBody object. +// - https://spec.openapis.org/oas/v3.1.0#request-body-object type RequestBody struct { Description low.NodeReference[string] Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]] @@ -16,14 +18,17 @@ type RequestBody struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// FindExtension attemps to locate an extension using the provided name. func (rb *RequestBody) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, rb.Extensions) } +// FindContent attempts to find content/MediaType defined using a specified name. func (rb *RequestBody) FindContent(cType string) *low.ValueReference[*MediaType] { return low.FindItemInMap[*MediaType](cType, rb.Content.Value) } +// Build will extract extensions and MediaType objects from the node. func (rb *RequestBody) Build(root *yaml.Node, idx *index.SpecIndex) error { rb.Extensions = low.ExtractExtensions(root) diff --git a/datamodel/low/v3/response.go b/datamodel/low/v3/response.go index 3cc70b5..3c004b0 100644 --- a/datamodel/low/v3/response.go +++ b/datamodel/low/v3/response.go @@ -11,16 +11,26 @@ import ( "gopkg.in/yaml.v3" ) -const ( - LinksLabel = "links" - DefaultLabel = "default" -) - +// Responses represents a low-level OpenAPI 3+ Responses object. +// +// 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[low.KeyReference[string]]low.ValueReference[*Response] Default low.NodeReference[*Response] } +// Build will extract default response and all Response objects for each code func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error { if utils.IsNodeMap(root) { codes, err := low.ExtractMapNoLookup[*Response](root, idx) @@ -39,15 +49,22 @@ func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error { r.Default = def } } else { - return fmt.Errorf("responses build failed: vn node is not a map! line %d, col %d", root.Line, root.Column) + return fmt.Errorf("responses build failed: vn node is not a map! line %d, col %d", + root.Line, root.Column) } return nil } +// FindResponseByCode will attempt to locate a Response using an HTTP response code. func (r *Responses) FindResponseByCode(code string) *low.ValueReference[*Response] { return low.FindItemInMap[*Response](code, r.Codes) } +// 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 low.NodeReference[string] Headers low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Header]] @@ -56,22 +73,27 @@ type Response struct { Links low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]] } +// FindExtension will attempt to locate an extension using the supplied key func (r *Response) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, r.Extensions) } +// FindContent will attempt to locate a MediaType instance using the supplied key. func (r *Response) FindContent(cType string) *low.ValueReference[*MediaType] { return low.FindItemInMap[*MediaType](cType, r.Content.Value) } +// FindHeader will attempt to locate a Header instance using the supplied key. func (r *Response) FindHeader(hType string) *low.ValueReference[*Header] { return low.FindItemInMap[*Header](hType, r.Headers.Value) } +// FindLink will attempt to locate a Link instance using the supplied key. func (r *Response) FindLink(hType string) *low.ValueReference[*Link] { return low.FindItemInMap[*Link](hType, r.Links.Value) } +// Build will extract headers, extensions, content and links from node. func (r *Response) Build(root *yaml.Node, idx *index.SpecIndex) error { r.Extensions = low.ExtractExtensions(root) diff --git a/datamodel/low/v3/security_scheme.go b/datamodel/low/v3/security_scheme.go index bc21b7e..9a9dc31 100644 --- a/datamodel/low/v3/security_scheme.go +++ b/datamodel/low/v3/security_scheme.go @@ -10,12 +10,16 @@ import ( "gopkg.in/yaml.v3" ) -const ( - SecurityLabel = "security" - SecuritySchemesLabel = "securitySchemes" - OAuthFlowsLabel = "flows" -) - +// SecurityScheme represents a low-level OpenAPI 3+ SecurityScheme object. +// +// Defines a security scheme that can be used by the operations. +// +// Supported schemes are HTTP authentication, an API key (either as a header, a cookie parameter or as a query parameter), +// mutual TLS (use of a client certificate), OAuth2’s common flows (implicit, password, client credentials and +// authorization code) as defined in RFC6749 (https://www.rfc-editor.org/rfc/rfc6749), and OpenID Connect Discovery. +// Please note that as of 2020, the implicit flow is about to be deprecated by OAuth 2.0 Security Best Current Practice. +// Recommended for most use case is Authorization Code Grant flow with PKCE. +// - https://spec.openapis.org/oas/v3.1.0#security-scheme-object type SecurityScheme struct { Type low.NodeReference[string] Description low.NodeReference[string] @@ -28,14 +32,28 @@ type SecurityScheme struct { Extensions map[low.KeyReference[string]]low.ValueReference[any] } +// SecurityRequirement is a low-level representation of an OpenAPI 3+ SecurityRequirement object. +// +// 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 []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]] } +// FindExtension attempts to locate an extension using the supplied key. func (ss *SecurityScheme) FindExtension(ext string) *low.ValueReference[any] { return low.FindItemInMap[any](ext, ss.Extensions) } +// Build will extract OAuthFlows and extensions from the node. func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error { ss.Extensions = low.ExtractExtensions(root) @@ -50,6 +68,7 @@ func (ss *SecurityScheme) Build(root *yaml.Node, idx *index.SpecIndex) error { return nil } +// FindRequirement will attempt to locate a security requirement string from a supplied name. func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference[string] { for _, r := range sr.ValueRequirements { for k, v := range r.Value { @@ -61,6 +80,7 @@ func (sr *SecurityRequirement) FindRequirement(name string) []low.ValueReference return nil } +// Build will extract all security requirements func (sr *SecurityRequirement) Build(root *yaml.Node, idx *index.SpecIndex) error { if utils.IsNodeArray(root) { var requirements []low.ValueReference[map[low.KeyReference[string]][]low.ValueReference[string]] diff --git a/datamodel/low/v3/server.go b/datamodel/low/v3/server.go index 7e2afa9..af428d3 100644 --- a/datamodel/low/v3/server.go +++ b/datamodel/low/v3/server.go @@ -10,22 +10,20 @@ import ( "gopkg.in/yaml.v3" ) -const ( - VariablesLabel = "variables" - ServersLabel = "servers" - ServerLabel = "server" -) - +// Server represents a low-level OpenAPI 3+ Server object. +// - https://spec.openapis.org/oas/v3.1.0#server-object type Server struct { URL low.NodeReference[string] Description low.NodeReference[string] Variables low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*ServerVariable]] } -func (s *Server) FindVariable(ext string) *low.ValueReference[*ServerVariable] { - return low.FindItemInMap[*ServerVariable](ext, s.Variables.Value) +// FindVariable attempts to locate a ServerVariable instance using the supplied key. +func (s *Server) FindVariable(serverVar string) *low.ValueReference[*ServerVariable] { + return low.FindItemInMap[*ServerVariable](serverVar, s.Variables.Value) } +// Build will extract server variables from the supplied node. func (s *Server) Build(root *yaml.Node, idx *index.SpecIndex) error { kn, vars := utils.FindKeyNode(VariablesLabel, root.Content) if vars == nil { diff --git a/datamodel/low/v3/server_variable.go b/datamodel/low/v3/server_variable.go index ec18ac9..889a97d 100644 --- a/datamodel/low/v3/server_variable.go +++ b/datamodel/low/v3/server_variable.go @@ -4,6 +4,13 @@ import ( "github.com/pb33f/libopenapi/datamodel/low" ) +// ServerVariable represents a low-level OpenAPI 3+ ServerVariable object. +// +// ServerVariable is an object representing a Server Variable for server URL template substitution. +// - https://spec.openapis.org/oas/v3.1.0#server-variable-object +// +// This is the only struct that is not Buildable, it's not used by anything other than a Server instance, +// and it has nothing to build that requires it to be buildable. type ServerVariable struct { Enum []low.NodeReference[string] Default low.NodeReference[string]