Exécuter des fonctions avec l'API Apps Script

L'API Google Apps Script fournit une méthode scripts.run qui exécute à distance une fonction Apps Script spécifiée. Vous pouvez utiliser cette méthode dans une application appelante pour exécuter à distance une fonction dans l'un de vos projets de script et recevoir une réponse.

Conditions requises

Vous devez satisfaire aux exigences suivantes pour qu'une application appelante puisse utiliser la méthode scripts.run. Vous devez :

  • Déployez le projet de script en tant qu'exécutable d'API. Vous pouvez déployer, annuler et redéployer des projets selon vos besoins.

  • Fournissez un jeton OAuth correctement défini pour l'exécution. Ce jeton OAuth doit couvrir tous les champs d'application utilisés par le script, et pas seulement ceux utilisés par la fonction appelée. Consultez la liste complète des niveaux d'autorisation dans la documentation de référence sur la méthode.

  • Assurez-vous que le script et le client OAuth2 de l'application appelante partagent un projet Google Cloud commun. Le projet Cloud doit être un projet Cloud standard. Les projets par défaut créés pour les projets Apps Script ne sont pas suffisants. Vous pouvez utiliser un nouveau projet Cloud standard ou un projet existant.

  • Activez l'API Google Apps Script dans le projet Cloud.

La méthode scripts.run

La méthode scripts.run nécessite des informations d'identification clés pour s'exécuter:

Vous pouvez éventuellement configurer votre script pour qu'il s'exécute en mode développement. Ce mode s'exécute avec la dernière version enregistrée du projet de script plutôt qu'avec la dernière version déployée. Pour ce faire, définissez la valeur booléenne devMode dans le corps de la requête sur true. Seul le propriétaire du script peut l'exécuter en mode Développement.

Gérer les types de données de paramètre

L'utilisation de la méthode scripts.run de l'API Apps Script implique généralement d'envoyer des données à Apps Script en tant que paramètres de fonction et de récupérer des données sous forme de valeurs renvoyées par la fonction. L'API ne peut accepter et renvoyer que des valeurs avec des types de base: chaînes, tableaux, objets, nombres et booléens. Ceux-ci sont semblables aux types de base en JavaScript. Les objets Apps Script plus complexes tels que Document ou Sheet ne peuvent pas être transmis depuis ou vers le projet de script par l'API.

Lorsque votre application appelante est écrite dans un langage à type fort tel que Java, elle transmet les paramètres sous la forme d'une liste ou d'un tableau d'objets génériques correspondant à ces types de base. Dans de nombreux cas, vous pouvez appliquer automatiquement des conversions de types simples. Par exemple, une fonction qui accepte un paramètre numérique peut se voir attribuer un objet Java Double, Integer ou Long comme paramètre, sans manipulation supplémentaire.

Lorsque l'API renvoie la réponse de la fonction, vous devez souvent caster la valeur renvoyée vers le type approprié avant de pouvoir l'utiliser. Voici quelques exemples basés sur Java:

  • Les numéros renvoyés par l'API à une application Java arrivent sous forme d'objets java.math.BigDecimal. Vous devrez peut-être les convertir en types Doubles ou int, selon les besoins.
  • Si la fonction Apps Script renvoie un tableau de chaînes, une application Java caste la réponse dans un objet List<String>:

    List<String> mylist = (List<String>)(op.getResponse().get("result"));
    
  • Si vous souhaitez renvoyer un tableau de Bytes, il peut être utile d'encoder le tableau en tant que chaîne base64 dans la fonction Apps Script et de renvoyer cette chaîne à la place:

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

Les exemples de code ci-dessous montrent comment interpréter la réponse de l'API.

Procédure générale

Voici la procédure générale à suivre pour exécuter des fonctions Apps Script à l'aide de l'API Apps Script:

Étape 1: Configurez le projet Cloud commun

Votre script et l'application appelante doivent partager le même projet Cloud. Il peut s'agir d'un projet existant ou d'un nouveau projet créé à cette fin. Une fois que vous avez créé un projet Cloud, vous devez changer de projet de script pour l'utiliser.

Étape 2: Déployez le script en tant qu'exécutable d'API

  1. Ouvrez le projet Apps Script contenant les fonctions que vous souhaitez utiliser.
  2. En haut à droite, cliquez sur Déployer > Nouveau déploiement.
  3. Dans la boîte de dialogue qui s'ouvre, cliquez sur Activer les types de déploiement > API Executable.
  4. Dans le menu déroulant "Qui a accès", sélectionnez les utilisateurs autorisés à appeler les fonctions du script à l'aide de l'API Apps Script.
  5. Cliquez sur Déployer.

