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

يمكنك استخدام الطرق الواردة في مجموعة الساعات لتلقّي إشعارات عندما تتغيّر البيانات في النماذج. توفر هذه الصفحة نظرة عامة مفاهيمية وتعليمات لإعداد الإشعارات الفورية وتلقيها.

نظرة عامة

تتيح ميزة الإشعارات الفورية في واجهة برمجة تطبيقات نماذج Google للتطبيقات الاشتراك في الإشعارات عندما تتغير البيانات في النماذج. ويتم تسليم الإشعارات إلى الموضوع Cloud Pub/Sub، ويتم ذلك عادةً خلال دقائق من إجراء التغيير.

لتلقّي الإشعارات الفورية، عليك إعداد موضوع في Cloud Pub/Sub، وإدخال اسم هذا الموضوع عند إنشاء ساعة لنوع الحدث المناسب.

في ما يلي تعريفات للمفاهيم الرئيسية المستخدمة في هذه المستندات:

  • الهدف هو مكان يتم إرسال الإشعارات إليه. الهدف الوحيد المتوافق هو موضوع Cloud Pub/Sub.
  • نوع الحدث هو فئة من الإشعارات التي يمكن لتطبيق تابع لجهة خارجية الاشتراك فيها.
  • الساعة هي عبارة عن تعليمات حول واجهة برمجة تطبيقات "نماذج Google" لتسليم إشعارات لنوع حدث معيّن على نموذج معيّن إلى هدف.

بعد إنشاء ساعة لنوع حدث في نموذج معيّن، يتلقّى هدف هذه الساعة (وهو موضوع Cloud Pub/Sub) إشعارات من تلك الأحداث في ذلك النموذج إلى أن تنتهي صلاحية الساعة. تبقى ساعتك صالحة لمدة أسبوع، ولكن يمكنك تمديدها في أي وقت قبل انتهاء صلاحيتها من خلال تقديم طلب إلى watches.renew().

لن يتلقّى موضوع Cloud Pub/Sub إشعارات إلا حول النماذج التي يمكنك الاطّلاع عليها باستخدام بيانات الاعتماد التي تقدّمها. على سبيل المثال، إذا أبطل المستخدم الإذن من طلبك أو فقد الإذن بتعديل المحتوى في نموذج تمت مشاهدته، لن يتمّ إرسال الإشعارات بعد ذلك.

أنواع الأحداث المتاحة

تقدّم واجهة برمجة التطبيقات لنماذج Google حاليًا فئتَين من الأحداث:

  • 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، وتمكين الفوترة لمشروع Play Console.
  4. يمكنك إنشاء موضوع Cloud Pub/Sub بإحدى الطرق الثلاث التالية:

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

  6. أخيرًا، قبل إنشاء ساعات تستهدف موضوعك، عليك منح الإذن لحساب خدمة إشعارات "نماذج Google" (forms-notifications@system.gserviceaccount.com) للنشر على موضوعك.

إنشاء ساعة

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

حذف ساعة

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;

التفويض

مثل جميع طلبات البيانات من "نماذج Google"، يجب منح الإذن بالمكالمات الواردة إلى watches.create() باستخدام رمز مميز للتفويض. يجب أن يشتمل الرمز المميز على نطاق يمنح الإذن بالاطّلاع على البيانات المتعلقة بالإشعارات التي يتم إرسالها.

ليتم تسليم الإشعارات، يجب أن يحتفظ التطبيق بمنح الإذن باستخدام OAuth من المستخدم المُصرح له بالنطاقات المطلوبة. إذا ألغى المستخدم ربط التطبيق، تتوقّف الإشعارات وقد يتم تعليق الساعة مع ظهور خطأ. لاستئناف الإشعارات بعد استعادة التفويض، يمكنك الاطّلاع على تجديد ساعة.

إدراج ساعات نموذج

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;

تجديد ساعة

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;

تقييد

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

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

الحدود القصوى المسموح بها

بالنسبة إلى نوع حدث ونموذج معيّنَين في أي وقت، يمكن أن يتضمّن كل مشروع على 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().