Apps Script API を使用して関数を実行する

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Google Apps Script API には、指定した Apps Script 関数をリモートで実行する scripts.run メソッドが用意されています。このメソッドを呼び出し元アプリケーションで使用すると、スクリプト プロジェクトの 1 つで関数をリモートで実行し、レスポンスを受信できます。

要件

呼び出し元のアプリケーションが scripts.run メソッドを使用するには、次の要件を満たす必要があります。義務事項:

  • スクリプト プロジェクトを API 実行可能ファイルとしてデプロイする。プロジェクトのデプロイ、デプロイ解除、再デプロイは、必要に応じて行うことができます。

  • 適切に実行するために、スコープの OAuth トークンを指定します。 この OAuth トークンは、呼び出された関数で使用されるスコープだけでなく、スクリプトで使用されるすべてのスコープをカバーする必要があります。メソッド リファレンスで承認スコープの完全なリストをご覧ください。

  • スクリプトと呼び出し元アプリケーションの OAuth2 クライアントが、共通の Google Cloud プロジェクトを共有していることを確認します。 Cloud プロジェクトは、標準の Cloud プロジェクトにする必要があります。Apps Script プロジェクト用に作成されるデフォルトのプロジェクトでは不十分です。新しい標準 Cloud プロジェクトを使用することも、既存のプロジェクトを使用することもできます。

  • Cloud プロジェクトで Google Apps Script API を有効にします

scripts.run メソッド

scripts.run メソッドを実行するには、次のキー識別情報が必要です。

開発モードで実行するようにスクリプトを構成することもできます。このモードは、最後にデプロイされたバージョンではなく、最近保存されたスクリプト プロジェクトのバージョンで実行されます。これを行うには、リクエスト本文devMode ブール値を true に設定します。スクリプトのオーナーのみが、開発モードでスクリプトを実行できます。

パラメータのデータ型の処理

通常、Apps Script API の scripts.run メソッドを使用するには、データを関数パラメータとして Apps Script に送信し、関数の戻り値としてデータを返します。API で取得できるのは、文字列、配列、オブジェクト、数値、ブール値などの基本的な型を持つ値のみです。これらは、JavaScript の基本的な型に似ています。より複雑な Apps Script オブジェクト(ドキュメントスプレッドシートなど)は、API によってスクリプト プロジェクトに渡したり、API プロジェクトからスクリプト プロジェクトに渡したりすることはできません。

呼び出し元アプリケーションが Java などの厳密な型の言語で記述されている場合は、パラメータを、これらの基本的な型に対応する汎用オブジェクトのリストまたは配列として渡します。多くの場合、単純な型変換を自動的に適用できます。たとえば、数値パラメータを取る関数は、追加の処理を行わずに、Java の Double オブジェクト、Integer オブジェクト、または Long オブジェクトをパラメータとして与えることができます。

API から関数のレスポンスが返される場合、通常は、使用する前に、返された値を正しい型にキャストする必要があります。Java ベースの例を次に示します。

  • API から Java アプリケーションに返される数値は java.math.BigDecimal オブジェクトとして到着するため、必要に応じて Doubles 型または int 型に変換する必要があります。
  • Apps Script 関数が文字列の配列を返す場合、Java アプリケーションはそのレスポンスを List<String> オブジェクトにキャストします。

    List<String> mylist = (List<String>)(op.getResponse().get("result"));
    
  • Bytes の配列を返す場合は、Apps Script 関数内で配列を base64 文字列としてエンコードし、その文字列を代わりに返すと便利な場合があります。

    return Utilities.base64Encode(myByteArray); // returns a String.
    

次のコードサンプルは、API レスポンスの解釈方法を示しています。

一般的な手順

以下では、Apps Script API を使用して Apps Script の関数を実行する一般的な手順を説明します。

ステップ 1: 共通の Cloud プロジェクトを設定する

