Wykonywanie funkcji przy użyciu interfejsu Apps Script API

Interfejs Google Apps Script API zapewnia Metoda scripts.run który zdalnie wykonuje określoną funkcję Apps Script. Możesz użyć tej metody w aplikacji wywołującej w celu uruchomienia funkcji w jednym z projektów skryptów zdalnie i otrzymać odpowiedź.

Wymagania

Aby aplikacja wywołująca mogła używać tych funkcji, musisz spełnić te wymagania scripts.run . Musisz:

  • Wdróż projekt skryptu jako plik wykonywalny interfejsu API. Dostępne opcje odpowiednio wdrażaj, wycofuj i ponownie wdrażaj projekty.

  • Udostępnij token OAuth o prawidłowym zakresie na potrzeby wykonania. Ten token OAuth musi obejmować wszystkie zakresy używane przez skrypt, a nie tylko te używane przez wywoływaną funkcję. Zobacz pełna lista zakresów autoryzacji .

  • Upewnij się, że skrypt i protokół OAuth2 aplikacji wywołującej klient korzysta ze wspólnego projektu Google Cloud. Projekt Cloud musi być standardowym projektem Cloud. Projekty domyślne utworzone na potrzeby projektów Apps Script są niewystarczające. Możesz użyć nowego standardowego lub istniejącego projektu Cloud.

  • Włączanie interfejsu Google Apps Script API w projekcie Cloud.

Metoda scripts.run

scripts.run wymaga do działania informacji identyfikujących klucz:

Opcjonalnie możesz skonfigurować skrypt do wykonywania w trybie programowania. Ten tryb jest wykonywany z ostatnio zapisaną wersją projektu skryptu a nie tylko ostatnio wdrożonej wersji. Aby to zrobić, ustaw parametr Wartość logiczna devMode w treść żądania do true. Tylko właściciel skryptu może wykonać go w trybie programisty.

Obsługa typów danych parametrów

Korzystanie z interfejsu Apps Script API Metoda scripts.run zwykle wymaga wysłania danych do Apps Script jako parametrów funkcji oraz na pobieranie danych jako wartości zwracanych przez funkcję. Interfejs API może pobierać i zwracać tylko wartości z typami podstawowymi: ciągi tekstowe, tablice, obiekty, liczby i wartości logiczne. Te są podobne do podstawowych typów kodu JavaScript. Bardziej złożone obiekty Apps Script, takie jak Document lub Arkusz nie można przekazać do lub z projektu skryptu przy użyciu interfejsu API.

Gdy aplikacja do wykonywania połączeń jest napisana w języku, np. Java, przekazuje w parametrach jako listę lub tablica obiektów ogólnych. odpowiadające tym podstawowym typom. W wielu przypadkach można zastosować proste tego typu konwersje. Na przykład funkcja, która przyjmuje liczbę może mieć obiekt Java Double, Integer lub Long jako bez dodatkowej obsługi.

