Google Apps Scriptでウェブアプリケーション作成入門【GAS】

これまでブログにて、Google Apps Scriptを用いた様々なアプリケーションやテクニックを紹介してきましたが、その多くはある程度Google Apps Scriptが出来る前提でのものでした。その為、入門者向けの内容ではなく、コードの説明も主にポイントになる点と、コード内のコメントでどのようなものを書いてるのかを読み取るものになっています。その中でGoogle Apps Scriptでウェブアプリケーションを作る方法をまとめて欲しいという話があったので、現時点で最新の作り方をまとめてみました。

目次

今回使用するクラス等

概要

Google Apps Scriptにはウェブアプリケーションを作り表示する為のサービスとして2種類のタイプのクラスが用意されています。どちらもウェブアプリケーションとして出力した結果は同じなのですが、その動作原理が異なるので、利用目的などに応じて使い分けると良いでしょう。また、ウェブアプリケーションとして公開する為の手順がとても重要なので、この手順はよく把握しておく必要性があります。コード変更時に嵌まるポイントの1つです。

また、HTML内ではjQueryといったライブラリやangularjsといったフレームワーク等、現在はかなり自由に使えるようになっています。速度も高速化されていますので、かつてのようにサニタイズされてライブラリが動かないだとか、遅いといった事がありません(それらの古いモードは現在廃止されました)。

用意の仕方

Google Apps Scriptのスクリプトのみではウェブアプリケーションは作れません。必ず表示する為のHTMLファイルが必要です。とはいえ、ドライブにHTMLファイルを用意するのではなく、スクリプトエディタの画面上で用意します。

  1. スクリプトエディタのメニューよりファイル⇒新規作成を開く
  2. HTMLを選択し、名前をつける
  3. HTMLファイルが作られるので、その中にHTMLやCSS、JavaScriptを記述してゆく

createHtmlOutputFromFile

非常にポピュラーなウェブアプリケーションを作る為のメソッドです。用意したHTMLをアウトプットするだけです。doGet()関数で呼び出すのがお決まりになっていますので、以下のコードで呼び出すだけです。一番よく使う形式です。以下の事例だとindex.htmlを呼び出しています。自分のコードでは、これに加えて、.setSandboxMode(HtmlService.SandboxMode.IFRAME)というものをつけていますが、これは今現在は省略可能です。

createTemplateFromFile

少し変わったウェブアプリケーションを作るためのメソッドです。同じく用意したHTMLをアウトプットします。doGet()関数で呼び出すのがお決まりになっていますが、特徴的なのがこれは動的に呼び出せるという点です。

  1. スクリプトレットというものを利用してGAS側のコードをHTML側から実行する事が可能です。
  2. GAS側で取得したデータをHTML側変数で受け取る事が可能である
  3. evaluate()という関数を必ず使う。

スクリプトレットとは、<?= kinoko(); ?>といった形で記述をし、この場合、kinoko関数実行するといったものになります。また、output.append(“キノコ”)関数も使え、HTML内に文字列を出力させる事が可能です。また、<?!= ?>という記述もあり、この場合強制的に実行する強引なやり方です。HTML側でGAS側の実行結果を変数に受け取るようなシーンで使用します。

viewportの指定

HTML Serviceでは、HTML側にスマフォ対応で利用するviewportのメタタグを記述しても無視されます。このメタタグはGAS側で出力する前にオプション指定する必要があります。

.addMetaTagでviewportを指定することが可能です。この指定をしない場合、スマートフォンに最適な表示にはならないので、スマフォ用アプリを作成する場合には必須の項目です。

ウェブアプリケーションとしてデプロイ

これらのメソッドを使っても、最終的に「ウェブアプリケーションとしてデプロイ」をしなければ、ウェブページは表示されません。また、このウェブアプリケーションとしてデプロイはいくつかのハマりポイントがありますので注意が必要です。公開の仕方は以下の手順。

  1. スクリプトエディタを開く
  2. 右上のデプロイをクリック
  3. 新しいデプロイをクリック
  4. 種類の選択ではウェブアプリを選択し、次のユーザとしてアプリケーションを実行で誰の権限で動かすかを指定する。自分か?アクセスしてるユーザの二択。
  5. アプリケーションにアクセスできるユーザを指定する。自分のみ、組織内全員、Googleアカウントを持つ全員、全員の四択。但し、全員の場合はGoogleアカウントが必要で、匿名含むの場合は、Googleアカウントなしでアクセス可能です。
  6. 最後に導入すると、ウェブアプリケーションのURLが取得できます。このURLでアクセスをします。URLの最後がexecが本番用、devがテスト用で、テスト用はデプロイをテストをクリックすると表示されますが、変更したコードがそのまますぐに反映されてしまうので、テスト用のURLで運用しないように。
  7. 次回以降コードを編集して再デプロイ時はデプロイを管理から同じURLにて、新しいバージョンを指定して発行することが出来ます。

