I would like to create a dynamodb step using the resource arn:aws:states:::dynamodb:getItem
where some attributes may not be available.
The relevant portion of my task is as follows:
"Get Config": {
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:getItem",
"Next": "DummyPass",
"Parameters": {
"Key": {
"pk": { "S.$": "$.someKey)" },
"sk": { "S": "A" }
},
"TableName": "test_table"
},
"ResultPath": "$.config",
"ResultSelector": {
"foo.$": "$.Item.foo.S",
"bar.$": "$.Item.bar.S"
},
}
Within this task I am attempting to select the DynamoDB typed annotated items and flatten into a JSON structure of just the keys and values.
There appears to not be any intrinsic functions available that could achieve this.
The workaround is to create a lambda, though it defeats the purpose of services now being supported by step functions.
I've resolved this by creating a lambda that exposes jmespath that takes a Data
and Expression
attribute:
const jmespath = require("jmespath");
// convenience that ensures correct sync vs async lambda definition.
const { wrappedApply } = require("./lambda-tools.js");
function jmespathHandler (options) {
return jmespath.search(options.Data, options.Expression);
}
module.exports = {
jmespathHandler: wrappedApply(jmespathHandler)
};
Given an input of the following:
{
"x": {
"foo": "FOO",
"bar": "BAR",
},
"y": {
"bat": "BAT"
}
}
I can now call this function with a definition like:
{
"Type": "Task"
"Resource": "arn:aws:states:::lambda:invoke",
"End": true,
"ResultSelector": {
"FormatOutput.$": "$.Payload"
},
"OutputPath": "$.FormatOutput",
"Parameters": {
"FunctionName": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
"Payload": {
"Data.$": "States.JsonMerge($.x, $.y, false)",
"Expression": "{Foo: foo, Bar: bar, Bat: bat, Baz: baz}"
}
},
}
Which will now output the following json.
{
"Foo": "FOO",
"Bar": "BAR",
"Bat": "BAT",
"Baz": null
}
You can see here that I've renamed the keys but also included a missing key Baz
that because selected will become null.
Note that I've demonstrated using States.JsonMerge
which could have been achieved within jmespath directly, however I just used this to demonstrate another tool you may find useful.
For completeness I'm including wrappedApply
here:
function wrappedApply (action) {
if (action.constructor.name === "AsyncFunction") {
return action;
}
/* eslint consistent-return: "off" */
return function (data, ctx, callback) {
if (typeof callback !== "function") {
return action(data, ctx, callback);
}
try {
const res = action(data, ctx, callback);
callback(null, res);
} catch (e) {
callback(e);
}
};
}
This can then be called to