Настройте и получайте push-уведомления

Вы можете использовать методы из коллекции Watches для получения уведомлений об изменении данных в формах. На этой странице представлен обзор концепции и инструкции по настройке и получению push-уведомлений.

Обзор

Функция push-уведомлений API Google Forms позволяет приложениям подписываться на уведомления об изменении данных в формах. Уведомления доставляются в тему Cloud Pub/Sub , как правило, в течение нескольких минут после изменения.

Чтобы получать push-уведомления, вам необходимо настроить тему Cloud Pub/Sub и указать название этой темы при создании отслеживания для соответствующего типа события.

Ниже приведены определения основных понятий, используемых в данной документации:

  • Цель — это место, куда отправляются уведомления. Поддерживается только тема Cloud Pub/Sub.
  • Тип события — это категория уведомлений, на которые может подписаться стороннее приложение.
  • Наблюдение — это инструкция API форм по доставке уведомлений о конкретном типе событий в конкретной форме целевому объекту .

После создания наблюдения за событием определенного типа в определенной форме, целевой объект наблюдения (тема Cloud Pub/Sub) будет получать уведомления о событиях в этой форме до истечения срока наблюдения. Срок наблюдения составляет неделю, но вы можете продлить его в любой момент до истечения срока наблюдения, выполнив запрос к watches.renew() .

Ваша тема Cloud Pub/Sub получает уведомления только о формах, которые вы можете просматривать, используя предоставленные вами учётные данные. Например, если пользователь отзывает разрешение в вашем приложении или теряет доступ к редактированию отслеживаемой формы, уведомления перестают приходить.

Доступные типы событий

В настоящее время API Google Forms предлагает две категории событий:

  • EventType.SCHEMA , который уведомляет об изменениях содержимого и настроек формы.
  • EventType.RESPONSES , который уведомляет об отправке ответов формы (как новых, так и обновленных).

Ответы на уведомления

Уведомления кодируются с помощью JSON и содержат:

  • Идентификатор инициирующей формы
  • Идентификатор запускающих часов
  • Тип события, вызвавшего уведомление
  • Другие поля, устанавливаемые Cloud Pub/Sub, такие как messageId и publishTime

Уведомления не содержат подробных данных формы или ответа. После получения каждого уведомления требуется отдельный вызов API для получения новых данных. Подробнее см. в разделе «Рекомендуемое использование ».

В следующем фрагменте показан пример уведомления об изменении схемы:

{
  "attributes": {
    "eventType": "SCHEMA",
    "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
    "watchId": "892515d1-a902-444f-a2fe-42b718fe8159"
  },
  "messageId": "767437830649",
  "publishTime": "2021-03-31T01:34:08.053Z"
}

В следующем фрагменте показан пример уведомления о новом ответе:

{
  "attributes": {
    "eventType": "RESPONSES",
    "formId": "18Xgmr4XQb-l0ypfCNGQoHAw2o82foMr8J0HPHdagS6g",
    "watchId": "5d7e5690-b1ff-41ce-8afb-b469912efd7d"
  },
  "messageId": "767467004397",
  "publishTime": "2021-03-31T01:43:57.285Z"
}

Настройте тему Cloud Pub/Sub

Уведомления доставляются в темы Cloud Pub/Sub. Из Cloud Pub/Sub вы можете получать уведомления через веб-хук или путем опроса конечной точки подписки.

Чтобы настроить тему Cloud Pub/Sub, выполните следующие действия:

  1. Выполните предварительные требования Cloud Pub/Sub .
  2. Настройте клиент Cloud Pub/Sub .
  3. Ознакомьтесь с ценами Cloud Pub/Sub и включите выставление счетов для вашего проекта Developer Console.
  4. Создайте тему Cloud Pub/Sub одним из трех способов:

  5. Создайте подписку в Cloud Pub/Sub, чтобы указать Cloud Pub/Sub, как доставлять ваши уведомления.

  6. Наконец, перед созданием отслеживаний, ориентированных на вашу тему, вам необходимо предоставить учетной записи службы уведомлений Forms (forms-notifications@system.gserviceaccount.com) разрешение на публикацию в вашей теме.

Создать часы

После того, как у вас есть тема, в которой учётная запись службы push-уведомлений Forms API может публиковать уведомления, вы можете создавать уведомления с помощью метода watches.create() . Этот метод проверяет, доступен ли учётной записи службы push-уведомлений предоставленная тема Cloud Pub/Sub, и завершается ошибкой, если учётная запись службы не может получить доступ к теме, например, если тема не существует или вы не предоставили ей разрешение на публикацию.

Питон

forms/snippets/create_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)

service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

watch = {
    "watch": {
        "target": {"topic": {"topicName": "<YOUR_TOPIC_PATH>"}},
        "eventType": "RESPONSES",
    }
}

form_id = "<YOUR_FORM_ID>"

# Print JSON response after form watch creation
result = service.forms().watches().create(formId=form_id, body=watch).execute()
print(result)

Node.js

формы/фрагменты/create_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Creates a watch on a form to get notifications for new responses.
 */