スクリプトと呼び出し元アプリケーションの両方が同じ Cloud プロジェクトを共有する必要があります。この Cloud プロジェクトは、既存のプロジェクトか、この目的のために作成された新しいプロジェクトになります。Cloud プロジェクトを作成したら、そのプロジェクトを使用するようにスクリプト プロジェクトを切り替える必要があります。

ステップ 2: スクリプトを API 実行可能ファイルとしてデプロイする

  1. 使用する関数を含む Apps Script プロジェクトを開きます。
  2. 右上の [Deploy] > [New Deployment] をクリックします。
  3. 表示されたダイアログで、[デプロイタイプを有効にする] [API 実行可能] をクリックします。
  4. [アクセスできるユーザー] プルダウン メニューで、Apps Script API を使用してスクリプトの関数を呼び出すことができるユーザーを選択します。
  5. [デプロイ] をクリックします。

ステップ 3: 呼び出し元アプリケーションを構成する

呼び出し元アプリケーションを使用するには、Apps Script API を有効にして、OAuth 認証情報を設定する必要があります。これを行うには、Cloud プロジェクトへのアクセス権が必要です。

  1. 呼び出し元のアプリケーションとスクリプトが使用している Cloud プロジェクトを構成します。これを行うには、次の手順を行います。
    1. Cloud プロジェクトで Apps Script API を有効にします
    2. OAuth 同意画面を構成する
    3. OAuth 認証情報を作成する
  2. スクリプト プロジェクトを開き、左側の [概要] をクリックします。
  3. [プロジェクト OAuth スコープ] で、スクリプトに必要なすべてのスコープを記録します。
  4. 呼び出し元のアプリケーション コードで、API 呼び出し用のスクリプト OAuth アクセス トークンを生成します。これは、API 自体が使用するトークンではなく、実行時にスクリプトが必要とするトークンです。Cloud プロジェクトのクライアント ID と記録されたスクリプト スコープを使用して作成する必要があります。

    Google クライアント ライブラリは、このトークンの構築とアプリケーションの OAuth の処理に大きく役立ちます。通常は、スクリプト スコープを使用して、上位レベルの「認証情報」オブジェクトを構築できます。スコープのリストから認証情報オブジェクトを作成する例については、Apps Script API のクイックスタートをご覧ください。

ステップ 4: script.run リクエストを行う

呼び出し元アプリケーションが構成されたら、scripts.run 呼び出しを行うことができます。各 API 呼び出しは次の手順で構成されます。

  1. スクリプト ID、関数名、必須パラメータを使用して、API リクエストを作成します。
  2. scripts.run 呼び出しを行い、ヘッダーにビルドしたスクリプト OAuth トークンを含める(基本的な POST リクエストを使用している場合)か、スクリプト スコープで作成した認証情報オブジェクトを使用します。
  3. スクリプトの実行を終了します。スクリプトの実行時間は最大で 6 分であるため、それを考慮してアプリケーションで許容される必要があります。
  4. 完了すると、スクリプト関数が値を返すことができます。値がサポートされている型である場合、API はその値をアプリケーションに戻します。

script.run API 呼び出しの例を以下に示します。

API リクエストの例

次の例では、Apps Script API 実行リクエストをさまざまな言語で作成し、Apps Script 関数を呼び出してユーザーのルート ディレクトリ内にあるフォルダのリストを出力します。実行された関数を含む Apps Script プロジェクトのスクリプト ID を、ENTER_YOUR_SCRIPT_ID_HERE で指定された場所で指定する必要があります。これらの例は、それぞれの言語の Google API クライアント ライブラリに依存しています。

ターゲット スクリプト

このスクリプトでは Drive API を使用します。

スクリプトをホストするプロジェクトで Drive API を有効にする必要があります。

また、呼び出し元のアプリケーションは、次のドライブ スコープを含む OAuth 認証情報を送信する必要があります。

  • https://www.googleapis.com/auth/drive

