Add strongly typed outputs

This change adds some convenience functions and types, to make strongly
typed outputs more pleasant to interact with.  It also includes tests
for output generally, in addition to these new functions and types.
This commit is contained in:
joeduffy 2018-06-08 09:17:12 -07:00
parent 7d8995991b
commit 74a896bb7a
3 changed files with 223 additions and 1 deletions

View file

@ -18,11 +18,12 @@ import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/pulumi/pulumi/pkg/resource"
"github.com/pulumi/pulumi/pkg/resource/deploy"
"github.com/pulumi/pulumi/pkg/tokens"
"github.com/pulumi/pulumi/pkg/version"
"github.com/stretchr/testify/assert"
)
type MockRegisterResourceEvent struct {

View file

@ -109,6 +109,42 @@ func (out *Output) Value() (interface{}, error) {
return out.voe.value, out.voe.err
}
// Array retrives the underlying value for this output property as an array.
func (out *Output) Array() ([]interface{}, error) {
v, err := out.Value()
if err != nil {
return nil, err
}
return v.([]interface{}), nil
}
// Bool retrives the underlying value for this output property as a bool.
func (out *Output) Bool() (bool, error) {
v, err := out.Value()
if err != nil {
return false, err
}
return v.(bool), nil
}
// Map retrives the underlying value for this output property as a map.
func (out *Output) Map() (map[string]interface{}, error) {
v, err := out.Value()
if err != nil {
return nil, err
}
return v.(map[string]interface{}), nil
}
// Number retrives the underlying value for this output property as a number.
func (out *Output) Number() (float64, error) {
v, err := out.Value()
if err != nil {
return 0, err
}
return v.(float64), nil
}
// String retrives the underlying value for this output property as a string.
func (out *Output) String() (string, error) {
v, err := out.Value()
@ -138,3 +174,33 @@ func (out *Output) URN() (URN, error) {
// Outputs is a map of property name to value, one for each resource output property.
type Outputs map[string]*Output
// ArrayOutput is an Output that is typed to return arrays of values.
type ArrayOutput Output
// Value returns the underlying array value.
func (out *ArrayOutput) Value() ([]interface{}, error) { return (*Output)(out).Array() }
// BoolOutput is an Output that is typed to return bool values.
type BoolOutput Output
// Value returns the underlying bool value.
func (out *BoolOutput) Value() (bool, error) { return (*Output)(out).Bool() }
// MapOutput is an Output that is typed to return string-keyed maps of values.
type MapOutput Output
// Value returns the underlying map value.
func (out *MapOutput) Value() (map[string]interface{}, error) { return (*Output)(out).Map() }
// NumberOutput is an Output that is typed to return number values.
type NumberOutput Output
// Value returns the underlying number value.
func (out *NumberOutput) Value() (float64, error) { return (*Output)(out).Number() }
// StringOutput is an Output that is typed to return number values.
type StringOutput Output
// Value returns the underlying number value.
func (out *StringOutput) Value() (string, error) { return (*Output)(out).String() }

View file

@ -0,0 +1,155 @@
// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package pulumi
import (
"testing"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
)
func TestBasicOutputs(t *testing.T) {
// Just test basic resolve and reject functionality.
{
out, resolve, _ := NewOutput(nil)
go func() {
resolve(42)
}()
v, err := out.Value()
assert.Nil(t, err)
assert.NotNil(t, v)
assert.Equal(t, 42, v.(int))
}
{
out, _, reject := NewOutput(nil)
go func() {
reject(errors.New("boom"))
}()
v, err := out.Value()
assert.NotNil(t, err)
assert.Nil(t, v)
}
}
func TestArrayOutputs(t *testing.T) {
out, resolve, _ := NewOutput(nil)
go func() {
resolve([]interface{}{nil, 0, "x"})
}()
{
v, err := out.Array()
assert.Nil(t, err)
assert.NotNil(t, v)
if assert.Equal(t, 3, len(v)) {
assert.Equal(t, nil, v[0])
assert.Equal(t, 0, v[1])
assert.Equal(t, "x", v[2])
}
}
{
arr := (*ArrayOutput)(out)
v, err := arr.Value()
assert.Nil(t, err)
assert.NotNil(t, v)
if assert.Equal(t, 3, len(v)) {
assert.Equal(t, nil, v[0])
assert.Equal(t, 0, v[1])
assert.Equal(t, "x", v[2])
}
}
}
func TestBoolOutputs(t *testing.T) {
out, resolve, _ := NewOutput(nil)
go func() {
resolve(true)
}()
{
v, err := out.Bool()
assert.Nil(t, err)
assert.True(t, v)
}
{
b := (*BoolOutput)(out)
v, err := b.Value()
assert.Nil(t, err)
assert.True(t, v)
}
}
func TestMapOutputs(t *testing.T) {
out, resolve, _ := NewOutput(nil)
go func() {
resolve(map[string]interface{}{
"x": 1,
"y": false,
"z": "abc",
})
}()
{
v, err := out.Map()
assert.Nil(t, err)
assert.NotNil(t, v)
assert.Equal(t, 1, v["x"])
assert.Equal(t, false, v["y"])
assert.Equal(t, "abc", v["z"])
}
{
b := (*MapOutput)(out)
v, err := b.Value()
assert.Nil(t, err)
assert.NotNil(t, v)
assert.Equal(t, 1, v["x"])
assert.Equal(t, false, v["y"])
assert.Equal(t, "abc", v["z"])
}
}
func TestNumberOutputs(t *testing.T) {
out, resolve, _ := NewOutput(nil)
go func() {
resolve(42.345)
}()
{
v, err := out.Number()
assert.Nil(t, err)
assert.Equal(t, 42.345, v)
}
{
b := (*NumberOutput)(out)
v, err := b.Value()
assert.Nil(t, err)
assert.Equal(t, 42.345, v)
}
}
func TestStringOutputs(t *testing.T) {
out, resolve, _ := NewOutput(nil)
go func() {
resolve("a stringy output")
}()
{
v, err := out.String()
assert.Nil(t, err)
assert.Equal(t, "a stringy output", v)
}
{
b := (*StringOutput)(out)
v, err := b.Value()
assert.Nil(t, err)
assert.Equal(t, "a stringy output", v)
}
}