Создайте пользовательский шаблон рабочего пространства и пошаговые инструкции.

1. Введение

В этом практическом занятии вы создадите интерактивный портал быстрой поддержки , напрямую взаимодействующий с Google Workspace Studio. Вы научитесь создавать как пользовательский начальный шаг, так и пользовательский шаг действия, которые можно мгновенно протестировать без использования какой-либо сторонней инфраструктуры.

Мы воспользуемся Google Apps Script для создания:

  • Веб-приложение с удобным интерфейсом для ручной отправки запросов в службу поддержки.
  • Пользовательский стартовый шаблон , который перехватывает отправленные веб-приложения и запускает рабочий процесс Workspace Studio.
  • Пользовательский шаг , который анализирует описание входящего запроса для определения уровня срочности («Высокий» или «Нормальный»).

Что вы узнаете

  • Как настроить начальные этапы и шаги в манифесте файла appsscript.json .
  • Как сформировать notifyUri в процессе регистрации стартового объекта.
  • Как программно запустить процесс Workspace Studio из внешнего кода с помощью UrlFetchApp .
  • Как создавать выходные переменные в пользовательских шагах для передачи данных дальше по потоку.

Предварительные требования

  • Учетная запись Google Workspace с включенной функцией Google Workspace Studio.
  • Параметр «Разрешить неопубликованные (тестовые) пользовательские шаги» должен быть включен в консоли администратора вашего домена (в разделе Приложения > Google Workspace > Workspace Studio > Настройки пользовательских шагов ).
  • Знание Google Apps Script.

2. Настройка проекта Apps Script

Для начала создадим новый проект Apps Script для размещения нашего кода:

  1. Перейдите на сайт script.google.com и нажмите «Новый проект» .
  2. Назовите проект «Портал быстрой поддержки» .
  3. Нажмите Настройки проекта Настройки проекта находятся на левой боковой панели.
  4. Установите флажок, чтобы отображать файл манифеста appsscript.json в редакторе .
  5. Вернуться к Редактор Режим редактора .

По умолчанию проекты Apps Script связаны со скрытым проектом Google Cloud по умолчанию. Чтобы вызвать API Google Workspace Studio и избежать ошибок PERMISSION_DENIED , необходимо переключить ваш скрипт на стандартный проект Cloud, включить API и правильно настроить платформу аутентификации Google.

  1. Выберите или создайте проект: перейдите в консоль Google Cloud и используйте выпадающее меню проектов в верхней части страницы, чтобы выбрать существующий проект или создать новый.
  2. Включите API: После того, как выбранный вами проект станет активен на верхней панели, откройте процесс включения API , нажмите «Далее» и «Включить» для API Workspace Studio .
  3. Настройка фирменной символики OAuth: Откройте страницу фирменной символики платформы Google Auth .
    1. Если появится запрос, нажмите «Начать» . (Если настройка уже выполнена, перейдите к шагу 4).
    2. В разделе «Информация о приложении» введите название приложения и адрес электронной почты службы поддержки пользователей. Нажмите «Далее» .
    3. В разделе «Аудитория» выберите «Внутренняя» (или «Внешняя» , если «Внутренняя» недоступна) и нажмите «Далее» .
    4. Укажите контактную информацию, согласитесь с политикой обработки данных и нажмите «Создать» .
  4. Настройка доступа к данным: Откройте страницу «Доступ к данным» и нажмите «Добавить или удалить области доступа» .
    1. В разделе « Добавить области действия вручную» вставьте https://www.googleapis.com/auth/workspace.studio.trigger .
    2. Нажмите «Добавить в таблицу» , затем «Обновить» и, наконец, нажмите «Сохранить» внизу страницы.
  5. Перейдите в раздел IAM & Admin > Settings (или на панель управления Cloud Console) и скопируйте номер проекта .
  6. Вернитесь к своему проекту Apps Script и нажмите Настройки проекта Настройки проекта находятся на левой боковой панели.
  7. В разделе «Проект Google Cloud Platform (GCP)» нажмите «Изменить проект» .
  8. Введите скопированный номер проекта GCP и нажмите «Установить проект» .

Настройте манифест

Откройте открывшийся файл 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. Создайте пользовательский интерфейс веб-приложения.

Чтобы предоставить наглядный и проверяемый способ запуска нашего стартового приложения без использования терминальных инструментов или сторонних веб-хуков, мы создадим веб-приложение на Apps Script.

В редакторе 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 . Скопируйте и вставьте в него приведенный ниже блок. Этот файл отвечает за настройку Starter и управление жизненным циклом 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 в папке Files , затем выберите функцию doGet из выпадающего списка на верхней панели инструментов.
  2. Нажмите «Выполнить» .
  3. Появится сообщение «Требуется авторизация». Нажмите «Проверить разрешения ».
  4. Выберите свою учетную запись и авторизуйте скрипт. Если вы видите предупреждение «Непроверенное приложение», нажмите «Дополнительно» -> «Перейти на портал быстрой поддержки», чтобы обойти это предупреждение.
  5. Дождитесь появления в журнале выполнения сообщения «Выполнение завершено» , что подтверждает полную авторизацию скрипта.

