Reputation: 41
I am trying to pass value dynamically to MessageAttribute parameter while doing an SNS publish from a Step function.
reference: https://docs.aws.amazon.com/step-functions/latest/dg/connect-sns.html
As per the example provided in the documentation if you want to publish to SNS from a Step function:
{
"StartAt": "Publish to SNS",
"States": {
"Publish to SNS": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "arn:aws:sns:us-east-1:111222333444:myTopic",
"Message.$": "$.input.message",
"MessageAttributes": {
"my attribute no 1": {
"DataType": "String",
"StringValue": "value of my attribute no 1"
},
"my attribute no 2": {
"DataType": "String",
"StringValue": "value of my attribute no 2"
}
}
},
"End": true
}
}
}
Now suppose my input to the state machine is as follows:
"SNSDetails": {
"attribute1": "some value",
"attribute2": "some other value",
}
How can I dynamically access the $.SNSDetails.attribute1 in the "StringValue" of "my attribute no 1" and similarly access $.SNSDetails.attribute2 in the StringValue of "my attribute no 2" instead of hardcoding it?
Ultimately I want the state machine to translate the value of "my attribute no 1" as "some value" and value of "my attribute no 2" as "some other value"
Thanks in advance.
Upvotes: 3
Views: 2662
Reputation: 12043
Stéphane Bouché is an excellent answer that helped me figure this out.
In my case, I needed to pass a boolean value to the header.
Amazon SNS supported message attribute data types are String, String Array, Number, and Binary.
See here
To work around this I used the Intrinsic function States.JsonToString
see here. I am sure this will be useful in many use cases.
Let's assume
"SNSDetails": {
"attribute0": false,
"attribute1": "some value",
"attribute2": "some other value",
}
{
"StartAt": "Publish to SNS",
"States": {
"Publish to SNS": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "arn:aws:sns:us-east-1:111222333444:myTopic",
"Message.$": "$.input.message",
"MessageAttributes": {
"my attribute no 1": {
"DataType": "String",
"StringValue.$": "States.JsonToString($.SNSDetails.attribute0)"
},
...
}
},
"End": true
}
}
}
Upvotes: 0
Reputation: 151
Yeah, so I ran into this exact same problem today ... this is how I handled it in a lambda:
attributes = {}
for key in event['request']['userAttributes']:
try:
value = event['request']['userAttributes'][key]
if isinstance(value, str) == False:
value = json.dumps(value)
attributes[key.replace(':', '-')] = {
'DataType': 'String',
'StringValue': value
}
except:
pass # do nothing
I only cared about some data from the incoming event ... and there is was the possibility that the value was not a string, so it had to be stringified before it could be added to the SNS event. The resulting variable could be passed directly into the MessageAttributes of the SNS event:
response = client.publish (
TargetArn = arn,
Message = 'some message',
MessageAttributes = attributes
)
Upvotes: 0
Reputation: 86
You can do so by appending .$
to the StringValue
attribute name: this will instruct the engine to evaluate the attribute value as a JSON Path.
Given this input payload:
{
"input": {
"message": "Hello world"
},
"SNSDetails": {
"attribute1": "some value",
"attribute2": "some other value",
}
}
...and this StepFunction code:
{
"StartAt": "Publish to SNS",
"States": {
"Publish to SNS": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "arn:aws:sns:us-east-1:111222333444:myTopic",
"Message.$": "$.input.message",
"MessageAttributes": {
"my attribute no 1": {
"DataType": "String",
"StringValue.$": "$.SNSDetails.attribute1"
},
"my attribute no 2": {
"DataType": "String",
"StringValue.$": "$.SNSDetails.attribute2"
}
}
},
"End": true
}
}
}
...it will correctly be resolved.
You can easily check the result in the Execution event history (TaskScheduled details) on your AWS console (Step Functions).
Upvotes: 4