Выполнение функций с помощью Apps Script API

Я пытаюсь выполнить функцию скрипта приложений, используя API скриптов приложений. Для этого я настроил целевой скрипт и скрипт вызова (код JavaScript) по инструкции от Google здесь.

Я точно следовал тому, как это описано, но я получаю следующие ошибки.

  1. Ошибка при вызове скрипта:

    ReferenceError: gapi не определен

  2. Ошибка в целевом скрипте при ручном запуске функции "getFoldersUnderRoot()"

    Исключение: К сожалению, произошла ошибка сервера. Пожалуйста, подождите немного и повторите попытку.

функция «getFoldersUnderRoot()» работала правильно перед подключением целевого скрипта к проекту GCP.

Любая помощь приветствуется, чтобы указать, что я делаю неправильно.

Пожалуйста, предоставьте минимальный воспроизводимый пример

Cooper 27.12.2020 02:45

Это два не связанных между собой вопроса. В будущем было бы лучше, если бы вы открыли для них разные вопросы. Начнем со второго, потому что без него первый все равно не будет работать. Создайте новый скрипт с одной функцией, в которой будет только Logger.log('Hello, world!'). Назначьте его своему проекту Google Cloud. Можете ли вы запустить его вручную из скрипта Google Apps?

Martí 28.12.2020 10:55
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
2
1 100
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я понял, как выполнять функции App Script с помощью Apps Script API. Поэтому я отправляю ответ на благо других. Также я бы попытался добавить недостающую информацию, которую Google не предоставил в своих инструкциях.

  1. Целевой сценарий — это файл сценария приложения (например, «code.gs») с необходимыми функциями для выполнения. Этот скрипт должен быть присоединен к проекту GCP с включенным App Script API.

  2. Сценарий вызова должен быть файлом html, сохраненным в локальной папке, а не файлом сценария приложения. Ниже приведен пример «index.html», который вызывает две функции «callScriptFunction()» и «getSheets()».

     <!DOCTYPE html>
     <html>
     <head>
        <title>Google Apps Script API Quickstart</title>
        <meta charset = "utf-8" />
      </head>
      <body>
       <p>Google Apps Script API Quickstart</p>
    
     <!--Add buttons to initiate auth sequence and sign out-->
     <button id = "authorize_button" style = "display: none;">Authorize</button>
     <button id = "signout_button" style = "display: none;">Sign Out</button>
    
     <pre id = "content" style = "white-space: pre-wrap;"></pre>
    
     <script type = "text/javascript">
       // Client ID and API key from the Developer Console
       var CLIENT_ID = 'YOUR_CLIENT_ID';
       var API_KEY = 'YOUR_API_KEY';
    
       // Array of API discovery doc URLs for APIs used by the quickstart
       var DISCOVERY_DOCS = ["https://script.googleapis.com/$discovery/rest?version=v1"];
    
       // Authorization scopes required by the API; multiple scopes can be
       // included, separated by spaces.
       var SCOPES = 'https://www.googleapis.com/auth/spreadsheets https://www.googleapis.com/auth/drive.readonly';
    
       var authorizeButton = document.getElementById('authorize_button');
       var signoutButton = document.getElementById('signout_button');
    
       /**
        *  On load, called to load the auth2 library and API client library.
        */
       function handleClientLoad() {
         gapi.load('client:auth2', initClient);
       }
    
       /**
        *  Initializes the API client library and sets up sign-in state
        *  listeners.
        */
       function initClient() {
         gapi.client.init({
           apiKey: API_KEY,
           clientId: CLIENT_ID,
           discoveryDocs: DISCOVERY_DOCS,
           scope: SCOPES
         }).then(function () {
           // Listen for sign-in state changes.
           gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
    
           // Handle the initial sign-in state.
           updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
           authorizeButton.onclick = handleAuthClick;
           signoutButton.onclick = handleSignoutClick;
         }, function(error) {
           appendPre(JSON.stringify(error, null, 2));
         });
       }
    
       /**
        *  Called when the signed in status changes, to update the UI
        *  appropriately. After a sign-in, the API is called.
        */
       function updateSigninStatus(isSignedIn) {
         if (isSignedIn) {
           authorizeButton.style.display = 'none';
           signoutButton.style.display = 'block';
        //   callScriptFunction();
           getSheets();
         } else {
           authorizeButton.style.display = 'block';
           signoutButton.style.display = 'none';
         }
       }
    
       /**
        *  Sign in the user upon button click.
        */
       function handleAuthClick(event) {
         gapi.auth2.getAuthInstance().signIn();
       }
    
       /**
        *  Sign out the user upon button click.
        */
       function handleSignoutClick(event) {
         gapi.auth2.getAuthInstance().signOut();
       }
    
       /**
        * Append a pre element to the body containing the given message
        * as its text node. Used to display the results of the API call.
        *
        * @param {string} message Text to be placed in pre element.
        */
       function appendPre(message) {
         var pre = document.getElementById('content');
         var textContent = document.createTextNode(message + '\n');
         pre.appendChild(textContent);
       }
    
       /**
        * Shows basic usage of the Apps Script API.
        *
        * Call the Apps Script API to create a new script project, upload files
        * to the project, and log the script's URL to the user.
        */
       function callScriptFunction() {
      var scriptId = "TARGET_SCRIPT_ID";
    
     // 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)
     gapi.client.script.scripts.run({
     'scriptId': scriptId,
     'resource': {
       'function': 'getFoldersUnderRoot',
       'devMode': true
        }
     }).then(function(resp) {
       var 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.
       var 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 (var i = 0; i < error.scriptStackTraceElements.length; i++) {
           var 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).
    
       var 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  + ')');
         });
         }
       }
     });
     }
    
    
    
      function getSheets() {
    
       // ID of the script to call. Acquire this from the Apps Script editor,
       // under Publish > Deploy as API executable.
       var scriptId = "TARGET_SCRIPT_ID";
    
      // Initialize parameters for function call.
      var sheetId = "SPREADSHEET_ID";
    
    
     gapi.client.script.scripts.run({
       'scriptId': scriptId,
       'resource': {
       'function': 'getSheetNames',
       'parameters': [sheetId],
       'devMode': true
     }
     }).then(function(resp) {
     var 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.
       var 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 (var i = 0; i < error.scriptStackTraceElements.length; i++) {
           var 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).
    
       var names = result.response.result;
       if (Object.keys(names).length == 0) {
           appendPre('No sheetnames returned!');
       } else {
         appendPre(names);
       }
      }
     });  
    }
    
     </script>
    
     <script async defer src = "https://apis.google.com/js/api.js"
       onload = "this.onload=function(){};handleClientLoad()"
       onreadystatechange = "if (this.readyState === 'complete') this.onload()">
     </script>
      </body>
     </html>
    
  3. Ниже приведен пример целевого скрипта.

      function getFoldersUnderRoot() {
       var root = DriveApp.getRootFolder();
       var folders = root.getFolders();
       var folderSet = {};
       while (folders.hasNext()) {
         var folder = folders.next();
         folderSet[folder.getId()] = folder.getName();
       }
       return folderSet;
      }
    
      function getSheetNames(sheetId) {
           var ss = SpreadsheetApp.openById(sheetId);
           var sheets = ss.getSheets();
           var names = sheets.map(function(sheet) {
             return sheet.getName();
           })
           return names;
         }
    
  4. Из терминала перейдите в рабочий каталог и выполните python3 -m http.server 8000. Откройте браузер и загрузите «http://localhost:8000/». Авторизуйтесь и продолжайте.

  5. Вам нужно внести в белый список «http://localhost:8000/» в учетных данных проекта.

  6. Вам необходимо добавить необходимые области на экране согласия OAuth проекта.

Я могу выполнить функцию «getSheetNames ()», но «getFoldersUnderRoot ()» выдает ошибку: Exception: We're sorry, a server error occurred. Please wait a bit and try again. Выполнение из редактора скриптов также дает ту же ошибку. Однако «getFoldersUnderRoot()» может выполняться на любом другом скрипте, который не подключен к проекту GCP.

Включили ли вы API Google Диска в консоли Google Cloud Platform для своего проекта?

Martí 29.12.2020 13:23

Спасибо за предложение. Действительно, включение Google Drive API решило проблему. Но почему это не написано в инструкции, предоставленной Google? Пункт 3 шага говорит только о включении Script API.

Partha S. Pal 29.12.2020 14:20

Другие вопросы по теме