Working through the model builder

Shaping out the design as I go, what makes sence, what feels right, what do we need, how do we want to use it and how to we want to search it etc.
This commit is contained in:
Dave Shanley
2022-07-30 15:27:21 -04:00
parent ecc7b20b0a
commit 7535cf568c
13 changed files with 290 additions and 124 deletions

View File

@@ -2,11 +2,9 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3"
) )
type Contact struct { type Contact struct {
Node *yaml.Node
Name low.NodeReference[string] Name low.NodeReference[string]
URL low.NodeReference[string] URL low.NodeReference[string]
Email low.NodeReference[string] Email low.NodeReference[string]

View File

@@ -1,48 +1,17 @@
package v3 package v3
import ( import (
"fmt"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3"
"reflect"
) )
type Document struct { type Document struct {
Version string Version low.NodeReference[string]
Info Info Info low.NodeReference[*Info]
Servers []Server Servers []low.NodeReference[*Server]
Paths Paths Paths *Paths
Components Components Components *Components
Security []SecurityRequirement Security []*SecurityRequirement
Tags []Tag Tags []*Tag
ExternalDocs ExternalDoc ExternalDocs *ExternalDoc
Extensions map[string]low.ObjectReference Extensions map[string]low.ObjectReference
} }
func (d Document) Build(node *yaml.Node) {
doc := Document{
Version: "",
Info: Info{},
Servers: nil,
Paths: Paths{},
Components: Components{},
Security: nil,
Tags: nil,
ExternalDocs: ExternalDoc{},
Extensions: nil,
}
var j interface{}
j = doc
t := reflect.TypeOf(j)
v := reflect.ValueOf(j)
k := t.Kind()
fmt.Println("Type ", t)
fmt.Println("Value ", v)
fmt.Println("Kind ", k)
for i := 0; i < v.NumField(); i++ {
fmt.Printf("Field:%d type:%T value:%v\n", i, v.Field(i), v.Field(i))
}
}

View File

@@ -1,16 +1,36 @@
package v3 package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"sync"
) )
type Info struct { type Info struct {
Node *yaml.Node
Title low.NodeReference[string] Title low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
TermsOfService low.NodeReference[string] TermsOfService low.NodeReference[string]
Contact Contact Contact low.NodeReference[*Contact]
License License License low.NodeReference[*License]
Version low.NodeReference[string] Version low.NodeReference[string]
} }
func (i *Info) Build(root *yaml.Node) error {
var wg sync.WaitGroup
wg.Add(2)
var errs []error
contact := Contact{}
_, kln, cn := utils.FindKeyNodeFull("contact", root.Content)
go datamodel.BuildModelAsync(cn, &contact, &wg, &errs)
license := License{}
_, kln, ln := utils.FindKeyNodeFull("license", root.Content)
go datamodel.BuildModelAsync(ln, &license, &wg, &errs)
wg.Wait()
i.Contact = low.NodeReference[*Contact]{Value: &contact, ValueNode: cn, KeyNode: kln}
i.License = low.NodeReference[*License]{Value: &license, ValueNode: ln, KeyNode: kln}
return nil
}

View File

@@ -2,11 +2,9 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3"
) )
type License struct { type License struct {
Node *yaml.Node
Name low.NodeReference[string] Name low.NodeReference[string]
URL low.NodeReference[string] URL low.NodeReference[string]
} }

View File

@@ -1,9 +1,49 @@
package v3 package v3
import "github.com/pb33f/libopenapi/datamodel/low" import (
"github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3"
)
type Server struct { type Server struct {
URL low.NodeReference[string] URL low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]
Variables map[string]ServerVariable Variables low.NodeReference[*map[string]low.NodeReference[*ServerVariable]]
}
func (s *Server) Build(root *yaml.Node) error {
kn, vars := utils.FindKeyNode("variables", root.Content)
if vars == nil {
return nil
}
variablesMap := make(map[string]low.NodeReference[*ServerVariable])
if utils.IsNodeMap(vars) {
var currentNode string
var keyNode *yaml.Node
for i, varNode := range vars.Content {
if i%2 == 0 {
currentNode = varNode.Value
keyNode = varNode
continue
}
variable := ServerVariable{}
err := datamodel.BuildModel(varNode, &variable)
if err != nil {
return err
}
variablesMap[currentNode] = low.NodeReference[*ServerVariable]{
ValueNode: varNode,
KeyNode: keyNode,
Value: &variable,
}
}
s.Variables = low.NodeReference[*map[string]low.NodeReference[*ServerVariable]]{
KeyNode: kn,
ValueNode: vars,
Value: &variablesMap,
}
}
return nil
} }

