تحديثات في الوقت الفعلي

يوضّح هذا القسم كيفية إرسال تعديلات محدّدة زمنيًا من عناصر المستودع إلى Google. تتيح لك واجهة برمجة التطبيقات للتحديثات في الوقت الفعلي إرسال التحديثات وحذف العناصر في مستودع "وضع الحماية" أو "الإنتاج" في الوقت الفعلي تقريبًا.

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

المتطلبات الأساسية

يجب تضمين العناصر التالية قبل تنفيذ التعديلات في الوقت الفعلي:

  1. تم تفعيل Maps Booking API:
    • في Google Cloud Platform، انتقِل إلى واجهات برمجة التطبيقات والخدمات > المكتبة
    • ابحث عن "Google Maps Booking API"
      تحديد موقع واجهات برمجة تطبيقات الحجز في "خرائط Google"
    • ابحث عن مثيل Sandbox ("Google Maps Booking API (إصدار مطوّري البرامج)") وانقر على تفعيل
    • ابحث عن مثيل الإنتاج ("Google Maps Booking API") وانقر على تفعيل
      تفعيل Google Maps Booking API
      .
  2. يتم إنشاء حساب خدمة باستخدام دور المحرِّر لمشروع Google Cloud Platform. لمعرفة المزيد من التفاصيل، يُرجى الاطّلاع على إعداد الحساب.
  3. تتم استضافة خلاصات بيانات الإنتاج أو وضع الحماية ونقلها. للحصول على مزيد من التفاصيل، راجِع نقل البيانات بشكل مجمّع.
  4. لمصادقة واجهة برمجة التطبيقات، ننصح بتثبيت مكتبة عملاء Google باللغة التي تختارها. استخدام "https://www.googleapis.com/auth/mapsbooking" كنطاق OAuth. وتستخدم نماذج التعليمات البرمجية المضمّنة أدناه هذه المكتبات. بخلاف ذلك، ستحتاج إلى التعامل مع عمليات تبادل الرموز المميّزة يدويًا كما هو موضّح في استخدام OAuth 2.0 للوصول إلى Google APIs.

نظرة عامة

تتيح واجهة برمجة التطبيقات للتحديثات في الوقت الفعلي نوعين من العمليات. تتمثل العملية الأولى في تأكيد تحديث الكيانات الحالية. العملية الثانية هي "حذف" لإزالة الكيانات من مستودعك. يتم تنفيذ العمليتَين على مجموعة من الكيانات المدرَجة في نص الطلب. يمكنك إجراء تعديلات على ما يصل إلى 1000 كيان في طلب بيانات واحد من واجهة برمجة التطبيقات. تقبل واجهة برمجة التطبيقات جميع الطلبات الواردة وتضعها في قائمة انتظار لمزيد من المعالجة. ولذلك، تتم معالجة طلبات RTU بشكل غير متزامن.

تعمل واجهة برمجة التطبيقات للتحديثات في الوقت الفعلي في بيئتين هما: وضع الحماية والإنتاج. يتم استخدام بيئة وضع الحماية لاختبار طلبات واجهة برمجة التطبيقات وبيئة الإنتاج لتحديث المحتوى المرئي لمستخدمي الطلبات الشاملة. أسماء المضيفين لكلتا البيئتين:

  • وضع الحماية - partnerdev-mapsbooking.googleapis.com
  • مرحلة الإنتاج - mapsbooking.googleapis.com

نقاط النهاية

تعرض واجهة برمجة التطبيقات الخاصة بالتحديثات في الوقت الفعلي نقطتَي نهاية لمعالجة الطلبات الواردة لتحديثات المستودع الإعلاني:

  • UPSERT - /v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
  • حذف - /v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete

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

رقم تعريف الشريك على بوابة الشريك

باستخدام 10000001 كقيمة PARTNER_ID كمثال من لقطة الشاشة أعلاه، ستبدو عناوين URL الكاملة لإرسال طلبات واجهة برمجة التطبيقات في وضع الحماية والإنتاج في الأمثلة أدناه.

# Sandbox UPSERT
https://partnerdev-mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchPush
# Sandbox DELETE
https://partnerdev-mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchDelete
# Production UPSERT
https://mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchPush
# Production DELETE
https://mapsbooking.googleapis.com/v1alpha/inventory/partners/10000001/feeds/owg.v2/record:batchDelete

تعديل العناصر

لتعديل الكيانات في المستودع، استخدِم UPSERT نقطة نهاية، وأرسِل طلبات HTTP POST. يجب أن يتضمّن كل طلب POST المَعلمة PARTNER_ID مع حمولة JSON التي تحتوي على البيانات المنظَّمة لأي نوع كيان مدرَج في مخطط المستودع.

حمولة بيانات الطلب

نص الطلب هو كائن JSON يضم قائمة بالسجلات. يتجاوب كل سجل مع الكيان الذي يتم تحديثه. وهو يتألّف من الحقل data_record مع ترميز حمولة الكيان في Base64 والحقل generation_timestamp الذي يشير إلى وقت تعديل الكيان:

  {
    "records": [
      {
        "data_record":"BASE_64_ENCODED_ENTITY",
        "generation_timestamp":"UPDATE_TIMESTAMP"
      }
    ]
  }

في الحمولة أعلاه، استبدل ما يلي:

  • BASE_64_ENCODED_ENTITY: سلسلة JSON بترميز Base64 الخاصة بالكيان يجب أن تكون بنية JSON للكيان الذي تم فك ترميزه البنية نفسها كما في مواصفات الخلاصة، على سبيل المثال:

    {"@type":"MenuSection","name":"My Updated Menu Section","menuId":{"@id":"10824","displayOrder":1},"@id":"853705"}
  • UPDATE_TIMESTAMP: احرص على تضمين الطابع الزمني لوقت إنشاء الكيان في الأنظمة الخلفية. يُستخدم هذا الطابع الزمني لضمان ترتيب تحديثات المستودع بشكل صحيح. وإذا لم يتم تضمين هذا الحقل، سيتم ضبطه على الوقت الذي تتلقّى فيه Google الطلب. عند تعديل كيان من خلال طلب batchPush، يتم استخدام الحقل generation_timestamp لتحديد إصدارات الكيان. اطّلِع على التنسيق المتوقَّع لقيم الوقت في مخطّط المستودع الارتباطي.

