تنفيذ الدوال باستخدام واجهة برمجة التطبيقات لبرمجة التطبيقات

توفر واجهة برمجة التطبيقات لبرمجة تطبيقات Google طريقة واحدة (scripts.run) ينفِّذ وظيفة "برمجة تطبيقات Google" محدّدة عن بُعد. يمكنك استخدام هذه الطريقة. في تطبيق استدعاء لتشغيل دالة في أحد مشاريع النصوص البرمجية عن بُعد وتلقي استجابة.

المتطلبات

يجب أن تستوفي المتطلبات التالية قبل أن يتمكن تطبيق الاتصال من استخدام scripts.run . يجب تنفيذ ما يلي:

  • انشر مشروع النص البرمجي كواجهة برمجة تطبيقات قابلة للتنفيذ. يمكنك لنشر المشاريع وإعادة نشرها وإعادة نشرها حسب الحاجة.

  • توفير رمز OAuth مميز ضمن النطاق بشكل صحيح من أجل التنفيذ يجب أن يغطي رمز OAuth المميز هذا جميع النطاقات بواسطة النص البرمجي، وليس فقط تلك التي تستخدمها الدالة المستدعاة. يمكنك الاطّلاع على قائمة كاملة بنطاقات التفويض في مرجع الطريقة.

  • تأكد من أن OAuth2 في النص البرمجي وتطبيق الاتصال العميل تشترك في مشروع مشترك على Google Cloud. يجب أن يكون المشروع على Google Cloud مشروعًا عاديًا على Cloud. المشاريع التلقائية التي يتم إنشاؤها لمشاريع "برمجة تطبيقات Google" غير كافية. ويمكنك استخدام مشروع عادي جديد على Google Cloud أو مشروع حالي.

  • تفعيل واجهة برمجة التطبيقات لبرمجة تطبيقات Google في مشروع Google Cloud

الطريقة scripts.run

scripts.run معلومات تعريفية أساسية لتشغيلها:

يمكنك اختياريًا ضبط النص البرمجي لتنفيذه في وضع التطوير. يتم تنفيذ هذا الوضع باستخدام أحدث نسخة محفوظة من مشروع النص البرمجي بدلاً من أحدث إصدار تم نشره. ويمكنك إجراء ذلك عن طريق تعيين قيمة منطقية devMode في نص الطلب إلى true. لا يمكن لأحد سوى مالك النص البرمجي تنفيذه في وضع التطوير.

التعامل مع أنواع بيانات المَعلمات

استخدام واجهة برمجة التطبيقات لبرمجة التطبيقات طريقة واحدة (scripts.run) إرسال البيانات إلى برمجة التطبيقات كمعلمات للدوال استرداد البيانات كدالة إرجاع قيم. يمكن لواجهة برمجة التطبيقات فقط تنفيذ بالأنواع الأساسية: السلاسل، والصفائف، والكائنات، والأرقام، والقيم المنطقية. هذه يشبهان الأنواع الأساسية في JavaScript. أكثر تعقيدًا كائنات "برمجة تطبيقات Google" مثل المستند أو الورقة لا يمكن تمريرها إلى أو من مشروع النص البرمجي من خلال واجهة برمجة التطبيقات.

عند كتابة طلب الاتصال بلغة عالية الكتابة، مثل Java، يتم تمريرها في المعلمات كقائمة أو مصفوفة من الكائنات العامة المقابلة لهذه الأنواع الأساسية. في كثير من الحالات، يمكنك تطبيق خيارات كتابة الإحالات الناجحة تلقائيًا. على سبيل المثال، الدالة التي تأخذ رقمًا يمكن تحديد كائن Java Double أو Integer أو Long باعتباره بدون معالجة إضافية.

