问题描述
我正在尝试使用 Apps Script API 执行 Apps Script 功能。为此,我使用 Google here 的说明设置了目标脚本和调用脚本(JavaScript 代码)。
我完全按照描述的方式进行操作,但出现以下错误。
函数“getFoldersUnderRoot()”在将目标脚本连接到 GCP 项目之前运行正常。
感谢任何帮助指出我做错了什么。
解决方法
我想出了如何使用 Apps Script API 执行 App Script 函数。所以我发布答案是为了他人的利益。此外,我会尝试插入 Google 未在其说明中提供的缺失信息。
-
目标脚本是一个应用程序脚本(例如“code.gs”)文件,其中包含要执行的所需功能。此脚本必须附加到启用了 App Script API 的 GCP 项目。
-
调用脚本必须是保存在本地文件夹中的 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>
-
下面是一个目标脚本的例子。
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; }
-
从终端切换到工作目录并执行python3 -m http.server 8000。打开浏览器并加载“http://localhost:8000/”。授权并继续。
-
您需要在项目凭据中将“http://localhost:8000/”列入白名单
-
您需要在项目的 OAuth 同意屏幕中添加所需的范围。
我能够执行函数“getSheetNames()”但“getFoldersUnderRoot()”抛出错误:Exception: We're sorry,a server error occurred. Please wait a bit and try again.
从脚本编辑器执行也给出同样的错误。但是,“getFoldersUnderRoot()”可在未附加到 GCP 项目的任何其他脚本上执行