diff --git a/datamodel/high/base/schema.go b/datamodel/high/base/schema.go index f2c3f47..817635e 100644 --- a/datamodel/high/base/schema.go +++ b/datamodel/high/base/schema.go @@ -4,239 +4,270 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/high" - lowmodel "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/base" - "sync" + "github.com/pb33f/libopenapi/datamodel/high" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/datamodel/low/base" + "sync" ) -// Schema represents a +// 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 { - SchemaTypeRef string - Title string - MultipleOf int64 - Maximum int64 - ExclusiveMaximumBool bool - ExclusiveMaximum int64 - Minimum int64 - ExclusiveMinimum int64 - ExclusiveMinimumBool bool - MaxLength int64 - MinLength int64 - Pattern string - Format string - MaxItems int64 - MinItems int64 - UniqueItems int64 - MaxProperties int64 - MinProperties int64 - Required []string - Enum []string - Type []string - AllOf []*SchemaProxy - OneOf []*SchemaProxy - AnyOf []*SchemaProxy - Not []*SchemaProxy - Items []*SchemaProxy - Properties map[string]*SchemaProxy - AdditionalProperties any - Description string - Default any - Nullable bool - Discriminator *Discriminator - ReadOnly bool - WriteOnly bool - XML *XML - ExternalDocs *ExternalDoc - Example any - Examples []any - Deprecated bool - Extensions map[string]any - low *base.Schema + + // 3.1 only, used to define a dialect for this schema, label is '$schema'. + SchemaTypeRef string + + // In versions 2 and 3.0, this ExclusiveMaximum can only be a boolean. + ExclusiveMaximumBool bool + + // In version 3.1, ExclusiveMaximum is an integer. + ExclusiveMaximum int64 + + // In versions 2 and 3.0, this ExclusiveMinimum can only be a boolean. + ExclusiveMinimum int64 + + // In version 3.1, ExclusiveMinimum is an integer. + ExclusiveMinimumBool bool + + // 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 []string + + // Schemas are resolved on demand using a SchemaProxy + AllOf []*SchemaProxy + + // Polymorphic Schemas are only available in version 3+ + OneOf []*SchemaProxy + AnyOf []*SchemaProxy + Discriminator *Discriminator + + // in 3.1 examples can be an array (which is recommended) + Examples []any + + // Compatible with all versions + Not []*SchemaProxy + Items []*SchemaProxy + Properties map[string]*SchemaProxy + Title string + MultipleOf int64 + Maximum int64 + Minimum int64 + MaxLength int64 + MinLength int64 + Pattern string + Format string + MaxItems int64 + MinItems int64 + UniqueItems int64 + MaxProperties int64 + MinProperties int64 + Required []string + Enum []string + AdditionalProperties any + Description string + Default any + Nullable bool + ReadOnly bool + WriteOnly bool + XML *XML + ExternalDocs *ExternalDoc + Example any + Deprecated bool + Extensions map[string]any + low *base.Schema } +// NewSchema will create a new high-level schema from a low-level one. func NewSchema(schema *base.Schema) *Schema { - s := new(Schema) - s.low = schema - s.Title = schema.Title.Value - s.MultipleOf = schema.MultipleOf.Value - s.Maximum = schema.Maximum.Value - s.Minimum = schema.Minimum.Value - // if we're dealing with a 3.0 spec using a bool - if !schema.ExclusiveMaximum.IsEmpty() && schema.ExclusiveMaximum.Value.IsA() { - s.ExclusiveMaximumBool = schema.ExclusiveMaximum.Value.A - } - // if we're dealing with a 3.1 spec using an int - if !schema.ExclusiveMaximum.IsEmpty() && schema.ExclusiveMaximum.Value.IsB() { - s.ExclusiveMaximum = schema.ExclusiveMaximum.Value.B - } - // if we're dealing with a 3.0 spec using a bool - if !schema.ExclusiveMinimum.IsEmpty() && schema.ExclusiveMinimum.Value.IsA() { - s.ExclusiveMinimumBool = schema.ExclusiveMinimum.Value.A - } - // if we're dealing with a 3.1 spec, using an int - if !schema.ExclusiveMinimum.IsEmpty() && schema.ExclusiveMinimum.Value.IsB() { - s.ExclusiveMinimum = schema.ExclusiveMinimum.Value.B - } - s.MaxLength = schema.MaxLength.Value - s.MinLength = schema.MinLength.Value - s.Pattern = schema.Pattern.Value - s.Format = schema.Format.Value - s.MaxItems = schema.MaxItems.Value - s.MinItems = schema.MinItems.Value - s.MaxProperties = schema.MaxProperties.Value - s.MinProperties = schema.MinProperties.Value + s := new(Schema) + s.low = schema + s.Title = schema.Title.Value + s.MultipleOf = schema.MultipleOf.Value + s.Maximum = schema.Maximum.Value + s.Minimum = schema.Minimum.Value + // if we're dealing with a 3.0 spec using a bool + if !schema.ExclusiveMaximum.IsEmpty() && schema.ExclusiveMaximum.Value.IsA() { + s.ExclusiveMaximumBool = schema.ExclusiveMaximum.Value.A + } + // if we're dealing with a 3.1 spec using an int + if !schema.ExclusiveMaximum.IsEmpty() && schema.ExclusiveMaximum.Value.IsB() { + s.ExclusiveMaximum = schema.ExclusiveMaximum.Value.B + } + // if we're dealing with a 3.0 spec using a bool + if !schema.ExclusiveMinimum.IsEmpty() && schema.ExclusiveMinimum.Value.IsA() { + s.ExclusiveMinimumBool = schema.ExclusiveMinimum.Value.A + } + // if we're dealing with a 3.1 spec, using an int + if !schema.ExclusiveMinimum.IsEmpty() && schema.ExclusiveMinimum.Value.IsB() { + s.ExclusiveMinimum = schema.ExclusiveMinimum.Value.B + } + s.MaxLength = schema.MaxLength.Value + s.MinLength = schema.MinLength.Value + s.Pattern = schema.Pattern.Value + s.Format = schema.Format.Value + s.MaxItems = schema.MaxItems.Value + s.MinItems = schema.MinItems.Value + s.MaxProperties = schema.MaxProperties.Value + s.MinProperties = schema.MinProperties.Value - // 3.0 spec is a single value - if !schema.Type.IsEmpty() && schema.Type.Value.IsA() { - s.Type = []string{schema.Type.Value.A} - } - // 3.1 spec may have multiple values - if !schema.Type.IsEmpty() && schema.Type.Value.IsB() { - for i := range schema.Type.Value.B { - s.Type = append(s.Type, schema.Type.Value.B[i].Value) - } - } - s.AdditionalProperties = schema.AdditionalProperties.Value - s.Description = schema.Description.Value - s.Default = schema.Default.Value - s.Nullable = schema.Nullable.Value - s.ReadOnly = schema.ReadOnly.Value - s.WriteOnly = schema.WriteOnly.Value - s.Example = schema.Example.Value - s.Deprecated = schema.Deprecated.Value - s.Extensions = high.ExtractExtensions(schema.Extensions) - if !schema.Discriminator.IsEmpty() { - s.Discriminator = NewDiscriminator(schema.Discriminator.Value) - } - if !schema.XML.IsEmpty() { - s.XML = NewXML(schema.XML.Value) - } - if !schema.ExternalDocs.IsEmpty() { - s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value) - } - var req []string - for i := range schema.Required.Value { - req = append(req, schema.Required.Value[i].Value) - } - s.Required = req + // 3.0 spec is a single value + if !schema.Type.IsEmpty() && schema.Type.Value.IsA() { + s.Type = []string{schema.Type.Value.A} + } + // 3.1 spec may have multiple values + if !schema.Type.IsEmpty() && schema.Type.Value.IsB() { + for i := range schema.Type.Value.B { + s.Type = append(s.Type, schema.Type.Value.B[i].Value) + } + } + s.AdditionalProperties = schema.AdditionalProperties.Value + s.Description = schema.Description.Value + s.Default = schema.Default.Value + s.Nullable = schema.Nullable.Value + s.ReadOnly = schema.ReadOnly.Value + s.WriteOnly = schema.WriteOnly.Value + s.Example = schema.Example.Value + s.Deprecated = schema.Deprecated.Value + s.Extensions = high.ExtractExtensions(schema.Extensions) + if !schema.Discriminator.IsEmpty() { + s.Discriminator = NewDiscriminator(schema.Discriminator.Value) + } + if !schema.XML.IsEmpty() { + s.XML = NewXML(schema.XML.Value) + } + if !schema.ExternalDocs.IsEmpty() { + s.ExternalDocs = NewExternalDoc(schema.ExternalDocs.Value) + } + var req []string + for i := range schema.Required.Value { + req = append(req, schema.Required.Value[i].Value) + } + s.Required = req - var enum []string - for i := range schema.Enum.Value { - enum = append(enum, schema.Enum.Value[i].Value) - } - s.Enum = enum + var enum []string + for i := range schema.Enum.Value { + enum = append(enum, schema.Enum.Value[i].Value) + } + s.Enum = enum - // async work. - // any polymorphic properties need to be handled in their own threads - // any properties each need to be processed in their own thread. - // we go as fast as we can. + // async work. + // any polymorphic properties need to be handled in their own threads + // any properties each need to be processed in their own thread. + // we go as fast as we can. - polyCompletedChan := make(chan bool) - propsChan := make(chan bool) - errChan := make(chan error) + polyCompletedChan := make(chan bool) + propsChan := make(chan bool) + errChan := make(chan error) - // schema async - buildOutSchema := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy, - doneChan chan bool, e chan error) { - bChan := make(chan *SchemaProxy) + // schema async + buildOutSchema := func(schemas []lowmodel.ValueReference[*base.SchemaProxy], items *[]*SchemaProxy, + doneChan chan bool, e chan error) { + bChan := make(chan *SchemaProxy) - // for every item, build schema async - buildSchemaChild := func(sch lowmodel.ValueReference[*base.SchemaProxy], bChan chan *SchemaProxy) { - p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - ValueNode: sch.ValueNode, - Value: sch.Value, - }} - bChan <- p - } - totalSchemas := len(schemas) - for v := range schemas { - go buildSchemaChild(schemas[v], bChan) - } - j := 0 - for j < totalSchemas { - select { - case t := <-bChan: - j++ - *items = append(*items, t) - } - } - doneChan <- true - } + // for every item, build schema async + buildSchemaChild := func(sch lowmodel.ValueReference[*base.SchemaProxy], bChan chan *SchemaProxy) { + p := &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + ValueNode: sch.ValueNode, + Value: sch.Value, + }} + bChan <- p + } + totalSchemas := len(schemas) + for v := range schemas { + go buildSchemaChild(schemas[v], bChan) + } + j := 0 + for j < totalSchemas { + select { + case t := <-bChan: + j++ + *items = append(*items, t) + } + } + doneChan <- true + } - // props async - plock := sync.RWMutex{} - var buildProps = func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool, - props map[string]*SchemaProxy) { - defer plock.Unlock() - plock.Lock() - props[k.Value] = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ - Value: v.Value, - KeyNode: k.KeyNode, - ValueNode: v.ValueNode, - }, - } - s.Properties = props - c <- true - } + // props async + plock := sync.RWMutex{} + var buildProps = func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*base.SchemaProxy], c chan bool, + props map[string]*SchemaProxy) { + defer plock.Unlock() + plock.Lock() + props[k.Value] = &SchemaProxy{schema: &lowmodel.NodeReference[*base.SchemaProxy]{ + Value: v.Value, + KeyNode: k.KeyNode, + ValueNode: v.ValueNode, + }, + } + s.Properties = props + c <- true + } - props := make(map[string]*SchemaProxy) - for k, v := range schema.Properties.Value { - go buildProps(k, v, propsChan, props) - } + props := make(map[string]*SchemaProxy) + for k, v := range schema.Properties.Value { + go buildProps(k, v, propsChan, props) + } - var allOf []*SchemaProxy - var oneOf []*SchemaProxy - var anyOf []*SchemaProxy - var not []*SchemaProxy - var items []*SchemaProxy + var allOf []*SchemaProxy + var oneOf []*SchemaProxy + var anyOf []*SchemaProxy + var not []*SchemaProxy + var items []*SchemaProxy - if !schema.AllOf.IsEmpty() { - go buildOutSchema(schema.AllOf.Value, &allOf, polyCompletedChan, errChan) - } - if !schema.AnyOf.IsEmpty() { - go buildOutSchema(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan) - } - if !schema.OneOf.IsEmpty() { - go buildOutSchema(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan) - } - if !schema.Not.IsEmpty() { - go buildOutSchema(schema.Not.Value, ¬, polyCompletedChan, errChan) - } - if !schema.Items.IsEmpty() { - go buildOutSchema(schema.Items.Value, &items, polyCompletedChan, errChan) - } + if !schema.AllOf.IsEmpty() { + go buildOutSchema(schema.AllOf.Value, &allOf, polyCompletedChan, errChan) + } + if !schema.AnyOf.IsEmpty() { + go buildOutSchema(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan) + } + if !schema.OneOf.IsEmpty() { + go buildOutSchema(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan) + } + if !schema.Not.IsEmpty() { + go buildOutSchema(schema.Not.Value, ¬, polyCompletedChan, errChan) + } + if !schema.Items.IsEmpty() { + go buildOutSchema(schema.Items.Value, &items, polyCompletedChan, errChan) + } - completeChildren := 0 - completedProps := 0 - totalProps := len(schema.Properties.Value) - totalChildren := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Items.Value) + len(schema.Not.Value) - if totalProps+totalChildren > 0 { - allDone: - for true { - select { - case <-polyCompletedChan: - completeChildren++ - if totalProps == completedProps && totalChildren == completeChildren { - break allDone - } - case <-propsChan: - completedProps++ - if totalProps == completedProps && totalChildren == completeChildren { - break allDone - } - } - } - } - s.OneOf = oneOf - s.AnyOf = anyOf - s.AllOf = allOf - s.Items = items - s.Not = not + completeChildren := 0 + completedProps := 0 + totalProps := len(schema.Properties.Value) + totalChildren := len(schema.AllOf.Value) + len(schema.OneOf.Value) + len(schema.AnyOf.Value) + len(schema.Items.Value) + len(schema.Not.Value) + if totalProps+totalChildren > 0 { + allDone: + for true { + select { + case <-polyCompletedChan: + completeChildren++ + if totalProps == completedProps && totalChildren == completeChildren { + break allDone + } + case <-propsChan: + completedProps++ + if totalProps == completedProps && totalChildren == completeChildren { + break allDone + } + } + } + } + s.OneOf = oneOf + s.AnyOf = anyOf + s.AllOf = allOf + s.Items = items + s.Not = not - return s + return s } +// GoLow will return the low-level instance of Schema that was used to create the high level one. func (s *Schema) GoLow() *base.Schema { - return s.low + return s.low } diff --git a/datamodel/high/base/schema_proxy.go b/datamodel/high/base/schema_proxy.go index de99955..8a20403 100644 --- a/datamodel/high/base/schema_proxy.go +++ b/datamodel/high/base/schema_proxy.go @@ -8,15 +8,52 @@ import ( "github.com/pb33f/libopenapi/datamodel/low/base" ) +// SchemaProxy exists as a stub that will create a Schema once (and only once) the Schema() method is called. An +// underlying low-level SchemaProxy backs this high-level one. +// +// 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 { schema *low.NodeReference[*base.SchemaProxy] buildError error } +// NewSchemaProxy creates a new high-level SchemaProxy from a low-level one. func NewSchemaProxy(schema *low.NodeReference[*base.SchemaProxy]) *SchemaProxy { return &SchemaProxy{schema: schema} } +// Schema will create a new Schema instance using NewSchema from the low-level SchemaProxy backing this high-level one. +// If there is a problem building the Schema, then this method will return nil. Use GetBuildError to gain access +// to that building error. func (sp *SchemaProxy) Schema() *Schema { s := sp.schema.Value.Schema() if s == nil { @@ -26,6 +63,7 @@ func (sp *SchemaProxy) Schema() *Schema { return NewSchema(s) } +// GetBuildError returns any error that was thrown when calling Schema() func (sp *SchemaProxy) GetBuildError() error { return sp.buildError } diff --git a/datamodel/high/base/schema_test.go b/datamodel/high/base/schema_test.go index d5228cd..aeef79f 100644 --- a/datamodel/high/base/schema_test.go +++ b/datamodel/high/base/schema_test.go @@ -4,18 +4,19 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/low" - lowbase "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/pb33f/libopenapi/index" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "fmt" + "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/index" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestNewSchemaProxy(t *testing.T) { - // check proxy - yml := `components: + // check proxy + yml := `components: schemas: rice: type: string @@ -28,34 +29,34 @@ func TestNewSchemaProxy(t *testing.T) { rice: $ref: '#/components/schemas/rice'` - var idxNode, compNode yaml.Node - mErr := yaml.Unmarshal([]byte(yml), &idxNode) - assert.NoError(t, mErr) - idx := index.NewSpecIndex(&idxNode) + var idxNode, compNode yaml.Node + mErr := yaml.Unmarshal([]byte(yml), &idxNode) + assert.NoError(t, mErr) + idx := index.NewSpecIndex(&idxNode) - yml = `properties: + yml = `properties: rice: $ref: '#/components/schemas/I-do-not-exist'` - _ = yaml.Unmarshal([]byte(yml), &compNode) + _ = yaml.Unmarshal([]byte(yml), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], idx) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], idx) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: idxNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: idxNode.Content[0], + } - sch1 := SchemaProxy{schema: &lowproxy} - assert.Nil(t, sch1.Schema()) - assert.Error(t, sch1.GetBuildError()) + sch1 := SchemaProxy{schema: &lowproxy} + assert.Nil(t, sch1.Schema()) + assert.Error(t, sch1.GetBuildError()) } func TestNewSchemaProxy_WithObject(t *testing.T) { - testSpec := `type: object + testSpec := `type: object description: something object discriminator: propertyName: athing @@ -164,32 +165,32 @@ externalDocs: enum: [fish, cake] required: [cake, fish]` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) - wentLow := compiled.GoLow() - assert.Equal(t, 102, wentLow.AdditionalProperties.ValueNode.Line) + wentLow := compiled.GoLow() + assert.Equal(t, 102, wentLow.AdditionalProperties.ValueNode.Line) } func TestNewSchemaProxy_WithObject_FinishPoly(t *testing.T) { - testSpec := `type: object + testSpec := `type: object description: something object discriminator: propertyName: athing @@ -294,35 +295,99 @@ externalDocs: enum: [fish, cake] required: [cake, fish]` - var compNode yaml.Node - _ = yaml.Unmarshal([]byte(testSpec), &compNode) + var compNode yaml.Node + _ = yaml.Unmarshal([]byte(testSpec), &compNode) - sp := new(lowbase.SchemaProxy) - err := sp.Build(compNode.Content[0], nil) - assert.NoError(t, err) + sp := new(lowbase.SchemaProxy) + err := sp.Build(compNode.Content[0], nil) + assert.NoError(t, err) - lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ - Value: sp, - ValueNode: compNode.Content[0], - } + lowproxy := low.NodeReference[*lowbase.SchemaProxy]{ + Value: sp, + ValueNode: compNode.Content[0], + } - schemaProxy := NewSchemaProxy(&lowproxy) - compiled := schemaProxy.Schema() + schemaProxy := NewSchemaProxy(&lowproxy) + compiled := schemaProxy.Schema() - assert.NotNil(t, compiled) - assert.Nil(t, schemaProxy.GetBuildError()) + assert.NotNil(t, compiled) + assert.Nil(t, schemaProxy.GetBuildError()) - assert.True(t, compiled.ExclusiveMaximumBool) - assert.False(t, compiled.ExclusiveMinimumBool) - assert.Equal(t, int64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum) - assert.Equal(t, int64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum) - assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) + assert.True(t, compiled.ExclusiveMaximumBool) + assert.False(t, compiled.ExclusiveMinimumBool) + assert.Equal(t, int64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum) + assert.Equal(t, int64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum) + assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) - wentLow := compiled.GoLow() - assert.Equal(t, 96, wentLow.AdditionalProperties.ValueNode.Line) - assert.Equal(t, 100, wentLow.XML.ValueNode.Line) + wentLow := compiled.GoLow() + assert.Equal(t, 96, wentLow.AdditionalProperties.ValueNode.Line) + assert.Equal(t, 100, wentLow.XML.ValueNode.Line) - wentLower := compiled.XML.GoLow() - assert.Equal(t, 100, wentLower.Name.ValueNode.Line) + wentLower := compiled.XML.GoLow() + assert.Equal(t, 100, wentLower.Name.ValueNode.Line) + +} + +func ExampleNewSchema() { + + // create an example schema object + // this can be either JSON or YAML. + yml := ` +title: this is a schema +type: object +properties: + aProperty: + description: this is an integer property + type: integer + format: int64` + + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) + + // build out the low-level model + var lowSchema lowbase.Schema + _ = low.BuildModel(&node, &lowSchema) + _ = lowSchema.Build(node.Content[0], nil) + + // build the high level model + highSchema := NewSchema(&lowSchema) + + // print out the description of 'aProperty' + fmt.Print(highSchema.Properties["aProperty"].Schema().Description) + // Output: this is an integer property + +} + +func ExampleNewSchemaProxy() { + + // create an example schema object + // this can be either JSON or YAML. + yml := ` +title: this is a schema +type: object +properties: + aProperty: + description: this is an integer property + type: integer + format: int64` + + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) + + // build out the low-level model + var lowSchema lowbase.SchemaProxy + _ = low.BuildModel(&node, &lowSchema) + _ = lowSchema.Build(node.Content[0], nil) + + // build the high level schema proxy + highSchema := NewSchemaProxy(&low.NodeReference[*lowbase.SchemaProxy]{ + Value: &lowSchema, + }) + + // print out the description of 'aProperty' + fmt.Print(highSchema.Schema().Properties["aProperty"].Schema().Description) + // Output: this is an integer property } diff --git a/datamodel/high/base/tag.go b/datamodel/high/base/tag.go index 958b0d6..3f7293d 100644 --- a/datamodel/high/base/tag.go +++ b/datamodel/high/base/tag.go @@ -4,38 +4,44 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/high" - low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" ) +// Tag represents a high-level Tag instance that is backed by a low-level one. +// - v2: https://swagger.io/specification/v2/#tagObject +// - v3: https://swagger.io/specification/#tag-object type Tag struct { - Name string - Description string - ExternalDocs *ExternalDoc - Extensions map[string]any - low *low.Tag + Name string + Description string + ExternalDocs *ExternalDoc + Extensions map[string]any + low *low.Tag } +// NewTag creates a new high-level Tag instance that is backed by a low-level one. func NewTag(tag *low.Tag) *Tag { - t := new(Tag) - t.low = tag - if !tag.Name.IsEmpty() { - t.Name = tag.Name.Value - } - if !tag.Description.IsEmpty() { - t.Description = tag.Description.Value - } - if !tag.ExternalDocs.IsEmpty() { - t.ExternalDocs = NewExternalDoc(tag.ExternalDocs.Value) - } - t.Extensions = high.ExtractExtensions(tag.Extensions) - return t + t := new(Tag) + t.low = tag + if !tag.Name.IsEmpty() { + t.Name = tag.Name.Value + } + if !tag.Description.IsEmpty() { + t.Description = tag.Description.Value + } + if !tag.ExternalDocs.IsEmpty() { + t.ExternalDocs = NewExternalDoc(tag.ExternalDocs.Value) + } + t.Extensions = high.ExtractExtensions(tag.Extensions) + return t } +// GoLow returns the low-level Tag instance used to create the high-level one. func (t *Tag) GoLow() *low.Tag { - return t.low + return t.low } +// Experimental mutation API. //func (t *Tag) SetName(value string) { // t.GoLow().Name.ValueNode.Value = value //} diff --git a/datamodel/high/base/tag_test.go b/datamodel/high/base/tag_test.go index 564a61f..038fa18 100644 --- a/datamodel/high/base/tag_test.go +++ b/datamodel/high/base/tag_test.go @@ -4,37 +4,66 @@ package base import ( - lowmodel "github.com/pb33f/libopenapi/datamodel/low" - lowbase "github.com/pb33f/libopenapi/datamodel/low/base" - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" - "testing" + "fmt" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" + "testing" ) func TestNewTag(t *testing.T) { - var cNode yaml.Node + var cNode yaml.Node - yml := `name: chicken + yml := `name: chicken description: nuggets externalDocs: url: https://pb33f.io x-hack: code` - _ = yaml.Unmarshal([]byte(yml), &cNode) + _ = yaml.Unmarshal([]byte(yml), &cNode) - var lowTag lowbase.Tag - _ = lowmodel.BuildModel(&cNode, &lowTag) - _ = lowTag.Build(cNode.Content[0], nil) + var lowTag lowbase.Tag + _ = lowmodel.BuildModel(&cNode, &lowTag) + _ = lowTag.Build(cNode.Content[0], nil) - highTag := NewTag(&lowTag) + highTag := NewTag(&lowTag) - assert.Equal(t, "chicken", highTag.Name) - assert.Equal(t, "nuggets", highTag.Description) - assert.Equal(t, "https://pb33f.io", highTag.ExternalDocs.URL) - assert.Equal(t, "code", highTag.Extensions["x-hack"]) + assert.Equal(t, "chicken", highTag.Name) + assert.Equal(t, "nuggets", highTag.Description) + assert.Equal(t, "https://pb33f.io", highTag.ExternalDocs.URL) + assert.Equal(t, "code", highTag.Extensions["x-hack"]) - wentLow := highTag.GoLow() - assert.Equal(t, 5, wentLow.FindExtension("x-hack").ValueNode.Line) + wentLow := highTag.GoLow() + assert.Equal(t, 5, wentLow.FindExtension("x-hack").ValueNode.Line) } + +func ExampleNewTag() { + + // create an example schema object + // this can be either JSON or YAML. + yml := ` +name: Purchases +description: All kinds of purchase related operations +externalDocs: + url: https://pb33f.io/purchases +x-hack: code` + + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) + + // build out the low-level model + var lowTag lowbase.Tag + _ = lowmodel.BuildModel(&node, &lowTag) + _ = lowTag.Build(node.Content[0], nil) + + // build the high level tag + highTag := NewTag(&lowTag) + + // print out the tag name + fmt.Print(highTag.Name) + // Output: Purchases +} diff --git a/datamodel/high/base/xml.go b/datamodel/high/base/xml.go index 0fa3c83..8336294 100644 --- a/datamodel/high/base/xml.go +++ b/datamodel/high/base/xml.go @@ -4,32 +4,38 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/high" - low "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/datamodel/high" + low "github.com/pb33f/libopenapi/datamodel/low/base" ) +// XML represents a high-level representation of an XML object defined by all versions of OpenAPI and backed by +// low-level XML object. +// v2 - https://swagger.io/specification/v2/#xmlObject +// v3 - https://swagger.io/specification/#xml-object type XML struct { - Name string - Namespace string - Prefix string - Attribute bool - Wrapped bool - Extensions map[string]any - low *low.XML + Name string + Namespace string + Prefix string + Attribute bool + Wrapped bool + Extensions map[string]any + low *low.XML } +// NewXML creates a new high-level XML instance from a low-level one. func NewXML(xml *low.XML) *XML { - x := new(XML) - x.low = xml - x.Name = xml.Name.Value - x.Namespace = xml.Namespace.Value - x.Prefix = xml.Namespace.Value - x.Attribute = xml.Attribute.Value - x.Wrapped = xml.Wrapped.Value - x.Extensions = high.ExtractExtensions(xml.Extensions) - return x + x := new(XML) + x.low = xml + x.Name = xml.Name.Value + x.Namespace = xml.Namespace.Value + x.Prefix = xml.Namespace.Value + x.Attribute = xml.Attribute.Value + x.Wrapped = xml.Wrapped.Value + x.Extensions = high.ExtractExtensions(xml.Extensions) + return x } +// GoLow returns the low level XML reference used to create the high level one. func (x *XML) GoLow() *low.XML { - return x.low + return x.low } diff --git a/datamodel/high/base/xml_test.go b/datamodel/high/base/xml_test.go new file mode 100644 index 0000000..4633e55 --- /dev/null +++ b/datamodel/high/base/xml_test.go @@ -0,0 +1,36 @@ +// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley +// SPDX-License-Identifier: MIT + +package base + +import ( + "fmt" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" + lowbase "github.com/pb33f/libopenapi/datamodel/low/base" + "gopkg.in/yaml.v3" +) + +func ExampleNewXML() { + + // create an example schema object + // this can be either JSON or YAML. + yml := ` +namespace: https://pb33f.io/schema +prefix: sample` + + // unmarshal raw bytes + var node yaml.Node + _ = yaml.Unmarshal([]byte(yml), &node) + + // build out the low-level model + var lowXML lowbase.XML + _ = lowmodel.BuildModel(&node, &lowXML) + _ = lowXML.Build(node.Content[0], nil) + + // build the high level tag + highXML := NewXML(&lowXML) + + // print out the XML namespace + fmt.Print(highXML.Namespace) + // Output: https://pb33f.io/schema +} diff --git a/datamodel/low/base/schema.go b/datamodel/low/base/schema.go index 28f1320..c2f638d 100644 --- a/datamodel/low/base/schema.go +++ b/datamodel/low/base/schema.go @@ -1,542 +1,542 @@ package base import ( - "fmt" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/index" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" - "strconv" + "fmt" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" + "strconv" ) const ( - 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" + 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" ) type SchemaDynamicValue[A any, B any] struct { - N int // 0 == A, 1 == B - A A - B B + N int // 0 == A, 1 == B + A A + B B } func (s SchemaDynamicValue[A, B]) IsA() bool { - return s.N == 0 + return s.N == 0 } func (s SchemaDynamicValue[A, B]) IsB() bool { - return s.N == 1 + return s.N == 1 } type Schema struct { - SchemaTypeRef low.NodeReference[string] - Title low.NodeReference[string] - MultipleOf low.NodeReference[int64] - Maximum low.NodeReference[int64] - ExclusiveMaximum low.NodeReference[SchemaDynamicValue[bool, int64]] - Minimum low.NodeReference[int64] - ExclusiveMinimum low.NodeReference[SchemaDynamicValue[bool, int64]] - MaxLength low.NodeReference[int64] - MinLength low.NodeReference[int64] - Pattern low.NodeReference[string] - Format low.NodeReference[string] - MaxItems low.NodeReference[int64] - MinItems low.NodeReference[int64] - UniqueItems low.NodeReference[int64] - MaxProperties low.NodeReference[int64] - MinProperties low.NodeReference[int64] - Required 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]] - Items low.NodeReference[[]low.ValueReference[*SchemaProxy]] - Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] - AdditionalProperties low.NodeReference[any] - Description low.NodeReference[string] - ContentEncoding low.NodeReference[string] - ContentMediaType low.NodeReference[string] - Default low.NodeReference[any] - Nullable low.NodeReference[bool] - Discriminator low.NodeReference[*Discriminator] - ReadOnly low.NodeReference[bool] - WriteOnly low.NodeReference[bool] - XML low.NodeReference[*XML] - ExternalDocs low.NodeReference[*ExternalDoc] - Example low.NodeReference[any] - Examples low.NodeReference[[]low.ValueReference[any]] - Deprecated low.NodeReference[bool] - Extensions map[low.KeyReference[string]]low.ValueReference[any] + SchemaTypeRef low.NodeReference[string] + Title low.NodeReference[string] + MultipleOf low.NodeReference[int64] + Maximum low.NodeReference[int64] + ExclusiveMaximum low.NodeReference[SchemaDynamicValue[bool, int64]] + Minimum low.NodeReference[int64] + ExclusiveMinimum low.NodeReference[SchemaDynamicValue[bool, int64]] + MaxLength low.NodeReference[int64] + MinLength low.NodeReference[int64] + Pattern low.NodeReference[string] + Format low.NodeReference[string] + MaxItems low.NodeReference[int64] + MinItems low.NodeReference[int64] + UniqueItems low.NodeReference[int64] + MaxProperties low.NodeReference[int64] + MinProperties low.NodeReference[int64] + Required 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]] + Items low.NodeReference[[]low.ValueReference[*SchemaProxy]] + Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] + AdditionalProperties low.NodeReference[any] + Description low.NodeReference[string] + ContentEncoding low.NodeReference[string] + ContentMediaType low.NodeReference[string] + Default low.NodeReference[any] + Nullable low.NodeReference[bool] + Discriminator low.NodeReference[*Discriminator] + ReadOnly low.NodeReference[bool] + WriteOnly low.NodeReference[bool] + XML low.NodeReference[*XML] + ExternalDocs low.NodeReference[*ExternalDoc] + Example low.NodeReference[any] + Examples low.NodeReference[[]low.ValueReference[any]] + Deprecated low.NodeReference[bool] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } 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) } func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { - if h, _, _ := utils.IsNodeRefValue(root); h { - ref, err := low.LocateRefNode(root, idx) - if ref != nil { - root = ref - if err != nil { - if !idx.AllowCircularReferenceResolving() { - return fmt.Errorf("build schema failed: %s", err.Error()) - } - } - } else { - return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d", - root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) - } - } + if h, _, _ := utils.IsNodeRefValue(root); h { + ref, err := low.LocateRefNode(root, idx) + if ref != nil { + root = ref + if err != nil { + if !idx.AllowCircularReferenceResolving() { + return fmt.Errorf("build schema failed: %s", err.Error()) + } + } + } else { + return fmt.Errorf("build schema failed: reference cannot be found: '%s', line %d, col %d", + root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) + } + } - s.extractExtensions(root) + s.extractExtensions(root) - // determine schema type, singular (3.0) or multiple (3.1), use a variable value - _, typeLabel, typeValue := utils.FindKeyNodeFull(TypeLabel, root.Content) - if typeValue != nil { - if utils.IsNodeStringValue(typeValue) { - s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ - KeyNode: typeLabel, - ValueNode: typeValue, - Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 0, A: typeValue.Value}, - } - } - if utils.IsNodeArray(typeValue) { + // determine schema type, singular (3.0) or multiple (3.1), use a variable value + _, typeLabel, typeValue := utils.FindKeyNodeFull(TypeLabel, root.Content) + if typeValue != nil { + if utils.IsNodeStringValue(typeValue) { + s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ + KeyNode: typeLabel, + ValueNode: typeValue, + Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 0, A: typeValue.Value}, + } + } + if utils.IsNodeArray(typeValue) { - var refs []low.ValueReference[string] - for r := range typeValue.Content { - refs = append(refs, low.ValueReference[string]{ - Value: typeValue.Content[r].Value, - ValueNode: typeValue.Content[r], - }) - } - s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ - KeyNode: typeLabel, - ValueNode: typeValue, - Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 1, B: refs}, - } - } - } + var refs []low.ValueReference[string] + for r := range typeValue.Content { + refs = append(refs, low.ValueReference[string]{ + Value: typeValue.Content[r].Value, + ValueNode: typeValue.Content[r], + }) + } + s.Type = low.NodeReference[SchemaDynamicValue[string, []low.ValueReference[string]]]{ + KeyNode: typeLabel, + ValueNode: typeValue, + Value: SchemaDynamicValue[string, []low.ValueReference[string]]{N: 1, B: refs}, + } + } + } - // determine exclusive minimum type, bool (3.0) or int (3.1) - _, exMinLabel, exMinValue := utils.FindKeyNodeFull(ExclusiveMinimumLabel, root.Content) - if exMinValue != nil { - if utils.IsNodeBoolValue(exMinValue) { - val, _ := strconv.ParseBool(exMinValue.Value) - s.ExclusiveMinimum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ - KeyNode: exMinLabel, - ValueNode: exMinValue, - Value: SchemaDynamicValue[bool, int64]{N: 0, A: val}, - } - } - if utils.IsNodeIntValue(exMinValue) { - val, _ := strconv.ParseInt(exMinValue.Value, 10, 64) - s.ExclusiveMinimum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ - KeyNode: exMinLabel, - ValueNode: exMinValue, - Value: SchemaDynamicValue[bool, int64]{N: 1, B: val}, - } - } - } + // determine exclusive minimum type, bool (3.0) or int (3.1) + _, exMinLabel, exMinValue := utils.FindKeyNodeFull(ExclusiveMinimumLabel, root.Content) + if exMinValue != nil { + if utils.IsNodeBoolValue(exMinValue) { + val, _ := strconv.ParseBool(exMinValue.Value) + s.ExclusiveMinimum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ + KeyNode: exMinLabel, + ValueNode: exMinValue, + Value: SchemaDynamicValue[bool, int64]{N: 0, A: val}, + } + } + if utils.IsNodeIntValue(exMinValue) { + val, _ := strconv.ParseInt(exMinValue.Value, 10, 64) + s.ExclusiveMinimum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ + KeyNode: exMinLabel, + ValueNode: exMinValue, + Value: SchemaDynamicValue[bool, int64]{N: 1, B: val}, + } + } + } - // determine exclusive maximum type, bool (3.0) or int (3.1) - _, exMaxLabel, exMaxValue := utils.FindKeyNodeFull(ExclusiveMaximumLabel, root.Content) - if exMaxValue != nil { - if utils.IsNodeBoolValue(exMaxValue) { - val, _ := strconv.ParseBool(exMaxValue.Value) - s.ExclusiveMaximum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ - KeyNode: exMaxLabel, - ValueNode: exMaxValue, - Value: SchemaDynamicValue[bool, int64]{N: 0, A: val}, - } - } - if utils.IsNodeIntValue(exMaxValue) { - val, _ := strconv.ParseInt(exMaxValue.Value, 10, 64) - s.ExclusiveMaximum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ - KeyNode: exMaxLabel, - ValueNode: exMaxValue, - Value: SchemaDynamicValue[bool, int64]{N: 1, B: val}, - } - } - } + // determine exclusive maximum type, bool (3.0) or int (3.1) + _, exMaxLabel, exMaxValue := utils.FindKeyNodeFull(ExclusiveMaximumLabel, root.Content) + if exMaxValue != nil { + if utils.IsNodeBoolValue(exMaxValue) { + val, _ := strconv.ParseBool(exMaxValue.Value) + s.ExclusiveMaximum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ + KeyNode: exMaxLabel, + ValueNode: exMaxValue, + Value: SchemaDynamicValue[bool, int64]{N: 0, A: val}, + } + } + if utils.IsNodeIntValue(exMaxValue) { + val, _ := strconv.ParseInt(exMaxValue.Value, 10, 64) + s.ExclusiveMaximum = low.NodeReference[SchemaDynamicValue[bool, int64]]{ + KeyNode: exMaxLabel, + ValueNode: exMaxValue, + Value: SchemaDynamicValue[bool, int64]{N: 1, B: val}, + } + } + } - // handle schema reference type if set. (3.1) - _, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFull(SchemaTypeLabel, root.Content) - if schemaRefNode != nil { - s.SchemaTypeRef = low.NodeReference[string]{ - Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefLabel} - } + // handle schema reference type if set. (3.1) + _, schemaRefLabel, schemaRefNode := utils.FindKeyNodeFull(SchemaTypeLabel, root.Content) + if schemaRefNode != nil { + s.SchemaTypeRef = low.NodeReference[string]{ + Value: schemaRefNode.Value, KeyNode: schemaRefLabel, ValueNode: schemaRefLabel} + } - // handle example if set. (3.0) - _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) - if expNode != nil { - s.Example = low.NodeReference[any]{Value: ExtractExampleValue(expNode), KeyNode: expLabel, ValueNode: expNode} - } + // handle example if set. (3.0) + _, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content) + if expNode != nil { + s.Example = low.NodeReference[any]{Value: ExtractExampleValue(expNode), KeyNode: expLabel, ValueNode: expNode} + } - // handle examples if set.(3.1) - _, expArrLabel, expArrNode := utils.FindKeyNodeFull(ExamplesLabel, root.Content) - if expArrNode != nil { - if utils.IsNodeArray(expArrNode) { - var examples []low.ValueReference[any] - for i := range expArrNode.Content { - examples = append(examples, low.ValueReference[any]{Value: ExtractExampleValue(expArrNode.Content[i]), ValueNode: expArrNode.Content[i]}) - } - s.Examples = low.NodeReference[[]low.ValueReference[any]]{ - Value: examples, - ValueNode: expArrNode, - KeyNode: expArrLabel, - } - } - } + // handle examples if set.(3.1) + _, expArrLabel, expArrNode := utils.FindKeyNodeFull(ExamplesLabel, root.Content) + if expArrNode != nil { + if utils.IsNodeArray(expArrNode) { + var examples []low.ValueReference[any] + for i := range expArrNode.Content { + examples = append(examples, low.ValueReference[any]{Value: ExtractExampleValue(expArrNode.Content[i]), ValueNode: expArrNode.Content[i]}) + } + s.Examples = low.NodeReference[[]low.ValueReference[any]]{ + Value: examples, + ValueNode: expArrNode, + KeyNode: expArrLabel, + } + } + } - _, addPLabel, addPNode := utils.FindKeyNodeFull(AdditionalPropertiesLabel, root.Content) - if addPNode != nil { - if utils.IsNodeMap(addPNode) { - schema, serr := low.ExtractObjectRaw[*Schema](addPNode, idx) - if serr != nil { - return serr - } - s.AdditionalProperties = low.NodeReference[any]{Value: schema, KeyNode: addPLabel, ValueNode: addPNode} - } + _, addPLabel, addPNode := utils.FindKeyNodeFull(AdditionalPropertiesLabel, root.Content) + if addPNode != nil { + if utils.IsNodeMap(addPNode) { + schema, serr := low.ExtractObjectRaw[*Schema](addPNode, idx) + if serr != nil { + return serr + } + s.AdditionalProperties = low.NodeReference[any]{Value: schema, KeyNode: addPLabel, ValueNode: addPNode} + } - if utils.IsNodeBoolValue(addPNode) { - b, _ := strconv.ParseBool(addPNode.Value) - s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode} - } - } + if utils.IsNodeBoolValue(addPNode) { + b, _ := strconv.ParseBool(addPNode.Value) + s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode} + } + } - // handle discriminator if set. - _, discLabel, discNode := utils.FindKeyNodeFull(DiscriminatorLabel, root.Content) - if discNode != nil { - var discriminator Discriminator - _ = low.BuildModel(discNode, &discriminator) - s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} - } + // handle discriminator if set. + _, discLabel, discNode := utils.FindKeyNodeFull(DiscriminatorLabel, root.Content) + if discNode != nil { + var discriminator Discriminator + _ = low.BuildModel(discNode, &discriminator) + s.Discriminator = low.NodeReference[*Discriminator]{Value: &discriminator, KeyNode: discLabel, ValueNode: discNode} + } - // handle externalDocs if set. - _, extDocLabel, extDocNode := utils.FindKeyNodeFull(ExternalDocsLabel, root.Content) - if extDocNode != nil { - var exDoc ExternalDoc - _ = low.BuildModel(extDocNode, &exDoc) - _ = exDoc.Build(extDocNode, idx) // throws no errors, can't check for one. - s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode} - } + // handle externalDocs if set. + _, extDocLabel, extDocNode := utils.FindKeyNodeFull(ExternalDocsLabel, root.Content) + if extDocNode != nil { + var exDoc ExternalDoc + _ = low.BuildModel(extDocNode, &exDoc) + _ = exDoc.Build(extDocNode, idx) // throws no errors, can't check for one. + s.ExternalDocs = low.NodeReference[*ExternalDoc]{Value: &exDoc, KeyNode: extDocLabel, ValueNode: extDocNode} + } - // handle xml if set. - _, xmlLabel, xmlNode := utils.FindKeyNodeFull(XMLLabel, root.Content) - if xmlNode != nil { - var xml XML - _ = low.BuildModel(xmlNode, &xml) - // extract extensions if set. - _ = xml.Build(xmlNode) // returns no errors, can't check for one. - s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode} - } + // handle xml if set. + _, xmlLabel, xmlNode := utils.FindKeyNodeFull(XMLLabel, root.Content) + if xmlNode != nil { + var xml XML + _ = low.BuildModel(xmlNode, &xml) + // extract extensions if set. + _ = xml.Build(xmlNode, idx) // returns no errors, can't check for one. + s.XML = low.NodeReference[*XML]{Value: &xml, KeyNode: xmlLabel, ValueNode: xmlNode} + } - // for property, build in a new thread! - bChan := make(chan schemaProxyBuildResult) + // for property, build in a new thread! + bChan := make(chan schemaProxyBuildResult) - var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult) { - c <- schemaProxyBuildResult{ - k: low.KeyReference[string]{ - KeyNode: label, - Value: label.Value, - }, - v: low.ValueReference[*SchemaProxy]{ - Value: &SchemaProxy{kn: label, vn: value, idx: idx}, - ValueNode: value, - }, - } - } + var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult) { + c <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: label, + Value: label.Value, + }, + v: low.ValueReference[*SchemaProxy]{ + Value: &SchemaProxy{kn: label, vn: value, idx: idx}, + ValueNode: value, + }, + } + } - // handle properties - _, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content) - if propsNode != nil { - propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]) - var currentProp *yaml.Node - totalProps := 0 - for i, prop := range propsNode.Content { - if i%2 == 0 { - currentProp = prop - continue - } + // handle properties + _, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content) + if propsNode != nil { + propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]) + var currentProp *yaml.Node + totalProps := 0 + for i, prop := range propsNode.Content { + if i%2 == 0 { + currentProp = prop + continue + } - // check our prop isn't reference - if h, _, _ := utils.IsNodeRefValue(prop); h { - ref, _ := low.LocateRefNode(prop, idx) - if ref != nil { - prop = ref - } else { - return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", - prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line) - } - } - totalProps++ - go buildProperty(currentProp, prop, bChan) - } - completedProps := 0 - for completedProps < totalProps { - select { - case res := <-bChan: - completedProps++ - propertyMap[res.k] = res.v - } - } - s.Properties = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]{ - Value: propertyMap, - KeyNode: propLabel, - ValueNode: propsNode, - } - } + // check our prop isn't reference + if h, _, _ := utils.IsNodeRefValue(prop); h { + ref, _ := low.LocateRefNode(prop, idx) + if ref != nil { + prop = ref + } else { + return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", + prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line) + } + } + totalProps++ + go buildProperty(currentProp, prop, bChan) + } + completedProps := 0 + for completedProps < totalProps { + select { + case res := <-bChan: + completedProps++ + propertyMap[res.k] = res.v + } + } + s.Properties = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]{ + Value: propertyMap, + KeyNode: propLabel, + ValueNode: propsNode, + } + } - var allOf, anyOf, oneOf, not, items []low.ValueReference[*SchemaProxy] + var allOf, anyOf, oneOf, not, items []low.ValueReference[*SchemaProxy] - _, allOfLabel, allOfValue := utils.FindKeyNodeFull(AllOfLabel, root.Content) - _, anyOfLabel, anyOfValue := utils.FindKeyNodeFull(AnyOfLabel, root.Content) - _, oneOfLabel, oneOfValue := utils.FindKeyNodeFull(OneOfLabel, root.Content) - _, notLabel, notValue := utils.FindKeyNodeFull(NotLabel, root.Content) - _, itemsLabel, itemsValue := utils.FindKeyNodeFull(ItemsLabel, root.Content) + _, allOfLabel, allOfValue := utils.FindKeyNodeFull(AllOfLabel, root.Content) + _, anyOfLabel, anyOfValue := utils.FindKeyNodeFull(AnyOfLabel, root.Content) + _, oneOfLabel, oneOfValue := utils.FindKeyNodeFull(OneOfLabel, root.Content) + _, notLabel, notValue := utils.FindKeyNodeFull(NotLabel, root.Content) + _, itemsLabel, itemsValue := utils.FindKeyNodeFull(ItemsLabel, root.Content) - errorChan := make(chan error) - allOfChan := make(chan schemaProxyBuildResult) - anyOfChan := make(chan schemaProxyBuildResult) - oneOfChan := make(chan schemaProxyBuildResult) - itemsChan := make(chan schemaProxyBuildResult) - notChan := make(chan schemaProxyBuildResult) + errorChan := make(chan error) + allOfChan := make(chan schemaProxyBuildResult) + anyOfChan := make(chan schemaProxyBuildResult) + oneOfChan := make(chan schemaProxyBuildResult) + itemsChan := make(chan schemaProxyBuildResult) + notChan := make(chan schemaProxyBuildResult) - totalBuilds := countSubSchemaItems(allOfValue) + - countSubSchemaItems(anyOfValue) + - countSubSchemaItems(oneOfValue) + - countSubSchemaItems(notValue) + - countSubSchemaItems(itemsValue) + totalBuilds := countSubSchemaItems(allOfValue) + + countSubSchemaItems(anyOfValue) + + countSubSchemaItems(oneOfValue) + + countSubSchemaItems(notValue) + + countSubSchemaItems(itemsValue) - if allOfValue != nil { - go buildSchema(allOfChan, allOfLabel, allOfValue, errorChan, idx) - } - if anyOfValue != nil { - go buildSchema(anyOfChan, anyOfLabel, anyOfValue, errorChan, idx) - } - if oneOfValue != nil { - go buildSchema(oneOfChan, oneOfLabel, oneOfValue, errorChan, idx) - } - if itemsValue != nil { - go buildSchema(itemsChan, itemsLabel, itemsValue, errorChan, idx) - } - if notValue != nil { - go buildSchema(notChan, notLabel, notValue, errorChan, idx) - } + if allOfValue != nil { + go buildSchema(allOfChan, allOfLabel, allOfValue, errorChan, idx) + } + if anyOfValue != nil { + go buildSchema(anyOfChan, anyOfLabel, anyOfValue, errorChan, idx) + } + if oneOfValue != nil { + go buildSchema(oneOfChan, oneOfLabel, oneOfValue, errorChan, idx) + } + if itemsValue != nil { + go buildSchema(itemsChan, itemsLabel, itemsValue, errorChan, idx) + } + if notValue != nil { + go buildSchema(notChan, notLabel, notValue, errorChan, idx) + } - completeCount := 0 - for completeCount < totalBuilds { - select { - case e := <-errorChan: - return e - case r := <-allOfChan: - completeCount++ - allOf = append(allOf, r.v) - case r := <-anyOfChan: - completeCount++ - anyOf = append(anyOf, r.v) - case r := <-oneOfChan: - completeCount++ - oneOf = append(oneOf, r.v) - case r := <-itemsChan: - completeCount++ - items = append(items, r.v) - case r := <-notChan: - completeCount++ - not = append(not, r.v) - } - } + completeCount := 0 + for completeCount < totalBuilds { + select { + case e := <-errorChan: + return e + case r := <-allOfChan: + completeCount++ + allOf = append(allOf, r.v) + case r := <-anyOfChan: + completeCount++ + anyOf = append(anyOf, r.v) + case r := <-oneOfChan: + completeCount++ + oneOf = append(oneOf, r.v) + case r := <-itemsChan: + completeCount++ + items = append(items, r.v) + case r := <-notChan: + completeCount++ + not = append(not, r.v) + } + } - if len(anyOf) > 0 { - s.AnyOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: anyOf, - KeyNode: anyOfLabel, - ValueNode: anyOfValue, - } - } - if len(oneOf) > 0 { - s.OneOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: oneOf, - KeyNode: oneOfLabel, - ValueNode: oneOfValue, - } - } - if len(allOf) > 0 { - s.AllOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: allOf, - KeyNode: allOfLabel, - ValueNode: allOfValue, - } - } - if len(not) > 0 { - s.Not = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: not, - KeyNode: notLabel, - ValueNode: notValue, - } + if len(anyOf) > 0 { + s.AnyOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: anyOf, + KeyNode: anyOfLabel, + ValueNode: anyOfValue, + } + } + if len(oneOf) > 0 { + s.OneOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: oneOf, + KeyNode: oneOfLabel, + ValueNode: oneOfValue, + } + } + if len(allOf) > 0 { + s.AllOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: allOf, + KeyNode: allOfLabel, + ValueNode: allOfValue, + } + } + if len(not) > 0 { + s.Not = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: not, + KeyNode: notLabel, + ValueNode: notValue, + } - } - if len(items) > 0 { - s.Items = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ - Value: items, - KeyNode: itemsLabel, - ValueNode: itemsValue, - } - } - return nil + } + if len(items) > 0 { + s.Items = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{ + Value: items, + KeyNode: itemsLabel, + ValueNode: itemsValue, + } + } + return nil } func countSubSchemaItems(node *yaml.Node) int { - if utils.IsNodeMap(node) { - return 1 - } - if utils.IsNodeArray(node) { - return len(node.Content) - } - return 0 + if utils.IsNodeMap(node) { + return 1 + } + if utils.IsNodeArray(node) { + return len(node.Content) + } + return 0 } type schemaProxyBuildResult struct { - k low.KeyReference[string] - v low.ValueReference[*SchemaProxy] + k low.KeyReference[string] + v low.ValueReference[*SchemaProxy] } func (s *Schema) extractExtensions(root *yaml.Node) { - s.Extensions = low.ExtractExtensions(root) + s.Extensions = low.ExtractExtensions(root) } func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) { - if valueNode != nil { - syncChan := make(chan *low.ValueReference[*SchemaProxy]) + if valueNode != nil { + syncChan := make(chan *low.ValueReference[*SchemaProxy]) - // build out a SchemaProxy for every sub-schema. - build := func(kn *yaml.Node, vn *yaml.Node, c chan *low.ValueReference[*SchemaProxy]) { - // a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can - // take on circular references through polymorphism. Like the resolver, if we try and follow these - // journey's through hyperspace, we will end up creating endless amounts of threads, spinning off - // chasing down circles, that in turn spin up endless threads. - // In order to combat this, we need a schema proxy that will only resolve the schema when asked, and then - // it will only do it one level at a time. - sp := new(SchemaProxy) - sp.kn = kn - sp.vn = vn - sp.idx = idx + // build out a SchemaProxy for every sub-schema. + build := func(kn *yaml.Node, vn *yaml.Node, c chan *low.ValueReference[*SchemaProxy]) { + // a proxy design works best here. polymorphism, pretty much guarantees that a sub-schema can + // take on circular references through polymorphism. Like the resolver, if we try and follow these + // journey's through hyperspace, we will end up creating endless amounts of threads, spinning off + // chasing down circles, that in turn spin up endless threads. + // In order to combat this, we need a schema proxy that will only resolve the schema when asked, and then + // it will only do it one level at a time. + sp := new(SchemaProxy) + sp.kn = kn + sp.vn = vn + sp.idx = idx - res := &low.ValueReference[*SchemaProxy]{ - Value: sp, - ValueNode: vn, - } - c <- res - } + res := &low.ValueReference[*SchemaProxy]{ + Value: sp, + ValueNode: vn, + } + c <- res + } - if utils.IsNodeMap(valueNode) { - if h, _, _ := utils.IsNodeRefValue(valueNode); h { - ref, _ := low.LocateRefNode(valueNode, idx) - if ref != nil { - valueNode = ref - } else { - errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", - valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column) - } - } + if utils.IsNodeMap(valueNode) { + if h, _, _ := utils.IsNodeRefValue(valueNode); h { + ref, _ := low.LocateRefNode(valueNode, idx) + if ref != nil { + valueNode = ref + } else { + errors <- fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", + valueNode.Content[1].Value, valueNode.Content[1].Line, valueNode.Content[1].Column) + } + } - // this only runs once, however to keep things consistent, it makes sense to use the same async method - // that arrays will use. - go build(labelNode, valueNode, syncChan) - select { - case r := <-syncChan: - schemas <- schemaProxyBuildResult{ - k: low.KeyReference[string]{ - KeyNode: labelNode, - Value: labelNode.Value, - }, - v: *r, - } - } - } - if utils.IsNodeArray(valueNode) { - refBuilds := 0 - for _, vn := range valueNode.Content { - if h, _, _ := utils.IsNodeRefValue(vn); h { - ref, _ := low.LocateRefNode(vn, idx) - if ref != nil { - vn = ref - } else { - err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", - vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column) - errors <- err - return - } - } - refBuilds++ - go build(vn, vn, syncChan) - } - completedBuilds := 0 - for completedBuilds < refBuilds { - select { - case res := <-syncChan: - completedBuilds++ - schemas <- schemaProxyBuildResult{ - k: low.KeyReference[string]{ - KeyNode: labelNode, - Value: labelNode.Value, - }, - v: *res, - } - } - } - } + // this only runs once, however to keep things consistent, it makes sense to use the same async method + // that arrays will use. + go build(labelNode, valueNode, syncChan) + select { + case r := <-syncChan: + schemas <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: labelNode, + Value: labelNode.Value, + }, + v: *r, + } + } + } + if utils.IsNodeArray(valueNode) { + refBuilds := 0 + for _, vn := range valueNode.Content { + if h, _, _ := utils.IsNodeRefValue(vn); h { + ref, _ := low.LocateRefNode(vn, idx) + if ref != nil { + vn = ref + } else { + err := fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d", + vn.Content[1].Value, vn.Content[1].Line, vn.Content[1].Column) + errors <- err + return + } + } + refBuilds++ + go build(vn, vn, syncChan) + } + completedBuilds := 0 + for completedBuilds < refBuilds { + select { + case res := <-syncChan: + completedBuilds++ + schemas <- schemaProxyBuildResult{ + k: low.KeyReference[string]{ + KeyNode: labelNode, + Value: labelNode.Value, + }, + v: *res, + } + } + } + } - } + } } func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*SchemaProxy], error) { - var schLabel, schNode *yaml.Node - errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d" - if rf, rl, _ := utils.IsNodeRefValue(root); rf { - // locate reference in index. - ref, _ := low.LocateRefNode(root, idx) - if ref != nil { - schNode = ref - schLabel = rl - } else { - return nil, fmt.Errorf(errStr, - root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) - } - } else { - _, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content) - if schNode != nil { - if h, _, _ := utils.IsNodeRefValue(schNode); h { - ref, _ := low.LocateRefNode(schNode, idx) - if ref != nil { - schNode = ref - } else { - return nil, fmt.Errorf(errStr, - schNode.Content[1].Value, schNode.Content[1].Line, schNode.Content[1].Column) - } - } - } - } + var schLabel, schNode *yaml.Node + errStr := "schema build failed: reference '%s' cannot be found at line %d, col %d" + if rf, rl, _ := utils.IsNodeRefValue(root); rf { + // locate reference in index. + ref, _ := low.LocateRefNode(root, idx) + if ref != nil { + schNode = ref + schLabel = rl + } else { + return nil, fmt.Errorf(errStr, + root.Content[1].Value, root.Content[1].Line, root.Content[1].Column) + } + } else { + _, schLabel, schNode = utils.FindKeyNodeFull(SchemaLabel, root.Content) + if schNode != nil { + if h, _, _ := utils.IsNodeRefValue(schNode); h { + ref, _ := low.LocateRefNode(schNode, idx) + if ref != nil { + schNode = ref + } else { + return nil, fmt.Errorf(errStr, + schNode.Content[1].Value, schNode.Content[1].Line, schNode.Content[1].Column) + } + } + } + } - if schNode != nil { - // check if schema has already been built. - schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx} - return &low.NodeReference[*SchemaProxy]{Value: schema, KeyNode: schLabel, ValueNode: schNode}, nil - } - return nil, nil + if schNode != nil { + // check if schema has already been built. + schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx} + return &low.NodeReference[*SchemaProxy]{Value: schema, KeyNode: schLabel, ValueNode: schNode}, nil + } + return nil, nil } diff --git a/datamodel/low/base/xml.go b/datamodel/low/base/xml.go index 68f0b5d..a57755d 100644 --- a/datamodel/low/base/xml.go +++ b/datamodel/low/base/xml.go @@ -1,20 +1,21 @@ package base import ( - "github.com/pb33f/libopenapi/datamodel/low" - "gopkg.in/yaml.v3" + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/index" + "gopkg.in/yaml.v3" ) type XML struct { - Name low.NodeReference[string] - Namespace low.NodeReference[string] - Prefix low.NodeReference[string] - Attribute low.NodeReference[bool] - Wrapped low.NodeReference[bool] - Extensions map[low.KeyReference[string]]low.ValueReference[any] + Name low.NodeReference[string] + Namespace low.NodeReference[string] + Prefix low.NodeReference[string] + Attribute low.NodeReference[bool] + Wrapped low.NodeReference[bool] + Extensions map[low.KeyReference[string]]low.ValueReference[any] } -func (x *XML) Build(root *yaml.Node) error { - x.Extensions = low.ExtractExtensions(root) - return nil +func (x *XML) Build(root *yaml.Node, _ *index.SpecIndex) error { + x.Extensions = low.ExtractExtensions(root) + return nil }