If part of a Step Function task contains a nested array, part of the array is serialized as an object with the indexes as their string keys.
The issue seems to be inside the TaskInput.fromObject call, more specifically in the recurseArray calls.
Reproduction Steps
let test = {
x: [["Hello", "world"], ["this", "is", "bugged"]]
}
console.log(util.inspect(renderObject(test), {showHidden: false, depth: null}))
What did you expect to happen?
This should have been returned:
{
x: [
['Hello', 'world'],
['this', 'is', 'bugged']
]
}
What actually happened?
It returned this:
{
x: [
{ '0': 'Hello', '1': 'world' },
{ '0': 'this', '1': 'is', '2': 'bugged' }
]
}
Environment
- CDK CLI Version : 1.98.0 (build 79f4512)
- Framework Version: ?
- Node.js Version: v12.22.1
- OS : Linux Red Hat 7.2.1-2
- Language (Version): TypeScript (4.0.5)
Other
After doing some research I noticed that for deeply nested arrays, every other array ends up mapped to an object. e.g.
let test = {
x: [[[[[[[[[[["Hi!"]]]]]]]]]]]
}
console.log(util.inspect(renderObject(test), {showHidden: false, depth: null}))
Output:
{
x: [
{
'0': [
{
'0': [
{
'0': [
{
'0': [ { '0': [ 'Hi!' ] } ]
}
]
}
]
}
]
}
]
}
The problem seems to come from this recurseArray method:
|
function recurseArray(key: string, arr: any[], handlers: FieldHandlers, visited: object[] = []): {[key: string]: any[] | string} { |
|
if (isStringArray(arr)) { |
|
const path = jsonPathStringList(arr); |
|
if (path !== undefined) { |
|
return handlers.handleList(key, arr); |
|
} |
|
|
|
// Fall through to correctly reject encoded strings inside an array. |
|
// They cannot be represented because there is no key to append a '.$' to. |
|
} |
|
|
|
return { |
|
[key]: arr.map(value => { |
|
if ((typeof value === 'string' && jsonPathString(value) !== undefined) |
|
|| (typeof value === 'number' && jsonPathNumber(value) !== undefined) |
|
|| (isStringArray(value) && jsonPathStringList(value) !== undefined)) { |
|
throw new Error('Cannot use JsonPath fields in an array, they must be used in objects'); |
|
} |
|
if (typeof value === 'object' && value !== null) { |
|
return recurseObject(value, handlers, visited); |
|
} |
|
return value; |
|
}), |
|
}; |
|
} |
Notice that it checks if value is an object before checking if it's an array, since arrays are actually considered objects by Javascript, it ends up calling the recurseObject method.
Recommendation:
- Check if
value is an array first and call the recurseArray method instead.
This is 🐛 Bug Report
If part of a Step Function task contains a nested array, part of the array is serialized as an object with the indexes as their string keys.
The issue seems to be inside the
TaskInput.fromObjectcall, more specifically in therecurseArraycalls.Reproduction Steps
What did you expect to happen?
This should have been returned:
What actually happened?
It returned this:
Environment
Other
After doing some research I noticed that for deeply nested arrays, every other array ends up mapped to an object. e.g.
Output:
The problem seems to come from this
recurseArraymethod:aws-cdk/packages/@aws-cdk/aws-stepfunctions/lib/json-path.ts
Lines 115 to 139 in 7966f8d
Notice that it checks if
valueis an object before checking if it's an array, since arrays are actually considered objects by Javascript, it ends up calling therecurseObjectmethod.Recommendation:
valueis an array first and call therecurseArraymethod instead.This is 🐛 Bug Report