عندما تعرض واجهة برمجة التطبيقات استجابة الدالة، ستحتاج غالبًا إلى إرسال التي تم إرجاعها إلى النوع الصحيح قبل استخدامها. إليك بعض الأمثلة أمثلة مستندة إلى Java:

  • تصل الأرقام التي تعرضها واجهة برمجة التطبيقات إلى تطبيق Java java.math.BigDecimal عناصر، وقد تحتاج إلى تحويلها إلى أنواع Doubles أو int حسب الحاجة.
  • إذا أرجعت دالة برمجة التطبيقات صفيفًا من السلاسل، فإن تطبيق Java لتحويل الاستجابة إلى كائن List<String>:

    List<String> mylist = (List<String>)(op.getResponse().get("result"));
    
  • إذا كنت تريد عرض مصفوفة من Bytes، قد يكون ذلك مناسبًا لك. لترميز الصفيف كسلسلة base64 ضمن دالة برمجة التطبيقات إرجاع تلك السلسلة بدلاً من ذلك:

    return Utilities.base64Encode(myByteArray); // returns a String.
    

توضّح أمثلة الرموز البرمجية أدناه طرق تفسير استجابة واجهة برمجة التطبيقات.

الإجراء العام

في ما يلي وصف للإجراء العام لاستخدام واجهة برمجة التطبيقات لبرمجة التطبيقات. لتنفيذ وظائف "برمجة تطبيقات Google":

الخطوة 1: إعداد المشروع الشائع على Cloud

يجب أن يتشارك النص البرمجي وتطبيق الاتصال التفاصيل نفسها المشروع على السحابة الإلكترونية. ويمكن أن يكون هذا المشروع على السحابة الإلكترونية مشروعًا حاليًا أو مشروع جديد تم إنشاؤه لهذا الغرض. بعد إنشاء مشروع على السحابة الإلكترونية، يمكنك تبديل مشروع النص البرمجي لاستخدامه

الخطوة 2: نشر النص البرمجي كتطبيق واجهة برمجة تطبيقات قابل للتنفيذ

  1. افتح مشروع "برمجة تطبيقات Google" الذي يتضمّن الدوال التي تريد استخدامها.
  2. في أعلى يسار الصفحة، انقر على نشر &gt; نشر جديد.
  3. في مربّع الحوار الذي يظهر، انقر على رمز تفعيل أنواع النشر . &gt; واجهة برمجة التطبيقات القابلة للتنفيذ.
  4. في قسم "من لديه إمكانية الوصول" القائمة المنسدلة، حدد المستخدمين الذين يُسمح لهم باستدعاء دوال النص البرمجي باستخدام واجهة برمجة التطبيقات لبرمجة التطبيقات.
  5. انقر على نشر.

الخطوة 3: ضبط تطبيق الاتصال

يجب على تطبيق الاتصال تفعيل واجهة برمجة التطبيقات لبرمجة التطبيقات وإنشاء بروتوكول OAuth بيانات الاعتماد قبل استخدامها. يجب أن يكون لديك إذن بالوصول إلى المشروع على السحابة الإلكترونية. للقيام بذلك.

  1. اضبط المشروع على السحابة الإلكترونية الذي يستخدمه تطبيق الاتصال والنص البرمجي. يمكنك إجراء ذلك باتّباع الخطوات التالية:
    1. تفعيل واجهة برمجة التطبيقات لبرمجة التطبيقات في المشروع على Google Cloud
    2. ضبط شاشة موافقة OAuth
    3. أنشئ بيانات اعتماد OAuth.
  2. افتح مشروع النص البرمجي وانقر على رمز نظرة عامة على يمين الصفحة.
  3. ضمن نطاقات Project Oauth، سجِّل جميع النطاقات التي يتطلبه النص البرمجي.
  4. في رمز تطبيق الاتصال، أنشئ رمز دخول OAuth نصيًا نصيًا لطلب بيانات من واجهة برمجة التطبيقات. وهذا ليس رمزًا مميزًا تستخدمه واجهة برمجة التطبيقات ذاتها، بل هو الذي يتطلبه النص البرمجي عند التنفيذ. يجب أن يتم إنشاؤه باستخدام معرِّف عميل المشروع على السحابة الإلكترونية ونطاقات النص البرمجي التي سجّلتها

    يمكن أن تفيد مكتبات برامج Google المساعدة في إنشاء هذا الرمز والتعامل مع بروتوكول OAuth للتطبيق، ما يسمح لك عادةً بإنشاء "بيانات اعتماد" ذات مستوى أعلى كائن باستخدام نطاقات البرنامج النصي. يمكنك الاطّلاع على أمثلة للبدء السريع في واجهة برمجة التطبيقات لبرمجة التطبيقات لإنشاء كائن بيانات اعتماد من قائمة النطاقات.

