ローカル環境でも快適に LIFF の開発ができる liff-sdk-local を作りました

なぜ作ったのか

LIFF(LINE Front-end Framework) は SSL 対応した URL を元に LIFF URL(line://app/xxx)を生成することで使えるようになります。
その仕様で動作確認のしずらさを感じることがよくあり、どうしたらもっと開発効率があがるかなと考えていました。

解決策その1として liff-cli を去年作り、1年で約5,600回!ダウンロード(npm install liff)されています。

github.com

LIFF のリリース当初(2018/6) は LIFF URL の生成が LINE Messaging API を直接実行するしかなかったため、毎回ドキュメントの curl サンプルをコピペして実行していました。
今では LINE Developers Console にて LIFF を管理できる機能が追加されてたため、LIFF URL の生成は楽になりました。

LIFF URL 生成は楽になったものの
そもそも LIFF URL を生成せずに LIFF の開発できないんだっけ?
と考えて今回の liff-sdk-local を作りました。

github.com

なにが変わるのか

liff-sdk-local を使うことで次のように LIFF 開発が変わります!

今までの LIFF 開発
  1. LIFF SDK を導入、実装
  2. SSL 対応のサーバーにファイルをアップロード or ngrok などを使ってローカル環境を外部公開
  3. 2 で生成された URL を LINE Developers Console or LINE Messaging API を使い LIFF URL を生成
  4. 3 を LINE に送信して動作確認
これからの LIFF 開発
  1. LIFF SDK と liff-local-sdk を導入、実装
  2. ローカル環境で動作確認

liff-sdk-local 対応 API

以下の LIFF API に対応しています。

liff-sdk-local 使い方

  1. GitHub リポジトリより最新版をダウンロードしてください。
  2. dist/liff-sdk-local.min.js をプロジェクトに追加し、本家の LIFF SDK をコメントアウトしてください。
<!-- <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script> -->
<script src="path/to/liff-sdk-local.min.js"></script>
<script>
   liff.init(function(data) {
       // 処理...
   });
</script>

これで LIFF URL を生成せずにローカル環境で LIFF の動作確認ができます。

戻り値をカスタマイズする

各 API の戻り値はデフォルトでは LIFF API のドキュメントのサンプルと同じ値が返ります。

戻り値を変更するには window.liffSettings に設定してください。

<script>
   window.liffSettings = {
       userId: 'U12345',
   };
</script>
<!-- <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script> -->
<script src="path/to/liff-sdk-local.min.js"></script>
<script>
   liff.init(function(data) {
       console.log(data.context.userId); // 出力: U12345
   });
</script>

設定可能な戻り値は以下です。

  • userId
  • displayName
  • pictureUrl
  • statusMessage
  • accessToken
  • language
  • type
  • utouId
  • groupId
  • roomId
  • viewType

おまけ

9/22 に開催された技術書典 7 にて販売された LINE API HANDBOOK で第 4 章「LIFF 活用事例と開発 Tips の紹介」を書かきました!

booth.pm

Sequelize PostgreSQL での SSL 接続設定(と主要オプションの紹介)

Heroku でホスティングしている PostgreSQL に
Sequelize から接続しようとしたときに SSL 必須のエラーが出てた.

エラーは解決はしたもののドキュメントに詳しい説明がなく
調査したときに関連する Sequelize のコードを読んだでわかったことをまとめた.

まずはエラー対策の結論

環境.dialect.dialectOptions.ssl: true にすると
SSLでの接続が可能になる.

{
    "production": {
        "dialect": "postgres",
        "dialectOptions": {
            "ssl": true
        }
    }
}

dialectOptions とは?

Sequelize のドキュメントには dialectOptions にどのようなパラメータが設定できるか
といった記述はない.

Sequelize は MySQL は mysql2, PostgreSQL は pg といったように
内部的には DB への接続部分に各 Node.js 実装のライブラリを使用している.

dialectOptions についてドキュメントに記述がないのは
dialectOptions は各ライブラリへそのまま設定値を受け渡すのが役割なためである.

主要な dialectOptions の設定項目

とはいえ今回の ssl のように
主要なパラメータのドキュメントはあった方が親切だと感じたので 調査していく過程で見つけたものをまとめておく.

PostgreSQL

application_name

pg_stat_activity で表示されるログに表示される名前を設定する.
これを使うことで DB 側で接続元ごとの状況を確認することが可能になる.

PostgreSQL: Documentation: 9.3: Error Reporting and Logging

ssl

true で DB に SSL 接続を行う.

client_encoding

接続する際の文字エンコーディングを指定する.
auto を指定すると接続元の環境をそのまま利用する.
Unix システムであれば LC_CTYPE の値を利用.

keepAlive

true でkeepAlive を有効にする.

statement_timeout

クエリをタイムアウトさせる時間(ミリ秒)

ドキュメントにミスみつけたので PR 出した

マージされた🎉

github.com

まとめ

  • SSL 必須のエラーを解決した.
  • Sequelize のコードを読んで主要な dialectOptions をまとめた.
  • ↑を追記する PR もまとめて出そう.

Amazon ECS で Mautic を動かす

OSS の MAツールである Mutic を動かすことがあり
公式が用意している Docker イメージを使ってローカルマシンでは簡単にセットアップできた.

その後サーバーに載せる際に Amazon ECS を使ってセットアップした後
Mautic コンテナに接続するところまでの手順をまとめておく.

前提

以下の設定は済んでいること前提に Mautic を動作させることにフォーカスする.

  • AWS の設定(認証ファイル, EC2 キーペア作成など)
  • ecs-cli のインストール
  • docker-compose 実行環境

Mautic をローカルマシンで起動

github.com

上記のを元に 80 ポートで立ち上がるように修正したのが以下のgist.

gist.github.com

docker-compose.yml として保存して docker-compose up で起動.

http://localhost/ で Mautic が表示される.

続いて Amazon ECS で動かす設定へ.

Amazon ECS で動かす

初期設定

$ ecs-cli configure --region ap-northeast-1 --cluster mautic

--region: ECS を設定するリージョン
--cluster: クラスタ名

今回はクラスタ名を mautic とする.

クラスタ作成

$ ecs-cli up --keypair KEY_NAME --capability-iam --size 1 --instance-type t3.medium

--keypair: 使用する EC2 キーペア
--size: クラスタに登録するインスタンス数
--instance-type: EC2 インスタンスタイプ

t2.micro で試したところメモリ不足のエラーがでたので t3.medium で作成.

INFO[0015] (service mautic) was unable to place a task because no container instance met all of its requirements. 
The closest matching (container-instance xxxxx) has insufficient memory available. For more information, see the Troubleshooting section of the Amazon ECS Developer Guide.

コンテナ起動

$ ecs-cli compose -f docker-compose.yml up

コンテナに接続

Mautic の言語設定を日本語にしたものの, キャッシュが原因で変更されなかったので
コンテナに接続して対応していく.

EC2 の DNS を特定して ssh 接続

  1. AWS コンソールで Amazon ECS を開く
  2. クラスターを選択
  3. 対象のクラスター名を選択(今回は mautic)
  4. ECS インスタンスのタブを選択
  5. EC2 インスタンスのインスタンスID(i-から始まる文字列)を選択
  6. おなじみの EC2 の画面でパブリック DNS が特定できる
  7. ssh接続
$ ssh ec2-user@ec2-xxx.amazonaws.com -i KEY_NAME

Docker コンテナに接続

EC2 の中で Docker コンテナが起動しているので特定して接続する.

$ docker ps
CONTAINER ID        IMAGE                            COMMAND                  CREATED             STATUS              PORTS                NAMES
xxx        mautic/mautic                    "/entrypoint.sh apac…"   1 days ago         Up 1 days          0.0.0.0:80->80/tcp   ecs-mautic-1-mautic-xxx
xxx        mysql:5.6                        "docker-entrypoint.s…"   1 days ago         Up 1 days          3306/tcp             ecs-mautic-1-mauticdb-xxx
xxx        amazon/amazon-ecs-agent:latest   "/agent"                 1 days ago         Up 1 days                               ecs-agent

mautic, mysql, amazon-ecs-agent の 3つのコンテンが立ち上がっている.

mautic の NAMES に対して以下のコマンドを実行すると
mautic が動いているコンテナに接続できる.

$ docker exec -it ecs-mautic-1-mautic-xxx bash

/var/www/html/app/cache/prod 配下のキャッシュを削除して
設定が反映されていることが確認できた.

まとめ

  • Mautic を Amazon ECS を使ってサーバに載せた.
  • Mautic のメール設定は Amazon SES を使えるためその後の連携が楽にできる.

LINE Messaging API の replyToken は何秒で無効になるのか検証

モチベーション

LINE Messaging API の公式ドキュメントでは
replyTokenの失効期限は一定期間と記載されており明確な数値がなく
正確に検証したいと思ったため.

応答できるイベントには応答トークンが発行されます。 応答トークンは一定の期間が経過すると無効になるため、メッセージを受信したらすぐに応答を返す必要があります。

developers.line.biz

結論

30秒

検証方法

検証用のシンプルな LINE ボットを開発.
express + line-bot-sdk-nodejs を使って heroku にデプロイ.
ソースコードは以下リポジトリ.

github.com

ボットの仕様

  • 数字(n)を送信数すると n 秒後に replyMessage を送信
  • replyMessage が送信できなかった場合は pushMessage でエラー内容を送信
  • 数字以外は数字を送信するように促す

検証内容

f:id:morugu:20190714152702p:plain
検証内容

まとめ

  • Messaging API の仕様を検証するためのボットを作った!
  • 30秒以下であれば replyMessage で返信が可能
  • 思っていたより期限が長かった!

Lambda から最新の AWS SDK を使って Aurora のデータベースクラスタの停止、開始を実行できるようにする

Lambda から Aurora のデータベースクラスタを止めるためにRDS.stopDBCluster を呼んだのですが stopDBCluster is not defined になったので Lambda Layers を使って解決してみました。

Amazon Aurora がデータベースクラスタの停止、開始へのサポートを開始

Lambda の実行環境調査

Lambda の実行環境(ランタイム: Node.js 8.10)では AWS SDK for JavaScript – 2.290.0 がデフォルトで含まれており
2.290.0までに対応している API であれば何も考えずに const AWS = require('aws-sdk'); で使うことができます。

docs.aws.amazon.com

2.290.0 は 2018/08/10 にリリースされたバージョンなので、それ以降にリリースされた API に関しては何かしら対策が必要となります。

Release Release v2.290.0 · aws/aws-sdk-js · GitHub

今回欲しいのは、RDS の start/stop DBCluster が入っているバージョンで
CHANGELOG から探してみると2.319.0で入っており、やはりそのままでは使えません。

aws-sdk-js/CHANGELOG.md at master · aws/aws-sdk-js · GitHub

feature: RDS: This launch enables RDS start-db-cluster and stop-db-cluster. Stopping and starting Amazon Aurora clusters helps you manage costs for development and test environments.

Lambda Layers で解決する

今までだと関数のコードと一緒に zip にしてアップしていたのですが、関数の容量が大きくなってしまうため
Re:invent 2018 で発表された Lambda Layers を使ってみます。

詳細な手順は以下の記事にわかりやすく書かれていますが、Python の例なので Node.js の場合の差分のみ記載します。

qiita.com

最新の AWS SDK の場合は以下のコマンドを実行して、できあがった zip をアップロードします。

$ mkdir nodejs && cd nodejs
$ yarn add aws-sdk
$ cd ../
$ zip aws-sdk-nodejs-latest.zip -r nodejs

Layers の作成が完了したら、aws-sdkをインポートすることで実行できるようになります。
※ Layers に aws-sdk が存在する場合は、実行環境のデフォルトのものより優先されてインポートされるようです。

RDS.stopDBClusterが使えるようになった参考コードは以下です。

const AWS = require('aws-sdk');

exports.handler = (e, ctx) => {
    const rds = new AWS.RDS();
    const params = {
        DBClusterIdentifier: CLUSTER_NAME
    };
    rds.stopDBCluster(params, (err, data) => {
        if (err) console.log(err);
        ctx.succeed();
    });
}

まとめ

  • Lambda で AWS SDK for JavaScript – 2.290.0 以降のバージョンを使いたい場合は対策が必要。
  • Lambda Layersを使うと、関数の容量を抑えらる・他の関数でも使い回せる と言ったメリットがある!

参考

docs.aws.amazon.com

dev.classmethod.jp