From 1efe201bf81e6184b4bfe11077fa974fd7168aa7 Mon Sep 17 00:00:00 2001 From: Shawn Wang Date: Fri, 7 Jun 2024 00:29:29 -0700 Subject: [PATCH] Add support of multiple tf schema sharing one sdk attr This change adds support of the "flat" polymorphic list conversion, where different type in a polymorphic SDK list is combined and mapped to a separate tf schema attr. Signed-off-by: Shawn Wang --- nsxt/metadata/metadata.go | 223 ++++++++++++++++++++-------- nsxt/metadata/metadata_poly_test.go | 215 ++++++++++++++++++++++++--- 2 files changed, 354 insertions(+), 84 deletions(-) diff --git a/nsxt/metadata/metadata.go b/nsxt/metadata/metadata.go index 58ab36511..974481e6d 100644 --- a/nsxt/metadata/metadata.go +++ b/nsxt/metadata/metadata.go @@ -17,7 +17,6 @@ import ( var logger = log.New(os.Stderr, "", log.LstdFlags|log.Lshortfile) const ( - PolymorphicTypeStatic = "static" PolymorphicTypeFlat = "flat" PolymorphicTypeWrapped = "wrapped" ) @@ -35,7 +34,11 @@ type Metadata struct { PolymorphicType string // SDK vapi binding type for converting polymorphic structs BindingType vapiBindings_.BindingType + // SDK resource type to match and filter for a schema key + // Only applicable to PolymorphicTypeFlat schema + ResourceType string // Map from schema key of polymorphic attr to this SDK resource type + // Only applicable to PolymorphicTypeWrapped schema ResourceTypeMap map[string]string // Type identifier name for both SDK and JSON (StructValue field name) TypeIdentifier TypeIdentifier @@ -164,8 +167,14 @@ func StructToSchema(elem reflect.Value, d *schema.ResourceData, metadata map[str if len(item.Metadata.PolymorphicType) > 0 { childElem := elem.FieldByName(item.Metadata.SdkFieldName) var nestedVal interface{} - if item.Metadata.PolymorphicType == PolymorphicTypeWrapped { + switch item.Metadata.PolymorphicType { + case PolymorphicTypeWrapped: nestedVal, err = polyStructToWrappedSchema(ctx, childElem, item) + case PolymorphicTypeFlat: + nestedVal, err = polyStructToFlatSchema(ctx, childElem, key, item) + default: + err = fmt.Errorf("%s unknown polymorphic type %s", ctx, + item.Metadata.PolymorphicType) } if err != nil { return @@ -276,22 +285,15 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str logger.Printf("[TRACE] %s parent %s key %s", ctx, parent, key) } if len(item.Metadata.PolymorphicType) > 0 { - var itemList []interface{} - if item.Metadata.SchemaType == "list" || item.Metadata.SchemaType == "struct" { - if len(parent) > 0 { - itemList = parentMap[key].([]interface{}) - } else { - itemList = d.Get(key).([]interface{}) - } - } else { - if len(parent) > 0 { - itemList = parentMap[key].(*schema.Set).List() - } else { - itemList = d.Get(key).(*schema.Set).List() - } - } - if item.Metadata.PolymorphicType == PolymorphicTypeWrapped { + itemList := getItemListForSchemaToStruct(d, item.Metadata.SchemaType, key, parent, parentMap) + switch item.Metadata.PolymorphicType { + case PolymorphicTypeWrapped: err = polyWrappedSchemaToStruct(ctx, elem, itemList, item) + case PolymorphicTypeFlat: + err = polyFlatSchemaToStruct(ctx, elem, key, itemList, item) + default: + err = fmt.Errorf("%s unknown polymorphic type %s", ctx, + item.Metadata.PolymorphicType) } if err != nil { return @@ -330,12 +332,7 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str } if item.Metadata.SchemaType == "struct" { nestedObj := reflect.New(item.Metadata.ReflectType) - var itemList []interface{} - if len(parent) > 0 { - itemList = parentMap[key].([]interface{}) - } else { - itemList = d.Get(key).([]interface{}) - } + itemList := getItemListForSchemaToStruct(d, item.Metadata.SchemaType, key, parent, parentMap) if len(itemList) == 0 { continue } @@ -349,21 +346,7 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str elem.FieldByName(item.Metadata.SdkFieldName).Set(nestedObj) } if item.Metadata.SchemaType == "list" || item.Metadata.SchemaType == "set" { - var itemList []interface{} - if item.Metadata.SchemaType == "list" { - if len(parent) > 0 { - itemList = parentMap[key].([]interface{}) - } else { - itemList = d.Get(key).([]interface{}) - } - } else { - if len(parent) > 0 { - itemList = parentMap[key].(*schema.Set).List() - } else { - itemList = d.Get(key).(*schema.Set).List() - } - } - + itemList := getItemListForSchemaToStruct(d, item.Metadata.SchemaType, key, parent, parentMap) // List of string, bool, int if childElem, ok := item.Schema.Elem.(*ExtendedSchema); ok { sliceElem := elem.FieldByName(item.Metadata.SdkFieldName) @@ -410,9 +393,53 @@ func SchemaToStruct(elem reflect.Value, d *schema.ResourceData, metadata map[str return } +func getItemListForSchemaToStruct(d *schema.ResourceData, schemaType, key, parent string, parentMap map[string]interface{}) []interface{} { + var itemList []interface{} + if schemaType == "list" || schemaType == "struct" { + if len(parent) > 0 { + itemList = parentMap[key].([]interface{}) + } else { + itemList = d.Get(key).([]interface{}) + } + } else if schemaType == "set" { + if len(parent) > 0 { + itemList = parentMap[key].(*schema.Set).List() + } else { + itemList = d.Get(key).(*schema.Set).List() + } + } + return itemList +} + +// getResourceTypeFromStructValue returns resource type from SDK object based on identifier +func getResourceTypeFromStructValue(ctx string, value data.StructValue, identifier TypeIdentifier) (string, error) { + if !value.HasField(identifier.GetJsonName()) { + err := fmt.Errorf( + "%s failed to get resource type", ctx) + logger.Printf("[ERROR] %v", err) + return "", err + } + var rTypeData data.DataValue + rTypeData, err := value.Field(identifier.GetJsonName()) + if err != nil { + return "", err + } + if strVal, ok := rTypeData.(*data.StringValue); ok { + return strVal.Value(), nil + } + if opVal, ok := rTypeData.(*data.OptionalValue); ok { + return opVal.String() + } + + err = fmt.Errorf("%s failed to convert resource type %s", + ctx, identifier.GetJsonName()) + logger.Printf("[ERROR] %v", err) + return "", err +} + func polyStructToWrappedSchema(ctx string, elem reflect.Value, item *ExtendedSchema) (ret []map[string]interface{}, err error) { if item.Metadata.PolymorphicType != PolymorphicTypeWrapped { - err = fmt.Errorf("%s polyStructToSchema called on non-polymorphic attr", ctx) + err = fmt.Errorf("%s polyStructToWrappedSchema called on non-wrapped polymorphic attr", ctx) logger.Printf("[ERROR] %v", err) return } @@ -441,32 +468,10 @@ func polyStructToWrappedSchema(ctx string, elem reflect.Value, item *ExtendedSch nestedSchema := make(map[string]interface{}) var key, rType string var childExtSch *ExtendedSchema - - // Get resource type from SDK object - if !childElem.HasField(item.Metadata.TypeIdentifier.GetJsonName()) { - err = fmt.Errorf("%s polyStructToSchema failed to get resource type for %s", - ctx, item.Metadata.SdkFieldName) - logger.Printf("[ERROR] %v", err) - return - } - var rTypeData data.DataValue - rTypeData, err = childElem.Field(item.Metadata.TypeIdentifier.GetJsonName()) + rType, err = getResourceTypeFromStructValue(ctx, childElem, item.Metadata.TypeIdentifier) if err != nil { return } - if strVal, ok := rTypeData.(*data.StringValue); ok { - rType = strVal.Value() - } else if opVal, ok := rTypeData.(*data.OptionalValue); ok { - rType, err = opVal.String() - if err != nil { - return - } - } else { - err = fmt.Errorf("%s polyStructToSchema failed to get resource type for %s", - ctx, item.Metadata.SdkFieldName) - logger.Printf("[ERROR] %v", err) - return - } // Get metadata for the corresponding type for k, v := range item.Metadata.ResourceTypeMap { @@ -505,7 +510,7 @@ func polyStructToWrappedSchema(ctx string, elem reflect.Value, item *ExtendedSch func polyWrappedSchemaToStruct(ctx string, elem reflect.Value, dataList []interface{}, item *ExtendedSchema) (err error) { if item.Metadata.PolymorphicType != PolymorphicTypeWrapped { - err = fmt.Errorf("%s polySchemaToStruct called on non-polymorphic attr", ctx) + err = fmt.Errorf("%s polyWrappedSchemaToStruct called on non-wrapped polymorphic attr", ctx) logger.Printf("[ERROR] %v", err) return } @@ -578,3 +583,95 @@ func polyWrappedSchemaToStruct(ctx string, elem reflect.Value, dataList []interf return } + +func polyStructToFlatSchema(ctx string, elem reflect.Value, key string, item *ExtendedSchema) (ret []map[string]interface{}, err error) { + if item.Metadata.PolymorphicType != PolymorphicTypeFlat { + err = fmt.Errorf("%s polyStructToFlatSchema called on non-flat polymorphic attr", ctx) + logger.Printf("[ERROR] %v", err) + return + } + + _, ok := item.Schema.Elem.(*ExtendedResource) + if !ok { + err = fmt.Errorf("%s polymorphic attr has non-ExtendedResource element", ctx) + logger.Printf("[ERROR] %v", err) + return + } + + converter := vapiBindings_.NewTypeConverter() + sliceValue := make([]map[string]interface{}, 0) + for i := 0; i < elem.Len(); i++ { + childValue := elem.Index(i).Elem().Interface().(data.StructValue) + nestedSchema := make(map[string]interface{}) + var rType string + + rType, err = getResourceTypeFromStructValue(ctx, childValue, item.Metadata.TypeIdentifier) + if err != nil { + return + } + if rType != item.Metadata.ResourceType { + continue + } + + dv, errors := converter.ConvertToGolang(&childValue, item.Metadata.BindingType) + if errors != nil { + err = errors[0] + logger.Printf("[ERROR] %v", err) + return + } + if err = StructToSchema(reflect.ValueOf(dv), nil, item.Schema.Elem.(*ExtendedResource).Schema, key, nestedSchema); err != nil { + return + } + sliceValue = append(sliceValue, nestedSchema) + } + + ret = sliceValue + return +} + +func polyFlatSchemaToStruct(ctx string, elem reflect.Value, key string, dataList []interface{}, item *ExtendedSchema) (err error) { + if item.Metadata.PolymorphicType != PolymorphicTypeFlat { + err = fmt.Errorf("%s polyFlatSchemaToStruct called on non-flat polymorphic attr", ctx) + logger.Printf("[ERROR] %v", err) + return + } + + childElem, ok := item.Schema.Elem.(*ExtendedResource) + if !ok { + err = fmt.Errorf("%s polymorphic attr has non-ExtendedResource element", ctx) + logger.Printf("[ERROR] %v", err) + return + } + + converter := vapiBindings_.NewTypeConverter() + rSlice := reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(&data.StructValue{})), len(dataList), len(dataList)) + for i, dataElem := range dataList { + nestedObj := reflect.New(item.Metadata.ReflectType) + nestedSchema := dataElem.(map[string]interface{}) + if err = SchemaToStruct(nestedObj.Elem(), nil, childElem.Schema, key, nestedSchema); err != nil { + return + } + + // set resource type + nestedObj.Elem().FieldByName(item.Metadata.TypeIdentifier.GetSdkName()).Set(reflect.ValueOf( + &item.Metadata.ResourceType)) + + dataValue, errors := converter.ConvertToVapi(nestedObj.Interface(), item.Metadata.BindingType) + if errors != nil { + err = errors[0] + logger.Printf("[ERROR] %v", err) + return + } + rSlice.Index(i).Set(reflect.ValueOf(dataValue.(*data.StructValue))) + logger.Printf("[TRACE] %s adding polymorphic value %+v to %s", + ctx, nestedObj.Interface(), item.Metadata.SdkFieldName) + } + + sliceElem := elem.FieldByName(item.Metadata.SdkFieldName) + if sliceElem.IsZero() { + sliceElem.Set(rSlice) + } else { + sliceElem.Set(reflect.AppendSlice(sliceElem, rSlice)) + } + return +} diff --git a/nsxt/metadata/metadata_poly_test.go b/nsxt/metadata/metadata_poly_test.go index 148078d92..450f40365 100644 --- a/nsxt/metadata/metadata_poly_test.go +++ b/nsxt/metadata/metadata_poly_test.go @@ -92,7 +92,7 @@ type testPolyListStruct struct { PolyList []*data.StructValue } -func testPolyStructSchema(t string) map[string]*schema.Schema { +func testPolyStructWrappedSchema(t string) map[string]*schema.Schema { schemaType := schema.TypeList maxItems := 0 if t == "set" { @@ -143,7 +143,7 @@ func testPolyStructSchema(t string) map[string]*schema.Schema { } } -func testPolyStructExtSchema(t, sdkName string) map[string]*ExtendedSchema { +func testPolyStructWrappedExtSchema(t, sdkName string) map[string]*ExtendedSchema { typeIdentier := TypeIdentifier{ SdkName: "Type_", JsonName: "type", @@ -218,7 +218,7 @@ func testPolyStructExtSchema(t, sdkName string) map[string]*ExtendedSchema { } } -func TestPolyStructToSchema(t *testing.T) { +func TestPolyStructToWrappedSchema(t *testing.T) { t.Run("cat struct", func(t *testing.T) { name := "matcha" rType := "FakeCat" @@ -234,10 +234,10 @@ func TestPolyStructToSchema(t *testing.T) { assert.Nil(t, errors, "unexpected error calling ConvertToGolang") obj.PolyStruct = dv.(*data.StructValue) d := schema.TestResourceDataRaw( - t, testPolyStructSchema("struct"), map[string]interface{}{}) + t, testPolyStructWrappedSchema("struct"), map[string]interface{}{}) elem := reflect.ValueOf(&obj).Elem() - err := StructToSchema(elem, d, testPolyStructExtSchema("struct", "PolyStruct"), "", nil) + err := StructToSchema(elem, d, testPolyStructWrappedExtSchema("struct", "PolyStruct"), "", nil) assert.NoError(t, err, "unexpected error calling StructToSchema") assert.Len(t, d.Get("poly_struct"), 1) polyData := d.Get("poly_struct").([]interface{})[0].(map[string]interface{}) @@ -264,10 +264,10 @@ func TestPolyStructToSchema(t *testing.T) { assert.Nil(t, errors, "unexpected error calling ConvertToGolang") obj.PolyStruct = dv.(*data.StructValue) d := schema.TestResourceDataRaw( - t, testPolyStructSchema("struct"), map[string]interface{}{}) + t, testPolyStructWrappedSchema("struct"), map[string]interface{}{}) elem := reflect.ValueOf(&obj).Elem() - err := StructToSchema(elem, d, testPolyStructExtSchema("struct", "PolyStruct"), "", nil) + err := StructToSchema(elem, d, testPolyStructWrappedExtSchema("struct", "PolyStruct"), "", nil) assert.NoError(t, err, "unexpected error calling StructToSchema") assert.Len(t, d.Get("poly_struct"), 1) polyData := d.Get("poly_struct").([]interface{})[0].(map[string]interface{}) @@ -307,10 +307,10 @@ func TestPolyStructToSchema(t *testing.T) { assert.Nil(t, errors, "unexpected error calling ConvertToGolang") obj.PolyList[1] = dv.(*data.StructValue) d := schema.TestResourceDataRaw( - t, testPolyStructSchema("struct"), map[string]interface{}{}) + t, testPolyStructWrappedSchema("struct"), map[string]interface{}{}) elem := reflect.ValueOf(&obj).Elem() - err := StructToSchema(elem, d, testPolyStructExtSchema("list", "PolyList"), "", nil) + err := StructToSchema(elem, d, testPolyStructWrappedExtSchema("list", "PolyList"), "", nil) assert.NoError(t, err, "unexpected error calling StructToSchema") assert.Len(t, d.Get("poly_struct"), 2) // idx 0: coffee @@ -359,10 +359,10 @@ func TestPolyStructToSchema(t *testing.T) { assert.Nil(t, errors, "unexpected error calling ConvertToGolang") obj.PolyList[0] = dv.(*data.StructValue) d := schema.TestResourceDataRaw( - t, testPolyStructSchema("struct"), map[string]interface{}{}) + t, testPolyStructWrappedSchema("struct"), map[string]interface{}{}) elem := reflect.ValueOf(&obj).Elem() - err := StructToSchema(elem, d, testPolyStructExtSchema("set", "PolyList"), "", nil) + err := StructToSchema(elem, d, testPolyStructWrappedExtSchema("set", "PolyList"), "", nil) assert.NoError(t, err, "unexpected error calling StructToSchema") assert.Len(t, d.Get("poly_struct"), 2) for _, v := range d.Get("poly_struct").([]interface{}) { @@ -386,10 +386,10 @@ func TestPolyStructToSchema(t *testing.T) { }) } -func TestSchemaToPolyStruct(t *testing.T) { +func TestWrappedSchemaToPolyStruct(t *testing.T) { t.Run("cat struct", func(t *testing.T) { d := schema.TestResourceDataRaw( - t, testPolyStructSchema("struct"), map[string]interface{}{ + t, testPolyStructWrappedSchema("struct"), map[string]interface{}{ "poly_struct": []interface{}{ map[string]interface{}{ "cat": []interface{}{ @@ -404,7 +404,7 @@ func TestSchemaToPolyStruct(t *testing.T) { obj := testPolyStruct{} elem := reflect.ValueOf(&obj).Elem() - err := SchemaToStruct(elem, d, testPolyStructExtSchema("struct", "PolyStruct"), "", nil) + err := SchemaToStruct(elem, d, testPolyStructWrappedExtSchema("struct", "PolyStruct"), "", nil) assert.NoError(t, err, "unexpected error calling SchemaToStruct") converter := vapiBindings_.NewTypeConverter() @@ -417,7 +417,7 @@ func TestSchemaToPolyStruct(t *testing.T) { t.Run("coffee struct", func(t *testing.T) { d := schema.TestResourceDataRaw( - t, testPolyStructSchema("struct"), map[string]interface{}{ + t, testPolyStructWrappedSchema("struct"), map[string]interface{}{ "poly_struct": []interface{}{ map[string]interface{}{ "coffee": []interface{}{ @@ -432,7 +432,7 @@ func TestSchemaToPolyStruct(t *testing.T) { obj := testPolyStruct{} elem := reflect.ValueOf(&obj).Elem() - err := SchemaToStruct(elem, d, testPolyStructExtSchema("struct", "PolyStruct"), "", nil) + err := SchemaToStruct(elem, d, testPolyStructWrappedExtSchema("struct", "PolyStruct"), "", nil) assert.NoError(t, err, "unexpected error calling SchemaToStruct") converter := vapiBindings_.NewTypeConverter() @@ -445,7 +445,7 @@ func TestSchemaToPolyStruct(t *testing.T) { t.Run("mixed list", func(t *testing.T) { d := schema.TestResourceDataRaw( - t, testPolyStructSchema("list"), map[string]interface{}{ + t, testPolyStructWrappedSchema("list"), map[string]interface{}{ "poly_struct": []interface{}{ map[string]interface{}{ "coffee": []interface{}{ @@ -468,7 +468,7 @@ func TestSchemaToPolyStruct(t *testing.T) { obj := testPolyListStruct{} elem := reflect.ValueOf(&obj).Elem() - err := SchemaToStruct(elem, d, testPolyStructExtSchema("list", "PolyList"), "", nil) + err := SchemaToStruct(elem, d, testPolyStructWrappedExtSchema("list", "PolyList"), "", nil) assert.NoError(t, err, "unexpected error calling SchemaToStruct") converter := vapiBindings_.NewTypeConverter() @@ -487,7 +487,7 @@ func TestSchemaToPolyStruct(t *testing.T) { t.Run("mixed set", func(t *testing.T) { d := schema.TestResourceDataRaw( - t, testPolyStructSchema("set"), map[string]interface{}{ + t, testPolyStructWrappedSchema("set"), map[string]interface{}{ "poly_struct": []interface{}{ map[string]interface{}{ "cat": []interface{}{ @@ -510,7 +510,7 @@ func TestSchemaToPolyStruct(t *testing.T) { obj := testPolyListStruct{} elem := reflect.ValueOf(&obj).Elem() - err := SchemaToStruct(elem, d, testPolyStructExtSchema("set", "PolyList"), "", nil) + err := SchemaToStruct(elem, d, testPolyStructWrappedExtSchema("set", "PolyList"), "", nil) assert.NoError(t, err, "unexpected error calling SchemaToStruct") converter := vapiBindings_.NewTypeConverter() @@ -523,7 +523,7 @@ func TestSchemaToPolyStruct(t *testing.T) { assert.Equal(t, int64(2), *cat.(testCatStruct).Age) assert.Equal(t, "FakeCat", *cat.(testCatStruct).Type_) } else { - coffee, errors := converter.ConvertToGolang(obj.PolyList[0], testCoffeeStructBindingType()) + coffee, errors := converter.ConvertToGolang(item, testCoffeeStructBindingType()) assert.Nil(t, errors, "unexpected error calling ConvertToGolang") assert.Equal(t, "mocha", *coffee.(testCoffeeStruct).Name) assert.Equal(t, false, *coffee.(testCoffeeStruct).IsDecaf) @@ -532,3 +532,176 @@ func TestSchemaToPolyStruct(t *testing.T) { } }) } + +func testPolyStructFlatSchema() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "cat": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + }, + "age": { + Type: schema.TypeInt, + }, + }, + }, + }, + "coffee": { + Type: schema.TypeList, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + }, + "is_decaf": { + Type: schema.TypeBool, + }, + }, + }, + }, + } +} + +func testPolyStructFlatExtSchema(sdkName string) map[string]*ExtendedSchema { + typeIdentifier := TypeIdentifier{ + SdkName: "Type_", + JsonName: "type", + } + + return map[string]*ExtendedSchema{ + "cat": { + Schema: schema.Schema{ + Type: schema.TypeList, + Elem: &ExtendedResource{ + Schema: map[string]*ExtendedSchema{ + "name": basicStringSchema("Name", false), + "age": basicIntSchema("Age", false), + }, + }, + }, + Metadata: Metadata{ + SchemaType: "list", + ReflectType: reflect.TypeOf(testCatStruct{}), + BindingType: testCatStructBindingType(), + TypeIdentifier: typeIdentifier, + PolymorphicType: PolymorphicTypeFlat, + ResourceType: "FakeCat", + SdkFieldName: sdkName, + }, + }, + "coffee": { + Schema: schema.Schema{ + Type: schema.TypeList, + Elem: &ExtendedResource{ + Schema: map[string]*ExtendedSchema{ + "name": basicStringSchema("Name", false), + "is_decaf": basicBoolSchema("IsDecaf", false), + }, + }, + }, + Metadata: Metadata{ + SchemaType: "list", + ReflectType: reflect.TypeOf(testCoffeeStruct{}), + BindingType: testCoffeeStructBindingType(), + TypeIdentifier: typeIdentifier, + PolymorphicType: PolymorphicTypeFlat, + ResourceType: "FakeCoffee", + SdkFieldName: sdkName, + }, + }, + } +} + +func TestPolyStructToFlatSchema(t *testing.T) { + t.Run("mixed list", func(t *testing.T) { + catName := "oolong" + coffeeName := "mocha" + catResType := "FakeCat" + coffeeResType := "FakeCoffee" + isDecaf := false + age := int64(2) + catObj := testCatStruct{ + Age: &age, + Name: &catName, + Type_: &catResType, + } + coffeeObj := testCoffeeStruct{ + IsDecaf: &isDecaf, + Name: &coffeeName, + Type_: &coffeeResType, + } + obj := testPolyListStruct{ + PolyList: make([]*data.StructValue, 2), + } + converter := vapiBindings_.NewTypeConverter() + dv, errors := converter.ConvertToVapi(coffeeObj, testCoffeeStructBindingType()) + assert.Nil(t, errors, "unexpected error calling ConvertToGolang") + obj.PolyList[0] = dv.(*data.StructValue) + dv, errors = converter.ConvertToVapi(catObj, testCatStructBindingType()) + assert.Nil(t, errors, "unexpected error calling ConvertToGolang") + obj.PolyList[1] = dv.(*data.StructValue) + d := schema.TestResourceDataRaw( + t, testPolyStructFlatSchema(), map[string]interface{}{}) + + elem := reflect.ValueOf(&obj).Elem() + err := StructToSchema(elem, d, testPolyStructFlatExtSchema("PolyList"), "", nil) + assert.NoError(t, err, "unexpected error calling StructToSchema") + + assert.Len(t, d.Get("coffee"), 1) + assert.Equal(t, map[string]interface{}{ + "name": coffeeName, + "is_decaf": false, + }, d.Get("coffee").([]interface{})[0].(map[string]interface{})) + + assert.Len(t, d.Get("cat"), 1) + assert.Equal(t, map[string]interface{}{ + "name": catName, + "age": 2, + }, d.Get("cat").([]interface{})[0].(map[string]interface{})) + }) +} + +func TestFlatSchemaToPolyStruct(t *testing.T) { + t.Run("mixed list", func(t *testing.T) { + d := schema.TestResourceDataRaw( + t, testPolyStructFlatSchema(), map[string]interface{}{ + "coffee": []interface{}{ + map[string]interface{}{ + "name": "mocha", + "is_decaf": true, + }, + }, + "cat": []interface{}{ + map[string]interface{}{ + "name": "oolong", + "age": 2, + }, + }, + }) + + obj := testPolyListStruct{} + elem := reflect.ValueOf(&obj).Elem() + err := SchemaToStruct(elem, d, testPolyStructFlatExtSchema("PolyList"), "", nil) + assert.NoError(t, err, "unexpected error calling SchemaToStruct") + + converter := vapiBindings_.NewTypeConverter() + assert.Len(t, obj.PolyList, 2) + for _, item := range obj.PolyList { + if item.HasField("age") { + cat, errors := converter.ConvertToGolang(item, testCatStructBindingType()) + assert.Nil(t, errors, "unexpected error calling ConvertToGolang") + assert.Equal(t, "oolong", *cat.(testCatStruct).Name) + assert.Equal(t, int64(2), *cat.(testCatStruct).Age) + assert.Equal(t, "FakeCat", *cat.(testCatStruct).Type_) + } else { + coffee, errors := converter.ConvertToGolang(item, testCoffeeStructBindingType()) + assert.Nil(t, errors, "unexpected error calling ConvertToGolang") + assert.Equal(t, "mocha", *coffee.(testCoffeeStruct).Name) + assert.Equal(t, true, *coffee.(testCoffeeStruct).IsDecaf) + assert.Equal(t, "FakeCoffee", *coffee.(testCoffeeStruct).Type_) + } + } + }) +}