בקטע הזה מוסבר איך שולחים ל-Google עדכונים של ישויות מלאי שטחי הפרסום שחשובים בזמן אמת. באמצעות Incremental Update API אפשר לדחוף עדכונים ולמחוק ישויות במלאי שטחי הפרסום בסביבת ה-Sandbox או בסביבת הייצור כמעט בזמן אמת.
הפונקציונליות הזו מיועדת בעיקר לעדכונים שלא ניתן לחזות מראש, כמו חסימות חירום. ככלל, כל שינוי שנשלח דרך Incremental Update API צריך להיכנס לתוקף תוך שעה לכל היותר. אם השינוי לא צריך להופיע באופן מיידי, אפשר להשתמש במקום זאת בטמעת נתונים בכמות גדולה. עיבוד של עדכונים מצטברים נמשך לא יותר מחמש דקות.
דרישות מוקדמות
לפני שמטמיעים עדכונים מצטברים, צריך את הפריטים הבאים:
- נוצר חשבון שירות עם תפקיד עריכה בפרויקט Actions. פרטים נוספים זמינים במאמר יצירה והגדרה של פרויקט.
- פידים של נתונים בסביבת ייצור או בארגז חול מתארחים ומוטמעים. מידע נוסף זמין במאמר טמעת נתונים בכמות גדולה.
- (אופציונלי, אבל מומלץ) מתקינים את ספריית הלקוח של Google בשפה הרצויה כדי להקל על השימוש ב-OAuth 2.0 בזמן הקריאה ל-API. בדוגמאות הקוד שמפורטות בהמשך נעשה שימוש בספריות האלה. אחרת, תצטרכו לטפל בהחלפות האסימונים באופן ידני, כפי שמתואר במאמר שימוש ב-OAuth 2.0 לגישה ל-Google APIs.
נקודות קצה
בבקשות הבאות, מחליפים את הפרטים הבאים:
- PROJECT_ID: מזהה הפרויקט ב-Google Cloud שמשויך לפרויקט שיצרתם בקטע יצירה והגדרה של פרויקט.
- TYPE: סוג הישות (הנכס
@type
) של האובייקט בפיד הנתונים שרוצים לעדכן. - ENTITY_ID (נקודת קצה למחיקה בלבד): מזהה הישות שרוצים למחוק. חשוב להקפיד על קידוד של מזהה הישות ב-URL.
- DELETE_TIME (נקודת קצה למחיקה בלבד): שדה אופציונלי לציון המועד שבו הישות נמחקה במערכות שלכם (ברירת המחדל היא מועד קבלת הבקשה). ערך הזמן לא יכול להיות עתידי. כששולחים ישות באמצעות קריאה מצטברת, ניהול גרסאות של ישויות משתמש גם בשדה
delete_time
במקרה של קריאת מחיקה. עיצוב הערך הזה בתורyyyy-mm-ddTHH:mm:ssZ
עדכון נקודת הקצה
כדי לשנות ישות, שולחים בקשת POST ל-HTTP לנקודת הקצה הבאה, ומצרפים עומס נתונים של עדכונים והוספות. אפשר לבצע עדכונים של עד 1,000 ישויות בקריאה יחידה ל-API.
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities:batchPush
לדוגמה, אם רוצים לעדכן ישויות בפרויקט עם המזהה 'delivery-provider-id', נקודת הקצה תהיה:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities:batchpush
מחיקת נקודת קצה
כדי למחוק ישות במלאי, שולחים בקשת HTTP DELETE לנקודת הקצה הבאה.
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
לדוגמה, כדי למחוק ישות מסוג 'MenuSection' עם המזהה 'menuSection_122' מהפרויקט 'delivery-provider-id', צריך לבצע קריאה ל-API מסוג HTTP DELETE אל:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING
סביבת Sandbox
כדי להשתמש ב-Incremental Update API במלאי שטחי הפרסום ב-sandbox, פועלים לפי ההנחיות בקטע נקודות קצה שלמעלה, אבל שולחים בקשות אל /v2/sandbox/apps/
במקום אל /v2/apps/
.
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities:batchPush
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
עדכון ישויות
כל בקשת POST חייבת לכלול את פרמטרים הבקשה יחד עם עומס העבודה (payload) של ה-JSON שמכיל את הנתונים המובְנים של כל סוג ישות שמופיע בסכמת המלאי.
עדכון המטען הייעודי (payload)
קובץ ה-JSON אמור להיראות כמו בפיד האצווה, עם ההבדלים הבאים:
- גודל גוף המטען הייעודי לא יכול לחרוג מ-5MB. בדומה לפידים של קבוצות, מומלץ להסיר רווחים לבנים כדי שיוכלו להיכנס יותר נתונים.
- המעטפה נראית כך:
{ "requests": [ { "entity": { "data":"ENTITY_DATA", "name": "apps/project_id>/entities/type/entity_id" }, "update_time":"UPDATE_TIMESTAMP" }, ], "vertical": "FOODORDERING" }
במטען הייעודי שלמעלה, מחליפים את הפרטים הבאים:
- ENTITY_DATA: ישות בפורמט JSON בסריאליזציה כמחרוזת. יש להעביר את הישות של JSON-LD כמחרוזת בשדה
data
. - UPDATE_TIMESTAMP (אופציונלי): חותמת הזמן שבה הישות עודכנה במערכות שלכם. ערך הזמן לא יכול להיות עתידי. חותמת הזמן שמוגדרת כברירת מחדל היא המועד שבו Google מקבלת את הבקשה. כששולחים ישות באמצעות בקשה מצטברת, ניהול הגרסאות של הישות משתמש גם בשדה
update_time
במקרה של בקשת הוספה/עדכון.
דוגמאות
דוגמה 1: עדכון מסעדה
נניח שצריך לעדכן בדחיפות את מספר הטלפון של מסעדה. העדכון מכיל את ה-JSON של המסעדה כולה.
נניח שיש לכם פיד באצווה שנראה כך:
{ "@type": "Restaurant", "@id": "restaurant12345", "name": "Some Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501234567", "streetAddress": "345 Spear St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US", "latitude": 37.472842, "longitude": -122.217144 }
לאחר מכן, העדכון המצטבר באמצעות HTTP POST יהיה:
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant12345", "data": { "@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 } } } "vertical": "FOODORDERING" }
דוגמה 2: עדכון של כמה מסעדות
כדי לעדכן שתי ישויות של מסעדות בקריאה אחת ל-API, בקשת ה-HTTP POST תהיה:
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant12345", "data": { "@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 } } }, { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant123", "data": { "@type": "Restaurant", "@id": "restaurant123", "name": "Some Other Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501231235", "streetAddress": "385 Spear St", "addressLocality": "San Mateo", "addressRegion": "CA", "postalCode": "94115", "addressCountry": "US" } } } ] "vertical": "FOODORDERING" }
דוגמה 3: עדכון המחיר של פריט בתפריט
נניח שצריך לשנות את המחיר של פריט בתפריט. בדומה לדוגמה 1, העדכון צריך לכלול את ה-JSON של הישות ברמה העליונה כולה (התפריט), והפיד משתמש בסכמת המלאי של גרסה 1.
נניח שיש לכם פיד באצווה שנראה כך:
{ "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 3.00, "priceCurrency": "USD" }
לאחר מכן, העדכון המצטבר באמצעות POST יהיה:
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/menuitemoffer/menuitemoffer6680262", "data": { "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 1.00, "priceCurrency": "USD" }, "vertical": "FOODORDERING" } } ] "vertical": "FOODORDERING" }
הוספת ישות
כדי להוסיף ישויות, כדאי להימנע משימוש בעדכוני מלאי. במקום זאת, צריך להשתמש בתהליך של פידים בכמות גדולה כפי שמתואר בסכימה של מלאי שטחי הפרסום בגרסה 2.
הסרת ישות
כדי להסיר ישויות ברמה העליונה, משתמשים בנקודת קצה ששונתה מעט, ובבקשה משתמשים ב-HTTP DELETE במקום ב-HTTP POST.
מחיקת ישות ברמה העליונה
נניח שאתם רוצים למחוק מסעדה בפיד. צריך גם למחוק את השירותים והתפריטים שלו.
דוגמה לנקודת קצה של ישות תפריט עם המזהה "provider/restaurant/menu/nr":
DELETE v2/apps/delivery-provider-id/entities/menu/provider%2Frestaurant%2Fmenu%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
נקודת קצה לדוגמה של ישות מסעדה עם המזהה "https://www.provider.com/restaurant/nr":
DELETE v2/apps/delivery-provider-id/entities/restaurant/provider%2Frestaurant%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
נקודת קצה לדוגמה של ישות שירות עם המזהה "https://www.provider.com/restaurant/service/nr":
DELETE v2/apps/delivery-provider-id/entities/service/provider%2Frestaurant%2Fservice%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
}
הסרת ישויות משנה
אין להשתמש ב-HTTP DELETE כדי להסיר ישות משנה בתוך ישות ברמה העליונה, כמו 'פריט תפריט' בתוך תפריט. במקום זאת, צריך להתייחס להסרת ישויות משנה כעדכון של ישות ברמה העליונה, שבו הישות המשנית מוסר מהרשימה הרלוונטית או מ-reverseReference.
קודי תגובה של API
קריאה שהצליחה לא מעידה על כך שהפיד תקין או נכון, אלא רק על כך שהקריאה ל-API בוצעה. לשיחות מוצלחות מקבלים קוד תגובה מסוג HTTP 200, יחד עם גוף תגובה ריק:
{}
במקרה של כשל, קוד התגובה של ה-HTTP לא יהיה 200, וגוף התגובה יציין מה השתבש.
לדוגמה, אם המשתמש מגדיר את הערך 'vertical' בתוך המעטפה לערך FAKE_VERTICAL
, תופיע ההודעה הבאה:
{
"error": {
"code": 400,
"message": "Invalid value at 'entity.vertical' (TYPE_ENUM), \"FAKE_VERTICAL\"",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "entity.vertical",
"description": "Invalid value at 'entity.vertical' (TYPE_ENUM), \"FAKE_VERTICAL\""
}
]
}
]
}
}
דוגמת קוד
בהמשך מפורטות כמה דוגמאות לשימוש ב-Incremental Update API בשפות שונות. בדוגמאות האלה נעשה שימוש בספריות Google Auth, והן מבוססות על פיד שמשתמש בסכימת המלאי v1. לקבלת פתרונות חלופיים, אפשר לעיין במאמר שימוש ב-OAuth 2.0 לאפליקציות שרת-אל-שרת.
עדכון ישויות
Node.js
הקוד הזה משתמש ב-Google Auth Library ל-Node.js.
const {auth} = require('google-auth-library') const request = require('request'); // The service account client secret file downloaded from the Google Cloud Console const serviceAccountJson = require('./service-account.json') // entity.json is a file that contains the entity data in json format const entity = require('./entity.json') const ENTITY_ID = 'your/entity/id' const PROJECT_ID = 'type/your-project-id' /** * Get the authorization token using a service account. */ async function getAuthToken() { let client = auth.fromJSON(serviceAccountJson) client.scopes = ['https://www.googleapis.com/auth/assistant'] const tokens = await client.authorize() return tokens.access_token; } /** * Send an incremental update to update or add an entity */ async function updateEntity(entity) { const token = await getAuthToken() request.post({ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, url: `https://actions.googleapis.com/v2/apps/${PROJECT_ID}/entities:batchPush`, body: { requests: [ { entity: { data: JSON.stringify(entity) name: `apps/${PROJECT_ID}/entities/${ENTITY_ID}` } } ], vertical: 'FOODORDERING' }, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) } updateEntity(entity)
Python
הקוד הזה משתמש בספריית Google Auth ל-Python.
from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession import json import urllib PROJECT_ID = 'your-project-id' ENTITY_ID = 'type/your/entity/id' ENDPOINT = 'https://actions.googleapis.com/v2/apps/%s/entities:batchPush' % ( PROJECT_ID) # service-account.json is the service account client secret file downloaded from the # Google Cloud Console credentials = service_account.Credentials.from_service_account_file( 'service-account.json') scoped_credentials = credentials.with_scopes( ['https://www.googleapis.com/auth/assistant']) authed_session = AuthorizedSession(scoped_credentials) # Retrieving the entity update_file = open("entity.json") #JSON file containing entity data in json format. data = update_file.read() entity = {} entity['data'] = data #entity JSON-LD serialized as string entity['name'] = 'apps/%s/entities/%s' % (PROJECT_ID, urllib.quote(ENTITY_ID, '') ) # Populating the request request = {} request['entity'] = entity requestArray = [request] # Populating the payload payload = {} payload['requests'] = requestArray payload['vertical'] = 'FOODORDERING' response = authed_session.post(ENDPOINT, json=payload) print(response.text) #if successful, will be '{}'
Java
הקוד הזה משתמש ב-Google Auth Library ל-Java.
private static final String PROJECT_ID = "your-project-id"; private static final String ENTITY_ID = "type/your-entity-id"; /** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/assistant")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an incremental update to update or add an entity. * @param entityId The id of the entity to update. * @param entity the json of the entity to be updated. */ public void updateEntity(String entityId, JSONObject data) { String authToken = getAuthToken(); String endpoint = String.format("https://actions.googleapis.com/v2/apps/%s/entities/:batchPush", PROJECT_ID); JSONObject entity = new JSONObject(); entity.put("data", data.toString()); entity.put("name", String.format("apps/%s/entities/%s", PROJECT_ID, URLEncoder.encode(ENTITY_ID, "UTF-8"))); JSONObject request = new JSONObject(); request.put("entity", entity); JSONArray requestArray = new JSONArray(); requestArray.put(request); JSONObject payload = new JSONObject(); payload.put("requests", requestArray); payload.put("vertical", FOODORDERING); // Execute POST request executePostRequest(endpoint, authToken, payload); }
הסרת ישויות
Node.js
הקוד הזה משתמש ב-Google Auth Library ל-Node.js.
const {auth} = require('google-auth-library') const request = require('request'); // The service account client secret file downloaded from the Google Cloud Console const serviceAccountJson = require('./service-account.json') // entity.json is a file that contains the entity data in json format const entity = require('./entity.json') const ENTITY_ID = 'restaurant/http://www.provider.com/somerestaurant' const PROJECT_ID = 'your-project-id' /** * Get the authorization token using a service account. */ async function getAuthToken() { let client = auth.fromJSON(serviceAccountJson) client.scopes = ['https://www.googleapis.com/auth/assistant'] const tokens = await client.authorize() return tokens.access_token; } /** * Send an incremental update to delete an entity */ async function deleteEntity(entityId) { const token = await getAuthToken() request.delete({ headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, url: `https://actions.googleapis.com/v2/apps/${PROJECT_ID}/entities/${encodeURIComponent(entityId)}?entity.vertical=FOODORDERING`, body: {}, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) } deleteEntity(ENTITY_ID)
Python
הקוד הזה משתמש בספריית Google Auth ל-Python.
from google.oauth2 import service_account from google.auth.transport.requests import AuthorizedSession import json import urllib # Service config PROJECT_ID = 'your-project-id' ENTITY_ID = 'restaurant/http://www.provider.com/somerestaurant' DELETE_TIME = '2018-04-07T14:30:00-07:00' ENDPOINT = 'https://actions.googleapis.com/v2/apps/%s/entities/%s?entity.vertical=FOODORDERING&delete_time=%s' % ( PROJECT_ID, urllib.quote(ENTITY_ID, ''), urllib.quote(DELETE_TIME, '')) # service-account.json is the service account client secret file downloaded from the # Google Cloud Console credentials = service_account.Credentials.from_service_account_file( 'service-account.json') scoped_credentials = credentials.with_scopes( ['https://www.googleapis.com/auth/assistant']) authed_session = AuthorizedSession(scoped_credentials) response = authed_session.delete(ENDPOINT) print(response.text) #if successful, will be '{}'
Java
הקוד הזה משתמש ב-Google Auth Library ל-Java.
private static final String PROJECT_ID = "your-project-id"; private static final String ENTITY_ID = "restaurant/http://www.provider.com/somerestaurant"; /** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/assistant")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an incremental update to delete an entity. * @param entityId The id of the entity to delete. */ public void deleteEntity(String entityId) { String authToken = getAuthToken(); String endpoint = String.format( "https://actions.googleapis.com/v2/apps/%s/entities/%s?entity.vertical=FOODORDERING", PROJECT_ID, URLEncoder.encode(entityId, "UTF-8")); // Execute DELETE request System.out.println(executeDeleteRequest(endpoint, authToken)); }
תרחישים לדוגמה
התרחישים הבאים הם דוגמאות לעדכונים מצטברים, לעדכוני פידים מלאים ולתוכן ברמה גבוהה בקריאה ל-API:
תרחיש | הישות לעדכון | תיאור והשפעות |
---|---|---|
השבתת שירות | Service |
אתם צריכים להשבית שירות מסיבה בלתי צפויה. עדכונים מצטברים: מעדכנים את הישות פידים מלאים: חשוב לעדכן את הישות מהפידים המלאים כך שהערך של |
פריט ספציפי חסר במלאי | MenuItemOffer |
עדכונים מצטברים: שולחים את הישות MenuItemOffer שעוטפת את הנתונים, כאשר הערך של inventoryLevel מוגדר ל-0 עבור MenuItem הנתון, וכל שאר הנתונים לא משתנים. |
שינוי במחיר של פריט בתפריט | MenuItemOffer |
עדכונים מצטברים: שולחים את הישות MenuItemOffer שעוטפת את הנתונים, כאשר הערך של price מוגדר למחיר המעודכן של MenuItem הנתון, וכל שאר הנתונים לא משתנים. |
הוספת ישות חדשה ברמה העליונה רלוונטי רק לישות מסוגים |
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 דקות. ישות לא עדכנית נמחקת תוך 7 ימים.
אתם יכולים לשלוח ל-Google:
- כמה משימות באצווה ביום כדי שהמלאי יהיה עדכני, או
- משימה אחת של אצווה ביום ו-APIs מצטברים כדי לשמור על עדכניות המלאי.