يجب أن يستوفي كل طلب تعديل في الوقت الفعلي الشروط التالية:

  • يجب ألا يتجاوز حجم نص الحمولة 5 ميغابايت. على غرار الخلاصات المجمّعة، نقترح إزالة المسافات البيضاء لملاءمة المزيد من البيانات.
  • يمكن أن يضم طلب batchPush ما يصل إلى 1,000 كيان.

أمثلة

مثال 1: تعديل مطعم

لنفترض أنك تحتاج بشكل عاجل إلى تحديث رقم هاتف أحد المطاعم. يحتوي التحديث على JSON للمطعم بأكمله.

ننصحك باستخدام خلاصة مجمّعة على النحو التالي:

{
  "@type": "Restaurant",
  "@id": "restaurant12345",
  "name": "Some Restaurant",
  "url": "https://www.provider.com/somerestaurant",
  "telephone": "+16501234570",
  "streetAddress": "345 Spear St",
  "addressLocality": "San Francisco",
  "addressRegion": "CA",
  "postalCode": "94105",
  "addressCountry": "US",
  "latitude": 37.472842,
  "longitude": -122.217144
}
  

ستكون عملية التحديث في الوقت الفعلي باستخدام طريقة POST عبر HTTP على النحو التالي:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant12345",
        "name": "Some Restaurant",
        "url": "https://www.provider.com/somerestaurant",
        "telephone": "+16501234570",
        "streetAddress": "345 Spear St",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94105",
        "addressCountry": "US",
        "latitude": 37.472842,
        "longitude": -122.217144
      }
      "generation_timestamp": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Base64

هذا المثال مماثل لحمولة Base64 مرمّزة.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzNDUiLCJuYW1lIjoiU29tZSBSZXN0YXVyYW50IiwidXJsIjoiaHR0cHM6Ly93d3cucHJvdmlkZXIuY29tL3NvbWVyZXN0YXVyYW50IiwidGVsZXBob25lIjoiKzE2NTAxMjM0NTcwIiwic3RyZWV0QWRkcmVzcyI6IjM0NSBTcGVhciBTdCIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMDUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIiwibGF0aXR1ZGUiOjM3LjQ3Mjg0MiwibG9uZ2l0dWRlIjotMTIyLjIxNzE0NH0="
      "generation_timestamp": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

مثال 2: تحديث مطاعم متعددة

لتعديل كيانَين للمطعم في استدعاء واحد من واجهة برمجة التطبيقات، سيكون طلب HTTP POST على النحو التالي:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant12345",
        "name": "Some Restaurant",
        "url": "https://www.provider.com/somerestaurant",
        "telephone": "+16501235555",
        "streetAddress": "345 Spear St",
        "addressLocality": "San Francisco",
        "addressRegion": "CA",
        "postalCode": "94105",
        "addressCountry": "US",
        "latitude": 37.472842,
        "longitude": -122.217144
      },
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    },
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "restaurant123",
        "name": "Some Other Restaurant",
        "url": "https://www.provider.com/someotherrestaurant",
        "telephone": "+16501231235",
        "streetAddress": "385 Spear St",
        "addressLocality": "San Mateo",
        "addressRegion": "CA",
        "postalCode": "94115",
        "addressCountry": "US"
      },
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    }
  ]
}
    

Base64

هذا المثال مماثل لحمولة Base64 مرمّزة.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzNDUiLCJuYW1lIjoiU29tZSBSZXN0YXVyYW50IiwidXJsIjoiaHR0cHM6Ly93d3cucHJvdmlkZXIuY29tL3NvbWVyZXN0YXVyYW50IiwidGVsZXBob25lIjoiKzE2NTAxMjM1NTU1Iiwic3RyZWV0QWRkcmVzcyI6IjM0NSBTcGVhciBTdCIsImFkZHJlc3NMb2NhbGl0eSI6IlNhbiBGcmFuY2lzY28iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMDUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIiwibGF0aXR1ZGUiOjM3LjQ3Mjg0MiwibG9uZ2l0dWRlIjotMTIyLjIxNzE0NH0=",
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    },
    {
      "data_record": "eyJAdHlwZSI6IlJlc3RhdXJhbnQiLCJAaWQiOiJyZXN0YXVyYW50MTIzIiwibmFtZSI6IlNvbWUgT3RoZXIgUmVzdGF1cmFudCIsInVybCI6Imh0dHBzOi8vd3d3LnByb3ZpZGVyLmNvbS9zb21lcmVzdGF1cmFudCIsInRlbGVwaG9uZSI6IisxNjUwMTIzMTIzNSIsInN0cmVldEFkZHJlc3MiOiIzODUgU3BlYXIgU3QiLCJhZGRyZXNzTG9jYWxpdHkiOiJTYW4gTWF0ZW8iLCJhZGRyZXNzUmVnaW9uIjoiQ0EiLCJwb3N0YWxDb2RlIjoiOTQxMTUiLCJhZGRyZXNzQ291bnRyeSI6IlVTIn0=",
      "generation_timestamp": "2022-08-19T17:11:10.850Z"
    }
  ]
}
    

المثال 3: تعديل سعر عنصر في القائمة

لنفترض أنك بحاجة إلى تغيير سعر عنصر في القائمة.

يمكنك إنشاء خلاصة مجمَّعة على النحو التالي:

{
  "@type": "MenuItemOffer",
  "@id": "menuitemoffer6680262",
  "sku": "offer-cola",
  "menuItemId": "menuitem896532",
  "price": 2,
  "priceCurrency": "USD"
}

بعد ذلك سيكون التحديث في الوقت الفعلي عبر طلب POST على النحو التالي:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "MenuItemOffer",
        "@id": "menuitemoffer6680262",
        "sku": "offer-cola",
        "menuItemId": "menuitem896532",
        "price": 2,
        "priceCurrency": "USD"
      },
      "generation_timestamp": "2022-08-19T17:20:10Z"
    }
  ]
}
    

Base64

هذا المثال مماثل لحمولة Base64 مرمّزة.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchPush
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtT2ZmZXIiLCJAaWQiOiJtZW51aXRlbW9mZmVyNjY4MDI2MiIsInNrdSI6Im9mZmVyLWNvbGEiLCJtZW51SXRlbUlkIjoibWVudWl0ZW04OTY1MzIiLCJwcmljZSI6MiwicHJpY2VDdXJyZW5jeSI6IlVTRCJ9",
      "generation_timestamp": "2022-08-19T17:20:10Z"
    }
  ]
}

    

