1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS SDK(JavaScript)で再帰処理をいい感じで書く

Last updated at Posted at 2017-02-23

AWS SDKのリソースのリスト取得系のAPIでは、「ある程度の塊で結果を返すけど、全部返しきれない場合はtokenも返すので、次回はそのtokenを使って呼んでくれたら続きを返すよ」という物が多いです。

例) IAM#listRoles()

var params = {
  Marker: 'STRING_VALUE',
  MaxItems: 0,
  PathPrefix: 'STRING_VALUE'
};
iam.listRoles(params, function(err, data) {
  if (err) console.log(err, err.stack); // an error occurred
  else     console.log(data);           // successful response
});

callbackのパラメータ

  • IsTruncated — (Boolean)
  • A flag that indicates whether there are more items to return.

 〜略〜

  • Marker — (String)
  • When IsTruncated is true, this element is present and contains the value to use for the Marker parameter in a subsequent pagination request.

callbackのパラメータのIsTruncateがtrueだったら、全部を返していないので次回呼び出し時にMarkerをパラメータにセットしてlistRoles()を呼ぶ必要があります。

これをPromiseを使っていい感じに書いてみます。

しばり

  • バベらない
  • Lambdaのコンソールにそのまま書ける

実装

const listIAMRoles = () => new Promise( // 1
    (resolve, reject) => {
        const iam = new AWS.IAM();
        const result = [];
        const loop = (marker) => {
            iam.listRoles({
                Marker: marker // 3
            }).promise() // 4
            .then((data) => {
                data.Roles.map((role) => { // 6
                    result.push({
                        roleName: role.RoleName,
                        arn: role.Arn,
                        path: role.Path
                    });
                });
                if (data.IsTruncated) { // 7
                    loop(data.Marker);
                } else {
                    resolve(result); // 8
                }
            })
            .catch(reject); // 5
        };
        loop(); // 2
    }
);

実装の説明

  1. listIAMRoles()がPromiseを返すようにします。
  2. まず、markerを引数に取るloop()関数を定義し、初回分として引数無しでloop()を呼び出します。
  3. loop()の引数をMakerプロパティにセットしてIAM#listRoles()を呼び出します。
  • 初回は引数markerがundefinedなので、listRoles()はMarkerがセットされずに呼ばれます。
  1. IAM#listRoles()の戻り値のPromiseを取得します。
  2. IAM#listRoles()が失敗した場合、.catch()に処理が進むので、そのままreject()を呼び出します。
  • reject()の引数にはIAM#listRoles()でのエラーが渡されるので、このlistIAMRoles()の呼び出し元の.catch(fn)の引数に渡されます。
  1. .then(fn)の引数にIAM#listRoles()の結果が渡されるので、任意に処理します。
  2. data.IsTruncatedがtrueの場合、まだIAM#listRoles()で返されるべきデータが残っているということなので、data.Makerを引数にloop()を再帰呼び出しします。
  3. もう残りのデータがない場合は、resolve()を呼び出して、listIAMRoles()を終了します。

Lambda内でつかう

listIAMRoles()がPromiseを返すので、こちらも.then()/.catch()で後続処理します。

exports.handler = (event, context, callback) => {
    listIAMRoles()
        .then((roles) => {
            callback(null, roles);
        })
        .catch((e) => {
            callback(e); // LambdaFunctionをエラーで終了する
        });
};

まとめ

再帰処理をいい感じで実装できました。IAM#listRoles()以外にもこのような
出し惜しみ系APIはたくさんあるので、同じように実装できると思います。

では!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?