Configurar y recibir notificaciones push

Puedes usar los métodos de la colección Watches para recibir notificaciones cuando cambien los datos en los formularios. En esta página, se proporciona una descripción general conceptual, además de instrucciones para configurar y recibir notificaciones push.

Descripción general

La función de notificaciones push de la API de Formularios de Google permite que las aplicaciones se suscriban a notificaciones cuando los datos cambian en los formularios. Las notificaciones se envían a un tema de Cloud Pub/Sub, por lo general, unos minutos después del cambio.

A fin de recibir notificaciones push, debes configurar un tema de Cloud Pub/Sub y proporcionar el nombre de ese tema cuando crees un reloj para el tipo de evento adecuado.

A continuación, se muestran las definiciones de los conceptos clave que se usan en esta documentación:

  • Un destino es el lugar al que se envían las notificaciones. El único destino admitido es un tema de Cloud Pub/Sub.
  • Un tipo de evento es una categoría de notificaciones a la que se puede suscribir una aplicación de terceros.
  • Un reloj es una instrucción para la API de Formularios de enviar notificaciones de un tipo de evento específico de un formulario determinado a un objetivo.

Una vez que creas una supervisión para un tipo de evento en un formulario en particular, el destino de ese reloj (que es un tema de Cloud Pub/Sub) recibe notificaciones de esos eventos en ese formulario hasta que vence el reloj. Tu reloj dura una semana, pero puedes extenderlo en cualquier momento antes de que venza si envías una solicitud a watches.renew().

Tu tema de Cloud Pub/Sub solo recibe notificaciones sobre formularios que puedes ver con las credenciales que proporcionas. Por ejemplo, si el usuario revoca el permiso de tu aplicación o pierde el acceso de edición a un formulario visto, ya no se entregarán las notificaciones.

Tipos de eventos disponibles

Actualmente, la API de Formularios de Google ofrece dos categorías de eventos:

  • EventType.SCHEMA, que notifica sobre las modificaciones en el contenido y la configuración de un formulario.
  • EventType.RESPONSES, que notifica cuando se envían respuestas del formulario (nuevas y actualizadas)

Respuestas a notificaciones

Las notificaciones se codifican con JSON y contienen lo siguiente:

  • El ID del formulario de activación
  • El ID del reloj que provocó la activación
  • El tipo de evento que activó la notificación
  • Otros campos configurados por Cloud Pub/Sub, como messageId y publishTime

Las notificaciones no contienen datos detallados de las respuestas ni del formulario. Después de recibir cada notificación, se requiere una llamada a la API por separado para recuperar datos nuevos. Consulta la sección Uso sugerido para obtener información sobre cómo lograrlo.

En el siguiente fragmento, se muestra una notificación de ejemplo para un cambio de esquema:

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

En el siguiente fragmento, se muestra una notificación de ejemplo para una respuesta nueva:

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

Configurar un tema de Cloud Pub/Sub

Las notificaciones se envían a los temas de Cloud Pub/Sub. Desde Cloud Pub/Sub, puedes recibir notificaciones en un webhook o mediante un sondeo de un extremo de suscripción.

Para configurar un tema de Cloud Pub/Sub, haz lo siguiente:

  1. Completa los requisitos previos de Cloud Pub/Sub.
  2. Configura un cliente de Cloud Pub/Sub.
  3. Revisa los precios de Cloud Pub/Sub y habilita la facturación para tu proyecto de Play Console.
  4. Puedes crear un tema de Cloud Pub/Sub de las siguientes tres maneras:

  5. Crea una suscripción en Cloud Pub/Sub para indicarle a Cloud Pub/Sub cómo entregar tus notificaciones.

  6. Por último, antes de crear relojes que se orienten a tu tema, debes otorgar permiso a la cuenta de servicio de notificaciones de Formularios (forms-notifications@system.gserviceaccount.com) para publicar en tu tema.

Cómo crear un reloj

Una vez que tengas un tema en el que la cuenta de servicio de notificaciones push de la API de Formularios pueda publicar, puedes crear notificaciones con el método watches.create(). Este método valida que la cuenta de servicio de notificaciones push puede acceder al tema de Cloud Pub/Sub proporcionado y falla si no puede alcanzar el tema; por ejemplo, si el tema no existe o si no le otorgaste permiso de publicación sobre ese tema.

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;

Cómo borrar un reloj

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

form/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;

Autorización

Al igual que todas las llamadas a la API de Formularios, las llamadas a watches.create() deben autorizarse con un token de autorización. El token debe incluir un alcance que otorgue acceso de lectura a los datos sobre qué notificaciones se envían.

Para que se entreguen las notificaciones, la aplicación debe retener un permiso de OAuth del usuario autorizado con los permisos requeridos. Si el usuario desconecta la aplicación, se detendrán las notificaciones y es posible que se suspenda el reloj con un error. Para reanudar las notificaciones después de recuperar la autorización, consulta Cómo renovar un reloj.

Obtén una lista de los relojes de un formulario

Python

form/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

form/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 un reloj

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

form/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;

Limitación

Las notificaciones están limitadas: cada reloj puede recibir como máximo una notificación cada treinta segundos. Este umbral de frecuencia está sujeto a cambios.

Debido a la limitación, una sola notificación puede corresponder a varios eventos. En otras palabras, una notificación indica que se produjeron uno o más eventos desde la última notificación.

Límites

Cada proyecto de la consola de Cloud puede tener lo siguiente en cualquier momento y para un tipo de evento y un formulario determinados:

  • hasta 20 vistas en total
  • hasta un reloj por usuario final

Además, en cualquier momento, cada formulario está limitado a 50 observaciones por tipo de evento en total en todos los proyectos de la consola de Cloud.

Un reloj se asocia con un usuario final cuando se crea o renueva con las credenciales de ese usuario. Un reloj se suspende si el usuario final asociado pierde el acceso al formulario o revoca el acceso de la app al formulario.

Confiabilidad

Cada reloj recibe una notificación al menos una vez después de cada evento en todas las circunstancias, excepto las extraordinarias. En la gran mayoría de los casos, la notificación se entrega minutos después de un evento.

Errores

Si las notificaciones de un reloj no se entregan de forma persistente, el estado del reloj se convierte en SUSPENDED y se establece el campo errorType del reloj. Para restablecer el estado de un reloj suspendido a ACTIVE y reanudar las notificaciones, consulta Cómo renovar un reloj.

Uso sugerido

  • Usar un solo tema de Cloud Pub/Sub como destino de muchas visualizaciones
  • Cuando recibes una notificación sobre un tema, el ID del formulario se incluye en la carga útil de la notificación. Úsalo con el tipo de evento para saber qué datos recuperar y desde qué formulario hacerlo.
  • Para recuperar datos actualizados después de una notificación con EventType.RESPONSES, llama a forms.responses.list().
    • Establece el filtro de la solicitud en timestamp > timestamp_of_the_last_response_you_fetched.
  • Para recuperar datos actualizados después de una notificación con EventType.SCHEMA, llama a forms.get().