Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tokens): enable type coercion #2680

Merged
merged 2 commits into from
Jun 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 12 additions & 14 deletions packages/@aws-cdk/cdk/lib/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,16 +93,16 @@ export class Token {
* on the string.
*/
public toString(): string {
const valueType = typeof this.valueOrFunction;
// Optimization: if we can immediately resolve this, don't bother
// registering a Token.
if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') {
return this.valueOrFunction.toString();
// registering a Token (unless it's already a token).
if (typeof(this.valueOrFunction) === 'string') {
return this.valueOrFunction;
}

if (this.tokenStringification === undefined) {
this.tokenStringification = TokenMap.instance().registerString(this, this.displayName);
}

return this.tokenStringification;
}

Expand Down Expand Up @@ -139,9 +139,8 @@ export class Token {
* is constructing a `FnJoin` or a `FnSelect` on it.
*/
public toList(): string[] {
const valueType = typeof this.valueOrFunction;
if (valueType === 'string' || valueType === 'number' || valueType === 'boolean') {
throw this.newError('Got a literal Token value; only intrinsics can ever evaluate to lists.');
if (Array.isArray(this.valueOrFunction)) {
return this.valueOrFunction;
}

if (this.tokenListification === undefined) {
Expand All @@ -160,14 +159,13 @@ export class Token {
* other operations can and probably will destroy the token-ness of the value.
*/
public toNumber(): number {
// Optimization: if we can immediately resolve this, don't bother
// registering a Token.
if (typeof(this.valueOrFunction) === 'number') {
return this.valueOrFunction;
}

if (this.tokenNumberification === undefined) {
const valueType = typeof this.valueOrFunction;
// Optimization: if we can immediately resolve this, don't bother
// registering a Token.
if (valueType === 'number') { return this.valueOrFunction; }
if (valueType !== 'function') {
throw this.newError(`Token value is not number or lazy, can't represent as number: ${this.valueOrFunction}`);
}
this.tokenNumberification = TokenMap.instance().registerNumber(this);
}

Expand Down
88 changes: 86 additions & 2 deletions packages/@aws-cdk/cdk/test/test.tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -467,15 +467,14 @@ export = {

'can number-encode and resolve Token objects'(test: Test) {
// GIVEN
const stack = new Stack();
const x = new Token(() => 123);

// THEN
const encoded = x.toNumber();
test.equal(true, Token.isToken(encoded), 'encoded number does not test as token');

// THEN
const resolved = stack.node.resolve({ value: encoded });
const resolved = resolve({ value: encoded });
test.deepEqual(resolved, { value: 123 });

test.done();
Expand Down Expand Up @@ -522,6 +521,91 @@ export = {
const token = fn1();
test.throws(() => token.throwError('message!'), /Token created:/);
test.done();
},

'type coercion': (() => {
const tests: any = { };

const inputs = [
() => 'lazy',
'a string',
1234,
{ an_object: 1234 },
[ 1, 2, 3 ],
false
];

for (const input of inputs) {
// GIVEN
const stringToken = new Token(input).toString();
const numberToken = new Token(input).toNumber();
const listToken = new Token(input).toList();

// THEN
const expected = typeof(input) === 'function' ? input() : input;

tests[`${input}<string>.toNumber()`] = (test: Test) => {
test.deepEqual(resolve(new Token(stringToken).toNumber()), expected);
test.done();
};

tests[`${input}<list>.toNumber()`] = (test: Test) => {
test.deepEqual(resolve(new Token(listToken).toNumber()), expected);
test.done();
};

tests[`${input}<number>.toNumber()`] = (test: Test) => {
test.deepEqual(resolve(new Token(numberToken).toNumber()), expected);
test.done();
};

tests[`${input}<string>.toString()`] = (test: Test) => {
test.deepEqual(resolve(new Token(stringToken).toString()), expected);
test.done();
};

tests[`${input}<list>.toString()`] = (test: Test) => {
test.deepEqual(resolve(new Token(listToken).toString()), expected);
test.done();
};

tests[`${input}<number>.toString()`] = (test: Test) => {
test.deepEqual(resolve(new Token(numberToken).toString()), expected);
test.done();
};

tests[`${input}<string>.toList()`] = (test: Test) => {
test.deepEqual(resolve(new Token(stringToken).toList()), expected);
test.done();
};

tests[`${input}<list>.toList()`] = (test: Test) => {
test.deepEqual(resolve(new Token(listToken).toList()), expected);
test.done();
};

tests[`${input}<number>.toList()`] = (test: Test) => {
test.deepEqual(resolve(new Token(numberToken).toList()), expected);
test.done();
};
}

return tests;
})(),

'toXxx short circuts if the input is of the same type': {
'toNumber(number)'(test: Test) {
test.deepEqual(new Token(123).toNumber(), 123);
test.done();
},
'toList(list)'(test: Test) {
test.deepEqual(new Token([1, 2, 3]).toList(), [1, 2, 3]);
test.done();
},
'toString(string)'(test: Test) {
test.deepEqual(new Token('string').toString(), 'string'),
test.done();
}
}
};

Expand Down