Configurar e receber notificações push

Use os métodos na coleção Watches para receber notificações quando os dados mudarem nos formulários. Nesta página, você encontra uma visão geral conceitual e instruções para configurar e receber notificações push.

Informações gerais

O recurso de notificações push da API Google Forms permite que os aplicativos se inscrevam para receber notificações quando os dados forem modificados nos formulários. As notificações são entregues a um tópico do Cloud Pub/Sub, geralmente em minutos após a alteração.

Para receber notificações push, configure um tópico do Cloud Pub/Sub e forneça o nome desse tópico ao criar um relógio para o tipo de evento adequado.

Veja abaixo as definições dos principais conceitos usados nesta documentação:

  • Um destino é um lugar para onde as notificações são enviadas. O único destino aceito é um tópico do Cloud Pub/Sub.
  • Um tipo de evento é uma categoria de notificações em que um app de terceiros pode se inscrever.
  • Um watch é uma instrução para a API de formulários enviar notificações sobre um determinado tipo de evento em um determinado formulário para um destino.

Depois de criar um relógio para um tipo de evento em um formulário específico, o destino desse relógio, que é um tópico do Cloud Pub/Sub, vai receber notificações desses eventos no formulário até que o relógio expire. Seu relógio dura uma semana, mas você pode estender a duração dele a qualquer momento antes de ele expirar, fazendo uma solicitação para watches.renew().

Seu tópico do Cloud Pub/Sub só recebe notificações sobre formulários que podem ser visualizados com as credenciais fornecidas. Por exemplo, se o usuário revogar a permissão do seu aplicativo ou perder o acesso de edição a um formulário monitorado, as notificações não serão mais entregues.

Tipos de evento disponíveis

No momento, a API Google Forms oferece duas categorias de eventos:

  • EventType.SCHEMA, que notifica sobre edições no conteúdo e nas configurações de um formulário.
  • EventType.RESPONSES, que notifica quando as respostas do formulário (novas e atualizadas) são enviadas.

Respostas a notificações

As notificações são codificadas com JSON e contêm:

  • O ID do formulário de acionamento
  • O ID do relógio de acionamento
  • Tipo de evento que acionou a notificação
  • Outros campos definidos pelo Cloud Pub/Sub, como messageId e publishTime

As notificações não contêm dados detalhados de formulário ou resposta. Depois que cada notificação é recebida, é necessário fazer uma chamada de API separada para buscar dados novos. Consulte Uso sugerido para saber como fazer isso.

O snippet a seguir demonstra um exemplo de notificação para uma alteração de esquema:

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

O snippet a seguir demonstra um exemplo de notificação para uma nova resposta:

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

Configurar um tópico do Cloud Pub/Sub

As notificações são entregues aos tópicos do Cloud Pub/Sub. Pelo Cloud Pub/Sub, é possível receber notificações em um web hook ou pesquisando um endpoint de assinatura.

Para configurar um tópico do Cloud Pub/Sub, faça o seguinte:

  1. Atenda aos pré-requisitos do Cloud Pub/Sub.
  2. Configure um cliente do Cloud Pub/Sub.
  3. Revise os preços do Cloud Pub/Sub e ative o faturamento para seu projeto do Play Console.
  4. Crie um tópico do Cloud Pub/Sub de uma destas três maneiras:

  5. Crie uma assinatura no Cloud Pub/Sub para instruir o Cloud Pub/Sub a enviar suas notificações.

  6. Por fim, antes de criar relógios direcionados ao seu tópico, você precisa conceder permissão à conta de serviço de notificações do Formulários (forms-notifications@system.gserviceaccount.com) para publicar no seu tópico.

Criar um relógio

Quando você tiver um tópico em que a conta de serviço de notificações push da API Forms possa publicar, será possível criar notificações usando o método watches.create(). Esse método valida que o tópico do Cloud Pub/Sub fornecido pode ser acessado pela conta de serviço de notificações push e falhará se não for possível fazer isso. Por exemplo, se o tópico não existir ou você não tiver concedido permissão de publicação para esse tópico.

Python

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

forms/snippets/create_watch.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';

