اجرای توابع با API اسکریپت Google Apps

API اسکریپت برنامه‌ها، متدی scripts.run ارائه می‌دهد که یک تابع مشخص‌شده‌ی اسکریپت گوگل اپس را از راه دور اجرا می‌کند. می‌توانید از این متد در یک برنامه‌ی فراخوانی‌کننده استفاده کنید تا یک تابع را در یکی از پروژه‌های اسکریپت خود از راه دور اجرا کرده و پاسخی دریافت کنید.

الزامات

قبل از اینکه یک برنامه فراخوانی بتواند از متد scripts.run استفاده کند، باید:

  • پروژه اسکریپت را به عنوان یک فایل اجرایی API مستقر کنید. می‌توانید در صورت نیاز پروژه‌ها را مستقر، لغو استقرار و مجدداً مستقر کنید.

  • یک توکن OAuth با دامنه‌ی دسترسی مناسب برای اجرا ارائه دهید. این توکن OAuth باید تمام دامنه‌های مورد استفاده توسط اسکریپت را پوشش دهد، نه فقط آنهایی که توسط تابع فراخوانی شده استفاده می‌شوند. لیست کامل دامنه‌های دسترسی را در مرجع متد مشاهده کنید.

  • مطمئن شوید که اسکریپت و کلاینت OAuth2 برنامه‌ی فراخوانی‌کننده، یک پروژه‌ی مشترک Google Cloud را به اشتراک می‌گذارند. پروژه‌ی Cloud باید یک پروژه‌ی Cloud استاندارد باشد؛ پروژه‌های پیش‌فرض ایجاد شده برای پروژه‌های Apps Script کافی نیستند. می‌توانید از یک پروژه‌ی Cloud استاندارد جدید یا یک پروژه‌ی موجود استفاده کنید.

  • API اسکریپت برنامه‌های گوگل را در پروژه ابری فعال کنید .

متد scripts.run

متد scripts.run به اطلاعات زیر نیاز دارد:

شما می‌توانید به صورت اختیاری اسکریپت خود را طوری پیکربندی کنید که در حالت توسعه اجرا شود. این حالت به جای آخرین نسخه پیاده‌سازی شده، با آخرین نسخه ذخیره شده پروژه اسکریپت اجرا می‌شود. برای انجام این کار، مقدار بولی devMode را در بدنه درخواست روی true تنظیم کنید. فقط مالک اسکریپت می‌تواند آن را در حالت توسعه اجرا کند.

مدیریت انواع داده‌های پارامتر

استفاده از متد scripts.run در API مربوط به Apps Script معمولاً شامل ارسال داده‌ها به Apps Script به عنوان پارامترهای تابع و دریافت داده‌ها به عنوان مقادیر بازگشتی تابع است. این API فقط می‌تواند مقادیری با انواع پایه مانند رشته‌ها، آرایه‌ها، اشیاء، اعداد و مقادیر بولی را دریافت و بازگرداند. اشیاء پیچیده‌تر Apps Script مانند Document یا Sheet را نمی‌توان توسط API به پروژه اسکریپت ارسال یا از آن دریافت کرد.

وقتی برنامه‌ی فراخوانی شما به زبانی با نوع‌بندی قوی مانند جاوا نوشته شده باشد، پارامترها را به صورت لیست یا آرایه‌ای از اشیاء عمومی مربوط به این انواع پایه ارسال می‌کند. در بسیاری از موارد، می‌توانید تبدیل نوع را به صورت خودکار اعمال کنید. به عنوان مثال، می‌توان به تابعی که پارامتر عددی می‌گیرد، یک شیء جاوا Double ، Integer یا Long را به عنوان پارامتر و بدون نیاز به پردازش اضافی داد.

وقتی API پاسخ تابع را برمی‌گرداند، اغلب لازم است قبل از استفاده از مقدار برگشتی، آن را به نوع صحیح تبدیل کنید. در اینجا چند مثال مبتنی بر جاوا آورده شده است:

  • اعدادی که توسط API به یک برنامه جاوا برگردانده می‌شوند، به صورت اشیاء java.math.BigDecimal دریافت می‌شوند و ممکن است نیاز باشد به انواع Double یا int تبدیل شوند.
  • اگر تابع Apps Script آرایه‌ای از رشته‌ها را برگرداند، یک برنامه جاوا پاسخ را در یک شیء 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: پروژه ابری مشترک را راه‌اندازی کنید

هم اسکریپت شما و هم برنامه فراخوانی کننده باید یک پروژه ابری مشترک داشته باشند. این پروژه ابری می‌تواند یک پروژه موجود یا یک پروژه جدید ایجاد شده برای این منظور باشد. پس از ایجاد یک پروژه ابری، باید پروژه اسکریپت خود را برای استفاده از آن تغییر دهید .

مرحله ۲: اسکریپت را به عنوان یک فایل اجرایی API مستقر کنید

  1. پروژه Apps Script را با توابعی که می‌خواهید استفاده کنید، باز کنید.
  2. در بالا سمت راست، روی Deploy > New Deployment کلیک کنید.
  3. در کادر محاوره‌ای که باز می‌شود، روی فعال کردن انواع استقرار کلیک کنید > فایل اجرایی API .
  4. در منوی کشویی «چه کسی دسترسی دارد»، کاربرانی را که مجاز به فراخوانی توابع اسکریپت با استفاده از API اسکریپت برنامه‌ها هستند، انتخاب کنید.
  5. روی استقرار کلیک کنید.

مرحله 3: پیکربندی برنامه فراخوانی

