توفر واجهة برمجة التطبيقات لبرمجة تطبيقات Google طريقة
scripts.run
تنفّذ عن بُعد وظيفة محددة في "برمجة تطبيقات Google". يمكنك استخدام هذه الطريقة
في تطبيق اتصال لتشغيل دالة في أحد مشاريع النصوص البرمجية
عن بُعد وتلقّي استجابة من خلالها.
المتطلّبات
يجب أن تستوفي المتطلبات التالية قبل أن يتمكن تطبيق الاتصال من استخدام
طريقة scripts.run
. يجب استيفاء المتطلبات:
انشر مشروع النص البرمجي كواجهة برمجة تطبيقات قابلة للتنفيذ. يمكنك نشر المشروعات وإلغاء نشرها وإعادة نشرها حسب الحاجة.
توفير رمز OAuth مميز بنطاق مناسب للتنفيذ يجب أن يغطّي رمز OAuth المميز جميع النطاقات التي يستخدمها النص البرمجي، وليس فقط النطاقات التي تستخدمها الدالة المُسمّاة. اطّلع على القائمة الكاملة لنطاقات التفويض في مرجع الطريقة.
تأكَّد من أنّ النص البرمجي وعميل OAuth2 في تطبيق الاتصال يتشاركان في مشروع Google Cloud مشترك. يجب أن يكون مشروع Google Cloud مشروع Cloud عادي، والمشاريع التلقائية التي تم إنشاؤها لمشاريع "برمجة التطبيقات" غير كافية. يمكنك استخدام مشروع عادي جديد على السحابة الإلكترونية أو مشروع حالي.
تفعيل واجهة برمجة التطبيقات لبرمجة تطبيقات Google في المشروع على السحابة الإلكترونية
طريقة scripts.run
تتطلّب الطريقة scripts.run
معلومات تعريفية للمفاتيح ليتم تشغيلها:
- رقم تعريف مشروع النص البرمجي.
- اسم الدالة المراد تنفيذها.
- تمثّل هذه السمة قائمة المعلمات التي تتطلّبها الدالة (إن توفّرت).
يمكنك اختياريًا ضبط النص البرمجي لتنفيذه في وضع التطوير.
يتم تنفيذ هذا الوضع باستخدام أحدث إصدار محفوظ من مشروع النص البرمجي
بدلاً من أحدث إصدار تم نشره. يمكنك إجراء ذلك من خلال ضبط القيمة المنطقية devMode
في نص الطلب على true
. لا يمكن إلا لمالك النص البرمجي تنفيذه في وضع التطوير.
التعامل مع أنواع بيانات المَعلمات
يتضمّن عادةً استخدام طريقة واجهة برمجة التطبيقات لبرمجة التطبيقات
scripts.run
إرسال البيانات إلى "برمجة التطبيقات" كمَعلمات للدوال
واستعادة البيانات كقيم عرض للدالة. يمكن لواجهة برمجة التطبيقات أخذ القيم وعرضها فقط بالأنواع الأساسية: السلاسل، والصفيفات، والكائنات، والأرقام، والقيم المنطقية. وهذه تشبه الأنواع الأساسية في JavaScript. طريقة أكثر تعقيدًا
لا يمكن تمرير كائنات "برمجة تطبيقات Google"، مثل مستند
أو ورقة، إلى مشروع النص البرمجي أو منه بواسطة واجهة برمجة التطبيقات.
عندما تتم كتابة تطبيق الاتصال بلغة عالية الكتابة مثل Java، يتم تمرير المعلَمات كقائمة أو مصفوفة من الكائنات العامة
المتوافقة مع هذه الأنواع الأساسية. في كثير من الحالات، يمكنك تطبيق
الإحالات الناجحة للأنواع البسيطة تلقائيًا. على سبيل المثال، يمكن منح دالة تستخدم مَعلمة رقميّة كائن Double
أو Integer
أو Long
من JavaScript كمَعلمة بدون معالجة إضافية.
عندما تعرض واجهة برمجة التطبيقات استجابة الدالة، غالبًا ما تحتاج إلى تحويل القيمة المعروضة إلى النوع الصحيح قبل استخدامها. فيما يلي بعض الأمثلة القائمة على Java:
- تصل الأرقام التي تعرضها واجهة برمجة التطبيقات إلى تطبيق Java على شكل
كائنات
java.math.BigDecimal
، وقد تحتاج إلى تحويلها إلى نوعَينDoubles
أوint
حسب الحاجة. إذا كانت دالة "برمجة تطبيقات Google" تعرض صفيفًا من السلاسل، سيُرسِل تطبيق Java الاستجابة في كائن
List<String>
:List<String> mylist = (List<String>)(op.getResponse().get("result"));
إذا كنت تريد عرض صفيف من
Bytes
، قد يكون من الملائم ترميز الصفيف كسلسلة Base64 ضمن دالة "برمجة التطبيقات" وعرض هذه السلسلة بدلاً من ذلك:return Utilities.base64Encode(myByteArray); // returns a String.
توضّح أمثلة الرموز البرمجية أدناه طرق تفسير استجابة واجهة برمجة التطبيقات.
الإجراء العام
يوضِّح ما يلي الإجراء العام لاستخدام واجهة برمجة التطبيقات لبرمجة التطبيقات لتنفيذ وظائف "برمجة التطبيقات":
الخطوة 1: إعداد المشروع المشترك على Google Cloud
يجب أن يشارك كل من النص البرمجي وتطبيق الاتصال نفس المشروع على السحابة الإلكترونية. يمكن أن يكون هذا المشروع على Google Cloud مشروعًا حاليًا أو مشروعًا جديدًا تم إنشاؤه لهذا الغرض. بعد إنشاء مشروع على السحابة الإلكترونية، عليك تبديل مشروع النص البرمجي لاستخدامه.
الخطوة 2: نشر النص البرمجي كواجهة برمجة تطبيقات قابلة للتنفيذ
- افتح مشروع "برمجة تطبيقات Google" الذي يتضمّن الدوال التي تريد استخدامها.
- في أعلى يسار الصفحة، انقر على نشر > نشر جديد.
- في مربّع الحوار الذي يُفتح، انقر على "تفعيل أنواع النشر"
> قابل للتنفيذ عبر واجهة برمجة التطبيقات.
- في القائمة المنسدلة "من لديه إذن الوصول"، اختَر المستخدمين المسموح لهم بطلب وظائف النص البرمجي باستخدام واجهة برمجة التطبيقات لبرمجة التطبيقات.
- انقر على نشر.
الخطوة 3: ضبط تطبيق الاتصال
يجب أن يفعِّل تطبيق الاتصال واجهة برمجة التطبيقات لبرمجة التطبيقات وأن ينشئ بيانات اعتماد OAuth قبل أن تتمكن من استخدامها. ولتنفيذ ذلك، يجب أن يكون لديك إذن بالوصول إلى المشروع على السحابة الإلكترونية.
- اضبط المشروع على السحابة الإلكترونية الذي يستخدمه تطبيق الاتصال والنص البرمجي. يمكنك إجراء ذلك من خلال اتّباع الخطوات التالية:
- افتح مشروع النص البرمجي، وعلى يمين الصفحة، انقر على نظرة عامة
.
- ضمن نطاقات Project Oauth، سجِّل جميع النطاقات التي يتطلبها النص البرمجي.
في رمز تطبيق الاتصال، أنشِئ رمز دخول OAuth برمجيًا لاستدعاء واجهة برمجة التطبيقات. وهذا ليس رمزًا مميّزًا تستخدمه واجهة برمجة التطبيقات نفسها، ولكنه رمزًا مميّزًا يتطلبه النص البرمجي عند التنفيذ. يجب إنشاؤه باستخدام معرِّف عميل المشروع على السحابة الإلكترونية ونطاقات النص البرمجي التي سجّلتها.
يمكن أن تساعد مكتبات برامج Google بشكل كبير في إنشاء هذا الرمز المميّز ومعالجة OAuth للتطبيق، ما يتيح لك عادةً إنشاء كائن "بيانات اعتماد" على مستوى أعلى باستخدام نطاقات النصوص البرمجية. يمكنك الاطّلاع على دليل البدء السريع لواجهة برمجة التطبيقات لبرمجة التطبيقات للحصول على أمثلة حول إنشاء عنصر بيانات اعتماد من قائمة النطاقات.
الخطوة 4: تقديم طلب script.run
بعد ضبط تطبيق الاتصال، يمكنك إجراء مكالمات
scripts.run
. يتكون كل طلب من واجهة برمجة التطبيقات
من الخطوات التالية:
- أنشئ طلب واجهة برمجة التطبيقات باستخدام رقم تعريف النص البرمجي واسم الدالة وأي معلمات مطلوبة.
- يمكنك إجراء استدعاء
scripts.run
وتضمين رمز OAuth المميز للنص البرمجي الذي أنشأته في العنوان (في حال استخدام طلبPOST
أساسي) أو استخدام كائن بيانات اعتماد أنشأته باستخدام نطاقات النصوص البرمجية. - اسمح للنص البرمجي بإنهاء التنفيذ. يُسمح للنصوص البرمجية أن تستغرق ما يصل إلى ست دقائق من وقت التنفيذ، لذا يجب أن يسمح التطبيق بذلك.
- عند الانتهاء، قد تعرض دالة النص البرمجي قيمةً تُعيدها واجهة برمجة التطبيقات إلى التطبيق إذا كانت القيمة من النوع المتوافق.
يمكنك العثور على أمثلة على طلبات البيانات من واجهة برمجة التطبيقات script.run
أدناه.
أمثلة على طلبات البيانات من واجهة برمجة التطبيقات
توضّح الأمثلة التالية كيفية تقديم طلب تنفيذ واجهة برمجة التطبيقات لبرمجة التطبيقات بلغات مختلفة، مع استدعاء إحدى وظائف برمجة التطبيقات لطباعة قائمة بالمجلدات في الدليل الجذري للمستخدم. يجب تحديد رقم تعريف النص البرمجي لمشروع "برمجة تطبيقات Google"
الذي يحتوي على الوظيفة المنفذة، في مكان الإشارة إليه باستخدام
ENTER_YOUR_SCRIPT_ID_HERE
. تعتمد الأمثلة على مكتبات عميل Google API للغات المعنية.
النص البرمجي للاستهداف
تستخدِم الدالة في هذا النص البرمجي واجهة برمجة تطبيقات Drive.
عليك تفعيل 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
from __future__ import print_function
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}."
f"{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()
القيود
هناك قيود مفروضة على واجهة برمجة التطبيقات لبرمجة التطبيقات:
مشروع مشترك على Google Cloud: يجب أن يشارك النص البرمجي الذي يتم استدعاءه وتطبيق الاتصال مشروعًا على السحابة الإلكترونية. يجب أن يكون المشروع على السحابة الإلكترونية مشروعًا عاديًا على السحابة الإلكترونية، والمشاريع التلقائية التي تم إنشاؤها لمشاريع "برمجة التطبيقات" غير كافية. يمكن أن يكون المشروع العادي على السحابة الإلكترونية مشروعًا جديدًا أو حاليًا.
المَعلمات الأساسية وأنواع الإرجاع: لا يمكن لواجهة برمجة التطبيقات تمرير أو إرجاع الكائنات الخاصة ببرمجة التطبيقات (مثل المستندات ووحدات البيانات والتقاويم وملفات Drive وما إلى ذلك) إلى التطبيق. يمكن فقط تمرير الأنواع الأساسية مثل السلاسل والصفيفات والكائنات والأرقام والقيم المنطقية.
نطاقات OAuth. يمكن لواجهة برمجة التطبيقات تنفيذ النصوص البرمجية التي تحتوي على نطاق مطلوب واحد على الأقل. وهذا يعني أنه لا يمكنك استخدام واجهة برمجة التطبيقات لاستدعاء نص برمجي لا يتطلب إذنًا لخدمة واحدة أو أكثر.
ما مِن مشغِّلات.لا يمكن لواجهة برمجة التطبيقات إنشاء عوامل تشغيل لبرمجة التطبيقات.