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 répondre 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 le déploiement et redéployer des projets si nécessaire.

  • Fournissez un jeton OAuth dont le champ d'application est correct 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 champs d'application d'autorisation dans la documentation de référence de 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 de clé 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 version enregistrée la plus récente du projet de script plutôt qu'avec la version la plus récemment 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 des paramètres

L'utilisation de la méthode scripts.run de l'API Apps Script implique généralement l'envoi de données à Apps Script en tant que paramètres de fonction et la récupération de données sous forme de valeurs de retour de fonction. L'API ne peut prendre et renvoyer des valeurs qu'avec des types de base: chaînes, tableaux, objets, nombres et booléens. Ils sont semblables aux types de base en JavaScript. L'API ne peut pas transmettre d'objets Apps Script plus complexes tels que Document ou Feuille au projet de script.

Lorsque l'application appelante est écrite dans un langage fortement typé 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 simples. Par exemple, une fonction qui accepte un paramètre numérique peut recevoir un objet Java Double, Integer ou Long en tant que paramètre sans traitement supplémentaire.

Lorsque l'API renvoie la réponse de la fonction, vous devez souvent convertir la valeur renvoyée dans 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 la forme d'objets java.math.BigDecimal et doivent être convertis en types Doubles ou int si nécessaire.
  • Si la fonction Apps Script renvoie un tableau de chaînes, une application Java convertit la réponse en un objet List<String>:

    List<String> mylist = (List<String>)(op.getResponse().get("result"));
    
  • Si vous souhaitez renvoyer un tableau de Bytes, il peut être pratique 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 illustrent différentes manières d'interpréter la réponse de l'API.

Procédure générale

Voici la procédure générale permettant d'utiliser l'API Apps Script pour exécuter des fonctions 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 projet créé à cette fin. Une fois que vous avez 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 exécutable.
  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: Configurer l'application appelante

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

  1. Configurez le projet Cloud utilisé par votre application appelante et votre script. Pour ce faire, procédez comme suit :
    1. Activez l'API Apps Script dans le projet Cloud.
    2. Configurez l'écran de consentement OAuth.
    3. Créer des identifiants OAuth
  2. Ouvrez le projet de script et, à gauche, cliquez sur Présentation .
  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 que l'API elle-même utilise, mais plutôt 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. En général, elles vous permettent de créer un objet "identifiants" de niveau supérieur à l'aide des champs d'application des scripts. Consultez les guides de démarrage rapide de l'API Apps Script pour obtenir des exemples de création d'un objet 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 des 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 identifiants que vous avez créé avec les champs d'application du script.
  3. Attendez la fin de l'exécution du script. Les scripts peuvent prendre jusqu'à six minutes d'exécution. Votre application doit donc le permettre.
  4. À l'issue de l'opération, la fonction de script peut renvoyer une valeur, que l'API renvoie à l'application si le type de valeur est compatible.

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

Exemples de requêtes API

Les exemples suivants montrent comment envoyer une requête d'exécution de l'API Apps Script dans plusieurs langages, 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é (indiqué par ENTER_YOUR_SCRIPT_ID_HERE). Les exemples s'appuient sur les bibliothèques clientes des API Google pour leurs langages respectifs.

Script cible

Dans ce script, la fonction 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 qui incluent 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 pour 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 d'objets spécifiques à Apps Script (tels que Documents, Blobs, Agendas, Fichiers Drive, etc.) à l'application. 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.