使用 Apps 脚本执行函数 API
Executing Functions using the Apps Script API
我正在尝试使用 Apps 脚本执行 Apps 脚本功能 API。为此,我使用 Google here.
中的说明设置了目标脚本和调用脚本(JavaScript 代码)
我完全按照描述的方式操作,但出现以下错误。
调用脚本时出错:
ReferenceError: gapi 未定义
手动 运行 函数“getFoldersUnderRoot()”时目标脚本出错
异常:很抱歉,发生服务器错误。请稍后重试。
函数“getFoldersUnderRoot()”在将目标脚本连接到 GCP 项目之前 运行 正确。
感谢任何帮助指出我做错了什么。
我想出了如何使用 Apps 脚本执行 App 脚本功能 API。所以我发布答案是为了其他人的利益。此外,我会尝试插入 Google 在他们的说明中未提供的缺失信息。
目标脚本是一个应用程序脚本(例如“code.gs”)文件,其中包含要执行的所需功能。此脚本必须附加到启用了 App 脚本 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, 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>
下面是目标脚本的例子。
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 Consent Screen 中添加所需的范围。
我可以执行“getSheetNames()”函数,但“getFoldersUnderRoot()”会抛出错误:Exception: We're sorry, a server error occurred. Please wait a bit and try again.
从脚本编辑器执行也会出现同样的错误。但是,“getFoldersUnderRoot()”可在未附加到 GCP 项目的任何其他脚本上执行
我正在尝试使用 Apps 脚本执行 Apps 脚本功能 API。为此,我使用 Google here.
中的说明设置了目标脚本和调用脚本(JavaScript 代码)我完全按照描述的方式操作,但出现以下错误。
调用脚本时出错:
ReferenceError: gapi 未定义
手动 运行 函数“getFoldersUnderRoot()”时目标脚本出错
异常:很抱歉,发生服务器错误。请稍后重试。
函数“getFoldersUnderRoot()”在将目标脚本连接到 GCP 项目之前 运行 正确。
感谢任何帮助指出我做错了什么。
我想出了如何使用 Apps 脚本执行 App 脚本功能 API。所以我发布答案是为了其他人的利益。此外,我会尝试插入 Google 在他们的说明中未提供的缺失信息。
目标脚本是一个应用程序脚本(例如“code.gs”)文件,其中包含要执行的所需功能。此脚本必须附加到启用了 App 脚本 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, 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>
下面是目标脚本的例子。
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 Consent Screen 中添加所需的范围。
我可以执行“getSheetNames()”函数,但“getFoldersUnderRoot()”会抛出错误:Exception: We're sorry, a server error occurred. Please wait a bit and try again.
从脚本编辑器执行也会出现同样的错误。但是,“getFoldersUnderRoot()”可在未附加到 GCP 项目的任何其他脚本上执行