הגדרה וקבלה של התראות

אתם יכולים להשתמש בשיטות באוסף Watches כדי לקבל התראות כשנתונים בטפסים משתנים. בדף הזה מובאת סקירה כללית של מושגים והוראות להגדרה ולקבלת התראות פוש.

סקירה כללית

התכונה 'התראות פוש' ב-Google Forms API מאפשרת לאפליקציות להירשם לקבלת התראות כשנתונים בטפסים משתנים. ההתראות מועברות לנושא Cloud Pub/Sub, בדרך כלל תוך דקות מהשינוי.

כדי לקבל התראות פוש, צריך להגדיר נושא Cloud Pub/Sub ולציין את שם הנושא הזה כשיוצרים מעקב אחרי סוג האירוע המתאים.

בהמשך מופיעות הגדרות של מושגי מפתח שמופיעים במסמך הזה:

  • יעד הוא מקום שאליו נשלחות התראות. היעד היחיד שנתמך הוא נושא ב-Cloud Pub/Sub.
  • סוג אירוע הוא קטגוריה של התראות שאפליקציה של צד שלישי יכולה להירשם אליהן.
  • צפייה היא הוראה ל-Forms API לשלוח התראות על סוג אירוע מסוים בטופס מסוים ליעד מסוים.

אחרי שיוצרים מעקב אחרי סוג אירוע בטופס מסוים, יעד המעקב (שהוא נושא Cloud Pub/Sub) מקבל התראות מהאירועים האלה בטופס עד שתוקף המעקב יפוג. התוקף של השעון הוא שבוע, אבל אפשר להאריך אותו בכל שלב לפני שהוא יפוג על ידי שליחת בקשה אל watches.renew().

הנושא שלכם ב-Cloud Pub/Sub מקבל רק התראות לגבי טפסים שאתם יכולים לראות באמצעות פרטי הכניסה שסיפקתם. לדוגמה, אם המשתמש מבטל את ההרשאה שניתנה לאפליקציה או מאבד את הרשאת העריכה בטופס שאחריו הוא עוקב, ההתראות לא יישלחו יותר.

סוגי האירועים הזמינים

ב-Google Forms API יש כרגע שתי קטגוריות של אירועים:

  • 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, אפשר לקבל התראות ב-webhook, או על ידי שליחת שאילתות לנקודת קצה של מינוי.

כדי להגדיר נושא ב-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) לפרסום בנושא.

יצירת שעון

אחרי שיש לכם נושא שחשבון השירות של שירות הודעות הפוש של Forms API יכול לפרסם בו, אתם יכולים ליצור התראות באמצעות השיטה watches.create(). בשיטה הזו מתבצעת אימות של נושא Cloud Pub/Sub שסופק, כדי לוודא שחשבון השירות של שירות ההתראות בדחיפה יכול להגיע אליו. אם הוא לא יכול להגיע לנושא, למשל אם הנושא לא קיים או שלא הענקתם לו הרשאת פרסום בנושא הזה, האימות ייכשל.

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

const formID = '<YOUR_FORM_ID>';

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

מחיקת שעון

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

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

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

אישור

כמו כל הקריאות ל-Forms API, גם הקריאות ל-watches.create() חייבות להיות מאושרות באמצעות אסימון הרשאה. הטוקן צריך לכלול היקף הרשאות שמעניק גישת קריאה לנתונים לגבי ההתראות שנשלחות.

כדי שההתראות יועברו, האפליקציה צריכה לשמור מענק הרשאה של OAuth מהמשתמש המורשה עם ההיקפים הנדרשים. אם המשתמש מנתק את האפליקציה, ההתראות מפסיקות והשעון עשוי להיות מושעה עם שגיאה. כדי לחדש את ההרשאה ולהמשיך לקבל התראות, אפשר לעיין במאמר בנושא חידוש ההרשאה לצפייה.

הצגת רשימת הצפיות בטופס

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

const formID = '<YOUR_FORM_ID>';

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

חידוש של שעון

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

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

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

ויסות נתונים (throttle)

יש הגבלה על מספר ההתראות – כל שעון יכול לקבל לכל היותר התראה אחת כל שלושים שניות. הסף הזה של תדירות עשוי להשתנות.

בגלל ההגבלה, התראה אחת יכולה להתייחס לכמה אירועים. במילים אחרות, התראה מציינת שהתרחשו אירוע אחד או יותר מאז ההתראה האחרונה.

מגבלות

בכל שלב, לכל פרויקט ב-Cloud Console, עבור טופס וסוג אירוע נתונים, יכולים להיות:

  • עד 20 צפיות בסך הכול
  • עד צפייה אחת לכל משתמש קצה

בנוסף, בכל שלב, כל טופס מוגבל ל-50 צפיות לכל סוג אירוע בסך הכול בכל הפרויקטים ב-Cloud Console.

שעון משויך למשתמש קצה כשהוא נוצר או מחודש עם אישורים של אותו משתמש. הצפייה מושעית אם משתמש הקצה המשויך מאבד את הגישה לטופס או מבטל את הגישה של האפליקציה לטופס.

אמינות

כל שעון מקבל התראה לפחות פעם אחת אחרי כל אירוע, אלא אם מדובר בנסיבות חריגות. ברוב המקרים, ההתראה מגיעה תוך דקות ספורות מהאירוע.

שגיאות

אם אי אפשר למסור התראות לשעון באופן עקבי, מצב השעון הופך ל-SUSPENDED והשדה errorType של השעון מוגדר. כדי לאפס את המצב של שעון מושעה ל-ACTIVE ולהמשיך לקבל התראות, אפשר לעיין במאמר בנושא חידוש שעון.

שימוש מומלץ

  • אפשר להשתמש בנושא יחיד ב-Cloud Pub/Sub כיעד של הרבה צפיות.
  • כשמקבלים התראה בנושא מסוים, מזהה הטופס נכלל במטען הייעודי (Payload) של ההתראה. משתמשים בו עם סוג האירוע כדי לדעת אילו נתונים צריך לאחזר ומאיזה טופס לאחזר אותם.
  • כדי לאחזר נתונים מעודכנים אחרי קבלת התראה עם EventType.RESPONSES, צריך להתקשר אל forms.responses.list().
    • מגדירים את המסנן בבקשה ל-timestamp > timestamp_of_the_last_response_you_fetched.
  • כדי לאחזר נתונים מעודכנים אחרי התראה עם EventType.SCHEMA, צריך להתקשר אל forms.get().