Шаг 2: Тестирование расширения и веб-приложения

Поскольку в вашем appsscript.json определены как дополнение, так и веб-приложение, вы можете настроить оба варианта одновременно.

  1. В правом верхнем углу окна Apps Script нажмите «Развернуть» > «Проверить развертывания» .
  2. Рядом с пунктом «Выберите тип» нажмите Включить типы развертывания Включите типы развертывания и убедитесь, что установлен флажок «Дополнения Google Workspace» , затем нажмите «Установить» .
  3. Нажмите Включить типы развертывания Снова включите типы развертывания , убедитесь, что отмечено «Веб-приложение» , и скопируйте URL-адрес (он будет заканчиваться на /dev ). Это URL-адрес веб-приложения «Быстрый портал поддержки» , которое вы создали.
  4. Нажмите «Готово» . Возможно, вам потребуется обновить страницу в браузере, прежде чем Workspace Studio зарегистрирует надстройку.

Шаг 3: Настройка рабочего процесса Studio Flow

  1. Откройте Google Workspace Studio по адресу studio.workspace.google.com .
  2. Нажмите Добавить инструмент Добавьте инструмент слева и выберите «Поток» .
  3. Чтобы настроить стартовый пакет, нажмите «Выбрать стартовый пакет» , затем выберите установленное расширение поддержки > «Создать заявку в службу поддержки ». Если появится сообщение «Требуется разрешение», нажмите «Предоставить разрешение» , чтобы разрешить надстройке доступ к вашим данным.
  4. Далее, в разделе «Действия» нажмите «Выбрать шаг» , прокрутите до пункта «Расширение поддержки» и выберите шаг «Определение срочности» .
  5. В разделе «Описание источника» нажмите Добавлять Переменные и выберите Шаг 1: Создать заявку в службу поддержки > Полное описание проблемы ( ticketDescription ).
  6. Нажмите «Выбрать шаг» еще раз и выберите встроенный шаг, например, Gmail > Отправить электронное письмо или Чат > Уведомить меня в чате .
    • Если вы выбрали Gmail:
      • Кому: Укажите свой адрес электронной почты.
      • Тема: ticketTitle - urgencyLevel
      • Сообщение: ticketDescription
    • Если вы выбрали чат:
      • Сообщение: Заявка ticketTitle priority is urgencyLevel . ticketDescription
  7. Включите рабочий процесс. Если появится запрос на переименование потока , введите имя, например, Support Ticket Router , и сохраните. Это запустит жизненный цикл onManageTrigger , сохранив notifyUri .

Шаг 4: Запустите симуляцию

  1. Откройте в новой вкладке браузера URL-адрес веб-приложения, скопированный вами ранее на шаге 2 этой страницы (он заканчивается на /dev).
  2. Заполните форму. Убедитесь, что в описании присутствует ключевое слово-триггер (например, «срочно» или «сбой»).
  3. Нажмите «Отправить» . Дождитесь сообщения «Заявка успешно обработана».
  4. Проверьте журнал активности Workspace Studio, свою почту Gmail или Google Chat (в зависимости от выбранного действия). Вы должны увидеть сообщение или электронное письмо с пометкой «Высокая срочность» , интегрированное с помощью ваших пользовательских элементов Apps Script.

6. Уборка

Чтобы избежать захламления рабочего пространства и учетной записи Google Cloud, вы можете очистить ресурсы, созданные вами в ходе этого практического занятия.

  1. Удалите рабочий процесс Workspace Studio:
    • Откройте studio.workspace.google.com .
    • Найдите схему Support Ticket Router .
    • Нажмите на меню с тремя точками рядом с ним и выберите «Удалить» .
  2. Удалите проект Apps Script:
    • Перейдите на script.google.com .
    • Найдите проект Support Quick Portal .
    • Нажмите на меню с тремя точками и выберите «Удалить» .
  3. Закрытие проекта Google Cloud:
    • Перейдите в консоль Google Cloud .
    • Убедитесь, что в верхнем выпадающем списке выбран ваш новый проект.
    • Перейдите в раздел IAM и администрирование > Настройки и нажмите «Выключить» .

7. Заключение

Вы успешно создали полноценную экосистему для пользовательских расширений Google Workspace Studio, не прибегая к услугам сторонних подрядчиков.

Что мы обсуждали:

  • Использование workflowTrigger совместно с onManageFunction позволяет корректно фиксировать и обрабатывать события жизненного цикла Google.
  • Аутентификация запросов к бэкэнду с использованием ScriptApp.getOAuthToken() по отношению к конечным точкам API workspacestudio.googleapis.com .
  • Использование workflowAction для синхронной обработки входных переменных и генерации последующих выходных данных.

Теперь вы можете масштабировать эти принципы для интеграции с реальными SaaS-платформами, такими как Jira, Salesforce или Zendesk, изменив источник срабатывания вашего веб-приложения (или входящего веб-хука).