إنشاء خطوات مخصّصة في Workspace Studio

1. مقدمة

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

سنستخدم "برمجة تطبيقات Google" لإنشاء ما يلي:

  • تطبيق ويب يتضمّن نموذجًا أنيقًا يتيح للمستخدمين إرسال طلبات الدعم يدويًا
  • مشغّل مخصّص يسجّل عمليات الإرسال من "تطبيق الويب" ويشغّل مسار Workspace Studio
  • خطوة مخصّصة تحلّل وصف التذكرة الواردة لتحديد مستويات الأولوية ("عالية" أو "عادية").

أهداف الدورة التعليمية

  • كيفية ضبط إجراءات التفعيل والخطوات في ملف بيان appsscript.json
  • كيفية إنشاء notifyUri أثناء دورة حياة تسجيل الحساب المبتدئ
  • كيفية تشغيل مسار Workspace Studio بشكل آلي من رمز خارجي باستخدام UrlFetchApp
  • كيفية إنشاء متغيّرات الإخراج في خطواتك المخصّصة لتمرير البيانات إلى الخطوات اللاحقة

المتطلبات الأساسية

  • حساب Google Workspace تم تفعيل Google Workspace Studio فيه
  • يجب تفعيل الإعداد السماح بالخطوات المخصّصة غير المنشورة (الاختبارية) في "وحدة تحكّم المشرف" الخاصة بنطاقك (ضمن التطبيقات > Google Workspace > Workspace Studio > إعدادات الخطوات المخصّصة).
  • أن تكون على دراية بـ "برمجة تطبيقات Google"

2. إعداد مشروع "برمجة تطبيقات Google"

أولاً، لننشئ مشروعًا جديدًا في برمجة تطبيقات لتضمين الرمز:

  1. انتقِل إلى script.google.com وانقر على مشروع جديد.
  2. أدخِل اسم المشروع Support Quick Portal.
  3. انقر على إعدادات المشروع إعدادات المشروع في الشريط الجانبي الأيمن.
  4. ضَع علامة في المربّع بجانب عرض ملف بيان appsscript.json في المحرّر.
  5. ارجع إلى عرض محرِّر المحرّر.

بشكلٍ تلقائي، ترتبط مشاريع "برمجة التطبيقات" بمشروع Google Cloud مخفي وتلقائي. لاستدعاء Google Workspace Studio API وتجنُّب أخطاء PERMISSION_DENIED، عليك تبديل النص البرمجي إلى مشروع عادي على السحابة الإلكترونية وتفعيل واجهة برمجة التطبيقات وإعداد "منصة المصادقة من Google" بشكلٍ صحيح.

  1. اختيار مشروع أو إنشاؤه: انتقِل إلى وحدة تحكّم Google Cloud واستخدِم القائمة المنسدلة الخاصة بالمشروع في أعلى الصفحة لاختيار مشروع حالي أو إنشاء مشروع جديد.
  2. تفعيل واجهة برمجة التطبيقات: بعد تفعيل المشروع الذي اخترته في الشريط العلوي، افتح عملية تفعيل واجهة برمجة التطبيقات وانقر على التالي ثم على تفعيل Workspace Studio API.
  3. ضبط العلامة التجارية لبروتوكول OAuth: افتح صفحة العلامة التجارية لمنصة Google Auth.
    1. انقر على البدء إذا طُلب منك ذلك. (إذا سبق ضبطها، انتقِل إلى الخطوة 4).
    2. ضمن معلومات التطبيق، أدخِل اسم التطبيق والبريد الإلكتروني المخصّص لدعم المستخدمين. بعد ذلك، انقر على التالي.
    3. ضمن الجمهور، اختَر داخلي (أو خارجي إذا لم يكن الخيار "داخلي" متاحًا) وانقر على التالي.
    4. قدِّم معلومات الاتصال، ووافِق على سياسة البيانات، ثم انقر على إنشاء.
  4. ضبط إذن الوصول إلى البيانات: افتح صفحة "إذن الوصول إلى البيانات" وانقر على إضافة نطاقات أو إزالتها.
    1. ضمن إضافة النطاقات يدويًا، الصِق https://www.googleapis.com/auth/workspace.studio.trigger.
    2. انقر على إضافة إلى الجدول، ثم على تعديل، وأخيرًا على حفظ في أسفل الصفحة.
  5. انتقِل إلى إدارة الهوية وإمكانية الوصول (IAM) والمشرف > الإعدادات (أو لوحة بيانات Cloud Console) وانسخ رقم المشروع.
  6. ارجع إلى مشروع برمجة تطبيقات وانقر على إعدادات المشروع إعدادات المشروع في الشريط الجانبي الأيمن.
  7. ضمن قسم مشروع Google Cloud Platform (GCP)، انقر على تغيير المشروع.
  8. أدخِل رقم مشروع Google Cloud Platform الذي نسخته وانقر على ضبط المشروع.

