Add back Computed<T> as a short-hand

This adds back Computed<T> as a short-hand for Promise<T | undefined>.
Subtly, all resource properties need to permit undefined flowing through
during planning  Rather than forcing the long-hand version, which is easy
to forget, we'll keep the convention of preferring Computed<T>.  It's
just a typedef and the runtime type is just a Promise.
This commit is contained in:
joeduffy 2017-09-20 09:57:18 -07:00 committed by Joe Duffy
parent f8ee6c570e
commit 1c2c972d37
5 changed files with 31 additions and 31 deletions

View file

@ -1,6 +1,6 @@
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
import { Property, PropertyValue } from "../resource";
import { Computed, ComputedValue } from "../resource";
import { Asset } from "./asset";
// An Archive represents a collection of named assets.
@ -12,9 +12,9 @@ export type AssetMap = {[name: string]: Asset};
// An AssetArchive is an archive created with a collection of named assets.
export class AssetArchive extends Archive {
public readonly assets: Property<AssetMap>; // a map of name to asset.
public readonly assets: Computed<AssetMap>; // a map of name to asset.
constructor(assets: PropertyValue<AssetMap>) {
constructor(assets: ComputedValue<AssetMap>) {
super();
this.assets = Promise.resolve<AssetMap | undefined>(assets);
}
@ -22,9 +22,9 @@ export class AssetArchive extends Archive {
// A FileArchive is an archive in a file-based archive in one of the supported formats (.tar, .tar.gz, or .zip).
export class FileArchive extends Archive {
public readonly path: Property<string>; // the path to the asset file.
public readonly path: Computed<string>; // the path to the asset file.
constructor(path: PropertyValue<string>) {
constructor(path: ComputedValue<string>) {
super();
this.path = Promise.resolve<string | undefined>(path);
}
@ -34,9 +34,9 @@ export class FileArchive extends Archive {
// protocol for fetching the archive's contents: `file://` is a local file (just like a FileArchive), `http://` and
// `https://` specify HTTP and HTTPS, respectively, and specific providers may recognize custom schemes.
export class RemoteArchive extends Archive {
public readonly uri: Property<string>; // the URI where the archive lives.
public readonly uri: Computed<string>; // the URI where the archive lives.
constructor(uri: PropertyValue<string>) {
constructor(uri: ComputedValue<string>) {
super();
this.uri = Promise.resolve<string | undefined>(uri);
}

View file

@ -1,6 +1,6 @@
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
import { Property, PropertyValue } from "../resource";
import { Computed, ComputedValue } from "../resource";
// Asset represents a blob of text or data that is managed as a first class entity.
export abstract class Asset {
@ -17,9 +17,9 @@ export class Blob extends Asset {
// FileAsset is a kind of asset produced from a given path to a file on the local filesystem.
export class FileAsset extends Asset {
public readonly path: Property<string>; // the path to the asset file.
public readonly path: Computed<string>; // the path to the asset file.
constructor(path: PropertyValue<string>) {
constructor(path: ComputedValue<string>) {
super();
this.path = Promise.resolve<string | undefined>(path);
}
@ -27,9 +27,9 @@ export class FileAsset extends Asset {
// StringAsset is a kind of asset produced from an in-memory UTF8-encoded string.
export class StringAsset extends Asset {
public readonly text: Property<string>; // the string contents.
public readonly text: Computed<string>; // the string contents.
constructor(text: PropertyValue<string>) {
constructor(text: ComputedValue<string>) {
super();
this.text = Promise.resolve<string | undefined>(text);
}
@ -39,9 +39,9 @@ export class StringAsset extends Asset {
// contents: `file://` specifies a local file, `http://` and `https://` specify HTTP and HTTPS, respectively. Note that
// specific providers may recognize alternative schemes; this is merely the base-most set that all providers support.
export class RemoteAsset extends Asset {
public readonly uri: Property<string>; // the URI where the asset lives.
public readonly uri: Computed<string>; // the URI where the asset lives.
constructor(uri: PropertyValue<string>) {
constructor(uri: ComputedValue<string>) {
super();
this.uri = Promise.resolve<string | undefined>(uri);
}

View file

@ -11,12 +11,12 @@ export abstract class Resource {
public readonly urn: Promise<URN>;
// id is the provider-assigned unique ID for this resource. It is set during deployments and may be missing
// (undefined) during planning phases.
public readonly id: Property<ID>;
public readonly id: Computed<ID>;
// creates and registers a new resource object. t is the fully qualified type token and name is the "name" part
// to use in creating a stable and globally unique URN for the object. dependsOn is an optional list of other
// resources that this resource depends on, controlling the order in which we perform resource operations.
constructor(t: string, name: string, props: PropertyValues, dependsOn?: Resource[]) {
constructor(t: string, name: string, props: ComputedValues, dependsOn?: Resource[]) {
if (t === undefined || t === "") {
throw new Error("Missing resource type argument");
}
@ -31,13 +31,13 @@ export abstract class Resource {
}
}
// Property is a property output for a resource. It is just a promise that also permits undefined values. The
// Computed is a property output for a resource. It is just a promise that also permits undefined values. The
// undefined values are used during planning, when the actual final value of a resource may not yet be known.
export type Property<T> = Promise<T | undefined>;
export type Computed<T> = Promise<T | undefined>;
// PropertyValue is a property input for a resource. It may be a promptly available T or a promise for one.
export type PropertyValue<T> = T | undefined | Property<T>;
// ComputedValue is a property input for a resource. It may be a promptly available T or a promise for one.
export type ComputedValue<T> = T | undefined | Computed<T>;
// PropertyValues is a map of property name to optional property input, one for each resource property value.
export type PropertyValues = {[key: string]: PropertyValue<any> | undefined};
// ComputedValues is a map of property name to optional property input, one for each resource property value.
export type ComputedValues = {[key: string]: ComputedValue<any> | undefined};

View file

@ -1,7 +1,7 @@
// Copyright 2016-2017, Pulumi Corporation. All rights reserved.
import * as asset from "../asset";
import { ID, PropertyValue, PropertyValues, Resource, URN } from "../resource";
import { ID, ComputedValue, ComputedValues, Resource, URN } from "../resource";
import { errorString, debuggablePromise } from "./debuggable";
import { Log } from "./log";
import { getMonitor, options, rpcKeepAlive, serialize } from "./settings";
@ -21,7 +21,7 @@ let resourceChain: Promise<void> = Promise.resolve();
// registerResource registers a new resource object with a given type t and name. It returns the auto-generated URN
// and the ID that will resolve after the deployment has completed. All properties will be initialized to property
// objects that the registration operation will resolve at the right time (or remain unresolved for deployments).
export function registerResource(res: Resource, t: string, name: string, props: PropertyValues | undefined,
export function registerResource(res: Resource, t: string, name: string, props: ComputedValues | undefined,
dependsOn: Resource[] | undefined): void {
Log.debug(
`Registering resource: t=${t}, name=${name}` +
@ -128,7 +128,7 @@ interface PropertyTransfer {
// transferProperties stores the properties on the resource object and returns a gRPC serializable
// proto.google.protobuf.Struct out of a resource's properties.
function transferProperties(res: Resource, t: string, name: string, props: PropertyValues | undefined,
function transferProperties(res: Resource, t: string, name: string, props: ComputedValues | undefined,
dependsOn: Resource[] | undefined): Promise<PropertyTransfer> {
// First set up an array of all promises that we will await on before completing the transfer.
let eventuals: Promise<any>[] = [];
@ -187,7 +187,7 @@ function transferProperties(res: Resource, t: string, name: string, props: Prope
// resolveProperties takes as input a gRPC serialized proto.google.protobuf.Struct and resolves all of the
// resource's matching properties to the values inside.
function resolveProperties(res: Resource, transfer: PropertyTransfer,
t: string, name: string, inputs: PropertyValues | undefined, outputsStruct: any, stable: boolean): void {
t: string, name: string, inputs: ComputedValues | undefined, outputsStruct: any, stable: boolean): void {
// Produce a combined set of property states, starting with inputs and then applying outputs. If the same
// property exists in the inputs and outputs states, the output wins.
@ -243,8 +243,8 @@ function resolveProperties(res: Resource, transfer: PropertyTransfer,
}
}
// unknownPropertyValue is a special value that the monitor recognizes.
export const unknownPropertyValue = "04da6b54-80e4-46f7-96ec-b56ff0331ba9";
// unknownComputedValue is a special value that the monitor recognizes.
export const unknownComputedValue = "04da6b54-80e4-46f7-96ec-b56ff0331ba9";
// specialSigKey is sometimes used to encode type identity inside of a map. See pkg/resource/properties.go.
export const specialSigKey = "4dabf18193072939515e22adb298388d";
// specialAssetSig is a randomly assigned hash used to identify assets in maps. See pkg/resource/asset.go.
@ -262,7 +262,7 @@ async function serializeProperty(prop: any, ctx?: string): Promise<any> {
if (!options.dryRun) {
Log.error(`Unexpected unknown property during deployment`);
}
return unknownPropertyValue;
return unknownComputedValue;
}
else if (prop === null || typeof prop === "boolean" ||
typeof prop === "number" || typeof prop === "string") {

View file

@ -176,8 +176,8 @@ describe("rpc", () => {
if (dryrun) {
// If this is a dry-run, we won't have the real values:
assert.deepEqual(res, {
otherIn: runtime.unknownPropertyValue,
otherOut: runtime.unknownPropertyValue,
otherIn: runtime.unknownComputedValue,
otherOut: runtime.unknownComputedValue,
});
}
else {