[sdk/go] Support maps in Invoke outputs and Read inputs (#6014)

* [sdk/go] Support maps in Invoke outputs and Read inputs

These are already supported by the implementation, but were prevented by overzealous input validation in Invoke and ReadResource.

Follow-up to #4522 and #4521.

* Add CHANGELOG

* PR feedback
This commit is contained in:
Luke Hoban 2021-01-06 15:35:13 +11:00 committed by GitHub
parent e1ac01a9f2
commit db14188526
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 5 deletions

View file

@ -22,6 +22,9 @@ CHANGELOG
give the user some information on how to resolve the problem
[#6044](https://github.com/pulumi/pulumi/pull/6044)
- [sdk/go] Support maps in Invoke outputs and Read inputs
[#6014](https://github.com/pulumi/pulumi/pull/6014)
## 2.16.2 (2020-12-23)
- Fix a bug in the core engine that could cause previews to fail if a resource with changes had

View file

@ -171,8 +171,10 @@ func (ctx *Context) Invoke(tok string, args interface{}, result interface{}, opt
}
resultV := reflect.ValueOf(result)
if resultV.Kind() != reflect.Ptr || resultV.Elem().Kind() != reflect.Struct {
return errors.New("result must be a pointer to a struct value")
if !(resultV.Kind() == reflect.Ptr &&
(resultV.Elem().Kind() == reflect.Struct ||
(resultV.Elem().Kind() == reflect.Map && resultV.Elem().Type().Key().Kind() == reflect.String))) {
return errors.New("result must be a pointer to a struct or map value")
}
options := &invokeOptions{}
@ -305,8 +307,9 @@ func (ctx *Context) ReadResource(
if propsType.Kind() == reflect.Ptr {
propsType = propsType.Elem()
}
if propsType.Kind() != reflect.Struct {
return errors.New("props must be a struct or a pointer to a struct")
if !(propsType.Kind() == reflect.Struct ||
(propsType.Kind() == reflect.Map && propsType.Key().Kind() == reflect.String)) {
return errors.New("props must be a struct or map or a pointer to a struct or map")
}
}

View file

@ -176,6 +176,7 @@ func TestReadResource(t *testing.T) {
}
err := RunErr(func(ctx *Context) error {
// Test struct-tag-based marshaling.
var res testResource2
err := ctx.ReadResource("test:resource:type", "resA", ID("someID"), &testResource2Inputs{
Foo: String("oof"),
@ -203,6 +204,20 @@ func TestReadResource(t *testing.T) {
assert.Equal(t, []Resource{&res}, deps)
assert.Equal(t, "qux", foo)
// Test map marshaling.
var res2 testResource2
err = ctx.ReadResource("test:resource:type", "resA", ID("someID"), Map{
"foo": String("oof"),
}, &res2)
assert.NoError(t, err)
foo, known, secret, deps, err = await(res2.Foo)
assert.NoError(t, err)
assert.True(t, known)
assert.False(t, secret)
assert.Equal(t, []Resource{&res2}, deps)
assert.Equal(t, "qux", foo)
return nil
}, WithMocks("project", "stack", mocks))
assert.NoError(t, err)
@ -224,16 +239,26 @@ func TestInvoke(t *testing.T) {
}
err := RunErr(func(ctx *Context) error {
// Test struct unmarshaling.
var result invokeResult
err := ctx.Invoke("test:index:func", &invokeArgs{
Bang: "gnab",
Bar: "rab",
}, &result)
assert.NoError(t, err)
assert.Equal(t, "oof", result.Foo)
assert.Equal(t, "zab", result.Baz)
// Test map unmarshaling.
var result2 map[string]interface{}
err = ctx.Invoke("test:index:func", &invokeArgs{
Bang: "gnab",
Bar: "rab",
}, &result2)
assert.NoError(t, err)
assert.Equal(t, "oof", result2["foo"].(string))
assert.Equal(t, "zab", result2["baz"].(string))
return nil
}, WithMocks("project", "stack", mocks))
assert.NoError(t, err)