Allow primitive type in maps via all FieldReaders

Now that we can read primitive type out of a map, each field reader
needs to be able to set the proper type as expected.
This commit is contained in:
James Bardin 2016-11-17 15:12:29 -05:00
parent 730014b33e
commit c4eefd4b5e
8 changed files with 153 additions and 23 deletions

View File

@ -214,6 +214,33 @@ func readObjectField(
}, nil }, nil
} }
// convert map values to the proper primitive type based on schema.Elem
func mapValuesToPrimitive(m map[string]interface{}, schema *Schema) error {
elemType := TypeString
if et, ok := schema.Elem.(ValueType); ok {
elemType = et
}
switch elemType {
case TypeInt, TypeFloat, TypeBool:
for k, v := range m {
vs, ok := v.(string)
if !ok {
continue
}
v, err := stringToPrimitive(vs, false, &Schema{Type: elemType})
if err != nil {
return err
}
m[k] = v
}
}
return nil
}
func stringToPrimitive( func stringToPrimitive(
value string, computed bool, schema *Schema) (interface{}, error) { value string, computed bool, schema *Schema) (interface{}, error) {
var returnVal interface{} var returnVal interface{}

View File

@ -85,7 +85,7 @@ func (r *ConfigFieldReader) readField(
case TypeList: case TypeList:
return readListField(&nestedConfigFieldReader{r}, address, schema) return readListField(&nestedConfigFieldReader{r}, address, schema)
case TypeMap: case TypeMap:
return r.readMap(k) return r.readMap(k, schema)
case TypeSet: case TypeSet:
return r.readSet(address, schema) return r.readSet(address, schema)
case typeObject: case typeObject:
@ -97,7 +97,7 @@ func (r *ConfigFieldReader) readField(
} }
} }
func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) { func (r *ConfigFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) {
// We want both the raw value and the interpolated. We use the interpolated // We want both the raw value and the interpolated. We use the interpolated
// to store actual values and we use the raw one to check for // to store actual values and we use the raw one to check for
// computed keys. Actual values are obtained in the switch, depending on // computed keys. Actual values are obtained in the switch, depending on
@ -170,6 +170,11 @@ func (r *ConfigFieldReader) readMap(k string) (FieldReadResult, error) {
panic(fmt.Sprintf("unknown type: %#v", mraw)) panic(fmt.Sprintf("unknown type: %#v", mraw))
} }
err := mapValuesToPrimitive(result, schema)
if err != nil {
return FieldReadResult{}, nil
}
var value interface{} var value interface{}
if !computed { if !computed {
value = result value = result

View File

@ -35,6 +35,17 @@ func TestConfigFieldReader(t *testing.T) {
"foo": "bar", "foo": "bar",
"bar": "baz", "bar": "baz",
}, },
"mapInt": map[string]interface{}{
"one": "1",
"two": "2",
},
"mapFloat": map[string]interface{}{
"oneDotTwo": "1.2",
},
"mapBool": map[string]interface{}{
"True": "true",
"False": "false",
},
"set": []interface{}{10, 50}, "set": []interface{}{10, 50},
"setDeep": []interface{}{ "setDeep": []interface{}{

View File

@ -69,12 +69,6 @@ func (r *DiffFieldReader) readMap(
resultSet = true resultSet = true
} }
// Determine what element type the map contains, defaulting to string
elemType := TypeString
if et, ok := schema.Elem.(ValueType); ok {
elemType = et
}
// Next, read all the elements we have in our diff, and apply // Next, read all the elements we have in our diff, and apply
// the diff to our result. // the diff to our result.
prefix := strings.Join(address, ".") + "." prefix := strings.Join(address, ".") + "."
@ -95,20 +89,12 @@ func (r *DiffFieldReader) readMap(
continue continue
} }
// Replace the new value with one of the correct Elem type if needed. result[k] = v.New
// We don't supported arbitrarily nested schemas, so we can only handle
// the primitive types here.
var vNew interface{} = v.New
switch elemType {
case TypeBool, TypeInt, TypeFloat:
v, err := stringToPrimitive(v.New, false, &Schema{Type: elemType})
if err != nil {
return FieldReadResult{}, err
}
vNew = v
} }
result[k] = vNew err = mapValuesToPrimitive(result, schema)
if err != nil {
return FieldReadResult{}, nil
} }
var resultVal interface{} var resultVal interface{}