Étape 3: Configurez l'application appelante

L'application appelante doit activer l'API Apps Script et établir des identifiants OAuth avant de pouvoir l'utiliser. Pour ce faire, vous devez avoir accès au projet Cloud.

  1. Configurez le projet Cloud que votre application appelante et votre script utilisent. Pour ce faire, procédez comme suit :
    1. Activez l'API Apps Script dans le projet Cloud.
    2. Configurer l'écran de consentement OAuth
    3. Créer des identifiants OAuth
  2. Ouvrez le projet de script et, à gauche, cliquez sur Overview (Aperçu) .
  3. Sous Champs d'application OAuth du projet, enregistrez tous les champs d'application requis par le script.
  4. Dans le code de l'application appelante, générez un jeton d'accès OAuth de script pour l'appel d'API. Il ne s'agit pas d'un jeton utilisé par l'API elle-même, mais d'un jeton requis par le script lors de l'exécution. Il doit être créé à l'aide de l'ID client du projet Cloud et des champs d'application de script que vous avez enregistrés.

    Les bibliothèques clientes Google peuvent grandement vous aider à créer ce jeton et à gérer OAuth pour l'application. Elles vous permettent généralement de créer un objet "identifiants" de niveau supérieur à l'aide des champs d'application du script. Consultez les guides de démarrage rapide de l'API Apps Script pour obtenir des exemples de création d'un objet d'identifiants à partir d'une liste de champs d'application.

Étape 4: Envoyez la requête script.run

Une fois l'application appelante configurée, vous pouvez effectuer des appels scripts.run. Chaque appel d'API comprend les étapes suivantes:

  1. Créez une requête API à l'aide de l'ID de script, du nom de la fonction et de tous les paramètres requis.
  2. Effectuez l'appel scripts.run et incluez le jeton OAuth de script que vous avez créé dans l'en-tête (si vous utilisez une requête POST de base) ou utilisez un objet d'identifiants que vous avez créé avec les champs d'application du script.
  3. Autorisez l'exécution du script. L'exécution des scripts peut prendre jusqu'à six minutes. Votre application doit donc l'autoriser.
  4. Une fois l'opération terminée, la fonction de script peut renvoyer une valeur, que l'API renvoie à l'application si la valeur est un type compatible.

Vous trouverez des exemples d'appels d'API script.run ci-dessous.

Exemples de requêtes API

Les exemples suivants montrent comment effectuer une requête d'exécution d'API Apps Script dans différentes langues, en appelant une fonction Apps Script pour afficher une liste de dossiers dans le répertoire racine de l'utilisateur. L'ID de script du projet Apps Script contenant la fonction exécutée doit être spécifié avec ENTER_YOUR_SCRIPT_ID_HERE. Les exemples s'appuient sur les bibliothèques clientes des API Google dans leur langage respectif.

Script cible

La fonction de ce script utilise l'API Drive.

Vous devez activer l'API Drive dans le projet hébergeant le script.

De plus, les applications appelantes doivent envoyer des identifiants OAuth incluant le champ d'application Drive suivant:

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

Les exemples d'applications présentés ici utilisent les bibliothèques clientes Google afin de créer des objets d'identification pour OAuth à l'aide de ce champ d'application.

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

Limites

L'API Apps Script présente plusieurs limites:

  1. Un projet Cloud commun. Le script appelé et l'application appelante doivent partager un projet Cloud. Le projet Cloud doit être un projet Cloud standard. Les projets par défaut créés pour les projets Apps Script ne sont pas suffisants. Le projet Cloud standard peut être un nouveau projet ou un projet existant.

  2. Types de paramètres et de renvois de base. L'API ne peut pas transmettre ni renvoyer à l'application des objets spécifiques à Apps Script (tels que Documents, Blobs, Agendas, Fichiers Drive, etc.). Seuls les types de base tels que les chaînes, les tableaux, les objets, les nombres et les valeurs booléennes peuvent être transmis et renvoyés.

  3. Champs d'application OAuth L'API ne peut exécuter que des scripts ayant au moins un champ d'application requis. Cela signifie que vous ne pouvez pas utiliser l'API pour appeler un script qui ne nécessite pas l'autorisation d'un ou de plusieurs services.

  4. Aucun déclencheur.L'API ne peut pas créer de déclencheurs Apps Script.