Menyiapkan dan menerima notifikasi push

Anda dapat menggunakan metode dalam koleksi Watches untuk menerima notifikasi saat data berubah dalam formulir. Halaman ini memberikan ringkasan konseptual dan petunjuk untuk menyiapkan dan menerima notifikasi push.

Ringkasan

Fitur notifikasi push Google Forms API memungkinkan aplikasi berlangganan notifikasi saat data berubah dalam formulir. Notifikasi dikirim ke topik Cloud Pub/Sub, biasanya dalam beberapa menit setelah perubahan.

Untuk menerima notifikasi push, Anda harus menyiapkan topik Cloud Pub/Sub dan memberikan nama topik tersebut saat membuat pantauan untuk jenis peristiwa yang sesuai.

Berikut adalah definisi konsep utama yang digunakan dalam dokumentasi ini:

  • Target adalah tempat pengiriman notifikasi. Satu-satunya target yang didukung adalah topik Cloud Pub/Sub.
  • Jenis peristiwa adalah kategori notifikasi yang dapat dilanggan oleh aplikasi pihak ketiga.
  • Pantauan adalah petunjuk ke Forms API untuk mengirim notifikasi untuk jenis peristiwa tertentu pada formulir tertentu ke target.

Setelah Anda membuat pantauan untuk jenis peristiwa pada formulir tertentu, target pantauan tersebut (yang merupakan topik Cloud Pub/Sub) akan menerima notifikasi dari peristiwa tersebut pada formulir tersebut hingga pantauan berakhir masa berlakunya. Pantauan Anda berlaku selama seminggu, tetapi Anda dapat memperpanjangnya kapan saja sebelum masa berlakunya berakhir dengan membuat permintaan ke watches.renew().

Topik Cloud Pub/Sub Anda hanya menerima notifikasi tentang formulir yang dapat Anda lihat dengan kredensial yang Anda berikan. Misalnya, jika pengguna mencabut izin dari aplikasi Anda atau kehilangan akses edit ke formulir yang dipantau, notifikasi tidak akan lagi dikirimkan.

Jenis peristiwa yang tersedia

Google Forms API saat ini menawarkan dua kategori peristiwa:

  • EventType.SCHEMA, yang memberi tahu tentang pengeditan pada konten dan setelan formulir.
  • EventType.RESPONSES, yang memberi tahu saat respons formulir (baru dan diperbarui) dikirimkan.

Respons notifikasi

Notifikasi dienkode dengan JSON dan berisi:

  • ID formulir yang memicu
  • ID pantauan yang memicu
  • Jenis peristiwa yang memicu notifikasi
  • Kolom lain yang ditetapkan oleh Cloud Pub/Sub, seperti messageId dan publishTime

Notifikasi tidak berisi data formulir atau respons yang mendetail. Setelah setiap notifikasi diterima, panggilan API terpisah diperlukan untuk mengambil data baru. Lihat Penggunaan yang disarankan untuk mengetahui cara melakukannya.

Cuplikan berikut menunjukkan contoh notifikasi untuk perubahan skema:

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

Cuplikan berikut menunjukkan contoh notifikasi untuk respons baru:

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

Menyiapkan topik Cloud Pub/Sub

Notifikasi dikirim ke topik Cloud Pub/Sub. Dari Cloud Pub/Sub, Anda dapat menerima notifikasi di webhook, atau dengan melakukan polling pada endpoint langganan.

Untuk menyiapkan topik Cloud Pub/Sub, lakukan hal berikut:

  1. Selesaikan Prasyarat Cloud Pub/Sub.
  2. Siapkan klien Cloud Pub/Sub.
  3. Tinjau harga Cloud Pub/Sub, dan aktifkan penagihan untuk project konsol Google Cloud Anda.
  4. Buat topik Cloud Pub/Sub dengan salah satu dari tiga cara berikut:

  5. Buat Langganan di Cloud Pub/Sub untuk memberi tahu Cloud Pub/Sub cara mengirimkan notifikasi Anda.

  6. Terakhir, sebelum membuat pantauan yang menargetkan topik Anda, Anda harus memberikan izin ke akun layanan notifikasi Formulir (forms-notifications@system.gserviceaccount.com) untuk memublikasikan ke topik Anda.

