עדכונים בזמן אמת

בקטע הזה נסביר איך לשלוח ל-Google עדכונים תלויי-זמן של ישויות המלאי. ה-API של העדכונים בזמן אמת מאפשר לדחוף עדכונים ולמחוק ישויות במלאי של Sandbox או בסביבת הייצור כמעט בזמן אמת.

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

דרישות מוקדמות

לפני שמטמיעים עדכונים בזמן אמת, צריך:

  1. Maps Booking API מופעל:
    • ב-GCP, עוברים אל APIs & Services > Library
    • חיפוש של 'Google Maps Booking API'
      איך מאתרים את ממשקי ה-API להזמנות במפות Google
    • מאתרים את המופע של Sandbox ('Google Maps Booking API (Dev)) ולוחצים על Enable
    • מאתרים את המופע בסביבת הייצור ('Google Maps Booking API') ולוחצים על Enable
      הפעלה של Google Maps Booking API
  2. יוצרים חשבון שירות עם הרשאת עריכה בפרויקט GCP. פרטים נוספים זמינים במאמר הגדרת חשבון.
  3. פידים של נתונים בסביבת ייצור או ארגז חול מתארחים ומוטמעים. לפרטים נוספים, ראו הטמעת נתונים של אצווה.
  4. לאימות API, מומלץ להתקין את ספריית הלקוח של Google בשפה לבחירתכם. משתמשים ב-"https://www.googleapis.com/auth/mapsbooking" כהיקף ה-OAuth. דוגמאות הקוד המפורטות בהמשך משתמשות בספריות אלו. אחרת, תצטרכו לטפל בהמרות של אסימונים באופן ידני, כפי שמתואר במאמר שימוש ב-OAuth 2.0 לגישה ל-Google APIs.

סקירה כללית

ממשק API ל'עדכונים בזמן אמת' תומך בשני סוגי פעולות. הפעולה הראשונה יש לבצע בעדכון הישויות הקיימות. הפעולה השנייה היא מחיקה של הסרת ישויות מהמלאי. שתי הפעולות מתבצעות על מגוון ישויות שמפורטות בגוף הבקשה. אפשר לעדכן עד 1,000 ישויות בקריאה אחת ל-API. ה-API מקבל את כל הבקשות הנכנסות ומציב אותן בתור להמשך עיבוד. לכן, בקשות RTU מעובדות באופן אסינכרוני.

ה-API לעדכונים בזמן אמת פועל בשתי סביבות: Sandbox וסביבת ייצור. סביבת Sandbox משמשת לבדיקת בקשות ה-API וסביבת הייצור, כדי לעדכן את התוכן שגלוי למשתמשי קצה להזמנות מקצה לקצה. שמות המארחים של שתי הסביבות:

  • ארגז חול – partnerdev-mapsbooking.googleapis.com
  • הפקה – mapsbooking.googleapis.com

נקודות קצה

ה-API של העדכונים בזמן אמת חושף שתי נקודות קצה (endpoint) כדי לטפל בבקשות הנכנסות לעדכוני מלאי:

  • 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 המלאות לשליחת בקשות API ב-Sandbox ובסביבת הייצור ייראו כמו בדוגמאות שבהמשך.

# 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 יחד עם המטען הייעודי (payload) של JSON שמכיל את הנתונים המובְנים של כל סוג ישות שרשום בסכימת המלאי.

מטען ייעודי (payload) של בקשה להגדלת נפח החשיפה

גוף הבקשה הוא אובייקט JSON עם רשימת רשומות. כל רשומה תואמת לישות שמתעדכנת. הוא מורכב מהשדה data_record עם המטען הייעודי (payload) של הישות שמקודד ב-Base64 וה-generation_timestamp שמציין את זמן עדכון הישות:

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

במטען הייעודי (payload) שלמעלה, מחליפים את הפריטים הבאים:

  • 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 משמש לניהול גרסאות של ישויות. כדאי לראות את הפורמט הצפוי של ערכי הזמן בסכימת המלאי היחסי.

כל בקשת עדכון בזמן אמת חייבת לעמוד בתנאים הבאים:

  • גוף המטען הייעודי לא יכול להיות גדול מ-5MB. בדומה לפידים באצווה, מומלץ להסיר רווחים לבנים כדי להתאים יותר נתונים.
  • יכולות להיות עד 1,000 ישויות בבקשה של batchPush.

דוגמאות

דוגמה 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
}
  

כך יתבצע העדכון בזמן אמת באמצעות 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": "+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

אותה דוגמה עם מטען ייעודי (payload) בקידוד 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: עדכון של כמה מסעדות

כדי לעדכן שתי ישויות של מסעדות בקריאה אחת ל-API, בקשת ה-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

אותה דוגמה עם מטען ייעודי (payload) בקידוד 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

