توفر واجهة برمجة التطبيقات "برمجة تطبيقات Google" طريقة
scripts.run التي
تنفّذ عن بُعد دالة محدّدة في "برمجة تطبيقات Google". يمكنك استخدام هذه الطريقة في تطبيق مستدعي لتشغيل دالة في أحد مشاريع النصوص البرمجية عن بُعد وتلقّي ردّ.
المتطلبات
قبل أن يتمكّن تطبيق مستدعي من استخدام طريقة
scripts.run، عليك تنفيذ ما يلي:
نشر مشروع النص البرمجي كملف API قابل للتنفيذ : يمكنك نشر المشاريع وإلغاء نشرها وإعادة نشرها حسب الحاجة.
توفير رمز OAuth مميّز بنطاق مناسب للتنفيذ : يجب أن يغطي رمز OAuth المميز هذا جميع النطاقات التي يستخدمها النص البرمجي، وليس النطاقات التي تستخدمها الدالة المستدعاة فقط. يمكنك الاطّلاع على القائمة الكاملة لـ نطاقات التفويض في المرجع الخاص بالطريقة.
التأكّد من أنّ النص البرمجي وعميل OAuth2 الخاص بالتطبيق المستدعي يشتركان في مشروع Google Cloud نفسه يجب أن يكون مشروع على السحابة الإلكترونية مشروعًا عاديًا ؛ ولا تكفي المشاريع التلقائية التي تم إنشاؤها لمشاريع "برمجة تطبيقات Google" . يمكنك استخدام مشروع جديد على السحابة الإلكترونية أو مشروع حالي.
**تفعيل واجهة برمجة التطبيقات "برمجة تطبيقات Google"** في مشروع على السحابة الإلكترونية.
طريقة scripts.run
تتطلب طريقة scripts.run
المعلومات التالية:
- رقم تعريف عملية نشر النص البرمجي المطلوب تنفيذه
- اسم الدالة المطلوب تنفيذها.
- قائمة المَعلمات التي تتطلبها الدالة، إن وُجدت
يمكنك اختياريًا ضبط النص البرمجي ليتم تنفيذه في وضع التطوير.
يتم في هذا الوضع تنفيذ أحدث إصدار تم حفظه من مشروع النص البرمجي بدلاً من أحدث إصدار تم نشره. لإجراء ذلك، اضبط القيمة المنطقية
devMode في
نص الطلب
على true. لا يمكن تنفيذ النص البرمجي في وضع التطوير إلا من قِبل مالكه.
التعامل مع أنواع بيانات المَعلمات
عادةً ما يتضمّن استخدام طريقة
scripts.run في واجهة برمجة التطبيقات "برمجة تطبيقات Google"
إرسال بيانات إلى "برمجة تطبيقات Google" كمَعلمات للدالة
واسترداد البيانات كقيم معروضة من الدالة. لا يمكن لواجهة برمجة التطبيقات قبول وعرض القيم إلا بأنواع أساسية: السلاسل والمصفوفات والكائنات والأرقام والقيم المنطقية. لا يمكن لواجهة برمجة التطبيقات تمرير كائنات أكثر تعقيدًا في "برمجة تطبيقات Google"، مثل
Document أو
Sheet، إلى مشروع النص البرمجي أو
منه.
عندما يكون التطبيق المستدعي مكتوبًا بلغة ذات أنواع بيانات محددة بدقة، مثل Java، يتم تمرير المَعلمات كقائمة أو مصفوفة من الكائنات العامة التي تتطابق مع هذه الأنواع الأساسية. في حالات كثيرة، يمكنك تطبيق عمليات تحويل الأنواع تلقائيًا. على سبيل المثال، يمكن تمرير كائن Java Double أو Integer أو Long كمعلَمة إلى دالة تأخذ معلَمة رقمية بدون الحاجة إلى معالجة إضافية.
عندما تعرض واجهة برمجة التطبيقات استجابة الدالة، غالبًا ما تحتاج إلى تحويل القيمة المعروضة إلى النوع الصحيح قبل أن تتمكّن من استخدامها. في ما يلي بعض الأمثلة المستندة إلى Java:
- تصل الأرقام التي تعرضها واجهة برمجة التطبيقات إلى تطبيق Java ككائنات
java.math.BigDecimalوقد تحتاج إلى تحويلها إلى أنواعDoubleأوint. إذا كانت دالة "برمجة تطبيقات Google" تعرض مصفوفة من السلاسل، يحوّل تطبيق Java الاستجابة إلى كائن
List<String>object:List<String> mylist = (List<String>)(op.getResponse().get("result"));إذا أردت عرض مصفوفة من
Bytes، عليك ترميز المصفوفة كسلسلة base64 ضمن دالة "برمجة تطبيقات Google" وعرض هذه السلسلة بدلاً من ذلك:return Utilities.base64Encode(myByteArray); // returns a string.
توضّح نماذج الرموز البرمجية أدناه طرق تفسير استجابة واجهة برمجة التطبيقات.
الإجراء العام
لاستخدام واجهة برمجة التطبيقات "برمجة تطبيقات Google" لتنفيذ دوال "برمجة تطبيقات Google"، اتّبِع الخطوات التالية:
الخطوة 1: إعداد مشروع على السحابة الإلكترونية المشترك
يجب أن يشترك النص البرمجي والتطبيق المستدعي في مشروع على السحابة الإلكترونية نفسه. يمكن أن يكون مشروع على السحابة الإلكترونية هذا مشروعًا حاليًا أو مشروعًا جديدًا تم إنشاؤه لهذا الغرض. بعد إنشاء مشروع على السحابة الإلكترونية، عليك تبديل مشروع النص البرمجي لاستخدامه.
الخطوة 2: نشر النص البرمجي كملف API قابل للتنفيذ
- افتح مشروع "برمجة تطبيقات Google" الذي يتضمّن الدوال التي تريد استخدامها.
- في أعلى يسار الصفحة، انقر على نشر > عملية نشر جديدة.
- في مربّع الحوار الذي يظهر، انقر على تفعيل أنواع عمليات النشر
> ملف API قابل للتنفيذ.
- في القائمة المنسدلة "المستخدمون الذين يمكنهم الوصول"، اختَر المستخدمين المسموح لهم باستدعاء دوال النص البرمجي باستخدام واجهة برمجة التطبيقات "برمجة تطبيقات Google".
- انقر على نشر.
الخطوة 3: إعداد التطبيق المستدعي
يجب أن يفعّل التطبيق المستدعي واجهة برمجة التطبيقات "برمجة تطبيقات Google" ويُنشئ بيانات اعتماد OAuth قبل الاستخدام. يجب أن يكون لديك إذن الوصول إلى مشروع على السحابة الإلكترونية لإجراء ذلك.
- اضبط مشروع على السحابة الإلكترونية الذي يستخدمه التطبيق المستدعي والنص البرمجي:
- افتح مشروع النص البرمجي وانقر على نظرة عامة
على يمين الصفحة.
- ضمن نطاقات OAuth للمشروع، سجِّل جميع النطاقات التي يتطلبها النص البرمجي.
في الرمز البرمجي للتطبيق المستدعي، أنشئ رمز دخول OAuth للنص البرمجي من أجل طلب بيانات من واجهة برمجة التطبيقات. هذا ليس رمزًا مميّزًا تستخدمه واجهة برمجة التطبيقات نفسها، بل رمزًا مميّزًا يتطلبه النص البرمجي عند تنفيذه. يجب إنشاؤه باستخدام الـ Cloud project client ID ونطاقات النص البرمجي التي سجّلتها.
يمكن أن تساعد مكتبات عملاء Google في إنشاء هذا الرمز المميز والتعامل مع OAuth للتطبيق، ما يسمح لك عادةً بإنشاء كائن "بيانات اعتماد" على مستوى أعلى باستخدام نطاقات النص البرمجي. يمكنك الاطّلاع على أدلة البدء السريع لواجهة برمجة التطبيقات "برمجة تطبيقات Google " للحصول على أمثلة على إنشاء كائن بيانات اعتماد من قائمة بالنطاقات.
الخطوة 4: إرسال طلب scripts.run
بعد ضبط التطبيق المستدعي، يمكنك إجراء
scripts.run طلبات:
- أنشئ طلب بيانات من واجهة برمجة التطبيقات باستخدام رقم تعريف عملية النشر واسم الدالة وأي مَعلمات مطلوبة.
- أجرِ
scripts.runالطلب وضِّمن رمز OAuth المميز للنص البرمجي الذي أنشأته في العنوان (إذا كنت تستخدم طلبPOSTأساسيًا) أو استخدِم كائن بيانات اعتماد أنشأته باستخدام نطاقات النص البرمجي. - اسمح للنص البرمجي بإنهاء التنفيذ. يمكن أن يستغرق تنفيذ النصوص البرمجية ما يصل إلى ست دقائق، لذا يجب أن يسمح تطبيقك بذلك.
- عند الانتهاء، قد تعرض دالة النص البرمجي قيمة، والتي تُعيدها واجهة برمجة التطبيقات إلى التطبيق إذا كان نوع القيمة متوافقًا.
يمكنك العثور على أمثلة على طلبات واجهة برمجة التطبيقات scripts.run في القسم
التالي.
لإعادة تحميل رمز الدخول، أضِف المقتطف التالي قبل طلب scripts.run من واجهة برمجة التطبيقات:
if (credential.getExpiresInSeconds() <= 360) {
credential.refreshToken();
}
أمثلة على طلبات بيانات من واجهة برمجة التطبيقات
توضّح الأمثلة التالية كيفية إجراء طلب تنفيذ من واجهة برمجة التطبيقات "برمجة تطبيقات Google" بلغات مختلفة، واستدعاء دالة في "برمجة تطبيقات Google" لطباعة قائمة بالمجلدات في الدليل الجذري للمستخدم. يجب تحديد رقم تعريف عملية نشر مشروع "برمجة تطبيقات Google" الذي يحتوي على الدالة التي تم تنفيذها في الموضع المحدّد بـ ENTER_YOUR_DEPLOYMENT_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;
}
جافا
/**
* 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()
القيود
تخضع واجهة برمجة التطبيقات "برمجة تطبيقات Google" للقيود التالية:
مشروع على السحابة الإلكترونية مشترك : يجب أن يشترك النص البرمجي الذي يتم استدعاؤه والتطبيق المستدعي في مشروع على السحابة الإلكترونية. يجب أن يكون مشروع على السحابة الإلكترونية مشروعًا عاديًا، ولا تكفي المشاريع التلقائية التي تم إنشاؤها لمشاريع "برمجة تطبيقات Google".
أنواع المَعلمات والقيم المعروضة الأساسية : لا يمكن لواجهة برمجة التطبيقات تمرير أو عرض كائنات خاصة بـ "برمجة تطبيقات Google" (مثل المستندات، الكائنات الثنائية الكبيرة، التقاويم، ملفات Drive، وما إلى ذلك) إلى التطبيق. لا يمكن تمرير وعرض سوى الأنواع الأساسية، مثل السلاسل والمصفوفات والكائنات والأرقام والقيم المنطقية.
نطاقات OAuth : لا يمكن لواجهة برمجة التطبيقات تنفيذ النصوص البرمجية التي تتضمّن نطاقًا واحدًا على الأقل مطلوبًا. يعني هذا أنّه لا يمكنك استخدام واجهة برمجة التطبيقات لاستدعاء نص برمجي لا يتطلب تفويض خدمة واحدة أو أكثر.
لا توجد علامات تشغيل : لا يمكن لواجهة برمجة التطبيقات إنشاء علامات تشغيل في "برمجة تطبيقات Google" .