Membuat pantauan

Setelah memiliki topik yang dapat dipublikasikan oleh akun layanan notifikasi push Forms API, Anda dapat membuat notifikasi menggunakan metode watches.create(). Metode ini memvalidasi bahwa topik Cloud Pub/Sub yang diberikan dapat dijangkau oleh akun layanan notifikasi push, dan akan gagal jika tidak dapat menjangkau topik tersebut; misalnya, jika topik tidak ada atau Anda belum memberikan izin publikasi pada topik tersebut.

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
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;
}

Menghapus pantauan

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

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;
}

Otorisasi

Seperti semua panggilan ke Forms API, panggilan ke watches.create() harus diotorisasi dengan token otorisasi. Token harus menyertakan cakupan yang memberikan akses baca ke data yang notifikasinya dikirim.

Agar notifikasi dapat dikirim, aplikasi harus mempertahankan pemberian OAuth dari pengguna yang diotorisasi dengan cakupan yang diperlukan. Jika pengguna memutuskan koneksi aplikasi, notifikasi akan berhenti dan pantauan dapat ditangguhkan dengan error. Untuk melanjutkan notifikasi setelah mendapatkan kembali otorisasi, lihat Memperbarui pantauan.

Mencantumkan pantauan formulir

Python

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;
}

Memperbarui pantauan

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

forms/snippets/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;
}

Throttling

Notifikasi di-throttle—setiap pantauan dapat menerima maksimal satu notifikasi setiap tiga puluh detik. Nilai minimum frekuensi ini dapat berubah.

Karena throttling, satu notifikasi dapat sesuai dengan beberapa peristiwa. Dengan kata lain, notifikasi menunjukkan bahwa satu atau beberapa peristiwa telah terjadi sejak notifikasi terakhir.

Batas

Setiap saat, untuk jenis formulir dan peristiwa tertentu, setiap project Konsol Cloud dapat memiliki:

  • maksimal 20 pantauan
  • maksimal satu pantauan per pengguna akhir

Selain itu, setiap formulir dibatasi hingga 50 pantauan per jenis peristiwa secara total di semua project Konsol Cloud.

Pantauan dikaitkan dengan pengguna akhir saat dibuat atau diperbarui dengan kredensial untuk pengguna tersebut. Pantauan ditangguhkan jika pengguna akhir terkait kehilangan akses ke formulir atau mencabut akses aplikasi ke formulir.

Keandalan

Setiap pantauan akan diberi tahu setidaknya sekali setelah setiap peristiwa dalam semua keadaan, kecuali keadaan luar biasa. Dalam sebagian besar kasus, notifikasi dikirim dalam beberapa menit setelah peristiwa.

Error

Jika notifikasi untuk pantauan terus gagal dikirim, status pantauan akan menjadi SUSPENDED dan kolom errorType pantauan akan ditetapkan. Untuk mereset status pantauan yang ditangguhkan ke ACTIVE dan melanjutkan notifikasi, lihat Memperbarui pantauan.

Penggunaan yang disarankan

  • Gunakan satu topik Cloud Pub/Sub sebagai target dari banyak pantauan.
  • Saat menerima notifikasi pada topik, ID formulir akan disertakan dalam payload notifikasi. Gunakan dengan jenis peristiwa untuk mengetahui data yang akan diambil dan formulir tempat data tersebut diambil.
  • Untuk mengambil data yang diperbarui setelah notifikasi dengan EventType.RESPONSES, panggil forms.responses.list().
    • Tetapkan filter pada permintaan ke timestamp > timestamp_of_the_last_response_you_fetched.
  • Untuk mengambil data yang diperbarui setelah notifikasi dengan EventType.SCHEMA, panggil forms.get().