Add a PropertyMap/Value.MapReplace function
This adds a handy MapReplace function on pkg/resource's PropertyMap and PropertyValue types. This is just like the existing Mappable function, except that it permits easy replacement of elements as the map transformation occurs. We need this to perform float64=>int transformations.
This commit is contained in:
parent
27b819dec6
commit
5d9f7918e9
|
@ -349,9 +349,15 @@ func (m PropertyMap) HasValue(k PropertyKey) bool {
|
|||
|
||||
// Mappable returns a mapper-compatible object map, suitable for deserialization into structures.
|
||||
func (m PropertyMap) Mappable() map[string]interface{} {
|
||||
return m.MapReplace(nil)
|
||||
}
|
||||
|
||||
// MapReplace returns a mapper-compatible object map, suitable for deserialization into structures. A replace function
|
||||
// repl may be passed that will replace elements using custom logic if appropriate.
|
||||
func (m PropertyMap) MapReplace(repl func(PropertyValue) (interface{}, bool)) map[string]interface{} {
|
||||
obj := make(map[string]interface{})
|
||||
for _, k := range m.StableKeys() {
|
||||
obj[string(k)] = m[k].Mappable()
|
||||
obj[string(k)] = m[k].MapReplace(repl)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
@ -640,6 +646,17 @@ func (v PropertyValue) TypeString() string {
|
|||
|
||||
// Mappable returns a mapper-compatible value, suitable for deserialization into structures.
|
||||
func (v PropertyValue) Mappable() interface{} {
|
||||
return v.MapReplace(nil)
|
||||
}
|
||||
|
||||
// MapReplace returns a mapper-compatible object map, suitable for deserialization into structures. A replace function
|
||||
// repl may be passed that will replace elements using custom logic if appropriate.
|
||||
func (v PropertyValue) MapReplace(repl func(PropertyValue) (interface{}, bool)) interface{} {
|
||||
if repl != nil {
|
||||
if vret, vrep := repl(v); vrep {
|
||||
return vret
|
||||
}
|
||||
}
|
||||
if v.IsNull() {
|
||||
return nil
|
||||
} else if v.IsBool() {
|
||||
|
@ -651,12 +668,12 @@ func (v PropertyValue) Mappable() interface{} {
|
|||
} else if v.IsArray() {
|
||||
var arr []interface{}
|
||||
for _, e := range v.ArrayValue() {
|
||||
arr = append(arr, e.Mappable())
|
||||
arr = append(arr, e.MapReplace(repl))
|
||||
}
|
||||
return arr
|
||||
}
|
||||
contract.Assert(v.IsObject())
|
||||
return v.ObjectValue().Mappable()
|
||||
return v.ObjectValue().MapReplace(repl)
|
||||
}
|
||||
|
||||
// String implements the fmt.Stringer interface to add slightly more information to the output.
|
||||
|
|
95
pkg/resource/properties_test.go
Normal file
95
pkg/resource/properties_test.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
|
||||
|
||||
package resource
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// TestMappable ensures that we properly convert from resource property maps to their "weakly typed" JSON-like
|
||||
// equivalents.
|
||||
func TestMappable(t *testing.T) {
|
||||
ma1 := map[string]interface{}{
|
||||
"a": float64(42.3),
|
||||
"b": false,
|
||||
"c": "foobar",
|
||||
"d": []interface{}{"x", float64(99), true},
|
||||
"e": map[string]interface{}{
|
||||
"e.1": "z",
|
||||
"e.n": float64(676.767),
|
||||
"e.^": []interface{}{"bbb"},
|
||||
},
|
||||
}
|
||||
ma1p := NewPropertyMapFromMap(ma1)
|
||||
assert.Equal(t, len(ma1), len(ma1p))
|
||||
ma1mm := ma1p.Mappable()
|
||||
assert.Equal(t, ma1, ma1mm)
|
||||
}
|
||||
|
||||
// TestReplace ensures that we properly convert from resource property maps to their "weakly typed" JSON-like
|
||||
// equivalents, but with additional and optional functions that replace values inline as we go.
|
||||
func TestMapReplace(t *testing.T) {
|
||||
// First, no replacements (nil repl).
|
||||
ma1 := map[string]interface{}{
|
||||
"a": float64(42.3),
|
||||
"b": false,
|
||||
"c": "foobar",
|
||||
"d": []interface{}{"x", float64(99), true},
|
||||
"e": map[string]interface{}{
|
||||
"e.1": "z",
|
||||
"e.n": float64(676.767),
|
||||
"e.^": []interface{}{"bbb"},
|
||||
},
|
||||
}
|
||||
ma1p := NewPropertyMapFromMap(ma1)
|
||||
assert.Equal(t, len(ma1), len(ma1p))
|
||||
ma1mm := ma1p.MapReplace(nil)
|
||||
assert.Equal(t, ma1, ma1mm)
|
||||
|
||||
// First, no replacements (false-returning repl).
|
||||
ma2 := map[string]interface{}{
|
||||
"a": float64(42.3),
|
||||
"b": false,
|
||||
"c": "foobar",
|
||||
"d": []interface{}{"x", float64(99), true},
|
||||
"e": map[string]interface{}{
|
||||
"e.1": "z",
|
||||
"e.n": float64(676.767),
|
||||
"e.^": []interface{}{"bbb"},
|
||||
},
|
||||
}
|
||||
ma2p := NewPropertyMapFromMap(ma2)
|
||||
assert.Equal(t, len(ma2), len(ma2p))
|
||||
ma2mm := ma2p.MapReplace(func(v PropertyValue) (interface{}, bool) {
|
||||
return nil, false
|
||||
})
|
||||
assert.Equal(t, ma2, ma2mm)
|
||||
|
||||
// Finally, actually replace some numbers with ints.
|
||||
ma3 := map[string]interface{}{
|
||||
"a": float64(42.3),
|
||||
"b": false,
|
||||
"c": "foobar",
|
||||
"d": []interface{}{"x", float64(99), true},
|
||||
"e": map[string]interface{}{
|
||||
"e.1": "z",
|
||||
"e.n": float64(676.767),
|
||||
"e.^": []interface{}{"bbb"},
|
||||
},
|
||||
}
|
||||
ma3p := NewPropertyMapFromMap(ma3)
|
||||
assert.Equal(t, len(ma3), len(ma3p))
|
||||
ma3mm := ma3p.MapReplace(func(v PropertyValue) (interface{}, bool) {
|
||||
if v.IsNumber() {
|
||||
return int(v.NumberValue()), true
|
||||
}
|
||||
return nil, false
|
||||
})
|
||||
// patch the original map so it can compare easily
|
||||
ma3["a"] = int(ma3["a"].(float64))
|
||||
ma3["d"].([]interface{})[1] = int(ma3["d"].([]interface{})[1].(float64))
|
||||
ma3["e"].(map[string]interface{})["e.n"] = int(ma3["e"].(map[string]interface{})["e.n"].(float64))
|
||||
assert.Equal(t, ma3, ma3mm)
|
||||
}
|
Loading…
Reference in a new issue