2022.07.07

GASで書いた関数を外部から叩いてみる

こんにちは。グループ研究開発本部の H.U と申します。

以前当ブログにて、Google Apps Scriptをローカル環境で開発するTipsについて紹介させていただいました。

この度、あるプロジェクトでGoogle Apps Script(以下、GAS)を使用する機会がありました。またその際に、GASの関数を外部から実行するという要件も付随しておりました。

本記事ではその際にどのようなことをやったのかをご紹介いたします。

(なお、GASのプログラムについての記載は本記事のスコープ外となりますのでご了承ください。またスクリーンショットなどは記事公開当時のものとなります)

今回やること

GASはGoogleの開発するプログラミング言語で、Google Workspaceのさまざまなソリューションを連携させるのに便利な機能を提供してくれています。

今回はこのGASで作成した関数を、外部すなわちGoogle Workspaceでないところ(e.g. ローカルPCで動かすCLIのスクリプト, CI/CDパイプライン etc…)から実行します。

また、その際にトリガーは使用せず、任意のタイミングで実行できるようにします。

このようなケースを考えてみましょう(なお詳細はプロジェクトの内容とも関わるため、ここで記載しているのは架空のケースです)。

・社内システムとして、データ可視化ツールがある

・可視化ツールは、特定のフォルダにあるCSVファイルをデータとして都度読み込む

・アンケートをGoogle Formで取っており、回答データはGoogle Workspaceにある

・データのCSVの生成から特定のフォルダへのダウンロードまでを自動化したい

・このうちデータのCSVの生成にGASを利用する

GASの関数を外部から実行する

GASの関数を外部から実行するためには「ウェブアプリ」として公開する方法と、「実行可能API」として公開する方法の主に二つがあります。

このうち今回のケースでは「実行可能API」として公開する方法を選択しました。

「実行可能API」を選択することによるメリットは下記の2点です。
  1. 特定の人にのみ実行させられるようにできる(※)
  2. doGet, doPost以外の関数も実行できる
まず 1 についてですが、「実行可能API」では実行可能な人を、同じ組織のGoogleアカウントを持つ人や自分のみに限定できます。

もし全員がAPIを実行できた場合、どうなるでしょう。個人利用で、何も重要なデータがない場合にはそれでも問題ないでしょう。しかし、Google Workspace上に重要なデータがあった場合、データを取得するコードになっていなくても、その状態はあまり好ましくありません。また上記のケースで考えてみても、もしかすると、誰もが勝手にCSVファイルを作成できる、例えばIoT機器だと誰でも勝手に米を炊けるような状態になってしまいます。

それを防ぐため、実行可能な人を絞れる機能は重要と言えます。

※これは一応「ウェブアプリ」でもできるのですが、curlなどで実行させたい場合、トークンの取得が必要です。このトークンの取得はGASのエディタからすることになるので面倒です。実行可能ユーザを絞りAPIとしてアプリに組み込んで利用したい場合は、「実行可能API」の方が良いのではないかと思います。

次に 2 についてですが、「ウェブアプリ」として公開する際は、GETリクエストはdoGet、POSTリクエストはdoPost関数を実行するというルールが課せられます。「実行可能API」では関数名は任意となるので、自由度は「実行可能API」の方が高いです。

実行可能APIの設定

では、実際に実行可能APIとして公開するためにはどのような手順を踏まえる必要があるのでしょうか。

必要なのは、大きく分けて以下の4ステップです。
  1. GCPプロジェクトの作成
  2. GCPプロジェクトとの紐付け
  3. 認証情報を作成する
  4. 実行可能APIとしてデプロイ

(1)GCPプロジェクトの作成

Google Cloud Platform(GCP)のプロジェクトを作成します。

これは後述する手順にて、GASのプロジェクトとプロジェクトと紐付けるです。

そのため、GCPのプロジェクトを作成する組織と、GASのプロジェクトの属する組織は同一である必要があります。

GCPのコンソールにログインします。

プロジェクトを作成します。プロジェクト名は任意です。

作成後、ダッシュボードから「プロジェクト番号」をコピーします。

プロジェクト番号
 

(2)GCPプロジェクトとの紐付け

コピーしてきた「プロジェクト番号」を使い、GASのプロジェクトとGCPのプロジェクトを紐付けます。

