Как создать стартовый шаблон

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

Стартовый материал запускает процесс, а шаг – это отдельная задача в последовательности задач, составляющих процесс. Создав стартер, вы позволите пользователям настраивать автоматические процессы, реагирующие на события в реальном времени из вашего приложения или сервиса.

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

Требования

Включение API

Включите Google Workspace Studio API в проекте Google Cloud:

Включите API

Как перейти на стандартный облачный проект Google Cloud (только для Google Apps Script)

По умолчанию в проектах Apps Script используется стандартный облачный проект, который не поддерживает включение API и настройку окон запроса доступа OAuth. Если вы создаете стартер с помощью Apps Script, вам нужно перейти на стандартный облачный проект Google Cloud.

  1. В редакторе Apps Script нажмите Настройки проекта .
  2. В разделе Проект Google Cloud Platform (GCP) нажмите Изменить проект.
  3. Введите стандартный номер облачного проекта и нажмите Set project (Задать проект).
  4. После переключения проектов повторно авторизуйте скрипт, запустив любую функцию в редакторе и предоставив запрошенные разрешения.

Подробнее о том, как перейти на стандартный проект Google Cloud…

Как настроить окно запроса доступа OAuth

Чтобы взаимодействовать с конечной точкой Workspace Studio API, ваше приложение или сервис должны пройти аутентификацию с помощью OAuth 2.0. Настройте окно запроса доступа OAuth для своего проекта и добавьте следующую область действия OAuth:

https://www.googleapis.com/auth/workspace.studio.trigger

Эта область действия позволяет приложению вызывать Workspace Studio API и запускать рабочие процессы, которые пользователь настроил для этого триггера.

Подробнее о настройке окна запроса доступа OAuth и выборе областей действия…

Офлайн-доступ и токены обновления

Поскольку триггеры асинхронно уведомляют Workspace Studio о событии во внешнем сервисе (это может произойти через несколько часов, дней или месяцев после того, как пользователь настроит поток), ваш сервис должен предоставить действительный токен доступа OAuth 2.0 при вызове конечной точки API.

Токен доступа, предоставляемый Google в объекте события дополнения (например, во время начальной настройки или запросов обратного вызова жизненного цикла), является кратковременным и действителен только в течение 1 часа. Этого недостаточно для асинхронной активации начальных событий в будущем. Чтобы вызывать Workspace Studio API, вашему сервису требуется офлайн-токен обновления, который позволяет генерировать новые токены доступа по запросу.

Способ обработки авторизации и получения токена обновления зависит от среды выполнения дополнения:

  • Дополнения HTTP (альтернативные среды выполнения). Для дополнений HTTP ваша бэкенд-служба должна реализовывать отдельный процесс авторизации OAuth 2.0, независимый от встроенной авторизации дополнения, чтобы запрашивать офлайн-доступ (access_type=offline) и получать токен обновления.

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

    Ваша бэкенд-служба должна надежно хранить токен обновления (например, в базе данных службы вместе с triggerId) и использовать его для получения нового токена доступа при каждом возникновении события перед отправкой запросов к конечной точке API notifyUri или triggers.fire стартера.

  • Дополнения Google Apps Script. Дополнения на основе Google Apps Script, которые используют запланированные (временные) триггеры для опроса событий, могут не реализовывать независимый поток OAuth. Поскольку триггеры, основанные на расписании, выполняются непосредственно в среде выполнения Google Apps Script, Google Apps Script автоматически управляет токенами OAuth и обновляет их, используя области действия, объявленные в манифесте. Вы можете получить токен доступа OAuth с помощью ScriptApp.getOAuthToken().

Как задать стартовую страницу в файле манифеста

Чтобы задать условие, добавьте его в файл манифеста дополнения (appsscript.json) в блоке addOns.studio.flows.workflowElements. Эта конфигурация требуется как для среды выполнения Apps Script, так и для среды выполнения HTTP (альтернативной среды выполнения). Настройте элемент как workflowTrigger, а не workflowAction (используется при определении шага). Подробнее о структуре манифеста для дополнений Google Workspace…