الخطوة 4: تقديم طلب "script.run"

بعد ضبط تطبيق الاتصال، يمكنك إجراء ما يلي: scripts.run مكالمة لكل واجهة برمجة تطبيقات الاتصال على الخطوات التالية:

  1. إنشاء طلب بيانات من واجهة برمجة التطبيقات وباستخدام معرّف النص البرمجي واسم الدالة وأي عناصر المعلَمات.
  2. إعداد scripts.run وتضمين رمز OAuth المميز للنص البرمجي الذي أنشأته في (في حال استخدام طلب POST أساسي) أو استخدام كائن بيانات اعتماد قمت بإنشائها باستخدام نطاقات البرنامج النصي.
  3. يجب السماح بإنهاء تنفيذ النص البرمجي. يُسمح للنصوص البرمجية أن تستغرق ما يصل إلى ست دقائق من وقت التنفيذ، لذلك يجب أن يسمح تطبيقك بذلك.
  4. عند الانتهاء، قد تعرض دالة script (البرنامج النصي) قيمةً تعرضها واجهة برمجة التطبيقات إلى التطبيق إذا كانت القيمة من النوع المتوافق.

يمكنك العثور على أمثلة على طلبات البيانات من واجهة برمجة التطبيقات script.run. أدناه.

أمثلة على طلبات البيانات من واجهة برمجة التطبيقات

توضّح الأمثلة التالية كيفية إجراء طلب تنفيذ واجهة برمجة التطبيقات لبرمجة التطبيقات في بلغات مختلفة، فإن استدعاء دالة برمجة التطبيقات لطباعة قائمة المجلدات في الدليل الجذر للمستخدم. رقم تعريف النص البرمجي لمشروع "برمجة تطبيقات 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()

القيود

هناك العديد من القيود على واجهة برمجة التطبيقات لبرمجة التطبيقات:

  1. مشروع مشترَك على Google Cloud: يتم استدعاء النص يجب أن يشارك تطبيق الاتصال مشروعًا على السحابة الإلكترونية. يجب أن يكون المشروع على السحابة الإلكترونية مشروع عادي على Cloud المشاريع التلقائية التي يتم إنشاؤها لمشاريع "برمجة تطبيقات Google" غير كافية. تشير رسالة الأشكال البيانية قد يكون المشروع العادي على السحابة الإلكترونية مشروعًا جديدًا أو حاليًا

  2. المَعلمات الأساسية وأنواع الإرجاع: لا يمكن لواجهة برمجة التطبيقات اجتياز الكائنات الخاصة ببرمجة التطبيقات (مثل المستندات Blobs، التقاويم، ملفات Drive وما إلى ذلك) إلى التطبيق. فقط الأنواع الأساسية مثل السلاسل والصفائف والكائنات والأرقام يمكن تمرير القيم المنطقية وإرجاعها.

  3. نطاقات OAuth. لا يمكن لواجهة برمجة التطبيقات سوى تنفيذ نصوص برمجية تحتوي على ما لا يقل عن لنطاق واحد مطلوب. ويعني هذا أنّه لا يمكنك استخدام واجهة برمجة التطبيقات لاستدعاء نص برمجي. لا يتطلب إذنًا لخدمة واحدة أو أكثر.

  4. ما مِن مشغّلات: يتعذّر على واجهة برمجة التطبيقات إنشاء "برمجة تطبيقات Google". المشغلات الخاصة.