[AWS CDK] aws_stepfunctions.JsonPath ClassのMethod(stringAt、numberAtなど)の使いどころを確認してみた

2022.07.12

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

AWS Step FunctionsをAWS CDKで実装する際に、aws_stepfunctions.JsonPath ClassのMethodを使うことによりJsonPathへのアクセスを定義することができます。

JsonPathへのアクセスを定義するMethodには次の4つがあるのですが、どれを使えばよいのか時々使い分けに困ることがあります。(特にstringAtとnumberAt)

  • stringAt
  • numberAt
  • listAt
  • objectAt

今回は、これらのMethodの使いどころについて確認してみました。

先に結論

stringAt、numberAt、listAt、objectAt、これらの使い分けはAWS CDKの実装における型ガードのためのものであり、BuildされたState Machine Definitionにおいてはすべて同じ定義となる。

public static stringAt(path: string): string

public static numberAt(path: string): number

public static listAt(path: string): string[]

public static objectAt(path: string): IResolvable

確認してみた

確認した環境は次のようになります。

$ npm ls typescript aws-cdk --depth=0
project@0.1.0 /Users/path/to/project
├── aws-cdk@2.29.1
└── typescript@3.9.10

Build後のDefinitionはどうなるか

まずstringAtnumberAtを使用した場合に、Build後のDefinitionではどのようになるかを確認してみます。

AWS CDK v2(TypeScript)で次のようなCDKスタックを作成します。

lib/aws-app-stack.ts

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

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

    const pass1 = new aws_stepfunctions.Pass(this, 'pass1', {
      parameters: {
        string_from_string: aws_stepfunctions.JsonPath.stringAt('$.string'),
        string_from_number: aws_stepfunctions.JsonPath.stringAt('$.number'),
        number_from_string: aws_stepfunctions.JsonPath.numberAt('$.string'),
        number_from_number: aws_stepfunctions.JsonPath.numberAt('$.number'),
      },
    });

    new aws_stepfunctions.StateMachine(this, 'stateMachine', {
      stateMachineName: 'stateMachine',
      definition: pass1,
    });
  }
}
  • JsonPathから文字列型と数値型の値をstringAtnumberAtの両方で取得するようにし、違いを見てみます。

上記をCDK Deployしてスタックをデプロイします。すると次のようなDefinitionのState Machineが作成されます。アクセス先のパスは違えど、4つすべて同じ定義となっていますね。

Definition

{
  "StartAt": "pass",
  "States": {
    "pass": {
      "Type": "Pass",
      "Parameters": {
        "string_from_string.$": "$.string",
        "string_from_number.$": "$.number",
        "number_from_string.$": "$.string",
        "number_from_number.$": "$.number"
      },
      "End": true
    }
  }
}

文字列型の値と数値型の値を持つ次の入力を指定してステートマシンを実行します。

Input

{
  "string": "12345",
  "number": 67890
}

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

TaskのOutputは次の通りです。JsonPathによるアクセス先の値がそのまま取得されているだけです。当然ながら、Methodの違いによってアクセスがエラーとなったり、型が変換されたりはしていません。

Output

{
  "string_from_string": "12345",
  "number_from_number": 67890,
  "number_from_string": "12345",
  "string_from_number": 67890
}

stringAtnumberAtのいずれを使用するかによって、Build後のDefinitionにおける差異は無いという結果となりました。

CDK実装におけるMethodの使い分け

Build後のDefinitionにおける差異は無い場合、CDK実装においてMethodを使い分ける意味は何でしょうか。

まずformatMethodを使った実装の例を考えてみます。

(method) JsonPath.format(formatString: string, ...values: string[]): string

formatの第2以降の引数の型はstringです。それに対してstringAtおよびnumberAtを指定した場合。

    new aws_stepfunctions.Pass(this, 'pass2', {
      parameters: {
        formatted: aws_stepfunctions.JsonPath.format(
          '{}_{}',
          aws_stepfunctions.JsonPath.stringAt('$.string'),
          aws_stepfunctions.JsonPath.numberAt('$.string'),
        ),
      },
    });

numberAtの方のみ型ガードでエラーとなりました。

もう一例見てみます。

Waitの引数として指定するDurationは数値型で指定する必要があります。それに対してstringAtおよびnumberAtを指定した場合。

    new aws_stepfunctions.Wait(this, 'wait', {
      time: aws_stepfunctions.WaitTime.duration(
        Duration.minutes(aws_stepfunctions.JsonPath.numberAt('$.string')),
      ),
    });

    new aws_stepfunctions.Wait(this, 'wait2', {
      time: aws_stepfunctions.WaitTime.duration(
        Duration.minutes(aws_stepfunctions.JsonPath.stringAt('$.string')),
      ),
    });

stringAtの方のみ型ガードでエラーとなりました。

以上のようにstringAtnumberAtを使用するMethodの型仕様によって使い分けることにより、JsonPathのアクセスによる取得結果の型を明確にしたコーディングを要請されます。

おわりに

AWS CDKのaws_stepfunctions.JsonPath ClassのMathod(stringAt、numberAtなど)の使いどころを確認してみました。

stringAtnumberAtの使い分けを意識する必要の無い実装(Parameter引数など)と、意識する必要のある実装(formatDurationなど)があり初めは混乱しましたが、分かってしまえば簡単ですね。

以上