إضافة كيانات

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

حذف العناصر

لحذف كيانات من مستودعك، استخدِم حذف نقطة النهاية وأرسِل طلبات POST عبر HTTP. يجب أن يتضمّن كل طلب POST المعلَمة PARTNER_ID مع حمولة JSON التي تحتوي على معرّف أي كيان في مستودعك.

حذف حمولة الطلب

تكون بنية نص طلب الحذف منظَّمةً بشكل مشابه لطلب تعديل. ويتضمّن أيضًا قائمة بسجلّات تحتوي على الحقلَين data_record وdelete_time:

  {
    "records": [
      {
        "data_record":"BASE_64_ENCODED_REFERENCE",
        "delete_time": "DELETE_TIMESTAMP"
      }
    ]
  }

في الحمولة أعلاه، استبدل ما يلي:

  • BASE_64_ENCODED_REFERENCE: سلسلة JSON بترميز Base64 للمرجع الخاص بالكيان الذي تتم إزالته ويتألف المرجع من نوع الكيان ومعرّفه فقط، على سبيل المثال تمثيل JSON لمرجع إلى MenuSection:

    {"@type":"MenuSection","@id":"853705"}
  • DELETE_TIMESTAMP: احرص على تضمين الطابع الزمني لوقت حذف الكيان في نظام الخلفية. ويُستخدم هذا الطابع الزمني لتحديد ترتيب تطبيق الحذف على المستودع.

يمكن أن يضم طلب batchDelete ما يصل إلى 1,000 كيان.

أمثلة

مثال 1: إزالة كيانَين من MenuItem

لإزالة عنصرَي القائمة في استدعاء واحد من واجهة برمجة التطبيقات، سيكون طلب HTTP POST على النحو التالي:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "MenuItem",
        "@id": "item_1234"
      },
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
    {
      "data_record": {
        "@type": "MenuItem",
        "@id": "item_5678"
      },
      "delete_time": "2022-08-21T15:23:00.000Z"
    }
  ]
}
    

Base64

هذا المثال مماثل لحمولة Base64 مرمّزة.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtIiwiQGlkIjoiaXRlbV8xMjM0In0="
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
    {
      "data_record": "eyJAdHlwZSI6Ik1lbnVJdGVtIiwiQGlkIjoiaXRlbV81Njc4In0="
      "delete_time": "2022-08-21T15:23:00.000Z"
    },
  ]
}
    

مثال 2: حذف كيان Restaurant

ضع في اعتبارك موقف تريد فيه حذف مطعم في الخلاصة المجمّعة. يجب حذف الكيان الخاص بالمطعم فقط. لا تحذف العناصر الفرعية، مثل الخدمات والقوائم، لأنه ستتم إزالتها تلقائيًا.

نموذج طلب لحذف كيان مطعم ذي رقم التعريف https://www.provider.com/restaurant/12345:

JSON

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": {
        "@type": "Restaurant",
        "@id": "https://www.provider.com/restaurant/12345"
      },
      "delete_time": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

Base64

هذا المثال مماثل لحمولة Base64 مرمّزة.

POST v1alpha/inventory/partners/PARTNER_ID/feeds/owg.v2/record:batchDelete
Host: mapsbooking.googleapis.com
Content-Type: application/json
{
  "records": [
    {
      "data_record": "ewogICJAdHlwZSI6ICJSZXN0YXVyYW50IiwKICAiQGlkIjogImh0dHBzOi8vd3d3LnByb3ZpZGVyLmNvbS9yZXN0YXVyYW50LzEyMzQ1Igp9"
      "delete_time": "2022-08-19T17:11:10.750Z"
    }
  ]
}
    

رموز الاستجابة للتحقق من صحة البيانات من خلال واجهة برمجة التطبيقات

هناك نوعان من عمليات التحقق التي يتم إجراؤها على طلبات البيانات من واجهة برمجة التطبيقات للتحديث في الوقت الفعلي:

  • على مستوى الطلب: تتحقّق عمليات التحقّق هذه من أنّ الحمولة تتّبع المخطط upsert أو delete، وأن كل data_record تحتوي على الحقلَين @id و@type. وعمليات التحقّق هذه متزامنة ويتم عرض النتائج في نص استجابة واجهة برمجة التطبيقات. يشير رمز الاستجابة 200 ونص JSON فارغ {} إلى أنّه تم اجتياز عمليات التحقّق هذه وأنّ العناصر في هذا الطلب قد تم وضعها في قائمة انتظار المعالجة. يعني ظهور رمز استجابة مختلف عن 200 أنه تعذّر إجراء عملية واحدة أو أكثر من عمليات التحقق هذه ورفض الطلب بأكمله (بما في ذلك جميع العناصر الموجودة في الحمولة). على سبيل المثال، إذا كان data_record لا يتضمّن @type، سيتم عرض استجابة الخطأ التالية:

    {
      "error": {
        "code": 400,
        "message": "Record:{\"@id\":\"2717/86853/DELIVERY\",\"applicableServiceType\":[\"DELIVERY\",\"TAKEOUT\"],\"menuId\":[{\"@id\":\"2717/DELIVERY\",\"displayOrder\":1},{\"@id\":\"2717/TAKEOUT\",\"displayOrder\":2}],\"name\":\"Salad\",\"offeredById\":[\"2717\"]} has following errors: \nThe entity type could not be extracted from the entity value.\n",
        "status": "INVALID_ARGUMENT",
        "details": [
          {
            "@type": "type.googleapis.com/google.rpc.DebugInfo",
            "detail": "[ORIGINAL ERROR] generic::invalid_argument: Failed to parse one or more rtu records. Record:{\"@id\":\"2717/86853/DELIVERY\",\"applicableServiceType\":[\"DELIVERY\",\"TAKEOUT\"],\"menuId\":[{\"@id\":\"2717/DELIVERY\",\"displayOrder\":1},{\"@id\":\"2717/TAKEOUT\",\"displayOrder\":2}],\"name\":\"Salad\",\"offeredById\":[\"2717\"]} has following errors: \nThe entity type could not be extracted from the entity value.\n [google.rpc.error_details_ext] { message: \"Record:{\\\"@id\\\":\\\"2717/86853/DELIVERY\\\",\\\"applicableServiceType\\\":[\\\"DELIVERY\\\",\\\"TAKEOUT\\\"],\\\"menuId\\\":[{\\\"@id\\\":\\\"2717/DELIVERY\\\",\\\"displayOrder\\\":1},{\\\"@id\\\":\\\"2717/TAKEOUT\\\",\\\"displayOrder\\\":2}],\\\"name\\\":\\\"Salad\\\",\\\"offeredById\\\":[\\\"2717\\\"]} has following errors: \\nThe entity type could not be extracted from the entity value.\\n\" }"
          }
        ]
      }
    }
    
  • على مستوى الكيان: يتم التحقّق من صحة كل كيان في الحمولة مقارنةً بالمخطط العلائقي. ولا يتم الإبلاغ عن المشاكل التي تمت مواجهتها في هذه المرحلة من التحقق في استجابة واجهة برمجة التطبيقات. ويتم الإبلاغ عنها فقط في لوحة بيانات إعداد تقارير RTU فقط.

