توفّر Google Apps Script API scripts.run
أسلوبًا
ينفِّذ عن بُعد دالة محدّدة في Apps Script. يمكنك استخدام هذه الطريقة
في تطبيق اتصال لتشغيل دالة في أحد مشاريع النصوص البرمجية
عن بُعد وتلقّي استجابة.
المتطلبات
يجب استيفاء المتطلبات التالية قبل أن يتمكّن تطبيق الاتصال من استخدام scripts.run
. عليك:
نشر مشروع النصوص البرمجية كملف قابل للتنفيذ لواجهة برمجة التطبيقات يمكنك نشر المشاريع وإلغاء نشرها وإعادة نشرها حسب الحاجة.
قدِّم رمز OAuth ذي النطاق المناسب لتنفيذ الإجراء. يجب أن يشمل رمز OAuth المميّز هذا جميع النطاقات التي يستخدمها النص البرمجي، وليس فقط النطاقات التي تستخدمها الدالة التي يتمّ استدعاؤها. اطّلِع على القائمة الكاملة لنطاقات التفويض في مرجع الطريقة.
تأكَّد من أنّ النص البرمجي وOAuth2 عميل التطبيق المُرسِل يتشاركان مشروع Google Cloud مشتركًا. يجب أن يكون مشروع Cloud مشروعًا عاديًا على Cloud، لأنّ المشاريع التلقائية التي تم إنشاؤها لمشاريع "برمجة التطبيقات" غير كافية. يمكنك استخدام مشروع Cloud عادي جديد أو مشروع حالي.
فعِّل Google Apps Script API في مشروع Cloud.
طريقة scripts.run
تتطلّب طريقة scripts.run
معلومات تحديد المفتاح من أجل تنفيذها:
- رقم تعريف مشروع النصوص البرمجية
- اسم الدالة المطلوب تنفيذها.
- قائمة المَعلمات التي تتطلّبها الدالة (إن توفّرت)
يمكنك اختياريًا ضبط النص البرمجي لتشغيله في وضع التطوير.
يتم تنفيذ هذا الوضع باستخدام أحدث إصدار تم حفظه من مشروع النصوص البرمجية
بدلاً من أحدث إصدار تم نشره. يمكنك إجراء ذلك من خلال ضبط القيمة الحقيقية أو الخاطئة لصيغة devMode
في
نص الطلب
على true
. لا يمكن تنفيذ النص البرمجي إلا من قِبل مالك النص البرمجي في وضع التطوير.
التعامل مع أنواع بيانات المَعلمات
إنّ استخدام واجهة برمجة التطبيقات Apps Script API
scripts.run
يتطلّب عادةً إرسال البيانات إلى Apps Script كمَعلمات للدالة،
واسترداد البيانات كقيم تعرضها الدالة. لا يمكن لواجهة برمجة التطبيقات قبول سوى
القيم ذات الأنواع الأساسية: السلاسل والأعمدة والكائنات والأرقام والقيم المنطقية. وهذه الأنواع
تشبه الأنواع الأساسية في JavaScript. لا يمكن لواجهة برمجة التطبيقات تمرير مزيد من كائنات برمجة التطبيقات المعقدة، مثل المستند
أو الجدول، إلى
أو من مشروع النص البرمجي.
عندما يكون تطبيق الاتصال مكتوبًا بلغة ذات أنواع محدّدة بدقة مثل
Java، يتم تمرير المَعلمات كقائمة أو صفيف من العناصر العامة
المتوافقة مع هذه الأنواع الأساسية. في كثير من الحالات، يمكنك تطبيق الإحالات الناجحة من نوع
بسيط تلقائيًا. على سبيل المثال، يمكن أن تتلقّى الدالة التي تأخذ مَعلمة عدد
عنصر Java Double
أو Integer
أو Long
كمَعلمة
بدون معالجة إضافية.
عندما تعرض واجهة برمجة التطبيقات استجابة الدالة، عليك غالبًا تحويل القيمة المعروضة إلى النوع الصحيح قبل استخدامها. في ما يلي بعض الأمثلة المستندة إلى Java:
- تظهر الأرقام التي تعرضها واجهة برمجة التطبيقات لتطبيق Java على شكل عناصر
java.math.BigDecimal
، وقد تحتاج إلى تحويلها إلى أنواعDoubles
أو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.
توضّح عيّنات الرموز البرمجية النموذجية أدناه طرقinterpreting the API response.
الإجراء العام
في ما يلي الخطوات العامة لاستخدام واجهة برمجة التطبيقات Apps Script API لتنفيذ دوالّ Apps Script:
الخطوة 1: إعداد مشروع Cloud المشترك
يجب أن يتشارك كل من النص البرمجي وتطبيق الاتصال مشروع Cloud نفسه. يمكن أن يكون مشروع Cloud هذا مشروعًا حاليًا أو مشروعًا جديدًا تم إنشاؤه لهذا الغرض. بعد إنشاء مشروع على Cloud، عليك تبديل مشروع النصوص البرمجية لاستخدامه.
الخطوة 2: نشر النص البرمجي كملف قابل للتنفيذ لواجهة برمجة التطبيقات
- افتح مشروع Apps Script الذي يتضمّن الدوالّ التي تريد استخدامها.
- في أعلى يسار الصفحة، انقر على نشر > نشر جديد.
- في مربّع الحوار المنبثق، انقر على تفعيل أنواع النشر > ملف تنفيذي لواجهة برمجة التطبيقات.
- في القائمة المنسدلة "المستخدمون الذين لديهم إذن الوصول"، اختَر المستخدمين الذين يُسمح لهم بالاتّصال بدوالّ النص البرمجي باستخدام واجهة برمجة التطبيقات Apps Script API.
- انقر على نشر.
الخطوة 3: ضبط تطبيق الاتصال
يجب أن يفعّل التطبيق المُرسِل واجهة برمجة التطبيقات Apps Script API وأن ينشئ بيانات اعتماد OAuth قبل أن يتمكّن من استخدامها. لتنفيذ ذلك، يجب أن يكون لديك إذن بالوصول إلى مشروع Cloud.
- اضبط مشروع Cloud الذي يستخدمه التطبيق المُطلِب والنص البرمجي. يمكنك إجراء ذلك من خلال اتّباع الخطوات التالية:
- افتح مشروع النص البرمجي، ثم انقر على نظرة عامة على يمين الصفحة.
- ضمن نطاقات OAuth للمشروع، سجِّل جميع النطاقات التي يتطلّبها الرمز البرمجي.
في رمز التطبيق المُستخدِم، أنشئ رمز دخول OAuth لنص برمجي لاستدعاء واجهة برمجة التطبيقات. هذا ليس رمزًا مميزًا تستخدمه واجهة برمجة التطبيقات نفسها، بل هو رمز يتطلّبه النص البرمجي عند تنفيذه. يجب إنشاؤه باستخدام معرّف عميل مشروع Cloud ونطاقات النصوص البرمجية التي سجّلتها.
يمكن أن تساعدك مكتبات عملاء Google بشكلٍ كبير في إنشاء هذا الرمز المميّز ومعالجة OAuth للتطبيق، ما يتيح لك عادةً إنشاء عنصر "بيانات اعتماد" من مستوى أعلى بدلاً من ذلك باستخدام نطاقات النصوص البرمجية. اطّلِع على الخطوات السريعة لاستخدام Apps Script API للحصول على أمثلة على إنشاء عنصر بيانات اعتماد من قائمة بالنطاقات.
الخطوة 4: تقديم طلب script.run
بعد ضبط تطبيق الاتصال، يمكنك إجراء مكالمات على
scripts.run
. تتكوّن كل عملية
اتصال بواجهة برمجة التطبيقات من الخطوات التالية:
- أنشئ طلب بيانات من واجهة برمجة التطبيقات باستخدام معرّف النص البرمجي واسم الدالة وأي مَعلمات مطلوبة.
- يمكنك إجراء طلب
scripts.run
وتضمين رمز OAuth المميّز للنص البرمجي الذي أنشأته في ملف الرأس (في حال استخدام طلبPOST
أساسي)، أو يمكنك استخدام عنصر بيانات اعتماد أنشأته باستخدام نطاقات النصوص البرمجية. - اسمح للنص البرمجي بإكمال التنفيذ. يُسمح للنصوص البرمجية باستخدام ما يصل إلى ست دقائق من وقت التنفيذ، لذا يجب أن يسمح تطبيقك بذلك.
- عند الانتهاء، قد تعرِض دالة النص البرمجي قيمةً، والتي تُعيدها واجهة برمجة التطبيقات إلى التطبيق إذا كانت القيمة من النوع المتوافق.
يمكنك العثور أدناه على أمثلة على script.run
طلبات البيانات من واجهة برمجة التطبيقات.
أمثلة على طلبات البيانات من واجهة برمجة التطبيقات
توضِّح الأمثلة التالية كيفية تقديم طلب تنفيذ واجهة برمجة التطبيقات Apps Script API بلغات مختلفة، من خلال استدعاء وظيفة Apps Script لطباعة قائمة بالملفّات في الدليل الجذر للمستخدم. يجب تحديد معرّف النص البرمجي لمشروع "برمجة تطبيقات Google" الذي يحتوي على الدالة التي تم تنفيذها حيث يُشار إليه باستخدامENTER_YOUR_SCRIPT_ID_HERE
. تعتمد الأمثلة على
مكتبات عملاء Google API للغات
المعنية.
النص البرمجي المستهدَف
تستخدم الدالة في هذا النص البرمجي واجهة برمجة التطبيقات Drive API.
يجب تفعيل واجهة برمجة التطبيقات Drive API في المشروع الذي يستضيف النص البرمجي.
بالإضافة إلى ذلك، يجب أن ترسل التطبيقات التي تطلب البيانات بيانات اعتماد 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
/**
* Call an Apps Script function to list the folders in the user's root Drive
* folder.
*
*/
async function callAppsScript() {
const scriptId = '1xGOh6wCm7hlIVSVPKm0y_dL-YqetspS5DEVmMzaxd_6AAvI-_u8DSgBT';
const {GoogleAuth} = require('google-auth-library');
const {google} = require('googleapis');
// Get credentials and build service
// TODO (developer) - Use appropriate auth mechanism for your app
const auth = new GoogleAuth({
scopes: 'https://www.googleapis.com/auth/drive',
});
const script = google.script({version: 'v1', auth});
try {
// Make the API request. The request object is included here as 'resource'.
const resp = await script.scripts.run({
auth: auth,
resource: {
function: 'getFoldersUnderRoot',
},
scriptId: scriptId,
});
if (resp.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 = resp.error.details[0];
console.log('Script error message: ' + error.errorMessage);
console.log('Script error stacktrace:');
if (error.scriptStackTraceElements) {
// There may not be a stacktrace if the script didn't start executing.
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 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
// Node.js object (folderSet).
const folderSet = resp.response.result;
if (Object.keys(folderSet).length == 0) {
console.log('No folders returned!');
} else {
console.log('Folders under your root folder:');
Object.keys(folderSet).forEach(function(id) {
console.log('\t%s (%s)', folderSet[id], id);
});
}
}
} catch (err) {
// TODO(developer) - Handle error
throw err;
}
}
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()
القيود
تفرض واجهة برمجة التطبيقات Apps Script API عدة قيود:
مشروع Cloud شائع يجب أن يتشارك النص البرمجي الذي يتم استدعاؤه والتطبيق الذي يستدعيه مشروعًا على Cloud. يجب أن يكون مشروع Cloud هو مشروع Cloud عادي، فالمشروعات التلقائية التي تم إنشاؤها لمشاريع "برمجة التطبيقات" غير كافية. يمكن أن يكون المشروع العادي على السحابة الإلكترونية مشروعًا جديدًا أو حاليًا.
أنواع المَعلمات الأساسية وأنواع النتائج لا يمكن لواجهة برمجة التطبيقات تمرير أو عرض عناصر خاصة بخدمة "برمجة التطبيقات" (مثل المستندات، العناصر المصغّرة، التقاويم، ملفات Drive، وما إلى ذلك) إلى التطبيق. لا يمكن تمرير أنواع أساسية فقط، مثل السلاسل والمصفوفات والكائنات والأرقام والقيمة "صحيح/خطأ"، وإرجاعها.
نطاقات OAuth: لا يمكن لواجهة برمجة التطبيقات تنفيذ النصوص البرمجية التي تحتوي على نطاق مطلوب واحد على الأقل. وهذا يعني أنّه لا يمكنك استخدام واجهة برمجة التطبيقات لاستدعاء نص برمجي لا يتطلّب تفويض خدمة واحدة أو أكثر.
ما مِن مشغِّلات: لا يمكن لواجهة برمجة التطبيقات إنشاء مشغِّلات Apps Script.