إعداد الإشعارات الفورية وتلقّيها

يمكنك استخدام الطرق في مجموعة 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

لا تحتوي الإشعارات على بيانات تفصيلية عن النموذج أو الردود. بعد تلقّي كل إشعار، يجب إجراء طلب منفصل إلى واجهة برمجة التطبيقات لجلب البيانات الجديدة. اطّلِع على الاستخدام المقترَح لمعرفة كيفية إجراء ذلك.

يعرض المقتطف التالي إشعارًا نموذجيًا بتغيير في المخطط:

{
  "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، وفعِّل الفوترة لمشروعك على Google Cloud Console.
  4. أنشِئ موضوع Cloud Pub/Sub بإحدى الطرق الثلاث التالية:

  5. أنشِئ اشتراكًا في Cloud Pub/Sub لإخبار Cloud Pub/Sub بكيفية إرسال إشعاراتك.

  6. أخيرًا، قبل إنشاء ساعات تستهدف موضوعك، عليك منح إذن النشر لحساب خدمة إشعارات "نماذج Google" (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';

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

حذف ساعة

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

التفويض

على غرار جميع الطلبات إلى 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';

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

تجديد ساعة

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

التقييد

يتم تقييد الإشعارات، إذ يمكن لكل ساعة تلقّي إشعار واحد على الأكثر كل 30 ثانية. قد يتغيّر هذا الحدّ الأقصى للتردد.

بسبب التقييد، قد يتوافق إشعار واحد مع أحداث متعددة. بعبارة أخرى، يشير الإشعار إلى وقوع حدث واحد أو أكثر منذ آخر إشعار.

الحدود

في أي وقت، لكل نموذج ونوع حدث، يمكن أن يتضمّن كل مشروع على Cloud Console ما يلي:

  • ما يصل إلى 20 ساعة إجمالاً
  • ساعة واحدة كحدّ أقصى لكل مستخدم نهائي

بالإضافة إلى ذلك، في أي وقت، يقتصر كل نموذج على 50 ساعة لكل نوع حدث إجمالاً على مستوى جميع مشاريع Cloud Console.

تتمّ ربط الساعة بمستخدم نهائي عند إنشائها أو تجديدها باستخدام بيانات اعتماد هذا المستخدم. يتم تعليق الساعة إذا فقد المستخدم النهائي المرتبط إمكانية الوصول إلى النموذج أو إذا أبطَل إمكانية وصول التطبيق إلى النموذج.

الموثوقية

يتم إشعار كل ساعة مرة واحدة على الأقل بعد كل حدث في جميع الظروف باستثناء الظروف الاستثنائية. في الغالبية العظمى من الحالات، يتم إرسال الإشعار في غضون دقائق من وقوع الحدث.

الأخطاء

إذا تعذّر إرسال إشعارات ساعة بشكل مستمر، يصبح حالة الساعة SUSPENDED ويتم ضبط حقل errorType للساعة. لإعادة ضبط حالة الساعة المعلّقة إلى ACTIVE واستئناف الإشعارات، اطّلِع على مقالة تجديد ساعة.

الاستخدام المقترَح

  • استخدِم موضوع Cloud Pub/Sub واحدًا كهدف للعديد من الساعات.
  • عند تلقّي إشعار في موضوع، يتم تضمين رقم تعريف النموذج في حمولة الإشعار. استخدِم رقم التعريف مع نوع الحدث لمعرفة البيانات التي يجب جلبها والنموذج الذي يجب جلبها منه.
  • لجلب البيانات المعدَّلة بعد إشعار يتضمّن EventType.RESPONSES، اتّصِل بـ forms.responses.list().
    • اضبط الفلتر على الطلب على timestamp > timestamp_of_the_last_response_you_fetched.
  • لجلب البيانات المعدَّلة بعد إشعار يتضمّن EventType.SCHEMA، اتّصِل بـ forms.get().