حصص واجهة برمجة التطبيقات

تحتوي تحديثات واجهة برمجة التطبيقات في الوقت الفعلي على حصة تبلغ 1,500 طلب كل 60 ثانية، أو 25 طلبًا في الثانية في المتوسط. وعند تجاوز إحدى الحصص، يستجيب Google برسالة الخطأ التالية:

{
  "error": {
    "code": 429,
    "message": "Insufficient tokens for quota ...",
    "status": "RESOURCE_EXHAUSTED",
    "details": [...]
  }
}

لمعالجة هذا الأمر، يمكنك إعادة محاولة الاستدعاء مرة أخرى على فترات زمنية أكبر إلى أن تنجح العملية. إذا كنت تستنفد الحصة بشكل منتظم، ننصحك بتضمين المزيد من الكيانات في طلب بيانات واحد من واجهة برمجة التطبيقات. يمكنك تضمين ما يصل إلى 1,000 كيان في طلب بيانات واحد من واجهة برمجة التطبيقات.

عيّنات تعليمات برمجية

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

تعديل العناصر

Node.js

يستخدم هذا الرمز مكتبة مصادقة Google لنظام Node.js.

/* Sample code for Real-time update batchPush implementation.
 *
 * Required libraries:
 * - google-auth-library
 */

const {JWT} = require('google-auth-library');

// ACTION REQUIRED: Change this to the path of the service account client secret
// file downloaded from the Google Cloud Console.
const serviceAccountJson = require('./service-account.json');

// ACTION REQUIRED: Change this to your Partner ID received from Google.
// The Partner ID is available on the Partner Portal.
const PARTNER_ID = 1234;

const HOST = {
  prod: 'https://mapsbooking.googleapis.com',
  sandbox: 'https://partnerdev-mapsbooking.googleapis.com'
};

// ACTION REQUIRED: Change to 'prod' for production
const ENV = 'sandbox';

// Feed name for Order with Google including the version.
const FEED_NAME = 'owg.v2';

// Endpoint url
const url = `${HOST[ENV]}/v1alpha/inventory/partners/${PARTNER_ID}/feeds/${
    FEED_NAME}/record:batchPush`;

/**
 * Send a Real-time update request to update/insert entities
 */
async function batchUpsert(entities) {
  /**
   * Sign JWT token using private key from service account secret file
   * provided. The client can be created without providing a service account
   * secret file by implementing Application Default Credentials.
   * https://github.com/googleapis/google-auth-library-nodejs
   */
  const client = new JWT({
    email: serviceAccountJson.client_email,
    key: serviceAccountJson.private_key,
    scopes: ['https://www.googleapis.com/auth/mapsbooking'],
  });
  const request = {records: toPushRecords(entities)};
  const body = JSON.stringify(request);
  try {
    const response = await client.request({
      method: 'POST',
      url,
      data: body,
      headers: {'Content-Type': 'application/json'}
    });
    console.log('request body:', body);
    console.log('response status:', response.status);
    console.log(
        'response data:', response.data);  // successful response returns '{}'
  } catch (error) {
    console.log('error:', error);
  }
}

/**
 * Maps array of entities to records for batch push requests
 */
const toPushRecords = (entities) => {
  return entities.map((entity) => {
    // Using dateModified to set generation_timestamp. Defaulting to the
    // current timestamp for records that do not have dateModified.
    const generation_timestamp =
        entity.dateModified ? entity.dateModified : new Date().toISOString();
    return {data_record: btoa(JSON.stringify(entity)), generation_timestamp};
  });
};

// Call batchUpsert with example entities. dateModified is optional and is
// used to hold the actual timestamp when the entity was updated/created.
batchUpsert([
  {
    '@type': 'MenuItemOffer',
    '@id': '6680261',
    'menuItemId': '18931508',
    'price': 15.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
  },
  {
    '@type': 'MenuItemOffer',
    '@id': '6680262',
    'menuItemId': '18931509',
    'price': 25.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
  }
]);

Python

يستخدم هذا الرمز مكتبة المصادقة من Google للغة Python.

"""Sample code for the Real-time update batchPush implementation."""

# Required libraries:
# - google-auth

import base64
import datetime
import json
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

# ACTION REQUIRED: Change this to the Partner ID received from Google.
# Partner ID is available on the Partner Portal.
# https://partnerdash.google.com/apps/reservewithgoogle
_PARTNER_ID = '1234'

# ACTION REQUIRED: Change this to the path of the service account client secret
# file downloaded from the Google Cloud Console.
_SERVICE_ACCOUNT_KEY_JSON_FILE = 'service-account-creds.json'

_HOST_MAP = {
    'sandbox': 'https://partnerdev-mapsbooking.googleapis.com',
    'prod': 'https://mapsbooking.googleapis.com'
}

# ACTION REQUIRED: Change to 'prod' for production
_ENV = 'sandbox'

# Feed name for Order with Google including the version.
_FEED_NAME = 'owg.v2'

_ENDPOINT = '{}/v1alpha/inventory/partners/{}/feeds/{}/record:batchPush'.format(
    _HOST_MAP[_ENV], _PARTNER_ID, _FEED_NAME)


