feat: add attribute to schema model

This commit is contained in:
Tristan Cartledge
2023-04-17 12:41:59 +00:00
committed by Dave Shanley
parent 4c331de207
commit 99bf12c1c7
6 changed files with 137 additions and 111 deletions

View File

@@ -5,9 +5,10 @@ package base
import ( import (
"fmt" "fmt"
"gopkg.in/yaml.v3"
"sync" "sync"
"gopkg.in/yaml.v3"
"github.com/pb33f/libopenapi/datamodel/high" "github.com/pb33f/libopenapi/datamodel/high"
lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowmodel "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
@@ -68,6 +69,9 @@ type Schema struct {
// in 3.1 Items can be a Schema or a boolean // in 3.1 Items can be a Schema or a boolean
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"` Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
// 3.1 only, part of the JSON Schema spec provides a way to identify a subschema
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
// Compatible with all versions // Compatible with all versions
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"` Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"` Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
@@ -77,26 +81,26 @@ type Schema struct {
Minimum *int64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` Minimum *int64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
UniqueItems *int64 `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` UniqueItems *int64 `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"` Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"` AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"` Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"`
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *base.Schema low *base.Schema
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
@@ -282,6 +286,10 @@ func NewSchema(schema *base.Schema) *Schema {
} }
s.Enum = enum s.Enum = enum
if !schema.Anchor.IsEmpty() {
s.Anchor = schema.Anchor.Value
}
// async work. // async work.
// any polymorphic properties need to be handled in their own threads // any polymorphic properties need to be handled in their own threads
// any properties each need to be processed in their own thread. // any properties each need to be processed in their own thread.

View File

@@ -266,7 +266,8 @@ minLength: 1
maxItems: 20 maxItems: 20
minItems: 10 minItems: 10
maxProperties: 30 maxProperties: 30
minProperties: 1` minProperties: 1
$anchor: anchor`
var compNode yaml.Node var compNode yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &compNode) _ = yaml.Unmarshal([]byte(testSpec), &compNode)
@@ -310,6 +311,7 @@ minProperties: 1`
assert.True(t, compiled.WriteOnly) assert.True(t, compiled.WriteOnly)
assert.True(t, *compiled.Deprecated) assert.True(t, *compiled.Deprecated)
assert.True(t, *compiled.Nullable) assert.True(t, *compiled.Nullable)
assert.Equal(t, "anchor", compiled.Anchor)
wentLow := compiled.GoLow() wentLow := compiled.GoLow()
assert.Equal(t, 129, wentLow.AdditionalProperties.ValueNode.Line) assert.Equal(t, 129, wentLow.AdditionalProperties.ValueNode.Line)
@@ -317,8 +319,7 @@ minProperties: 1`
// now render it out! // now render it out!
schemaBytes, _ := compiled.Render() schemaBytes, _ := compiled.Render()
assert.Len(t, schemaBytes, 3460) assert.Len(t, schemaBytes, 3476)
} }
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) { func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {

View File

@@ -39,11 +39,12 @@ const (
OneOfLabel = "oneOf" OneOfLabel = "oneOf"
NotLabel = "not" NotLabel = "not"
TypeLabel = "type" TypeLabel = "type"
DiscriminatorLabel = "discriminator" DiscriminatorLabel = "discriminator"
ExclusiveMinimumLabel = "exclusiveMinimum" ExclusiveMinimumLabel = "exclusiveMinimum"
ExclusiveMaximumLabel = "exclusiveMaximum" ExclusiveMaximumLabel = "exclusiveMaximum"
SchemaLabel = "schema" SchemaLabel = "schema"
SchemaTypeLabel = "$schema" SchemaTypeLabel = "$schema"
AnchorLabel = "$anchor"
) )
/* /*

View File

@@ -90,6 +90,7 @@ type Schema struct {
PropertyNames low.NodeReference[*SchemaProxy] PropertyNames low.NodeReference[*SchemaProxy]
UnevaluatedItems low.NodeReference[*SchemaProxy] UnevaluatedItems low.NodeReference[*SchemaProxy]
UnevaluatedProperties low.NodeReference[*SchemaProxy] UnevaluatedProperties low.NodeReference[*SchemaProxy]
Anchor low.NodeReference[string]
// Compatible with all versions // Compatible with all versions
Title low.NodeReference[string] Title low.NodeReference[string]
@@ -394,6 +395,9 @@ func (s *Schema) Hash() [32]byte {
if !s.UnevaluatedItems.IsEmpty() { if !s.UnevaluatedItems.IsEmpty() {
d = append(d, low.GenerateHashString(s.UnevaluatedItems.Value)) d = append(d, low.GenerateHashString(s.UnevaluatedItems.Value))
} }
if !s.Anchor.IsEmpty() {
d = append(d, fmt.Sprint(s.Anchor.Value))
}
depSchemasKeys := make([]string, len(s.DependentSchemas.Value)) depSchemasKeys := make([]string, len(s.DependentSchemas.Value))
z = 0 z = 0
@@ -514,6 +518,7 @@ func (s *Schema) GetExtensions() map[low.KeyReference[string]]low.ValueReference
// - PropertyNames // - PropertyNames
// - UnevaluatedItems // - UnevaluatedItems
// - UnevaluatedProperties // - UnevaluatedProperties
// - Anchor
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
s.Reference = new(low.Reference) s.Reference = new(low.Reference)
if h, _, _ := utils.IsNodeRefValue(root); h { if h, _, _ := utils.IsNodeRefValue(root); h {
@@ -615,6 +620,14 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
} }
// handle anchor if set. (3.1)
_, anchorLabel, anchorNode := utils.FindKeyNodeFullTop(AnchorLabel, root.Content)
if anchorNode != nil {
s.Anchor = low.NodeReference[string]{
Value: anchorNode.Value, KeyNode: anchorLabel, ValueNode: anchorLabel,
}
}
// handle example if set. (3.0) // handle example if set. (3.0)
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
if expNode != nil { if expNode != nil {

View File

@@ -150,7 +150,8 @@ examples:
contains: contains:
type: int type: int
maxContains: 10 maxContains: 10
minContains: 1` minContains: 1
$anchor: anchor`
} }
func Test_Schema(t *testing.T) { func Test_Schema(t *testing.T) {
@@ -308,6 +309,7 @@ func Test_Schema(t *testing.T) {
assert.Equal(t, "string", sch.PropertyNames.Value.Schema().Type.Value.A) assert.Equal(t, "string", sch.PropertyNames.Value.Schema().Type.Value.A)
assert.Equal(t, "boolean", sch.UnevaluatedItems.Value.Schema().Type.Value.A) assert.Equal(t, "boolean", sch.UnevaluatedItems.Value.Schema().Type.Value.A)
assert.Equal(t, "integer", sch.UnevaluatedProperties.Value.Schema().Type.Value.A) assert.Equal(t, "integer", sch.UnevaluatedProperties.Value.Schema().Type.Value.A)
assert.Equal(t, "anchor", sch.Anchor.Value)
} }
func TestSchemaAllOfSequenceOrder(t *testing.T) { func TestSchemaAllOfSequenceOrder(t *testing.T) {

View File

@@ -5,86 +5,86 @@ package v3
// Label definitions used to look up vales in yaml.Node tree. // Label definitions used to look up vales in yaml.Node tree.
const ( const (
ComponentsLabel = "components" ComponentsLabel = "components"
SchemasLabel = "schemas" SchemasLabel = "schemas"
EncodingLabel = "encoding" EncodingLabel = "encoding"
HeadersLabel = "headers" HeadersLabel = "headers"
ExpressionLabel = "expression" ExpressionLabel = "expression"
InfoLabel = "info" InfoLabel = "info"
SwaggerLabel = "swagger" SwaggerLabel = "swagger"
ParametersLabel = "parameters" ParametersLabel = "parameters"
RequestBodyLabel = "requestBody" RequestBodyLabel = "requestBody"
RequestBodiesLabel = "requestBodies" RequestBodiesLabel = "requestBodies"
ResponsesLabel = "responses" ResponsesLabel = "responses"
CallbacksLabel = "callbacks" CallbacksLabel = "callbacks"
ContentLabel = "content" ContentLabel = "content"
PathsLabel = "paths" PathsLabel = "paths"
PathLabel = "path" PathLabel = "path"
WebhooksLabel = "webhooks" WebhooksLabel = "webhooks"
JSONSchemaDialectLabel = "jsonSchemaDialect" JSONSchemaDialectLabel = "jsonSchemaDialect"
JSONSchemaLabel = "$schema" JSONSchemaLabel = "$schema"
GetLabel = "get" GetLabel = "get"
PostLabel = "post" PostLabel = "post"
PatchLabel = "patch" PatchLabel = "patch"
PutLabel = "put" PutLabel = "put"
DeleteLabel = "delete" DeleteLabel = "delete"
OptionsLabel = "options" OptionsLabel = "options"
HeadLabel = "head" HeadLabel = "head"
TraceLabel = "trace" TraceLabel = "trace"
LinksLabel = "links" LinksLabel = "links"
DefaultLabel = "default" DefaultLabel = "default"
SecurityLabel = "security" SecurityLabel = "security"
SecuritySchemesLabel = "securitySchemes" SecuritySchemesLabel = "securitySchemes"
OAuthFlowsLabel = "flows" OAuthFlowsLabel = "flows"
VariablesLabel = "variables" VariablesLabel = "variables"
ServersLabel = "servers" ServersLabel = "servers"
ServerLabel = "server" ServerLabel = "server"
ImplicitLabel = "implicit" ImplicitLabel = "implicit"
PasswordLabel = "password" PasswordLabel = "password"
ClientCredentialsLabel = "clientCredentials" ClientCredentialsLabel = "clientCredentials"
AuthorizationCodeLabel = "authorizationCode" AuthorizationCodeLabel = "authorizationCode"
DescriptionLabel = "description" DescriptionLabel = "description"
URLLabel = "url" URLLabel = "url"
NameLabel = "name" NameLabel = "name"
EmailLabel = "email" EmailLabel = "email"
TitleLabel = "title" TitleLabel = "title"
TermsOfServiceLabel = "termsOfService" TermsOfServiceLabel = "termsOfService"
VersionLabel = "version" VersionLabel = "version"
OpenAPILabel = "openapi" OpenAPILabel = "openapi"
HostLabel = "host" HostLabel = "host"
BasePathLabel = "basePath" BasePathLabel = "basePath"
LicenseLabel = "license" LicenseLabel = "license"
ContactLabel = "contact" ContactLabel = "contact"
NamespaceLabel = "namespace" NamespaceLabel = "namespace"
PrefixLabel = "prefix" PrefixLabel = "prefix"
AttributeLabel = "attribute" AttributeLabel = "attribute"
WrappedLabel = "wrapped" WrappedLabel = "wrapped"
PropertyNameLabel = "propertyName" PropertyNameLabel = "propertyName"
SummaryLabel = "summary" SummaryLabel = "summary"
ValueLabel = "value" ValueLabel = "value"
ExternalValue = "externalValue" ExternalValue = "externalValue"
SchemaDialectLabel = "$schema" SchemaDialectLabel = "$schema"
ExclusiveMaximumLabel = "exclusiveMaximum" ExclusiveMaximumLabel = "exclusiveMaximum"
ExclusiveMinimumLabel = "exclusiveMinimum" ExclusiveMinimumLabel = "exclusiveMinimum"
TypeLabel = "type" TypeLabel = "type"
TagsLabel = "tags" TagsLabel = "tags"
MultipleOfLabel = "multipleOf" MultipleOfLabel = "multipleOf"
MaximumLabel = "maximum" MaximumLabel = "maximum"
MinimumLabel = "minimum" MinimumLabel = "minimum"
MaxLengthLabel = "maxLength" MaxLengthLabel = "maxLength"
MinLengthLabel = "minLength" MinLengthLabel = "minLength"
PatternLabel = "pattern" PatternLabel = "pattern"
FormatLabel = "format" FormatLabel = "format"
MaxItemsLabel = "maxItems" MaxItemsLabel = "maxItems"
ExamplesLabel = "examples" ExamplesLabel = "examples"
MinItemsLabel = "minItems" MinItemsLabel = "minItems"
UniqueItemsLabel = "uniqueItems" UniqueItemsLabel = "uniqueItems"
MaxPropertiesLabel = "maxProperties" MaxPropertiesLabel = "maxProperties"
MinPropertiesLabel = "minProperties" MinPropertiesLabel = "minProperties"
RequiredLabel = "required" RequiredLabel = "required"
EnumLabel = "enum" EnumLabel = "enum"
SchemaLabel = "schema" SchemaLabel = "schema"
NotLabel = "not" NotLabel = "not"
ItemsLabel = "items" ItemsLabel = "items"
PropertiesLabel = "properties" PropertiesLabel = "properties"
AllOfLabel = "allOf" AllOfLabel = "allOf"
@@ -136,4 +136,5 @@ const (
UnevaluatedPropertiesLabel = "unevaluatedProperties" UnevaluatedPropertiesLabel = "unevaluatedProperties"
DependentSchemasLabel = "dependentSchemas" DependentSchemasLabel = "dependentSchemas"
PatternPropertiesLabel = "patternProperties" PatternPropertiesLabel = "patternProperties"
AnchorLabel = "$anchor"
) )