mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-10 12:37:48 +00:00
V2 low-base docs in place
refactored constants a little also to clean things up.
This commit is contained in:
@@ -9,7 +9,7 @@ import (
|
|||||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Example represents an Example object as defined by OpenAPI 3+
|
// Example represents a high-level Example object as defined by OpenAPI 3+
|
||||||
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
||||||
type Example struct {
|
type Example struct {
|
||||||
Summary string
|
Summary string
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ExternalDoc represents an External Documentation object as defined by OpenAPI 2 and 3
|
// ExternalDoc represents a high-level External Documentation object as defined by OpenAPI 2 and 3
|
||||||
//
|
//
|
||||||
// Allows referencing an external resource for extended documentation.
|
// Allows referencing an external resource for extended documentation.
|
||||||
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Info represents an Info object as defined by both OpenAPI 2 and OpenAPI 3.
|
// Info represents a high-level Info object as defined by both OpenAPI 2 and OpenAPI 3.
|
||||||
//
|
//
|
||||||
// The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented
|
// The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented
|
||||||
// in editing or documentation generation tools for convenience.
|
// in editing or documentation generation tools for convenience.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
low "github.com/pb33f/libopenapi/datamodel/low/base"
|
||||||
)
|
)
|
||||||
|
|
||||||
// License is a representation of a License object as defined by OpenAPI 2 and OpenAPI 3
|
// License is a high-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
|
||||||
// v2 - https://swagger.io/specification/v2/#licenseObject
|
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||||
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||||
type License struct {
|
type License struct {
|
||||||
|
|||||||
11
datamodel/low/base/base.go
Normal file
11
datamodel/low/base/base.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
// Package base contains shared low-level models that are used between both versions 2 and 3 of OpenAPI.
|
||||||
|
// These models are consistent across both specifications, except for the Schema.
|
||||||
|
//
|
||||||
|
// OpenAPI 3 contains all the same properties that an OpenAPI 2 specification does, and more. The choice
|
||||||
|
// to not duplicate the schemas is to allow a graceful degradation pattern to be used. Schemas are the most complex
|
||||||
|
// beats, particularly when polymorphism is used. By re-using the same superset Schema across versions, we can ensure
|
||||||
|
// that all the latest features are collected, without damaging backwards compatibility.
|
||||||
|
package base
|
||||||
30
datamodel/low/base/constants.go
Normal file
30
datamodel/low/base/constants.go
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package base
|
||||||
|
|
||||||
|
// Constants for labels used to look up values within OpenAPI specifications.
|
||||||
|
const (
|
||||||
|
TagsLabel = "tags"
|
||||||
|
ExternalDocsLabel = "externalDocs"
|
||||||
|
ExamplesLabel = "examples"
|
||||||
|
ExampleLabel = "example"
|
||||||
|
ValueLabel = "value"
|
||||||
|
InfoLabel = "info"
|
||||||
|
ContactLabel = "contact"
|
||||||
|
LicenseLabel = "license"
|
||||||
|
PropertiesLabel = "properties"
|
||||||
|
AdditionalPropertiesLabel = "additionalProperties"
|
||||||
|
XMLLabel = "xml"
|
||||||
|
ItemsLabel = "items"
|
||||||
|
AllOfLabel = "allOf"
|
||||||
|
AnyOfLabel = "anyOf"
|
||||||
|
OneOfLabel = "oneOf"
|
||||||
|
NotLabel = "not"
|
||||||
|
TypeLabel = "type"
|
||||||
|
DiscriminatorLabel = "discriminator"
|
||||||
|
ExclusiveMinimumLabel = "exclusiveMinimum"
|
||||||
|
ExclusiveMaximumLabel = "exclusiveMaximum"
|
||||||
|
SchemaLabel = "schema"
|
||||||
|
SchemaTypeLabel = "$schema"
|
||||||
|
)
|
||||||
@@ -9,12 +9,16 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Contact represents a low-level representation of the Contact definitions found at
|
||||||
|
// v2 - https://swagger.io/specification/v2/#contactObject
|
||||||
|
// v3 - https://spec.openapis.org/oas/v3.1.0#contact-object
|
||||||
type Contact struct {
|
type Contact struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
URL low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
Email low.NodeReference[string]
|
Email low.NodeReference[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build is not implemented for Contact (there is nothing to build).
|
||||||
func (c *Contact) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (c *Contact) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
// not implemented.
|
// not implemented.
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -7,11 +7,20 @@ import (
|
|||||||
"github.com/pb33f/libopenapi/datamodel/low"
|
"github.com/pb33f/libopenapi/datamodel/low"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Discriminator is only used by OpenAPI 3+ documents, it represents a polymorphic discriminator used for schemas
|
||||||
|
//
|
||||||
|
// When request bodies or response payloads may be one of a number of different schemas, a discriminator object can be
|
||||||
|
// used to aid in serialization, deserialization, and validation. The discriminator is a specific object in a schema
|
||||||
|
// which is used to inform the consumer of the document of an alternative schema based on the value associated with it.
|
||||||
|
//
|
||||||
|
// When using the discriminator, inline schemas will not be considered.
|
||||||
|
// v3 - https://spec.openapis.org/oas/v3.1.0#discriminator-object
|
||||||
type Discriminator struct {
|
type Discriminator struct {
|
||||||
PropertyName low.NodeReference[string]
|
PropertyName low.NodeReference[string]
|
||||||
Mapping map[low.KeyReference[string]]low.ValueReference[string]
|
Mapping map[low.KeyReference[string]]low.ValueReference[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindMappingValue will return a ValueReference containing the string mapping value
|
||||||
func (d *Discriminator) FindMappingValue(key string) *low.ValueReference[string] {
|
func (d *Discriminator) FindMappingValue(key string) *low.ValueReference[string] {
|
||||||
for k, v := range d.Mapping {
|
for k, v := range d.Mapping {
|
||||||
if k.Value == key {
|
if k.Value == key {
|
||||||
|
|||||||
@@ -11,12 +11,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Example represents a low-level Example object as defined by OpenAPI 3+
|
||||||
ExamplesLabel = "examples"
|
// v3 - https://spec.openapis.org/oas/v3.1.0#example-object
|
||||||
ExampleLabel = "example"
|
|
||||||
ValueLabel = "value"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Example struct {
|
type Example struct {
|
||||||
Summary low.NodeReference[string]
|
Summary low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
@@ -25,10 +21,12 @@ type Example struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||||
func (ex *Example) FindExtension(ext string) *low.ValueReference[any] {
|
func (ex *Example) FindExtension(ext string) *low.ValueReference[any] {
|
||||||
return low.FindItemInMap[any](ext, ex.Extensions)
|
return low.FindItemInMap[any](ext, ex.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build extracts extensions and example value
|
||||||
func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
ex.Extensions = low.ExtractExtensions(root)
|
ex.Extensions = low.ExtractExtensions(root)
|
||||||
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
_, ln, vn := utils.FindKeyNodeFull(ValueLabel, root.Content)
|
||||||
@@ -68,6 +66,7 @@ func (ex *Example) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractExampleValue will extract a primitive example value (if possible), or just the raw Value property if not.
|
||||||
func ExtractExampleValue(exp *yaml.Node) any {
|
func ExtractExampleValue(exp *yaml.Node) any {
|
||||||
if utils.IsNodeBoolValue(exp) {
|
if utils.IsNodeBoolValue(exp) {
|
||||||
v, _ := strconv.ParseBool(exp.Value)
|
v, _ := strconv.ParseBool(exp.Value)
|
||||||
|
|||||||
@@ -9,16 +9,23 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ExternalDoc represents a low-level External Documentation object as defined by OpenAPI 2 and 3
|
||||||
|
//
|
||||||
|
// Allows referencing an external resource for extended documentation.
|
||||||
|
// v2 - https://swagger.io/specification/v2/#externalDocumentationObject
|
||||||
|
// v3 - https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
||||||
type ExternalDoc struct {
|
type ExternalDoc struct {
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
URL low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||||
func (ex *ExternalDoc) FindExtension(ext string) *low.ValueReference[any] {
|
func (ex *ExternalDoc) FindExtension(ext string) *low.ValueReference[any] {
|
||||||
return low.FindItemInMap[any](ext, ex.Extensions)
|
return low.FindItemInMap[any](ext, ex.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build will extract extensions from the ExternalDoc instance.
|
||||||
func (ex *ExternalDoc) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (ex *ExternalDoc) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
ex.Extensions = low.ExtractExtensions(root)
|
ex.Extensions = low.ExtractExtensions(root)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -9,12 +9,13 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Info represents a low-level Info object as defined by both OpenAPI 2 and OpenAPI 3.
|
||||||
InfoLabel = "info"
|
//
|
||||||
ContactLabel = "contact"
|
// The object provides metadata about the API. The metadata MAY be used by the clients if needed, and MAY be presented
|
||||||
LicenseLabel = "license"
|
// in editing or documentation generation tools for convenience.
|
||||||
)
|
//
|
||||||
|
// v2 - https://swagger.io/specification/v2/#infoObject
|
||||||
|
// v3 - https://spec.openapis.org/oas/v3.1.0#info-object
|
||||||
type Info struct {
|
type Info struct {
|
||||||
Title low.NodeReference[string]
|
Title low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
@@ -24,6 +25,7 @@ type Info struct {
|
|||||||
Version low.NodeReference[string]
|
Version low.NodeReference[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build will extract out the Contact and Info objects from the supplied root node.
|
||||||
func (i *Info) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (i *Info) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
// extract contact
|
// extract contact
|
||||||
contact, _ := low.ExtractObject[*Contact](ContactLabel, root, idx)
|
contact, _ := low.ExtractObject[*Contact](ContactLabel, root, idx)
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// License is a low-level representation of a License object as defined by OpenAPI 2 and OpenAPI 3
|
||||||
|
// v2 - https://swagger.io/specification/v2/#licenseObject
|
||||||
|
// v3 - https://spec.openapis.org/oas/v3.1.0#license-object
|
||||||
type License struct {
|
type License struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
URL low.NodeReference[string]
|
URL low.NodeReference[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build is not implemented for License (there is nothing to build)
|
||||||
func (l *License) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (l *License) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,44 +9,70 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// SchemaDynamicValue is used to hold multiple possible values for a schema property. There are two values, a left
|
||||||
PropertiesLabel = "properties"
|
// value (A) and a right value (B). The left value (A) is a 3.0 schema property value, the right value (B) is a 3.1
|
||||||
AdditionalPropertiesLabel = "additionalProperties"
|
// schema value.
|
||||||
XMLLabel = "xml"
|
//
|
||||||
ItemsLabel = "items"
|
// OpenAPI 3.1 treats a Schema as a real JSON schema, which means some properties become incompatible, or others
|
||||||
AllOfLabel = "allOf"
|
// now support more than one primitive type or structure.
|
||||||
AnyOfLabel = "anyOf"
|
// The N value is a bit to make it each to know which value (A or B) is used, this prevents having to
|
||||||
OneOfLabel = "oneOf"
|
// if/else on the value to determine which one is set.
|
||||||
NotLabel = "not"
|
|
||||||
TypeLabel = "type"
|
|
||||||
DiscriminatorLabel = "discriminator"
|
|
||||||
ExclusiveMinimumLabel = "exclusiveMinimum"
|
|
||||||
ExclusiveMaximumLabel = "exclusiveMaximum"
|
|
||||||
SchemaLabel = "schema"
|
|
||||||
SchemaTypeLabel = "$schema"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SchemaDynamicValue[A any, B any] struct {
|
type SchemaDynamicValue[A any, B any] struct {
|
||||||
N int // 0 == A, 1 == B
|
N int // 0 == A, 1 == B
|
||||||
A A
|
A A
|
||||||
B B
|
B B
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsA will return true if the 'A' or left value is set. (OpenAPI 3)
|
||||||
func (s SchemaDynamicValue[A, B]) IsA() bool {
|
func (s SchemaDynamicValue[A, B]) IsA() bool {
|
||||||
return s.N == 0
|
return s.N == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsB will return true if the 'B' or right value is set (OpenAPI 3.1)
|
||||||
func (s SchemaDynamicValue[A, B]) IsB() bool {
|
func (s SchemaDynamicValue[A, B]) IsB() bool {
|
||||||
return s.N == 1
|
return s.N == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Schema represents a JSON Schema that support Swagger, OpenAPI 3 and OpenAPI 3.1
|
||||||
|
//
|
||||||
|
// Until 3.1 OpenAPI had a strange relationship with JSON Schema. It's been a super-set/sub-set
|
||||||
|
// mix, which has been confusing. So, instead of building a bunch of different models, we have compressed
|
||||||
|
// all variations into a single model that makes it easy to support multiple spec types.
|
||||||
|
//
|
||||||
|
// - v2 schema: https://swagger.io/specification/v2/#schemaObject
|
||||||
|
// - v3 schema: https://swagger.io/specification/#schema-object
|
||||||
|
// - v3.1 schema: https://spec.openapis.org/oas/v3.1.0#schema-object
|
||||||
type Schema struct {
|
type Schema struct {
|
||||||
|
|
||||||
|
// Reference to the '$schema' dialect setting (3.1 only)
|
||||||
SchemaTypeRef low.NodeReference[string]
|
SchemaTypeRef low.NodeReference[string]
|
||||||
|
|
||||||
|
// In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean.
|
||||||
|
ExclusiveMaximum low.NodeReference[SchemaDynamicValue[bool, int64]]
|
||||||
|
|
||||||
|
// In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean.
|
||||||
|
ExclusiveMinimum low.NodeReference[SchemaDynamicValue[bool, int64]]
|
||||||
|
|
||||||
|
// In versions 2 and 3.0, this Type is a single value, so array will only ever have one value
|
||||||
|
// in version 3.1, Type can be multiple values
|
||||||
|
Type low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]
|
||||||
|
|
||||||
|
// Schemas are resolved on demand using a SchemaProxy
|
||||||
|
AllOf low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
||||||
|
|
||||||
|
// Polymorphic Schemas are only available in version 3+
|
||||||
|
OneOf low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
||||||
|
AnyOf low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
||||||
|
Discriminator low.NodeReference[*Discriminator]
|
||||||
|
|
||||||
|
// in 3.1 examples can be an array (which is recommended)
|
||||||
|
Examples low.NodeReference[[]low.ValueReference[any]]
|
||||||
|
|
||||||
|
// Compatible with all versions
|
||||||
Title low.NodeReference[string]
|
Title low.NodeReference[string]
|
||||||
MultipleOf low.NodeReference[int64]
|
MultipleOf low.NodeReference[int64]
|
||||||
Maximum low.NodeReference[int64]
|
Maximum low.NodeReference[int64]
|
||||||
ExclusiveMaximum low.NodeReference[SchemaDynamicValue[bool, int64]]
|
|
||||||
Minimum low.NodeReference[int64]
|
Minimum low.NodeReference[int64]
|
||||||
ExclusiveMinimum low.NodeReference[SchemaDynamicValue[bool, int64]]
|
|
||||||
MaxLength low.NodeReference[int64]
|
MaxLength low.NodeReference[int64]
|
||||||
MinLength low.NodeReference[int64]
|
MinLength low.NodeReference[int64]
|
||||||
Pattern low.NodeReference[string]
|
Pattern low.NodeReference[string]
|
||||||
@@ -58,10 +84,6 @@ type Schema struct {
|
|||||||
MinProperties low.NodeReference[int64]
|
MinProperties low.NodeReference[int64]
|
||||||
Required low.NodeReference[[]low.ValueReference[string]]
|
Required low.NodeReference[[]low.ValueReference[string]]
|
||||||
Enum low.NodeReference[[]low.ValueReference[string]]
|
Enum low.NodeReference[[]low.ValueReference[string]]
|
||||||
Type low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]
|
|
||||||
AllOf low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
|
||||||
OneOf low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
|
||||||
AnyOf low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
|
||||||
Not low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
Not low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
||||||
Items low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
Items low.NodeReference[[]low.ValueReference[*SchemaProxy]]
|
||||||
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
||||||
@@ -71,21 +93,35 @@ type Schema struct {
|
|||||||
ContentMediaType low.NodeReference[string]
|
ContentMediaType low.NodeReference[string]
|
||||||
Default low.NodeReference[any]
|
Default low.NodeReference[any]
|
||||||
Nullable low.NodeReference[bool]
|
Nullable low.NodeReference[bool]
|
||||||
Discriminator low.NodeReference[*Discriminator]
|
|
||||||
ReadOnly low.NodeReference[bool]
|
ReadOnly low.NodeReference[bool]
|
||||||
WriteOnly low.NodeReference[bool]
|
WriteOnly low.NodeReference[bool]
|
||||||
XML low.NodeReference[*XML]
|
XML low.NodeReference[*XML]
|
||||||
ExternalDocs low.NodeReference[*ExternalDoc]
|
ExternalDocs low.NodeReference[*ExternalDoc]
|
||||||
Example low.NodeReference[any]
|
Example low.NodeReference[any]
|
||||||
Examples low.NodeReference[[]low.ValueReference[any]]
|
|
||||||
Deprecated low.NodeReference[bool]
|
Deprecated low.NodeReference[bool]
|
||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindProperty will return a ValueReference pointer containing a SchemaProxy pointer
|
||||||
|
// from a property key name. if found
|
||||||
func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] {
|
func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] {
|
||||||
return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value)
|
return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build will perform a number of operations.
|
||||||
|
// Extraction of the following happens in this method:
|
||||||
|
// - Extensions
|
||||||
|
// - Type
|
||||||
|
// - ExclusiveMinimum and ExclusiveMaximum
|
||||||
|
// - Examples
|
||||||
|
// - AdditionalProperties
|
||||||
|
// - Discriminator
|
||||||
|
// - ExternalDocs
|
||||||
|
// - XML
|
||||||
|
// - Properties
|
||||||
|
// - AllOf, OneOf, AnyOf
|
||||||
|
// - Not
|
||||||
|
// - Items
|
||||||
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
if h, _, _ := utils.IsNodeRefValue(root); h {
|
if h, _, _ := utils.IsNodeRefValue(root); h {
|
||||||
ref, err := low.LocateRefNode(root, idx)
|
ref, err := low.LocateRefNode(root, idx)
|
||||||
@@ -400,6 +436,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// count the number of sub-schemas in a node.
|
||||||
func countSubSchemaItems(node *yaml.Node) int {
|
func countSubSchemaItems(node *yaml.Node) int {
|
||||||
if utils.IsNodeMap(node) {
|
if utils.IsNodeMap(node) {
|
||||||
return 1
|
return 1
|
||||||
@@ -410,15 +447,18 @@ func countSubSchemaItems(node *yaml.Node) int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// schema build result container used for async building.
|
||||||
type schemaProxyBuildResult struct {
|
type schemaProxyBuildResult struct {
|
||||||
k low.KeyReference[string]
|
k low.KeyReference[string]
|
||||||
v low.ValueReference[*SchemaProxy]
|
v low.ValueReference[*SchemaProxy]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extract extensions from schema
|
||||||
func (s *Schema) extractExtensions(root *yaml.Node) {
|
func (s *Schema) extractExtensions(root *yaml.Node) {
|
||||||
s.Extensions = low.ExtractExtensions(root)
|
s.Extensions = low.ExtractExtensions(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// build out a child schema for parent schema.
|
||||||
func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) {
|
func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) {
|
||||||
|
|
||||||
if valueNode != nil {
|
if valueNode != nil {
|
||||||
@@ -505,6 +545,9 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExtractSchema will return a pointer to a NodeReference that contains a *SchemaProxy if successful. The function
|
||||||
|
// will specifically look for a key node named 'schema' and extract the value mapped to that key. If the operation
|
||||||
|
// fails then no NodeReference is returned and an error is returned instead.
|
||||||
func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*SchemaProxy], error) {
|
func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*SchemaProxy], error) {
|
||||||
var schLabel, schNode *yaml.Node
|
var schLabel, schNode *yaml.Node
|
||||||
errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d"
|
errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d"
|
||||||
|
|||||||
@@ -9,6 +9,38 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called.
|
||||||
|
//
|
||||||
|
// Why use a Proxy design?
|
||||||
|
//
|
||||||
|
// There are three reasons.
|
||||||
|
//
|
||||||
|
// 1. Circular References and Endless Loops.
|
||||||
|
//
|
||||||
|
// JSON Schema allows for references to be used. This means references can loop around and create infinite recursive
|
||||||
|
// structures, These 'Circular references' technically mean a schema can NEVER be resolved, not without breaking the
|
||||||
|
// loop somewhere along the chain.
|
||||||
|
//
|
||||||
|
// Polymorphism in the form of 'oneOf' and 'anyOf' in version 3+ only exacerbates the problem.
|
||||||
|
//
|
||||||
|
// These circular traps can be discovered using the resolver, however it's still not enough to stop endless loops and
|
||||||
|
// endless goroutine spawning. A proxy design means that resolving occurs on demand and runs down a single level only.
|
||||||
|
// preventing any run-away loops.
|
||||||
|
//
|
||||||
|
// 2. Performance
|
||||||
|
//
|
||||||
|
// Even without circular references, Polymorphism creates large additional resolving chains that take a long time
|
||||||
|
// and slow things down when building. By preventing recursion through every polymorphic item, building models is kept
|
||||||
|
// fast and snappy, which is desired for realtime processing of specs.
|
||||||
|
//
|
||||||
|
// - Q: Yeah, but, why not just use state to avoiding re-visiting seen polymorphic nodes?
|
||||||
|
// - A: It's slow, takes up memory and still has runaway potential in very, very long chains.
|
||||||
|
//
|
||||||
|
// 3. Short Circuit Errors.
|
||||||
|
//
|
||||||
|
// Schemas are where things can get messy, mainly because the Schema standard changes between versions, and
|
||||||
|
// it's not actually JSONSchema until 3.1, so lots of times a bad schema will break parsing. Errors are only found
|
||||||
|
// when a schema is needed, so the rest of the document is parsed and ready to use.
|
||||||
type SchemaProxy struct {
|
type SchemaProxy struct {
|
||||||
kn *yaml.Node
|
kn *yaml.Node
|
||||||
vn *yaml.Node
|
vn *yaml.Node
|
||||||
@@ -17,12 +49,24 @@ type SchemaProxy struct {
|
|||||||
buildError error
|
buildError error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build will prepare the SchemaProxy for rendering, it does not build the Schema, only sets up internal state.
|
||||||
func (sp *SchemaProxy) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (sp *SchemaProxy) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
sp.vn = root
|
sp.vn = root
|
||||||
sp.idx = idx
|
sp.idx = idx
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Schema will first check if this SchemaProxy has already rendered the schema, and return the pre-rendered version
|
||||||
|
// first.
|
||||||
|
//
|
||||||
|
// If this is the first run of Schema(), then the SchemaProxy will create a new Schema from the underlying
|
||||||
|
// yaml.Node. Once built out, the SchemaProxy will record that Schema as rendered and store it for later use,
|
||||||
|
// (this is what is we mean when we say 'pre-rendered').
|
||||||
|
//
|
||||||
|
// Schema() then returns the newly created Schema.
|
||||||
|
//
|
||||||
|
// If anything goes wrong during the build, then nothing is returned and the error that occurred can
|
||||||
|
// be retrieved by using GetBuildError()
|
||||||
func (sp *SchemaProxy) Schema() *Schema {
|
func (sp *SchemaProxy) Schema() *Schema {
|
||||||
if sp.rendered != nil {
|
if sp.rendered != nil {
|
||||||
return sp.rendered
|
return sp.rendered
|
||||||
@@ -31,10 +75,6 @@ func (sp *SchemaProxy) Schema() *Schema {
|
|||||||
_ = low.BuildModel(sp.vn, schema)
|
_ = low.BuildModel(sp.vn, schema)
|
||||||
err := schema.Build(sp.vn, sp.idx)
|
err := schema.Build(sp.vn, sp.idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
//low.Log.Error("unable to build schema",
|
|
||||||
// zap.Int("line", sp.vn.Line),
|
|
||||||
// zap.Int("column", sp.vn.Column),
|
|
||||||
// zap.String("error", err.Error()))
|
|
||||||
sp.buildError = err
|
sp.buildError = err
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -42,6 +82,8 @@ func (sp *SchemaProxy) Schema() *Schema {
|
|||||||
return schema
|
return schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBuildError returns the build error that was set when Schema() was called. If Schema() has not been run, or
|
||||||
|
// there were no errors during build, then nil will be returned.
|
||||||
func (sp *SchemaProxy) GetBuildError() error {
|
func (sp *SchemaProxy) GetBuildError() error {
|
||||||
return sp.buildError
|
return sp.buildError
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,12 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
// Tag represents a low-level Tag instance that is backed by a low-level one.
|
||||||
TagsLabel = "tags"
|
//
|
||||||
ExternalDocsLabel = "externalDocs"
|
// Adds metadata to a single tag that is used by the Operation Object. It is not mandatory to have a Tag Object per
|
||||||
NameLabel = "name"
|
// tag defined in the Operation Object instances.
|
||||||
DescriptionLabel = "description"
|
// - v2: https://swagger.io/specification/v2/#tagObject
|
||||||
)
|
// - v3: https://swagger.io/specification/#tag-object
|
||||||
|
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
Description low.NodeReference[string]
|
Description low.NodeReference[string]
|
||||||
@@ -23,10 +22,12 @@ type Tag struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FindExtension returns a ValueReference containing the extension value, if found.
|
||||||
func (t *Tag) FindExtension(ext string) *low.ValueReference[any] {
|
func (t *Tag) FindExtension(ext string) *low.ValueReference[any] {
|
||||||
return low.FindItemInMap[any](ext, t.Extensions)
|
return low.FindItemInMap[any](ext, t.Extensions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build will extract extensions and external docs for the Tag.
|
||||||
func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||||
t.Extensions = low.ExtractExtensions(root)
|
t.Extensions = low.ExtractExtensions(root)
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ func (t *Tag) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: future mutation API experiment code is here. this snippet is to re-marshal the object.
|
||||||
//func (t *Tag) MarshalYAML() (interface{}, error) {
|
//func (t *Tag) MarshalYAML() (interface{}, error) {
|
||||||
// m := make(map[string]interface{})
|
// m := make(map[string]interface{})
|
||||||
// for i := range t.Extensions {
|
// for i := range t.Extensions {
|
||||||
|
|||||||
@@ -6,6 +6,14 @@ import (
|
|||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// XML represents a low-level representation of an XML object defined by all versions of OpenAPI.
|
||||||
|
//
|
||||||
|
// A metadata object that allows for more fine-tuned XML model definitions.
|
||||||
|
//
|
||||||
|
// When using arrays, XML element names are not inferred (for singular/plural forms) and the name property SHOULD be
|
||||||
|
// used to add that information. See examples for expected behavior.
|
||||||
|
// v2 - https://swagger.io/specification/v2/#xmlObject
|
||||||
|
// v3 - https://swagger.io/specification/#xml-object
|
||||||
type XML struct {
|
type XML struct {
|
||||||
Name low.NodeReference[string]
|
Name low.NodeReference[string]
|
||||||
Namespace low.NodeReference[string]
|
Namespace low.NodeReference[string]
|
||||||
@@ -15,6 +23,7 @@ type XML struct {
|
|||||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build will extract extensions from the XML instance.
|
||||||
func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error {
|
||||||
x.Extensions = low.ExtractExtensions(root)
|
x.Extensions = low.ExtractExtensions(root)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
Reference in New Issue
Block a user