AWS Step FunctionsでStateMachineを書いてみる

俺メモ
こんな記事を見るより[こっち](http://qiita.com/ketancho/items/147a141c9f8a6de86c97)を読んだ方が100倍わかりやすいです。

# StepFunctions
AWS re:Invent2016 にてStepFunctionsがリリースされました。
状態マシンを書いてLambdaの処理をさせることができるサービスです。
とりあえず書きかたをメモっておきましょうか。

# シンプルなStepFunctions

``` js:JSON
{
  "Comment": "ここにコメントを入れる",
  "StartAt": "開始関数の名前をここに入れる",
  "States": { // 関数の定義一覧をここに書く
    "HelloWorld": { // 関数名
      "Type": "Task", // 関数のタイプ。TaskはLambdaの処理が入る
      "Resource": "LambdaのARNを入れる",
      // End: trueを指定すると、この処理が終わったら状態マシン終了。
      "End": true, // この処理が終わったら状態マシン終了。
      // Next: "hoge"を指定すると、この処理が終わったら、次にhogeを実行する。
      "Next": "hoge"
    }
  }
}
```

![2016-12-13_12h17_08.png](https://qiita-image-store.s3.amazonaws.com/0/71143/37936131-3f7b-4865-88d2-0092f70b2a9f.png)

# Type
## Task
LambdaのARNが指定できる。

``` js:JSON
"HelloWorld": {
  "Type": "Task",
  "Resource": "LambdaのARN",
  "End": true
}
```

![2016-12-13_12h17_27.png](https://qiita-image-store.s3.amazonaws.com/0/71143/279b3bd6-b2dd-a08a-a3eb-6b54dc1a10b5.png)

## Wait
指定した秒数とか(いろいろ指定できる)待たせることができる。

``` js:JSON
"WaitWorld": {
  "Type": "Wait",
  // 待つ秒数の指定には様々な方法がある。
  // 以下のいずれかを指定する。
  "Seconds": 10, // 10秒待つ。
  "Timestamp": "2015-09-04T01:59:00Z" // 指定したTimeStampまで待つ(そんなん使うんか？)
  "SecondsPath": "$.hogeTimestamp", // 変数が使える(後述)
  "TimestampPath": "$.hogeSeconds" // 変数が使える
}
```

## Choice
前段の処理結果によって後続の処理を分岐する

``` js:JSON
"ChoiceWorld": {
  "Type": "Choice",
  "Choices": [ {
    // 前段の処理で帰ってくるパラメータは $ に入っている。
    // $がオブジェクトとしたとき、$.response_cdの中の値をチェックする
    "Variable": "$.response_cd",
    // response_cdの値が"1"であるなら、NextWorldを実行する。
    "StringEquals": "1",
    "Next": "NextWorld1"
  }, {
    "Variable": "$.response_cd",
    "StringEquals": "2",
    "Next": "NextWorld2"
  } ],
  "Default": "NextWorld3"
}
```

``` javascript:前段処理のjavascript
exports.handler = function(event, context, callback) {
  // 
  callback( null, {
    "response_cd": "1"
  } );
};
```
![2016-12-13_12h40_13.png](https://qiita-image-store.s3.amazonaws.com/0/71143/471a36d8-c310-b709-c743-94155be9fc71.png)


## Parallel
並列処理を行います。
Branchesで配列を設定でき、Branchesの配列一つ一つには、さらに新規にStepFunctionsの処理を書くことができます。

``` js:JSON
"ParallelWorld": {
  "Type": "Parallel",
  "Next": "NextWorld",
  // Branchesの中身は、一般的なStep Functionsと同じ。(Step Functionsの中にStep Functionsを定義するようなイメージ)
  "Branches": [ {
    "StartAt": "World1",
    "States": {
      "World1": {
        "Type": "Task",
        "Resource": "ARN",
        "End": true
      }
    }
  }, {
    "StartAt": "World2-1",
    "States": {
      "World2-1": {
        "Type": "Task",
        "Resource": "ARN",
        "Next": "World2-2"
      },
      "World2-2": {
        "Type": "Task",
        "Resource": "ARN",
        "End": true
      }
    }
  } ]
}
```
![2016-12-13_12h29_56.png](https://qiita-image-store.s3.amazonaws.com/0/71143/27f4d399-d407-312d-099e-049db0851c0b.png)

# 変数について
前段から返却されたパラメータについては、$を使ってアクセスすることができます。
たとえば、以下のようなコードで値を返した場合、

``` js:前段のコード
exports.handler = function(event, context, callback) {
  callback( null, {
    "response": 20
  } );
};
```

変数として、`$`でアクセスすることができます。
例えばWaitのSecondPathの場合、

``` js:JSON
"WaitWorld": {
  "Type": "Wait",
  "SecondPath": "$.response"
  "Next": "NextWorld"
}
```

`$.response`で秒数を取得できます。
オブジェクトではなく、文字列などを返却した場合は$でその文字列にアクセスできます。



# その他
## Catch
Taskが何らかのエラーで失敗した場合に、Catchハンドラで捕まえてエラー時の処理を実行させることができます。

``` js:JSON
"CatchWorld": {
  "Type": "Task",
  "Resource": "ARN",
  "Catch": [ {
    "ErrorEquals": [ "HandledError" ],
    "Next": "NextWorld"
  } ],
  "End": true
},
"NextWorld": {
  // ...
}
```

## Retry
Catchとだいたい一緒です。
違いは、エラーになった場合に、再実行するところでしょうか。`BackoffRate`は何のことかわかりません。

``` js:JSON
"CatchWorld": {
  "Type": "Task",
  "Resource": "ARN",
  {
    "ErrorEquals": ["HandledError"],
    "IntervalSeconds": 1, // 再実行するときにインターバルをあける
    "MaxAttempts": 2, // 再実行回数
    "BackoffRate": 2.0
  },
  "End": true
}
```


# 例
試しに何か書いてみます。

``` js
{
  "Comment": "A Hello World example of the Amazon States Language using an AWS Lambda Function",
  "StartAt": "Func1",
  "States": {
    "Func1": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
      "Next": "Choice1"
    },
    "Choice1": {
      "Type": "Choice",
      "Choices": [ {
        "Variable": "$",
        "StringEquals": "Hello",
        "Next": "Func2-A"
      }, {
        "Variable": "$",
        "StringEquals": "Bye",
        "Next": "Func2-B"
      } ]
    },
    "Func2-A": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
      "Next": "Parallel1"
    },
    "Func2-B": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
      "Next": "Parallel1"
    },
    "Parallel1": {
      "Type": "Parallel",
      "Branches": [ {
        "StartAt": "World3-1-1",
        "States": {
          "World3-1-1": {
            "Type": "Task",
            "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
            "Next": "World3-1-2"
          },
          "World3-1-2": {
            "Type": "Task",
            "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
            "End": true
          }
        }
      }, {
        "StartAt": "World3-2",
        "States": {
          "World3-2": {
            "Type": "Task",
            "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
            "End": true
          }
        }
      } ],
      "Next": "World4"
    },
    "World4": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:ap-northeast-1:***:function:stepFunction1",
      "End": true
    }
  }
}
```
![2016-12-13_12h34_39.png](https://qiita-image-store.s3.amazonaws.com/0/71143/e5170746-fb4e-4815-e998-4070ca5a7b40.png)