API Apps Script предоставляет метод scripts.run , который удаленно выполняет указанную функцию Google Apps Script. Вы можете использовать этот метод в вызывающем приложении для удаленного запуска функции в одном из ваших проектов скриптов и получения ответа.
Требования
Прежде чем вызывающее приложение сможет использовать метод scripts.run , необходимо:
Разверните проект скрипта как исполняемый файл API. Вы можете развертывать, удалять и повторно развертывать проекты по мере необходимости.
Предоставьте корректно настроенный токен OAuth для выполнения. Этот токен OAuth должен охватывать все области действия , используемые скриптом, а не только те, которые используются вызываемой функцией. Полный список областей действия авторизации см. в справочнике методов.
Убедитесь, что скрипт и OAuth2-клиент вызывающего приложения используют общий проект Google Cloud. Проект Cloud должен быть стандартным проектом Cloud ; проекты по умолчанию, созданные для проектов Apps Script, недостаточны. Вы можете использовать новый стандартный проект Cloud или существующий.
Включите API Google Apps Script в облачном проекте.
Метод scripts.run
Для работы метода scripts.run требуется следующая информация:
- Идентификатор проекта скрипта .
- Название функции для выполнения.
- Список параметров , необходимых функции, если таковые имеются.
При желании вы можете настроить свой скрипт для выполнения в режиме разработки . В этом режиме скрипт выполняется с использованием последней сохраненной версии проекта, а не последней развернутой версии. Для этого установите логическое значение devMode в теле запроса в значение true . Только владелец скрипта может выполнить его в режиме разработки.
Обработка типов данных параметров
Использование метода scripts.run в API Apps Script обычно включает передачу данных в Apps Script в качестве параметров функции и получение данных обратно в виде возвращаемых значений функции. API может принимать и возвращать только значения базовых типов: строки, массивы, объекты, числа и логические значения. Более сложные объекты Apps Script, такие как Document или Sheet, не могут быть переданы в проект скрипта или из него с помощью API.
Когда ваше приложение, выполняющее вызов функции, написано на языке со строгой типизацией, таком как Java, оно передает параметры в виде списка или массива обобщенных объектов, соответствующих этим базовым типам. Во многих случаях преобразование типов может применяться автоматически. Например, функции, принимающей числовой параметр, можно передать в качестве параметра объект типа Java Double , Integer или Long без дополнительной обработки.
Когда API возвращает ответ от функции, часто требуется привести возвращаемое значение к нужному типу, прежде чем его можно будет использовать. Вот несколько примеров на Java:
- Числа, возвращаемые API в Java-приложение, поступают в виде объектов
java.math.BigDecimalи могут потребовать преобразования в типыDoubleилиint. Если функция Apps Script возвращает массив строк, Java-приложение преобразует ответ в объект
List<String>:List<String> mylist = (List<String>)(op.getResponse().get("result"));Если вам нужно вернуть массив
Bytes, закодируйте массив в строку base64 внутри функции Apps Script и верните вместо этого именно эту строку:return Utilities.base64Encode(myByteArray); // returns a string.
Приведенные ниже примеры кода иллюстрируют способы интерпретации ответа API.
Общая процедура
Чтобы использовать API Apps Script для выполнения функций Apps Script, выполните следующие действия:
Шаг 1: Настройка общего облачного проекта
И ваш скрипт, и вызывающее приложение должны использовать один и тот же облачный проект. Это может быть существующий проект или новый проект, созданный специально для этой цели. После того, как у вас появится облачный проект, необходимо переключить ваш скриптовый проект на его использование .
Шаг 2: Разверните скрипт как исполняемый файл API.
- Откройте проект Apps Script с функциями, которые вы хотите использовать.
- В правом верхнем углу нажмите «Развернуть» > «Новое развертывание» .
- В открывшемся диалоговом окне нажмите «Включить типы развертывания».
> Исполняемый файл API .
- В раскрывающемся меню «Кто имеет доступ» выберите пользователей, которым разрешено вызывать функции скрипта с помощью API Apps Script.
- Нажмите «Развернуть» .
Шаг 3: Настройка вызывающего приложения
Перед использованием вызывающее приложение должно включить API Apps Script и установить учетные данные OAuth. Для этого вам необходим доступ к проекту Cloud.
- Настройте облачный проект, который используют ваше вызывающее приложение и скрипт:
- Откройте проект скрипта и слева нажмите «Обзор».
.
- В разделе «Области действия Project OAuth» укажите все области действия, необходимые для выполнения скрипта.
В коде вызывающего приложения сгенерируйте токен доступа OAuth для скрипта, используемого для вызова API. Этот токен не используется самим API, а требуется скрипту при выполнении. Он должен быть создан с использованием идентификатора клиента проекта Cloud и областей действия скрипта, которые вы записали.
Клиентские библиотеки Google могут помочь в создании этого токена и обработке OAuth для приложения, обычно позволяя создать объект «учетных данных» более высокого уровня, используя области видимости скрипта. Примеры создания объекта учетных данных из списка областей видимости см. в кратких руководствах по API Apps Script.
Шаг 4: Выполните запрос scripts.run
После настройки вызывающего приложения вы можете выполнять вызовы scripts.run :
- Сформируйте API-запрос, используя идентификатор скрипта, имя функции и все необходимые параметры.
- Выполните вызов
scripts.runи включите созданный вами токен OAuth для скрипта в заголовок (если используется базовыйPOSTзапрос) или используйте объект учетных данных, созданный вами с учетом областей действия скрипта. - Дождитесь завершения выполнения скрипта. Выполнение скрипта может занять до шести минут, поэтому ваше приложение должно это учитывать.
- По завершении выполнения скриптовая функция может вернуть значение, которое API передаст обратно приложению, если это значение является поддерживаемым типом.
Примеры вызовов API scripts.run можно найти в следующем разделе.
Чтобы обновить токен доступа, добавьте следующий фрагмент кода перед запросом API, выполняемым scripts.run :
if (credential.getExpiresInSeconds() <= 360) {
credential.refreshToken();
}
Примеры запросов к API
В следующих примерах показано, как выполнить запрос на выполнение API Apps Script на разных языках, вызывая функцию Apps Script для вывода списка папок в корневом каталоге пользователя. Идентификатор скрипта проекта Apps Script, содержащего выполняемую функцию, необходимо указать в поле ENTER_YOUR_SCRIPT_ID_HERE . Примеры основаны на библиотеках Google API Client .
Целевой скрипт
Функция в этом скрипте использует API Google Drive.
Необходимо включить API Google Drive в проекте, в котором размещен скрипт.
Кроме того, вызывающие приложения должны отправлять учетные данные OAuth, которые включают следующую область действия Drive:
-
https://www.googleapis.com/auth/drive
В приведенных здесь примерах приложений используются клиентские библиотеки Google для создания объектов учетных данных для OAuth с использованием этой области видимости.
/**
* 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
import {GoogleAuth} from 'google-auth-library';
import {google} from 'googleapis';
/**
* Calls an Apps Script function to list the folders in the user's root Drive folder.
*/
async function callAppsScript() {
// The ID of the Apps Script project to call.
const scriptId = '1xGOh6wCm7hlIVSVPKm0y_dL-YqetspS5DEVmMzaxd_6AAvI-_u8DSgBT';
// Authenticate with Google and get an authorized client.
// TODO (developer): Use an appropriate auth mechanism for your app.
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/drive',
});
// Create a new Apps Script API client.
const script = google.script({version: 'v1', auth});
const resp = await script.scripts.run({
auth,
requestBody: {
// The name of the function to call in the Apps Script project.
function: 'getFoldersUnderRoot',
},
scriptId,
});
if (resp.data.error?.details?.[0]) {
// The API executed, but the script returned an error.
// Extract the error details.
const error = resp.data.error.details[0];
console.log(`Script error message: ${error.errorMessage}`);
console.log('Script error stacktrace:');
if (error.scriptStackTraceElements) {
// Log the stack trace.
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 script executed successfully.
// The structure of the response depends on the Apps Script function's return value.
const folderSet = resp.data.response ?? {};
if (Object.keys(folderSet).length === 0) {
console.log('No folders returned!');
} else {
console.log('Folders under your root folder:');
Object.keys(folderSet).forEach((id) => {
console.log('\t%s (%s)', folderSet[id], id);
});
}
}
}
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()
Ограничения
API Apps Script имеет следующие ограничения:
Стандартный облачный проект . Вызываемый скрипт и вызывающее приложение должны использовать один и тот же облачный проект. Облачный проект должен быть стандартным ; проекты по умолчанию, созданные для проектов Apps Script, недостаточны.
Основные типы параметров и возвращаемых значений . API не может передавать или возвращать в приложение объекты, специфичные для Apps Script (такие как документы , большие двоичные объекты, календари , файлы Google Диска и т. д.). Передавать и возвращать можно только основные типы, такие как строки, массивы, объекты, числа и логические значения.
Области действия OAuth . API может выполнять только скрипты, имеющие хотя бы одну обязательную область действия. Это означает, что вы не можете использовать API для вызова скрипта, который не требует авторизации одной или нескольких служб.
Нет триггеров . API не может создавать триггеры Apps Script.