AWS Step Functionsで組み込み関数だけで配列の作成を行う方法2通り(AWS CDK v2)

2022.06.19

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

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

今回は、AWS Step Functionsで組み込み関数だけで配列の作成を行うステートマシンをAWS CDK v2(TypeScript)で実装してみました。

方法

以下の2通りの方法が確認できたのでそれぞれ紹介します。

  • aws_stepfunctions.JsonPath.array
  • aws_stepfunctions_tasks.DynamoAttributeValue.fromList

方法1:aws_stepfunctions.JsonPath.array

次のようなCDKスタックを作成します。

lib/process-stack.ts

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

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

    // 配列の作成
    const createArrayTask = new aws_stepfunctions.Pass(
      this,
      'createArrayTask',
      {
        parameters: {
          array: aws_stepfunctions.JsonPath.array(
            'あああ',
            aws_stepfunctions.JsonPath.stringAt('$.val1'),
            aws_stepfunctions.JsonPath.stringAt('$.val2'),
          ),
        },
        resultPath: '$.createArrayTaskOutPut',
      },
    );

    // ステートマシン
    new aws_stepfunctions.StateMachine(this, 'stateMachine', {
      stateMachineName: 'stateMachine',
      definition: createArrayTask,
    });
  }
}

上記をCDK Deployしてスタックをデプロイします。これにより次のDefinitionのステートマシンが作成されます。

definition

{
  "StartAt": "createArrayTask",
  "States": {
    "createArrayTask": {
      "Type": "Pass",
      "ResultPath": "$.createArrayTaskOutPut",
      "Parameters": {
        "array.$": "States.Array('あああ', $.val1, $.val2)"
      },
      "End": true
    }
  }
}

次の入力を指定してステートマシンを実行します。

input

{
  "val1": "candy",
  "val2": "choco"
}

すると実行が成功しました。

createArrayTaskで、JSONPathから取得した値を使用して配列が作成できています。

方法2:aws_stepfunctions_tasks.DynamoAttributeValue.fromList

次のようなCDKスタックを作成します。

lib/process-stack.ts

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

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

    // 適当なDynamoDBテーブル
    const someTable = new aws_dynamodb.Table(this, 'someTable', {
      tableName: 'someTable',
      partitionKey: {
        name: 'id',
        type: aws_dynamodb.AttributeType.STRING,
      },
      billingMode: aws_dynamodb.BillingMode.PAY_PER_REQUEST,
      removalPolicy: RemovalPolicy.DESTROY,
    });

    // 配列の作成
    const createArrayTask = new aws_stepfunctions.Pass(
      this,
      'createArrayTask',
      {
        parameters: {
          array: aws_stepfunctions_tasks.DynamoAttributeValue.fromList([
            aws_stepfunctions_tasks.DynamoAttributeValue.fromString(
              aws_stepfunctions.JsonPath.stringAt('$.val1'),
            ),
            aws_stepfunctions_tasks.DynamoAttributeValue.fromString(
              aws_stepfunctions.JsonPath.stringAt('$.val2'),
            ),
          ]),
        },
        resultPath: '$.createArrayTaskOutPut',
      },
    );

    // 配列への要素追加
    const appendToArrayTask = new aws_stepfunctions_tasks.DynamoUpdateItem(
      this,
      'appendToArrayTask',
      {
        table: someTable,
        key: {
          id: aws_stepfunctions_tasks.DynamoAttributeValue.fromString('aaaaaa'), //適当なキー値
        },
        expressionAttributeNames: {
          '#array': 'array',
        },
        expressionAttributeValues: {
          //要素追加先の配列として利用
          ':target':
            aws_stepfunctions_tasks.DynamoAttributeValue.listFromJsonPath(
              aws_stepfunctions.JsonPath.stringAt(
                '$.createArrayTaskOutPut.array.attributeValue.L',
              ),
            ),
          ':appends': aws_stepfunctions_tasks.DynamoAttributeValue.fromList([
            aws_stepfunctions_tasks.DynamoAttributeValue.fromString(
              aws_stepfunctions.JsonPath.stringAt('$.val3'),
            ),
          ]),
        },
        updateExpression: 'SET #array = list_append(:target, :appends)',
        returnValues: aws_stepfunctions_tasks.DynamoReturnValues.UPDATED_NEW,
        resultSelector: {
          items: aws_stepfunctions.JsonPath.stringAt(
            '$.Attributes.array.L[*].S',
          ),
        },
        resultPath: '$.appendToArrayTask.OutPut',
      },
    );

    // 通常の配列に変換する場合
    const convertToUsualArray = new aws_stepfunctions.Pass(
      this,
      'convertToUsualArray',
      {
        parameters: {
          usualArray: aws_stepfunctions.JsonPath.stringAt(
            '$.createArrayTaskOutPut.array.attributeValue.L[*].S',
          ),
        },
      },
    );

    // ステートマシン
    new aws_stepfunctions.StateMachine(this, 'stateMachine', {
      stateMachineName: 'stateMachine',
      definition: createArrayTask
        .next(appendToArrayTask)
        .next(convertToUsualArray),
    });
  }
}
  • 使用しているのはDynamoAttributeValue ClassのfromList Methodです。
    • 正確に言うとStep Functionsの組み込み関数ではありませんね。
  • これによりDynamoAttributeValue型の配列を作成します。
    • DynamoAttributeValue型なのでステートマシン内の後続処理でDynamoDBへの処理に利用する際に便利です。
    • またJsonPathを上手く使用すれば通常の配列に簡単に変換できます。

