Preserve nested objects in failure payloads #261
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Context
I am trying to save additional context and debugging information to the completion task in the case of a failed task, using code such as the following:
Bug
This works fine if
foo
is a number, string, or array. However iffoo
is a non-array object then pg-boss will convert it to the empty object. So if I do:I will end up with:
in the response property of my completion task.
Cause of the bug
This is caused by the following line in the
mapCompletionDataArg
function inmanager.js
:Here
Object.getOwnPropertyNames(data)
is an array of strings that functions as a replacer function, which means that only properties whose name is in the array will get serialised. This is a clever trick to ensure that the non-enumerableerror
andstack
properties get serialized. Unfortunately the replacer function/array also gets applied to properties in any nested objects.In our example above
Object.getOwnPropertyNames(data)
will return['message', 'stack', 'foo']
. As the stringbar
is not included in this array, when the{ bar: 'baz' }
object is filtered by the replacer array thebar
property is filtered out (if the nested object had more properties, then any other properties that didn't happen to share a name with a property on the top-level object would also be filtered out).Solution
I have replaced the problematic line above with the following code:
It uses a "dumb loop" to perform the object clone, avoiding the need for
JSON.stringify
entirely. I did it this way because I thought it would be faster, but it does mean that we have a shallow clone of the object rather than deep clone. I believe this shouldn't make any difference here, but if you did want a deep clone then you could runnewData
through a standardJSON.parse(JSON.serialize(...))
(no replacer needed) to get a deep clone which still retains the nested property data and the message and stack properties.Notes
I've also added a test for this case, and a section to the README explaining how to setup and run the test suite.