برنامه‌ی فراخوانی‌کننده باید API مربوط به Apps Script را فعال کرده و قبل از استفاده، اعتبارنامه‌های OAuth را ایجاد کند. برای انجام این کار باید به پروژه‌ی Cloud دسترسی داشته باشید.

  1. پروژه ابری که برنامه فراخوانی و اسکریپت شما از آن استفاده می‌کند را پیکربندی کنید:
    1. API مربوط به Apps Script را در پروژه Cloud فعال کنید .
    2. صفحه رضایت OAuth را پیکربندی کنید .
    3. اعتبارنامه‌های OAuth ایجاد کنید .
  2. پروژه اسکریپت را باز کنید و در سمت چپ، روی Overview کلیک کنید. .
  3. در بخش «محدوده‌های پروژه OAuth» ، تمام محدوده‌هایی را که اسکریپت به آنها نیاز دارد، ثبت کنید.
  4. در کد برنامه فراخوانی، یک توکن دسترسی OAuth اسکریپت برای فراخوانی API ایجاد کنید. این توکنی نیست که خود API از آن استفاده کند، بلکه توکنی است که اسکریپت هنگام اجرا به آن نیاز دارد. این توکن باید با استفاده از شناسه کلاینت پروژه Cloud و محدوده‌های اسکریپتی که ثبت کرده‌اید، ساخته شود.

    کتابخانه‌های کلاینت گوگل می‌توانند در ساخت این توکن و مدیریت OAuth برای برنامه کمک کنند و معمولاً به شما این امکان را می‌دهند که با استفاده از محدوده‌های اسکریپت، یک شیء "credentials" سطح بالاتر بسازید. برای مثال‌هایی از ساخت یک شیء credentials از لیستی از محدوده‌ها، به راهنمای سریع API اسکریپت برنامه‌ها مراجعه کنید.

مرحله ۴: درخواست scripts.run را ایجاد کنید

پس از پیکربندی برنامه فراخوانی، می‌توانید فراخوانی‌های scripts.run را انجام دهید:

  1. با استفاده از شناسه استقرار، نام تابع و هر پارامتر مورد نیاز، یک درخواست API بسازید.
  2. فراخوانی scripts.run را انجام دهید و توکن OAuth اسکریپت را که در هدر ساخته‌اید (در صورت استفاده از یک درخواست POST ساده) وارد کنید، یا از یک شیء credentials که با محدوده‌های اسکریپت ساخته‌اید استفاده کنید.
  3. اجازه دهید اجرای اسکریپت تمام شود. اسکریپت‌ها می‌توانند تا شش دقیقه زمان اجرا داشته باشند، بنابراین برنامه شما باید این زمان را در نظر بگیرد.
  4. پس از اتمام، تابع اسکریپت ممکن است مقداری را برگرداند که اگر مقدار از نوع پشتیبانی شده باشد، API آن را به برنامه برمی‌گرداند.

می‌توانید نمونه‌هایی از فراخوانی‌های API scripts.run را در بخش زیر بیابید.

برای به‌روزرسانی توکن دسترسی خود، قطعه کد زیر را قبل از درخواست API مربوط به scripts.run اضافه کنید:

if (credential.getExpiresInSeconds() <= 360) {
  credential.refreshToken();
}

مثال‌های درخواست API

مثال‌های زیر نحوه‌ی ایجاد یک درخواست اجرای API مربوط به Apps Script را به زبان‌های مختلف نشان می‌دهند، که در آن‌ها یک تابع Apps Script برای چاپ لیستی از پوشه‌های موجود در دایرکتوری ریشه‌ی کاربر فراخوانی می‌شود. شناسه‌ی استقرار پروژه‌ی Apps Script که شامل تابع اجرا شده است، باید در جایی که با ENTER_YOUR_DEPLOYMENT_ID_HERE مشخص شده است، مشخص شود. این مثال‌ها به کتابخانه‌های Google API Client متکی هستند.

اسکریپت هدف

تابع موجود در این اسکریپت از Drive API استفاده می‌کند.

شما باید Drive API را در پروژه‌ای که اسکریپت در آن قرار دارد فعال کنید .

علاوه بر این، برنامه‌های فراخوانی باید اعتبارنامه‌های OAuth را ارسال کنند که شامل محدوده Drive زیر است:

  • https://www.googleapis.com/auth/drive

برنامه‌های نمونه در اینجا از کتابخانه‌های کلاینت گوگل برای ساخت اشیاء اعتبارنامه برای 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);
  }
}

جاوا اسکریپت

/**
 * 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;
  }
}

نود جی اس


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);
      });
    }
  }
}

پایتون

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 اسکریپت برنامه‌ها محدودیت‌های زیر را دارد:

  1. یک پروژه ابری رایج . اسکریپتی که فراخوانی می‌شود و برنامه فراخوانی‌کننده باید یک پروژه ابری مشترک داشته باشند. پروژه ابری باید یک پروژه ابری استاندارد باشد؛ پروژه‌های پیش‌فرض ایجاد شده برای پروژه‌های اسکریپت برنامه‌ها کافی نیستند.

  2. پارامترهای پایه و انواع بازگشتی . API نمی‌تواند اشیاء مختص اسکریپت برنامه‌ها (مانند Documents ، Blobs ، Calendars ، Drive Files و غیره) را به برنامه ارسال یا ارسال کند. فقط انواع پایه مانند رشته‌ها، آرایه‌ها، اشیاء، اعداد و مقادیر بولی را می‌توان ارسال و ارسال کرد.

  3. دامنه‌های OAuth . این API فقط می‌تواند اسکریپت‌هایی را اجرا کند که حداقل یک دامنه مورد نیاز داشته باشند. این بدان معناست که شما نمی‌توانید از API برای فراخوانی اسکریپتی استفاده کنید که نیازی به مجوز یک یا چند سرویس ندارد.

  4. بدون تریگر . API نمی‌تواند تریگرهای Apps Script ایجاد کند.