上記をCDK Deployしてスタックをデプロイします。これにより次のDefinitionのステートマシンが作成されます。

definition

{
  "StartAt": "createArrayTask",
  "States": {
    "createArrayTask": {
      "Type": "Pass",
      "ResultPath": "$.createArrayTaskOutPut",
      "Parameters": {
        "array": {
          "attributeValue": {
            "L": [
              {
                "S.$": "$.val1"
              },
              {
                "S.$": "$.val2"
              }
            ]
          }
        }
      },
      "Next": "appendToArrayTask"
    },
    "appendToArrayTask": {
      "Next": "convertToUsualArray",
      "Type": "Task",
      "ResultPath": "$.appendToArrayTask.OutPut",
      "ResultSelector": {
        "items.$": "$.Attributes.array.L"
      },
      "Resource": "arn:aws:states:::dynamodb:updateItem",
      "Parameters": {
        "Key": {
          "id": {
            "S": "aaaaaa"
          }
        },
        "TableName": "someTable",
        "ExpressionAttributeNames": {
          "#array": "array"
        },
        "ExpressionAttributeValues": {
          ":target": {
            "L.$": "$.createArrayTaskOutPut.array.attributeValue.L"
          },
          ":appends": {
            "L": [
              {
                "S.$": "$.val3"
              }
            ]
          }
        },
        "ReturnValues": "UPDATED_NEW",
        "UpdateExpression": "SET #array = list_append(:target, :appends)"
      }
    },
    "convertToUsualArray": {
      "Type": "Pass",
      "Parameters": {
        "usualArray.$": "$.createArrayTaskOutPut.array.attributeValue.L[*].S"
      },
      "End": true
    }
  }
}

次の入力を指定してステートマシンを実行します。

input

{
  "val1": "candy",
  "val2": "choco",
  "val3": "cacao"
}

すると実行が成功しました。

createArrayTaskでは、JSONPathから取得した値を使用してAttributeValue形式の配列が取得できています。

    "appendToArrayTask": {
      "OutPut": {
        "items": [
          {
            "S": "candy"
          },
          {
            "S": "choco"
          },
          {
            "S": "cacao"
          }
        ]
      }
    }

appendToArrayTaskでは、createArrayTaskで作成した配列に要素を追加できています。

    "appendToArrayTask": {
      "OutPut": {
        "items": [
          {
            "S": "candy"
          },
          {
            "S": "choco"
          },
          {
            "S": "cacao"
          }
        ]
      }
    }

convertToUsualArrayでは、createArrayTaskで作成したAttributeValue形式の配列を通常の形式に変換できています。

    "usualArray": [
      "candy",
      "choco"
    ]

おわりに

AWS Step Functionsで組み込み関数だけで配列の作成を行うステートマシンをAWS CDK v2(TypeScript)で実装してみました。

Itrinsic functionsのCDKの組み込みのClassが今までもあるだろうなと思いつつ探しきれていなかったので、今回方法が確認できて良かったです。

参考

以上