[AWS Step Functions / AWS CDK] 任意のJavaScript表現を実行できるEvaluateExpressionタスクを使って文字列置換をしてみた

2022.06.20

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

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

今日もいつものようにAWS Step Fucntionsのドキュメントやソースコードを見ていたところ、EvaluateExpressionなるものを見つけました。

内容を見てみると、なんと任意のJavaScript表現を実行できるタスクのようです...!!!

そこで今回は、EvaluateExpressionタスクを使って文字列置換を行うステートマシンを実装してみました。

やってみた

実装

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

lib/process-stack.ts

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

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

    //JSONPathを使わない場合
    const evaluateTask1 = new aws_stepfunctions_tasks.EvaluateExpression(
      this,
      'evaluateTask1',
      {
        expression: '"インスタンスメソッド".replace("インスタンス", "クラス")',
        resultPath: '$.evaluateTask1',
      },
    );

    //JSONPathを使う場合
    const evaluateTask2 = new aws_stepfunctions_tasks.EvaluateExpression(
      this,
      'evaluateTask2',
      {
        expression: aws_stepfunctions.JsonPath.format(
          '"{}".replace("{}", "{}")',
          aws_stepfunctions.JsonPath.stringAt('$.original'),
          aws_stepfunctions.JsonPath.stringAt('$.substr'),
          aws_stepfunctions.JsonPath.stringAt('$.newSubstr'),
        ),
        resultPath: '$.evaluateTask2',
      },
    );

    // ステートマシン
    new aws_stepfunctions.StateMachine(this, 'stateMachine', {
      stateMachineName: 'stateMachine',
      definition: evaluateTask1.next(evaluateTask2),
    });
  }
}
  • EvaluateExpression()expressionで、実行したいJavaScript表現を指定します。
    • States.Formatを使用してInputの値を指定すること可能です。

上記をCDK Deployしてスタックをデプロイします。これにより次のDefinitionのステートマシンが作成されます。種明かしなのですがEvaluateExpression()はLambda関数リソース(Node.jsランタイム)が隠蔽されたHigh Level Constructです。

Definition

{
  "StartAt": "evaluateTask1",
  "States": {
    "evaluateTask1": {
      "Next": "evaluateTask2",
      "Type": "Task",
      "ResultPath": "$.evaluateTask1",
      "Resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:ProcessStack-Evalda2d1181604e4a4586941a6abd7fe42dF-oEnIyas250LB",
      "Parameters": {
        "expression": "\"インスタンスメソッド\".replace(\"インスタンス\", \"クラス\")",
        "expressionAttributeValues": {}
      }
    },
    "evaluateTask2": {
      "End": true,
      "Type": "Task",
      "ResultPath": "$.evaluateTask2",
      "Resource": "arn:aws:lambda:ap-northeast-1:XXXXXXXXXXXX:function:ProcessStack-Evalda2d1181604e4a4586941a6abd7fe42dF-oEnIyas250LB",
      "Parameters": {
        "expression.$": "States.Format('\"{}\".replace(\"{}\", \"{}\")', $.original, $.substr, $.newSubstr)",
        "expressionAttributeValues": {}
      }
    }
  }
}

作成されたLambda関数のコードを見ると、引数のexpressioneval()してReturnしていることが分かります。

Lambda Function

'use strict';
Object.defineProperty(exports, '__esModule', { value: !0 }),
  (exports.handler = void 0);
function escapeRegex(x) {
  return x.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
async function handler(event) {
  console.log('Event: %j', event);
  const expression = Object.entries(event.expressionAttributeValues).reduce(
    (exp, [k, v]) =>
      exp.replace(new RegExp(escapeRegex(k), 'g'), JSON.stringify(v)),
    event.expression
  );
  return console.log(`Expression: ${expression}`), eval(expression);
}
exports.handler = handler;
//# sourceMappingURL=index.js.map

動作確認

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

{
    "original": "オールドモード",
  	"substr": "オールド",
  	"newSubstr": "ネクスト"
}

実行が成功しました。

evaluateTask1では、文字列置換によりクラスメソッドが取得できています!

evaluateTask1では、JSONPathの入力を元にした文字列置換によりネクストモードが取得できています!

おわりに

任意のJavaScript表現を実行できるEvaluateExpressionタスクを使って文字列置換をしてみました。

ある程度の処理のタスクであれば最小限のコードの記述だけで作成できてしまうなんて凄すぎますね。今回やった文字列置換の他にも四則演算や日付処理などeval()に渡せるものなら何でも出来てしまいます。

ローコードツールとしてのAWS Step Functionsの可能性を大幅に拡張する機能だと思うので、今後はどんどん利用し、またどんどん周知していきたいと思います。

参考

以上