AWS Step Functionsの新しい組み込み関数がAWS CDKの専用クラスで実装できるようになっていました

2022.11.26

こんにちは、CX事業本部 IoT事業部の若槻です。

AWS Step Functionsの大型アップデートとして、今年の8月に14個の新しい組み込み関数(Intrinsic functions)が追加されました。

組み込み関数を使うと配列操作やJsonオブジェクト操作などのデータ処理を追加のTask(AWS Lambdaなど)を呼び出すことなく行えるのですが、この時のアップデートにより、より多くの操作が組み込み関数を使用して行えるようになり、Step Functionsの実装をより簡潔化できるようになりました。

しかし残念ながら、当時は、まだそれらの新しい組み込み関数を実装できるAWS CDKの専用クラスは提供されていなかったため、CDKで新しい組み込み関数を実装したい場合は"States.MathAdd(6, 4)"のように関数をベタ書きで記述する必要がありました。

しかし本日改めて確認してみると、新しい組み込み関数の専用クラスがCDKでも利用可能となっていたので、試してみました。

やってみた

CDK Construct Libraryのアップグレード

リリース履歴を追ってみると、CDK Construct Libraryで削除保護の設定がサポートされたのは本記事の執筆日の25日前にリリースされたv2.50.0からでした。

stepfunctions: add intrinsic functions (#22431) (8f85b08), closes #22068 #22629

必要に応じてライブラリをアップグレードします。

npm i aws-cdk-lib@latest aws-cdk@latest constructs@latest

私の環境の場合は前回のエントリで別の新機能を使えるようにするために既に2.51.0アップグレード済みでした。

実装

新しい組み込み関数を含む、現在使える18個すべての組み込み関数の実装を試してみます。(ちなみに下記表および検証方法は、のんぴのエントリを参考しました)

組み込み関数 説明
States.Array 配列を返す
States.ArrayPartition 配列を分割する
States.ArrayContains 特定の値が配列に存在するかどうか返す
States.ArrayRange 特定の範囲の要素を含む配列を作成する
States.ArrayGetItem 配列の指定されたインデックスの値を返す
States.ArrayLength 配列の長さを返す
States.ArrayUnique 配列から重複する値を削除して一意の要素のみを含む配列を返す
States.Base64Encode MIME Base64でをエンコードする
States.Base64Decode MIME Base64でデコードする
States.Hash ハッシュ値を計算する
States.JsonMerge 2つのJSONオブジェクトを1つのオブジェクトにマージする
States.StringToJson エスケープされたJSON文字列からJSONオブジェクトを返す
States.JsonToString JSONオブジェクトからエスケープされたJSON文字列を返す
States.MathRandom 指定された開始番号と終了番号の間の乱数を返す
States.MathAdd 2つの数値の合計を返す
States.StringSplit 文字列を配列に分割する
States.UUID UUID v4を返す
States.Format テンプレートリテラルと補完値から文字列を返す

CDK Stackの実装です。aws_stepfunctions.JsonPathに含まれる組み込み関数の専用クラスを使用しています。

lib/aws-cdk-app-stack.ts

import { aws_stepfunctions, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class AwsCdkAppStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    const intrinsicFunctionsPass = new aws_stepfunctions.Pass(
      this,
      'intrinsicFunctionsPass',
      {
        parameters: {
          StatesArray: aws_stepfunctions.JsonPath.array(
            aws_stepfunctions.JsonPath.stringAt('$.json1'),
            aws_stepfunctions.JsonPath.stringAt('$.json2')
          ),
          StatesArrayPartition: aws_stepfunctions.JsonPath.arrayPartition(
            aws_stepfunctions.JsonPath.listAt('$.inputArray'),
            2
          ),
          StatesArrayContains: aws_stepfunctions.JsonPath.arrayContains(
            aws_stepfunctions.JsonPath.listAt('$.inputArray'),
            'd'
          ),
          StatesArrayRange: aws_stepfunctions.JsonPath.arrayRange(0, 1000, 333),
          StatesArrayGetItem: aws_stepfunctions.JsonPath.arrayGetItem(
            aws_stepfunctions.JsonPath.listAt('$.inputArray'),
            3
          ),
          StatesArrayLength: aws_stepfunctions.JsonPath.arrayLength(
            aws_stepfunctions.JsonPath.listAt('$.inputArray')
          ),
          StatesArrayUnique: aws_stepfunctions.JsonPath.arrayUnique(
            aws_stepfunctions.JsonPath.listAt('$.inputArray')
          ),
          StatesBase64Encode: aws_stepfunctions.JsonPath.base64Encode(
            aws_stepfunctions.JsonPath.stringAt('$.inputText')
          ),
          StatesBase64Decode: aws_stepfunctions.JsonPath.base64Decode(
            aws_stepfunctions.JsonPath.stringAt('$.inputBase64')
          ),
          StatesHash: aws_stepfunctions.JsonPath.hash(
            aws_stepfunctions.JsonPath.stringAt('$.inputText'),
            'SHA-512'
          ),
          StatesJsonMerge: aws_stepfunctions.JsonPath.jsonMerge(
            aws_stepfunctions.JsonPath.stringAt('$.json1'),
            aws_stepfunctions.JsonPath.stringAt('$.json2')
          ),
          StatesStringToJson:
            aws_stepfunctions.JsonPath.stringToJson('{"foo": "bar"}'),
          StatesJsonToString: aws_stepfunctions.JsonPath.jsonToString(
            aws_stepfunctions.JsonPath.stringAt('$.json1')
          ),
          StatesMathRandom: aws_stepfunctions.JsonPath.mathRandom(0, 10000),
          StatesMathAdd: aws_stepfunctions.JsonPath.mathAdd(200, 12),
          StatesStringSplit: aws_stepfunctions.JsonPath.stringSplit(
            aws_stepfunctions.JsonPath.stringAt('$.inputText'),
            'me'
          ),
          StatesUUID: aws_stepfunctions.JsonPath.uuid(),
          StatesFormat: aws_stepfunctions.JsonPath.format(
            'Hello, this is {}.',
            aws_stepfunctions.JsonPath.stringAt('$.inputText')
          ),
        },
      }
    );

    new aws_stepfunctions.StateMachine(this, 'myStateMachine', {
      stateMachineName: 'myStateMachine',
      definition: intrinsicFunctionsPass,
    });
  }
}

