fixed debugTrace

This commit is contained in:
Shreyas Goenka 2023-01-13 20:03:24 +01:00
parent 1ee35c3633
commit 276ba00067
No known key found for this signature in database
GPG Key ID: 92A07DF49CCB0622
2 changed files with 63 additions and 32 deletions

View File

@ -31,11 +31,11 @@ type Item struct {
} }
func NewSchema(golangType reflect.Type) (*Schema, error) { func NewSchema(golangType reflect.Type) (*Schema, error) {
traceSet := map[reflect.Type]struct{}{} seenTypes := map[reflect.Type]struct{}{}
trace := list.New() debugTrace := list.New()
rootProp, err := toProperity(golangType, traceSet, trace) rootProp, err := toProperity(golangType, seenTypes, debugTrace)
if err != nil { if err != nil {
return nil, err return nil, errWithTrace(err.Error(), debugTrace)
} }
return &Schema{ return &Schema{
Type: rootProp.Type, Type: rootProp.Type,
@ -46,15 +46,15 @@ func NewSchema(golangType reflect.Type) (*Schema, error) {
// TODO: add tests for errors being triggered // TODO: add tests for errors being triggered
type JavascriptType int type JavascriptType string
const ( const (
Invalid JavascriptType = iota Invalid JavascriptType = "invalid"
Boolean Boolean JavascriptType = "boolean"
String String JavascriptType = "string"
Number Number JavascriptType = "number"
Object Object JavascriptType = "object"
Array Array JavascriptType = "array"
) )
func javascriptType(golangType reflect.Type) (JavascriptType, error) { func javascriptType(golangType reflect.Type) (JavascriptType, error) {
@ -84,13 +84,13 @@ func javascriptType(golangType reflect.Type) (JavascriptType, error) {
// TODO: add a simple test for this // TODO: add a simple test for this
func errWithTrace(prefix string, trace *list.List) error { func errWithTrace(prefix string, trace *list.List) error {
traceString := "" traceString := "root"
curr := trace.Front() curr := trace.Front()
for curr.Next() != nil { for curr.Next() != nil {
traceString += " -> " + curr.Value.(reflect.Type).Name() traceString += " -> " + curr.Value.(string)
curr = curr.Next() curr = curr.Next()
} }
return fmt.Errorf("[ERROR] " + prefix + " type traveral trace: " + traceString) return fmt.Errorf("[ERROR] " + prefix + ". traveral trace: " + traceString)
} }
// TODO: handle case of self referential pointers in structs // TODO: handle case of self referential pointers in structs
@ -100,25 +100,22 @@ func errWithTrace(prefix string, trace *list.List) error {
// checks and errors out for cycles // checks and errors out for cycles
// wraps the error with context // wraps the error with context
func safeToProperty(golangType reflect.Type, traceSet map[reflect.Type]struct{}, trace *list.List) (*Property, error) { func safeToProperty(golangType reflect.Type, seenTypes map[reflect.Type]struct{}, debugTrace *list.List) (*Property, error) {
trace.PushBack(golangType)
// detect cycles. Fail if a cycle is detected // detect cycles. Fail if a cycle is detected
// TODO: Add references here for cycles // TODO: Add references here for cycles
// TODO: move this check somewhere nicer // TODO: move this check somewhere nicer
_, ok := traceSet[golangType] _, ok := seenTypes[golangType]
if ok { if ok {
fmt.Println("[DEBUG] traceSet: ", traceSet) fmt.Println("[DEBUG] traceSet: ", seenTypes)
return nil, errWithTrace("cycle detected", trace) return nil, fmt.Errorf("cycle detected")
} }
// add current child field to history // add current child field to history
traceSet[golangType] = struct{}{} seenTypes[golangType] = struct{}{}
props, err := toProperity(golangType, traceSet, trace) props, err := toProperity(golangType, seenTypes, debugTrace)
if err != nil { if err != nil {
return nil, errWithTrace(err.Error(), trace) return nil, err
} }
delete(traceSet, golangType) delete(seenTypes, golangType)
back := trace.Back()
trace.Remove(back)
return props, nil return props, nil
} }
@ -155,11 +152,19 @@ func addStructFields(fields []reflect.StructField, golangType reflect.Type) []re
return fields return fields
} }
// TODO: add ignore for -
// TODO: add doc string explaining numHistoryOccurances // TODO: add doc string explaining numHistoryOccurances
func toProperity(golangType reflect.Type, traceSet map[reflect.Type]struct{}, trace *list.List) (*Property, error) { func toProperity(golangType reflect.Type, seenTypes map[reflect.Type]struct{}, debugTrace *list.List) (*Property, error) {
// *Struct and Struct generate identical json schemas // *Struct and Struct generate identical json schemas
// TODO: add test case for pointer
if golangType.Kind() == reflect.Pointer { if golangType.Kind() == reflect.Pointer {
return toProperity(golangType.Elem(), traceSet, trace) return toProperity(golangType.Elem(), seenTypes, debugTrace)
}
// TODO: add test case for interfaces
if golangType.Kind() == reflect.Interface {
return nil, nil
} }
rootJavascriptType, err := javascriptType(golangType) rootJavascriptType, err := javascriptType(golangType)
@ -175,7 +180,7 @@ func toProperity(golangType reflect.Type, traceSet map[reflect.Type]struct{}, tr
if err != nil { if err != nil {
return nil, err return nil, err
} }
elemProps, err := safeToProperty(elemGolangType, traceSet, trace) elemProps, err := safeToProperty(elemGolangType, seenTypes, debugTrace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -190,9 +195,9 @@ func toProperity(golangType reflect.Type, traceSet map[reflect.Type]struct{}, tr
var additionalProperties *Property var additionalProperties *Property
if golangType.Kind() == reflect.Map { if golangType.Kind() == reflect.Map {
if golangType.Key().Kind() != reflect.String { if golangType.Key().Kind() != reflect.String {
return nil, errWithTrace("only string keyed maps allowed", trace) return nil, fmt.Errorf("only string keyed maps allowed")
} }
additionalProperties, err = safeToProperty(golangType.Elem(), traceSet, trace) additionalProperties, err = safeToProperty(golangType.Elem(), seenTypes, debugTrace)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -208,17 +213,24 @@ func toProperity(golangType reflect.Type, traceSet map[reflect.Type]struct{}, tr
childJsonTag := child.Tag.Get("json") childJsonTag := child.Tag.Get("json")
childName := strings.Split(childJsonTag, ",")[0] childName := strings.Split(childJsonTag, ",")[0]
// add current field to debug trace
debugTrace.PushBack(childName)
// skip non json annotated fields // skip non json annotated fields
if childName == "" { if childName == "" {
continue continue
} }
// recursively compute properties for this child field // recursively compute properties for this child field
fieldProps, err := safeToProperty(child.Type, traceSet, trace) fieldProps, err := safeToProperty(child.Type, seenTypes, debugTrace)
if err != nil { if err != nil {
return nil, errWithTrace(err.Error(), trace) return nil, err
} }
properities[childName] = fieldProps properities[childName] = fieldProps
// remove current field from debug trace
back := debugTrace.Back()
debugTrace.Remove(back)
} }
} }

View File

@ -5,6 +5,7 @@ import (
"reflect" "reflect"
"testing" "testing"
"github.com/databricks/bricks/bundle/config"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@ -337,3 +338,21 @@ func TestEmbeddedStructSchema(t *testing.T) {
t.Log("[DEBUG] expected: ", expected) t.Log("[DEBUG] expected: ", expected)
assert.Equal(t, expected, string(jsonSchema)) assert.Equal(t, expected, string(jsonSchema))
} }
// Only for testing bundle, will be removed
func TestBundleSchema(t *testing.T) {
elem := config.Root{}
schema, err := NewSchema(reflect.TypeOf(elem))
assert.NoError(t, err)
jsonSchema, err := json.MarshalIndent(schema, " ", " ")
assert.NoError(t, err)
expected :=
``
t.Log("[DEBUG] actual: ", string(jsonSchema))
t.Log("[DEBUG] expected: ", expected)
assert.Equal(t, expected, string(jsonSchema))
}