אותה דוגמה עם מטען ייעודי (payload) בקידוד 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"
    }
  ]
}

    

הוספת ישויות

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

מחיקת ישויות

כדי למחוק ישויות מהמלאי, משתמשים בנקודת הקצה DELETE ושולחים בקשות HTTP POST. כל בקשת POST חייבת לכלול את הפרמטר PARTNER_ID יחד עם המטען הייעודי (payload) של JSON שמכיל את המזהה של ישות כלשהי במלאי.

מחיקת המטען הייעודי (payload) של הבקשה

הגוף של בקשת מחיקה דומה במבנה של בקשת עדכון. יש גם רשימה של רשומות עם השדות data_record ו-delete_time:

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

במטען הייעודי (payload) שלמעלה, מחליפים את הפריטים הבאים:

  • BASE_64_ENCODED_REFERENCE: מחרוזת JSON בקידוד Base64 של ההפניה לישות שרוצים להסיר. קובץ עזר מורכב רק מסוג הישות ומהמזהה, לדוגמה ייצוג JSON של הפניה ל-MenuSection:

    {"@type":"MenuSection","@id":"853705"}
  • DELETE_TIMESTAMP: הקפידו לכלול את חותמת הזמן של המועד שבו הישות נמחקה במערכת הקצה העורפי שלכם. חותמת הזמן הזו משמשת לקביעת הסדר שבו המחיקה תחול על מלאי שטחי הפרסום.

יכולות להיות עד 1,000 ישויות בבקשה של batchDelete.

דוגמאות

דוגמה 1: הסרת שתי ישויות של MenuItem

כדי להסיר שני פריטים מהתפריט בקריאה אחת ל-API, בקשת ה-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

אותה דוגמה עם מטען ייעודי (payload) בקידוד 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

אותה דוגמה עם מטען ייעודי (payload) בקידוד 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"
    }
  ]
}
    

קודי תגובה של אימות ו-API

קיימים שני סוגים של אימותים בקריאות ל-API לעדכון בזמן אמת:

  • Request-level – האימותים האלה בודקים שמטען הייעודי (payload) תואם לסכימה ה קבועה או המחיקה, וכל data_record מכיל את השדה @id וגם את השדה @type. הבדיקות האלה סינכרוניות והתוצאות מוחזרות בגוף התגובה של ה-API. אם קוד התגובה 200 וגוף JSON ריק {}, המשמעות היא שהאימותים האלה עברו והישויות בבקשה הזו תורנו בתור לעיבוד. אם קוד תגובה שונה מ-200, המשמעות היא שאחד או יותר מהאימותים האלה נכשל והבקשה כולה נדחתה (כולל כל הישויות במטען הייעודי (payload). לדוגמה, אם ב-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\" }"
          }
        ]
      }
    }
    
  • Entity-level – כל ישות במטען הייעודי (payload) מאומתת מול הסכימה היחסית. בעיות שאותרו בשלב האימות הזה לא מדווחות בתגובת ה-API. הם מדווחים רק בלוח הבקרה של דיווח על RTU.

מכסות ל-API

לעדכונים בזמן אמת ב-API יש מכסה של 1,500 בקשות בכל 60 שניות, או 25 בקשות לשנייה בממוצע. במקרה של חריגה מהמכסה, Google מגיבה עם הודעת השגיאה הבאה:

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

כדי לפתור את הבעיה, נסו שוב לבצע את השיחה במרווחי זמן גדולים יותר במהירות, עד שהיא תסתיים. אם אתם ממצים את המכסה באופן קבוע, כדאי לכלול עוד ישויות בבקשת API אחת. אפשר לכלול עד 1,000 ישויות בקריאה אחת ל-API.

דוגמאות קוד

בהמשך מפורטות כמה דוגמאות לשימוש ב-API לעדכון בזמן אמת בשפות שונות. בדוגמאות האלה נעשה שימוש בספריות האימות של 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 auth ל-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 auth ל-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;
  }
}

תרחישים לדוגמה

התרחישים הבאים הם דוגמאות לעדכונים בזמן אמת, לעדכוני פידים באצווה ולתוכן ברמה גבוהה בקריאה ל-API:

תרחיש הישות שיש לעדכן תיאור ואפקטים
השבתה של שירות Service

צריך להשבית שירות מסיבה בלתי צפויה.

עדכונים בזמן אמת: צריך לעדכן את הישות Service הרלוונטית על ידי הגדרת הנכס isDisabled שלה ל-true, אבל להשאיר את שאר הנכסים ללא שינוי.

פידים מלאים: לפני האחזור הבא של Google, חשוב לעדכן את הישות מהפידים המלאים כך שהערך של isDisabled יוגדר ל-true. אחרת, הישות תופעל מחדש.

פריט מסוים חסר במלאי 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 אחת מהאפשרויות הבאות:

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