View File

@ -420,6 +420,41 @@ func TestDiffFieldReader(t *testing.T) {
New: "baz", New: "baz",
}, },
"mapInt.%": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapInt.one": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"mapInt.two": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapFloat.%": &terraform.ResourceAttrDiff{
Old: "",
New: "1",
},
"mapFloat.oneDotTwo": &terraform.ResourceAttrDiff{
Old: "",
New: "1.2",
},
"mapBool.%": &terraform.ResourceAttrDiff{
Old: "",
New: "2",
},
"mapBool.True": &terraform.ResourceAttrDiff{
Old: "",
New: "true",
},
"mapBool.False": &terraform.ResourceAttrDiff{
Old: "",
New: "false",
},
"set.#": &terraform.ResourceAttrDiff{ "set.#": &terraform.ResourceAttrDiff{
Old: "0", Old: "0",
New: "2", New: "2",

View File

@ -26,7 +26,7 @@ func (r *MapFieldReader) ReadField(address []string) (FieldReadResult, error) {
case TypeList: case TypeList:
return readListField(r, address, schema) return readListField(r, address, schema)
case TypeMap: case TypeMap:
return r.readMap(k) return r.readMap(k, schema)
case TypeSet: case TypeSet:
return r.readSet(address, schema) return r.readSet(address, schema)
case typeObject: case typeObject:
@ -36,7 +36,7 @@ func (r *MapFieldReader) ReadField(address []string) (FieldReadResult, error) {
} }
} }
func (r *MapFieldReader) readMap(k string) (FieldReadResult, error) { func (r *MapFieldReader) readMap(k string, schema *Schema) (FieldReadResult, error) {
result := make(map[string]interface{}) result := make(map[string]interface{})
resultSet := false resultSet := false
@ -61,6 +61,11 @@ func (r *MapFieldReader) readMap(k string) (FieldReadResult, error) {
return true return true
}) })
err := mapValuesToPrimitive(result, schema)
if err != nil {
return FieldReadResult{}, nil
}
var resultVal interface{} var resultVal interface{}
if resultSet { if resultSet {
resultVal = result resultVal = result

View File

@ -41,6 +41,17 @@ func TestMapFieldReader(t *testing.T) {
"setDeep.10.value": "foo", "setDeep.10.value": "foo",
"setDeep.50.index": "50", "setDeep.50.index": "50",
"setDeep.50.value": "bar", "setDeep.50.value": "bar",
"mapInt.%": "2",
"mapInt.one": "1",
"mapInt.two": "2",
"mapFloat.%": "1",
"mapFloat.oneDotTwo": "1.2",
"mapBool.%": "2",
"mapBool.True": "true",
"mapBool.False": "false",
}), }),
} }
}) })

View File

@ -211,6 +211,18 @@ func testFieldReader(t *testing.T, f func(map[string]*Schema) FieldReader) {
// Maps // Maps
"map": &Schema{Type: TypeMap}, "map": &Schema{Type: TypeMap},
"mapInt": &Schema{
Type: TypeMap,
Elem: TypeInt,
},
"mapFloat": &Schema{
Type: TypeMap,
Elem: TypeFloat,
},
"mapBool": &Schema{
Type: TypeMap,
Elem: TypeBool,
},
// Sets // Sets
"set": &Schema{ "set": &Schema{
@ -335,6 +347,44 @@ func testFieldReader(t *testing.T, f func(map[string]*Schema) FieldReader) {
false, false,
}, },
"mapInt": {
[]string{"mapInt"},
FieldReadResult{
Value: map[string]interface{}{
"one": 1,
"two": 2,
},
Exists: true,
Computed: false,
},
false,
},
"mapFloat": {
[]string{"mapFloat"},
FieldReadResult{
Value: map[string]interface{}{
"oneDotTwo": 1.2,
},
Exists: true,
Computed: false,
},
false,
},
"mapBool": {
[]string{"mapBool"},
FieldReadResult{
Value: map[string]interface{}{
"True": true,
"False": false,
},
Exists: true,
Computed: false,
},
false,
},
"mapelem": { "mapelem": {
[]string{"map", "foo"}, []string{"map", "foo"},
FieldReadResult{ FieldReadResult{