mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
Massive refactor on how the library handles schemas.
Schemas are now rendered on demand. There is no reasonable way to navigate the mayhem that is circular dependencies through multiple inheritance and polymorphism. So now using a msuch simpler design (and MUCH faster), there is a `SchemaProxy` for every schema reference. This holds a reference to the low model and index, that renders the schema on demand. Once rendered, it's done. Any children can also be rendered on demand, and so down the rabbit hole you do (if you want). All circular dependencies are know by the index, so you can decide when you want to stop, or just keep going for ever, however it's now a choice, not something decided for you. Signed-off-by: Dave Shanley <dave@quobix.com>
This commit is contained in:
@@ -47,7 +47,7 @@ func getSeenSchema(key string) *Schema {
|
||||
}
|
||||
|
||||
type Components struct {
|
||||
Schemas map[string]*Schema
|
||||
Schemas map[string]*SchemaProxy
|
||||
Responses map[string]*Response
|
||||
Parameters map[string]*Parameter
|
||||
Examples map[string]*Example
|
||||
@@ -72,8 +72,8 @@ func NewComponents(comp *low.Components) *Components {
|
||||
requestBodyMap := make(map[string]*RequestBody)
|
||||
headerMap := make(map[string]*Header)
|
||||
securitySchemeMap := make(map[string]*SecurityScheme)
|
||||
schemas := make(map[string]*Schema)
|
||||
schemaChan := make(chan componentResult[*Schema])
|
||||
schemas := make(map[string]*SchemaProxy)
|
||||
schemaChan := make(chan componentResult[*SchemaProxy])
|
||||
cbChan := make(chan componentResult[*Callback])
|
||||
linkChan := make(chan componentResult[*Link])
|
||||
responseChan := make(chan componentResult[*Response])
|
||||
@@ -173,15 +173,13 @@ func buildComponent[N any, O any](comp int, key string, orig O, c chan component
|
||||
c <- componentResult[N]{comp: comp, res: f(orig), key: key}
|
||||
}
|
||||
|
||||
func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference[*low.Schema], c chan componentResult[*Schema]) {
|
||||
var sch *Schema
|
||||
if ss := getSeenSchema(orig.GenerateMapKey()); ss != nil {
|
||||
sch = ss
|
||||
} else {
|
||||
sch = NewSchema(orig.Value)
|
||||
addSeenSchema(orig.GenerateMapKey(), sch)
|
||||
}
|
||||
c <- componentResult[*Schema]{res: sch, key: key.Value}
|
||||
func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference[*low.SchemaProxy], c chan componentResult[*SchemaProxy]) {
|
||||
var sch *SchemaProxy
|
||||
sch = &SchemaProxy{schema: &lowmodel.NodeReference[*low.SchemaProxy]{
|
||||
Value: orig.Value,
|
||||
ValueNode: orig.ValueNode,
|
||||
}}
|
||||
c <- componentResult[*SchemaProxy]{res: sch, key: key.Value}
|
||||
}
|
||||
|
||||
func (c *Components) GoLow() *low.Components {
|
||||
|
||||
@@ -162,34 +162,35 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
|
||||
goLow := h.Components.GoLow()
|
||||
|
||||
a := h.Components.Schemas["Error"]
|
||||
assert.Equal(t, "No such burger as 'Big-Whopper'", a.Properties["message"].Example)
|
||||
abcd := a.Schema().Properties["message"].Schema().Example
|
||||
assert.Equal(t, "No such burger as 'Big-Whopper'", abcd)
|
||||
assert.Equal(t, 428, goLow.Schemas.KeyNode.Line)
|
||||
assert.Equal(t, 3, goLow.Schemas.KeyNode.Column)
|
||||
assert.Equal(t, 431, a.GoLow().Description.KeyNode.Line)
|
||||
assert.Equal(t, 431, a.Schema().GoLow().Description.KeyNode.Line)
|
||||
|
||||
b := h.Components.Schemas["Burger"]
|
||||
assert.Len(t, b.Required, 2)
|
||||
assert.Equal(t, "golden slices of happy fun joy", b.Properties["fries"].Description)
|
||||
assert.Equal(t, int64(2), b.Properties["numPatties"].Example)
|
||||
assert.Equal(t, 443, goLow.FindSchema("Burger").Value.Properties.KeyNode.Line)
|
||||
assert.Equal(t, 7, goLow.FindSchema("Burger").Value.Properties.KeyNode.Column)
|
||||
assert.Equal(t, 445, b.GoLow().FindProperty("name").ValueNode.Line)
|
||||
assert.Len(t, b.Schema().Required, 2)
|
||||
assert.Equal(t, "golden slices of happy fun joy", b.Schema().Properties["fries"].Schema().Description)
|
||||
assert.Equal(t, int64(2), b.Schema().Properties["numPatties"].Schema().Example)
|
||||
assert.Equal(t, 443, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Line)
|
||||
assert.Equal(t, 7, goLow.FindSchema("Burger").Value.Schema().Properties.KeyNode.Column)
|
||||
assert.Equal(t, 445, b.Schema().GoLow().FindProperty("name").ValueNode.Line)
|
||||
|
||||
f := h.Components.Schemas["Fries"]
|
||||
assert.Equal(t, "salt", f.Properties["seasoning"].Items[0].Example)
|
||||
assert.Len(t, f.Properties["favoriteDrink"].Properties["drinkType"].Enum, 2)
|
||||
assert.Equal(t, "salt", f.Schema().Properties["seasoning"].Schema().Items[0].Schema().Example)
|
||||
assert.Len(t, f.Schema().Properties["favoriteDrink"].Schema().Properties["drinkType"].Schema().Enum, 2)
|
||||
|
||||
d := h.Components.Schemas["Drink"]
|
||||
assert.Len(t, d.Required, 2)
|
||||
assert.True(t, d.AdditionalProperties.(bool))
|
||||
assert.Equal(t, "drinkType", d.Discriminator.PropertyName)
|
||||
assert.Equal(t, "some value", d.Discriminator.Mapping["drink"])
|
||||
assert.Equal(t, 511, d.Discriminator.GoLow().PropertyName.ValueNode.Line)
|
||||
assert.Equal(t, 23, d.Discriminator.GoLow().PropertyName.ValueNode.Column)
|
||||
assert.Len(t, d.Schema().Required, 2)
|
||||
assert.True(t, d.Schema().AdditionalProperties.(bool))
|
||||
assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName)
|
||||
assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"])
|
||||
assert.Equal(t, 511, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line)
|
||||
assert.Equal(t, 23, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Column)
|
||||
|
||||
pl := h.Components.Schemas["SomePayload"]
|
||||
assert.Equal(t, "is html programming? yes.", pl.XML.Name)
|
||||
assert.Equal(t, 518, pl.XML.GoLow().Name.ValueNode.Line)
|
||||
assert.Equal(t, "is html programming? yes.", pl.Schema().XML.Name)
|
||||
assert.Equal(t, 518, pl.Schema().XML.GoLow().Name.ValueNode.Line)
|
||||
|
||||
ext := h.Components.Extensions
|
||||
assert.Equal(t, "loud", ext["x-screaming-baby"])
|
||||
@@ -225,7 +226,7 @@ func TestNewDocument_Components_Responses(t *testing.T) {
|
||||
h := NewDocument(doc)
|
||||
assert.Len(t, h.Components.Responses, 1)
|
||||
assert.Equal(t, "all the dressings for a burger.", h.Components.Responses["DressingResponse"].Description)
|
||||
assert.Equal(t, "array", h.Components.Responses["DressingResponse"].Content["application/json"].Schema.Type)
|
||||
assert.Equal(t, "array", h.Components.Responses["DressingResponse"].Content["application/json"].Schema.Schema().Type)
|
||||
assert.Equal(t, 347, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Line)
|
||||
assert.Equal(t, 7, h.Components.Responses["DressingResponse"].GoLow().Description.KeyNode.Column)
|
||||
}
|
||||
@@ -266,12 +267,12 @@ func TestNewDocument_Components_Parameters(t *testing.T) {
|
||||
bh := h.Components.Parameters["BurgerHeader"]
|
||||
assert.Equal(t, "burgerHeader", bh.Name)
|
||||
assert.Equal(t, 387, bh.GoLow().Name.KeyNode.Line)
|
||||
assert.Len(t, bh.Schema.Properties, 2)
|
||||
assert.Len(t, bh.Schema.Schema().Properties, 2)
|
||||
assert.Equal(t, "big-mac", bh.Example)
|
||||
assert.True(t, bh.Required)
|
||||
assert.Equal(t, "this is a header",
|
||||
bh.Content["application/json"].Encoding["burgerTheme"].Headers["someHeader"].Description)
|
||||
assert.Len(t, bh.Content["application/json"].Schema.Properties, 2)
|
||||
assert.Len(t, bh.Content["application/json"].Schema.Schema().Properties, 2)
|
||||
assert.Equal(t, 404, bh.Content["application/json"].Encoding["burgerTheme"].GoLow().ContentType.ValueNode.Line)
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ type Header struct {
|
||||
Style string
|
||||
Explode bool
|
||||
AllowReserved bool
|
||||
Schema *Schema
|
||||
Schema *SchemaProxy
|
||||
Example any
|
||||
Examples map[string]*Example
|
||||
Content map[string]*MediaType
|
||||
@@ -35,13 +35,11 @@ func NewHeader(header *low.Header) *Header {
|
||||
h.Explode = header.Explode.Value
|
||||
h.AllowReserved = header.AllowReserved.Value
|
||||
if !header.Schema.IsEmpty() {
|
||||
// check if schema has been seen or not.
|
||||
if v := getSeenSchema(header.Schema.GenerateMapKey()); v != nil {
|
||||
h.Schema = v
|
||||
} else {
|
||||
h.Schema = NewSchema(header.Schema.Value)
|
||||
addSeenSchema(header.Schema.GenerateMapKey(), h.Schema)
|
||||
}
|
||||
h.Schema = &SchemaProxy{schema: &lowmodel.NodeReference[*low.SchemaProxy]{
|
||||
Value: header.Schema.Value,
|
||||
KeyNode: header.Schema.KeyNode,
|
||||
ValueNode: header.Schema.ValueNode,
|
||||
}}
|
||||
}
|
||||
h.Content = ExtractContent(header.Content.Value)
|
||||
h.Example = header.Example.Value
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
type MediaType struct {
|
||||
Schema *Schema
|
||||
Schema *SchemaProxy
|
||||
Example any
|
||||
Examples map[string]*Example
|
||||
Encoding map[string]*Encoding
|
||||
@@ -22,13 +22,7 @@ func NewMediaType(mediaType *low.MediaType) *MediaType {
|
||||
m := new(MediaType)
|
||||
m.low = mediaType
|
||||
if !mediaType.Schema.IsEmpty() {
|
||||
// check if schema has been seen or not.
|
||||
if v := getSeenSchema(mediaType.Schema.GenerateMapKey()); v != nil {
|
||||
m.Schema = v
|
||||
} else {
|
||||
m.Schema = NewSchema(mediaType.Schema.Value)
|
||||
addSeenSchema(mediaType.Schema.GenerateMapKey(), m.Schema)
|
||||
}
|
||||
m.Schema = &SchemaProxy{schema: &mediaType.Schema}
|
||||
}
|
||||
m.Example = mediaType.Example
|
||||
m.Examples = ExtractExamples(mediaType.Examples.Value)
|
||||
|
||||
@@ -18,7 +18,7 @@ type Parameter struct {
|
||||
Style string
|
||||
Explode bool
|
||||
AllowReserved bool
|
||||
Schema *Schema
|
||||
Schema *SchemaProxy
|
||||
Example any
|
||||
Examples map[string]*Example
|
||||
Content map[string]*MediaType
|
||||
@@ -37,14 +37,8 @@ func NewParameter(param *low.Parameter) *Parameter {
|
||||
p.Style = param.Style.Value
|
||||
p.Explode = param.Explode.Value
|
||||
p.AllowReserved = param.AllowReserved.Value
|
||||
|
||||
if !param.Schema.IsEmpty() {
|
||||
if v := getSeenSchema(param.Schema.GenerateMapKey()); v != nil {
|
||||
p.Schema = v
|
||||
} else {
|
||||
p.Schema = NewSchema(param.Schema.Value)
|
||||
addSeenSchema(param.Schema.GenerateMapKey(), p.Schema)
|
||||
}
|
||||
p.Schema = &SchemaProxy{schema: ¶m.Schema}
|
||||
}
|
||||
p.Required = param.Required.Value
|
||||
p.Example = param.Example.Value
|
||||
|
||||
@@ -29,12 +29,12 @@ type Schema struct {
|
||||
Required []string
|
||||
Enum []string
|
||||
Type string
|
||||
AllOf []*Schema
|
||||
OneOf []*Schema
|
||||
AnyOf []*Schema
|
||||
Not []*Schema
|
||||
Items []*Schema
|
||||
Properties map[string]*Schema
|
||||
AllOf []*SchemaProxy
|
||||
OneOf []*SchemaProxy
|
||||
AnyOf []*SchemaProxy
|
||||
Not []*SchemaProxy
|
||||
Items []*SchemaProxy
|
||||
Properties map[string]*SchemaProxy
|
||||
AdditionalProperties any
|
||||
Description string
|
||||
Default any
|
||||
@@ -105,28 +105,32 @@ func NewSchema(schema *low.Schema) *Schema {
|
||||
|
||||
polyCompletedChan := make(chan bool)
|
||||
propsChan := make(chan bool)
|
||||
errChan := make(chan error)
|
||||
|
||||
// schema async
|
||||
buildOutSchema := func(schemas []lowmodel.NodeReference[*low.Schema], items *[]*Schema, doneChan chan bool) {
|
||||
bChan := make(chan *Schema)
|
||||
buildOutSchema := func(schemas []lowmodel.ValueReference[*low.SchemaProxy], items *[]*SchemaProxy,
|
||||
doneChan chan bool, e chan error) {
|
||||
bChan := make(chan *SchemaProxy)
|
||||
eChan := make(chan error)
|
||||
|
||||
// for every item, build schema async
|
||||
buildSchemaChild := func(sch lowmodel.NodeReference[*low.Schema], bChan chan *Schema) {
|
||||
if ss := getSeenSchema(sch.GenerateMapKey()); ss != nil {
|
||||
bChan <- ss
|
||||
return
|
||||
}
|
||||
ns := NewSchema(sch.Value)
|
||||
addSeenSchema(sch.GenerateMapKey(), ns)
|
||||
bChan <- ns
|
||||
buildSchemaChild := func(sch lowmodel.ValueReference[*low.SchemaProxy], bChan chan *SchemaProxy, e chan error) {
|
||||
p := &SchemaProxy{schema: &lowmodel.NodeReference[*low.SchemaProxy]{
|
||||
ValueNode: sch.ValueNode,
|
||||
Value: sch.Value,
|
||||
}}
|
||||
bChan <- p
|
||||
}
|
||||
totalSchemas := len(schemas)
|
||||
for v := range schemas {
|
||||
go buildSchemaChild(schemas[v], bChan)
|
||||
go buildSchemaChild(schemas[v], bChan, eChan)
|
||||
}
|
||||
j := 0
|
||||
for j < totalSchemas {
|
||||
select {
|
||||
case er := <-eChan:
|
||||
e <- er
|
||||
return
|
||||
case t := <-bChan:
|
||||
j++
|
||||
*items = append(*items, t)
|
||||
@@ -137,48 +141,45 @@ func NewSchema(schema *low.Schema) *Schema {
|
||||
|
||||
// props async
|
||||
plock := sync.RWMutex{}
|
||||
var buildProps = func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*low.Schema], c chan bool,
|
||||
props map[string]*Schema) {
|
||||
if ss := getSeenSchema(v.GenerateMapKey()); ss != nil {
|
||||
defer plock.Unlock()
|
||||
plock.Lock()
|
||||
props[k.Value] = ss
|
||||
|
||||
} else {
|
||||
defer plock.Unlock()
|
||||
plock.Lock()
|
||||
props[k.Value] = NewSchema(v.Value)
|
||||
addSeenSchema(k.GenerateMapKey(), props[k.Value])
|
||||
var buildProps = func(k lowmodel.KeyReference[string], v lowmodel.ValueReference[*low.SchemaProxy], c chan bool,
|
||||
props map[string]*SchemaProxy) {
|
||||
defer plock.Unlock()
|
||||
plock.Lock()
|
||||
props[k.Value] = &SchemaProxy{schema: &lowmodel.NodeReference[*low.SchemaProxy]{
|
||||
Value: v.Value,
|
||||
KeyNode: k.KeyNode,
|
||||
ValueNode: v.ValueNode,
|
||||
},
|
||||
}
|
||||
s.Properties = props
|
||||
c <- true
|
||||
}
|
||||
|
||||
props := make(map[string]*Schema)
|
||||
props := make(map[string]*SchemaProxy)
|
||||
for k, v := range schema.Properties.Value {
|
||||
go buildProps(k, v, propsChan, props)
|
||||
}
|
||||
|
||||
var allOf []*Schema
|
||||
var oneOf []*Schema
|
||||
var anyOf []*Schema
|
||||
var not []*Schema
|
||||
var items []*Schema
|
||||
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)
|
||||
go buildOutSchema(schema.AllOf.Value, &allOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.AnyOf.IsEmpty() {
|
||||
go buildOutSchema(schema.AnyOf.Value, &anyOf, polyCompletedChan)
|
||||
go buildOutSchema(schema.AnyOf.Value, &anyOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.OneOf.IsEmpty() {
|
||||
go buildOutSchema(schema.OneOf.Value, &oneOf, polyCompletedChan)
|
||||
go buildOutSchema(schema.OneOf.Value, &oneOf, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.Not.IsEmpty() {
|
||||
go buildOutSchema(schema.Not.Value, ¬, polyCompletedChan)
|
||||
go buildOutSchema(schema.Not.Value, ¬, polyCompletedChan, errChan)
|
||||
}
|
||||
if !schema.Items.IsEmpty() {
|
||||
go buildOutSchema(schema.Items.Value, &items, polyCompletedChan)
|
||||
go buildOutSchema(schema.Items.Value, &items, polyCompletedChan, errChan)
|
||||
}
|
||||
|
||||
completeChildren := 0
|
||||
|
||||
27
datamodel/high/3.0/schema_proxy.go
Normal file
27
datamodel/high/3.0/schema_proxy.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
|
||||
)
|
||||
|
||||
type SchemaProxy struct {
|
||||
schema *low.NodeReference[*v3.SchemaProxy]
|
||||
buildError error
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) Schema() *Schema {
|
||||
s := sp.schema.Value.Schema()
|
||||
if s == nil {
|
||||
sp.buildError = sp.GetBuildError()
|
||||
return nil
|
||||
}
|
||||
return NewSchema(s)
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) GetBuildError() error {
|
||||
return sp.buildError
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func getSeenSchema(key string) *Schema {
|
||||
}
|
||||
|
||||
type Components struct {
|
||||
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]]
|
||||
Schemas low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
|
||||
Responses low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]]
|
||||
Parameters low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]]
|
||||
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
|
||||
@@ -60,8 +60,8 @@ func (co *Components) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, co.Extensions)
|
||||
}
|
||||
|
||||
func (co *Components) FindSchema(schema string) *low.ValueReference[*Schema] {
|
||||
return low.FindItemInMap[*Schema](schema, co.Schemas.Value)
|
||||
func (co *Components) FindSchema(schema string) *low.ValueReference[*SchemaProxy] {
|
||||
return low.FindItemInMap[*SchemaProxy](schema, co.Schemas.Value)
|
||||
}
|
||||
|
||||
func (co *Components) FindResponse(response string) *low.ValueReference[*Response] {
|
||||
@@ -103,7 +103,7 @@ func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
skipChan := make(chan bool)
|
||||
errorChan := make(chan error)
|
||||
paramChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Parameter]])
|
||||
schemaChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]])
|
||||
schemaChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]])
|
||||
responsesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Response]])
|
||||
examplesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]])
|
||||
requestBodiesChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*RequestBody]])
|
||||
@@ -112,7 +112,7 @@ func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
linkChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Link]])
|
||||
callbackChan := make(chan low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Callback]])
|
||||
|
||||
go extractComponentValues[*Schema](SchemasLabel, root, skipChan, errorChan, schemaChan, idx)
|
||||
go extractComponentValues[*SchemaProxy](SchemasLabel, root, skipChan, errorChan, schemaChan, idx)
|
||||
go extractComponentValues[*Parameter](ParametersLabel, root, skipChan, errorChan, paramChan, idx)
|
||||
go extractComponentValues[*Response](ResponsesLabel, root, skipChan, errorChan, responsesChan, idx)
|
||||
go extractComponentValues[*Example](ExamplesLabel, root, skipChan, errorChan, examplesChan, idx)
|
||||
@@ -136,7 +136,6 @@ func (co *Components) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
n++
|
||||
case schemas := <-schemaChan:
|
||||
co.Schemas = schemas
|
||||
cacheSchemas(co.Schemas.Value)
|
||||
n++
|
||||
case responses := <-responsesChan:
|
||||
co.Responses = responses
|
||||
@@ -191,6 +190,7 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
|
||||
|
||||
// for every component, build in a new thread!
|
||||
bChan := make(chan componentBuildResult[T])
|
||||
eChan := make(chan error)
|
||||
var buildComponent = func(label *yaml.Node, value *yaml.Node, c chan componentBuildResult[T], ec chan<- error) {
|
||||
var n T = new(N)
|
||||
_ = low.BuildModel(value, n)
|
||||
@@ -221,12 +221,14 @@ func extractComponentValues[T low.Buildable[N], N any](label string, root *yaml.
|
||||
continue
|
||||
}
|
||||
totalComponents++
|
||||
go buildComponent(currentLabel, v, bChan, errorChan)
|
||||
go buildComponent(currentLabel, v, bChan, eChan)
|
||||
}
|
||||
|
||||
completedComponents := 0
|
||||
for completedComponents < totalComponents {
|
||||
select {
|
||||
case e := <-eChan:
|
||||
errorChan <- e
|
||||
case r := <-bChan:
|
||||
componentValues[r.k] = r.v
|
||||
completedComponents++
|
||||
|
||||
@@ -76,8 +76,8 @@ func TestComponents_Build_Success(t *testing.T) {
|
||||
err = n.Build(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "one of many", n.FindSchema("one").Value.Description.Value)
|
||||
assert.Equal(t, "two of many", n.FindSchema("two").Value.Description.Value)
|
||||
assert.Equal(t, "one of many", n.FindSchema("one").Value.Schema().Description.Value)
|
||||
assert.Equal(t, "two of many", n.FindSchema("two").Value.Schema().Description.Value)
|
||||
assert.Equal(t, "three of many", n.FindResponse("three").Value.Description.Value)
|
||||
assert.Equal(t, "four of many", n.FindResponse("four").Value.Description.Value)
|
||||
assert.Equal(t, "five of many", n.FindParameter("five").Value.Description.Value)
|
||||
@@ -92,8 +92,10 @@ func TestComponents_Build_Success(t *testing.T) {
|
||||
assert.Equal(t, "fourteen of many", n.FindSecurityScheme("fourteen").Value.Description.Value)
|
||||
assert.Equal(t, "fifteen of many", n.FindLink("fifteen").Value.Description.Value)
|
||||
assert.Equal(t, "sixteen of many", n.FindLink("sixteen").Value.Description.Value)
|
||||
assert.Equal(t, "seventeen of many", n.FindCallback("seventeen").Value.FindExpression("{reference}").Value.Description.Value)
|
||||
assert.Equal(t, "eighteen of many", n.FindCallback("eighteen").Value.FindExpression("{raference}").Value.Description.Value)
|
||||
assert.Equal(t, "seventeen of many",
|
||||
n.FindCallback("seventeen").Value.FindExpression("{reference}").Value.Description.Value)
|
||||
assert.Equal(t, "eighteen of many",
|
||||
n.FindCallback("eighteen").Value.FindExpression("{raference}").Value.Description.Value)
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) {
|
||||
if er := runFunc(info, doc, idx); er != nil {
|
||||
*ers = append(*ers, er)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}
|
||||
extractionFuncs := []func(i *datamodel.SpecInfo, d *Document, idx *index.SpecIndex) error{
|
||||
@@ -46,15 +45,14 @@ func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) {
|
||||
extractComponents,
|
||||
extractSecurity,
|
||||
extractExternalDocs,
|
||||
extractPaths,
|
||||
}
|
||||
|
||||
wg.Add(len(extractionFuncs))
|
||||
for _, f := range extractionFuncs {
|
||||
go runExtraction(info, &doc, idx, f, &errors, &wg)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
extractPaths(info, &doc, idx)
|
||||
return &doc, errors
|
||||
}
|
||||
|
||||
@@ -62,11 +60,8 @@ func extractInfo(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex)
|
||||
_, ln, vn := utils.FindKeyNodeFull(InfoLabel, info.RootNode.Content)
|
||||
if vn != nil {
|
||||
ir := Info{}
|
||||
err := low.BuildModel(vn, &ir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ir.Build(vn, idx)
|
||||
_ = low.BuildModel(vn, &ir)
|
||||
_ = ir.Build(vn, idx)
|
||||
nr := low.NodeReference[*Info]{Value: &ir, ValueNode: vn, KeyNode: ln}
|
||||
doc.Info = nr
|
||||
}
|
||||
@@ -95,11 +90,11 @@ func extractComponents(info *datamodel.SpecInfo, doc *Document, idx *index.SpecI
|
||||
_, ln, vn := utils.FindKeyNodeFull(ComponentsLabel, info.RootNode.Content)
|
||||
if vn != nil {
|
||||
ir := Components{}
|
||||
err := low.BuildModel(vn, &ir)
|
||||
_ = low.BuildModel(vn, &ir)
|
||||
err := ir.Build(vn, idx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ir.Build(vn, idx)
|
||||
nr := low.NodeReference[*Components]{Value: &ir, ValueNode: vn, KeyNode: ln}
|
||||
doc.Components = nr
|
||||
}
|
||||
@@ -114,11 +109,8 @@ func extractServers(info *datamodel.SpecInfo, doc *Document, idx *index.SpecInde
|
||||
for _, srvN := range vn.Content {
|
||||
if utils.IsNodeMap(srvN) {
|
||||
srvr := Server{}
|
||||
err := low.BuildModel(srvN, &srvr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srvr.Build(srvN, idx)
|
||||
_ = low.BuildModel(srvN, &srvr)
|
||||
_ = srvr.Build(srvN, idx)
|
||||
servers = append(servers, low.ValueReference[*Server]{
|
||||
Value: &srvr,
|
||||
ValueNode: srvN,
|
||||
@@ -143,11 +135,10 @@ func extractTags(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex)
|
||||
for _, tagN := range vn.Content {
|
||||
if utils.IsNodeMap(tagN) {
|
||||
tag := Tag{}
|
||||
err := low.BuildModel(tagN, &tag)
|
||||
if err != nil {
|
||||
_ = low.BuildModel(tagN, &tag)
|
||||
if err := tag.Build(tagN, idx); err != nil {
|
||||
return err
|
||||
}
|
||||
tag.Build(tagN, idx)
|
||||
tags = append(tags, low.ValueReference[*Tag]{
|
||||
Value: &tag,
|
||||
ValueNode: tagN,
|
||||
|
||||
@@ -56,10 +56,10 @@ func BenchmarkCreateDocument_k8s(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkCreateDocument_Stripe(b *testing.B) {
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
_, err := CreateDocument(info)
|
||||
if err != nil {
|
||||
panic("this should not error")
|
||||
@@ -78,6 +78,20 @@ func BenchmarkCreateDocument_Petstore(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateDocumentStripe(t *testing.T) {
|
||||
|
||||
data, _ := ioutil.ReadFile("../../../test_specs/stripe.yaml")
|
||||
info, _ := datamodel.ExtractSpecInfo(data)
|
||||
d, err := CreateDocument(info)
|
||||
if err != nil {
|
||||
panic("broken something")
|
||||
}
|
||||
|
||||
assert.Equal(t, "3.0.0", d.Version.Value)
|
||||
assert.Equal(t, "Stripe API", d.Info.Value.Title.Value)
|
||||
assert.NotEmpty(t, d.Info.Value.Title.Value)
|
||||
}
|
||||
|
||||
func TestCreateDocument(t *testing.T) {
|
||||
initTest()
|
||||
assert.Equal(t, "3.0.1", doc.Version.Value)
|
||||
@@ -174,8 +188,8 @@ func TestCreateDocument_Paths(t *testing.T) {
|
||||
assert.Len(t, burgerId.Value.Get.Value.Parameters.Value, 2)
|
||||
param := burgerId.Value.Get.Value.Parameters.Value[1]
|
||||
assert.Equal(t, "burgerHeader", param.Value.Name.Value)
|
||||
prop := param.Value.Schema.Value.FindProperty("burgerTheme")
|
||||
assert.Equal(t, "something about a theme?", prop.Value.Description.Value)
|
||||
prop := param.Value.Schema.Value.Schema().FindProperty("burgerTheme")
|
||||
assert.Equal(t, "something about a theme?", prop.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "big-mac", param.Value.Example.Value)
|
||||
|
||||
// check content
|
||||
@@ -189,7 +203,7 @@ func TestCreateDocument_Paths(t *testing.T) {
|
||||
header := encoding.Value.FindHeader("someHeader")
|
||||
assert.NotNil(t, header.Value)
|
||||
assert.Equal(t, "this is a header", header.Value.Description.Value)
|
||||
assert.Equal(t, "string", header.Value.Schema.Value.Type.Value)
|
||||
assert.Equal(t, "string", header.Value.Schema.Value.Schema().Type.Value)
|
||||
|
||||
// check request body on operation
|
||||
burgers := doc.Paths.Value.FindPath("/burgers")
|
||||
@@ -206,7 +220,7 @@ func TestCreateDocument_Paths(t *testing.T) {
|
||||
content := requestBody.FindContent("application/json").Value
|
||||
|
||||
assert.NotNil(t, content)
|
||||
assert.Len(t, content.Schema.Value.Properties.Value, 4)
|
||||
assert.Len(t, content.Schema.Value.Schema().Properties.Value, 4)
|
||||
assert.Len(t, content.GetAllExamples(), 2)
|
||||
|
||||
ex := content.FindExample("pbjBurger")
|
||||
@@ -253,7 +267,7 @@ func TestCreateDocument_Paths(t *testing.T) {
|
||||
assert.NotNil(t, respContent)
|
||||
|
||||
assert.NotNil(t, respContent.Schema.Value)
|
||||
assert.Len(t, respContent.Schema.Value.Required.Value, 2)
|
||||
assert.Len(t, respContent.Schema.Value.Schema().Required.Value, 2)
|
||||
|
||||
respExample := respContent.FindExample("quarterPounder")
|
||||
assert.NotNil(t, respExample.Value)
|
||||
@@ -304,19 +318,20 @@ func TestCreateDocument_Components_Schemas(t *testing.T) {
|
||||
|
||||
burger := components.FindSchema("Burger")
|
||||
assert.NotNil(t, burger.Value)
|
||||
assert.Equal(t, "The tastiest food on the planet you would love to eat everyday", burger.Value.Description.Value)
|
||||
assert.Equal(t, "The tastiest food on the planet you would love to eat everyday", burger.Value.Schema().Description.Value)
|
||||
|
||||
er := components.FindSchema("Error")
|
||||
assert.NotNil(t, er.Value)
|
||||
assert.Equal(t, "Error defining what went wrong when providing a specification. The message should help indicate the issue clearly.", er.Value.Description.Value)
|
||||
assert.Equal(t, "Error defining what went wrong when providing a specification. The message should help "+
|
||||
"indicate the issue clearly.", er.Value.Schema().Description.Value)
|
||||
|
||||
fries := components.FindSchema("Fries")
|
||||
assert.NotNil(t, fries.Value)
|
||||
|
||||
assert.Len(t, fries.Value.Properties.Value, 3)
|
||||
p := fries.Value.FindProperty("favoriteDrink")
|
||||
assert.Len(t, fries.Value.Schema().Properties.Value, 3)
|
||||
p := fries.Value.Schema().FindProperty("favoriteDrink")
|
||||
assert.Equal(t, "a frosty cold beverage can be coke or sprite",
|
||||
p.Value.Description.Value)
|
||||
p.Value.Schema().Description.Value)
|
||||
|
||||
}
|
||||
|
||||
@@ -395,7 +410,7 @@ func TestCreateDocument_Components_Headers(t *testing.T) {
|
||||
useOil := components.FindHeader("UseOil")
|
||||
assert.NotNil(t, useOil.Value)
|
||||
assert.Equal(t, "this is a header", useOil.Value.Description.Value)
|
||||
assert.Equal(t, "string", useOil.Value.Schema.Value.Type.Value)
|
||||
assert.Equal(t, "string", useOil.Value.Schema.Value.Schema().Type.Value)
|
||||
}
|
||||
|
||||
func TestCreateDocument_Components_Links(t *testing.T) {
|
||||
@@ -442,7 +457,7 @@ func TestCreateDocument_Component_Discriminator(t *testing.T) {
|
||||
initTest()
|
||||
|
||||
components := doc.Components.Value
|
||||
dsc := components.FindSchema("Drink").Value.Discriminator.Value
|
||||
dsc := components.FindSchema("Drink").Value.Schema().Discriminator.Value
|
||||
assert.NotNil(t, dsc)
|
||||
assert.Equal(t, "drinkType", dsc.PropertyName.Value)
|
||||
assert.Equal(t, "some value", dsc.FindMappingValue("drink").Value)
|
||||
@@ -453,8 +468,8 @@ func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
|
||||
initTest()
|
||||
components := doc.Components.Value
|
||||
d := components.FindSchema("Dressing")
|
||||
assert.NotNil(t, d.Value.AdditionalProperties.Value)
|
||||
if n, ok := d.Value.AdditionalProperties.Value.(*Schema); ok {
|
||||
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
|
||||
if n, ok := d.Value.Schema().AdditionalProperties.Value.(*Schema); ok {
|
||||
assert.Equal(t, "something in here.", n.Description.Value)
|
||||
} else {
|
||||
assert.Fail(t, "should be a schema")
|
||||
@@ -465,8 +480,8 @@ func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) {
|
||||
initTest()
|
||||
components := doc.Components.Value
|
||||
d := components.FindSchema("Drink")
|
||||
assert.NotNil(t, d.Value.AdditionalProperties.Value)
|
||||
assert.True(t, d.Value.AdditionalProperties.Value.(bool))
|
||||
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
|
||||
assert.True(t, d.Value.Schema().AdditionalProperties.Value.(bool))
|
||||
}
|
||||
|
||||
func TestCreateDocument_Components_Error(t *testing.T) {
|
||||
@@ -477,6 +492,51 @@ func TestCreateDocument_Components_Error(t *testing.T) {
|
||||
bark:
|
||||
$ref: #bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
assert.Len(t, err, 0)
|
||||
|
||||
ob := doc.Components.Value.FindSchema("bork").Value
|
||||
ob.Schema()
|
||||
assert.Error(t, ob.GetBuildError())
|
||||
}
|
||||
|
||||
func TestCreateDocument_Paths_Errors(t *testing.T) {
|
||||
yml := `paths:
|
||||
/p:
|
||||
$ref: #bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_Tags_Errors(t *testing.T) {
|
||||
yml := `tags:
|
||||
- $ref: #bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_Security_Error(t *testing.T) {
|
||||
yml := `security:
|
||||
$ref: #bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
assert.Len(t, err, 1)
|
||||
}
|
||||
|
||||
func TestCreateDocument_ExternalDoc_Error(t *testing.T) {
|
||||
yml := `externalDocs:
|
||||
$ref: #bork`
|
||||
|
||||
info, _ := datamodel.ExtractSpecInfo([]byte(yml))
|
||||
var err []error
|
||||
doc, err = CreateDocument(info)
|
||||
|
||||
@@ -123,3 +123,10 @@ value:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestExtractExampleValue(t *testing.T) {
|
||||
assert.True(t, ExtractExampleValue(&yaml.Node{Tag: "!!bool", Value: "true"}).(bool))
|
||||
assert.Equal(t, int64(10), ExtractExampleValue(&yaml.Node{Tag: "!!int", Value: "10"}).(int64))
|
||||
assert.Equal(t, 33.2, ExtractExampleValue(&yaml.Node{Tag: "!!float", Value: "33.2"}).(float64))
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type Header struct {
|
||||
Style low.NodeReference[string]
|
||||
Explode low.NodeReference[bool]
|
||||
AllowReserved low.NodeReference[bool]
|
||||
Schema low.NodeReference[*Schema]
|
||||
Schema low.NodeReference[*SchemaProxy]
|
||||
Example low.NodeReference[any]
|
||||
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
|
||||
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
|
||||
|
||||
@@ -60,11 +60,11 @@ content:
|
||||
assert.True(t, n.Required.Value)
|
||||
assert.False(t, n.Deprecated.Value)
|
||||
assert.NotNil(t, n.Schema.Value)
|
||||
assert.Equal(t, "my triple M, my loves", n.Schema.Value.Description.Value)
|
||||
assert.NotNil(t, n.Schema.Value.Properties.Value)
|
||||
assert.Equal(t, "she is my heart.", n.Schema.Value.FindProperty("michelle").Value.Description.Value)
|
||||
assert.Equal(t, "she is my song.", n.Schema.Value.FindProperty("meddy").Value.Description.Value)
|
||||
assert.Equal(t, "he is my champion.", n.Schema.Value.FindProperty("maddy").Value.Description.Value)
|
||||
assert.Equal(t, "my triple M, my loves", n.Schema.Value.Schema().Description.Value)
|
||||
assert.NotNil(t, n.Schema.Value.Schema().Properties.Value)
|
||||
assert.Equal(t, "she is my heart.", n.Schema.Value.Schema().FindProperty("michelle").Value.Schema().Description.Value)
|
||||
assert.Equal(t, "she is my song.", n.Schema.Value.Schema().FindProperty("meddy").Value.Schema().Description.Value)
|
||||
assert.Equal(t, "he is my champion.", n.Schema.Value.Schema().FindProperty("maddy").Value.Schema().Description.Value)
|
||||
|
||||
if v, ok := n.Example.Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "my love.", v["michelle"])
|
||||
@@ -76,7 +76,7 @@ content:
|
||||
|
||||
con := n.FindContent("family/love").Value
|
||||
assert.NotNil(t, con)
|
||||
assert.Equal(t, "family love.", con.Schema.Value.Description.Value)
|
||||
assert.Equal(t, "family love.", con.Schema.Value.Schema().Description.Value)
|
||||
assert.Nil(t, n.FindContent("unknown"))
|
||||
|
||||
ext := n.FindExtension("x-family-love").Value
|
||||
|
||||
@@ -4,78 +4,78 @@
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"github.com/pb33f/libopenapi/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type MediaType struct {
|
||||
Schema low.NodeReference[*Schema]
|
||||
Example low.NodeReference[any]
|
||||
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
|
||||
Encoding low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
Schema low.NodeReference[*SchemaProxy]
|
||||
Example low.NodeReference[any]
|
||||
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
|
||||
Encoding low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (mt *MediaType) FindExtension(ext string) *low.ValueReference[any] {
|
||||
return low.FindItemInMap[any](ext, mt.Extensions)
|
||||
return low.FindItemInMap[any](ext, mt.Extensions)
|
||||
}
|
||||
|
||||
func (mt *MediaType) FindPropertyEncoding(eType string) *low.ValueReference[*Encoding] {
|
||||
return low.FindItemInMap[*Encoding](eType, mt.Encoding.Value)
|
||||
return low.FindItemInMap[*Encoding](eType, mt.Encoding.Value)
|
||||
}
|
||||
|
||||
func (mt *MediaType) FindExample(eType string) *low.ValueReference[*Example] {
|
||||
return low.FindItemInMap[*Example](eType, mt.Examples.Value)
|
||||
return low.FindItemInMap[*Example](eType, mt.Examples.Value)
|
||||
}
|
||||
|
||||
func (mt *MediaType) GetAllExamples() map[low.KeyReference[string]]low.ValueReference[*Example] {
|
||||
return mt.Examples.Value
|
||||
return mt.Examples.Value
|
||||
}
|
||||
|
||||
func (mt *MediaType) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
mt.Extensions = low.ExtractExtensions(root)
|
||||
mt.Extensions = low.ExtractExtensions(root)
|
||||
|
||||
// handle example if set.
|
||||
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
mt.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
|
||||
}
|
||||
// handle example if set.
|
||||
_, expLabel, expNode := utils.FindKeyNodeFull(ExampleLabel, root.Content)
|
||||
if expNode != nil {
|
||||
mt.Example = low.NodeReference[any]{Value: expNode.Value, KeyNode: expLabel, ValueNode: expNode}
|
||||
}
|
||||
|
||||
//handle schema
|
||||
sch, sErr := ExtractSchema(root, idx)
|
||||
if sErr != nil {
|
||||
return sErr
|
||||
}
|
||||
if sch != nil {
|
||||
mt.Schema = *sch
|
||||
}
|
||||
//handle schema
|
||||
sch, sErr := ExtractSchema(root, idx)
|
||||
if sErr != nil {
|
||||
return sErr
|
||||
}
|
||||
if sch != nil {
|
||||
mt.Schema = *sch
|
||||
}
|
||||
|
||||
// handle examples if set.
|
||||
exps, expsL, expsN, eErr := low.ExtractMapFlat[*Example](ExamplesLabel, root, idx)
|
||||
if eErr != nil {
|
||||
return eErr
|
||||
}
|
||||
if exps != nil {
|
||||
mt.Examples = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]{
|
||||
Value: exps,
|
||||
KeyNode: expsL,
|
||||
ValueNode: expsN,
|
||||
}
|
||||
}
|
||||
// handle examples if set.
|
||||
exps, expsL, expsN, eErr := low.ExtractMapFlat[*Example](ExamplesLabel, root, idx)
|
||||
if eErr != nil {
|
||||
return eErr
|
||||
}
|
||||
if exps != nil {
|
||||
mt.Examples = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]{
|
||||
Value: exps,
|
||||
KeyNode: expsL,
|
||||
ValueNode: expsN,
|
||||
}
|
||||
}
|
||||
|
||||
// handle encoding
|
||||
encs, encsL, encsN, encErr := low.ExtractMapFlat[*Encoding](EncodingLabel, root, idx)
|
||||
if encErr != nil {
|
||||
return encErr
|
||||
}
|
||||
if encs != nil {
|
||||
mt.Encoding = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]{
|
||||
Value: encs,
|
||||
KeyNode: encsL,
|
||||
ValueNode: encsN,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
// handle encoding
|
||||
encs, encsL, encsN, encErr := low.ExtractMapFlat[*Encoding](EncodingLabel, root, idx)
|
||||
if encErr != nil {
|
||||
return encErr
|
||||
}
|
||||
if encs != nil {
|
||||
mt.Encoding = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Encoding]]{
|
||||
Value: encs,
|
||||
KeyNode: encsL,
|
||||
ValueNode: encsN,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ x-rock: and roll`
|
||||
err = n.Build(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "and roll", n.FindExtension("x-rock").Value)
|
||||
assert.Equal(t, "string", n.Schema.Value.Type.Value)
|
||||
assert.Equal(t, "string", n.Schema.Value.Schema().Type.Value)
|
||||
assert.Equal(t, "hello", n.Example.Value)
|
||||
assert.Equal(t, "why?", n.FindExample("what").Value.Value.Value)
|
||||
assert.Equal(t, "there?", n.FindExample("where").Value.Value.Value)
|
||||
|
||||
@@ -25,7 +25,7 @@ type Parameter struct {
|
||||
Style low.NodeReference[string]
|
||||
Explode low.NodeReference[bool]
|
||||
AllowReserved low.NodeReference[bool]
|
||||
Schema low.NodeReference[*Schema]
|
||||
Schema low.NodeReference[*SchemaProxy]
|
||||
Example low.NodeReference[any]
|
||||
Examples low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Example]]
|
||||
Content low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*MediaType]]
|
||||
|
||||
@@ -64,11 +64,11 @@ content:
|
||||
assert.Equal(t, "happy", n.Name.Value)
|
||||
assert.Equal(t, "path", n.In.Value)
|
||||
assert.NotNil(t, n.Schema.Value)
|
||||
assert.Equal(t, "my triple M, my loves", n.Schema.Value.Description.Value)
|
||||
assert.NotNil(t, n.Schema.Value.Properties.Value)
|
||||
assert.Equal(t, "she is my heart.", n.Schema.Value.FindProperty("michelle").Value.Description.Value)
|
||||
assert.Equal(t, "she is my song.", n.Schema.Value.FindProperty("meddy").Value.Description.Value)
|
||||
assert.Equal(t, "he is my champion.", n.Schema.Value.FindProperty("maddy").Value.Description.Value)
|
||||
assert.Equal(t, "my triple M, my loves", n.Schema.Value.Schema().Description.Value)
|
||||
assert.NotNil(t, n.Schema.Value.Schema().Properties.Value)
|
||||
assert.Equal(t, "she is my heart.", n.Schema.Value.Schema().FindProperty("michelle").Value.Schema().Description.Value)
|
||||
assert.Equal(t, "she is my song.", n.Schema.Value.Schema().FindProperty("meddy").Value.Schema().Description.Value)
|
||||
assert.Equal(t, "he is my champion.", n.Schema.Value.Schema().FindProperty("maddy").Value.Schema().Description.Value)
|
||||
|
||||
if v, ok := n.Example.Value.(map[string]interface{}); ok {
|
||||
assert.Equal(t, "my love.", v["michelle"])
|
||||
@@ -80,7 +80,7 @@ content:
|
||||
|
||||
con := n.FindContent("family/love").Value
|
||||
assert.NotNil(t, con)
|
||||
assert.Equal(t, "family love.", con.Schema.Value.Description.Value)
|
||||
assert.Equal(t, "family love.", con.Schema.Value.Schema().Description.Value)
|
||||
assert.Nil(t, n.FindContent("unknown"))
|
||||
|
||||
ext := n.FindExtension("x-family-love").Value
|
||||
|
||||
@@ -30,9 +30,9 @@ type Paths struct {
|
||||
}
|
||||
|
||||
func (p *Paths) FindPath(path string) *low.ValueReference[*PathItem] {
|
||||
for k, p := range p.PathItems {
|
||||
for k, j := range p.PathItems {
|
||||
if k.Value == path {
|
||||
return &p
|
||||
return &j
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -39,7 +39,7 @@ func (r *Responses) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
r.Default = def
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("responses build failed: root node is not a map! line %d, col %d", root.Line, root.Column)
|
||||
return fmt.Errorf("responses build failed: vn node is not a map! line %d, col %d", root.Line, root.Column)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ default:
|
||||
|
||||
con := ok.Value.FindContent("nice/rice")
|
||||
assert.NotNil(t, con.Value)
|
||||
assert.Equal(t, "this is some content.", con.Value.Schema.Value.Description.Value)
|
||||
assert.Equal(t, "this is some content.", con.Value.Schema.Value.Schema().Description.Value)
|
||||
|
||||
head := ok.Value.FindHeader("header1")
|
||||
assert.NotNil(t, head.Value)
|
||||
|
||||
@@ -40,12 +40,12 @@ type Schema struct {
|
||||
Required low.NodeReference[[]low.ValueReference[string]]
|
||||
Enum low.NodeReference[[]low.ValueReference[string]]
|
||||
Type low.NodeReference[string]
|
||||
AllOf low.NodeReference[[]low.NodeReference[*Schema]]
|
||||
OneOf low.NodeReference[[]low.NodeReference[*Schema]]
|
||||
AnyOf low.NodeReference[[]low.NodeReference[*Schema]]
|
||||
Not low.NodeReference[[]low.NodeReference[*Schema]]
|
||||
Items low.NodeReference[[]low.NodeReference[*Schema]]
|
||||
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]]
|
||||
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]
|
||||
Default low.NodeReference[any]
|
||||
@@ -60,8 +60,8 @@ type Schema struct {
|
||||
Extensions map[low.KeyReference[string]]low.ValueReference[any]
|
||||
}
|
||||
|
||||
func (s *Schema) FindProperty(name string) *low.ValueReference[*Schema] {
|
||||
return low.FindItemInMap[*Schema](name, s.Properties.Value)
|
||||
func (s *Schema) FindProperty(name string) *low.ValueReference[*SchemaProxy] {
|
||||
return low.FindItemInMap[*SchemaProxy](name, s.Properties.Value)
|
||||
}
|
||||
|
||||
func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
@@ -83,7 +83,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
|
||||
if ref != nil {
|
||||
root = ref
|
||||
} else {
|
||||
return fmt.Errorf("build schema failed: reference cannot be found: %s, line %d, col %d",
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -140,39 +140,16 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
|
||||
}
|
||||
|
||||
// for property, build in a new thread!
|
||||
bChan := make(chan schemaBuildResult)
|
||||
eChan := make(chan error)
|
||||
bChan := make(chan schemaProxyBuildResult)
|
||||
|
||||
var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaBuildResult, ec chan<- error) {
|
||||
// have we seen this before?
|
||||
seen := getSeenSchema(fmt.Sprintf("%d:%d", value.Line, value.Column))
|
||||
if seen != nil {
|
||||
c <- schemaBuildResult{
|
||||
k: low.KeyReference[string]{
|
||||
KeyNode: label,
|
||||
Value: label.Value,
|
||||
},
|
||||
v: low.ValueReference[*Schema]{
|
||||
Value: seen,
|
||||
ValueNode: value,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
p := new(Schema)
|
||||
_ = low.BuildModel(value, p)
|
||||
err := p.BuildLevel(value, idx, level)
|
||||
if err != nil {
|
||||
ec <- err
|
||||
return
|
||||
}
|
||||
c <- schemaBuildResult{
|
||||
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[*Schema]{
|
||||
Value: p,
|
||||
v: low.ValueReference[*SchemaProxy]{
|
||||
Value: &SchemaProxy{kn: label, vn: value, idx: idx},
|
||||
ValueNode: value,
|
||||
},
|
||||
}
|
||||
@@ -181,7 +158,7 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
|
||||
// handle properties
|
||||
_, propLabel, propsNode := utils.FindKeyNodeFull(PropertiesLabel, root.Content)
|
||||
if propsNode != nil {
|
||||
propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*Schema])
|
||||
propertyMap := make(map[low.KeyReference[string]]low.ValueReference[*SchemaProxy])
|
||||
var currentProp *yaml.Node
|
||||
totalProps := 0
|
||||
for i, prop := range propsNode.Content {
|
||||
@@ -201,64 +178,106 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
|
||||
}
|
||||
}
|
||||
totalProps++
|
||||
go buildProperty(currentProp, prop, bChan, eChan)
|
||||
go buildProperty(currentProp, prop, bChan)
|
||||
}
|
||||
completedProps := 0
|
||||
for completedProps < totalProps {
|
||||
select {
|
||||
case err := <-eChan:
|
||||
return err
|
||||
case res := <-bChan:
|
||||
completedProps++
|
||||
propertyMap[res.k] = res.v
|
||||
}
|
||||
}
|
||||
s.Properties = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*Schema]]{
|
||||
s.Properties = low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]{
|
||||
Value: propertyMap,
|
||||
KeyNode: propLabel,
|
||||
ValueNode: propsNode,
|
||||
}
|
||||
}
|
||||
|
||||
// extract all sub-schemas
|
||||
var errors []error
|
||||
var allOf, anyOf, oneOf, not, items []low.ValueReference[*SchemaProxy]
|
||||
|
||||
var allOf, anyOf, oneOf, not, items []low.NodeReference[*Schema]
|
||||
_, 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)
|
||||
|
||||
// make this async at some point to speed things up.
|
||||
allOfLabel, allOfValue := buildSchema(&allOf, AllOfLabel, root, level, &errors, idx)
|
||||
anyOfLabel, anyOfValue := buildSchema(&anyOf, AnyOfLabel, root, level, &errors, idx)
|
||||
oneOfLabel, oneOfValue := buildSchema(&oneOf, OneOfLabel, root, level, &errors, idx)
|
||||
notLabel, notValue := buildSchema(¬, NotLabel, root, level, &errors, idx)
|
||||
itemsLabel, itemsValue := buildSchema(&items, ItemsLabel, root, level, &errors, idx)
|
||||
errorChan := make(chan error)
|
||||
allOfChan := make(chan schemaProxyBuildResult)
|
||||
anyOfChan := make(chan schemaProxyBuildResult)
|
||||
oneOfChan := make(chan schemaProxyBuildResult)
|
||||
itemsChan := make(chan schemaProxyBuildResult)
|
||||
notChan := make(chan schemaProxyBuildResult)
|
||||
|
||||
if len(errors) > 0 {
|
||||
// todo fix this
|
||||
return errors[0]
|
||||
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)
|
||||
}
|
||||
|
||||
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.NodeReference[*Schema]]{
|
||||
s.AnyOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{
|
||||
Value: anyOf,
|
||||
KeyNode: anyOfLabel,
|
||||
ValueNode: anyOfValue,
|
||||
}
|
||||
}
|
||||
if len(oneOf) > 0 {
|
||||
s.OneOf = low.NodeReference[[]low.NodeReference[*Schema]]{
|
||||
s.OneOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{
|
||||
Value: oneOf,
|
||||
KeyNode: oneOfLabel,
|
||||
ValueNode: oneOfValue,
|
||||
}
|
||||
}
|
||||
if len(allOf) > 0 {
|
||||
s.AllOf = low.NodeReference[[]low.NodeReference[*Schema]]{
|
||||
s.AllOf = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{
|
||||
Value: allOf,
|
||||
KeyNode: allOfLabel,
|
||||
ValueNode: allOfValue,
|
||||
}
|
||||
}
|
||||
if len(not) > 0 {
|
||||
s.Not = low.NodeReference[[]low.NodeReference[*Schema]]{
|
||||
s.Not = low.NodeReference[[]low.ValueReference[*SchemaProxy]]{
|
||||
Value: not,
|
||||
KeyNode: notLabel,
|
||||
ValueNode: notValue,
|
||||
@@ -266,74 +285,74 @@ func (s *Schema) BuildLevel(root *yaml.Node, idx *index.SpecIndex, level int) er
|
||||
|
||||
}
|
||||
if len(items) > 0 {
|
||||
s.Items = low.NodeReference[[]low.NodeReference[*Schema]]{
|
||||
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
|
||||
}
|
||||
|
||||
type schemaBuildResult struct {
|
||||
k low.KeyReference[string]
|
||||
v low.ValueReference[*Schema]
|
||||
}
|
||||
|
||||
type schemaProxyBuildResult struct {
|
||||
k low.KeyReference[string]
|
||||
v low.ValueReference[*SchemaProxy]
|
||||
}
|
||||
|
||||
func (s *Schema) extractExtensions(root *yaml.Node) {
|
||||
s.Extensions = low.ExtractExtensions(root)
|
||||
}
|
||||
|
||||
func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNode *yaml.Node, level int,
|
||||
errors *[]error, idx *index.SpecIndex) (labelNode *yaml.Node, valueNode *yaml.Node) {
|
||||
|
||||
_, labelNode, valueNode = utils.FindKeyNodeFull(attribute, rootNode.Content)
|
||||
//wg.Add(1)
|
||||
if valueNode == nil {
|
||||
return nil, nil
|
||||
}
|
||||
func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml.Node, errors chan error, idx *index.SpecIndex) {
|
||||
|
||||
if valueNode != nil {
|
||||
syncChan := make(chan *low.ValueReference[*SchemaProxy])
|
||||
errorChan := make(chan error)
|
||||
|
||||
build := func(kn *yaml.Node, vn *yaml.Node) *low.NodeReference[*Schema] {
|
||||
schema := new(Schema)
|
||||
// build out a SchemaProxy for every sub-schema.
|
||||
build := func(kn *yaml.Node, vn *yaml.Node, c chan *low.ValueReference[*SchemaProxy], e chan error) {
|
||||
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
||||
ref := low.LocateRefNode(vn, idx)
|
||||
if ref != nil {
|
||||
vn = ref
|
||||
} else {
|
||||
*errors = append(*errors, 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))
|
||||
return nil
|
||||
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)
|
||||
e <- err
|
||||
}
|
||||
}
|
||||
|
||||
seen := getSeenSchema(fmt.Sprintf("%d:%d", vn.Line, vn.Column))
|
||||
if seen != nil {
|
||||
return &low.NodeReference[*Schema]{
|
||||
Value: seen,
|
||||
KeyNode: kn,
|
||||
ValueNode: vn,
|
||||
}
|
||||
}
|
||||
// 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
|
||||
|
||||
_ = low.BuildModel(vn, schema)
|
||||
|
||||
// add schema before we build, so it doesn't get stuck in an infinite loop.
|
||||
addSeenSchema(fmt.Sprintf("%d:%d", vn.Line, vn.Column), schema)
|
||||
|
||||
err := schema.BuildLevel(vn, idx, level)
|
||||
if err != nil {
|
||||
*errors = append(*errors, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &low.NodeReference[*Schema]{
|
||||
Value: schema,
|
||||
KeyNode: kn,
|
||||
res := &low.ValueReference[*SchemaProxy]{
|
||||
Value: sp,
|
||||
ValueNode: vn,
|
||||
}
|
||||
c <- res
|
||||
}
|
||||
|
||||
if utils.IsNodeMap(valueNode) {
|
||||
@@ -342,42 +361,66 @@ func buildSchema(schemas *[]low.NodeReference[*Schema], attribute string, rootNo
|
||||
if ref != nil {
|
||||
valueNode = ref
|
||||
} else {
|
||||
*errors = append(*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))
|
||||
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)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
schema := build(labelNode, valueNode)
|
||||
if schema != nil {
|
||||
*schemas = append(*schemas, *schema)
|
||||
// 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, errorChan)
|
||||
select {
|
||||
case e := <-errorChan:
|
||||
errors <- e
|
||||
break
|
||||
case r := <-syncChan:
|
||||
schemas <- schemaProxyBuildResult{
|
||||
k: low.KeyReference[string]{
|
||||
KeyNode: labelNode,
|
||||
Value: labelNode.Value,
|
||||
},
|
||||
v: *r,
|
||||
}
|
||||
}
|
||||
}
|
||||
if utils.IsNodeArray(valueNode) {
|
||||
//fmt.Println("polymorphic looping sucks dude.")
|
||||
refBuilds := 0
|
||||
for _, vn := range valueNode.Content {
|
||||
if h, _, _ := utils.IsNodeRefValue(vn); h {
|
||||
ref := low.LocateRefNode(vn, idx)
|
||||
if ref != nil {
|
||||
vn = ref
|
||||
} else {
|
||||
*errors = append(*errors, 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))
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
schema := build(vn, vn)
|
||||
if schema != nil {
|
||||
*schemas = append(*schemas, *schema)
|
||||
refBuilds++
|
||||
go build(vn, vn, syncChan, errorChan)
|
||||
}
|
||||
completedBuilds := 0
|
||||
for completedBuilds < refBuilds {
|
||||
select {
|
||||
case res := <-syncChan:
|
||||
completedBuilds++
|
||||
schemas <- schemaProxyBuildResult{
|
||||
k: low.KeyReference[string]{
|
||||
KeyNode: labelNode,
|
||||
Value: labelNode.Value,
|
||||
},
|
||||
v: *res,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return labelNode, valueNode
|
||||
}
|
||||
|
||||
func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*Schema], error) {
|
||||
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 {
|
||||
@@ -407,20 +450,8 @@ func ExtractSchema(root *yaml.Node, idx *index.SpecIndex) (*low.NodeReference[*S
|
||||
|
||||
if schNode != nil {
|
||||
// check if schema has already been built.
|
||||
seen := getSeenSchema(fmt.Sprintf("%d:%d", schNode.Line, schNode.Column))
|
||||
if seen != nil {
|
||||
return &low.NodeReference[*Schema]{Value: seen, KeyNode: schLabel, ValueNode: schNode}, nil
|
||||
}
|
||||
|
||||
var schema Schema
|
||||
_ = low.BuildModel(schNode, &schema)
|
||||
err := schema.Build(schNode, idx)
|
||||
addSeenSchema(fmt.Sprintf("%d:%d", schNode.Line, schNode.Column), &schema)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &low.NodeReference[*Schema]{Value: &schema, KeyNode: schLabel, ValueNode: schNode}, nil
|
||||
schema := &SchemaProxy{kn: schLabel, vn: schNode, idx: idx}
|
||||
return &low.NodeReference[*SchemaProxy]{Value: schema, KeyNode: schLabel, ValueNode: schNode}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
48
datamodel/low/3.0/schema_proxy.go
Normal file
48
datamodel/low/3.0/schema_proxy.go
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v3
|
||||
|
||||
import (
|
||||
"github.com/pb33f/libopenapi/datamodel/low"
|
||||
"github.com/pb33f/libopenapi/index"
|
||||
"go.uber.org/zap"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type SchemaProxy struct {
|
||||
kn *yaml.Node
|
||||
vn *yaml.Node
|
||||
idx *index.SpecIndex
|
||||
rendered *Schema
|
||||
buildError error
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) Build(root *yaml.Node, idx *index.SpecIndex) error {
|
||||
sp.vn = root
|
||||
sp.idx = idx
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) Schema() *Schema {
|
||||
if sp.rendered != nil {
|
||||
return sp.rendered
|
||||
}
|
||||
schema := new(Schema)
|
||||
_ = low.BuildModel(sp.vn, schema)
|
||||
err := schema.Build(sp.vn, sp.idx)
|
||||
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
|
||||
return nil
|
||||
}
|
||||
sp.rendered = schema
|
||||
return schema
|
||||
}
|
||||
|
||||
func (sp *SchemaProxy) GetBuildError() error {
|
||||
return sp.buildError
|
||||
}
|
||||
@@ -121,94 +121,98 @@ additionalProperties: true `
|
||||
assert.Len(t, sch.Properties.Value, 2)
|
||||
v := sch.FindProperty("somethingB")
|
||||
|
||||
assert.Equal(t, "https://pb33f.io", v.Value.ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, "the best docs", v.Value.ExternalDocs.Value.Description.Value)
|
||||
assert.Equal(t, "https://pb33f.io", v.Value.Schema().ExternalDocs.Value.URL.Value)
|
||||
assert.Equal(t, "the best docs", v.Value.Schema().ExternalDocs.Value.Description.Value)
|
||||
|
||||
j := v.Value.FindProperty("somethingBProp")
|
||||
assert.NotNil(t, j.Value)
|
||||
assert.NotNil(t, j.Value.XML.Value)
|
||||
assert.Equal(t, "an xml thing", j.Value.XML.Value.Name.Value)
|
||||
assert.Equal(t, "an xml namespace", j.Value.XML.Value.Namespace.Value)
|
||||
assert.Equal(t, "a prefix", j.Value.XML.Value.Prefix.Value)
|
||||
assert.Equal(t, true, j.Value.XML.Value.Attribute.Value)
|
||||
assert.Len(t, j.Value.XML.Value.Extensions, 1)
|
||||
j := v.Value.Schema().FindProperty("somethingBProp").Value.Schema()
|
||||
assert.NotNil(t, j)
|
||||
assert.NotNil(t, j.XML.Value)
|
||||
assert.Equal(t, "an xml thing", j.XML.Value.Name.Value)
|
||||
assert.Equal(t, "an xml namespace", j.XML.Value.Namespace.Value)
|
||||
assert.Equal(t, "a prefix", j.XML.Value.Prefix.Value)
|
||||
assert.Equal(t, true, j.XML.Value.Attribute.Value)
|
||||
assert.Len(t, j.XML.Value.Extensions, 1)
|
||||
|
||||
assert.NotNil(t, v.Value.AdditionalProperties.Value)
|
||||
assert.NotNil(t, v.Value.Schema().AdditionalProperties.Value)
|
||||
|
||||
var addProps map[string]interface{}
|
||||
v.Value.AdditionalProperties.ValueNode.Decode(&addProps)
|
||||
v.Value.Schema().AdditionalProperties.ValueNode.Decode(&addProps)
|
||||
assert.Equal(t, "yes", addProps["why"])
|
||||
assert.Equal(t, true, addProps["thatIs"])
|
||||
|
||||
// check polymorphic values allOf
|
||||
assert.Equal(t, "an allof thing", sch.AllOf.Value[0].Value.Description.Value)
|
||||
assert.Len(t, sch.AllOf.Value[0].Value.Properties.Value, 2)
|
||||
f := sch.AllOf.Value[0].Value.Schema()
|
||||
assert.Equal(t, "an allof thing", f.Description.Value)
|
||||
assert.Len(t, f.Properties.Value, 2)
|
||||
|
||||
v = sch.AllOf.Value[0].Value.FindProperty("allOfA")
|
||||
v = f.FindProperty("allOfA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "allOfA description", v.Value.Description.Value)
|
||||
assert.Equal(t, "allOfAExp", v.Value.Example.Value)
|
||||
|
||||
v = sch.AllOf.Value[0].Value.FindProperty("allOfB")
|
||||
io := v.Value.Schema()
|
||||
|
||||
assert.Equal(t, "allOfA description", io.Description.Value)
|
||||
assert.Equal(t, "allOfAExp", io.Example.Value)
|
||||
|
||||
qw := f.FindProperty("allOfB").Value.Schema()
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "allOfB description", v.Value.Description.Value)
|
||||
assert.Equal(t, "allOfBExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "allOfB description", qw.Description.Value)
|
||||
assert.Equal(t, "allOfBExp", qw.Example.Value)
|
||||
|
||||
// check polymorphic values anyOf
|
||||
assert.Equal(t, "an anyOf thing", sch.AnyOf.Value[0].Value.Description.Value)
|
||||
assert.Len(t, sch.AnyOf.Value[0].Value.Properties.Value, 2)
|
||||
assert.Equal(t, "an anyOf thing", sch.AnyOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Len(t, sch.AnyOf.Value[0].Value.Schema().Properties.Value, 2)
|
||||
|
||||
v = sch.AnyOf.Value[0].Value.FindProperty("anyOfA")
|
||||
v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "anyOfA description", v.Value.Description.Value)
|
||||
assert.Equal(t, "anyOfAExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "anyOfA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "anyOfAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
v = sch.AnyOf.Value[0].Value.FindProperty("anyOfB")
|
||||
v = sch.AnyOf.Value[0].Value.Schema().FindProperty("anyOfB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "anyOfB description", v.Value.Description.Value)
|
||||
assert.Equal(t, "anyOfBExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "anyOfB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "anyOfBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
// check polymorphic values oneOf
|
||||
assert.Equal(t, "a oneof thing", sch.OneOf.Value[0].Value.Description.Value)
|
||||
assert.Len(t, sch.OneOf.Value[0].Value.Properties.Value, 2)
|
||||
assert.Equal(t, "a oneof thing", sch.OneOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Len(t, sch.OneOf.Value[0].Value.Schema().Properties.Value, 2)
|
||||
|
||||
v = sch.OneOf.Value[0].Value.FindProperty("oneOfA")
|
||||
v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "oneOfA description", v.Value.Description.Value)
|
||||
assert.Equal(t, "oneOfAExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "oneOfA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "oneOfAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
v = sch.OneOf.Value[0].Value.FindProperty("oneOfB")
|
||||
v = sch.OneOf.Value[0].Value.Schema().FindProperty("oneOfB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "oneOfB description", v.Value.Description.Value)
|
||||
assert.Equal(t, "oneOfBExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "oneOfB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "oneOfBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
// check values NOT
|
||||
assert.Equal(t, "a not thing", sch.Not.Value[0].Value.Description.Value)
|
||||
assert.Len(t, sch.Not.Value[0].Value.Properties.Value, 2)
|
||||
assert.Equal(t, "a not thing", sch.Not.Value[0].Value.Schema().Description.Value)
|
||||
assert.Len(t, sch.Not.Value[0].Value.Schema().Properties.Value, 2)
|
||||
|
||||
v = sch.Not.Value[0].Value.FindProperty("notA")
|
||||
v = sch.Not.Value[0].Value.Schema().FindProperty("notA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "notA description", v.Value.Description.Value)
|
||||
assert.Equal(t, "notAExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "notA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "notAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
v = sch.Not.Value[0].Value.FindProperty("notB")
|
||||
v = sch.Not.Value[0].Value.Schema().FindProperty("notB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "notB description", v.Value.Description.Value)
|
||||
assert.Equal(t, "notBExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "notB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "notBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
// check values Items
|
||||
assert.Equal(t, "an items thing", sch.Items.Value[0].Value.Description.Value)
|
||||
assert.Len(t, sch.Items.Value[0].Value.Properties.Value, 2)
|
||||
assert.Equal(t, "an items thing", sch.Items.Value[0].Value.Schema().Description.Value)
|
||||
assert.Len(t, sch.Items.Value[0].Value.Schema().Properties.Value, 2)
|
||||
|
||||
v = sch.Items.Value[0].Value.FindProperty("itemsA")
|
||||
v = sch.Items.Value[0].Value.Schema().FindProperty("itemsA")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "itemsA description", v.Value.Description.Value)
|
||||
assert.Equal(t, "itemsAExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "itemsA description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "itemsAExp", v.Value.Schema().Example.Value)
|
||||
|
||||
v = sch.Items.Value[0].Value.FindProperty("itemsB")
|
||||
v = sch.Items.Value[0].Value.Schema().FindProperty("itemsB")
|
||||
assert.NotNil(t, v)
|
||||
assert.Equal(t, "itemsB description", v.Value.Description.Value)
|
||||
assert.Equal(t, "itemsBExp", v.Value.Example.Value)
|
||||
assert.Equal(t, "itemsB description", v.Value.Schema().Description.Value)
|
||||
assert.Equal(t, "itemsBExp", v.Value.Schema().Example.Value)
|
||||
|
||||
// check discriminator
|
||||
assert.NotNil(t, sch.Discriminator.Value)
|
||||
@@ -220,123 +224,123 @@ additionalProperties: true `
|
||||
assert.Equal(t, "party", mv.Value)
|
||||
}
|
||||
|
||||
func TestSchema_BuildLevel_TooDeep(t *testing.T) {
|
||||
clearSchemas()
|
||||
// if you design data models like this, you're doing it fucking wrong. Seriously. why, what is so complex about a model
|
||||
// that it needs to be 30+ levels deep? I have seen this shit in the wild, it's unreadable, un-parsable garbage.
|
||||
yml := `type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object
|
||||
properties:
|
||||
aValue:
|
||||
type: object`
|
||||
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
idx := index.NewSpecIndex(&idxNode)
|
||||
|
||||
var n Schema
|
||||
err := low.BuildModel(&idxNode, &n)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = n.Build(idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
|
||||
}
|
||||
//func TestSchema_BuildLevel_TooDeep(t *testing.T) {
|
||||
// clearSchemas()
|
||||
// // if you design data models like this, you're doing it fucking wrong. Seriously. why, what is so complex about a model
|
||||
// // that it needs to be 30+ levels deep? I have seen this shit in the wild, it's unreadable, un-parsable garbage.
|
||||
// yml := `type: object
|
||||
//properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object
|
||||
// properties:
|
||||
// aValue:
|
||||
// type: object`
|
||||
//
|
||||
// var idxNode yaml.Node
|
||||
// _ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
// idx := index.NewSpecIndex(&idxNode)
|
||||
//
|
||||
// var n Schema
|
||||
// err := low.BuildModel(&idxNode, &n)
|
||||
// assert.NoError(t, err)
|
||||
//
|
||||
// err = n.Build(idxNode.Content[0], idx)
|
||||
// assert.Error(t, err)
|
||||
//
|
||||
//}
|
||||
|
||||
func TestSchema_Build_ErrorAdditionalProps(t *testing.T) {
|
||||
clearSchemas()
|
||||
@@ -380,7 +384,7 @@ properties:
|
||||
var n Schema
|
||||
err := n.Build(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Description.Value)
|
||||
assert.Equal(t, "this is something", n.FindProperty("aValue").Value.Schema().Description.Value)
|
||||
|
||||
}
|
||||
|
||||
@@ -452,11 +456,11 @@ items:
|
||||
assert.NoError(t, schErr)
|
||||
|
||||
desc := "poly thing"
|
||||
assert.Equal(t, desc, sch.OneOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.Not.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.Items.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.OneOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.Not.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.Items.Value[0].Value.Schema().Description.Value)
|
||||
}
|
||||
|
||||
func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) {
|
||||
@@ -542,11 +546,11 @@ items:
|
||||
assert.NoError(t, schErr)
|
||||
|
||||
desc := "poly thing"
|
||||
assert.Equal(t, desc, sch.OneOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.Not.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.Items.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.OneOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.AnyOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.Not.Value[0].Value.Schema().Description.Value)
|
||||
assert.Equal(t, desc, sch.Items.Value[0].Value.Schema().Description.Value)
|
||||
}
|
||||
|
||||
func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) {
|
||||
@@ -649,6 +653,39 @@ allOf:
|
||||
|
||||
}
|
||||
|
||||
func Test_Schema_Polymorphism_BorkChild_Array(t *testing.T) {
|
||||
clearSchemas()
|
||||
|
||||
yml := `components:
|
||||
schemas:
|
||||
Something:
|
||||
$ref: #borko`
|
||||
|
||||
var iNode yaml.Node
|
||||
mErr := yaml.Unmarshal([]byte(yml), &iNode)
|
||||
assert.NoError(t, mErr)
|
||||
idx := index.NewSpecIndex(&iNode)
|
||||
|
||||
yml = `type: object
|
||||
allOf:
|
||||
- type: object
|
||||
allOf:
|
||||
- $ref: #bork'`
|
||||
|
||||
var sch Schema
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
|
||||
err := low.BuildModel(&idxNode, &sch)
|
||||
assert.NoError(t, err)
|
||||
|
||||
schErr := sch.Build(idxNode.Content[0], idx)
|
||||
assert.NoError(t, schErr)
|
||||
assert.Nil(t, sch.AllOf.Value[0].Value.Schema()) // child can't be resolved, so this will be nil.
|
||||
assert.Error(t, sch.AllOf.Value[0].Value.GetBuildError())
|
||||
|
||||
}
|
||||
|
||||
func Test_Schema_Polymorphism_RefMadness(t *testing.T) {
|
||||
clearSchemas()
|
||||
|
||||
@@ -679,7 +716,7 @@ allOf:
|
||||
assert.NoError(t, schErr)
|
||||
|
||||
desc := "madness"
|
||||
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, desc, sch.AllOf.Value[0].Value.Schema().Description.Value)
|
||||
|
||||
}
|
||||
|
||||
@@ -709,8 +746,8 @@ allOf:
|
||||
err := low.BuildModel(&idxNode, &sch)
|
||||
assert.NoError(t, err)
|
||||
|
||||
schErr := sch.Build(idxNode.Content[0], idx)
|
||||
assert.Error(t, schErr)
|
||||
_ = sch.Build(idxNode.Content[0], idx)
|
||||
assert.Nil(t, sch.AllOf.Value[0].Value.Schema())
|
||||
|
||||
}
|
||||
|
||||
@@ -771,8 +808,8 @@ func TestExtractSchema(t *testing.T) {
|
||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res.Value)
|
||||
aValue := res.Value.FindProperty("aValue")
|
||||
assert.Equal(t, "this is something", aValue.Value.Description.Value)
|
||||
aValue := res.Value.Schema().FindProperty("aValue")
|
||||
assert.Equal(t, "this is something", aValue.Value.Schema().Description.Value)
|
||||
}
|
||||
|
||||
func TestExtractSchema_Ref(t *testing.T) {
|
||||
@@ -798,7 +835,7 @@ func TestExtractSchema_Ref(t *testing.T) {
|
||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res.Value)
|
||||
assert.Equal(t, "this is something", res.Value.Description.Value)
|
||||
assert.Equal(t, "this is something", res.Value.Schema().Description.Value)
|
||||
}
|
||||
|
||||
func TestExtractSchema_Ref_Fail(t *testing.T) {
|
||||
@@ -847,7 +884,7 @@ func TestExtractSchema_RefRoot(t *testing.T) {
|
||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, res.Value)
|
||||
assert.Equal(t, "this is something", res.Value.Description.Value)
|
||||
assert.Equal(t, "this is something", res.Value.Schema().Description.Value)
|
||||
}
|
||||
|
||||
func TestExtractSchema_RefRoot_Fail(t *testing.T) {
|
||||
@@ -892,8 +929,11 @@ func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) {
|
||||
var idxNode yaml.Node
|
||||
_ = yaml.Unmarshal([]byte(yml), &idxNode)
|
||||
|
||||
_, err := ExtractSchema(idxNode.Content[0], idx)
|
||||
assert.Error(t, err)
|
||||
s, _ := ExtractSchema(idxNode.Content[0], idx)
|
||||
|
||||
b := s.Value.Schema()
|
||||
assert.Nil(t, b)
|
||||
assert.Error(t, s.Value.GetBuildError())
|
||||
|
||||
}
|
||||
|
||||
@@ -1036,6 +1076,7 @@ func TestExtractSchema_OneOfRef(t *testing.T) {
|
||||
|
||||
res, err := ExtractSchema(idxNode.Content[0], idx)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "a frosty cold beverage can be coke or sprite", res.Value.OneOf.Value[0].Value.Description.Value)
|
||||
assert.Equal(t, "a frosty cold beverage can be coke or sprite",
|
||||
res.Value.Schema().OneOf.Value[0].Value.Schema().Description.Value)
|
||||
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ func ExtractObject[T Buildable[N], N any](label string, root *yaml.Node, idx *in
|
||||
vn = ref
|
||||
} else {
|
||||
return NodeReference[T]{}, fmt.Errorf("object build failed: reference cannot be found: %s",
|
||||
root.Content[1].Value)
|
||||
vn.Content[1].Value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
datamodel/low/log.go
Normal file
12
datamodel/low/log.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package low
|
||||
|
||||
import "go.uber.org/zap"
|
||||
|
||||
var Log *zap.Logger
|
||||
|
||||
func init() {
|
||||
Log, _ = zap.NewProduction()
|
||||
}
|
||||
3
go.mod
3
go.mod
@@ -13,4 +13,7 @@ require (
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect
|
||||
github.com/iancoleman/strcase v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.23.0 // indirect
|
||||
)
|
||||
|
||||
15
go.sum
15
go.sum
@@ -7,6 +7,10 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
||||
github.com/dprotaso/go-yit v0.0.0-20191028211022-135eb7262960/go.mod h1:9HQzr9D/0PGwMEbC3d5AB7oi67+h4TsQqItC1GVYG58=
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 h1:PRxIJD8XjimM5aTknUK9w6DHLDox2r2M3DI4i2pnd3w=
|
||||
github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936/go.mod h1:ttYvX5qlB+mlV1okblJqcSMtR4c52UKxDiX9GRBS8+Q=
|
||||
github.com/francoispqt/gojay v0.0.0-20181220093123-f2cc13a668ca h1:F2BD6Vhei4w0rtm4eNpzylNsB07CcCbpYA+xlqMx3mA=
|
||||
github.com/francoispqt/gojay v0.0.0-20181220093123-f2cc13a668ca/go.mod h1:H8Wgri1Asi1VevY3ySdpIK5+KCpqzToVswNq8g2xZj4=
|
||||
github.com/francoispqt/onelog v0.0.0-20190306043706-8c2bb31b10a4 h1:N9eG+1y9e3tnNPXKjssLMa8MumIBDWWoJQWM7htGWUc=
|
||||
github.com/francoispqt/onelog v0.0.0-20190306043706-8c2bb31b10a4/go.mod h1:v1Il1fkBpjiYPpEJcGxqgrPUPcHuTC7eHh9zBV3CLBE=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
@@ -57,14 +61,24 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk=
|
||||
github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
|
||||
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
|
||||
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
|
||||
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@@ -131,5 +145,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20191026110619-0b21df46bc1d/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
Reference in New Issue
Block a user