このサンプル アプリケーションでは、Google クライアント ライブラリを使用し、このスコープを使用して OAuth の認証情報オブジェクトを作成します。

/**
 * Return the set of folder names contained in the user's root folder as an
 * object (with folder IDs as keys).
 * @return {Object} A set of folder names keyed by folder ID.
 */
function getFoldersUnderRoot() {
  const root = DriveApp.getRootFolder();
  const folders = root.getFolders();
  const folderSet = {};
  while (folders.hasNext()) {
    const folder = folders.next();
    folderSet[folder.getId()] = folder.getName();
  }
  return folderSet;
}

Java


/**
 * Create a HttpRequestInitializer from the given one, except set
 * the HTTP read timeout to be longer than the default (to allow
 * called scripts time to execute).
 *
 * @param {HttpRequestInitializer} requestInitializer the initializer
 *                                 to copy and adjust; typically a Credential object.
 * @return an initializer with an extended read timeout.
 */
private static HttpRequestInitializer setHttpTimeout(
    final HttpRequestInitializer requestInitializer) {
  return new HttpRequestInitializer() {
    @Override
    public void initialize(HttpRequest httpRequest) throws IOException {
      requestInitializer.initialize(httpRequest);
      // This allows the API to call (and avoid timing out on)
      // functions that take up to 6 minutes to complete (the maximum
      // allowed script run time), plus a little overhead.
      httpRequest.setReadTimeout(380000);
    }
  };
}

/**
 * Build and return an authorized Script client service.
 *
 * @param {Credential} credential an authorized Credential object
 * @return an authorized Script client service
 */
public static Script getScriptService() throws IOException {
  Credential credential = authorize();
  return new Script.Builder(
      HTTP_TRANSPORT, JSON_FACTORY, setHttpTimeout(credential))
      .setApplicationName(APPLICATION_NAME)
      .build();
}

/**
 * Interpret an error response returned by the API and return a String
 * summary.
 *
 * @param {Operation} op the Operation returning an error response
 * @return summary of error response, or null if Operation returned no
 * error
 */
public static String getScriptError(Operation op) {
  if (op.getError() == null) {
    return null;
  }

  // Extract the first (and only) set of error details and cast as a Map.
  // The values of this map are the script's 'errorMessage' and
  // 'errorType', and an array of stack trace elements (which also need to
  // be cast as Maps).
  Map<String, Object> detail = op.getError().getDetails().get(0);
  List<Map<String, Object>> stacktrace =
      (List<Map<String, Object>>) detail.get("scriptStackTraceElements");

  java.lang.StringBuilder sb =
      new StringBuilder("\nScript error message: ");
  sb.append(detail.get("errorMessage"));
  sb.append("\nScript error type: ");
  sb.append(detail.get("errorType"));

  if (stacktrace != null) {
    // There may not be a stacktrace if the script didn't start
    // executing.
    sb.append("\nScript error stacktrace:");
    for (Map<String, Object> elem : stacktrace) {
      sb.append("\n  ");
      sb.append(elem.get("function"));
      sb.append(":");
      sb.append(elem.get("lineNumber"));
    }
  }
  sb.append("\n");
  return sb.toString();
}

public static void main(String[] args) throws IOException {
  // ID of the script to call. Acquire this from the Apps Script editor,
  // under Publish > Deploy as API executable.
  String scriptId = "ENTER_YOUR_SCRIPT_ID_HERE";
  Script service = getScriptService();

  // Create an execution request object.
  ExecutionRequest request = new ExecutionRequest()
      .setFunction("getFoldersUnderRoot");

  try {
    // Make the API request.
    Operation op =
        service.scripts().run(scriptId, request).execute();

    // Print results of request.
    if (op.getError() != null) {
      // The API executed, but the script returned an error.
      System.out.println(getScriptError(op));
    } else {
      // The result provided by the API needs to be cast into
      // the correct type, based upon what types the Apps
      // Script function returns. Here, the function returns
      // an Apps Script Object with String keys and values,
      // so must be cast into a Java Map (folderSet).
      Map<String, String> folderSet =
          (Map<String, String>) (op.getResponse().get("result"));
      if (folderSet.size() == 0) {
        System.out.println("No folders returned!");
      } else {
        System.out.println("Folders under your root folder:");
        for (String id : folderSet.keySet()) {
          System.out.printf(
              "\t%s (%s)\n", folderSet.get(id), id);
        }
      }
    }
  } catch (GoogleJsonResponseException e) {
    // The API encountered a problem before the script was called.
    e.printStackTrace(System.out);
  }
}

