使用 Apps Script API 执行函数

问题描述

我正在尝试使用 Apps Script API 执行 Apps Script 功能。为此,我使用 Google here 的说明设置了目标脚本和调用脚本(JavaScript 代码)。

我完全按照描述的方式进行操作,但出现以下错误

  1. 调用脚本时出错:

    ReferenceError: gapi 未定义

  2. 手动运行函数“getFoldersUnderRoot()”时目标脚本出错

    异常:很抱歉,发生服务器错误。请稍等,然后重试。

函数“getFoldersUnderRoot()”在将目标脚本连接到 GCP 项目之前运行正常。

感谢任何帮助指出我做错了什么。

解决方法

我想出了如何使用 Apps Script API 执行 App Script 函数。所以我发布答案是为了他人的利益。此外,我会尝试插入 Google 未在其说明中提供的缺失信息。

  1. 目标脚本是一个应用程序脚本(例如“code.gs”)文件,其中包含要执行的所需功能。此脚本必须附加到启用了 App Script API 的 GCP 项目。

  2. 调用脚本必须是保存在本地文件夹中的 html 文件,而不是 App 脚本文件。下面是一个例子“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,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,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 项目的任何其他脚本上执行

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...