Gdy interfejs API zwraca odpowiedź funkcji, często trzeba rzutować zwróciła wartość prawidłowego typu, zanim będzie można jej użyć. Oto kilka Przykłady w Javie:

  • Liczby zwracane przez interfejs API do aplikacji Java mają postać java.math.BigDecimal obiektów (może być konieczne przekonwertowanie na Doubles lub int typów, w zależności od potrzeb.
  • Jeśli funkcja Apps Script zwraca tablicę ciągów znaków, aplikacja w Javie przesyła odpowiedź do obiektu List<String>:

    List<String> mylist = (List<String>)(op.getResponse().get("result"));
    
  • Jeśli chcesz zwrócić tablicę z elementami Bytes, może Ci się to przydać do zakodowania tablicy jako ciągu base64 w funkcji Apps Script, zwróci ten ciąg znaków:

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

Przykładowe fragmenty kodu poniżej pokazują, jak zinterpretowanie odpowiedzi interfejsu API.

Procedura ogólna

Poniżej opisano ogólną procedurę korzystania z interfejsu Apps Script API do uruchomienia funkcji Apps Script:

Krok 1. Skonfiguruj wspólny projekt Cloud

Skrypt i aplikacja wywołująca muszą mieć takie same dane Projekt w chmurze. Ten projekt Cloud może być istniejącym projektem lub nowy projekt utworzony w tym celu. Mając projekt w chmurze, musisz przełączyć projekt skryptu, aby go używać.

Krok 2. Wdróż skrypt jako wykonywalny interfejs API

  1. Otwórz projekt Apps Script z funkcjami, których chcesz używać.
  2. W prawym górnym rogu kliknij Wdróż &gt; Nowe wdrożenie.
  3. W otwartym oknie dialogowym kliknij Włącz typy wdrożeń . &gt; Plik wykonywalny interfejsu API.
  4. W sekcji „Kto ma dostęp” wybierz użytkowników, którzy mogą wywoływać funkcje skryptu przy użyciu interfejsu Apps Script API.
  5. Kliknij Wdróż.

Krok 3. Skonfiguruj aplikację do połączeń

Aplikacja wywołująca musi włączyć interfejs Apps Script API i ustanowić protokół OAuth , zanim będzie można go użyć. Musisz mieć dostęp do projektu Cloud w tym celu.

  1. Skonfiguruj projekt Cloud, którego używa aplikacja i skrypt wywołujący. Aby to zrobić:
    1. Włącz interfejs Apps Script API w projekcie Cloud.
    2. Skonfiguruj ekran zgody OAuth
    3. Utwórz dane logowania OAuth.
  2. Otwórz projekt skryptu i po lewej stronie kliknij Przegląd .
  3. W sekcji Zakresy protokołu OAuth projektu zapisz wszystkie zakresy, które wymaga skryptu.
  4. W kodzie aplikacji wywołującej wygeneruj token dostępu OAuth skryptu dla wywołania interfejsu API. Nie jest to token używany przez interfejs API, lecz jeden wymaga wykonania skryptu. Powinien być utworzony za pomocą Identyfikator klienta projektu Cloud i zarejestrowane zakresy skryptów.

    Biblioteki klienta Google pomaga w tworzeniu tego tokena i obsłudze protokołu OAuth dla aplikacji zwykle umożliwiające utworzenie „danych logowania wyższego poziomu” obiekt za pomocą zakresów skryptów. Zobacz Przykłady znajdziesz w krótkich wprowadzeniach do interfejsu Apps Script API. tworzenia obiektu danych logowania z listy zakresów.

Krok 4. Wyślij żądanie script.run

Po skonfigurowaniu aplikacji do nawiązywania połączeń możesz scripts.run połączeń. Każdy interfejs API składa się z następujących kroków:

  1. utworzyć żądanie interfejsu API, używając identyfikatora skryptu, nazwy funkcji i innych wymaganych wartości .
  2. Niech scripts.run i dołącz skrypt OAuth utworzony nagłówka (w przypadku podstawowego żądania POST) albo obiektu danych logowania. utworzone za pomocą zakresów skryptów.
  3. Zezwól na zakończenie wykonywania skryptu. Skrypty mogą zajmować do trwa sześć minut, więc aplikacja powinna na to pozwolić.
  4. Po zakończeniu funkcja skryptu może zwrócić wartość, której interfejs API jest zwracany do aplikacji, jeśli wartość jest obsługiwanego typu.

Zobacz przykłady wywołań interfejsu API script.run poniżej.

Przykłady żądań do interfejsu API

W przykładach poniżej pokazujemy, jak utworzyć żądanie wykonania interfejsu Apps Script API w różnych językach, wywołując funkcję Apps Script, aby wyświetlić listę folderu głównego użytkownika. Identyfikator skryptu w projekcie Apps Script , które zawierają wykonywaną funkcję, należy określić tam, gdzie wskazano ENTER_YOUR_SCRIPT_ID_HERE Przykłady opierają się na Biblioteki klienta interfejsów API Google dla odpowiednich języki.

Skrypt docelowy

Funkcja w tym skrypcie używa interfejsu Drive API.

Musisz włączyć interfejs Drive API w projektu hostującego skrypt.

Dodatkowo wywołujące aplikacje muszą wysyłać dane uwierzytelniające OAuth zawierające do tego zakresu Dysku:

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

W przykładowych aplikacjach tutaj wykorzystywane są biblioteki klienta Google obiektów danych logowania na potrzeby protokołu OAuth przy użyciu tego zakresu.

/**
 * 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

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}.{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()

Ograniczenia

Interfejs Apps Script API ma kilka ograniczeń:

  1. Wspólny projekt Cloud. Wywoływany skrypt i parametr aplikacja wywołująca musi mieć wspólny projekt Cloud. Projekt Cloud musi być standardowy projekt Cloud; Projekty domyślne utworzone na potrzeby projektów Apps Script są niewystarczające. standardowy projekt Cloud może być nowym lub istniejącym projektem.

  2. Podstawowe parametry i typy zwracanych parametrów. Interfejs API nie może przekazywać ani zwracać obiekty specyficzne dla Apps Script (takie jak Dokumenty, Obiekty blob, Kalendarze, Pliki na Dysku itp.) do aplikacji. Tylko podstawowe typy, takie jak ciągi tekstowe, tablice, obiekty, liczby i mogą być przekazywane i zwracane.

  3. Zakresy protokołu OAuth. Interfejs API może uruchamiać tylko skrypty mające co najmniej 1 wymagany zakres. Oznacza to, że nie można używać interfejsu API do wywoływania skryptu które nie wymagają autoryzacji co najmniej jednej usługi.

  4. Brak wyzwalaczy. Interfejs API nie może tworzyć Apps Script. reguł.