инкрементные обновления инвентаря v2

В этом разделе описывается, как отправлять в Google срочные обновления объектов вашего инвентаря. API добавочного обновления позволяет отправлять обновления и удалять объекты в песочнице или производственном инвентаре практически в реальном времени.

Эта функция в первую очередь предназначена для обновлений, которые вы не можете предвидеть, например для аварийного закрытия. Как правило, любое изменение, отправленное через API добавочного обновления, должно быть изменением, которое должно быть опубликовано не более чем через час. Если изменение не требуется немедленно отражать, вместо этого можно использовать пакетную обработку . Инкрементальные обновления обрабатываются не более пяти минут.

Предварительные условия

Перед внедрением добавочных обновлений необходимы следующие элементы:

  1. В вашем проекте Actions создается сервисная учетная запись с ролью редактора. Дополнительные сведения см. в разделе Создание и настройка проекта .
  2. Фиды производственных или изолированных данных размещаются и принимаются. Дополнительные сведения см. в разделе Пакетный прием .
  3. (Необязательно, но рекомендуется) Установите клиентскую библиотеку Google на выбранном вами языке, чтобы упростить использование OAuth 2.0 при вызове API. В приведенных ниже примерах кода используются эти библиотеки. В противном случае вам придется обрабатывать обмен токенами вручную, как описано в разделе Использование OAuth 2.0 для доступа к API Google .

Конечные точки

В приведенных ниже запросах замените следующее:

  • PROJECT_ID : идентификатор проекта Google Cloud, связанный с проектом, который вы создали в разделе «Создание и настройка проекта» .
  • TYPE : тип сущности (свойство @type ) объекта в фиде данных, который вы хотите обновить.
  • ENTITY_ID (только удаление конечной точки): идентификатор объекта, подлежащего удалению. Убедитесь, что URL-адрес кодирует идентификатор вашего объекта.
  • DELETE_TIME (только удаление конечной точки): необязательное поле для обозначения времени удаления объекта в ваших системах (по умолчанию — момент получения запроса). Временная стоимость не должна находиться в будущем. При отправке объекта посредством инкрементного вызова управление версиями объекта также использует поле delete_time в случае вызова удаления. Отформатируйте это значение как yyyy-mm-ddTHH:mm:ssZ

Обновить конечную точку

Чтобы изменить сущность, отправьте запрос HTTP POST к следующей конечной точке и включите полезные данные обновлений и дополнений . Вы можете обновлять до 1000 объектов за один вызов 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

Песочница

Чтобы использовать API добавочного обновления в своем инвентаре песочницы, следуйте инструкциям в разделе « Конечные точки» выше, но отправляйте запросы к /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, содержащими структурированные данные любого типа объекта, указанного в схеме инвентаризации.

Обновить полезную нагрузку

JSON должен выглядеть так же, как и в пакетном канале, со следующими отличиями:

  • Размер тела полезной нагрузки не должен превышать 5 МБ. Как и в случае с пакетными фидами, мы предлагаем удалять пробелы, чтобы вместить больше данных.
  • Конверт выглядит следующим образом:
{
  "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 для всего объекта верхнего уровня (меню), а канал использует схему инвентаризации 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"
}

Добавление объекта

Чтобы добавлять объекты, избегайте использования обновлений инвентаря. Вместо этого используйте процесс пакетной подачи, как описано для схемы инвентаризации версии 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 для удаления подобъекта внутри объекта верхнего уровня, например пункта меню внутри меню. Вместо этого рассматривайте удаление подобъектов как обновление объекта верхнего уровня, в котором подобъект удаляется из соответствующего списка илиverseReference .

Коды ответа API

Успешный вызов не означает, что фид действителен или правилен, а означает лишь то, что был выполнен вызов API. Успешные вызовы получают код ответа HTTP 200 вместе с пустым телом ответа:

{}

В случае сбоев код ответа HTTP не будет равен 200, а в тексте ответа указывается, что пошло не так.

Например, если пользователь установил значение «вертикальное» в конверте на 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\""
          }
        ]
      }
    ]
  }
}

Пример кода

Ниже приведены некоторые примеры использования API добавочного обновления на разных языках. В этих примерах используются библиотеки Google Auth и предполагается, что канал использует схему инвентаризации v1. Альтернативные решения см. в разделе Использование 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)

Питон

Этот код использует библиотеку аутентификации 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 '{}'

Джава

Этот код использует библиотеку аутентификации 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)

Питон

Этот код использует библиотеку аутентификации 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 '{}'

Джава

Этот код использует библиотеку аутентификации 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

Вам необходимо отключить услугу по непредвиденной причине.

Дополнительные обновления: обновите рассматриваемую сущность Service , установив для ее свойства isDisabled значение true , но оставив другие свойства прежними.

Полные каналы: обязательно обновите объект из полных каналов, чтобы для isDisabled было установлено значение true перед следующей выборкой Google, иначе объект будет повторно включен.

Конкретного товара нет в наличии MenuItemOffer Инкрементальные обновления: отправьте инкапсулирующую сущность MenuItemOffer с inventoryLevel равным 0 для данного MenuItem , и всеми остальными данными без изменений.
Изменение цены на позиции меню MenuItemOffer Дополнительные обновления: отправьте инкапсулирующий объект MenuItemOffer с price , установленной на обновленную цену для данного MenuItem , и все остальные данные без изменений.

Добавить новую сущность верхнего уровня

Применимо только для объектов типов Menu , Restaurant и Service .

Menu , Restaurant , Service

Например, вам нужно добавить новое меню в ресторан.

Полные каналы: добавьте объект в свои каналы данных и дождитесь пакетной обработки.

Удалить объект верхнего уровня навсегда

Применимо только для объектов типов Menu , Restaurant и Service .

Menu , Restaurant , Service

Дополнительные обновления: отправьте явное удаление .

Полные фиды. Обязательно удалите объект из полных фидов до следующей загрузки Google, иначе объект будет добавлен повторно.

Добавить новую зону доставки в конкретную Service ServiceArea Инкрементальные каналы: отправьте рассматриваемый объект ServiceArea со всеми его полями, как обычно в полных каналах, с новой областью доставки, указанной в polygon , geoRadius или postalCode .
Обновить расчетное время доставки в Service ServiceHours Инкрементальные каналы: отправляйте ServiceHours так же, как и в каналах, за исключением того, что его leadTimeMin обновляется соответствующим образом.
Обновить цены на доставку в Service Fee Дополнительные фиды: отправьте полную Fee доставки с обновленной price .
Обновите часы доставки или самовывоза в Service ServiceHours Инкрементальные каналы: отправляйте ServiceHours так же, как и в каналах, за исключением того, что его свойства opens и closes обновляются соответствующим образом.
Service (изменение минимальной суммы заказа) Fee Дополнительные фиды: отправьте полную Fee с обновленной minPrice .
Удалить MenuItem навсегда Menu Инкрементные каналы: отправьте MenuItem так же, как и в каналах, но с пустым parentMenuSectionId .

SLO по времени обработки пакетных заданий и дополнительных обновлений

Сущность, обновленная или удаленная в пакетном режиме, будет обработана в течение 2 часов в режиме наилучших усилий, тогда как сущность, обновленная посредством добавочного обновления, будет обработана в течение 5 минут. Устаревший объект удаляется через 7 дней.

Вы можете отправить Google:

  • Несколько пакетных заданий в день для поддержания актуальности ваших запасов, ИЛИ
  • Одно пакетное задание в день и дополнительные API для поддержания актуальности ваших запасов.