Google Apps Scriptを色々なアプリから実行する【GAS】

Google Apps Scriptにて2015年11月に新機能として実装された「Apps Script API」というものがあります(以前は、Google Apps Script Execution APIと呼ばれていました)。これは、Google Apps Scriptで用意してある関数を外部の環境(Javaや.NET、Node.jsなど)から実行できるようにするというものです。別途Google APIの認証用の手続きが必要ですが、この機能を利用するとG Suiteの利便性と利用範囲が格段に向上します。

  1. Googleアカウントを持つものであれば、認証をしてそのGoogle Apps Scriptの関数を直接実行する事が出来る
  2. ローカル環境(例えばイントラネット)に用意したHTML内でGoogle Apps Scriptの実行環境を利用してアプリケーションを作れる
  3. Javaや.NET、VBA(Access VBAとの連携事例を参考)やNode.jsといった言語で使用する事が出来る。
  4. アクセス権限を極特定の関数に絞る事が出来る
  5. iOSやAndroidのアプリからももちろん実行する事が可能になる。
  6. doGet()やdoPost()で同じような事が出来ましたが、1プロジェクト内で1個しか用意出来ない。Execution APIはダイレクトに関数を叩けるので、その制限がない
  7. ただし、doGetのようにURLにつなげてパラメータを渡すわけではないので、基本POSTで送信する
  8. 通常、doGetのようなものを使って、REST APIを作った場合、実行後にURLがリダイレクトされて結果が出力されます。しかし、Apps Script APIは他のWeb API同様URLは固定なので、リダイレクトされると困るケースには重宝します(例えば、Amazon Echoから使うような場合)

準備するもの、使用するメソッド

事前準備

事前にスプレッドシートのメニューにある「セットアップ」内にある「初期化」を実行しておいて下さい。この作業により、スプレッドシートのIDがスクリプトプロパティに格納されます。この作業はデータの塊を返す関数内でopenByIdにてスプレッドシートを指定してる為で、getActiveSpreadsheetでは指定が出来ない為です。意外と嵌まりやすいポイントですので注意して下さい。

概要

Apps Script APIを利用できるようにする為には事前準備が必要です。今回のサンプルは特定のスプレッドシートのシートにあるデータの塊を取得して、HTML側にテーブルとして表示するというサンプルです。必要なものは

  1. Cloud ConsoleにてOAuth2.0クライアントIDを作成する
  2. Google Apps Script側で用意したデータの塊を返す関数
  3. 今回使用するスプレッドシートを実行可能APIとして導入した際に発行される現在のAPI ID
  4. スプレッドシートのスコープを取得する

デスクトップに置いたHTMLをそのまま実行では取得が出来ないので、別途Webサーバやアプリが必要です。自分は仮想環境内にUbuntu Linuxを入れ、手軽にWebサーバを作れるxamppを導入して環境を用意しました。WindowsでもMac OS Xでもxamppを利用できますので、通常はお使いのPCにxamppをインストールしてサーバを実行するだけでOKです。

プロジェクトを移動

今回の発表直前の2019年4月8日より、Google Apps ScriptからCloud Platform Projectへ直接アクセスが出来なくなりました。これまでにデプロイしてるものについては、これまで通り「リソース」⇒「Google Cloud Platform API ダッシュボード」からアクセスが可能です。

今回の変更はスプレッドシート上で動かすスクリプトやGoogleの拡張サービスを利用しないタイプのスクリプトであれば特に問題はありませんが、「Apps Script API」や「Google Picker API」、「Cloud SQL接続」などGCP上のAPIを利用する場合には以下の手順を踏んで、Google Apps Scriptにプロジェクトを連結する必要があります。これまでは、自動的にGCP上にGoogle Apps Script用のプロジェクトが生成されていたのですが、今後は自分の組織(もしくはGCPプロジェクト)上で作成されたプロジェクトでなければならないということです。詳細はこちらのページを見てください。

連結する手順は以下の通り

  1. Google Cloud Consoleを開く
  2. 左上にある▼をクリックする
  3. ダイアログが出てくるので、新規プロジェクトを作るか?既存のプロジェクトを選択する。この時、G Suiteであれば選択元は「自分のドメイン」を選択する必要があります。
  4. プロジェクト情報パネルから「プロジェクト番号」をコピーする
  5. 対象のGoogle Apps Scriptのスクリプトエディタを開く
  6. 「リソース」⇒「Cloud Platform プロジェクト」を開く
  7. 4.で入手した番号をプロジェクトを変更のテキストボックスに入れて、プロジェクトを設定ボタンをクリックする
  8. 無事に移動が完了すればメッセージが表示されます。
  9. この時、元の自動作成されたプロジェクトはシャットダウンされて消えます。これで設定完了です。

今回のこの変更だと1つ作ったプロジェクトに集約する必要があるので、クォータについてプロジェクト毎のカウントだったので問題なかったものが、集約されることで、クォータに引っ掛かる可能性があります。

