בקטע הזה מוסבר איך לשלוח עדכונים של המלאי במלאי שטחי פרסום בנפרד. ל-Google. ממשק ה-API של העדכונים המצטברים מאפשר לשלוח עדכונים ולמחוק שנמצאים במלאי שטחי הפרסום ב-Sandbox או בסביבת הייצור כמעט בזמן אמת.
הפונקציונליות הזו מיועדת בעיקר לעדכונים שלא ניתן לחזות אותם, כמו סגירות חירום. בדרך כלל, כל שינוי שנשלח דרך שינוי מצטבר של API צריך להיות שינוי שלא יכול להיות יותר מ- שעה. אם השינוי לא צריך להופיע באופן מיידי, אפשר להשתמש במקום זאת, ניתן לבצע הטמעת נתונים באצווה. העיבוד של עדכונים מצטברים נמשך עד חמש דקות.
דרישות מוקדמות
כדי להטמיע עדכונים מצטברים:
- המערכת יוצרת חשבון שירות עם תפקיד עריכה בפרויקט Actions. פרטים נוספים זמינים במאמר יוצרים ומגדירים פרויקט.
- פידים של נתונים בסביבת ייצור או ב-Sandbox מתארחים ומוטמעים בהם. פרטים נוספים זמינים במאמר הטמעת נתונים באצווה.
- (אופציונלי, אבל מומלץ) מתקינים את ספריית הלקוח של Google בשפה שבחרת כדי להקל על השימוש ב-OAuth 2.0 בזמן קריאה ל API. דוגמאות הקוד שכלולות בהמשך משתמשות בספריות האלה. אחרת, צריך לטפל בחילופי אסימונים באופן ידני, כפי שמתואר במאמר שימוש ב-OAuth 2.0 לגישה ל-Google APIs.
נקודות קצה (endpoints)
בבקשות הבאות, מחליפים את מה שכתוב בשדות הבאים:
- PROJECT_ID: מזהה הפרויקט ב-Google Cloud שמשויך לפרויקט שנוצר בקטע יצירה והגדרה של פרויקט.
- TYPE: סוג הישות (נכס
@type
) של האובייקט בפיד הנתונים שרוצים לעדכן. - ENTITY_ID (מחיקה של נקודת הקצה בלבד): המזהה של הישות שרוצים למחוק. צריך לוודא כתובת ה-URL מקודדת את מזהה הישות ב-SAML.
- DELETE_TIME (מחיקה של נקודת הקצה בלבד): שדה אופציונלי לציון
הזמן שבו הישות נמחקה במערכות שלך (ברירת המחדל היא כשהבקשה
התקבל). ערך הזמן לא יכול להיות בעתיד. כששולחים ישות
באמצעות קריאה מצטברת, ניהול גרסאות של ישויות
משתמש גם בשדה
delete_time
במקרה של מחיקת שיחה. עיצוב ערך בתורyyyy-mm-ddTHH:mm:ssZ
עדכון נקודת הקצה
כדי לשנות ישות, צריך לשלוח בקשת HTTP POST לנקודת הקצה הבאה וכוללים מטען ייעודי (payload) של עדכונים ותוספות. אפשר לבצע עדכונים של עד 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_122' מהשדה 'delivery-provider-id' צריך לבצע קריאה ל-HTTP DELETE API כדי:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING
סביבת Sandbox
כדי להשתמש ב-Inremental Update API במלאי שטחי הפרסום ב-Sandbox, פועלים לפי ההנחיות שמפורטות בנקודות הקצה (Endpoints) שלמעלה, אבל
לשלוח בקשות אל /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 חייבת לכלול את הפרמטרים של הבקשה יחד עם קובץ ה-JSON המטען הייעודי (payload) שמכיל את הנתונים המובְנים של כל סוג ישות שמופיע של מלאי שטחי פרסום.
עדכון המטען הייעודי (Payload)
ה-JSON אמור להופיע בדיוק כמו בפיד באצווה, עם את ההבדלים הבאים:
- גודלו של גוף המטען הייעודי (Payload) לא יעלה על 5MB. באופן דומה לפעולת אצווה מומלץ להסיר רווחים לבנים כדי להתאים יותר נתונים.
- המעטפה נראית כך:
{ "requests": [ { "entity": { "data":"ENTITY_DATA", "name": "apps/project_id>/entities/type/entity_id" }, "update_time":"UPDATE_TIMESTAMP" }, ], "vertical": "FOODORDERING" }
במטען הייעודי (payload) שלמעלה, מחליפים את הרכיבים הבאים:
- 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 של כל הישות ברמה העליונה (התפריט), וגם הפיד מתבסס על סכימת המלאי v1.
נניח שאתם יוצרים פיד בכמות גדולה שנראה כך:
{ "@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" }
הוספת ישות
כדי להוסיף ישויות, אין להשתמש בעדכוני מלאי. במקום זאת, כדאי להשתמש בפידים בכמות גדולה כפי שמתואר בסכימת מלאי v2.
הסרת ישות
כדי להסיר ישויות ברמה העליונה, צריך להשתמש בנקודת קצה (endpoint) שונה מעט. ולהשתמש ב-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
דוגמה לנקודת קצה (endpoint) לדוגמה לישות שירות עם מזהה "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\""
}
]
}
]
}
}
דוגמת קוד
בהמשך מפורטות כמה דוגמאות לשימוש ב-Inremental Update API בשפות שונות. הדוגמאות האלה מתבססות על ספריות Google Auth, והן מבוססות על פיד באמצעות את סכימת המלאי של גרסה 1. לפתרונות חלופיים, אפשר לעיין במאמר בנושא שימוש ב-OAuth 2.0 לאפליקציות משרת לשרת.
עדכון ישויות
Node.js
הקוד הזה משתמש בספריית האימות של Google ל-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 ל-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 ל-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 ל-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 ל-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 ל-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 ריק.
|
SLO לגבי זמן העיבוד של משימות באצווה ועדכונים מצטברים
ישות שעדכנה או נמחקה דרך קבוצת קבצים תעובד בתוך 2 שעות במצב המתאים ביותר, ואילו ישות שעודכנו באמצעות עדכון מצטבר תעובד תוך 5 דקות. ישות לא פעילה נמחקת תוך 7 ימים.
אפשר לשלוח ל-Google:
- משימות באצווה מרובות ביום כדי לשמור על עדכניות המלאי, או
- משימה אחת באצווה ביום וממשקי API מצטברים כדי לשמור על מלאי עדכני.