ضبط ملف البيان

افتح ملف appsscript.json الذي أصبح مرئيًا حديثًا. استبدِل محتواه بالرمز البرمجي أدناه. يسرد هذا البيان بشكل صريح نطاقات OAuth المطلوبة ويحدّد إجراء التفعيل على أنّه workflowTrigger والإجراء على أنّه workflowAction ضمن الحظر studio.flows.workflowElements.

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "webapp": {
     "executeAs": "USER_ACCESSING",
     "access": "MYSELF"
  },
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/workspace.studio.trigger",
    "https://www.googleapis.com/auth/script.storage",
    "https://www.googleapis.com/auth/script.locale"
  ],
  "urlFetchWhitelist": [
    "https://workspacestudio.googleapis.com/"
  ],
  "addOns": {
    "common": {
      "name": "Support Extension",
      "logoUrl": "https://www.gstatic.com/images/branding/productlogos/buganizer/v1/192px.svg",
      "useLocaleFromApp": true
    },
    "studio": {
      "flows": {
        "workflowElements": [
          {
            "id": "supportTicketTrigger",
            "state": "ACTIVE",
            "name": "New Support Ticket",
            "description": "Fires when someone submits a ticket via the Web App.",
            "workflowTrigger": {
              "inputs": [],
              "outputs": [
                {
                  "id": "ticketTitle",
                  "description": "The title of the ticket",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                },
                {
                  "id": "ticketDescription",
                  "description": "The full description of the issue",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                }
              ],
              "onConfigFunction": "onConfigTrigger",
              "onManageFunction": "onManageTrigger"
            }
          },
          {
            "id": "urgencyDetectorStep",
            "state": "ACTIVE",
            "name": "Detect Urgency",
            "description": "Scans the ticket description to detect High or Normal urgency.",
            "workflowAction": {
              "inputs": [
                {
                  "id": "ticketDescription",
                  "description": "The ticket description",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                }
              ],
              "outputs": [
                {
                  "id": "urgencyLevel",
                  "description": "The urgency High/Normal",
                  "cardinality": "SINGLE",
                  "dataType": { "basicType": "STRING" }
                }
              ],
              "onConfigFunction": "onConfigUrgency",
              "onExecuteFunction": "onExecuteUrgency"
            }
          }
        ]
      }
    }
  }
}

3- إنشاء واجهة مستخدم لتطبيق الويب

لتوفير طريقة مرئية وقابلة للاختبار لتشغيل إجراء التفعيل بدون استخدام أدوات سطر الأوامر أو الويب هوك التابعة لجهات خارجية، سننشئ تطبيق ويب باستخدام "برمجة تطبيقات Google".

في محرّر Apps Script، انقر على إضافة ملف إضافة ملف بجانب الملفات، ثم اختَر HTML. أدخِل اسمًا له index.html (تتم إضافة الامتداد .html تلقائيًا).

ألصِق رمز واجهة المستخدم المبسّطة التالي:

<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <style>
      body { font-family: Arial, sans-serif; padding: 20px; max-width: 500px; margin: auto; }
      label { font-weight: bold; display: block; margin-top: 15px; }
      input, textarea { width: 100%; margin-top: 5px; padding: 10px; box-sizing: border-box; }
      button { background-color: #1a73e8; color: white; border: none; padding: 10px 15px; margin-top: 15px; font-weight: bold; cursor: pointer; border-radius: 4px; }
      button:hover { background-color: #1557b0; }
      #status { margin-top: 15px; color: green; font-weight: bold; }
    </style>
  </head>
  <body>
    <h2>Support Quick Portal</h2>
    <p>Submit your issue below to trigger the Workspace Studio flow.</p>

    <label for="title">Ticket Title</label>
    <input type="text" id="title" placeholder="e.g. Broken monitor">

    <label for="description">Issue Description</label>
    <textarea id="description" rows="4" placeholder="Explain your issue (use words like 'urgent' to test the step!)..."></textarea>

    <button onclick="submitTicket()">Submit Ticket</button>
    <div id="status"></div>

    <script>
      function submitTicket() {
        const title = document.getElementById('title').value;
        const description = document.getElementById('description').value;
        const statusEl = document.getElementById('status');

        if (!title || !description) {
           statusEl.style.color = "red";
           statusEl.innerText = "Please fill out both fields.";
           return;
        }

        statusEl.style.color = "green";
        statusEl.innerText = "Submitting ticket...";

        google.script.run
          .withSuccessHandler(function(response) {
            statusEl.innerText = response;
          })
          .withFailureHandler(function(error) {
            statusEl.style.color = 'red';
            statusEl.innerText = 'Error: ' + error.message;
          })
          .submitTicketToStudio(title, description);
      }
    </script>
  </body>
</html>

4. إنشاء نصوص الخلفية البرمجية

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

WebApp.gs

أعِد تسمية ملف Code.gs التلقائي في أداة التعديل إلى WebApp.gs، واحذف الرمز النموذجي myFunction، ثم انسخ الكتلة أدناه والصقها فيه. يتولّى هذا الملف استضافة تطبيق الويب:

/** --- WEB APP HOSTING --- **/

/**
 * Serves the HTML UI when users visit the web app URL.
 */
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index')
      .setTitle('Support Quick Portal')
      .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
}

/**
 * Called by the Web App form. It calls the Workspace Studio API.
 */
function submitTicketToStudio(title, description) {
  const props = PropertiesService.getUserProperties();
  const notifyUri = props.getProperty('notifyUri');
  const triggerId = props.getProperty('triggerId');

  if (!notifyUri || !triggerId) {
    throw new Error('Trigger URL missing! Make sure the flow is enabled in Workspace Studio.');
  }

  // Use ScriptApp.getOAuthToken() (requires the workspace.studio.trigger scope in manifest)
  const token = ScriptApp.getOAuthToken();
  const requestId = Utilities.getUuid();

  // Package our outputs mapped in appsscript.json
  const payload = {
    "name": "triggers/" + triggerId,
    "outputs": {
      "ticketTitle": { "stringValues": [title] },
      "ticketDescription": { "stringValues": [description] }
    },
    "requestId": requestId
  };

  const options = {
    "method": "POST",
    "contentType": "application/json",
    "headers": { "Authorization": "Bearer " + token },
    "payload": JSON.stringify(payload),
    "muteHttpExceptions": true
  };

  // Fire!
  const response = UrlFetchApp.fetch(notifyUri, options);
  if (response.getResponseCode() !== 200) {
    console.error(response.getContentText());
    throw new Error('Workspace Studio API Error: ' + response.getResponseCode());
  }
  return 'Ticket successfully routed to Workspace Studio!';
}

Starter.gs

بعد ذلك، مرِّر مؤشر الماوس فوق الملفات، وانقر على إضافة ملف إضافة ملف، ثم اختَر نص برمجي وأطلِق عليه الاسم Starter.gs. انسخوا الكتلة أدناه والصقوها فيه. يتولّى هذا الملف إعداد "إجراءات التفعيل" وإدارة دورة حياة Workspace Studio:

/** --- STARTER LOGIC --- **/

/**
 * Workspace Studio calls this to display the config UI when the user adds the starter.
 * We just return a static message since there are no properties to configure.
 */
function onConfigTrigger() {
  const section = CardService.newCardSection()
      .setHeader("Configure Web App Starter")
      .addWidget(CardService.newTextParagraph().setText("Ready to go! Once you enable this flow, deploy your Apps Script project as a Web App to submit tickets."));

  return CardService.newCardBuilder().addSection(section).build();
}

/**
 * Workspace Studio posts data here to manage the lifecycle (enable/disable the trigger).
 * Critically, we must construct or save the `notifyUri` and `triggerId` to call it later.
 */
function onManageTrigger(event) {
  const triggerCreation = event.workflow.triggerCreation;
  const triggerDeletion = event.workflow.triggerDeletion;

  const props = PropertiesService.getUserProperties();

  if (triggerCreation) {
    // Save the unique URL and ID when the flow is enabled
    // Note: notifyUri might be omitted, so fallback to constructing it dynamically
    const notifyUri = triggerCreation.notifyUri || `https://workspacestudio.googleapis.com/v1/triggers/${triggerCreation.triggerId}:fire`;
    props.setProperty('notifyUri', notifyUri);
    props.setProperty('triggerId', triggerCreation.triggerId);
  } else if (triggerDeletion) {
    // Cleanup if the user deletes the flow
    props.deleteProperty('notifyUri');
    props.deleteProperty('triggerId');
  }
}

Action.gs

بعد ذلك، مرِّر مؤشر الماوس فوق الملفات، وانقر على إضافة ملف إضافة ملف، ثم اختَر نص برمجي وأطلِق عليه الاسم Action.gs. انسخ والصق منطق "خطوة الإلحاح" أدناه فيه:

/** --- ACTION STEP LOGIC --- **/

/**
 * Workspace Studio configuration card for the Step.
 * We prompt the user to bind a variable to our "ticketDescription" input.
 */
function onConfigUrgency() {
  const input = CardService.newTextInput()
      .setFieldName("ticketDescription")
      .setTitle("Source Description")
      .setHint('Select the description variable outputted by the starter')
      .setHostAppDataSource(
        CardService.newHostAppDataSource().setWorkflowDataSource(CardService.newWorkflowDataSource().setIncludeVariables(true))
      );

  const section = CardService.newCardSection()
      .setHeader("Urgency Detector Step")
      .addWidget(input);

  return CardService.newCardBuilder().addSection(section).build();
}

/**
 * The synchronous logic executed during the Step.
 */
function onExecuteUrgency(event) {
  // Read mapped inputs
  let description = event.workflow.actionInvocation.inputs["ticketDescription"].stringValues[0];
  if (!description) description = "";

  const urgentKeywords = ['urgent', 'crash', 'broken', 'down', 'fire'];
  let urgencyLevel = 'Normal';

  if (urgentKeywords.some(keyword => description.toLowerCase().includes(keyword))) {
    urgencyLevel = 'High';
  }

  // Package outputs correctly
  const variableDataMap = {
    "urgencyLevel": AddOnsResponseService.newVariableData().addStringValue(urgencyLevel)
  };

  return outputVariables(variableDataMap);
}

/**
 * Helper strictly enforcing the expected Step object return type.
 */
function outputVariables(variableDataMap) {
  const workflowAction = AddOnsResponseService.newReturnOutputVariablesAction()
      .setVariableDataMap(variableDataMap);
  const hostAppAction = AddOnsResponseService.newHostAppAction()
      .setWorkflowAction(workflowAction);
  return AddOnsResponseService.newRenderActionBuilder()
      .setHostAppAction(hostAppAction)
      .build();
}

5- التفعيل والاختبار

بعد كتابة الرمز، لننفّذ المشروع ونضبط إعداداته.

الخطوة 1: إعادة تفويض النص البرمجي

بما أنّك ربطت مشروعًا جديدًا عاديًا على Google Cloud، عليك منح الأذونات اللازمة لنطاقات البرنامج النصي (مثل script.external_request المستخدَم من قِبل UrlFetchApp) بشكل صريح في المشروع الجديد.

  1. في محرّر Apps Script، انقر على WebApp.gs ضمن الملفات، ثم اختَر الدالة doGet من القائمة المنسدلة في شريط الأدوات العلوي.
  2. انقر على تشغيل.
  3. سيظهر طلب "يجب الحصول على إذن". انقر على مراجعة الأذونات.
  4. اختَر حسابك وامنح الإذن للبرنامج النصي. إذا ظهر لك التحذير "تطبيق لم يتم التحقّق منه"، انقر على الإعدادات المتقدّمة -> الانتقال إلى "بوابة الدعم السريع" لتجاوز التحذير.
  5. انتظِر إلى أن يعرض سجلّ التنفيذ الرسالة اكتمل التنفيذ، ما يؤكّد أنّه تم منح النص البرمجي الإذن الكامل.

الخطوة 2: اختبار الإضافة وتطبيق الويب

بما أنّ ملف appsscript.json يحدّد كلاً من الإضافة وتطبيق الويب، يمكنك ضبط كليهما في الوقت نفسه.

  1. انقر على نشر > عمليات النشر التجريبية في أعلى يسار صفحة برمجة تطبيقات.
  2. بجانب اختيار النوع، انقر على تفعيل أنواع النشر تفعيل أنواع النشر وتأكَّد من وضع علامة في المربّع بجانب إضافات Google Workspace، ثم انقر على تثبيت.
  3. انقر على تفعيل أنواع النشر تفعيل أنواع النشر مرة أخرى، وتأكَّد من وضع علامة في المربّع بجانب تطبيق الويب، وانسخ عنوان URL (سينتهي بـ /dev). هذا هو عنوان URL لتطبيق الويب Support Quick Portal الذي أنشأته.
  4. انقر على تم. قد تحتاج إلى إعادة تحميل المتصفّح قبل أن يسجّل Workspace Studio الإضافة.

الخطوة 3: ضبط إعدادات Studio Flow

  1. افتح Google Workspace Studio على studio.workspace.google.com.
  2. انقر على إضافة أداة إضافة أداة على يمين الصفحة واختَر المخطط الانسيابي.
  3. لضبط Starter، انقر على اختيار Starter، ثم اختَر إضافة الدعم المثبّتة > طلب دعم جديد. إذا ظهرت لك الرسالة "مطلوب إذن"، انقر على منح الإذن للسماح للإضافة بالوصول إلى بياناتك.
  4. بعد ذلك، ضِمن "الإجراءات"، انقر على اختيار خطوة، وانتقِل إلى "إضافة دعم"، ثم اختَر خطوة تحديد مدى الإلحاح.
  5. في وصف المصدر، انقر على إضافة المتغيّرات واختَر الخطوة 1: طلب دعم جديد > الوصف الكامل للمشكلة (ticketDescription).
  6. انقر على اختيار خطوة مرة أخرى، واختَر خطوة مضمّنة مثل Gmail > إرسال رسالة إلكترونية أو Chat > إرسال إشعار إليّ في Chat.
    • إذا اخترت Gmail:
      • إلى: أدخِل عنوان بريدك الإلكتروني.
      • الموضوع: أولوية التذكرة ticketTitle هي urgencyLevel
      • الرسالة: ticketDescription
    • إذا اخترت Chat:
      • الرسالة: أولوية التذكرة ticketTitle هي urgencyLevel. ticketDescription
  7. فعِّل سير العمل. إذا طُلب منك إعادة تسمية المسار، أدخِل اسمًا مثل Support Ticket Router واحفظه. يؤدي ذلك إلى تشغيل دورة حياة onManageTrigger، ما يؤدي إلى حفظ notifyUri.

الخطوة 4: تشغيل المحاكاة

  1. افتح عنوان URL لتطبيق الويب الذي نسخته سابقًا في الخطوة 2 من هذه الصفحة في علامة تبويب متصفّح جديدة (ينتهي بـ /dev).
  2. املأ بيانات النموذج المطلوب. احرص على تضمين كلمة رئيسية مشغّلة (مثل "عاجل" أو "تعطُّل") في الوصف.
  3. انقر على إرسال. انتظِر ظهور الرسالة "تم توجيه طلب الدعم بنجاح".
  4. اطّلِع على "سجلّ الأنشطة" في Workspace Studio أو "البريد الوارد" في Gmail أو Google Chat (حسب الإجراء الذي اخترته). من المفترض أن تظهر لك رسالة أو بريد إلكتروني مصنّف على أنّه "مهم" ومدمج بشكل أصلي باستخدام عناصر "برمجة تطبيقات Google" المخصّصة.

6. تنظيف

لتجنُّب إحداث فوضى في مساحة عملك وحسابك على Google Cloud، يمكنك تنظيف الموارد التي أنشأتها أثناء استخدام هذا الدرس البرمجي.

  1. حذف مسار Workspace Studio:
    • افتح studio.workspace.google.com.
    • ابحث عن Support Ticket Router التدفق المناسب لك.
    • انقر على قائمة النقاط الثلاث بجانبها واختَر حذف.
  2. حذف مشروع "برمجة تطبيقات Google":
    • انتقِل إلى script.google.com.
    • ابحث عن المشروع Support Quick Portal.
    • انقر على قائمة الخيارات الإضافية واختَر إزالة.
  3. إيقاف مشروع Google Cloud:
    • انتقِل إلى Google Cloud Console.
    • تأكَّد من اختيار مشروعك الجديد في القائمة المنسدلة في أعلى الصفحة.
    • انتقِل إلى إدارة الهوية وإمكانية الوصول (IAM) > الإعدادات وانقر على إيقاف.

7. الخاتمة

لقد أنشأت بنجاح منظومة متكاملة كاملة لإضافات Google Workspace Studio المخصّصة بدون الاعتماد على أي اشتراكات خارجية تابعة لجهات خارجية.

المواضيع التي تناولناها:

  • استخدام workflowTrigger إلى جانب onManageFunction لتسجيل أحداث مراحل النشاط في Google والتعامل معها بشكلٍ صحيح
  • المصادقة على طلبات الخلفية باستخدام ScriptApp.getOAuthToken() مقابل نقاط نهاية واجهة برمجة التطبيقات workspacestudio.googleapis.com
  • استخدام workflowAction لمعالجة متغيّرات الإدخال بشكل متزامن وإنشاء مخرجات لاحقة

يمكنك الآن توسيع نطاق هذه المبادئ لتشمل منصات SaaS حقيقية، مثل Jira أو Salesforce أو Zendesk، وذلك من خلال تعديل المكان الذي يتم منه تشغيل تطبيق الويب (أو ويب هوك الوارد).