JavaScriptで一貫したイベント処理（ページ遷移、処理実行）AWSを添えて

JavaScriptで一貫してイベントを処理するページを作成（既存のものに追加しただけですが…）したので、作成時わかりにくかったことをまとめました。
その際AWSを使用してサーバーレス開発もしてます。
**文面から分かる通り初心者なのですが**、少しでも同じところでつまずいた方の助けになれば幸いです。

#作った画面
![作った画面](https://qiita-image-store.s3.amazonaws.com/0/308128/385e4af8-3552-dcef-9a5e-ae18265eb0be.png)


そのまんま作った画面です。
変更、追加を行った際にイベントで処理してます。

##今回使用したAWSサービス
最もわかりにくかったのはAWS※1のサービスです。
始めて触れたということもありますが、どこで何をしているのかきちんと理解しないと、どこで設定していいのやら困ってしまいます。特にLambda※2。
getやpost、putなど、したい処理ごとにLambda(ロールや環境変数なども)を作成しなければならないのでちょっと面倒です。ですが、作ってしまえばそれぞれ独立して動くのでとても変更がしやすい…。

今回使用したAWSサービス関係性について軽く説明します。
S3では、フロント側のhtml,jsファイルの保持をしています。ページが開かれファイルが実行、リクエストがAPI Gatewayに送られることで、関連するLambdaが呼び出されます。
呼び出されたLambdaは実行内容に従って、Dynamo DBにデータを登録したり、変更、削除などを行ったりします。（アップロードされたファイルをS3に置きに行くなんてこともできるようです。）
※IAMやプールも使用していますが、今回は割愛します。

![Screenshot 2018-11-27 at 10.23.58.png](https://qiita-image-store.s3.amazonaws.com/0/308128/b31366a9-47e9-cdf0-cf3b-91b8ff25a41d.png)

###AWSとは(※1)
正式名称はAmazon Web Service
AWSは、クラウドサービスです。AWS では現在 100 以上のサービスを展開し、企業で必要な IT リソースを網羅しています。そのサービスは、コンピュート、ストレージ、ネットワーキング、データベースのみならず、データ分析（アナリティクス）、アプリケーション サービス、展開、管理、デベロッパー、モバイル、IoT、人工知能（AI）、セキュリティ、ハイブリッド、エンタープライス・アプリケーションなど、多岐にわたっています。
引用：[アマゾン ウェブ サービス | AWS](https://aws.amazon.com/jp/s/dm/landing-page/start-your-free-trial-d/?sc_channel=PS&sc_campaign=acquisition_JP&sc_publisher=google&sc_medium=cloud_computing_rlsa_b&sc_content=aws_e&sc_detail=aws%20%E3%81%A8%E3%81%AF&sc_category=cloud_computing_rlsa&sc_segment=311623858957&sc_matchtype=e&sc_country=JP&sc_brand=brand&ef_id=Cj0KCQiArenfBRCoARIsAFc1FqcgZAEKX8gGDYjp8SYoJ5H9-_TJjOxi2vzL8tbNqcF3W8ayAEv0WpAaAlydEALw_wcB:G:s)

※2　Lambdaは、コードさえアップロードすれば、高可用性を実現しながらコードを実行およびスケーリングするために必要なことを、すべて行なうサービス。コードは、他の AWS サービスから自動的にトリガーするよう設定することも、ウェブやモバイルアプリケーションから直接呼び出すよう設定することもできます。
引用:[AWS Lambda (サーバーレスでコードを実行・自動管理) | AWS](https://aws.amazon.com/jp/lambda/)
（Lambdaでググるとわかりやすい解説がたくさんあるので、ぜひググってみてください。）

#イベントでの処理
以下S3に置いてあるjsファイルのコードです。イベントの部分だけ抜粋。
※コード中に出てくるapps変数は、アプリ共通の名前空間として使用しています。ここにAWSの接続情報（エンドポイントやリージョン、バケット名など）を格納しています。


```javascript:eventSet.js
// 各種イベント
var events = [
   ['addTenant', 'click', apps.addTenant],
   ['modify', 'click', apps.modifyTenant],
];
```

```javascript:追加.js
apps.addTenant = ()=>{
    (async ()=>{
        // テナントを追加する
        var tenantParam = {'mail': mail, 'domainn': domainn, 'tenant': tenant};
        await apps.xhr('POST', 'tenant', tenantParam, null);
        
        //  テナント追加完了！
        location.reload();

    })().catch((err)=>{
        // 例外処理（reject）
        console.log(err);
        apps.alertAndHideLoader();
    });
}
```

```javascript:変更.js
apps.modifyTenant = ()=>{    
    (async ()=>{
        // (1) 変更するテナントのID取得
        var id = apps.e('modify').getAttribute('id');
        // 変更するテナントの全てのデータ取得
        var data = apps.e('mod-'+id).data
        
        // 新しいテナント名
        var name = event.target.getAttribute('name');
        //ドメインだけを抜き出す
        var index   = name.indexOf("@");
        var domain = name.slice(index);
        var mail = name;
        
        // (2) テナント情報を変更する
        var tenantParam = {
            'id': id,
            'mail': mail && mail.length > 0 ? mail : data.mail
        }
        await apps.xhr('PUT', 'tenant', tenantParam, null);
           
        // (3) テナント情報変更完了！
        $('#modifyModal').modal('hide');
        location.reload(true);

    })().catch((err)=>{
        // 例外処理（reject）
        console.log(err);
        apps.alertAndHideLoader();
    });
}
```

ここでセットしたデータがAPI Gatewayを通じて呼び出されたLambdaで処理されます。
以下Lambdaで動くjsファイル(post)のコードです。

```javascript:Lambda.js
module.exports.post = (event, context, callback) => {
    const item_data = !event.body ? null : JSON.parse(event.body);
    console.log(event.body);
    
    var queryError = function (param) {return createErrorResponse(400, 'Bad Request. query parameter['+param+'] is empty.')};
  
    // リクエストボディがない場合
    if (!item_data) { callback(null, queryError(' - ')); return; }
  
    // パラメータ取得
    // 管理者メールアドレス、ドメイン(必須)
    var mail = !item_data.mail ? null : item_data.mail;
    var name = !item_data.name ? null : item_data.name;
    var domainn = !item_data.domainn ? null : item_data.domainn;

    // エラー
    if(!mail) { callback(null, queryError('mail')); return; }
    if(!name) { callback(null, queryError('name')); return; }
    if(!domainn) { callback(null, queryError('domainn')); return; }


    const getParams = {
        TableName: tenantTable
    };

    docClient.scan(getParams, function(err, data) {
        if (err) {
          console.error('Unable to get item. Error JSON:', JSON.stringify(err, null, 2));
          // エラー処理する
          const response = createErrorResponse(500, 'ERROR Failed to fetch data from DYNAMODB.');
          callback(null, response);
          return;
        }
        
        // ID
        var id = uuid.v4().split('-').join('');
        
        // 日付
        var currentDate = getCurrentDate();

        // シーケンス番号
        let sequence = data.Count;

        // DB保存
        const params = {
            TableName: tenantTable,
            Item: {
                "domainn": domainn,
                "mail": mail,
                "id": id,
                "sequence": sequence,
                "name": name,
                "createdAt": currentDate,
                "updatedAt": currentDate
            }
        };

        docClient.put(params, function(err, data) {
            if (err) {
                console.log(JSON.stringify(err, null, 2));
                const res = createErrorResponse(500, 'DYNAMODB ERROR: Failed to insert app data');
                callback(null, res);
                return;
            }
            callback(null, createResponse(200, {"id": id,"sequence":sequence}));
        });  

    });
};
```
API Gatewayから呼び出されたLambda(post)が実行されることでデータがDynamo DBに登録され処理は完了です。
（Dynamo DBは、No SQLで動くのであとは良きに計らってくれます。便利）

##こぼれ話 
今回コードを書く際Cloud9※3を使用していました。
リアルタイムで同時編集ができるのは大変便利ですね。
あと、開発環境がブラウザで完結するのでChromeBookでも開発できます！（笑）
基本無料ですが、EC2を動かせばそちらで料金がかかるので要注意です。
※3　AWS Cloud9 は、コードを記述、実行、デバッグできるクラウドベースの統合開発環境 (IDE)です。詳しくは、[AWS Cloud9／公式 | クラウドベースの統合開発環境‎](https://aws.amazon.com/jp/cloud9/?sc_channel=PS&sc_campaign=acquisition_JP&sc_publisher=google&sc_medium=cloud9_rlsa_b&sc_content=cloud9_bmm&sc_detail=%2Bcloud9&sc_category=cloud9&sc_segment=269937092338&sc_matchtype=b&sc_country=JP&sc_brand=brand&ef_id=CjwKCAiA0O7fBRASEiwAYI9QAr-KR-eyZZ6K3V73rr1Tbborw5RDIiBBRd6yqUOm08uFKwCL9QLRbxoCPsQQAvD_BwE:G:s)

今回、先輩の知恵を多大にお借りしなんとか作り上げることができました。
今後も引き続き勉強していきます！！