View File

@@ -2,11 +2,9 @@ package v3
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"gopkg.in/yaml.v3"
) )
type ServerVariable struct { type ServerVariable struct {
Node *yaml.Node
Enum []low.NodeReference[string] Enum []low.NodeReference[string]
Default low.NodeReference[string] Default low.NodeReference[string]
Description low.NodeReference[string] Description low.NodeReference[string]

View File

@@ -7,15 +7,17 @@ type HasNode interface {
} }
type Buildable interface { type Buildable interface {
Build(node *yaml.Node) Build(node *yaml.Node) error
} }
type NodeReference[T comparable] struct { type NodeReference[T any] struct {
Value T Value T
Node *yaml.Node ValueNode *yaml.Node
KeyNode *yaml.Node
} }
type ObjectReference struct { type ObjectReference struct {
Value map[string]interface{} Value map[string]interface{}
Node *yaml.Node ValueNode *yaml.Node
KeyNode *yaml.Node
} }

View File

@@ -1,11 +1,13 @@
package utils package datamodel
import ( import (
"fmt" "fmt"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"reflect" "reflect"
"strconv" "strconv"
"sync"
) )
func BuildModel(node *yaml.Node, model interface{}) error { func BuildModel(node *yaml.Node, model interface{}) error {
@@ -19,12 +21,12 @@ func BuildModel(node *yaml.Node, model interface{}) error {
fName := v.Type().Field(i).Name fName := v.Type().Field(i).Name
// we need to find a matching field in the YAML, the cases may be off, so take no chances. // we need to find a matching field in the YAML, the cases may be off, so take no chances.
cases := []Case{PascalCase, CamelCase, ScreamingSnakeCase, cases := []utils.Case{utils.PascalCase, utils.CamelCase, utils.ScreamingSnakeCase,
SnakeCase, KebabCase, RegularCase} utils.SnakeCase, utils.KebabCase, utils.RegularCase}
var vn, kn *yaml.Node var vn, kn *yaml.Node
for _, tryCase := range cases { for _, tryCase := range cases {
kn, vn = FindKeyNode(ConvertCase(fName, tryCase), node.Content) kn, vn = utils.FindKeyNode(utils.ConvertCase(fName, tryCase), node.Content)
if vn != nil { if vn != nil {
break break
} }
@@ -38,7 +40,7 @@ func BuildModel(node *yaml.Node, model interface{}) error {
field := v.FieldByName(fName) field := v.FieldByName(fName)
kind := field.Kind() kind := field.Kind()
switch kind { switch kind {
case reflect.Struct, reflect.Slice, reflect.Map: case reflect.Struct, reflect.Slice, reflect.Map, reflect.Pointer:
err := SetField(field, vn, kn) err := SetField(field, vn, kn)
if err != nil { if err != nil {
return err return err
@@ -57,7 +59,7 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
case reflect.TypeOf(map[string]low.ObjectReference{}): case reflect.TypeOf(map[string]low.ObjectReference{}):
if valueNode != nil { if valueNode != nil {
if IsNodeMap(valueNode) { if utils.IsNodeMap(valueNode) {
if field.CanSet() { if field.CanSet() {
items := make(map[string]low.ObjectReference) items := make(map[string]low.ObjectReference)
var currentLabel string var currentLabel string
@@ -73,7 +75,8 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
} }
items[currentLabel] = low.ObjectReference{ items[currentLabel] = low.ObjectReference{
Value: decoded, Value: decoded,
Node: sliceItem, ValueNode: sliceItem,
KeyNode: valueNode,
} }
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
@@ -84,7 +87,7 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
case reflect.TypeOf(map[string]low.NodeReference[string]{}): case reflect.TypeOf(map[string]low.NodeReference[string]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeMap(valueNode) { if utils.IsNodeMap(valueNode) {
if field.CanSet() { if field.CanSet() {
items := make(map[string]low.NodeReference[string]) items := make(map[string]low.NodeReference[string])
var currentLabel string var currentLabel string
@@ -95,7 +98,8 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
} }
items[currentLabel] = low.NodeReference[string]{ items[currentLabel] = low.NodeReference[string]{
Value: sliceItem.Value, Value: sliceItem.Value,
Node: sliceItem, ValueNode: sliceItem,
KeyNode: valueNode,
} }
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
@@ -110,9 +114,9 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
if err != nil { if err != nil {
return err return err
} }
if IsNodeMap(valueNode) { if utils.IsNodeMap(valueNode) {
if field.CanSet() { if field.CanSet() {
or := low.ObjectReference{Value: decoded, Node: valueNode} or := low.ObjectReference{Value: decoded, ValueNode: valueNode}
field.Set(reflect.ValueOf(or)) field.Set(reflect.ValueOf(or))
} }
} }
@@ -120,7 +124,7 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.ObjectReference{}): case reflect.TypeOf([]low.ObjectReference{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.ObjectReference var items []low.ObjectReference
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
@@ -131,7 +135,8 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
} }
items = append(items, low.ObjectReference{ items = append(items, low.ObjectReference{
Value: decoded, Value: decoded,
Node: sliceItem, ValueNode: sliceItem,
KeyNode: valueNode,
}) })
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
@@ -141,9 +146,13 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf(low.NodeReference[string]{}): case reflect.TypeOf(low.NodeReference[string]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeStringValue(valueNode) { if utils.IsNodeStringValue(valueNode) {
if field.CanSet() { if field.CanSet() {
nr := low.NodeReference[string]{Value: valueNode.Value, Node: valueNode} nr := low.NodeReference[string]{
Value: valueNode.Value,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr)) field.Set(reflect.ValueOf(nr))
} }
} }
@@ -151,10 +160,14 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf(low.NodeReference[bool]{}): case reflect.TypeOf(low.NodeReference[bool]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeBoolValue(valueNode) { if utils.IsNodeBoolValue(valueNode) {
if field.CanSet() { if field.CanSet() {
bv, _ := strconv.ParseBool(valueNode.Value) bv, _ := strconv.ParseBool(valueNode.Value)
nr := low.NodeReference[bool]{Value: bv, Node: valueNode} nr := low.NodeReference[bool]{
Value: bv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr)) field.Set(reflect.ValueOf(nr))
} }
} }
@@ -162,10 +175,14 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf(low.NodeReference[int]{}): case reflect.TypeOf(low.NodeReference[int]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeIntValue(valueNode) { if utils.IsNodeIntValue(valueNode) {
if field.CanSet() { if field.CanSet() {
fv, _ := strconv.Atoi(valueNode.Value) fv, _ := strconv.Atoi(valueNode.Value)
nr := low.NodeReference[int]{Value: fv, Node: valueNode} nr := low.NodeReference[int]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr)) field.Set(reflect.ValueOf(nr))
} }
} }
@@ -173,10 +190,14 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf(low.NodeReference[int64]{}): case reflect.TypeOf(low.NodeReference[int64]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeIntValue(valueNode) || IsNodeFloatValue(valueNode) { // if utils.IsNodeIntValue(valueNode) || utils.IsNodeFloatValue(valueNode) { //
if field.CanSet() { if field.CanSet() {
fv, _ := strconv.ParseInt(valueNode.Value, 10, 64) fv, _ := strconv.ParseInt(valueNode.Value, 10, 64)
nr := low.NodeReference[int64]{Value: fv, Node: valueNode} nr := low.NodeReference[int64]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr)) field.Set(reflect.ValueOf(nr))
} }
} }
@@ -184,10 +205,14 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf(low.NodeReference[float32]{}): case reflect.TypeOf(low.NodeReference[float32]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeFloatValue(valueNode) { if utils.IsNodeFloatValue(valueNode) {
if field.CanSet() { if field.CanSet() {
fv, _ := strconv.ParseFloat(valueNode.Value, 32) fv, _ := strconv.ParseFloat(valueNode.Value, 32)
nr := low.NodeReference[float32]{Value: float32(fv), Node: valueNode} nr := low.NodeReference[float32]{
Value: float32(fv),
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr)) field.Set(reflect.ValueOf(nr))
} }
} }
@@ -195,10 +220,14 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf(low.NodeReference[float64]{}): case reflect.TypeOf(low.NodeReference[float64]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeFloatValue(valueNode) { if utils.IsNodeFloatValue(valueNode) {
if field.CanSet() { if field.CanSet() {
fv, _ := strconv.ParseFloat(valueNode.Value, 64) fv, _ := strconv.ParseFloat(valueNode.Value, 64)
nr := low.NodeReference[float64]{Value: fv, Node: valueNode} nr := low.NodeReference[float64]{
Value: fv,
ValueNode: valueNode,
KeyNode: keyNode,
}
field.Set(reflect.ValueOf(nr)) field.Set(reflect.ValueOf(nr))
} }
} }
@@ -206,11 +235,15 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.NodeReference[string]{}): case reflect.TypeOf([]low.NodeReference[string]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.NodeReference[string] var items []low.NodeReference[string]
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
items = append(items, low.NodeReference[string]{Value: sliceItem.Value, Node: sliceItem}) items = append(items, low.NodeReference[string]{
Value: sliceItem.Value,
ValueNode: sliceItem,
KeyNode: valueNode,
})
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
} }
@@ -219,12 +252,16 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.NodeReference[float32]{}): case reflect.TypeOf([]low.NodeReference[float32]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.NodeReference[float32] var items []low.NodeReference[float32]
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
fv, _ := strconv.ParseFloat(sliceItem.Value, 32) fv, _ := strconv.ParseFloat(sliceItem.Value, 32)
items = append(items, low.NodeReference[float32]{Value: float32(fv), Node: sliceItem}) items = append(items, low.NodeReference[float32]{
Value: float32(fv),
ValueNode: sliceItem,
KeyNode: valueNode,
})
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
} }
@@ -233,12 +270,12 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.NodeReference[float64]{}): case reflect.TypeOf([]low.NodeReference[float64]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.NodeReference[float64] var items []low.NodeReference[float64]
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
fv, _ := strconv.ParseFloat(sliceItem.Value, 64) fv, _ := strconv.ParseFloat(sliceItem.Value, 64)
items = append(items, low.NodeReference[float64]{Value: fv, Node: sliceItem}) items = append(items, low.NodeReference[float64]{Value: fv, ValueNode: sliceItem})
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
} }
@@ -247,12 +284,16 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.NodeReference[int]{}): case reflect.TypeOf([]low.NodeReference[int]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.NodeReference[int] var items []low.NodeReference[int]
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
iv, _ := strconv.Atoi(sliceItem.Value) iv, _ := strconv.Atoi(sliceItem.Value)
items = append(items, low.NodeReference[int]{Value: iv, Node: sliceItem}) items = append(items, low.NodeReference[int]{
Value: iv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
} }
@@ -261,12 +302,16 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.NodeReference[int64]{}): case reflect.TypeOf([]low.NodeReference[int64]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.NodeReference[int64] var items []low.NodeReference[int64]
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64) iv, _ := strconv.ParseInt(sliceItem.Value, 10, 64)
items = append(items, low.NodeReference[int64]{Value: iv, Node: sliceItem}) items = append(items, low.NodeReference[int64]{
Value: iv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
} }
@@ -275,12 +320,16 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
break break
case reflect.TypeOf([]low.NodeReference[bool]{}): case reflect.TypeOf([]low.NodeReference[bool]{}):
if valueNode != nil { if valueNode != nil {
if IsNodeArray(valueNode) { if utils.IsNodeArray(valueNode) {
if field.CanSet() { if field.CanSet() {
var items []low.NodeReference[bool] var items []low.NodeReference[bool]
for _, sliceItem := range valueNode.Content { for _, sliceItem := range valueNode.Content {
bv, _ := strconv.ParseBool(sliceItem.Value) bv, _ := strconv.ParseBool(sliceItem.Value)
items = append(items, low.NodeReference[bool]{Value: bv, Node: sliceItem}) items = append(items, low.NodeReference[bool]{
Value: bv,
ValueNode: sliceItem,
KeyNode: valueNode,
})
} }
field.Set(reflect.ValueOf(items)) field.Set(reflect.ValueOf(items))
} }
@@ -288,8 +337,18 @@ func SetField(field reflect.Value, valueNode *yaml.Node, keyNode *yaml.Node) err
} }
break break
default: default:
m := field.Type() // we want to ignore everything else.
return fmt.Errorf("unknown type, cannot parse: %v", m) break
} }
return nil return nil
} }
func BuildModelAsync(n *yaml.Node, model interface{}, lwg *sync.WaitGroup, errors *[]error) {
if n != nil {
err := BuildModel(n, model)
if err != nil {
*errors = append(*errors, err)
}
}
lwg.Done()
}

View File

@@ -1,4 +1,4 @@
package utils package datamodel
import ( import (
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
@@ -106,7 +106,7 @@ there:
hd := hotdog{} hd := hotdog{}
cErr := BuildModel(&rootNode, &hd) cErr := BuildModel(&rootNode, &hd)
assert.Equal(t, 200, hd.Fat.Value) assert.Equal(t, 200, hd.Fat.Value)
assert.Equal(t, 3, hd.Fat.Node.Line) assert.Equal(t, 3, hd.Fat.ValueNode.Line)
assert.Equal(t, true, hd.Grilled.Value) assert.Equal(t, true, hd.Grilled.Value)
assert.Equal(t, "yummy", hd.Name.Value) assert.Equal(t, "yummy", hd.Name.Value)
assert.Equal(t, float32(200.45), hd.Ketchup.Value) assert.Equal(t, float32(200.45), hd.Ketchup.Value)
@@ -119,7 +119,7 @@ there:
assert.Len(t, hd.MaxTempAlt, 5) assert.Len(t, hd.MaxTempAlt, 5)
assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value) assert.Equal(t, int64(7392837462032342), hd.MaxTempHigh.Value)
assert.Equal(t, 2, hd.Temps[1].Value) assert.Equal(t, 2, hd.Temps[1].Value)
assert.Equal(t, 26, hd.Temps[1].Node.Line) assert.Equal(t, 26, hd.Temps[1].ValueNode.Line)
assert.Len(t, hd.UnknownElements.Value, 2) assert.Len(t, hd.UnknownElements.Value, 2)
assert.Len(t, hd.LotsOfUnknowns, 3) assert.Len(t, hd.LotsOfUnknowns, 3)
assert.Len(t, hd.Where, 2) assert.Len(t, hd.Where, 2)
@@ -129,24 +129,6 @@ there:
assert.NoError(t, cErr) assert.NoError(t, cErr)
} }
func TestBuildModel_UnsupportedType(t *testing.T) {
type notSupported struct {
cake low.NodeReference[uintptr]
}
ns := notSupported{}
yml := `cake: -99999`
var rootNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &rootNode)
assert.NoError(t, mErr)
cErr := BuildModel(&rootNode, &ns)
assert.Error(t, cErr)
assert.Nil(t, ns.cake)
}
func TestBuildModel_UseCopyNotRef(t *testing.T) { func TestBuildModel_UseCopyNotRef(t *testing.T) {
yml := `cake: -99999` yml := `cake: -99999`

View File

@@ -2,7 +2,9 @@ package openapi
import ( import (
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low"
v3 "github.com/pb33f/libopenapi/datamodel/low/3.0" v3 "github.com/pb33f/libopenapi/datamodel/low/3.0"
"github.com/pb33f/libopenapi/utils"
) )
func CreateDocument(spec []byte) (*v3.Document, error) { func CreateDocument(spec []byte) (*v3.Document, error) {
@@ -13,7 +15,64 @@ func CreateDocument(spec []byte) (*v3.Document, error) {
return nil, err return nil, err
} }
doc := &v3.Document{} doc := v3.Document{Version: low.NodeReference[string]{Value: info.Version, ValueNode: info.RootNode}}
doc.Build(info.RootNode.Content[0])
return doc, nil // build an index
//idx := index.NewSpecIndex(info.RootNode)
datamodel.BuildModel(info.RootNode.Content[0], &doc)
// extract info
extractErr := extractInfo(info, &doc)
if extractErr != nil {
return nil, extractErr
}
// extract servers
extractErr = extractServers(info, &doc)
if extractErr != nil {
return nil, extractErr
}
return &doc, nil
}
func extractInfo(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull("info", info.RootNode.Content)
if vn != nil {
ir := v3.Info{}
err := datamodel.BuildModel(vn, &ir)
if err != nil {
return err
}
err = ir.Build(vn)
nr := low.NodeReference[*v3.Info]{Value: &ir, ValueNode: vn, KeyNode: ln}
doc.Info = nr
}
return nil
}
func extractServers(info *datamodel.SpecInfo, doc *v3.Document) error {
_, ln, vn := utils.FindKeyNodeFull("servers", info.RootNode.Content)
if vn != nil {
if utils.IsNodeArray(vn) {
var servers []low.NodeReference[*v3.Server]
for _, srvN := range vn.Content {
if utils.IsNodeMap(srvN) {
srvr := v3.Server{}
err := datamodel.BuildModel(srvN, &srvr)
if err != nil {
return err
}
srvr.Build(srvN)
servers = append(servers, low.NodeReference[*v3.Server]{
Value: &srvr,
ValueNode: srvN,
KeyNode: ln,
})
}
}
doc.Servers = servers
}
}
return nil
} }

View File

@@ -13,7 +13,7 @@ func TestCreateDocument_NoData(t *testing.T) {
} }
func TestCreateDocument(t *testing.T) { func TestCreateDocument(t *testing.T) {
data, aErr := ioutil.ReadFile("../test_specs/petstorev3.json") data, aErr := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
assert.NoError(t, aErr) assert.NoError(t, aErr)
doc, err := CreateDocument(data) doc, err := CreateDocument(data)

View File

@@ -6,8 +6,11 @@ info:
termsOfService: https://quobix.com termsOfService: https://quobix.com
contact: contact:
name: quobix name: quobix
email: test@quobix.com
url: https://quobix.com
license: license:
name: Quobix name: Quobix
url: https://quobix.com/made-up
version: "1.2" version: "1.2"
tags: tags:
- name: "Burgers" - name: "Burgers"
@@ -21,7 +24,22 @@ tags:
description: "Find out more information about our products)" description: "Find out more information about our products)"
url: "https://quobix.com/" url: "https://quobix.com/"
servers: servers:
- url: https://quobix.com/api - url: "{scheme}://api.quobix.com"
description: "this is our main API server, for all fun API things."
variables:
scheme:
enum: [https]
default: https
description: this is a server variable for the scheme
- url: "https://{domain}.{host}.com"
description: "this is our second API server, for all fun API things."
variables:
domain:
default: "api"
description: the default API domain is 'api'
host:
default: "quobix.com"
description: the default host for this API is 'quobix.com'
paths: paths:
/burgers: /burgers:
post: post:

View File

@@ -239,6 +239,29 @@ func FindKeyNode(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode
return nil, nil return nil, nil
} }
func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) {
for i, v := range nodes {
if i%2 == 0 && key == v.Value {
return v, nodes[i], nodes[i+1] // next node is what we need.
}
for x, j := range v.Content {
if key == j.Value {
if IsNodeMap(v) {
if x+1 == len(v.Content) {
return v, v.Content[x], v.Content[x]
}
return v, v.Content[x], v.Content[x+1] // next node is what we need.
}
if IsNodeArray(v) {
return v, v.Content[x], v.Content[x]
}
}
}
}
return nil, nil, nil
}
var ObjectLabel = "object" var ObjectLabel = "object"
var IntegerLabel = "integer" var IntegerLabel = "integer"
var NumberLabel = "number" var NumberLabel = "number"