def batch_upsert(entities):
  """Makes a batchPush request using the Real-time updates REST service.

  Args:
      entities: The list of entity objects to update or add.
  """

  # Creates credentials by providing a json file. Credentials can also be
  # provided by implementing Application Default Credentials.
  # https://googleapis.dev/python/google-auth/latest/user-guide.html
  credentials = service_account.Credentials.from_service_account_file(
      _SERVICE_ACCOUNT_KEY_JSON_FILE,
      scopes=['https://www.googleapis.com/auth/mapsbooking'])
  authorized_session = AuthorizedSession(credentials)

  # JSON request object
  batch_request = {'records': [create_push_record(x) for x in entities]}
  response = authorized_session.post(_ENDPOINT, json=batch_request)
  print('request body:', json.dumps(batch_request))
  print('response status:', response.status_code)
  print('response data:', response.text)  # successful response returns '{}'


def create_push_record(entity):
  """Creates a record from an entity for batchPush requests.

  Args:
      entity: The entity object to create the record from.

  Returns:
      The constructed record for the batchPush request payload.
  """
  data_bytes = json.dumps(entity).encode('utf-8')
  base64_bytes = base64.b64encode(data_bytes)
  # Using dateModified to set generation_timestamp. Defaulting to the
  # current timestamp for records that do not have dateModified.
  generation_timestamp = entity.dateModified if 'dateModified' in entity else datetime.datetime.now(
  ).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
  return {
      'generation_timestamp': generation_timestamp,
      'data_record': base64_bytes.decode('utf-8')
  }


# Call batch_upsert with example entities. dateModified is optional and is
# used to hold the actual timestamp when the entity was updated/created.
batch_upsert([{
    '@type': 'MenuItemOffer',
    '@id': '6680261',
    'menuItemId': '18931508',
    'price': 15.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
}, {
    '@type': 'MenuItemOffer',
    '@id': '6680262',
    'menuItemId': '18931509',
    'price': 25.5,
    'priceCurrency': 'USD',
    'applicableServiceType': ['DELIVERY', 'TAKEOUT'],
    'inventoryLevel': 0,
    'dateModified': '2022-06-19T15:43:50.970Z'
}])

Java

يستخدم هذا الرمز مكتبة المصادقة من Google للغة Java.

تم إنشاء نموذجَي رمز مصدر العميل في الحِزمتَين rtusamples.inventory وrtusamples.realtime من خلال اتّباع الخطوات الواردة في المقالة إنشاء مكتبات العملاء.

/*
 * Required Libraries:
 * - JDK >= 11
 * - google-auth-library-oauth2-http
 */
package rtusamples;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import rtusamples.inventory.MenuItemOffer;
import rtusamples.inventory.MenuItemOfferType;
import rtusamples.inventory.ServiceTypeElement;
import rtusamples.realtime.BatchPushGenericRecordRequest;
import rtusamples.realtime.GenericRecord;

/** Sample code for Real-time update batchPush implementation. */
public final class BasicPush {
  // ACTION REQUIRED: Change this to your Partner ID received from Google. The Partner ID is
  // available on the Partner Portal.
  private static final long PARTNER_ID = 12345678;

  // ACTION REQUIRED: Change this to the path of the service account client secret file downloaded
  // from the Google Cloud Console.
  private static final String JSON_KEY_FULL_PATH =
      "<path to your JSON credentials>/credentials.json";

  // ACTION REQUIRED: Change this to the endpoint that is needed.
  private static final String ENDPOINT =
      //    "https://partnerdev-mapsbooking.googleapis.com"; // for sandbox
      "https://mapsbooking.googleapis.com"; // for prod

  // Feed name for Order with Google including the version.
  private static final String FEED_NAME = "owg.v2";

  private static final ObjectMapper objectMapper = new ObjectMapper();

  private static final DateTimeFormatter TIMESTAMP_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

  private static final Charset UTF_8 = Charset.forName("UTF-8");

  public static void main(String[] args) throws Exception {

    /**
     * Create credentials from service account secret file. Alternatively, the credentials can be
     * created by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-java
     */
    // GoogleCredentials sourceCredentials =
    //     GoogleCredentials.getApplicationDefault()
    //         .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // ImpersonatedCredentials credentials =
    //     ImpersonatedCredentials.create(
    //         sourceCredentials,
    //         "fo-test@projectname.iam.gserviceaccount.com",
    //         null,
    //         Arrays.asList("https://www.googleapis.com/auth/mapsbooking"),
    //         300);

    GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream(JSON_KEY_FULL_PATH))
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // Create example MenuItemOffer entities, dateModified is optional and is used to hold
    // the actual timestamp when the entity was updated/created.
    MenuItemOffer menuItemOfferPizza = new MenuItemOffer();
    menuItemOfferPizza.setID("6680261");
    menuItemOfferPizza.setType(MenuItemOfferType.MENU_ITEM_OFFER);
    menuItemOfferPizza.setMenuItemID("18931508");
    menuItemOfferPizza.setPrice(15.5);
    menuItemOfferPizza.setPriceCurrency("USD");
    menuItemOfferPizza.setApplicableServiceType(
        new ServiceTypeElement[] {ServiceTypeElement.TAKEOUT, ServiceTypeElement.DELIVERY});
    menuItemOfferPizza.setInventoryLevel(0.0);
    menuItemOfferPizza.setDateModified("2022-10-07T13:00:00.000Z");

    MenuItemOffer menuItemOfferSalad = new MenuItemOffer();
    menuItemOfferSalad.setID("6680262");
    menuItemOfferSalad.setType(MenuItemOfferType.MENU_ITEM_OFFER);
    menuItemOfferSalad.setMenuItemID("18931509");
    menuItemOfferSalad.setPrice(25.5);
    menuItemOfferSalad.setPriceCurrency("USD");
    menuItemOfferSalad.setApplicableServiceType(
        new ServiceTypeElement[] {ServiceTypeElement.TAKEOUT, ServiceTypeElement.DELIVERY});
    menuItemOfferSalad.setInventoryLevel(0.0);
    menuItemOfferSalad.setDateModified("2022-10-07T13:00:00.000Z");

    // Example array of MenuItemOffer entities to update.
    List<MenuItemOffer> menuItemOffers = Arrays.asList(menuItemOfferPizza, menuItemOfferSalad);

    // Create list of GenericRecord from menuItemOffers.
    List<GenericRecord> menuItemOfferGenericRecords =
        menuItemOffers.stream()
            .map(
                (menuItemOffer) ->
                    toBatchPushRecord(menuItemOffer, menuItemOffer.getDateModified()))
            .collect(Collectors.toList());