async function createWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // The request body to create a watch.
  const watchRequest = {
    watch: {
      target: {
        topic: {
          // TODO: Replace with a valid Cloud Pub/Sub topic name.
          topicName: 'projects/<YOUR_TOPIC_PATH>',
        },
      },
      // The event type to watch for. 'RESPONSES' is the only supported type.
      eventType: 'RESPONSES',
    },
  };

  // Send the request to create the watch.
  const result = await formsClient.forms.watches.create({
    formId: formID,
    requestBody: watchRequest,
  });

  console.log(result.data);
  return result.data;
}

Удалить часы

Питон

forms/snippets/delete_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secret.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after deleting a form watch
result = (
    service.forms().watches().delete(formId=form_id, watchId=watch_id).execute()
)
print(result)

Node.js

forms/snippets/delete_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Deletes a watch from a form.
 */
async function deleteWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to delete the watch.
  const result = await formsClient.forms.watches.delete({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}

Авторизация

Как и все вызовы API Forms, вызовы watches.create() должны быть авторизованы с помощью токена авторизации. Токен должен включать область действия, предоставляющую доступ на чтение к данным, о которых отправляются уведомления.

  • Для изменений схемы это означает любую область, которая предоставляет доступ на чтение к формам с помощью forms.get() .
  • Для ответов это означает любую область, которая предоставляет доступ на чтение к ответам формы , например, с помощью forms.responses.list() .

Для доставки уведомлений приложение должно сохранить разрешение OAuth от авторизованного пользователя с необходимыми областями действия. Если пользователь отключит приложение, уведомления прекратятся, а часы могут быть приостановлены с ошибкой. Чтобы возобновить уведомления после восстановления авторизации, см. раздел «Продление часов» .

Перечислить часы формы

Питон

forms/snippets/list_watches.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"

# Print JSON list of form watches
result = service.forms().watches().list(formId=form_id).execute()
print(result)

Node.js

forms/snippets/list_watches.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';

/**
 * Lists the watches for a given form.
 */
async function listWatches() {
  // Authenticate with Google and get an authorized client.
  const auth = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/forms.responses.readonly',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth,
  });

  // Get the list of watches for the form.
  const result = await formsClient.forms.watches.list({
    formId: formID,
  });

  console.log(result.data);
  return result.data;
}

Обновить часы

Питон

forms/snippets/renew_watch.py
from apiclient import discovery
from httplib2 import Http
from oauth2client import client, file, tools

SCOPES = "https://www.googleapis.com/auth/drive"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"

store = file.Storage("token.json")
creds = None
if not creds or creds.invalid:
  flow = client.flow_from_clientsecrets("client_secrets.json", SCOPES)
  creds = tools.run_flow(flow, store)
service = discovery.build(
    "forms",
    "v1",
    http=creds.authorize(Http()),
    discoveryServiceUrl=DISCOVERY_DOC,
    static_discovery=False,
)

form_id = "<YOUR_FORM_ID>"
watch_id = "<YOUR_WATCH_ID>"

# Print JSON response after renewing a form watch
result = (
    service.forms().watches().renew(formId=form_id, watchId=watch_id).execute()
)
print(result)

Node.js

формы/фрагменты/renew_watch.js
import path from 'node:path';
import {authenticate} from '@google-cloud/local-auth';
import {forms} from '@googleapis/forms';

// TODO: Replace with a valid form ID.
const formID = '<YOUR_FORM_ID>';
// TODO: Replace with a valid watch ID.
const watchID = '<YOUR_FORMS_WATCH_ID>';

/**
 * Renews a watch on a form.
 */
async function renewWatch() {
  // Authenticate with Google and get an authorized client.
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });

  // Create a new Forms API client.
  const formsClient = forms({
    version: 'v1',
    auth: authClient,
  });

  // Send the request to renew the watch.
  const result = await formsClient.forms.watches.renew({
    formId: formID,
    watchId: watchID,
  });

  console.log(result.data);
  return result.data;
}

Регулирование

Уведомления ограничены: каждые часы могут получать не более одного уведомления каждые тридцать секунд. Этот порог частоты может быть изменён.

Из-за ограничения количества событий одно уведомление может соответствовать нескольким событиям. Другими словами, уведомление указывает на то, что с момента последнего уведомления произошло одно или несколько событий.

Пределы

В любой момент времени для заданной формы и типа события каждый проект Cloud Console может иметь:

  • до 20 часов всего
  • до одних часов на конечного пользователя

Кроме того, в любой момент времени каждая форма ограничена 50 просмотрами на тип события в общей сложности по всем проектам Cloud Console.

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

Надежность

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

Ошибки

Если уведомления для часов постоянно не доставляются, часы переходят в состояние SUSPENDED , и устанавливается значение поля errorType . Чтобы сбросить приостановленное состояние часов до ACTIVE и возобновить отправку уведомлений, см. раздел «Продление часов» (Renew a watch) .

Предлагаемое использование

  • Используйте одну тему Cloud Pub/Sub в качестве цели для многих просмотров.
  • При получении уведомления по теме идентификатор формы включается в полезную нагрузку уведомления. Используйте его вместе с типом события, чтобы узнать, какие данные и из какой формы их следует извлекать.
  • Чтобы получить обновленные данные после уведомления с EventType.RESPONSES , вызовите forms.responses.list() .
    • Установите фильтр для запроса на timestamp > timestamp_of_the_last_response_you_fetched .
  • Чтобы получить обновленные данные после уведомления с помощью EventType.SCHEMA , вызовите forms.get() .