EC-CUBE4 に Web API を標準搭載するために考えたこと

EC-CUBE Advent Calendar 2020 の17日目の記事です。

昨日 EC-CUBE 4.1 のベータ版がリリースされました。

公式のニュースはこちら。
https://www.ec-cube.net/news/detail.php?news_id=369

EC-CUBE 4.1 βでは以下の改善がされています。

- Symfony 4.4 へのアップデート
- プラグインインストーラの負荷軽減
- Web API を標準搭載

プルリクを見ればどんな実装かはわかりますが、なぜそのような実装になっているのかを読み取るのは難しいのではないでしょうか。
この記事では「Web API を標準搭載」に着目し、なぜこのような実装になっているのか、実装時に考えたことを説明してみようと思います。

**注意**

と思って書いていたんですが、細かすぎて誰得な記事になってしまいました。
いったん最後まで書きましたが、あまり面白くないかもしれません。
先にテーマを絞ってしまったことを少し反省。

## プラグイン OR 本体の改修

EC-CUBE に Web API を標準搭載する方法として「プラグイン」と「本体の改修」の２種類が思い浮かびます。
EC-CUBE 4.1 βでは「プラグイン」が選択されました。

- ソースコード管理のしやすさ
  - 4.0 対応の Web API プラグインがすでにあるため、本体を改修すると 4.0 と 4.1 でソースコードを別で管理する必要があります。プラグインなら同一のソースコードで管理が可能です。
- プラグイン単位のアップデート
  - プラグインであればプラグイン単位でアップデートが可能です。本体の改修の場合は本体の次のリリースまで機能の改善を待たなければなりません。
- 他プラグインへの展開
  - プラグインの方式であれば将来的に Web API プラグイン以外のプラグインも搭載できるよう拡張できる可能性があります。例えば「商品レビュー」や「関連商品」、「おすすめ商品」などのよく使われているプラグインも搭載できますし、B2B向けなどある程度領域を絞ったプラグインをセットにして搭載することも想定できます。制作会社が独自で搭載するプラグインを選びたい可能性もあります。

## いつプラグインをインストール/有効化するか

プラグインをインストール/有効化するタイミングはいくつか考えられます。

- インストール/有効化された状態でGit管理
- パッケージング時にインストール/有効化
- EC-CUBEのインストール時にインストール/有効化

EC-CUBE 4.1 β ではパッケージング時にプラグインをインストール、EC-CUBEのインストール時にプラグインを有効化しています。

どのタイミングでプラグインをインストール/有効化すべきかを判断するには、それぞれでどんな処理をしているのか知る必要があります。

- インストース処理
  - ソースコードの取得・展開
- 有効化処理
  - DBのスキーマの更新（初回有効化時）
  - 有効化時の独自処理（ファイル設置、ブロックを配置したりなど）

Composerの機能を拡張し、プラグインのインストール機能 `ec-cube/plugin-installer` を実装しています。 `composer install` で `ec-cube/plugin-installer` もインストールされますが、同時にEC-CUBEのプラグインのインストールはできません。（詳細は端折ります。）ですので、プラグインのインストールまでGitで管理するのはやめました。
EC-CUBEのインストールはユーザの環境で行われます。プラグインのインストールという重たい処理はEC-CUBEのインストール時にはできるだけさせたくありません。
そういうわけでパッケージング時にプラグインをインストールすることにしました。

有効化も重たい処理ですのでパッケージング時にしたかったのですが、いくつか課題があったためEC-CUBEのインストール時にプラグインを有効化するようにしました。
有効化時のDBのスキーマの更新は頑張ればパッケージング時でも可能かもしれません。
有効化時の独自処理にはファイル設置、ブロックを配置したりなど色々な処理が想定でき、 Web API プラグイン以外のプラグインでの利用を想定した場合はパッケージング時の実行はできません。かといってEC-CUBEのインストール時にプラグインの独自処理を実行するには今の設計では大掛かりな変更となってしまいそうです。

そういうわけでEC-CUBE 4.1 β ではパッケージング時にプラグインをインストール、EC-CUBEのインストール時にプラグインを有効化しています。