図:プロジェクト番号をコピーしておきます

図:プロジェクトを他のプロジェクトに紐付けしました。

図:GCPの拡張サービスを使うには手順が必要になった

Developer ConsoleにてOAuthクライアントIDを取得する

今回使用するスプレッドシートをコピーした後、まずはスクリプトエディタの画面に入ります。その後以下の手順でクライアントIDを取得します。

  1. メニューより「リソース」⇒「Googleの拡張サービス」を開きます。
  2. 出た画面の下にある「 Google Cloud Platform API ダッシュボード」をクリックします。
  3. 上記の▼をクリックして、選択元は自分のドメインを選び、新しいプロジェクトをクリックします。
  4. APIライブラリを開き、「Apps Script API」を検索して、有効にします。
  5. 次に認証情報を開き、「認証情報を作成」をクリックし、「必要な認証情報」を選択します。APIを呼び出す場所は「ウェブブラウザ」でOKです。
  6. 再度、認証情報をクリックし、認証情報を作成⇒OAuthクライアントIDを選択します。
  7. アプリケーションの種類は「ウェブアプリケーション」を選択し、作成します。
  8. 承認済みの JavaScript 生成元には、http://localhostと入力すればOKです。
  9. 承認済みのリダイレクト URIは、空っぽのままでOKです。
  10. これで作成すると、クライアントIDクライアントシークレットが発行されますので、コピーして置きます。
  11. 次にOAuth同意画面というタブをクリックします。この画面は新規でプロジェクトを作成した場合には、利用範囲を内部に制限することが可能です。GASから作った場合には、少し画面が異なるので注意。今回はプロジェクトをつくった場合として進めます。
  12. アプリケーションの種類は「内部」にし、ドメイン内のユーザに限定します。
  13. アプリケーションのロゴはGoogleの認証画面に出てくるアイコンです。指定がなくても問題ありません。
  14. Google APIのスコープには今回追加で、../auth/spreadsheetsを選び追加します。
  15. 認証ドメインは自分のドメインのWebサーバからのみ呼び出せるようにする場合に使います。今回は空で良いです。保存をクリック。

図:Apps Script APIを有効にする

図:認証情報を追加する

図:OAuthクライアントを作成する

図:クライアントIDとシークレットが作成される

実行可能APIとして導入する

クライアントIDが取得できたら、まずは関数を用意しておきます。実行可能APIとして導入するという機能は、ウェブアプリケーションとして導入と同じく、ソースコードを変更する度に発行し直さないと行けないので注意が必要です。ソースコードを変更後に再度導入をせずに実行しても、以前のソースコードのままで返ってくるので嵌るポイントです。今回のスプレッドシートは、イチゴのデータベースを用意しそのデータの塊を取得してそのまま帰すberryget()という関数を用意しました。

実行可能APIとして導入する手順は以下の通りです。

  1. スクリプトエディタのメニューより、「公開」⇒「実行可能APIとして導入」を実行します
  2. 適当にバージョンの説明を入力して、配置ボタンを押し、続行すると現在の API IDが取得できます。アクセスできるユーザは全員で良いでしょう。

これだけです。

図:配置ボタンを押すと完了

スコープを取得する

スコープとは、アクセスするGoogleのサービスを差し、アクセスを許可するサービスを限定する事が出来ます。今回はスプレッドシートと一部のサービスのみですので、例えばGoogle Driveなどはhttps://www.googleapis.com/auth/driveとなっています。今回のスクリプトでのスコープは以下の手順で取得できます。

  1. どれでも良いのでとりあえず、1つ関数を実行しておき、認証を済ませておく。
  2. スクリプトエディタのメニューより「ファイル」⇒「プロジェクトのプロパティ」を開く
  3. 出てきた画面の「スコープ」を開く
  4. 出てきたスコープをメモしておく

HTMLファイルにID類を記述する

ここまで取得してきた各種ID等をHTMLファイルに記述します。

  1. CLIENT_IDにクライアントIDを記述します。シングルコーテーションで括るのを忘れずに。
  2. SCOPESに取得したスコープを記述します。配列の形で記述するので、複数指定が可能です。今回のケースで言えば、['https://www.googleapis.com/auth/script.storage','https://www.googleapis.com/auth/spreadsheets'];といった形で記述をします。
  3. scriptIdに現在の API IDで取得したIDを記述します。

GAS側の実行する関数を記述する

最後にGAS側で用意した関数を記述します。HTMLファイルの中盤にあるrequestがソレですが、関数名を記述します。今回のケースで言えば、'function': 'berryget'と記述します。しかし、関数に引数を渡したいこともあるでしょう。其の場合には、追加でparametersという項目を追加して、配列の形で引数を記述すると関数に引数として渡せます。具体的には以下のような形になります。

GAS側コード