※5.の設定はGoogle Workspaceの設定次第では、Googleアカウントを持つ全員や全員は選択肢に出てこない事があります。

図:デプロイの仕方で挙動が変わります。

嵌まるポイント

さてハマりポイントなのですが、以下のような感じです。

  • G SuiteことGoogle Appsの場合、外部との共有を管理者が許可していない場合、匿名でウェブアプリケーションは実行出来ませんし、外部に公開は出来ません。
  • 公開済みの場合、再度公開を実行すると最新のコードをテストという項目があり、URLの最後に/devが付きます。公開していない状態でも最新コードで表示されます(ファイルオーナーのみ
  • 但し上記のそれはデベロッパーモードであり、コードの変更が直ちに反映されてしまうので、一般公開のURLとして使わない事。execの場合、新しい版で公開し直さない限り、コードは反映されません(但し、HTML部分のみで、GAS部分は反映されてしまいます)。
  • きちんと正式公開した場合はURLの最後に/execが付き、これを貼り付けるガジェットのURLや一般公開のURLとして使うこと。
  • コードを変更した場合、execのURLに反映させる為には、プロジェクトバージョンを変更し新規作成にしなければコードが反映しません。
  • 新開発画面からは旧来と異なり、同じプロジェクトではない形でウェブアプリケーションを公開してしまうと、URLが変わってしまいますので注意。必ずデプロイを管理から行うこと。
  • 何度もプロジェクトバージョンを新規作成してると、版が積み上がります。これらはファイル⇒版を管理の中で確認が出来、過去の版を廃止する事が可能です。過去の版でウェブアプリケーションを表示する場合は、プロジェクトバージョンを古いものにして公開すればOK.
  • 但し、過去のプロジェクトバージョンのコードに戻す事は不可能。変更履歴から復元は可能ですが、全て変わってしまうので、細かく保存しましょう。
  • ウェブアプリケーションを無効にするをクリックすると、ウェブアプリケーションが停止します。停止しないと版を廃止する事はできません。
  • ウェブアプリケーションとして公開は、プロジェクトオーナーでなければ出来ません。これはスプレッドシートのオーナーとはイコールではありません。複数プロジェクトを設置した場合、片方が自分が作ったものではない場合、そのプロジェクトを自分が削除したりウェブアプリケーションとして公開する内容を変更したりは出来ません。
  • 忘れがちですが、コードを書いて、一度doGet()などを実行し、承認をしておきましょう。これをしないと動きません。
  • シートにアクセスするような場合、ウェブアプリケーションの場合は、SpreadsheetApp.getActiveSpreadsheet()でアクセスは出来ません。SpreadsheetApp.openById(“シートのID”)でなければ、シートのデータは取得できません。但し、スプレッドシート上のダイアログとして使う場合は、取得が可能です。
  • メールを送信するMailAppを使っている場合、Fromの送信者アドレスはウェブアプリケーションとして公開した人のアドレスになります。このアドレスは任意のアドレスに変更は出来ません(但し、その人のメアドにエイリアスを設定してる場合には、MailAppのオプションにfrom: ‘eye4brain@xxx.gr.jp’といった形でFromを変更は出来ますが、あくまでもエイリアスです)。
  • HTML Serviceで出力したHTML上では、ページ内リンクのアンカーは利用する事ができません。つまり、アンカーだけではページ内でジャンプが不可能です。

アクセス権限

概要

前項のハマりどころをもう少し深く掘り下げてみます。このアクセス権限が結構クセモノで、混乱するポイントなのですが、シートのアクセス権限(特に書き込み)とウェブアプリケーションのアクセス権限は別物という事です。以下の表のような感じになります。

スプレッドシート権限 WebApp(ユーザ権限) WebApp(オーナー) 備考
閲覧・編集権限 誰でもアクセスすることが可能です
特定ユーザ 🔺 ユーザ権限の時はシート権限が必要
非公開 非公開でもアクセス可能

※上記の表の⭕は、一般ユーザがアクセスした場合に、WebAppを実行できるかどうか?どうかを示しています(実行権限は制限していない場合です)

※非公開でもアクセス可能ですが、WebAppの実行権限が自分のみの場合、オーナーだけが実行できます。

※G Suiteだとさらに実行権限にはドメイン内ユーザのみという権限もあります。

よって、スプレッドシートの書き込み権限が自分のみであっても、実行権限が自分で、ウェブアクセスの権限が全員ならば、自分以外の人であっても、自分の権限を持って書き込みが可能です。一方で実行権限がアクセスしてる人の場合、ウェブにアクセスは出来ますが、シートの書き込み権限が無いことになるので、書き込めません。

この時、スプレッドシートの編集履歴は「自分」しか残りません。自分の権限で書き込みをさせている為です。逆を言えばシートは非公開で閲覧させたくないが、ウェブは使わせたいなんて時は実行権限を自分にしておくと良いでしょう。

ユーザ各々の実行権限で書き込みをする為には、以下のような設定にします。

この場合、スプレッドシートに個別にユーザに編集権限を加える、もしくは全員編集可にしておく必要があります。編集権限はグループアドレスでもOKです。この時のシートの編集履歴はそれぞれの人のメアドが記録される事になります。但し、各々のユーザはウェブアプリケーションアクセス時に1度だけ、実行承認をしなければなりません。また、後にコードを追記して新しいAPIを使った場合も、承認画面が改めて出ることになります。

※また要注意ポイントとして、WebAppの実行権限を自分にした場合、全ユーザが自分の権限でGASを実行するため、例えばUrlfetchAppなどのQuota制限のあるAPIの実行を伴う場合、リミットに達しやすくなります。各ユーザの場合には、各ユーザが1日辺り実行できるリミットとなります。

GASの実行権限

また、このアクセス権限が曲者で、以下のような特徴があります。

  1. オーナーの権限で動かす場合、ドライブのファイルはオーナーがアクセス出来る範囲になる
  2. ユーザの権限で動かす場合は、ドライブのファイルはユーザがアクセス出来る範囲となる(事前にそのユーザにアクセス権限の無いスプレッドシート等はアクセスできない)
  3. 2.は逆を言えばアクセス出来る以上そのファイルの中身はユーザは全部閲覧可能(なので、他のユーザの情報などを管理するようなシートとしては使えない。後述の特定ユーザに使わせるといった事がユーザ権限で動かす場合には使えません。)
  4. 1.はオーナーの権限で動かしているので、対象のファイルがオーナー以外非公開でも、ユーザがそのファイルにGAS経由でアクセス可能となる(ファイル自体は見えないので、ユーザ情報の管理などでシートを使う事が可能)
  5. また、Picker APIなども1.と2.で見えるフォルダの一覧が、オーナー側か?ユーザ側か?挙動が変わるので注意。
  6. 但し、Session.getActiveUser()で取得出来る実行ユーザ情報は、オーナーではなくアクセスしたユーザのアドレスなどになる

特定企業とファイルをシェアするようなアプリの場合、そこに無関係の別の企業のアクセス情報であったり、またファイルにつけた許可メアドなどが、ユーザ権限で動かした場合には見えてしまうので、非常によろしくないです。情報漏えいにつながるので、この権限周りは一度自分で色々試してどういう結果になるのかを見に付けておくべきでしょう。

また、この実行権限はスクリプトプロパティでも継承されるため、特にgetUserPropertiesを使ってる場合は要注意です。

Google Apps Scriptで使う情報はプロパティを利用しよう【GAS】

作成者権限で動かしつつユーザ権限でも実行させる

前述のアクセス権限にあるよう、GAS作成者が管理者で様々なメソッドをウェブアプリを通して実行させたいので実行権限を自分にしてデプロイすると、今度はユーザがアクセスしてきてもそれらすべての実行の結果は自分自身が実行したことになります。つまり、カレンダーに登録作業をしても全て自分のカレンダーに登録されてしまいます。かといって、ユーザ権限で動かすことにすると、現在のアプリの元になってるスプレッドシートを全員に共有が必要になるだけじゃなく、見せたくないファイルも見せる必要性が出てきたり、ユーザ毎に認証も必要になります。

そこでこの問題を解決する「自分の権限でウェブアプリを動かしつつ、一部をユーザの権限で動かす手法」があります。以下のエントリーにまとめていますが、サービスアカウントを利用した手法になるので少々環境構築が厄介ですが、このテクニックを利用する事で、GASでウェブアプリの可能性が一気に広がる為、ぜひ体得して利用してみて下さい。

Google Apps Scriptで一部だけをユーザ権限で動かしたい時は?【GAS】

アクセスできるユーザで全員が指定出来ない

デプロイ時にアクセスできるユーザでどこまでアプリにアクセスを許可するかを指定出来ますが、無償のGoogleアカウントでも使えますがちょっと設定が必要になります。

Google Workspaceの場合にはそもそもテナントに於いて、Googleドライブの共有設定に於いて外部での共有がオンになっている必要があります。

  1. Admin Consoleにログインする
  2. 左サイドバーから「アプリ」⇒Google Workspace⇒ドライブとドキュメントを開く
  3. 共有設定を開く
  4. 共有オプションを開き、外部との共有をオンにする

ただしこの設定はドライブ内の全ユーザやファイルに対してもデフォルトではオンとなってしまい、推奨される設定ではありません。よって、組織部門で外部共有専用の部門を作成した上で、なおかつその部門にだけ所属する特別な共有ドライブを作成し、そこの中でだけ許可するといったような高度な管理が必要です。この内容については以下のエントリーを参考にしてみてください。

Google Workspaceで高度なアクセス制限を実現する

また、無償ユーザも含めてですが、「無条件に全員アクセス可能」な全員を指定するためには、

  • ファイルそのものが外部に共有されている必要性は無いものの
  • 次のユーザとして実行が「ウェブアプリケーションにアクセスしているユーザ」の場合は、「全員」を選択出来ません
  • よって、実行権限は「自分」としてデプロイする必要があります。

更に注意点として、Google Workspaceのバグとして外部共有をオンにする前にデプロイしていたウェブアプリケーションに於いては、後からオンにしても「全員」のオプションが使えません。よってこの場合、再度新しいウェブアプリとしてデプロイする必要性があります。後から変更が出来ません。

図:使うにはちょっと設定が必要

WebアプリのURLを取得する

旧式の方法

現在、デプロイしてある最新のウェブアプリケーションのURLを取得したいケースがあります。例えばこのウェブアプリを利用する対象者にメールで通知であったり。しかし、いちいちデプロイ管理画面からURLをコピーして、Gmailでメールを作って貼り付けて送るではあまりにもスマートじゃありません。

以下のコードで一発で最新のデプロイしたウェブアプリのURLを取得出来ます(最後がexecのURLになります)。但しこの方法はV8エンジンがオンの状態の場合、誤ったURL(というかデプロイメントID)が返ってくる問題が報告されています。現在も解消されていません。

V8での取得方法

前述の方法はV8がオンの場合、正しいデプロイされたウェブアプリのURLが取得出来ません。この問題に対してStackOverFlowではGCPのApps Script APIのDeployments.listを利用して取得する方法を提示しています。現在、Apps Script APIは旧Execution APIの機能である「直接REST APIのようにGASの関数を叩く」という機能の他にこういった機能も持ち合わせています。

こちらについては以下のエントリーで別に紹介しています。

Google Apps ScriptでWebアプリのURLを取得する【GAS】

テクニックと注意点

現在、ファイルを開くことができません

デプロイして、ウェブアプリケーションを開いたら「現在、ファイルを開くことができません」というエラーが出て、ウェブアプリが表示されないケースがあります。これは、開こうとした人が、2つ以上のGoogleアカウントにログインしている状態の時に発生します。以前は発生していなかったのですが、Google側がマルチアカウント対応を諦めたのか?ここ数年表示するようになっています。

解決法は、アクセスする人が一度アカウントを全部ログアウトして、1つだけGoogleアカウントにログインした状態にすること。

ドライブ上のPDFをiframeで表示する

Google Drive上にあるPDFファイルをHTML Service上で表示したい場合には、当然ながらiframeを使ってURLを指定する必要があります。しかし、この時に指定するURLがちょっと特殊で、ファイルの共有リンクで指定しても403エラーで見ることが出来ません。

しかし、以下のようなURLにすると埋め込み表示させることが可能になります。

そして上記のURLをiframeでsrcに指定するとこうなります。

すると、webapp上でPDFをプレビューできます。但し、CORSのエラーがいくつか出てる状態(直接的にプレビューする分には問題が無い)

図:Webアプリ上でドライブのPDFを表示してみた

ドライブ上のHTMLを表示する

どうしても、Google Apps Scriptの開発エディタ上でHTMLを書くのが辛いというケースでは以下のような形でHTMLファイルを呼び込んでウェブアプリケーションとして表示するテクニックがあります。

  1. 予めドライブ上に作り込んだHTMLファイルをアップロードしておく
  2. そのファイルのIDを取得しておく

以下のようなコードでこのHTMLファイルを元にウェブアプリケーションを構築し、エディタ上でHTMLを追加せずに作業が出来るので、他のアプリでも使うようなテンプレートとして流用できる利点があります。

  • 上記の状態でウェブアプリケーションとして公開すると、HTMLファイルを取得して表示してくれます。
  • スクリプトレットを使いたい場合には、HtmlService.createTemplate(html)として読み込めばOK。HTMLファイル内にスクリプトレットで例えばGAS側の関数や外部のCSS、JSなどを読み込ませればOK
  • Google Drive File Streamと合わせてローカルでHTMLファイルをVSCodeで編集するといったことが出来るので、色々と利点のあるテクニックです。

図:こんな感じで配置する

外部のJSやCSSを読み込み

嵌まるポイントの1つなのですが、HTML Serviceで外部JavaScriptやCSS類などを呼び出す場合、http://www.xxx.com/test.jsといった形で呼び出すことは出来ません。httpではなくhttps://でなければならないというルールがあります。https://が使えるのであれば、//www.xxx.com/test.jsという形で頭をつけずに指定して呼び出すことも可能です。

個人でレンタルサーバをファイルの置き場にしている場合、共用SSLなどがない場合には呼び出せませんのでご注意を。呼び出し方そのものは、外部のファイルの呼び出し方と全く一緒です。headタグ内に記述しましょう。

  • ※外部に公開してるGoogle Driveのフォルダであれば、直リンクで呼び出すことも可能です。Direct Link Generatorなどを利用してURLを取得してみましょう。
  • また、createTemplateFromFileを使ったケースの場合、GAS内でJavaScriptやCSSを独立して管理して呼び出すことも可能です。

HTML側⇔GAS側で通信する

Google Apps Script側と生成したウェブアプリケーションは、サーバーとクライアントの関係にあります。その為そのままでは、HTML側からGoogle Apps Script側に命令やデータを送ったり、またGoogle Apps Script側からHTML側へ返してあげる事が出来ません。しかし、Google Apps Scriptではこの相互通信をする手段として特別な関数を用意しています。以下に示すもの以外にも、google.script.url関数やgoogle.script.history関数なんてものも新規に追加されています。

  • ※これらの関数は非同期で実行されてしまいますので、基本的に順番に実行はされません。
  • ※これらの関数はHTML側で記述します。

また、これらの関数はプライベート関数にしておかないと、誰でも簡単に実行できてしまうので、そちらも意識しておく必要があります。

Google Apps Scriptの関数は誰でも実行できちゃうので注意【GAS】

google.script.run関数

Google Apps Script側に用意してる関数を一方的に実行する関数です。引数で値や配列などを渡してあげる事が可能です。一方的に実行して完了してしまうので、返り値を受け取るといった事は出来ません。以下のような構文で実行します。

google.script.run.withSuccessHandler関数

この関数は上記の関数とは異なり、返り値を受け取る事が可能です。withSuccessHandlerで成功時、withFailureHandlerで失敗時の処理をそれぞれ定義でき、同時に定義することも可能です。返り値を受け取って、HTML側で引き続き返り値の処理をする事が出来ます。例えば以下のような記述をします。

直接値を変数で受け取る

直接実行した結果を変数で受け取る事も可能です。ただし、非同期で実行される点は同じですので、受け取った変数を元に続けてなにか作業をしたい場合には、うまく動作しないことがあります。

Promiseで同期的に処理

前項のgoogle.script.run.withSuccessHandlerによるGASからの返り値に基づいて処理するコードでは単一の処理ならばこれでも良いですが、いくつかの連続した処理をしなければならない時には、コードが複雑になってしまいます。そこで、Promiseを利用して順番に非同期なgoogle.script.runを実行して計算する手法があります。

GAS側コード

test1〜3はそれぞれ、セルの値を取得して返すだけの関数です。これをHTML側から順番に取得して計算結果を表示します。

HTML側コード

  • Promise〜thenによる順番に実行する処理です。
  • 1つ目のgoogle.script.runの結果だけはresolveで返しています。コード内では使っていませんが、resolveで返す場合、次のthenではresultで値を引き継げます。
  • グローバル変数に加算しつつ最後にalertで表示しています。
  • Promiseを使わない場合、これら3つの処理は次の処理を待たずに実行されてしまうので、600の回答が得られることが保証できなくなります。
  • es6-promise.min.jsの読み込みはIEなどの古いブラウザ用のライブラリです。
オブジェクトに変数付で渡す

一旦createTemplateFromFileで生成したオブジェクト(output)に対して、output.dataという形で変数を渡せます。すると、evaluateした先のHTMLでこの値を受取る事ができるので、<?=data?>で表示させる事が可能です。前項のdoIt関数で処理するよりもお手軽ですね。送る側は特にJSON.stringifyする必要なくそのまま渡せばよいです。

HTML側は以下のような感じになります。

Google Drive内に別途作成したCSSやJSのファイルを使いたい場合もあるでしょう。そうすることで、他のGASプロジェクトでもそのまま流用できる為、非常に便利ですし、手間が省ける(プロジェクト毎に微妙に違うとか防げますし)。また、他のシステムから自動で上書きで吐き出されるJSONファイルを固定でロードするような仕組みにしておけば、インポートの手間も省けますね。

この場合、直接スクリプトレットでドライブのデータを取得するコードを書いても反映されないので、GAS側で一度取得して返すようにしてあげます。

GAS側コード

  • 各CSSやJSのファイルの文字コードに注意。Shift-JISならば、getDataAsStringではShift-JISを指定する必要があります。
  • ドライブにある各ファイルから中身を取得し、HTML側へ返してあげています。
HTML側コード

  • retdrivecssでドライブのCSSを、retdrivejsでJSファイルを取得しています。
  • ドライブのJSファイルにあるtestman()という関数を呼び出しています。

ドライブからCSSやJSファイルを直接受け取る方法 - その2

もうひとつ、ドライブにあるCSSとJSファイルを呼び出す方法があります。こちらのほうがスマートかもしれません。HTML側へ直接対象のファイルの直リンクダウンロード出来る形でURLをそれぞれ呼び出してあげるだけ。

  • https://drive.google.com/uc?export=download&id=に続けてファイルのIDを入れるだけ
  • 但しファイルのアクセス権限には注意。厳格にすると呼び出せなくなる

GAS側からJSON形式でHTML側へ渡す

GAS側からHTML側へreturnする場合、通常JSON.stringifyを行ってHTML側でJSON.parseするのが定石です。スプレッドシートで取得したデータは通常の配列であるため、受け取り手であるHTML側も配列で受け取って処理をすることになります。さてこの時、GAS側からスプレッドシートのデータをJSON配列で渡せたら、非常に便利です。理由は

  1. 通常の配列ではスプレッドシート側で列位置の変更が生じると、HTML側コードを変更する必要がある
  2. JSON配列の場合には、配列の中の特定値を指定できるので、列の変更が生じてもHTML側で変更は必要ない。
  3. 配列の何番目に「hoge」が入っているからといったことで頭を悩ませながらコードを書かなくて良い

GAS側からJSON化して渡すには、スプレッドシートのデータをJSONで取得するを参考にしましょう。主要なコードは以下の通り。

HTML側ではJSON.parseで受け取ると、例えばjson[0].pointといったように、0レコード目のpoint列の値といった具合に取得が可能になります。やり方しだいでは、複雑なJSONを構築して渡すこともできますね。

Google Sitesにページ全体で埋め込み

この手法がもっとも手軽でもっともスマートなのが、Google Sitesを用意して、そちらに「ページ全体で埋め込み」を利用して埋め込むだけで、この帯は表示されなくなります。もちろんアプリもサイトも外部に公開するか?対象者は閲覧可能な状態にしておく必要はあります。

こちらにサンプルを用意しました。

図:青色の帯が出ないで表示されるようになる。

スクリプトプロパティを共通化する

1シート2プロジェクト体制の場合、スクリプトプロパティも個別になり具合が悪い事があります。その場合、3プロジェクトにしてあげて、3つ目のプロジェクトのスクリプトプロパティを1つ目、2つ目から読み書き出来るようにしてあげれば、共通化する事が可能です。以下の手順で共通化をしましょう(ライブラリ化する作業です。詳細は参照してみてください。)

  1. 3つ目のプロジェクトをつくり、スクリプトエディタにて読み書きする為の関数を用意する
  2. スクリプトエディタのメニューより「ファイル」⇒「版を管理」を開く
  3. 適当な名前をつけて、新しいバージョンを保存をクリック
  4. 続けてメニューより「ファイル」⇒「プロジェクトのプロパティ」を開く
  5. スクリプトIDを控えておく
  6. 1つ目のプロジェクトおよび2つ目のプロジェクトをそれぞれ開く
  7. スクリプトエディタのメニューより「リソース」⇒「ライブラリ」を開く
  8. ライブラリを追加に5.のスクリプトIDを入れて、追加ボタンをクリック
  9. バージョンを指定して、識別子を作ります。識別子は英語で他のライブラリと重複しない名前で適当につけます。自分はsetmanとつけてます。
  10. 既存のスクリプトプロパティを呼び出して書き込むコードをライブラリの関数を呼び出す形に書き換える。この時呼び出し方は、setman.kinoko("value")で、3つ目のプロジェクトのkinokoという関数にvalueを渡せるようになります。

図:こうしてライブラリ化しておくと参照が可能になる

ちなみに、自分が3つ目のプロジェクトでスクリプトプロパティを読み書きさせてるコードは以下の通り。

Cloud Consoleプロジェクトも共通化する

1シート2プロジェクト体制の場合、Google Cloud Console側のプロジェクトも個別になります。これでは、Cloud Console側で設定した各種APIの設定や認証情報も個別になってしまうので、二度手間になります。そこで、以下の作業をする事で、1つ目のプロジェクト側に紐つける事が可能になります。(Cloud Consoleを弄ってみるを参照してください。)

図:プロジェクト確認ダイアログ

ページ内リンクについて

Google Apps ScriptのHTML上では、<a href="#hoge">のようなページ内リンクが利用出来ません。クリックをしても何も起きず、また、URLをコピーして開いても、真っ白です。こんな場合、jQueryを使う事でこのアンカーについてページ内リンクのように移動できるようにする事が可能です。

ただし、上記の場合、hrefが#で始まる何かをクリックした場合の挙動をコントロールしているので、別にアンカーに対してイベントリスナーでコマンドを割り当ててるとオカシナ挙動になるので、注意は必要です。

同時アクセス数について

HTML Serviceだけに限らず、Google Apps Scriptの同一スクリプトに対するアクセス集中に関する資料は、Google公式によると「最大30接続」。これについては、こちらでも立証しています。しかし、過去の経験から「一時的にアクセスが集中した場合、処理が止まる可能性」は十分にあります。スプレッドシート上の呼び出しであっても「同時呼び出しの数が多すぎます」といったエラーに遭遇する事があります。

当然ながら、Quotaに掛かるようなコードである場合、複数名同時に利用することによってLimitに掛かってしまい、エラーとなって正しく処理がなされない可能性があります。設計上考慮すべきは以下の項目になります。

  1. 瞬間的に30名以上の接続や利用が見込まれるケースの場合、スプレッドシートは1つでも、受ける窓口(HTML Service)は、部署毎やチームごとなどに分けてあげる必要性がある。
  2. UrlfetchAppのリミットは1日にコールできる数以外にも、10秒間の間に呼び出せるのがおよそ3回まで(これについては公式に掲載なし。しかし、事実Limit execeedでストップする)なので、ユーザの呼び出しに応じて使わないようにする。
  3. HTML Serviceの場合、オーナー権限での呼び出しだと1ユーザが全てを実行する事になるので、Limitに達しやすい。よって、実行権限は「各ユーザ毎の権限」で行うようにする(こうする事で殆どのQuotaは回避可能)
  4. ただし、3.の場合そのユーザに対して、対象のスプレッドシートへのアクセス権限も必要となるので、運用上シートを見せずに運用は、トリッキーな回避策が必要になる。
  5. 1.の場合、必ず排他制御が必要になる。また、スプレッドシートへの書き込みは必ずまとめて1発で書き込むようにしないと、排他制御の時間リミットに達するので、1行ずつ書き込みはしないように。
  6. 他のアプリからのアクセスを行う場合には、doGetやdoPostではなく、Google Apps Script Execusion APIを利用するようにしましょう。
  7. 大規模ユーザをさばく必要がある場合には、Cloud Functions + Cloud SQLの活用を検討し、スプレッドシート側は、管理者が必要な時に、Cloud SQLからデータを取れるように仕様変更をするのが望ましい。
  8. Google Formで対応出来るものは、そちらで対応させる事で、同時にGoogle Apps Script呼び出しを減らせます。但し、自動応答などのスクリプトを使ってる場合には効果はない。

図:StackOverFlow掲載の同時アクセスの計測結果

デプロイを自動化する

たまたまGithubを探索していたところ、Google Apps Script APIを利用する事で、現在公開されているウェブアプリケーションのデプロイを自動化出来ることがわかりました。但し、今回のコードは「同じURLでウェブアプリケーションをデプロイ」するパターンになります。

こちらのサイトにて、そのコードが公開されています。使い方ですが、以下の手順になります。予め、GCP側でプロジェクトを用意しておく必要があります。

  1. ウェブアプリケーションをデプロイしたいGASアプリのプロジェクト設定を開いてスクリプトIDを調べておく(以後、アプリAと呼びます)
  2. スクリプトを実行するGASにて、プロジェクト設定を開く(以後、アプリBと呼びます)
  3. アプリBのappscript.jsonを表示しておく
  4. アプリBのGCPプロジェクトを予め作っておいたGCPプロジェクト側の番号を登録して、紐つける
  5. GCP側では、サイドバーからAPIとサービス -> ライブラリを開く
  6. Apps Script APIを有効化しておく
  7. Githubのコードをコピーして、アプリBのエディタに貼り付ける
  8. 7.の中にあるprojectIdの変数に、1.のスクリプトIDを入れておく
  9. 7.の中にあるdescriptionの変数に、デプロイする版の名前を適当に入れる
  10. アプリBのsaveAndDeployNewVersion関数を実行し、承認をする。
  11. アプリAのウェブアプリケーションが同じURLにて、新しい版でリリースされる。

複数管理するGASのウェブアプリについて、スクリプトIDをアプリBで管理しておけば、リリース日に合わせて、コードを実行する事で一括でデプロイが出来る利点がありますね(トリガーも使えるので、リリース日に自動リリースをすることも可能)

図:GASからデプロイができました

デプロイバージョンの制限

2024年2月29日、Google Apps Scriptのデプロイに関して小さな制限事項が設けられました。それがバージョンを最大200までとする制限。1プロジェクトについて200回デプロイした場合、201回目をデプロイしたい場合には、過去のバージョンをアーカイブするか?削除しなければデプロイできなくなります。

デプロイした過去のバージョンを削除したい場合には

  1. スクリプトエディタを開く
  2. プロジェクト履歴を開く
  3. 対象のバージョンの右にある「︙」をクリックして、このバージョンを削除するをクリック

ライブラリやウェブアプリケーションとしてデプロイしたものが対象になります。200回もデプロイすることはないとは思いますが、長い年月で溜まってしまった場合削除するのも大変な作業なので最新版は10個程度に留めてアーカイブなり削除なりマメにしておきましょう。

図:バージョンを削除しましょう

HTMLダイアログの閉じるを検知できない

Spreadsheet上でHTMLにてダイアログを出した時に、google.script.host.close()や、直接閉じた際に、以下のようなJSのコードでunloadを検知はできても、その後にGAS側のコードを呼び出すことができません

故にユーザには処理の続行を明示的に示すボタンを押して処理を促すか?閉じたことをきっかけに発動するような仕掛けを使わないで実現するか?の2択になりますので要注意。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)