## プラグインインストール時の通信先

EC-CUBE 4.0 のプラグインインストール/有効化時には通称 `package-api` と呼ばれるサーバにリクエストしてプラグインを取得しています。 `package-api` はオーナーズストアと連携されており、購入済みのプラグインのみインストールできるよう、通常は認証キーの設定が必要です。 Web API プラグインをインストール・有効化する際は認証キーを設定していませんので、EC-CUBE 4.1 β のリリースに際し、認証キーなしで Web API プラグインをインストールできる `package-api-41beta` が準備されました。記事作成時点で `package-api-41beta` は Web API プラグインしかインストールできません。認証キーの確認以外は `mock-package-api` と同じですので、みなさんが自分のプラグインなどで動作確認される場合は `mock-package-api` をご利用ください。EC-CUBE 4.1 リリース時の  `package-api` の構成はまだ決まっていませんが、初期インストールされる一部のプラグインのみ認証キーが不要になるような改修がされるんではないでしょうか。

参考
https://github.com/EC-CUBE/ec-cube/pull/4782

## パッケージングスクリプト

実はパッケージングスクリプトもGitで管理されています。
以下が EC-CUBE 4.0 のパッケージングスクリプトとなります。
https://github.com/EC-CUBE/ec-cube/blob/4.0/.github/workflows/deploy.yml
GitHub Actions で設定されており、 GitHub でリリースタグが切られると自動でパッケージングスクリプトが走り、リリースタグにパッケージングがアップされます。

Web API プラグインがインストールされるようしたパッケージングスクリプト改修のプルリクは以下です。
https://github.com/EC-CUBE/ec-cube/pull/4784
追加した処理は以下です。

- DB の services を追加（今まではEC-CUBEのインストールもしないので不要だった）
- EC-CUBEのインストール
- プラグインのインストール
- DBの永続化（初期読み込みの CSV にプラグイン情報を追記）

スクリプト自体を理解するには GitHub Actions の設定を見るのが助けになると思います。
https://github.com/EC-CUBE/ec-cube/tree/4.0/.github/workflows
UnitTest, E2Eテストなどの自動テストの設定がされています。

## EC-CUBEのインストール処理

EC-CUBEのインストール時にプラグイン有効化処理を行います。
改修のプルリクは以下。
https://github.com/EC-CUBE/ec-cube/pull/4786
こちらも一筋縄では行かず、色々と対策をしています。

### キャッシュの課題

EC-CUBEのインストール時、プラグインの有効化時などの処理にはキャッシュの削除・再生成の処理が必要になります。
こちらは重たい処理となっており、一つのリクエストでキャッシュの生成と削除を同時にできない問題がありました。
また、EC-CUBEのインストール時には `APP_ENV=install` で実行されていますが、プラグインをインストールするには `APP_ENV=prod` で実行されている必要があります。
（さらっと書きましたがここらへんだいぶ調査しました）
これらの問題に対応するため、EC-CUBEのインストール完了直後に Ajax でプラグインの有効化とキャッシュの削除をするような実装となっています。

### APP_ENV が変わる課題

`InstallController` は安全のため `APP_ENV=install` でしか有効にならないので `InstallPluginController` を別で作成しています。
AjaxでのアクセスはEC-CUBEのインストール直後に実行されるため管理画面へログインする前にリクエストされます。プラグインの有効化、キャッシュの削除のリクエストはEC-CUBEのインストール時以外に実行されては困りますので、適切にアクセスを制限する必要があります。Ajaxのアクセス前後で `APP_ENV` が変更となるため、Sessionも共有できません。こちらは `APP_ENV=install` でトランザクションチェック用のファイルを作成し、 `APP_ENV=prod` ではそのファイルをチェックすることでアクセス制限をしています。

## おわりに

書き始めてしまったのでキリがいいところまで書きましたが、細かすぎて誰得な記事になってしまったかもしれません。
まだまだ書ききれなかったことがいっぱいあるので、興味のある人は雑談でもしましょう。
次はもっと実用的な記事が書けたらいいですね。
ではまた。