В блоке workflowTrigger укажите:

  • inputs – переменные, которые пользователь настраивает на карточке конфигурации (например, название проекта, фильтр ресурсов и т. д.).
  • outputs – переменные, которые запускающий шаг может передавать последующим шагам в рамках рабочего процесса.
  • onConfigFunction – название функции обратного вызова, которая показывает интерфейс конфигурации пользователя.
  • onManageFunction – название функции обратного вызова, которая используется Google для обработки создания и удаления подписки Starter.

В примере кода ниже показано, как определить манифест для запуска события.

JSON

{
  "timeZone": "America/Los_Angeles",
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8",
  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/workspace.studio.trigger"
  ],
  "urlFetchWhitelist": [
    "https://workspacestudio.googleapis.com/"
  ],
  "addOns": {
    "common": {
      "name": "Trigger App",
      "logoUrl": "https://fonts.gstatic.com/s/i/short-term/release/googlesymbols/start/default/24px.svg",
      "useLocaleFromApp": true
    },
    "studio": {
      "flows": {
        "workflowElements": [
          {
            "id": "triggerDemo",
            "state": "ACTIVE",
            "name": "Event Trigger",
            "description": "Fires when a event occurs in the app.",
            "workflowTrigger": {
              "inputs": [
                {
                  "id": "projectId",
                  "description": "The project identifier to watch.",
                  "cardinality": "SINGLE",
                  "dataType": {
                    "basicType": "STRING"
                  }
                }
              ],
              "outputs": [
                {
                  "id": "eventName",
                  "description": "The name of the triggered event.",
                  "cardinality": "SINGLE",
                  "dataType": {
                    "basicType": "STRING"
                  }
                },
                {
                  "id": "eventMessage",
                  "description": "Detailed event message description.",
                  "cardinality": "SINGLE",
                  "dataType": {
                    "basicType": "STRING"
                  }
                }
              ],
              "onConfigFunction": "onConfigTrigger",
              "onManageFunction": "onManageTrigger"
            }
          }
        ]
      }
    }
  }
}

Управление жизненным циклом начальной подписки

Когда пользователь настраивает и включает процесс, содержащий ваш стартер, или если процесс отключен или удален, Google вызывает ваше дополнение, используя функцию обратного вызова onManageFunction, объявленную в манифесте.

Объект события жизненного цикла