function onOpen() {
    var ui = SpreadsheetApp.getUi();
    ui.createMenu('▶セットアップ')
    .addItem('初期化', 'getMySheetId')
    .addToUi();
}
 
//自分自身のIDを取得するコード
function getMySheetId(){
  var sheet = SpreadsheetApp.getActiveSpreadsheet();
  var myid = sheet.getId();
 
  var Properties = PropertiesService.getScriptProperties();
  Properties.setProperty("mysheetid", myid);
  
  return myid;
}
 
//イチゴデータベースを取得して返す
function berryget() {
  var Properties = PropertiesService.getScriptProperties();
  var sheetid = Properties.getProperty("mysheetid");
  
  var sheet = SpreadsheetApp.openById(sheetid);
  var ss = sheet.getSheetByName("berrymaster").getRange("A2:E").getValues();
  
  return ss;
}

HTML側コード

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <script type="text/javascript">
      //Developer Consoleで設定したクライアントIDを入力
      var CLIENT_ID = 'ここにクライアントIDを記述する';
      //必要としてるスコープ(カンマ区切りで複数設定)
      var SCOPES = ['ここにスコープを記述する'];

      //認証を実行するルーチン
      function checkAuth() {
        gapi.auth.authorize(
          {
            'client_id': CLIENT_ID,
            'scope': SCOPES.join(' '),
            'immediate': true
          }, handleAuthResult);
      }

      function handleAuthResult(authResult) {
        var authorizeDiv = document.getElementById('authorize-div');
        if (authResult && !authResult.error) {
          authorizeDiv.style.display = 'none';
          callScriptFunction();
        } else {
          authorizeDiv.style.display = 'inline';
        }
      }

      function handleAuthClick(event) {
        gapi.auth.authorize(
          {client_id: CLIENT_ID, scope: SCOPES, immediate: false},
          handleAuthResult);
        return false;
      }

      //Execution APIを実行してスクリプト側の関数を叩くルーチン
      function callScriptFunction() {
        //現在のAPI IDを入力する
        var scriptId = "ここに現在のAPI IDを記述する";

        // スクリプト側の関数(引数を必要とする場合は、'parameters': [sheetId]といった具合に渡せる)
        var request = {
            'function': 'ここにGAS側関数名を記述する'
        };

        // APIリクエストを組み立てる
        var op = gapi.client.request({
            'root': 'https://script.googleapis.com',
            'path': 'v1/scripts/' + scriptId + ':run',
            'method': 'POST',
            'body': request
        });

        //リクエストの実行と結果の受け取り
        op.execute(function(resp) {
          if (resp.error && resp.error.status) {
            //APIの実行に失敗した場合の処理
            appenddiv('APIの呼び出し失敗:');
            appenddiv(JSON.stringify(resp, null, 2));
          } else if (resp.error) {
            //APIの実行結果、エラーが発生した場合の処理
            var error = resp.error.details[0];
            appenddiv('スクリプトエラーメッセージ: ' + error.errorMessage);

            if (error.scriptStackTraceElements) {
              appenddiv('スクリプトエラー:');
              for (var i = 0; i < error.scriptStackTraceElements.length; i++) {
                var trace = error.scriptStackTraceElements[i];
                appenddiv('\t' + trace.function + ':' + trace.lineNumber);
              }
            }
          } else {
            //APIの実行に成功し、帰ってきた値を処理するルーチン
            var data = resp.response.result;
            var length = data.length;
            var clength = data[0].length;

            if (length == 0) {
                appenddiv('データがないよ');
            } else {
              appenddiv('<p><b>見つかったデータの詳細</b></p>');
              var html = "<table border='1'>";

              for(var i = 0;i<length;i++){
                html += "<tr>";
                for(var j = 0;j<clength;j++){
                  html += "<td>" + data[i][j] + "</td>";
                }
                html += "</tr>";
              }

              html += "</table>";
              appenddiv(html);
            }
          }
        });
      }

      //idがoutputのタグに結果を書き込む関数
      function appenddiv(message) {
        var pre = document.getElementById('output');
        pre.innerHTML = pre.innerHTML + message;
      }

    </script>
    <script src="https://apis.google.com/js/client.js?onload=checkAuth">
    </script>
  </head>
  <body>
    <div id="authorize-div" style="display: none">
      <span>Apps Script APIを実行する</span>
      <button id="authorize-button" onclick="handleAuthClick(event)" class="action">
        認証実行
      </button>
    </div>
    <div id="output"></div>
  </body>
</html>

Webサーバ上に配置して実行する

作成が完了したHTMLファイルをWebサーバに配置します。Ubuntu Linuxにインストールしたxamppでは、/opt/lampp/htdocs以下が配置場所になりますので、ここにtestというフォルダを自分は作りました。testフォルダ内にstart.htmlを配置して、Webサーバを起動します。xamppの場合の起動コマンドは、

コメントを残す

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

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