בקטע הזה מוסבר איך אפשר לשלוח עדכונים של פידים דחופים אל Google. Incremental Updates API מאפשר לעדכן ולמחוק ישויות ב- כמעט בזמן אמת.
הפונקציונליות הזו מיועדת בעיקר לעדכונים שלא ניתן לחזות אותם, כמו סגירות חירום. בדרך כלל, כל שינוי שנשלח דרך שינוי ה-API של העדכונים המצטברים צריך להיות שינוי שצריך להיכנס לתוקף לא יותר מ- שבוע. אם השינוי לא צריך להופיע באופן מיידי, אפשר להשתמש במקום זאת, לבצע עדכון אצווה. המערכת מעבדת עדכונים מצטברים דקות.
הגדרה
כדי להטמיע עדכונים מצטברים:
- מבצעים את השלבים שמפורטים בקטע יצירה והגדרה של פרויקט כדי יוצרים פרויקט.
- מבצעים את השלבים שמפורטים בקטע הגדרת חשבון שירות. כדי ליצור חשבון שירות. לתשומת ליבך, התפקיד שלך צריך להיות מוגדר כ'בעלים'. של פרויקט להוספה של 'עורך' תפקיד בחשבון השירות
- (אופציונלי, אבל מומלץ) מתקינים את ספריית הלקוח של Google בשפה שבחרת כדי להקל על השימוש ב-OAuth 2.0 בזמן קריאה ל API. דוגמאות הקוד שכלולות בהמשך משתמשות בספריות האלה. אחרת, צריך לטפל בחילופי אסימונים באופן ידני, כפי שמתואר במאמר שימוש ב-OAuth 2.0 לגישה ל-Google APIs.
נקודת קצה
כדי להודיע ל-Google על עדכון, צריך לשלוח בקשת HTTP POST למאפיין המצטבר ה-API של העדכונים כולל מטען ייעודי (payload) של עדכונים ותוספות. סכימת המלאי שבה אתם משתמשים קובעת לאיזו נקודת קצה לשלוח את הבקשה:
מלאי גרסה 2
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID:push
מלאי גרסה 1
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/ENTITY_ID:push
כדי להסיר ישות, צריך לשלוח בקשת HTTP DELETE לנקודת הקצה הבאה תואמת לסכימת המלאי שבה משתמשים:
מלאי גרסה 2
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
מלאי גרסה 1
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
בבקשות שלמעלה, מחליפים את הפרטים הבאים:
- PROJECT_ID: מזהה הפרויקט ב-Google Cloud שמשויך לפרויקט שנוצר בקטע יצירה והגדרה של פרויקט.
- TYPE (סכימת מלאי v2 בלבד): סוג הישות (נכס אחד (
@type
)) של האובייקט בפיד הנתונים שרוצים לעדכן. - ENTITY_ID: מזהה הישות שכלולה במטען הייעודי (Payload). צריך לוודא כתובת ה-URL מקודדת את מזהה הישות ב-SAML.
- DELETE_TIME (מחיקה של נקודת הקצה בלבד): שדה אופציונלי לציון
הזמן שבו הישות נמחקה במערכות שלך (ברירת המחדל היא כשהבקשה
התקבל). ערך הזמן לא יכול להיות בעתיד. כששולחים ישות
באמצעות קריאה מצטברת, ניהול גרסאות של ישויות
משתמש גם בשדה
delete_time
במקרה של מחיקת שיחה. עיצוב ערך בתורyyyy-mm-ddTHH:mm:ssZ
לדוגמה, יש לכם פרויקט עם המזהה 'delivery-provider-id' שמשתמשת את סכימת המלאי של גרסה 2. אתם רוצים לערוך שינויים במסעדה באמצעות סוג הישות של מסעדה – "תפריט" ומזהה הישות בתפריט 'menuSection_122'. נקודת הקצה לעדכון הנתונים תהיה:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122:push
כדי להסיר את אותה הישות, צריך לשלוח את הקריאה הבאה ל-API מסוג HTTP DELETE API:
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING
בקשות ב-Sandbox
בבקשות ל-Sandbox, צריך לפעול לפי ההנחיות בנקודת קצה (endpoint) שלמעלה, אבל
לשלוח בקשות אל /v2/sandbox/apps/
במקום אל /v2/apps/
. לדוגמה,
המבנה של בקשת מחיקה ב-Sandbox לסכימה של מלאי שטחי הפרסום בגרסה 2 הוא:
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
עדכונים ותוספות
הפידים היומיים של אצווה צריכים גם לכלול את כל השינויים שנשלחים באמצעות הטופס הזה API. אחרת, העדכונים בכמות גדולה יחליפו את השינויים המצטברים.
מטען ייעודי (payload)
כל בקשת POST חייבת לכלול את הפרמטרים של הבקשה יחד עם קובץ ה-JSON המטען הייעודי (payload) שמכיל את הנתונים המובְנים של כל סוג ישות שמופיע של מלאי שטחי פרסום.
ה-JSON אמור להופיע בדיוק כמו בפיד באצווה, עם את ההבדלים הבאים:
- גודלו של גוף המטען הייעודי (Payload) לא יעלה על 5MB. באופן דומה לפעולת אצווה מומלץ להסיר רווחים לבנים כדי להתאים יותר נתונים.
- המעטפה נראית כך:
{ "entity": { "data":"ENTITY_DATA", "vertical":"FOODORDERING" }, "update_time":"UPDATE_TIMESTAMP" }
במטען הייעודי (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/apps/provider-project/entities/Restaurant/restaurant12345:push Host: actions.googleapis.com Content-Type: application/ld+json { "entity": { "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: עדכון מחיר של אפשרות בתפריט
נניח שצריך לשנות את המחיר של אפשרות בתפריט. כמו בדוגמה 1, חייב להכיל את ה-JSON של כל הישות ברמה העליונה (התפריט), וגם הפיד מתבסס על סכימת המלאי v1.
נניח שאתם יוצרים פיד בכמות גדולה שנראה כך:
{ "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 3.00, "priceCurrency": "USD" }
אז העדכון המצטבר דרך POST ייראה כך:
POST v2/apps/provider-project/entities/MenuItemOffer/menuitemoffer6680262:push Host: actions.googleapis.com Content-Type: application/ld+json { "entity": { "data": { "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 1.00, "priceCurrency": "USD" }, "vertical": "FOODORDERING" } }
הוספת ישות
כדי להוסיף ישויות, אין להשתמש בעדכוני מלאי. במקום זאת, כדאי להשתמש בפידים בכמות גדולה כפי שמתואר בסכימת מלאי v2.
הסרת ישות
כדי להסיר ישויות ברמה העליונה, צריך להשתמש בנקודת קצה (endpoint) שונה מעט. ולהשתמש ב-HTTP DELETE במקום ב-HTTP POST בבקשה.
אין להשתמש ב-HTTP DELETE כדי להסיר ישות משנה בתוך ישות ברמה עליונה, כמו אפשרות בתפריט. במקום זאת, צריך להתייחס בהסרה של ישויות משנה עדכון לישות ברמה עליונה שבה ישות המשנה מוסרת רשימה או פרמטר רלוונטיים.
דוגמה 1: מחיקת ישות ברמה העליונה
כדאי להביא בחשבון מצב שבו רוצים למחוק מסעדה בפיד שנעשה בו שימוש את סכימת המלאי של גרסה 1. צריך גם למחוק את השירותים והתפריטים שלו.
נקודת קצה לדוגמה של ישות בתפריט עם מזהה "https://www.provider.com/restaurant/menu/nr":
DELETE v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fmenu%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
נקודת קצה לדוגמה של ישות של מסעדה עם מזהה "https://www.provider.com/restaurant/nr":
DELETE v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
דוגמה לנקודת קצה (endpoint) לדוגמה לישות שירות עם מזהה "https://www.provider.com/restaurant/service/nr":
DELETE v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fservice%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
}
דוגמה 2: הסרת ישויות משנה
כדי להסיר ישות משנה מתוך ישות ברמה העליונה, צריך לשלוח את הישות ברמה העליונה ישות עם ישות המשנה שהוסרה מהשדה המתאים. הבאים ההנחה היא שהפיד משתמש בסכימת המלאי v1.
לדוגמה, כדי להסיר אזור שירות, מעדכנים את השירות עם אזור השירות.
הוסרו מהרשימה areaServed
.
POST v2/apps/delivery-provider-id/entities/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fservice%2Fnr:push
Host: actions.googleapis.com
Content-Type: application/ld+json
{
"entity": {
// Note: "data" is not serialized as a string in our example for readability.
"data": {
"@type": "Service",
"provider": {
"@type": "Restaurant",
"@id": "https://www.provider.com/restaurant/nr"
},
"areaServed": [
{
"@type": "GeoCircle",
"geoMidpoint": {
"@type": "GeoCoordinates",
"latitude": "42.362757",
"longitude": "-71.087109"
},
"geoRadius": "10000"
}
// area2 is removed.
]
...
},
"vertical": "FOODORDERING"
}
}
קודי תגובה של 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 Updates 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 = '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 update or add an entity */ async function updateEntity(entityId, 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/${encodeURIComponent(entityId)}:push`, body: { entity: { data: JSON.stringify(entity), vertical: 'FOODORDERING', } }, json: true }, (err, res, body) => { if (err) { return console.log(err); } console.log(`Response: ${JSON.stringify(res)}`) }) } updateEntity(ENTITY_ID, 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 = 'restaurant/http://www.provider.com/somerestaurant' ENDPOINT = 'https://actions.googleapis.com/v2/apps/%s/entities/%s:push' % ( PROJECT_ID, urllib.quote(ENTITY_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() # Populating the entity with wrapper entity = {} entity['data'] = data #entity JSON-LD serialized as string entity['vertical'] = 'FOODORDERING' request = {} request['entity'] = entity response = authed_session.post(ENDPOINT, json=request) 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 = "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 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 entity) { String authToken = getAuthToken(); String endpoint = String.format( "https://actions.googleapis.com/v2/apps/%s/entities/%s:push", PROJECT_ID, URLEncoder.encode(entityId, "UTF-8")); JSONObject data = new JSONObject(); data.put("data", entity.toString()); data.put("vertical", "FOODORDERING"); JSONObject jsonBody = new JSONObject(); jsonBody.put("entity", data); // Execute POST request executePostRequest(endpoint, authToken, jsonBody); }
הסרת ישויות
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:
תרחיש | ישות ברמה העליונה | תיאור ואפקטים |
---|---|---|
השבתת שירות | DisabledService |
צריך להשבית שירות מסיבה בלתי צפויה. עדכונים מצטברים: שליחת הישות פידים מלאים: הקפידו לעדכן את הישות מהפידים המלאים
צריך להגדיר את |
פריט מסוים חסר במלאי | Menu |
עדכונים מצטברים: שליחת Menu עדכונים מקיפים
ישות עם offer.inventoryLevel מוגדרת ל-0 עבור הנתון
MenuItem וכל שאר הנתונים ללא שינוי. |
שינוי במחיר של האפשרות בתפריט | Menu |
עדכונים מצטברים: שליחת Menu עדכונים מקיפים
ישות עם offer.price מוגדרת למחיר המעודכן של הנכס הנתון
MenuItem וכל שאר הנתונים ללא שינוי. |
הוספת ישות חדשה ברמה העליונה רלוונטי רק לישויות מסוג |
Menu , Restaurant Service |
למשל, אתם צריכים להוסיף תפריט חדש למסעדה. עדכונים מצטברים: שליחת הישות החדשה של התפריט יחד עם המסעדה.
הישות עם השדה |
מחיקה סופית של ישות ברמה העליונה רלוונטי רק לישויות מסוג |
Menu , Restaurant Service |
עדכונים מצטברים: מחיקה מפורשת. פידים מלאים: הקפידו להסיר את הישות מהפידים המלאים לפני באחזור הבא של Google, אחרת הישות תתווסף מחדש. |
הוספת אזור משלוחים חדש בService ספציפי |
Service |
פידים מצטברים: שולחים את הישות Service הרלוונטית עם כל
ללא שינוי, כמו שבדרך כלל עושים בפידים מלאים, עם אזור חדש למשלוחים
צוין בתוך areaServed מהService . |
עדכון זמן ההגעה המשוער למסירה בService |
Service |
פידים מצטברים: שולחים את הService באותו אופן כמו ב-
העדכונים, מלבד העדכון של ה-hoursAvailable.deliveryHours שלהם
בהתאם. |
עדכון מחירי המשלוח בService |
Service |
פידים מצטברים: שליחת Service מלאים באמצעות
הטבלה offers.priceSpecification.price עודכנה. |
עדכון שעות הפעילות של משלוחים או טייק אוויי בService |
Service |
פידים מצטברים: שולחים את הService באותו אופן כמו ב-
העדכונים, מלבד העדכון של ה-hoursAvailable שלהם
בהתאם. |
Service (שינוי סכום ההזמנה המינימלי) |
Service |
פידים מצטברים: שליחת Service מלאים באמצעות
Service.offers.priceSpecification.eligibleTransactionVolume
עודכן |
מחיקת MenuItem לתמיד |
Menu |
פידים מצטברים: שולחים את הMenu באופן זהה לזה של
פידים, אבל לאחר הסרת ה-MenuItem הזה מ
רשימה hasMenuItems . |
SLO לגבי זמן העיבוד של משימות באצווה ועדכונים מצטברים
ישות שנוספה באמצעות עדכון אצווה או עדכון מצטבר תעובד יום עד יומיים. ישות שעדכנה או נמחקה דרך קבוצה תעובד בשני שלבים שעות לעומת ישות שעודכנה באמצעות עדכון מצטבר תוך 5 דקות. ישות לא פעילה נמחקת תוך 7 ימים.
אפשר לשלוח ל-Google:
- משימות באצווה מרובות ביום כדי לשמור על עדכניות המלאי, או
- משימה אחת באצווה ביום וממשקי API מצטברים כדי לשמור על מלאי עדכני.