Функция обратного вызова получает объект WorkflowEventObject, содержащий контекст действия. В частности, это:

  • Создание триггера (event.workflow.triggerCreation). Срабатывает, когда поток публикуется или включается.

    • triggerId – уникальная строка UUID, идентифицирующая этот экземпляр регистрации.

    • notifyUri – уникальный URL конечной точки REST API, связанный с этой начальной регистрацией (например, https://workspacestudio.googleapis.com/v1/triggers/YOUR_TRIGGER_ID:fire). Если параметр notifyUri отсутствует в полезной нагрузке события, создайте URL конечной точки напрямую, используя параметр triggerId: https://workspacestudio.googleapis.com/v1/triggers/{triggerId}:fire.

    • inputs – переменные, настроенные пользователем на карточке.

  • Удаление триггера (event.workflow.triggerDeletion). Срабатывает, когда триггер удаляется из автоматизации или когда вся автоматизация отключается или удаляется.

    • triggerId: уникальная строка UUID экземпляра подписки, которую нужно удалить.

Жизненный цикл подписки Alternate Runtimes (HTTP API)

Для дополнений, созданных с использованием альтернативных сред выполнения, уведомления о жизненном цикле подписки доставляются с помощью HTTP-запросов POST на настроенный URL-адрес конечной точки HTTP дополнения с названием действия, указанным функцией обратного вызова onManageFunction. Полезная нагрузка соответствует JSON-представлению WorkflowEventObject.

Подробнее о том, как создать дополнение Google Workspace, использующее конечные точки HTTP…

Как реализовать обратные вызовы жизненного цикла в Apps Script

В следующем примере Apps Script показано, как настроить карточку пользовательского интерфейса, обработать события жизненного цикла подписки с помощью onManageTrigger и отправить исходный запрос обратно в Google при возникновении события.

Apps Script

/**
 * Generates and returns the user configuration card to collect inputs.
 */
function onConfigTrigger() {
  const projectInput = CardService.newTextInput()
    .setFieldName("projectId")
    .setTitle("Project ID")
    .setHint("Enter the project identifier to watch");

  const section = CardService.newCardSection()
    .setHeader("Configure Event Trigger")
    .addWidget(projectInput);

  const card = CardService.newCardBuilder()
    .addSection(section)
    .build();

  return card;
}

/**
 * Handles subscription lifecycle events sent from Google Workspace Studio.
 *
 * @param {Object} event The Workspace Studio event object.
 */
function onManageTrigger(event) {
  const triggerCreation = event.workflow.triggerCreation;
  const triggerDeletion = event.workflow.triggerDeletion;

  if (triggerCreation) {
    const triggerId = triggerCreation.triggerId;
    // Fall back to constructing the endpoint URL if notifyUri is omitted.
    const notifyUri = triggerCreation.notifyUri ||
        ("https://workspacestudio.googleapis.com/v1/triggers/" +
         triggerId + ":fire");
    const inputs = triggerCreation.inputs;

    // Extract input values configured by the user.
    const projectId = inputs["projectId"].stringValues[0];

    // Tip: In Apps Script, use PropertiesService to persist triggerId and notifyUri:
    // const props = PropertiesService.getUserProperties();
    // props.setProperty("notifyUri", notifyUri);
    // props.setProperty("triggerId", triggerId);
    //
    // TODO: Save triggerId, notifyUri, and projectId in your database/service.
    // Your backend service listens for events related to 'projectId'
    // and calls notifyUri when those events occur.
    console.log("Trigger subscription created: " + triggerId +
                ", Notify URI: " + notifyUri +
                ", Match Project: " + projectId);

  } else if (triggerDeletion) {
    const triggerId = triggerDeletion.triggerId;

    // In Apps Script, clean up stored properties:
    // PropertiesService.getUserProperties().deleteProperty("notifyUri");
    // PropertiesService.getUserProperties().deleteProperty("triggerId");
    //
    // TODO: Remove references to triggerId from your database and stop
    // sending future event notifications to the associated notifyUri.
    console.log("Trigger subscription deleted: " + triggerId);
  }
}

/**
 * Mock function showing how your backend service fires the trigger.
 * This logic runs on your service when a watched event occurs.
 *
 * @param {string} notifyUri The stored notifyUri associated with the trigger.
 * @param {string} triggerId The stored triggerId.
 * @param {string} [userAccessToken] The OAuth 2.0 access token for the user.
 *     In Apps Script, defaults to ScriptApp.getOAuthToken().
 */
function simulateEventFire(notifyUri, triggerId, userAccessToken) {
  // In Apps Script, obtain the OAuth token using ScriptApp.getOAuthToken().
  const token = userAccessToken || ScriptApp.getOAuthToken();
  // A unique UUID version 4 is recommended as the requestId for idempotency.
  const requestId = Utilities.getUuid();

  const payload = {
    "name": "triggers/" + triggerId,
    "outputs": {
      "eventName": { "stringValues": ["EventOccurred"] },
      "eventMessage": { "stringValues": ["Hello from the service!"] }
    },
    "requestId": requestId
  };

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

  const response = UrlFetchApp.fetch(notifyUri, options);
  const responseCode = response.getResponseCode();

  if (responseCode === 200) {
    console.log("Trigger successfully fired!");
  } else if (responseCode === 404) {
    // 404 means the trigger registration is invalid or deleted.
    console.log("Trigger not found. Stop sending events for this trigger.");
    // TODO: Clean up the trigger from your backend database.
  } else if (responseCode === 429 || responseCode >= 500) {
    console.log("Temporary error (" + responseCode + "). Retry using exponential backoff.");
  } else {
    console.log("Failed to fire trigger. HTTP Code: " + responseCode + " - " + response.getContentText());
  }
}

Как использовать API Workspace Studio

Вы можете использовать Workspace Studio API (workspacestudio.googleapis.com), чтобы программно уведомлять Google о событиях запуска.

Конечные точки находятся в базовом пути: https://workspacestudio.googleapis.com/v1.

Уведомляет о начальном событии.

Активирует условие с помощью метода triggers.fire, чтобы запустить выполнение автоматизации.

  • Метод HTTP: POST.
  • Путь: /v1/triggers/{triggerId}:fire (где {triggerId} – уникальный идентификатор, полученный при создании подписки на триггер).
  • Область действия OAuth: https://www.googleapis.com/auth/workspace.studio.trigger

В примере кода ниже показано, как запустить триггер в запросе.

Запрос

{
  "name": "triggers/TRIGGER_ID",
  "outputs": {
    "eventName": {
      "stringValues": [
        "EventOccurred"
      ]
    },
    "eventMessage": {
      "stringValues": [
        "Hello from the service!"
      ]
    }
  },
  "log": {
    "textFormatElements": [
      {
        "text": "An event occurred in the app."
      }
    ]
  },
  "requestId": "UNIQUE_REQUEST_ID"
}
  • name (строка, обязательный параметр): название ресурса стартера в формате triggers/{triggerId}.
  • outputs (карта, необязательно): карта выходных переменных стартера, представляющих данные о событии. Каждое значение представляет собой объект VariableData, поддерживающий типизированные списки (например, stringValues, booleanValues, integerValues).
  • log (объект, необязательно) – представление разметки TextFormat, которое показывается в журналах активности по выполнению Workspace Studio.
  • requestId (строка, необязательно) – уникальный идентификатор (рекомендуется UUID v4), содержащий до 36 символов ASCII, который обеспечивает идемпотентность API при повторных попытках.

Ответ

В случае успеха ответ возвращает пустой объект JSON {}.

Квоты Workspace Studio API

Трафик, отправляемый в сервис workspacestudio.googleapis.com, ограничен, чтобы предотвратить перегрузку системы, обеспечить справедливое использование ресурсов и защитить общую производительность Google Workspace.

Действуют следующие квоты:

Тип квоты Квота
В минуту на проект 1000 стартовых запросов
В минуту на пользователя 100 начальных запросов

Квоты бывают следующих типов:

  • В минуту на проект. Ограничивает общее количество событий запуска, инициированных из облачного проекта Google Cloud одного разработчика, до 1000 запросов в минуту для всех пользователей, запускающих его стартеры.
  • В минуту на пользователя. Ограничивает общее количество вызовов функции starter, которые может выполнить конечный пользователь в определенном облачном проекте, до 100 запросов в минуту.

Как устранять ошибки, связанные с квотой на основе времени

Если вы превысите эти квоты, API вернет код ошибки HTTP 429 Too Many Requests (или 429 Resource Exhausted), указывающий на превышение квоты.

Чтобы устранить эти ошибки, код должен перехватывать исключение и использовать стратегию усеченной экспоненциальной выдержки. При экспоненциальной выдержке неудачные запросы повторяются с постепенно увеличивающимися задержками между попытками, включая рандомизированный джиттер (пересчет рандомизированной задержки на каждой итерации), чтобы предотвратить синхронизацию и повторную попытку нескольких клиентов одновременно:

  1. Отправьте запрос к Workspace Studio API.
  2. Если запрос не удается выполнить и возвращается ошибка 429, подождите 1 second + random_number_milliseconds и повторите попытку.
  3. Если это не поможет, подождите 2 seconds + random_number_milliseconds и повторите попытку.
  4. Если это не поможет, подождите 4 seconds + random_number_milliseconds и повторите попытку.
  5. Продолжайте этот цикл, удваивая задержку до порогового значения maximum_backoff (обычно 32 или 64 секунды).
  6. Достигнув максимального времени задержки, повторяйте попытки с этим постоянным интервалом, пока не будет достигнуто максимальное количество попыток. Затем остановите процесс и запишите ошибку в журнал.

Рекомендации

При разработке и реализации стартера учитывайте следующие рекомендации:

Передавайте отдельные события, а не пакетные списки

Настройте стартер так, чтобы он отправлял отдельное событие для каждого отдельного случая (например, для каждой обновленной записи, каждого нового сообщения или каждой назначенной задачи), а не одно событие, содержащее пакет или список элементов:

  • Соответствие встроенным триггерам. В Workspace Studio встроенные триггеры Google Workspace (например, получение письма в Gmail или присоединение пользователя к чат-группе в Google Chat) срабатывают при одном событии. Отправка событий с одним элементом соответствует этому поведению и обеспечивает согласованную и предсказуемую работу пользователей со всеми стартерами.
  • Упрощенная настройка потока. Последующие шаги в потоке обычно обрабатывают один элемент за раз. Передача событий, связанных с одним товаром, позволяет пользователям сопоставлять переменные напрямую, не добавляя сложные шаги для выполнения итераций по массивам или выполнения синтаксического анализа списков.
  • Обрабатывайте опросы и пакетные изменения по отдельности. Если ваша бэкенд-служба опрашивает внешний API и обнаруживает несколько измененных элементов за один интервал опроса, запускайте отдельное стартовое событие для каждого элемента, а не объединяйте их в одно пакетное событие.
  • Управляйте частотой событий и квотами. Поскольку отправка отдельных событий для нескольких измененных элементов может привести к резкому увеличению количества запросов, убедитесь, что ваш сервис не превышает квоты Workspace Studio API (например, ограничение в 100 запросов в минуту на пользователя). Если в результате цикла опроса получено большое количество элементов (например, более 100 измененных записей), отправляйте события постепенно, чтобы избежать ошибок 429 Too Many Requests.

Основные сценарии и пограничные случаи

При интеграции стартеров разработчики должны учитывать особенности обработки ошибок и функции среды выполнения:

  • Нет поддержки тестовых запусков. Workspace Studio не поддерживает тестовые запуски для начальных запросов.
  • Идемпотентность и предотвращение повторного воспроизведения. Хотя это и не обязательно, рекомендуется включить уникальный requestId (например, UUID) в полезную нагрузку HTTP или Apps Script. Указание requestId обеспечивает идемпотентность, позволяя API обнаруживать и игнорировать повторяющиеся уведомления, предотвращая многократный запуск потока для одного события.
  • Отключение и повторное включение рабочих процессов. Когда рабочий процесс, содержащий ваш стартер, отключается в Workspace Studio, Google отправляет событие жизненного цикла triggerDeletion в ваш обратный вызов onManageFunction. Кроме того, любые вызовы связанного метода FireTrigger возвращают код ошибки 404 Not Found (Requested entity was not found.). Ваш сервис должен реагировать на ошибки 404, прекращая будущие доставки уведомлений о событиях для этого идентификатора экземпляра стартера.

    Если пользователь позже снова включит поток, Google инициирует новый жизненный цикл подписки, вызвав обратный вызов onManageFunction с новым событием triggerCreation, содержащим новые triggerId и notifyUri. Предыдущий триггер triggerId был отключен навсегда и не будет активирован повторно, поэтому вашему сервису не нужно проверять, включен ли старый экземпляр триггера. Подробнее о том, как управлять жизненным циклом начальной подписки…

  • Идемпотентное удаление подписки. Функция обратного вызова onManageFunction должна обрабатывать запросы на удаление от Google идемпотентно. Если Google вызывает хук удаления несколько раз для одного и того же triggerId (например, при повторных попытках из-за временных потерь соединения), функция должна возвращать успешный результат.

  • Квоты на процессы. Помимо квот на Studio API Workspace Studio, на пользовательские процессы распространяются дополнительные внутренние квоты. Высокочастотные циклы или чрезмерный объем событий могут превысить пороговые значения безопасности, что приведет к автоматическому отключению потока.

  • Ошибка Permission denied (403) при активации триггера. Если вызовы конечной точки triggers.fire возвращают 403 PERMISSION_DENIED со статусом ошибки SERVICE_DISABLED, проверьте следующее:

    • Google Workspace Studio API включен в вашем проекте Google Cloud.
    • Если вы используете Google Apps Script, ваш проект связан со стандартным облачным проектом, а не с облачным проектом Apps Script по умолчанию.
    • Токен доступа OAuth включает область действия https://www.googleapis.com/auth/workspace.studio.trigger.