Skip to content

Commit e4fb811

Browse files
RomainMullerrix0rrr
authored andcommitted
fix(core): Make filterUndefined null-safe (#2789)
There are a couple of places where fields accept values that are typed as `Json` per the JSII type specification. This conveys that literal `null` values may be passed and need to be preserved (as far as JSII is concerned - see aws/jsii#523). However in the CloudFormation domain, `null` is semantically equivalent to `undefined`. Now enters Javascript's confusing type system, where `null` is an `object` that cannot be converted to `object` (you read this correctly): ```js typeof null === 'object' // => true Object.entries(null); // => Thrown: // TypeError: Cannot convert undefined or null to object // at Function.entries (<canonymous>) ``` So this changes the `undefined` checks to the `null`-coercing way, so that `null` and `undefined` are handled the same way.
1 parent 5c90256 commit e4fb811

File tree

3 files changed

+20
-8
lines changed

3 files changed

+20
-8
lines changed

packages/@aws-cdk/cdk/lib/util.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,17 @@ export function ignoreEmpty(obj: any): any {
5353
}
5454

5555
/**
56-
* Returns a copy of `obj` without undefined values in maps or arrays.
56+
* Returns a copy of `obj` without `undefined` (or `null`) values in maps or arrays.
5757
*/
5858
export function filterUndefined(obj: any): any {
5959
if (Array.isArray(obj)) {
60-
return obj.filter(x => x !== undefined).map(x => filterUndefined(x));
60+
return obj.filter(x => x != null).map(x => filterUndefined(x));
6161
}
6262

6363
if (typeof(obj) === 'object') {
6464
const ret: any = { };
6565
for (const [key, value] of Object.entries(obj)) {
66-
if (value === undefined) {
66+
if (value == null) {
6767
continue;
6868
}
6969
ret[key] = filterUndefined(value);

packages/@aws-cdk/cdk/test/test.util.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import { Test } from 'nodeunit';
1+
import { Test, testCase } from 'nodeunit';
22
import { Stack } from '../lib';
3-
import { capitalizePropertyNames, ignoreEmpty } from '../lib/util';
3+
import { capitalizePropertyNames, filterUndefined, ignoreEmpty } from '../lib/util';
44

5-
export = {
5+
export = testCase({
66
'capitalizeResourceProperties capitalizes all keys of an object (recursively) from camelCase to PascalCase'(test: Test) {
77
const c = new Stack();
88

@@ -71,8 +71,20 @@ export = {
7171
test.deepEqual(stack.resolve(ignoreEmpty({ xoo: { resolve: () => [ undefined, undefined ] }})), { xoo: [] });
7272
test.done();
7373
}
74+
},
75+
76+
'filterUnderined': {
77+
'is null-safe (aka treats null and undefined the same)'(test: Test) {
78+
test.deepEqual(filterUndefined({ 'a null': null, 'a not null': true }), { 'a not null': true });
79+
test.done();
80+
},
81+
82+
'removes undefined, but leaves the rest'(test: Test) {
83+
test.deepEqual(filterUndefined({ 'an undefined': undefined, 'yes': true }), { yes: true });
84+
test.done();
85+
}
7486
}
75-
};
87+
});
7688

7789
class SomeToken {
7890
public foo = 60;

packages/@aws-cdk/cdk/test/util.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ import { ConstructNode, Stack } from '../lib';
22

33
export function toCloudFormation(stack: Stack): any {
44
return ConstructNode.synth(stack.node, { skipValidation: true }).getStack(stack.name).template;
5-
}
5+
}

0 commit comments

Comments
 (0)