    // List of records to be updated/created.
    List<GenericRecord> recordsToBeUpdated = new ArrayList<>();

    // Add list of menuItemOffer generic records.
    recordsToBeUpdated.addAll(menuItemOfferGenericRecords);

    // Request object that contains all records.
    BatchPushGenericRecordRequest batchPushRequest = new BatchPushGenericRecordRequest();
    batchPushRequest.setRecords(recordsToBeUpdated.toArray(new GenericRecord[0]));

    // Execute batchPush request.
    BasicPush basicPush = new BasicPush();
    basicPush.batchPush(batchPushRequest, credentials);
  }

  public void batchPush(
      BatchPushGenericRecordRequest batchPushRequest, GoogleCredentials credentials)
      throws IOException {

    credentials.refreshIfExpired();
    AccessToken token = credentials.getAccessToken();

    String requestBody = objectMapper.writeValueAsString(batchPushRequest);
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(
                URI.create(
                    String.format(
                        "%s/v1alpha/inventory/partners/%s/feeds/%s/record:batchPush",
                        ENDPOINT, PARTNER_ID, FEED_NAME)))
            .header("Content-Type", "application/json")
            .header("Authorization", String.format("Bearer %s", token.getTokenValue()))
            .POST(BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = null;
    try {
      response = client.send(request, BodyHandlers.ofString());
      System.out.println("Request body:" + requestBody);
      System.out.println("Response status:" + response.statusCode());
      System.out.println("Response body:" + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static <T> GenericRecord toBatchPushRecord(T entity, String dateModified) {
    GenericRecord genericRecord = new GenericRecord();
    try {
      String json = objectMapper.writeValueAsString(entity);
      genericRecord.setDataRecord(Base64.getEncoder().encodeToString(json.getBytes(UTF_8)));
      // Using dateModified to set generation_timestamp. Defaulting to the
      // current timestamp for records that do not have dateModified.
      String generationTimestamp =
          Optional.ofNullable(dateModified)
              .orElse(OffsetDateTime.now(Clock.systemUTC()).format(TIMESTAMP_FORMATTER));
      genericRecord.setGenerationTimestamp(generationTimestamp);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
    }
    return genericRecord;
  }
}

إزالة العناصر

Node.js

يستخدم هذا الرمز مكتبة مصادقة Google لنظام Node.js.

/* Sample code for Real-time update batchDelete implementation.
 *
 * Required libraries:
 * - google-auth-library
 */

const {JWT} = require('google-auth-library');

// ACTION REQUIRED: Change this to the path of the service account client secret
// file downloaded from the Google Cloud Console.
const serviceAccountJson = require('./service-account.json');

// ACTION REQUIRED: Change this to your Partner ID received from Google.
// The Partner ID is available on the Partner Portal.
const PARTNER_ID = 1234;

const HOST = {
  prod: 'https://mapsbooking.googleapis.com',
  sandbox: 'https://partnerdev-mapsbooking.googleapis.com'
};

// ACTION REQUIRED: Change to 'prod' for production
const ENV = 'sandbox';

// Feed name for Order with Google including the version.
const FEED_NAME = 'owg.v2';

// Endpoint url
const url = `${HOST[ENV]}/v1alpha/inventory/partners/${PARTNER_ID}/feeds/${
    FEED_NAME}/record:batchDelete`;


/**
 * Send a Real-time update request to delete entities
 */
async function batchDelete(entities) {
  try {
    /**
     * Sign JWT token using private key from service account secret file
     * provided. The client can be created without providing a service account
     * secret file by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-nodejs
     */
    const client = new JWT({
      email: serviceAccountJson.client_email,
      key: serviceAccountJson.private_key,
      scopes: ['https://www.googleapis.com/auth/mapsbooking'],
    });
    const request = {
      records: toDeleteRecords(entities)
    };
    const body = JSON.stringify(request);
    try {
      const response = await client.request({
        method: 'POST',
        url,
        data: body,
        headers: {'Content-Type': 'application/json'}
      });
      console.log('request body:', body);
      console.log('response status:', response.status);
      console.log('response data:', response.data);  // successful response returns '{}'
    } catch (error) {
      console.log('error:', error);
    }
  }

  /**
   * Maps array of entities to records for batch delete requests
   */
  const toDeleteRecords = (entities) => {
    return entities.map((entity) => {
      // Using dateModified to set delete_time. Defaulting to the current
      // timestamp for records that do not have dateModified.
      const delete_time =
          entity.dateModified ? entity.dateModified : new Date().toISOString();
      return {data_record: btoa(JSON.stringify(entity)), delete_time};
    });
  };

  // Call batchDelete with example entities. dateModified is optional and is
  // used to hold the actual timestamp when the entity was deleted.
  batchDelete([
    {
      '@type': 'Menu',
      '@id': '853706',
      'dateModified': '2022-06-19T15:43:50.970Z'
    },
    {
      '@type': 'Menu',
      '@id': '853705',
      'dateModified': '2022-06-19T15:13:00.280Z'
    }
  ]);

Python

يستخدم هذا الرمز مكتبة المصادقة من Google للغة Python.

"""Sample code for the Real-time update batchDelete implementation."""

# Required libraries:
# - google-auth

import base64
import datetime
import json
from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

# ACTION REQUIRED: Change this to the Partner ID received from Google.
# Partner ID is available on the Partner Portal.
# https://partnerdash.google.com/apps/reservewithgoogle
_PARTNER_ID = '1234'

# ACTION REQUIRED: Change this to the path of the service account client secret
# file downloaded from the Google Cloud Console.
_SERVICE_ACCOUNT_KEY_JSON_FILE = 'service-account-creds.json'

_HOST_MAP = {
    'sandbox': 'https://partnerdev-mapsbooking.googleapis.com',
    'prod': 'https://mapsbooking.googleapis.com'
}

# ACTION REQUIRED: Change to 'prod' for production
_ENV = 'sandbox'

# Feed name for Order with Google including the version.
_FEED_NAME = 'owg.v2'

_ENDPOINT = '{}/v1alpha/inventory/partners/{}/feeds/{}/record:batchDelete'.format(
    _HOST_MAP[_ENV], _PARTNER_ID, _FEED_NAME)


def batch_delete(entities):
  """Makes a batch delete request using the Real-time updates REST service.

  Args:
      entities: The list of entity objects to delete.
  """

  # Creates credentials by providing a json file. Credentials can also be
  # provided by implementing Application Default Credentials.
  # https://googleapis.dev/python/google-auth/latest/user-guide.html
  credentials = service_account.Credentials.from_service_account_file(
      _SERVICE_ACCOUNT_KEY_JSON_FILE,
      scopes=['https://www.googleapis.com/auth/mapsbooking'])
  authorized_session = AuthorizedSession(credentials)

  # JSON request object
  batch_request = {'records': [create_delete_record(x) for x in entities]}
  response = authorized_session.post(_ENDPOINT, json=batch_request)
  print('request body:', json.dumps(batch_request))
  print('response status:', response.status_code)
  print('response data:', response.text)  # successful response returns '{}'


def create_delete_record(entity):
  """Creates a record from an entity for batchDelete requests.

  Args:
    entity: The entity object to create the record from.

  Returns:
    The constructed record for the batchDelete request payload.
  """
  data_bytes = json.dumps(entity).encode('utf-8')
  base64_bytes = base64.b64encode(data_bytes)
  # Using dateModified to set delete_time. Defaulting to the current
  # timestamp for records that do not have dateModified.
  delete_time = entity.dateModified if 'dateModified' in entity else datetime.datetime.now(
  ).strftime('%Y-%m-%dT%H:%M:%S.%fZ')
  return {
      'delete_time': delete_time,
      'data_record': base64_bytes.decode('utf-8')
  }


# Call batch_delete with example entities. dateModified is optional and is
# used to hold the actual timestamp when the entity was deleted.
batch_delete([{
    '@type': 'Menu',
    '@id': '853706',
    'dateModified': '2022-06-19T13:10:00.000Z'
}, {
    '@type': 'Menu',
    '@id': '853705',
    'dateModified': '2022-06-19T13:30:10.000Z'
}])

Java

يستخدم هذا الرمز مكتبة المصادقة من Google للغة Java.

تم إنشاء نموذجَي رمز مصدر العميل في الحِزمتَين rtusamples.inventory وrtusamples.realtime من خلال اتّباع الخطوات الواردة في المقالة إنشاء مكتبات العملاء.

/*
 * Required Libraries:
 * - JDK >= 11
 * - google-auth-library-oauth2-http
 */
package rtusamples;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.auth.oauth2.AccessToken;
import com.google.auth.oauth2.GoogleCredentials;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublishers;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.charset.Charset;
import java.time.Clock;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import rtusamples.inventory.Menu;
import rtusamples.inventory.MenuType;
import rtusamples.realtime.BatchDeleteGenericRecordsRequest;
import rtusamples.realtime.GenericDeleteRecord;

/** Sample code for the Real-time update batchDelete implementation. */
public final class BasicDelete {
  // ACTION REQUIRED: Change this to your Partner ID received from Google. The Partner ID is
  // available on the Partner Portal.
  private static final long PARTNER_ID = 123456789;

  // ACTION REQUIRED: Change this to the path of the service account client secret file downloaded
  // from the Google Cloud Console.
  private static final String JSON_KEY_FULL_PATH =
      "<path to your JSON credentials>/credentials.json";

  // ACTION REQUIRED: Change this to the endpoint that is needed.
  private static final String ENDPOINT =
      "https://partnerdev-mapsbooking.googleapis.com"; // for sandbox
  // "https://mapsbooking.googleapis.com" // for prod

  // Feed name for Order with Google including the version.
  private static final String FEED_NAME = "owg.v2";

  private static final ObjectMapper objectMapper = new ObjectMapper();

  private static final DateTimeFormatter TIMESTAMP_FORMATTER =
      DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss[.SSS]'Z'");

  private static final Charset UTF_8 = Charset.forName("UTF-8");

  public static void main(String[] args) throws Exception {

    /**
     * Create credentials from service account secret file. Alternatively, the credentials can be
     * created by implementing Application Default Credentials.
     * https://github.com/googleapis/google-auth-library-java
     */
    // GoogleCredentials sourceCredentials =
    //     GoogleCredentials.getApplicationDefault()
    //         .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // ImpersonatedCredentials credentials =
    //     ImpersonatedCredentials.create(
    //         sourceCredentials,
    //         "fo-test@projectname.iam.gserviceaccount.com",
    //         null,
    //         Arrays.asList("https://www.googleapis.com/auth/mapsbooking"),
    //         300);

    GoogleCredentials credentials =
        GoogleCredentials.fromStream(new FileInputStream(JSON_KEY_FULL_PATH))
            .createScoped(Arrays.asList("https://www.googleapis.com/auth/mapsbooking"));

    // Create example Menu entities, dateModified is optional and is used to hold
    // the actual timestamp when the entity was deleted.
    Menu menuLunch = new Menu();
    menuLunch.setID("853705");
    menuLunch.setType(MenuType.MENU);
    menuLunch.setDateModified("2022-09-19T13:10:00.000Z");

    Menu menuDinner = new Menu();
    menuDinner.setID("853706");
    menuDinner.setType(MenuType.MENU);
    menuDinner.setDateModified("2022-09-19T13:13:10.000Z");

    // Example array of Menu entities to update.
    List<Menu> menus = Arrays.asList(menuLunch, menuDinner);

    // Create list of GenericDeleteRecord from menus.
    List<GenericDeleteRecord> menuGenericDeleteRecords =
        menus.stream()
            .map((menu) -> toBatchDeleteRecord(menu, menu.getDateModified()))
            .collect(Collectors.toList());

    // List of records to be deleted.
    List<GenericDeleteRecord> recordsToBeDeleted = new ArrayList<>();

    // Add list of menu generic records.
    recordsToBeDeleted.addAll(menuGenericDeleteRecords);

    // Request object that contains all records.
    BatchDeleteGenericRecordsRequest batchDeleteRequest = new BatchDeleteGenericRecordsRequest();
    batchDeleteRequest.setRecords(recordsToBeDeleted.toArray(new GenericDeleteRecord[0]));

    // Execute batchDelete request.
    BasicDelete basicDelete = new BasicDelete();
    basicDelete.batchDelete(batchDeleteRequest, credentials);
  }

  public void batchDelete(
      BatchDeleteGenericRecordsRequest batchDeleteRequest, GoogleCredentials credentials)
      throws IOException {

    credentials.refreshIfExpired();
    AccessToken token = credentials.getAccessToken();

    String requestBody = objectMapper.writeValueAsString(batchDeleteRequest);
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request =
        HttpRequest.newBuilder()
            .uri(
                URI.create(
                    String.format(
                        "%s/v1alpha/inventory/partners/%s/feeds/%s/record:batchDelete",
                        ENDPOINT, PARTNER_ID, FEED_NAME)))
            .header("Content-Type", "application/json")
            .header("Authorization", String.format("Bearer %s", token.getTokenValue()))
            .POST(BodyPublishers.ofString(requestBody))
            .build();

    HttpResponse<String> response = null;
    try {
      response = client.send(request, BodyHandlers.ofString());
      System.out.println("Request body:" + requestBody);
      System.out.println("Response status:" + response.statusCode());
      System.out.println("Response body:" + response.body());
    } catch (IOException | InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static <T> GenericDeleteRecord toBatchDeleteRecord(T entity, String dateModified) {
    GenericDeleteRecord genericRecord = new GenericDeleteRecord();
    try {
      String json = objectMapper.writeValueAsString(entity);
      genericRecord.setDataRecord(Base64.getEncoder().encodeToString(json.getBytes(UTF_8)));
      // Using dateModified to set delete_time. Defaulting to the current
      // timestamp for records that do not have dateModified.
      String deleteTime =
          Optional.ofNullable(dateModified)
              .orElse(OffsetDateTime.now(Clock.systemUTC()).format(TIMESTAMP_FORMATTER));
      genericRecord.setDeleteTime(deleteTime);
    } catch (JsonProcessingException e) {
      System.out.println(e.getMessage());
    }
    return genericRecord;
  }
}

حالات الاستخدام

إنّ حالات الاستخدام التالية هي أمثلة على التعديلات في الوقت الفعلي وتحديثات الخلاصة المجمّعة والمحتوى المتوفّر على مستوى عالٍ في طلب البيانات من واجهة برمجة التطبيقات:

السيناريو العنصر المطلوب تعديله الوصف والتأثيرات
إيقاف خدمة Service

تحتاج إلى إيقاف خدمة لسبب غير متوقَّع.

تعديلات في الوقت الفعلي: يمكنك تعديل العنصر Service المعني من خلال ضبط السمة isDisabled على true مع إبقاء الخصائص الأخرى كما هي.

الخلاصات الكاملة: احرص على تعديل العنصر من الخلاصات الكاملة لضبط isDisabled على true قبل عملية الجلب التالية من Google، وإلّا ستتم إعادة تفعيل الكيان.

سلعة معيّنة غير متوفّرة. MenuItemOffer التعديلات في الوقت الفعلي: أرسِل الكيان MenuItemOffer الذي يتضمّن غلافًا مع ضبط القيمة inventoryLevel على 0 للسمة MenuItem المحدّدة، مع عدم تغيير جميع البيانات الأخرى.
تغيير سعر صنف في القائمة MenuItemOffer التعديلات في الوقت الفعلي: أرسِل عنصر MenuItemOffer الذي يظهر في الصورة مع ضبط القيمة price على السعر المعدَّل لسمة MenuItem المحدّدة، وسيتم تغيير جميع البيانات الأخرى.

إضافة كيان جديد ذي مستوى أعلى

ينطبق فقط على الكيانات من أنواع Menu وRestaurant وService.

Menu وRestaurant وService

على سبيل المثال، تحتاج إلى إضافة قائمة طعام جديدة إلى أحد المطاعم.

الخلاصات الكاملة: أضِف العنصر في خلاصات البيانات وانتظِر حتى يتم نقل البيانات بشكل مجمّع.

حذف الكيان ذي المستوى الأعلى نهائيًا

ينطبق فقط على الكيانات من أنواع Menu وRestaurant وService.

Menu وRestaurant وService

تعديل في الوقت الفعلي: أرسِل عملية حذف فاضحة.

الخلاصات الكاملة: احرص على إزالة العنصر من الخلاصات الكاملة قبل عملية الجلب التالية من Google، وإلّا ستتم إضافة الكيان من جديد.

إضافة منطقة تسليم جديدة في Service محدّد ServiceArea الخلاصات المجمّعة: أرسِل الكيان ServiceArea المعنيّ مع الاحتفاظ بجميع حقوله بدون تغيير، كما تفعل عادةً في الخلاصات الكاملة، مع تحديد منطقة تسليم جديدة ضمن polygon أو geoRadius أو postalCode.
تعديل الوقت المقدّر للوصول في Service ServiceHours الخلاصات المجمّعة: أرسِل السمة ServiceHours تمامًا كما في الخلاصات، باستثناء السمة leadTimeMin التي يتم تعديلها وفقًا لذلك.
تعديل أسعار التوصيل في Service Fee الخلاصات المجمّعة: إرسال قيمة Fee للتسليم الكامل مع تعديل price.
تعديل ساعات عمل خدمة التوصيل أو طلب الوجبات الجاهزة لتناولها خارج المطعم في Service ServiceHours الخلاصات المجمّعة: أرسِل السمة ServiceHours كما هي الحال في الخلاصات، باستثناء أنّ السمتَين opens وcloses يتم تعديلهما وفقًا لذلك.
Service (تغيير الحدّ الأدنى لمبلغ الطلب) Fee الخلاصات المجمّعة: إرسال Fee بالكامل مع تعديل minPrice
حذف MenuItem نهائيًا Menu الخلاصات المجمّعة: أرسِل السمة MenuItem تمامًا كما في الخلاصات، ولكن على أن تكون قيمة parentMenuSectionId فارغة.

أوقات معالجة المهام المجمّعة وآخر الأخبار في الوقت الفعلي

إنّ كيانًا يتم تعديله أو حذفه من خلال خلاصة مجمّعة ستتمّ معالجته في غضون ساعتين، في حين ستتم معالجة الكيان الذي تم تعديله من خلال تعديل في الوقت الفعلي خلال 5 دقائق. ويتم حذف كيان قديم خلال 14 يومًا.

يمكنك إما إرسال ما يلي إلى Google:

  • إجراء مهام مجمَّعة متعددة في اليوم لإبقاء المستودع محدّثًا، أو
  • مهمة مجمَّعة واحدة في اليوم وتحديثات في الوقت الفعلي للحفاظ على مستودعك مُحدّثًا