Search code examples
typescriptamazon-web-servicesamazon-snsaws-cdkaws-step-functions

AWS CDK Step Functions SNS Task - input path parameter injected into message


I'm trying to make a step function workflow for processing transactions and broadcasting a message when the transaction has been successful. Came up with something like this:

new SnsPublish(this, 'publish-successful-transaction', {
            message: TaskInput.fromText(`transaction #${TaskInput.fromJsonPathAt('$.transactionId')} was processed successfully!`),
            resultPath: '$.message',
            topic: this.notification.topic
        })

However, predictably .fromJsonPath() returns an instance of TaskInput which results in the normal object-in-a-string behaviour i.e [Object object].

I tried getting the value property from the TaskInput instance but that results in an error:

Error: Field references must be the entire string, cannot concatenate them
(found 'transaction #${Token[transactionId.269]} was processed successfully!')

Is there a way to have a custom text message with injected input path parameter? Can't seem to find anything on this even though it seems like quite a straightforward use case. What am I missing?


Solution

  • <Edit> Turns out there is a simple, one-line solution after all:

    message: sfn.TaskInput.fromJsonPathAt("States.Format('transaction {} was processed successfully!', $.transactionId)")
    

    The answer's original options are valid but more verbose </Edit>


    Step Functions have a States.Format intrinsic function to interpolate strings. But because intrinsic functions aren't accepted in all fields (including not in SnsPublish.message), the solution is a bit less straightforward than you might hope.

    First, the formatting. There are two equivalent CDK syntaxes for intrinsic functions:

    // produce identical State Machine JSON output:
    {
      // State Machine Language JSON Syntax - DON'T FORGET .$ SUFFIX IN THE KEY
      'myMessage.$' "States.Format('transaction {} was processed successfully!', $.transactionId)",
      // JsonPath helpers - CDK only
      sameMessage: sfn.JsonPath.format('transaction {} was processed successfully!', sfn.JsonPath.stringAt('$.transactionId'))
    }
    

    Next, you have two options to wire things up:

    Option 1: sfn.Pass + tasks.SnsPublish

    Add a Pass task to format the message and use the result inSnsPublish's message property:

    const formatMessage = new sfn.Pass(this, 'formatMessage', {
      parameters: { 'message.$': "States.Format('transaction {} was processed successfully!', $.transactionId)", },
      resultPath: '$.formatted',
    });
    
    const snsPublish = new tasks.SnsPublish(this, 'SnsPublish', {
      topic: this.notification.topic,
      message: sfn.TaskInput.fromJsonPathAt('$.formatted.message'),
    });
    

    Option 2: tasks.CallAwsService

    The parameters property of the generic CallAwsService task can handle intrinsic functions. Format and publish in a single task state:

    const snsPublish2 = new tasks.CallAwsService(this, 'SnsPublish2', {
      service: 'sns',
      action: 'publish',
      parameters: {
        TopicArn: this.notification.topic.topicArn,
        'Message.$': "States.Format('transaction {} was processed successfully!', $.transactionId)",
      },
      iamResources: [this.notification.topic.topicArn],
    });