async function runSample(query) {
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const forms = google.forms({
    version: 'v1',
    auth: authClient,
  });
  const watchRequest = {
    watch: {
      target: {
        topic: {
          topicName: 'projects/<YOUR_TOPIC_PATH>',
        },
      },
      eventType: 'RESPONSES',
    },
  };
  const res = await forms.forms.watches.create({
    formId: formID,
    requestBody: watchRequest,
  });
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

Excluir um relógio

Python

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

formulários/snippets/delete_watch.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';
const watchID = '<YOUR_FORMS_WATCH_ID>';

async function runSample(query) {
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const forms = google.forms({
    version: 'v1',
    auth: authClient,
  });
  const res = await forms.forms.watches.delete({
    formId: formID,
    watchId: watchID,
  });
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

Autorização

Assim como todas as chamadas para a API Forms, as chamadas para watches.create() precisam ser autorizadas com um token de autorização. O token precisa incluir um escopo que conceda acesso de leitura aos dados sobre os quais as notificações estão sendo enviadas.

Para que as notificações sejam enviadas, o aplicativo precisa reter uma concessão do OAuth do usuário autorizado com os escopos necessários. Se o usuário desconectar o aplicativo, as notificações serão interrompidas e o smartwatch poderá ser suspenso com um erro. Para retomar as notificações depois de recuperar a autorização, consulte Renovar um relógio.

Listar os relógios de um formulário

Python

formulários/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

formulários/snippets/list_watches.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';

async function runSample(query) {
  const auth = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/forms.responses.readonly',
  });
  const forms = google.forms({
    version: 'v1',
    auth: auth,
  });
  const res = await forms.forms.watches.list({formId: formID});
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

Renovar um relógio

Python

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

formulários/snippets/renew_watch.js
'use strict';

const path = require('path');
const google = require('@googleapis/forms');
const {authenticate} = require('@google-cloud/local-auth');

const formID = '<YOUR_FORM_ID>';
const watchID = '<YOUR_FORMS_WATCH_ID>';

async function runSample(query) {
  const authClient = await authenticate({
    keyfilePath: path.join(__dirname, 'credentials.json'),
    scopes: 'https://www.googleapis.com/auth/drive',
  });
  const forms = google.forms({
    version: 'v1',
    auth: authClient,
  });
  const res = await forms.forms.watches.renew({
    formId: formID,
    watchId: watchID,
  });
  console.log(res.data);
  return res.data;
}

if (module === require.main) {
  runSample().catch(console.error);
}
module.exports = runSample;

Limitação

As notificações são limitadas. Cada smartwatch pode receber no máximo uma notificação a cada 30 segundos. Esse limite de frequência está sujeito a mudanças.

Por causa da limitação, uma única notificação pode corresponder a vários eventos. Em outras palavras, uma notificação indica que um ou mais eventos ocorreram desde a última notificação.

Limites

A qualquer momento, para um determinado formulário e tipo de evento, cada projeto do Console do Cloud pode ter:

  • até 20 visualizações no total
  • até um relógio por usuário final

Além disso, a qualquer momento, cada formulário é limitado a 50 relógios por tipo de evento no total em todos os projetos do Console do Cloud.

Um smartwatch é associado a um usuário final quando é criado ou renovado com credenciais para esse usuário. Um smartwatch é suspenso se o usuário final associado perder ou revogar o acesso do app ao formulário.

Confiabilidade

Cada relógio é notificado pelo menos uma vez após cada evento em todas as circunstâncias, exceto em extraordinários. Na grande maioria dos casos, uma notificação é entregue alguns minutos após um evento.

Erros

Se as notificações de um smartwatch não forem entregues persistentemente, o estado do relógio se tornará SUSPENDED e o campo errorType do relógio será definido. Para redefinir o estado de um smartwatch suspenso como ACTIVE e retomar as notificações, consulte Renovar um relógio.

Uso sugerido

  • usar um único tópico do Cloud Pub/Sub como destino de vários relógios.
  • Ao receber uma notificação sobre um tópico, o ID do formulário é incluído no payload da notificação. Use-o com o tipo de evento para saber quais dados buscar e em qual formulário.
  • Para buscar dados atualizados após uma notificação com EventType.RESPONSES, chame forms.responses.list().
    • Defina o filtro na solicitação como timestamp > timestamp_of_the_last_response_you_fetched.
  • Para buscar dados atualizados após uma notificação com EventType.SCHEMA, chame forms.get().