JavaScript

/**
 * Load the API and make an API call.  Display the results on the screen.
 */
function callScriptFunction() {
  const scriptId = '<ENTER_YOUR_SCRIPT_ID_HERE>';

  // Call the Apps Script API run method
  //   'scriptId' is the URL parameter that states what script to run
  //   'resource' describes the run request body (with the function name
  //              to execute)
  try {
    gapi.client.script.scripts.run({
      'scriptId': scriptId,
      'resource': {
        'function': 'getFoldersUnderRoot',
      },
    }).then(function(resp) {
      const result = resp.result;
      if (result.error && result.error.status) {
        // The API encountered a problem before the script
        // started executing.
        appendPre('Error calling API:');
        appendPre(JSON.stringify(result, null, 2));
      } else if (result.error) {
        // The API executed, but the script returned an error.

        // Extract the first (and only) set of error details.
        // The values of this object are the script's 'errorMessage' and
        // 'errorType', and an array of stack trace elements.
        const error = result.error.details[0];
        appendPre('Script error message: ' + error.errorMessage);

        if (error.scriptStackTraceElements) {
          // There may not be a stacktrace if the script didn't start
          // executing.
          appendPre('Script error stacktrace:');
          for (let i = 0; i < error.scriptStackTraceElements.length; i++) {
            const trace = error.scriptStackTraceElements[i];
            appendPre('\t' + trace.function + ':' + trace.lineNumber);
          }
        }
      } else {
        // The structure of the result will depend upon what the Apps
        // Script function returns. Here, the function returns an Apps
        // Script Object with String keys and values, and so the result
        // is treated as a JavaScript object (folderSet).

        const folderSet = result.response.result;
        if (Object.keys(folderSet).length == 0) {
          appendPre('No folders returned!');
        } else {
          appendPre('Folders under your root folder:');
          Object.keys(folderSet).forEach(function(id) {
            appendPre('\t' + folderSet[id] + ' (' + id + ')');
          });
        }
      }
    });
  } catch (err) {
    document.getElementById('content').innerText = err.message;
    return;
  }
}

Node.js

/**
 * Call an Apps Script function to list the folders in the user's root Drive
 * folder.
 *
 */
