Implement unknown outputs
This commit implements unknown outputs in the same style as our Node.js language provider. That is to say, during previews, it's possible that certain outputs will not have known values. In those cases, we want to flow sufficient information through the resolution of values, so that we may skip applies. We also return this fact from the direct accessors.
This commit is contained in:
parent
5a71ab9d12
commit
20af051caf
|
@ -162,14 +162,14 @@ func (ctx *Context) RegisterResource(
|
||||||
urn, resolveURN, rejectURN := NewOutput(nil)
|
urn, resolveURN, rejectURN := NewOutput(nil)
|
||||||
|
|
||||||
var id *Output
|
var id *Output
|
||||||
var resolveID func(interface{})
|
var resolveID func(interface{}, bool)
|
||||||
var rejectID func(error)
|
var rejectID func(error)
|
||||||
if custom {
|
if custom {
|
||||||
id, resolveID, rejectID = NewOutput(nil)
|
id, resolveID, rejectID = NewOutput(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
state := make(map[string]*Output)
|
state := make(map[string]*Output)
|
||||||
resolveState := make(map[string]func(interface{}))
|
resolveState := make(map[string]func(interface{}, bool))
|
||||||
rejectState := make(map[string]func(error))
|
rejectState := make(map[string]func(error))
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
state[key], resolveState[key], rejectState[key] = NewOutput(nil)
|
state[key], resolveState[key], rejectState[key] = NewOutput(nil)
|
||||||
|
@ -208,16 +208,18 @@ func (ctx *Context) RegisterResource(
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
glog.V(9).Infof("RegisterResource(%s, %s): success: %s %s %d", t, name, resp.Urn, resp.Id, len(outprops))
|
glog.V(9).Infof("RegisterResource(%s, %s): success: %s %s %d", t, name, resp.Urn, resp.Id, len(outprops))
|
||||||
resolveURN(URN(resp.Urn))
|
resolveURN(URN(resp.Urn), true)
|
||||||
if resolveID != nil {
|
if resolveID != nil {
|
||||||
resolveID(ID(resp.Id))
|
resolveID(ID(resp.Id), true)
|
||||||
}
|
}
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
out, err := unmarshalOutput(outprops[key])
|
out, err := unmarshalOutput(outprops[key])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rejectState[key](err)
|
rejectState[key](err)
|
||||||
} else {
|
} else {
|
||||||
resolveState[key](out)
|
// During previews, it's possible that nils will be returned due to unknown values.
|
||||||
|
known := !ctx.DryRun() || out != nil
|
||||||
|
resolveState[key](out, known)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,13 @@ type Output struct {
|
||||||
type valueOrError struct {
|
type valueOrError struct {
|
||||||
value interface{} // a value, if the output resolved to a value.
|
value interface{} // a value, if the output resolved to a value.
|
||||||
err error // an error, if the producer yielded an error instead of a value.
|
err error // an error, if the producer yielded an error instead of a value.
|
||||||
|
known bool // true if this value is known, versus just being a placeholder during previews.
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOutput returns an output value that can be used to rendezvous with the production of a value or error. The
|
// NewOutput returns an output value that can be used to rendezvous with the production of a value or error. The
|
||||||
// function returns the output itself, plus two functions: one for resolving a value, and another for rejecting with an
|
// function returns the output itself, plus two functions: one for resolving a value, and another for rejecting with an
|
||||||
// error; exactly one function must be called. This acts like a promise.
|
// error; exactly one function must be called. This acts like a promise.
|
||||||
func NewOutput(deps []Resource) (*Output, func(interface{}), func(error)) {
|
func NewOutput(deps []Resource) (*Output, func(interface{}, bool), func(error)) {
|
||||||
out := &Output{
|
out := &Output{
|
||||||
sync: make(chan *valueOrError, 1),
|
sync: make(chan *valueOrError, 1),
|
||||||
deps: deps,
|
deps: deps,
|
||||||
|
@ -49,19 +50,19 @@ func NewOutput(deps []Resource) (*Output, func(interface{}), func(error)) {
|
||||||
|
|
||||||
// resolve will resolve the output. It is not exported, because we want to control the capabilities tightly, such
|
// resolve will resolve the output. It is not exported, because we want to control the capabilities tightly, such
|
||||||
// that anybody who happens to have an Output is not allowed to resolve it; only those who created it can.
|
// that anybody who happens to have an Output is not allowed to resolve it; only those who created it can.
|
||||||
func (out *Output) resolve(v interface{}) {
|
func (out *Output) resolve(v interface{}, known bool) {
|
||||||
// If v is another output, chain this rather than resolving to an output directly.
|
// If v is another output, chain this rather than resolving to an output directly.
|
||||||
if other, isOut := v.(*Output); isOut {
|
if other, isOut := v.(*Output); known && isOut {
|
||||||
go func() {
|
go func() {
|
||||||
real, err := other.Value()
|
real, otherKnown, err := other.Value()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
out.reject(err)
|
out.reject(err)
|
||||||
} else {
|
} else {
|
||||||
out.resolve(real)
|
out.resolve(real, otherKnown)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
out.sync <- &valueOrError{value: v}
|
out.sync <- &valueOrError{value: v, known: known}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,27 +79,33 @@ func (out *Output) Apply(applier func(v interface{}) (interface{}, error)) *Outp
|
||||||
result, resolve, reject := NewOutput(out.Deps())
|
result, resolve, reject := NewOutput(out.Deps())
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
reject(err)
|
reject(err)
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
// If we have a value, run the applier to transform it.
|
if known {
|
||||||
u, err := applier(v)
|
// If we have a known value, run the applier to transform it.
|
||||||
if err != nil {
|
u, err := applier(v)
|
||||||
reject(err)
|
if err != nil {
|
||||||
break
|
reject(err)
|
||||||
} else {
|
|
||||||
// Now that we've transformed the value, it's possible we have another output. If so, pluck it
|
|
||||||
// out and go around to await it until we hit a real value. Note that we are not capturing the
|
|
||||||
// resources of this inner output, intentionally, as the output returned should be related to
|
|
||||||
// this output already.
|
|
||||||
if newout, ok := v.(*Output); ok {
|
|
||||||
out = newout
|
|
||||||
} else {
|
|
||||||
resolve(u)
|
|
||||||
break
|
break
|
||||||
|
} else {
|
||||||
|
// Now that we've transformed the value, it's possible we have another output. If so, pluck it
|
||||||
|
// out and go around to await it until we hit a real value. Note that we are not capturing the
|
||||||
|
// resources of this inner output, intentionally, as the output returned should be related to
|
||||||
|
// this output already.
|
||||||
|
if newout, ok := v.(*Output); ok {
|
||||||
|
out = newout
|
||||||
|
} else {
|
||||||
|
resolve(u, true)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// If the value isn't known, skip the apply function.
|
||||||
|
resolve(nil, false)
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,12 +114,10 @@ func (out *Output) Apply(applier func(v interface{}) (interface{}, error)) *Outp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deps returns the dependencies for this output property.
|
// Deps returns the dependencies for this output property.
|
||||||
func (out *Output) Deps() []Resource {
|
func (out *Output) Deps() []Resource { return out.deps }
|
||||||
return out.deps
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value retrieves the underlying value for this output property.
|
// Value retrieves the underlying value for this output property.
|
||||||
func (out *Output) Value() (interface{}, error) {
|
func (out *Output) Value() (interface{}, bool, error) {
|
||||||
// If neither error nor value are available, first await the channel. Only one Goroutine will make it through this
|
// If neither error nor value are available, first await the channel. Only one Goroutine will make it through this
|
||||||
// and is responsible for closing the channel, to signal to other awaiters that it's safe to read the values.
|
// and is responsible for closing the channel, to signal to other awaiters that it's safe to read the values.
|
||||||
if out.voe == nil {
|
if out.voe == nil {
|
||||||
|
@ -121,184 +126,184 @@ func (out *Output) Value() (interface{}, error) {
|
||||||
close(out.sync)
|
close(out.sync)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return out.voe.value, out.voe.err
|
return out.voe.value, out.voe.known, out.voe.err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Archive retrives the underlying value for this output property as an archive.
|
// Archive retrives the underlying value for this output property as an archive.
|
||||||
func (out *Output) Archive() (asset.Archive, error) {
|
func (out *Output) Archive() (asset.Archive, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return nil, err
|
return nil, known, err
|
||||||
}
|
}
|
||||||
return v.(asset.Archive), nil
|
return v.(asset.Archive), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Array retrives the underlying value for this output property as an array.
|
// Array retrives the underlying value for this output property as an array.
|
||||||
func (out *Output) Array() ([]interface{}, error) {
|
func (out *Output) Array() ([]interface{}, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return nil, err
|
return nil, known, err
|
||||||
}
|
}
|
||||||
return cast.ToSlice(v), nil
|
return cast.ToSlice(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Asset retrives the underlying value for this output property as an asset.
|
// Asset retrives the underlying value for this output property as an asset.
|
||||||
func (out *Output) Asset() (asset.Asset, error) {
|
func (out *Output) Asset() (asset.Asset, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return nil, err
|
return nil, known, err
|
||||||
}
|
}
|
||||||
return v.(asset.Asset), nil
|
return v.(asset.Asset), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bool retrives the underlying value for this output property as a bool.
|
// Bool retrives the underlying value for this output property as a bool.
|
||||||
func (out *Output) Bool() (bool, error) {
|
func (out *Output) Bool() (bool, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return false, err
|
return false, known, err
|
||||||
}
|
}
|
||||||
return cast.ToBool(v), nil
|
return cast.ToBool(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map retrives the underlying value for this output property as a map.
|
// Map retrives the underlying value for this output property as a map.
|
||||||
func (out *Output) Map() (map[string]interface{}, error) {
|
func (out *Output) Map() (map[string]interface{}, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return nil, err
|
return nil, known, err
|
||||||
}
|
}
|
||||||
return cast.ToStringMap(v), nil
|
return cast.ToStringMap(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float32 retrives the underlying value for this output property as a float32.
|
// Float32 retrives the underlying value for this output property as a float32.
|
||||||
func (out *Output) Float32() (float32, error) {
|
func (out *Output) Float32() (float32, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToFloat32(v), nil
|
return cast.ToFloat32(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Float64 retrives the underlying value for this output property as a float64.
|
// Float64 retrives the underlying value for this output property as a float64.
|
||||||
func (out *Output) Float64() (float64, error) {
|
func (out *Output) Float64() (float64, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToFloat64(v), nil
|
return cast.ToFloat64(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ID retrives the underlying value for this output property as an ID.
|
// ID retrives the underlying value for this output property as an ID.
|
||||||
func (out *Output) ID() (ID, error) {
|
func (out *Output) ID() (ID, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return "", err
|
return "", known, err
|
||||||
}
|
}
|
||||||
return ID(cast.ToString(v)), nil
|
return ID(cast.ToString(v)), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int retrives the underlying value for this output property as a int.
|
// Int retrives the underlying value for this output property as a int.
|
||||||
func (out *Output) Int() (int, error) {
|
func (out *Output) Int() (int, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToInt(v), nil
|
return cast.ToInt(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int8 retrives the underlying value for this output property as a int8.
|
// Int8 retrives the underlying value for this output property as a int8.
|
||||||
func (out *Output) Int8() (int8, error) {
|
func (out *Output) Int8() (int8, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToInt8(v), nil
|
return cast.ToInt8(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int16 retrives the underlying value for this output property as a int16.
|
// Int16 retrives the underlying value for this output property as a int16.
|
||||||
func (out *Output) Int16() (int16, error) {
|
func (out *Output) Int16() (int16, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToInt16(v), nil
|
return cast.ToInt16(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int32 retrives the underlying value for this output property as a int32.
|
// Int32 retrives the underlying value for this output property as a int32.
|
||||||
func (out *Output) Int32() (int32, error) {
|
func (out *Output) Int32() (int32, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToInt32(v), nil
|
return cast.ToInt32(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Int64 retrives the underlying value for this output property as a int64.
|
// Int64 retrives the underlying value for this output property as a int64.
|
||||||
func (out *Output) Int64() (int64, error) {
|
func (out *Output) Int64() (int64, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToInt64(v), nil
|
return cast.ToInt64(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// String retrives the underlying value for this output property as a string.
|
// String retrives the underlying value for this output property as a string.
|
||||||
func (out *Output) String() (string, error) {
|
func (out *Output) String() (string, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return "", err
|
return "", known, err
|
||||||
}
|
}
|
||||||
return cast.ToString(v), nil
|
return cast.ToString(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uint retrives the underlying value for this output property as a uint.
|
// Uint retrives the underlying value for this output property as a uint.
|
||||||
func (out *Output) Uint() (uint, error) {
|
func (out *Output) Uint() (uint, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToUint(v), nil
|
return cast.ToUint(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uint8 retrives the underlying value for this output property as a uint8.
|
// Uint8 retrives the underlying value for this output property as a uint8.
|
||||||
func (out *Output) Uint8() (uint8, error) {
|
func (out *Output) Uint8() (uint8, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToUint8(v), nil
|
return cast.ToUint8(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uint16 retrives the underlying value for this output property as a uint16.
|
// Uint16 retrives the underlying value for this output property as a uint16.
|
||||||
func (out *Output) Uint16() (uint16, error) {
|
func (out *Output) Uint16() (uint16, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToUint16(v), nil
|
return cast.ToUint16(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uint32 retrives the underlying value for this output property as a uint32.
|
// Uint32 retrives the underlying value for this output property as a uint32.
|
||||||
func (out *Output) Uint32() (uint32, error) {
|
func (out *Output) Uint32() (uint32, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToUint32(v), nil
|
return cast.ToUint32(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uint64 retrives the underlying value for this output property as a uint64.
|
// Uint64 retrives the underlying value for this output property as a uint64.
|
||||||
func (out *Output) Uint64() (uint64, error) {
|
func (out *Output) Uint64() (uint64, bool, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return 0, err
|
return 0, known, err
|
||||||
}
|
}
|
||||||
return cast.ToUint64(v), nil
|
return cast.ToUint64(v), true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// URN retrives the underlying value for this output property as a URN.
|
// URN retrives the underlying value for this output property as a URN.
|
||||||
func (out *Output) URN() (URN, error) {
|
func (out *Output) URN() (URN, error) {
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
if err != nil {
|
if err != nil || !known {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return URN(cast.ToString(v)), nil
|
return URN(cast.ToString(v)), nil
|
||||||
|
@ -311,7 +316,9 @@ type Outputs map[string]*Output
|
||||||
type ArchiveOutput Output
|
type ArchiveOutput Output
|
||||||
|
|
||||||
// Value returns the underlying archive value.
|
// Value returns the underlying archive value.
|
||||||
func (out *ArchiveOutput) Value() (asset.Archive, error) { return (*Output)(out).Archive() }
|
func (out *ArchiveOutput) Value() (asset.Archive, bool, error) {
|
||||||
|
return (*Output)(out).Archive()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the archive value when it is available.
|
// Apply applies a transformation to the archive value when it is available.
|
||||||
func (out *ArchiveOutput) Apply(applier func(asset.Archive) (interface{}, error)) *Output {
|
func (out *ArchiveOutput) Apply(applier func(asset.Archive) (interface{}, error)) *Output {
|
||||||
|
@ -324,7 +331,9 @@ func (out *ArchiveOutput) Apply(applier func(asset.Archive) (interface{}, error)
|
||||||
type ArrayOutput Output
|
type ArrayOutput Output
|
||||||
|
|
||||||
// Value returns the underlying array value.
|
// Value returns the underlying array value.
|
||||||
func (out *ArrayOutput) Value() ([]interface{}, error) { return (*Output)(out).Array() }
|
func (out *ArrayOutput) Value() ([]interface{}, bool, error) {
|
||||||
|
return (*Output)(out).Array()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the array value when it is available.
|
// Apply applies a transformation to the array value when it is available.
|
||||||
func (out *ArrayOutput) Apply(applier func([]interface{}) (interface{}, error)) *Output {
|
func (out *ArrayOutput) Apply(applier func([]interface{}) (interface{}, error)) *Output {
|
||||||
|
@ -337,7 +346,9 @@ func (out *ArrayOutput) Apply(applier func([]interface{}) (interface{}, error))
|
||||||
type AssetOutput Output
|
type AssetOutput Output
|
||||||
|
|
||||||
// Value returns the underlying asset value.
|
// Value returns the underlying asset value.
|
||||||
func (out *AssetOutput) Value() (asset.Asset, error) { return (*Output)(out).Asset() }
|
func (out *AssetOutput) Value() (asset.Asset, bool, error) {
|
||||||
|
return (*Output)(out).Asset()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the asset value when it is available.
|
// Apply applies a transformation to the asset value when it is available.
|
||||||
func (out *AssetOutput) Apply(applier func(asset.Asset) (interface{}, error)) *Output {
|
func (out *AssetOutput) Apply(applier func(asset.Asset) (interface{}, error)) *Output {
|
||||||
|
@ -350,7 +361,9 @@ func (out *AssetOutput) Apply(applier func(asset.Asset) (interface{}, error)) *O
|
||||||
type BoolOutput Output
|
type BoolOutput Output
|
||||||
|
|
||||||
// Value returns the underlying bool value.
|
// Value returns the underlying bool value.
|
||||||
func (out *BoolOutput) Value() (bool, error) { return (*Output)(out).Bool() }
|
func (out *BoolOutput) Value() (bool, bool, error) {
|
||||||
|
return (*Output)(out).Bool()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the bool value when it is available.
|
// Apply applies a transformation to the bool value when it is available.
|
||||||
func (out *BoolOutput) Apply(applier func(bool) (interface{}, error)) *Output {
|
func (out *BoolOutput) Apply(applier func(bool) (interface{}, error)) *Output {
|
||||||
|
@ -363,7 +376,9 @@ func (out *BoolOutput) Apply(applier func(bool) (interface{}, error)) *Output {
|
||||||
type Float32Output Output
|
type Float32Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Float32Output) Value() (float32, error) { return (*Output)(out).Float32() }
|
func (out *Float32Output) Value() (float32, bool, error) {
|
||||||
|
return (*Output)(out).Float32()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the float32 value when it is available.
|
// Apply applies a transformation to the float32 value when it is available.
|
||||||
func (out *Float32Output) Apply(applier func(float32) (interface{}, error)) *Output {
|
func (out *Float32Output) Apply(applier func(float32) (interface{}, error)) *Output {
|
||||||
|
@ -376,7 +391,9 @@ func (out *Float32Output) Apply(applier func(float32) (interface{}, error)) *Out
|
||||||
type Float64Output Output
|
type Float64Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Float64Output) Value() (float64, error) { return (*Output)(out).Float64() }
|
func (out *Float64Output) Value() (float64, bool, error) {
|
||||||
|
return (*Output)(out).Float64()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the float64 value when it is available.
|
// Apply applies a transformation to the float64 value when it is available.
|
||||||
func (out *Float64Output) Apply(applier func(float64) (interface{}, error)) *Output {
|
func (out *Float64Output) Apply(applier func(float64) (interface{}, error)) *Output {
|
||||||
|
@ -389,7 +406,9 @@ func (out *Float64Output) Apply(applier func(float64) (interface{}, error)) *Out
|
||||||
type IntOutput Output
|
type IntOutput Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *IntOutput) Value() (int, error) { return (*Output)(out).Int() }
|
func (out *IntOutput) Value() (int, bool, error) {
|
||||||
|
return (*Output)(out).Int()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the int value when it is available.
|
// Apply applies a transformation to the int value when it is available.
|
||||||
func (out *IntOutput) Apply(applier func(int) (interface{}, error)) *Output {
|
func (out *IntOutput) Apply(applier func(int) (interface{}, error)) *Output {
|
||||||
|
@ -402,7 +421,9 @@ func (out *IntOutput) Apply(applier func(int) (interface{}, error)) *Output {
|
||||||
type Int8Output Output
|
type Int8Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Int8Output) Value() (int8, error) { return (*Output)(out).Int8() }
|
func (out *Int8Output) Value() (int8, bool, error) {
|
||||||
|
return (*Output)(out).Int8()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the int8 value when it is available.
|
// Apply applies a transformation to the int8 value when it is available.
|
||||||
func (out *Int8Output) Apply(applier func(int8) (interface{}, error)) *Output {
|
func (out *Int8Output) Apply(applier func(int8) (interface{}, error)) *Output {
|
||||||
|
@ -415,7 +436,9 @@ func (out *Int8Output) Apply(applier func(int8) (interface{}, error)) *Output {
|
||||||
type Int16Output Output
|
type Int16Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Int16Output) Value() (int16, error) { return (*Output)(out).Int16() }
|
func (out *Int16Output) Value() (int16, bool, error) {
|
||||||
|
return (*Output)(out).Int16()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the int16 value when it is available.
|
// Apply applies a transformation to the int16 value when it is available.
|
||||||
func (out *Int16Output) Apply(applier func(int16) (interface{}, error)) *Output {
|
func (out *Int16Output) Apply(applier func(int16) (interface{}, error)) *Output {
|
||||||
|
@ -428,7 +451,9 @@ func (out *Int16Output) Apply(applier func(int16) (interface{}, error)) *Output
|
||||||
type Int32Output Output
|
type Int32Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Int32Output) Value() (int32, error) { return (*Output)(out).Int32() }
|
func (out *Int32Output) Value() (int32, bool, error) {
|
||||||
|
return (*Output)(out).Int32()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the int32 value when it is available.
|
// Apply applies a transformation to the int32 value when it is available.
|
||||||
func (out *Int32Output) Apply(applier func(int32) (interface{}, error)) *Output {
|
func (out *Int32Output) Apply(applier func(int32) (interface{}, error)) *Output {
|
||||||
|
@ -441,7 +466,7 @@ func (out *Int32Output) Apply(applier func(int32) (interface{}, error)) *Output
|
||||||
type Int64Output Output
|
type Int64Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Int64Output) Value() (int64, error) { return (*Output)(out).Int64() }
|
func (out *Int64Output) Value() (int64, bool, error) { return (*Output)(out).Int64() }
|
||||||
|
|
||||||
// Apply applies a transformation to the int64 value when it is available.
|
// Apply applies a transformation to the int64 value when it is available.
|
||||||
func (out *Int64Output) Apply(applier func(int64) (interface{}, error)) *Output {
|
func (out *Int64Output) Apply(applier func(int64) (interface{}, error)) *Output {
|
||||||
|
@ -454,7 +479,9 @@ func (out *Int64Output) Apply(applier func(int64) (interface{}, error)) *Output
|
||||||
type MapOutput Output
|
type MapOutput Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *MapOutput) Value() (map[string]interface{}, error) { return (*Output)(out).Map() }
|
func (out *MapOutput) Value() (map[string]interface{}, bool, error) {
|
||||||
|
return (*Output)(out).Map()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the number value when it is available.
|
// Apply applies a transformation to the number value when it is available.
|
||||||
func (out *MapOutput) Apply(applier func(map[string]interface{}) (interface{}, error)) *Output {
|
func (out *MapOutput) Apply(applier func(map[string]interface{}) (interface{}, error)) *Output {
|
||||||
|
@ -467,7 +494,9 @@ func (out *MapOutput) Apply(applier func(map[string]interface{}) (interface{}, e
|
||||||
type StringOutput Output
|
type StringOutput Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *StringOutput) Value() (string, error) { return (*Output)(out).String() }
|
func (out *StringOutput) Value() (string, bool, error) {
|
||||||
|
return (*Output)(out).String()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the number value when it is available.
|
// Apply applies a transformation to the number value when it is available.
|
||||||
func (out *StringOutput) Apply(applier func(string) (interface{}, error)) *Output {
|
func (out *StringOutput) Apply(applier func(string) (interface{}, error)) *Output {
|
||||||
|
@ -480,7 +509,9 @@ func (out *StringOutput) Apply(applier func(string) (interface{}, error)) *Outpu
|
||||||
type UintOutput Output
|
type UintOutput Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *UintOutput) Value() (uint, error) { return (*Output)(out).Uint() }
|
func (out *UintOutput) Value() (uint, bool, error) {
|
||||||
|
return (*Output)(out).Uint()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the uint value when it is available.
|
// Apply applies a transformation to the uint value when it is available.
|
||||||
func (out *UintOutput) Apply(applier func(uint) (interface{}, error)) *Output {
|
func (out *UintOutput) Apply(applier func(uint) (interface{}, error)) *Output {
|
||||||
|
@ -493,7 +524,9 @@ func (out *UintOutput) Apply(applier func(uint) (interface{}, error)) *Output {
|
||||||
type Uint8Output Output
|
type Uint8Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Uint8Output) Value() (uint8, error) { return (*Output)(out).Uint8() }
|
func (out *Uint8Output) Value() (uint8, bool, error) {
|
||||||
|
return (*Output)(out).Uint8()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the uint8 value when it is available.
|
// Apply applies a transformation to the uint8 value when it is available.
|
||||||
func (out *Uint8Output) Apply(applier func(uint8) (interface{}, error)) *Output {
|
func (out *Uint8Output) Apply(applier func(uint8) (interface{}, error)) *Output {
|
||||||
|
@ -506,7 +539,9 @@ func (out *Uint8Output) Apply(applier func(uint8) (interface{}, error)) *Output
|
||||||
type Uint16Output Output
|
type Uint16Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Uint16Output) Value() (uint16, error) { return (*Output)(out).Uint16() }
|
func (out *Uint16Output) Value() (uint16, bool, error) {
|
||||||
|
return (*Output)(out).Uint16()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the uint16 value when it is available.
|
// Apply applies a transformation to the uint16 value when it is available.
|
||||||
func (out *Uint16Output) Apply(applier func(uint16) (interface{}, error)) *Output {
|
func (out *Uint16Output) Apply(applier func(uint16) (interface{}, error)) *Output {
|
||||||
|
@ -519,7 +554,9 @@ func (out *Uint16Output) Apply(applier func(uint16) (interface{}, error)) *Outpu
|
||||||
type Uint32Output Output
|
type Uint32Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Uint32Output) Value() (uint32, error) { return (*Output)(out).Uint32() }
|
func (out *Uint32Output) Value() (uint32, bool, error) {
|
||||||
|
return (*Output)(out).Uint32()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the uint32 value when it is available.
|
// Apply applies a transformation to the uint32 value when it is available.
|
||||||
func (out *Uint32Output) Apply(applier func(uint32) (interface{}, error)) *Output {
|
func (out *Uint32Output) Apply(applier func(uint32) (interface{}, error)) *Output {
|
||||||
|
@ -532,7 +569,9 @@ func (out *Uint32Output) Apply(applier func(uint32) (interface{}, error)) *Outpu
|
||||||
type Uint64Output Output
|
type Uint64Output Output
|
||||||
|
|
||||||
// Value returns the underlying number value.
|
// Value returns the underlying number value.
|
||||||
func (out *Uint64Output) Value() (uint64, error) { return (*Output)(out).Uint64() }
|
func (out *Uint64Output) Value() (uint64, bool, error) {
|
||||||
|
return (*Output)(out).Uint64()
|
||||||
|
}
|
||||||
|
|
||||||
// Apply applies a transformation to the uint64 value when it is available.
|
// Apply applies a transformation to the uint64 value when it is available.
|
||||||
func (out *Uint64Output) Apply(applier func(uint64) (interface{}, error)) *Output {
|
func (out *Uint64Output) Apply(applier func(uint64) (interface{}, error)) *Output {
|
||||||
|
|
|
@ -26,10 +26,11 @@ func TestBasicOutputs(t *testing.T) {
|
||||||
{
|
{
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
resolve(42)
|
resolve(42, true)
|
||||||
}()
|
}()
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.NotNil(t, v)
|
assert.NotNil(t, v)
|
||||||
assert.Equal(t, 42, v.(int))
|
assert.Equal(t, 42, v.(int))
|
||||||
}
|
}
|
||||||
|
@ -38,7 +39,7 @@ func TestBasicOutputs(t *testing.T) {
|
||||||
go func() {
|
go func() {
|
||||||
reject(errors.New("boom"))
|
reject(errors.New("boom"))
|
||||||
}()
|
}()
|
||||||
v, err := out.Value()
|
v, _, err := out.Value()
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Nil(t, v)
|
assert.Nil(t, v)
|
||||||
}
|
}
|
||||||
|
@ -47,11 +48,12 @@ func TestBasicOutputs(t *testing.T) {
|
||||||
func TestArrayOutputs(t *testing.T) {
|
func TestArrayOutputs(t *testing.T) {
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
resolve([]interface{}{nil, 0, "x"})
|
resolve([]interface{}{nil, 0, "x"}, true)
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
v, err := out.Array()
|
v, known, err := out.Array()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.NotNil(t, v)
|
assert.NotNil(t, v)
|
||||||
if assert.Equal(t, 3, len(v)) {
|
if assert.Equal(t, 3, len(v)) {
|
||||||
assert.Equal(t, nil, v[0])
|
assert.Equal(t, nil, v[0])
|
||||||
|
@ -61,7 +63,7 @@ func TestArrayOutputs(t *testing.T) {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
arr := (*ArrayOutput)(out)
|
arr := (*ArrayOutput)(out)
|
||||||
v, err := arr.Value()
|
v, _, err := arr.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotNil(t, v)
|
assert.NotNil(t, v)
|
||||||
if assert.Equal(t, 3, len(v)) {
|
if assert.Equal(t, 3, len(v)) {
|
||||||
|
@ -75,17 +77,19 @@ func TestArrayOutputs(t *testing.T) {
|
||||||
func TestBoolOutputs(t *testing.T) {
|
func TestBoolOutputs(t *testing.T) {
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
resolve(true)
|
resolve(true, true)
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
v, err := out.Bool()
|
v, known, err := out.Bool()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.True(t, v)
|
assert.True(t, v)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
b := (*BoolOutput)(out)
|
b := (*BoolOutput)(out)
|
||||||
v, err := b.Value()
|
v, known, err := b.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.True(t, v)
|
assert.True(t, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,11 +101,12 @@ func TestMapOutputs(t *testing.T) {
|
||||||
"x": 1,
|
"x": 1,
|
||||||
"y": false,
|
"y": false,
|
||||||
"z": "abc",
|
"z": "abc",
|
||||||
})
|
}, true)
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
v, err := out.Map()
|
v, known, err := out.Map()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.NotNil(t, v)
|
assert.NotNil(t, v)
|
||||||
assert.Equal(t, 1, v["x"])
|
assert.Equal(t, 1, v["x"])
|
||||||
assert.Equal(t, false, v["y"])
|
assert.Equal(t, false, v["y"])
|
||||||
|
@ -109,8 +114,9 @@ func TestMapOutputs(t *testing.T) {
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
b := (*MapOutput)(out)
|
b := (*MapOutput)(out)
|
||||||
v, err := b.Value()
|
v, known, err := b.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.NotNil(t, v)
|
assert.NotNil(t, v)
|
||||||
assert.Equal(t, 1, v["x"])
|
assert.Equal(t, 1, v["x"])
|
||||||
assert.Equal(t, false, v["y"])
|
assert.Equal(t, false, v["y"])
|
||||||
|
@ -121,17 +127,19 @@ func TestMapOutputs(t *testing.T) {
|
||||||
func TestNumberOutputs(t *testing.T) {
|
func TestNumberOutputs(t *testing.T) {
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
resolve(42.345)
|
resolve(42.345, true)
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
v, err := out.Float64()
|
v, known, err := out.Float64()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, 42.345, v)
|
assert.Equal(t, 42.345, v)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
b := (*Float64Output)(out)
|
b := (*Float64Output)(out)
|
||||||
v, err := b.Value()
|
v, known, err := b.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, 42.345, v)
|
assert.Equal(t, 42.345, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,17 +147,19 @@ func TestNumberOutputs(t *testing.T) {
|
||||||
func TestStringOutputs(t *testing.T) {
|
func TestStringOutputs(t *testing.T) {
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
resolve("a stringy output")
|
resolve("a stringy output", true)
|
||||||
}()
|
}()
|
||||||
{
|
{
|
||||||
v, err := out.String()
|
v, known, err := out.String()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, "a stringy output", v)
|
assert.Equal(t, "a stringy output", v)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
b := (*StringOutput)(out)
|
b := (*StringOutput)(out)
|
||||||
v, err := b.Value()
|
v, known, err := b.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, "a stringy output", v)
|
assert.Equal(t, "a stringy output", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,11 +170,12 @@ func TestResolveOutputToOutput(t *testing.T) {
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
other, resolveOther, _ := NewOutput(nil)
|
other, resolveOther, _ := NewOutput(nil)
|
||||||
resolve(other)
|
resolve(other, true)
|
||||||
go func() { resolveOther(99) }()
|
go func() { resolveOther(99, true) }()
|
||||||
}()
|
}()
|
||||||
v, err := out.Value()
|
v, known, err := out.Value()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, v, 99)
|
assert.Equal(t, v, 99)
|
||||||
}
|
}
|
||||||
// Similarly, test that resolving an output to a rejected output yields an error.
|
// Similarly, test that resolving an output to a rejected output yields an error.
|
||||||
|
@ -172,10 +183,10 @@ func TestResolveOutputToOutput(t *testing.T) {
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() {
|
go func() {
|
||||||
other, _, rejectOther := NewOutput(nil)
|
other, _, rejectOther := NewOutput(nil)
|
||||||
resolve(other)
|
resolve(other, true)
|
||||||
go func() { rejectOther(errors.New("boom")) }()
|
go func() { rejectOther(errors.New("boom")) }()
|
||||||
}()
|
}()
|
||||||
v, err := out.Value()
|
v, _, err := out.Value()
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Nil(t, v)
|
assert.Nil(t, v)
|
||||||
}
|
}
|
||||||
|
@ -185,18 +196,34 @@ func TestOutputApply(t *testing.T) {
|
||||||
// Test that resolved outputs lead to applies being run.
|
// Test that resolved outputs lead to applies being run.
|
||||||
{
|
{
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() { resolve(42) }()
|
go func() { resolve(42, true) }()
|
||||||
var ranApp bool
|
var ranApp bool
|
||||||
b := (*IntOutput)(out)
|
b := (*IntOutput)(out)
|
||||||
app := b.Apply(func(v int) (interface{}, error) {
|
app := b.Apply(func(v int) (interface{}, error) {
|
||||||
ranApp = true
|
ranApp = true
|
||||||
return v + 1, nil
|
return v + 1, nil
|
||||||
})
|
})
|
||||||
v, err := app.Value()
|
v, known, err := app.Value()
|
||||||
assert.True(t, ranApp)
|
assert.True(t, ranApp)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, v, 43)
|
assert.Equal(t, v, 43)
|
||||||
}
|
}
|
||||||
|
// Test that resolved, but known outputs, skip the running of applies.
|
||||||
|
{
|
||||||
|
out, resolve, _ := NewOutput(nil)
|
||||||
|
go func() { resolve(42, false) }()
|
||||||
|
var ranApp bool
|
||||||
|
b := (*IntOutput)(out)
|
||||||
|
app := b.Apply(func(v int) (interface{}, error) {
|
||||||
|
ranApp = true
|
||||||
|
return v + 1, nil
|
||||||
|
})
|
||||||
|
_, known, err := app.Value()
|
||||||
|
assert.False(t, ranApp)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.False(t, known)
|
||||||
|
}
|
||||||
// Test that rejected outputs do not run the apply, and instead flow the error.
|
// Test that rejected outputs do not run the apply, and instead flow the error.
|
||||||
{
|
{
|
||||||
out, _, reject := NewOutput(nil)
|
out, _, reject := NewOutput(nil)
|
||||||
|
@ -207,7 +234,7 @@ func TestOutputApply(t *testing.T) {
|
||||||
ranApp = true
|
ranApp = true
|
||||||
return v + 1, nil
|
return v + 1, nil
|
||||||
})
|
})
|
||||||
v, err := app.Value()
|
v, _, err := app.Value()
|
||||||
assert.False(t, ranApp)
|
assert.False(t, ranApp)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Nil(t, v)
|
assert.Nil(t, v)
|
||||||
|
@ -215,24 +242,25 @@ func TestOutputApply(t *testing.T) {
|
||||||
// Test that an an apply that returns an output returns the resolution of that output, not the output itself.
|
// Test that an an apply that returns an output returns the resolution of that output, not the output itself.
|
||||||
{
|
{
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() { resolve(42) }()
|
go func() { resolve(42, true) }()
|
||||||
var ranApp bool
|
var ranApp bool
|
||||||
b := (*IntOutput)(out)
|
b := (*IntOutput)(out)
|
||||||
app := b.Apply(func(v int) (interface{}, error) {
|
app := b.Apply(func(v int) (interface{}, error) {
|
||||||
other, resolveOther, _ := NewOutput(nil)
|
other, resolveOther, _ := NewOutput(nil)
|
||||||
go func() { resolveOther(v + 1) }()
|
go func() { resolveOther(v+1, true) }()
|
||||||
ranApp = true
|
ranApp = true
|
||||||
return other, nil
|
return other, nil
|
||||||
})
|
})
|
||||||
v, err := app.Value()
|
v, known, err := app.Value()
|
||||||
assert.True(t, ranApp)
|
assert.True(t, ranApp)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
assert.True(t, known)
|
||||||
assert.Equal(t, v, 43)
|
assert.Equal(t, v, 43)
|
||||||
}
|
}
|
||||||
// Test that an an apply that reject an output returns the rejection of that output, not the output itself.
|
// Test that an an apply that reject an output returns the rejection of that output, not the output itself.
|
||||||
{
|
{
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
go func() { resolve(42) }()
|
go func() { resolve(42, true) }()
|
||||||
var ranApp bool
|
var ranApp bool
|
||||||
b := (*IntOutput)(out)
|
b := (*IntOutput)(out)
|
||||||
app := b.Apply(func(v int) (interface{}, error) {
|
app := b.Apply(func(v int) (interface{}, error) {
|
||||||
|
@ -241,7 +269,7 @@ func TestOutputApply(t *testing.T) {
|
||||||
ranApp = true
|
ranApp = true
|
||||||
return other, nil
|
return other, nil
|
||||||
})
|
})
|
||||||
v, err := app.Value()
|
v, _, err := app.Value()
|
||||||
assert.True(t, ranApp)
|
assert.True(t, ranApp)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
assert.Nil(t, v)
|
assert.Nil(t, v)
|
||||||
|
|
|
@ -107,16 +107,22 @@ func marshalInput(v interface{}) (interface{}, []Resource, error) {
|
||||||
}, nil, nil
|
}, nil, nil
|
||||||
case *Output:
|
case *Output:
|
||||||
// Await the value and return its raw value.
|
// Await the value and return its raw value.
|
||||||
ov, err := t.Value()
|
ov, known, err := t.Value()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
// TODO: unknownValue
|
|
||||||
e, d, err := marshalInput(ov)
|
if known {
|
||||||
if err != nil {
|
// If the value is known, marshal it.
|
||||||
return nil, nil, err
|
e, d, merr := marshalInput(ov)
|
||||||
|
if merr != nil {
|
||||||
|
return nil, nil, merr
|
||||||
|
}
|
||||||
|
return e, append(t.Deps(), d...), nil
|
||||||
|
} else {
|
||||||
|
// Otherwise, simply return the unknown value sentinel.
|
||||||
|
return rpcTokenUnknownValue, t.Deps(), nil
|
||||||
}
|
}
|
||||||
return e, append(t.Deps(), d...), err
|
|
||||||
case CustomResource:
|
case CustomResource:
|
||||||
// Resources aren't serializable; instead, serialize a reference to ID, tracking as a dependency.a
|
// Resources aren't serializable; instead, serialize a reference to ID, tracking as a dependency.a
|
||||||
e, d, err := marshalInput(t.ID())
|
e, d, err := marshalInput(t.ID())
|
||||||
|
@ -196,6 +202,11 @@ func unmarshalOutputs(outs *structpb.Struct) (map[string]interface{}, error) {
|
||||||
// unmarshalOutput unmarshals a single output variable into its runtime representation. For the most part, this just
|
// unmarshalOutput unmarshals a single output variable into its runtime representation. For the most part, this just
|
||||||
// returns the raw value. In a small number of cases, we need to change a type.
|
// returns the raw value. In a small number of cases, we need to change a type.
|
||||||
func unmarshalOutput(v interface{}) (interface{}, error) {
|
func unmarshalOutput(v interface{}) (interface{}, error) {
|
||||||
|
// Check for nils and unknowns.
|
||||||
|
if v == nil || v == rpcTokenUnknownValue {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
// In the case of assets and archives, turn these into real asset and archive structures.
|
// In the case of assets and archives, turn these into real asset and archive structures.
|
||||||
if m, ok := v.(map[string]interface{}); ok {
|
if m, ok := v.(map[string]interface{}); ok {
|
||||||
if m[rpcTokenSpecialSigKey] == rpcTokenSpecialAssetSig {
|
if m[rpcTokenSpecialSigKey] == rpcTokenSpecialAssetSig {
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
func TestMarshalRoundtrip(t *testing.T) {
|
func TestMarshalRoundtrip(t *testing.T) {
|
||||||
// Create interesting inputs.
|
// Create interesting inputs.
|
||||||
out, resolve, _ := NewOutput(nil)
|
out, resolve, _ := NewOutput(nil)
|
||||||
resolve("outputty")
|
resolve("outputty", true)
|
||||||
input := map[string]interface{}{
|
input := map[string]interface{}{
|
||||||
"s": "a string",
|
"s": "a string",
|
||||||
"a": true,
|
"a": true,
|
||||||
|
|
Loading…
Reference in a new issue