AWS Step Functionsは各種ステートと入出力をうまくつかうことで、簡単なデータ変換であればLambdaを使わずに実装できます。2024/11/22にリリースされた変数機能とJSONata形式のサポートにより、その自由度の幅はさらに広がり、また実装がしやすくなりました。
従来ResultPathなどを使うことで、ステートの入力を保ったまま出力を追加することができました。JSONata形式を利用する場合、どのような手段があるか考えてみます。
以降の検証では、共通で以下のJSONを入力として使用します。
{
"input1": "value1",
"input2": "value2"
}
案1: 変数を利用する
そもそもステートの入出力にこだわる必要がない場合、変数を使う方法が一番簡潔です。変数に出力することで実装できないか検討しましょう。
{
"Type": "Pass",
"End": true,
"Assign": {
"variable3": "{% $uppercase($states.input.input1) %}"
}
}
実行した様子は以下のとおりです。
後続のステートでは $variable3
と記述することで変数の内容を参照できます。
案2: 持ち越したい入力をすべて出力に列挙する
Outputに必要な入力をすべて書くことで、Inputの内容をOutputに引き継ぐことができます。
{
"Type": "Pass",
"End": true,
"Output": {
"input1": "{% $states.input.input1 %}",
"input2": "{% $states.input.input2 %}",
"custom3": "{% $uppercase($states.input.input1) %}"
}
}
直感的ではありますが、入力される内容が変わるたびにPassステートの定義も書き換えなければいけません。なんとかならないでしょうか。
案3: $merge関数を使う
$merge
関数を使うことで、複数のオブジェクトをマージすることができます。
https://docs.jsonata.org/object-functions#merge
ステートのOutputには、これまでの例のようにKey-Valueを指定する方法のほか、JSONata形式の文字列を直接指定することもできます。そのため、以下のように記述することで $states.input
に新たなプロパティを追加した状態で出力できます。
{
"Type": "Pass",
"End": true,
"Output": "{% $merge([$states.input, { 'custom3': $uppercase($states.input.input1) }]) %}"
}
実行結果は以下の通りです。入力が正しく引き継がれているのがわかります。
同じキーのプロパティが存在した場合、配列の最後のプロパティが採用されます。
$merge() 関数の引数はオブジェクトの配列です。私は配列にするのを忘れて引数を複数指定するミスをよくしてしまうのでお気をつけください(「Argument 2 of function “merge” does not match function signature」というエラーが出ます)。
案4: $siftを利用して入力から特定のプロパティを削除する
$sift
は、特定の条件を満たすKey-Valueのみを返す関数です。
https://docs.jsonata.org/higher-order-functions#sift
以下の記述で、Keyが特定の条件を満たすプロパティのみを出力できます。
{
"Type": "Pass",
"End": true,
"Output": "{% $states.input.$sift(function($v, $k) {$k != 'input1'}) %}"
}
出力から input1
というプロパティが削除されていることがわかります。ここでは !=
を使っていますが、 $k in ['input1', 'input2']
のように許可リスト形式にしたり、$k ~> /^input/
のように正規表現を使うことも可能です。
あまり使う機会はないかもしれませんが、手段として覚えておくと何かの役に立つかもしれません。
まとめ
ステートの入力を保ったまま出力を追加する方法について検証しました。
変数を使うのが正直一番わかりやすいと思うのですが、JSONPathで書かれた既存のステートマシンを、極力処理を変えずにJSONataに置き換えたい場合などに利用できるかもしれません。