CDK Deployし、次のInputを指定してState Machineを実行します。

Input

{
  "inputArray": [
    "a",
    "a",
    "b",
    "c",
    "c",
    "c",
    "d",
    "e",
    "e",
    "f",
    "g"
  ],
  "inputText": "classmethod",
  "inputBase64": "44Kv44Op44K544Oh44K944OD44OJ",
  "json1": {
    "a": {
      "a1": 1,
      "a2": 2
    },
    "b": 2
  },
  "json2": {
    "a": {
      "a3": 1,
      "a4": 2
    },
    "c": 3
  }
}

実行が成功しました。

ちゃんと期待通りのOutputを得ることができました。

Output

{
  "StatesStringSplit": [
    "class",
    "thod"
  ],
  "StatesArrayPartition": [
    [
      "a",
      "a"
    ],
    [
      "b",
      "c"
    ],
    [
      "c",
      "c"
    ],
    [
      "d",
      "e"
    ],
    [
      "e",
      "f"
    ],
    [
      "g"
    ]
  ],
  "StatesBase64Encode": "Y2xhc3NtZXRob2Q=",
  "StatesArray": [
    {
      "a": {
        "a1": 1,
        "a2": 2
      },
      "b": 2
    },
    {
      "a": {
        "a3": 1,
        "a4": 2
      },
      "c": 3
    }
  ],
  "StatesJsonMerge": {
    "a": {
      "a3": 1,
      "a4": 2
    },
    "b": 2,
    "c": 3
  },
  "StatesFormat": "Hello, this is classmethod.",
  "StatesJsonToString": "{\"a\":{\"a1\":1,\"a2\":2},\"b\":2}",
  "StatesBase64Decode": "クラスメソッド",
  "StatesUUID": "2eb7bd4d-0009-4c0e-89cc-6833f218d78e",
  "StatesMathRandom": 7801,
  "StatesArrayUnique": [
    "a",
    "b",
    "c",
    "d",
    "e",
    "f",
    "g"
  ],
  "StatesHash": "79943e30c9f79c2051dfb2716135498646546321cfd8b77ef683db35b787a8a604db11dc722c7d3100fcb9b08c42cfa5ba7167db2ccf18d36725d1ba6c6fc757",
  "StatesArrayRange": [
    0,
    333,
    666,
    999
  ],
  "StatesArrayContains": true,
  "StatesArrayGetItem": "c",
  "StatesStringToJson": {
    "foo": "bar"
  },
  "StatesArrayLength": 11,
  "StatesMathAdd": 212
}

注意点

ドキュメントによると、States.MathRandomでは3つ目の引数でシード値をオプションで取ります。

引数1(開始番号)、引数2(終了番号)および引数3(シード値)で同じ値が指定される限り、States.MathRandomは同じ値を返すようになります。

しかしCDKのクラス実装では3つ目の引数であるシード値の指定はサポートされていないようです。

もしCDKでシード値を指定したい場合はクラスを使わずに"States.MathRandom(0, 10000, 3)"のように関数をベタ書きするようにしましょう。

おわりに

AWS Step Functionsの新しい組み込み関数がAWS CDKの専用クラスで実装できるようになっていたので試してみました。

今までも関数ベタ書きにより使えなくは無かったのですが、クラスを使うことができればTypeScriptの型補完を効かせつつ実装できるので開発が便利になります。嬉しいですね。

以上