移転しました。

約3秒後に自動的にリダイレクトします。

社内向けWebアプリのフロントをスプレッドシート+GASで作る

こんにちは。Webエンジニアの @umanoda です。 今年のブラックフライデーfitbit versa 2 を買いました。使い勝手よくて気に入っているのですが、手首と電極が触れているところがカブれてしまいました……。惜しい。

さて最近、社内向けのWebアプリをGASで作成しました。 弊社では G suite を利用して社内アカウントを管理しています。GASを利用することで、Webアプリで必要になるユーザー認証周りの処理をまるっと省略できるのが便利だと感じたので紹介したいと思います。

実現したいこと

  • G suiteに登録されている社内ユーザーにWebアプリでサービスを提供したい
  • 誰が実行者かを判別したい
  • 実行者にファイルをダウンロードさせたい

実現方法

共有ドライブに置いたスプレッドシートとGASをフロントとして利用しました。

見た目にはこだわらない、動けばいいやのショボアプリです。ざっくりと以下のような仕組みで動きます

f:id:umanoda:20191223190746p:plain

  • フォームっぽいものをセルで表現する
  • ボタンっぽいものを図形の挿入で表現する
  • ボタンにスクリプトを割り当て、クリックしたときにGASが実行されるようにする
  • GAS内部でセルの情報を読み取り、バックエンドのAPIにリクエストを投げる。バックエンドはGoogle Cloud Functionsを用意
  • ファイルをダウンロードするために、モーダルダイアログを開く

今回、スプレッドシート+GASをつかってファイルをダウンロードするアプリで、こういう工夫をしたというところを二点紹介します。 なお、GASの一般的な使い方の説明は今回しません。

GASでのユーザー認証

社内システムではあるのですが、特定の担当者にだけ利用させたいというニーズがあり、実行ユーザーの制限を必要がありました。 GASの Session.getActiveUser()) によって操作しているG suiteにログインしているEmailを取得することができるので、これを利用します。

実行できるユーザーを限定する場合、「シートの保護」機能をつかって一部のユーザーにしか変更できないシートを用意すると便利です

// GAS: Code.js
function main() {
  if (!_validateAccount()) {
    SpreadsheetApp.getUi().alert("実行権限がありません");
  }

  // 続きの処理。APIリクエスト組み立てなど
}

function _validateAccount() {
  var currentAccount = Session.getActiveUser();

  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getSheetByName("管理者用");
  var range = sheet.getRange(1, 2, sheet.getLastRow(), 1); // 「管理者用」シートのB1から最後まで取得
  var rangeValues = range.getValues();

  for (var idx=0; idx < rangeValues.length; idx++) {
    let row = range_values[idx][0];
    if (row == currentAccount) {
      return true;
    }
    return false;
  }
}

注意点としては、GASはスプレッドシートの編集権限があれば変更することができます。実行ユーザーの権限チェックをコメントアウトするなどされると、このシートにアクセスできる人ならば誰でもチェックをすり抜けることができてしまいます。 厳密にやるなら、APIリクエストのサーバーでも権限チェックが必要でしょう。あくまでここでは、「余計なリクエストが走らないためのチェック」「誰が担当者として指名されているのかの明示化」程度のもの割り切りきっています。

ファイルのダウンロード

GASの処理はサーバー側で完結してしまうため、ユーザーにファイルをダウンロードさせるには一工夫が必要です。 テンプレートファイルを用意することで、レンダー結果がiframeに描画されたモーダルダイアログをスプレッドシート上に表示することが出来ます。

Ref. HtmlService | G Suite Developer

// GASでテンプレートを呼び出す

// hoge.html をテンプレートとして使う
var template = HtmlService.createTemplateFromFile("hoge");

// 変数に値やメソッドを設定
template.huga = "aiueo"
template.piyo = function () { alert("kakikukeko") }
テンプレート側での呼び出し
<div>
  <?= huga ?> // 値の埋め込み
  <? piyo() ?> // メソッドの実行
</div>

これを利用して、以下のようにするとGASで生成したURLからファイルをダウンロードすることが出来ます。

<!-- GAS: index.html -->
<html>
<body>
  <p>ダウンロード中です</p>
  <a id="download" href="<?= url ?>" target="_blank">ダウンロード</a>

<script>
  var a = document.getElementById("download");
  a.click();
</script>
</body>
</html>
// GAS: Code.js
var template = HtmlService.createTemplateFromFile("index");
template.url = downloadUrl;
SpreadsheetApp.getUi().showModalDialog(html.evaluate(), "Dialogタイトル");

コード管理

GAS上で使えるエディタだと、最低限の構文チェックのみしかされません。ESLintなどを活用するため、ローカルでコーディングして clasp を使ってデプロイするようにしています。

Typescriptで書いたコードをclaspでデプロイすると、自動的にトランスコンパイルしてサーバー上に配置してくれるのがとても便利です。

まとめ

社内向けのちょっとしたサービスを作る際のTipsとして、スプレッドシート+GASでのフロントエンド構築について書きました。

G suite管理下で公開範囲を限定したサービスを手軽に作れるのが最も大きな利点です。その他、スプレッドシートの入力規則を利用して手軽に入力フォームを作成できるのも良い点かなと思います。

欠点としては、スプレッドシート上が自由に編集できてしまうため、容易にアプリケーションを壊せてしまいます。防ぐためには「シートの保護」機能を使って編集できるセルを限定するなどの工夫が必要となります。

公開範囲をカスタマイズしたり、もっとリッチなGUIを提供するのならば、Firebaseを使うのも良いでしょうね。