紐付けはGASのエディターの「設定」より行えます。

「Google Cloud Platform(GCP)プロジェクト」の項目の「プロジェクトを変更」ボタンをクリックします。

すると下図のような入力欄が表示されるので、(1)で作成したプロジェクトのプロジェクト番号を入力し、「プロジェクトを設定」をクリックします。

問題なく設定が完了し、プロジェクト番号が表示されれば成功です。

GAS設定画面
 

(3)認証情報を作成する

GCPのコンソール上で、認証情報を作成します。

「APIとサービス」より、「Google Apps Script API」を有効化します。

その後、同項目より「同意画面」を作成し「認証情報」を作成します。

認証情報は「OAuthクライアントID」を選択します。

詳しい手順は、下記のURLをご参照ください。

https://support.google.com/workspacemigrate/answer/9222992?hl=ja

認証情報_OAuthクライアントID
 

(4)実行可能APIとしてデプロイ

GASのエディターより「デプロイ」ボタンをクリックし、「新しいデプロイ」を選択します。

新しいデプロイ
左上の「歯車」マークより、「実行可能API」を選択します。

アクセスできるユーザーは、任意のものを選んでください。

今回は、同じ組織の人のみ実行できるような設定をします。

新しいデプロイ_実行可能API
「デプロイ」することで、実行可能なAPIとなります。

GASの関数を実行する

では、実際に関数を実行してみましょう。

GASの関数の実行には、scripts.runのAPIを使用します。

URLは、「https://script.googleapis.com/v1/scripts/{scriptId}:run」(※)となります。

※scriptIdには、GASエディターの「設定」で確認できるスクリプトIDを入れる旨がドキュメントには記載されていますが、デプロイ時に表示されるURLに含まれるIDを使用しないと動かないケースもありますので、ご注意ください

 

作成したクライアントIDを使用してトークンを取得し、それをヘッダーに含める形でリクエストします。

実行する関数は、body にて指定できます。また、パラメータ引数を渡すことも可能です。

これらを踏まえ、ローカルからNode.jsでGASを動かすサンプルは下記のようになります。

 
const axios = require('axios');

const CLIENT_ID = ***
const CLIENT_SECRET = ***
const REFRESH_TOKEN = ***
const SCRIPT_ID = ***

/**
 * メイン関数
 * @return string
 */
async function main() {
  try {
    // アクセストークンを取得する
    const accessToken = await getAccessToken();

    // GASを実行
    const url = `https://script.googleapis.com/v1/scripts/${SCRIPT_ID}:run`;
    const headers = {
      Authorization: `Bearer ${accessToken}`,
    };
    const payload = {
      function: 'myFunction',
      parameters: ['Hello, world!']
    };
    // リクエスト
    const instance = axios.create({ headers });
    const response = await instance.post(url, payload);

    if (response.status !== 200 || !response.data || response.data.error) {
        throw new Error('Failed to run google apps script.');
    }

    const { result } = response.data.response;
    console.log(result);
  } catch (e) {
    throw e;
  }
}

/**
 * アクセストークンを取得する関数
 * @return string
 */
async function getAccessToken() {
  const payload = {
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    refresh_token: REFRESH_TOKEN,
    grant_type: 'refresh_token',
  };
  // リクエスト
  const response = await axios.post(TOKEN_URL, payload);

  if (response.status !== 200 || !response.data) {
    throw new Error('Failed to get access token.');
  }

  const accessToken = response.data.access_token;
  return accessToken;
}

main();
 

さいごに

今回は、GASの関数を外部から実行するためにすることと、その実践をやってみました。

自動化ツールとして便利なGASの可能性が、ご一読いただいた皆様の中で広がれば幸いに存じます。

 

グループ研究開発本部では、グループ全体のインテグレーションを支援してくれるアーキテクトを募集しています。インフラ設計、構築経験者の方、グループ研究開発本部にご興味を持って頂ける方がいらっしゃいましたら、ぜひ募集職種一覧からご応募をお願いします。

皆さんのご応募をお待ちしています。

 

参考

Manage scripts programmatically with the REST API

Executing Functions using the Apps Script API

  • Twitter
  • Facebook
  • はてなブックマークに追加

グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。

 
  • AI研究開発室
  • 大阪研究開発グループ

関連記事