Google Apps Scriptに追加されたマニフェストについて

最近、Google Apps Scriptへ新たな機能がいろいろと追加されています。その中でこれまで苦労していたことが解消されるような便利な機能が追加されましたので、こちらで紹介させていただきます。

紹介させていただく新機能は、[Manifests](https://developers.google.com/apps-script/concepts/manifests)です。

Manifestsの使用方法はスクリプトエディタを起動して上のタブにある「表示」を開くとその中に「マニフェストファイルを表示として」見ることができます。これを選択すると、スクリプトエディタの左側にあるスクリプト一覧の下に``appsscript.json``というファイルが現れます。これをクリックすると、中身を見ることができます。データはjsonです。スクリプトエディタを初めて起動したときは下記のようになっています。

~~~json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
  },
  "exceptionLogging": "STACKDRIVER"
}
~~~

例えば、拡張サービスでDrive APIをオンにすると、次のように変化します。

~~~json
{
  "timeZone": "Asia/Tokyo",
  "dependencies": {
    "enabledAdvancedServices": [{
      "userSymbol": "Drive",
      "serviceId": "drive",
      "version": "v2"
    }]
  },
  "exceptionLogging": "STACKDRIVER"
}
~~~

また、拡張サービスのSheets APIをオンにするため、enabledAdvancedServicesへ``{"userSymbol": "Sheets", "serviceId": "sheets", "version": "v4"}``を追加して保存した後に拡張サービスを確認すると、Sheet APIがONになっています。これは、``appsscript.json``を直接編集することでスクリプトエディタで実行するスクリプトの機能として反映されることを意味しています。これまではブラウザから各スイッチをクリックしてようやく機能追加に至っていたことが、jsonの編集によってできるようになったことは極めて大きな変化だと思います。

jsonの各プロパティは次の通りです。

# appsscript.jsonの中身

| プロパティ | 内容 |
|:-----|:-----|
| timeZone | タイムゾーンの設定 |
| oauthScopes[] | スコープの設定 |
| dependencies | 拡張サービス（dependencies.enabledAdvancedServices[]）<br>ライブラリの設定（dependencies.enabledAdvancedServices[]） |
| exceptionLogging | ログの記録場所（初期値: STACKDRIVER） |
| webapp | ウェブアプリケーションの設定（デプロイされていることが前提） |
| executionApi | 実行可能APIの設定（デプロイされていることが前提） |
| urlFetchWhitelist[] | URL Fetch ServiceでアクセスできるURLの設定 |
| gmail | Gmailアドオンとしてデプロイされているリソースの設定 |

詳細は[こちら](https://developers.google.com/apps-script/concepts/manifests#manifest_structure)から見ることができます。

# 特に便利な機能
追加されたマニフェスト機能は全体が便利になっているのですが、特に個人的にこれは助かると感じたことは、ライブラリ設定とスコープ設定の項目です。

- ライブラリ設定では、これまで一度ライブラリを登録すると、登録したライブラリのプロジェクトIDを調べるために少し手間を感じていましたが、これがマニフェストを見るだけで分かることは大きな飛躍です。

- スコープ設定では、これまで拡張サービスには無く、スクリプトエディタ上で動作させようとすると、OAuth2認証プロセスを実行しないとできなかったことができるようになりました。これもまた大きな飛躍です。
    - 例えば、スクリプトをダウンロードするにはスコープは``https://www.googleapis.com/auth/drive``で問題ないのですが、アップデートや新規作成をしようとすると、``https://www.googleapis.com/auth/drive.scripts``が必要になります。これまでは``ScriptApp.getOAuthToken()``を使用してアクセストークンを取得すると、スコープとして``https://www.googleapis.com/auth/drive``は含まれていたのですが、``https://www.googleapis.com/auth/drive.scripts``は含まれませんでした。ユーザが意図的に含めることもできませんでした。これにより、GASを使ってスクリプトを更新、新規作成したい場合は、OAuth2認証プロセスを実行するためのスクリプトを用意する必要がありました。マニフェスト追加によって、``oauthScopes[]``へ``https://www.googleapis.com/auth/drive.scripts``を記入するだけで``ScriptApp.getOAuthToken()``で取得するアクセストークンにそのスコープが加わりました。これにより、OAuth2認証プロセスのためのスクリプトを用意する必要がなくなりました。
    - この機能を利用すると、アクセストークンが必要なAPIを使用する際、GASでアクセストークンを発行してメールやWeb Apps経由などで外部へ転送して使用することも可能のように思われました。（実際に行う際はセキュリティを考えて用意する必要があると思われます。）

マニフェスト全体について、現状のスクリプトエディタでの設定内容を他者へ説明する際、マニフェストを確認してもらいながらだと、とても説明しやすくなると思いました。これまでは、「ここをクリックして、さらにここをクリックして・・・・」といった操作を伝えた後に、ようやく一つの必要な情報に辿り着いていたのが、一度に複数の情報を確認することができるようになりました。

# 気付いたこと
拡張子がjsonのファイルはこれまでプロジェクトには保存することができませんでした。これが気になりましたのでプロジェクトのrawデータをダウンロードしたところ、typeがjsonになっていました。server_js, htmlに次ぐ新たなtypeです。

~~さらに、appsscript自体を外部から更新できるかどうかも試してみましたが、残念ならが今の段階ではできませんでした。セキュリティの問題かと思われます。マニフェストとは無関係の別のjsonデータをプロジェクトへアップロードしようとしても同様に"404 Bad Request"のエラーが返されました。~~
[追記2](#add2)をご参照ください。

この情報がお役に立ちましたら幸いです。また新たに便利な機能に気づいた際はご紹介させていただきたいと思います。

# 追記1
``oauthScopes[]``へスコープを登録すると、すでに認証していたとしても登録したスコープ以外は使用できなくなるので注意が必要です。

ファイル -> オブジェクトのプロパティ -> スコープ　を見ると、``oauthScopes[]``を使用する前は``https://www.googleapis.com/auth/spreadsheets``がありましたが、``oauthScopes[]``で``https://www.googleapis.com/auth/drive``を追加した後に、ファイル -> オブジェクトのプロパティ -> スコープ　を見ると、スコープは``https://www.googleapis.com/auth/drive``のみになっていました。この状況でスクリプトを実行すると、スプレッドシートを使用する箇所で「呼び出す権限がありません」とのエラーが発生しました。``oauthScopes[]``を使用する場合は少し注意が必要です。

<a name="add2"></a>
# 追記2 
- 本記事を書いていたときには外部からのappsscript.json更新はできなかったのですが、今、appsscript.jsonの外部からの更新ができるようになっていることを確認することができました。これにより、スクリプトを使用したマニフェストの更新が可能であることを確認することができました。
    - ただし、アップロードできるjsonの内容は、マニフェストで使用する[jsonの構造](https://developers.google.com/apps-script/concepts/manifests#manifest_structure)に合わせる必要があります。無関係のjsonをアップロードすると、ファイル名だけで中身のないファイルがプロジェクト内に作られます。これを削除することは可能です。

これはいろいろな応用ができるようになると思われます。
2017年10月29日 更新