Retty Tech Blog

実名口コミグルメサービスRettyのエンジニアによるTech Blogです。プロダクト開発にまつわるナレッジをアウトプットして、世の中がHappyになっていくようなコンテンツを発信します。

toB開発でのGoogle Apps Scriptを使用した開発について

この記事は、Retty Advent Calendar 2021 Part2の23日目の記事です!

Part1:

adventar.org

Part2:

adventar.org

はじめに

RettyでtoB(飲食店)向けのWebエンジニアをしている渡辺です。
ビール好きですが、友人の結婚式に行くために久々にスーツに袖を通したところ、太りすぎて着れなくなっていたので最近はハイボールや焼酎を飲むことが多くなりました。

toB開発では、一部Google Apps Scriptを使用しています。
チームで、スプレッドシートを使った複雑な集計処理の自動化・整理をするために、Google Apps Scriptの改修を行いました。今回は、その改修でおきた問題とその解消方法を紹介していきます。

Google Apps Scriptとは

f:id:rettydev:20211221163618j:plain

Google Apps Script is a rapid application development platform that makes it fast and easy to create business applications that integrate with Google Workspace. You write code in modern JavaScript and have access to built-in libraries for favorite Google Workspace applications like Gmail, Calendar, Drive, and more. There's nothing to install—we give you a code editor right in your browser, and your scripts run on Google's servers.

developers.google.com

Google Workspaceと統合したビジネスアプリケーションを簡単に作成することができるアプリケーション開発プラットフォームです。JavaScriptでコードを書くことができ、Gmail、カレンダー、ドライブなどGoogleが提供しているサービスを操作するライブラリも用意されてます。 ただ、JavaScriptをベースにしているようですが、JavaScriptの全ての組み込み関数を使えるわけではないです。

問題1:複数のスプレッドシートに紐づくGoogle Apps Scriptが存在しており、処理が重複していた

データの種類によってスプレッドシートが管理されていたため、同じような処理が複数のGoogle Apps Scriptに存在していました。このままでは、保守性が低いため共通ができないか考えて以下の解決方法を対応しました。

解決方法:共通処理を独自のライブラリとして定義する

以下のように、共通化したい処理を.gsファイル単位で実装して、ライブラリ用のGoogle Apps Scriptを作成します。

f:id:rettydev:20211219013835p:plain

ライブラリの閲覧や編集の権限の設定を右上のアイコンから必要に応じて、特定のアカウントにのみ付与します。公開範囲を間違えると不正に編集されたりする可能性があるので注意してください。 f:id:rettydev:20211219015615p:plain

他のGoogle Apps Scriptから上記の関数を呼び出すためには、サイドメニューの「ライブラリ」の「+」ボタンを押して上記のGoogle Apps Scriptをライブラリとして追加します。 f:id:rettydev:20211219011958p:plain

「+」ボタンを押すとライブラリ追加のモーダルが出るので追加したいスクリプトIDを入力して検索します。 f:id:rettydev:20211219012348p:plain

スクリプトIDはURLの script.google.com/home/projects//editの間の文字列になります。 f:id:rettydev:20211219012318p:plain

スクリプトIDを入力して検索すると追加したいライブラリが表示されますので「追加」ボタンを押します。 f:id:rettydev:20211219013431p:plain

追加するとサイドメニューの「ライブラリ」に追加した「TestLibrary」が表示されます。 ライブラリ名.関数名のように記述すると、TestLibraryの関数が使えます。 f:id:rettydev:20211219013934p:plain

以上で、共通処理を独自のライブラリとして定義する事ができました。

FYI:自作のライブラリを公開する方法【Google Apps Script / GAS】 | すずきライフ

問題2:Google Apps Scriptの実行時間の制限により、大量のデータを処理できない

Google Apps Scriptは、プランによって実行時間が制限されます。
詳しくは以下をご確認ください。
Quotas for Google Services  |  Apps Script  |  Google Developers
取得するデータ量によっては、処理が完了しないまま終了してしまいます。 そのため、以下の解決方法で対応しました。

解決方法:実行時間の制限をトリガーとPropertyService使って回避

トリガーは特定のイベントが発生したときに、AppsScriptで関数を自動的に実行できるものです。 developers.google.com

PropertyServiceは、key-valueでデータを保存できるサービスです。

developers.google.com

この2つを使用して、ループ処理の途中でトリガーを発行し、途中から処理を再開させる方法を紹介したいと思います。 テスト用に以下のようなコードを用意しました。 処理の内容としては、test1の文字列を10回表示させるものです。 1ループごとに1分sleepさせて5分すぎたらトリガーをセットして、中断したループカウントから再開します。

function myFunction() {
  const startTime = new Date();
  const testMessage = TestLibrary.getTestMessage1();
  const counterPropertyKey = 'myFunctionLoopCounterKey';
  // 実行するユーザーごとのpropertyを取得してユーザーごとに処理を完結させる。
  const userProperties = PropertiesService.getUserProperties();

  if (userProperties.getProperty(counterPropertyKey)) {
    var loopCounter = parseInt(userProperties.getProperty(counterPropertyKey));
  } else {
    var loopCounter = 0;
  }
  
  while (loopCounter < 10) {
    diff = parseInt((new Date() - startTime) / (60 * 1000));
    // 5分をすぎたらTriggerの登録を実行
    if (diff >= 5) {
      //トリガーを発行
      setTrigger('myFunction');
      userProperties.setProperty(counterPropertyKey, loopCounter);
      return;
    }
    const count = loopCounter + 1
    console.log(testMessage + ' ループカウント:' + count)
    loopCounter++;
    // 60秒sleepさせる
    Utilities.sleep(60 * 1000);
  }
  // メッセージを表示し終えたらpropertyを削除する
  userProperties.deleteProperty(counterPropertyKey);
}

/**
 * トリガーを発行
 * @param string funcName  トリガーで実行させる関数名
 */
function setTrigger(funcName){
  //2分後に再実行
  const dt = new Date();
  dt.setMinutes(dt.getMinutes() + 2);
  ScriptApp.newTrigger(funcName).timeBased().at(dt).create().getUniqueId();
}

結果の画像

トリガーは以下のように設定されます。 f:id:rettydev:20211221163027p:plain

2分後に中断したループカウントから再開してます。 f:id:rettydev:20211221162419p:plain

終わりに

私は、今回始めてGoogle Apps Scriptを使用しましたが、JavaScriptのように記述できて、Googleが提供しているサービスと連携しやすく便利だなと感じました。ただ、JavaScriptとは一部書き方が違う部分や使えない書き方もあったので戸惑いました。 今回紹介できなかった部分もありますが、また知見が溜まったらTechBlogなどで更新していこうと思います。