async function callAppsScript() {
  const scriptId = '1xGOh6wCm7hlIVSVPKm0y_dL-YqetspS5DEVmMzaxd_6AAvI-_u8DSgBT';

  const {GoogleAuth} = require('google-auth-library');
  const {google} = require('googleapis');

  // Get credentials and build service
  // TODO (developer) - Use appropriate auth mechanism for your app
  const auth = new GoogleAuth({
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const script = google.script({version: 'v1', auth});

  try {
    // Make the API request. The request object is included here as 'resource'.
    const resp = await script.scripts.run({
      auth: auth,
      resource: {
        function: 'getFoldersUnderRoot',
      },
      scriptId: scriptId,
    });
    if (resp.error) {
      // The API executed, but the script returned an error.

      // Extract the first (and only) set of error details. The values of this
      // object are the script's 'errorMessage' and 'errorType', and an array
      // of stack trace elements.
      const error = resp.error.details[0];
      console.log('Script error message: ' + error.errorMessage);
      console.log('Script error stacktrace:');

      if (error.scriptStackTraceElements) {
        // There may not be a stacktrace if the script didn't start executing.
        for (let i = 0; i < error.scriptStackTraceElements.length; i++) {
          const trace = error.scriptStackTraceElements[i];
          console.log('\t%s: %s', trace.function, trace.lineNumber);
        }
      }
    } else {
      // The structure of the result will depend upon what the Apps Script
      // function returns. Here, the function returns an Apps Script Object
      // with String keys and values, and so the result is treated as a
      // Node.js object (folderSet).
      const folderSet = resp.response.result;
      if (Object.keys(folderSet).length == 0) {
        console.log('No folders returned!');
      } else {
        console.log('Folders under your root folder:');
        Object.keys(folderSet).forEach(function(id) {
          console.log('\t%s (%s)', folderSet[id], id);
        });
      }
    }
  } catch (err) {
    // TODO(developer) - Handle error
    throw err;
  }
}

Python

from __future__ import print_function

import google.auth
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError


def main():
    """Runs the sample.
    """
    # pylint: disable=maybe-no-member
    script_id = '1VFBDoJFy6yb9z7-luOwRv3fCmeNOzILPnR4QVmR0bGJ7gQ3QMPpCW-yt'

    creds, _ = google.auth.default()
    service = build('script', 'v1', credentials=creds)

    # Create an execution request object.
    request = {"function": "getFoldersUnderRoot"}

    try:
        # Make the API request.
        response = service.scripts().run(scriptId=script_id,
                                         body=request).execute()
        if 'error' in response:
            # The API executed, but the script returned an error.
            # Extract the first (and only) set of error details. The values of
            # this object are the script's 'errorMessage' and 'errorType', and
            # a list of stack trace elements.
            error = response['error']['details'][0]
            print(f"Script error message: {0}.{format(error['errorMessage'])}")

            if 'scriptStackTraceElements' in error:
                # There may not be a stacktrace if the script didn't start
                # executing.
                print("Script error stacktrace:")
                for trace in error['scriptStackTraceElements']:
                    print(f"\t{0}: {1}."
                          f"{format(trace['function'], trace['lineNumber'])}")
        else:
            # The structure of the result depends upon what the Apps Script
            # function returns. Here, the function returns an Apps Script
            # Object with String keys and values, and so the result is
            # treated as a Python dictionary (folder_set).
            folder_set = response['response'].get('result', {})
            if not folder_set:
                print('No folders returned!')
            else:
                print('Folders under your root folder:')
                for (folder_id, folder) in folder_set.items():
                    print(f"\t{0} ({1}).{format(folder, folder_id)}")

    except HttpError as error:
        # The API encountered a problem before the script started executing.
        print(f"An error occurred: {error}")
        print(error.content)


if __name__ == '__main__':
    main()

制限事項

Apps Script API には次のような制限があります。

  1. 共通の Cloud プロジェクト。呼び出されるスクリプトと呼び出し元のアプリケーションは、Cloud プロジェクトを共有する必要があります。Cloud プロジェクトは、標準の Cloud プロジェクトである必要があります。Apps Script プロジェクト用に作成されたデフォルトのプロジェクトでは不十分です。標準の Cloud プロジェクトは、新しいプロジェクトか既存のプロジェクトのいずれかです。

  2. 基本パラメータと戻り値の型。API は、Apps Script 固有のオブジェクト(ドキュメントBLOBカレンダードライブ ファイルなど)をアプリケーションに渡したり、返したりすることはできません。文字列、配列、オブジェクト、数値、ブール値などの基本的な型のみを渡して返すことができます。

  3. OAuth スコープ。API は、少なくとも 1 つの必要なスコープを持つスクリプトのみを実行できます。つまり、API を使用して、1 つ以上のサービスの承認を必要としないスクリプトを呼び出すことはできません。

  4. トリガーなし。この API は Apps Script のトリガーを作成できません。