Actualizaciones incrementales del inventario de la versión 2

En esta sección, se describe cómo puedes enviar actualizaciones urgentes de tus entidades de inventario a Google. La API de Incremental Update te permite enviar actualizaciones y borrar entidades en tu inventario de Sandbox o de producción casi en tiempo real.

Esta funcionalidad está destinada principalmente a actualizaciones que no puedes prever, como cierres de emergencia. Como regla, cualquier cambio que se envíe a través de la API de actualización incremental debe publicarse en un plazo máximo de una hora. Si no es necesario que el cambio se refleje de inmediato, puedes usar la transferencia masiva. Las actualizaciones incrementales se procesan en un plazo máximo de cinco minutos.

Requisitos previos

Los siguientes elementos son obligatorios antes de implementar actualizaciones incrementales:

  1. Se crea una cuenta de servicio con el rol de editor en tu proyecto de Acciones. Para obtener más detalles, consulta Cómo crear y configurar un proyecto.
  2. Se alojan y transfieren feeds de datos de producción o de zona de pruebas. Para obtener más detalles, consulta Transferencia por lotes.
  3. (Opcional, pero recomendado) Instala la biblioteca cliente de Google en el idioma que prefieras para facilitar el uso de OAuth 2.0 cuando llames a la API. En los ejemplos de código que se incluyen a continuación, se usan estas bibliotecas. De lo contrario, deberás controlar los intercambios de tokens de forma manual, como se describe en Cómo usar OAuth 2.0 para acceder a las APIs de Google.

Extremos

En las siguientes solicitudes, reemplaza lo siguiente:

  • PROJECT_ID: El ID del proyecto de Google Cloud asociado con el proyecto que creaste en Cómo crear y configurar un proyecto.
  • TYPE: Es el tipo de entidad (propiedad @type) del objeto en el feed de datos que deseas actualizar.
  • ENTITY_ID (solo para borrar el extremo): ID de la entidad que se borrará. Asegúrate de codificar en URL el ID de tu entidad.
  • DELETE_TIME (solo extremo de eliminación): Es un campo opcional para indicar el momento en que se borró la entidad en tus sistemas (el valor predeterminado es cuando se recibe la solicitud). El valor de hora no debe ser futuro. Cuando se envía una entidad a través de una llamada incremental, el control de versiones de entidades también usa el campo delete_time en el caso de una llamada de eliminación. Dale formato a este valor como yyyy-mm-ddTHH:mm:ssZ.

Actualizar extremo

Para modificar una entidad, realiza una solicitud HTTP POST al siguiente extremo y, luego, incluye una carga útil de actualizaciones y adiciones. Puedes actualizar hasta 1,000 entidades en una sola llamada a la API.

https://actions.googleapis.com/v2/apps/PROJECT_ID/entities:batchPush

Por ejemplo, si deseas actualizar entidades en un proyecto con un ID "delivery-provider-id", el extremo sería el siguiente:

https://actions.googleapis.com/v2/apps/delivery-provider-id/entities:batchpush

Borrar extremo

Para borrar una entidad de tu inventario, realiza una solicitud HTTP DELETE al siguiente extremo.

https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME

Por ejemplo, para borrar una entidad "MenuSection" con el ID "menuSection_122" de tu proyecto "delivery-provider-id", debes realizar una llamada a la API HTTP DELETE a la siguiente URL:

https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING

Entorno de zona de pruebas

Para usar la API de Incremental Update en tu inventario de zona de pruebas, sigue las instrucciones de los Endpoints anteriores, pero realiza solicitudes a /v2/sandbox/apps/ en lugar de a /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

Actualiza entidades

Cada solicitud POST debe incluir los parámetros de solicitud junto con la carga útil JSON que contiene los datos estructurados de cualquier tipo de entidad que se indique en el esquema del inventario.

Actualiza la carga útil

El JSON debería aparecer igual que en el feed por lotes, con las siguientes diferencias:

  • El cuerpo de la carga útil no debe superar los 5 MB. Al igual que con los feeds por lotes, te sugerimos que quites los espacios en blanco para que se ajusten más datos.
  • El sobre es el siguiente:
{
  "requests": [
    {
      "entity": {
        "data":"ENTITY_DATA",
        "name": "apps/project_id>/entities/type/entity_id"
      },
      "update_time":"UPDATE_TIMESTAMP"
    },
  ],
  "vertical": "FOODORDERING"
}

En la carga útil anterior, reemplaza lo siguiente:

  • ENTITY_DATA: Entidad en formato JSON serializada como una cadena. La entidad JSON-LD se debe pasar como una cadena en el campo data.
  • UPDATE_TIMESTAMP (opcional): Es la marca de tiempo en la que se actualizó la entidad en tus sistemas. El valor de hora no debe ser futuro. La marca de tiempo predeterminada es el momento en que Google recibe la solicitud. Cuando se envía una entidad a través de una solicitud incremental, el control de versiones de entidades también usa el campo update_time en el caso de una solicitud de adición o actualización.

Ejemplos

Ejemplo 1: Actualiza un restaurante

Supongamos que necesitas actualizar con urgencia el número de teléfono de un restaurante. Tu actualización contiene el JSON de todo el restaurante.

Considera un feed por lotes que se vea de la siguiente manera:

{
  "@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
}

Luego, tu actualización incremental por HTTP POST sería la siguiente:

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"
}

Ejemplo 2: Actualiza varios restaurantes

Para actualizar dos entidades de restaurantes en una sola llamada a la API, la solicitud HTTP POST sería la siguiente:

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"
}

Ejemplo 3: Actualiza el precio de un elemento de menú

Supongamos que necesitas cambiar el precio de un elemento de menú. Al igual que en el ejemplo 1, tu actualización debe contener el JSON de toda la entidad de nivel superior (el menú) y el feed debe usar el esquema de inventario de la versión 1.

Considera un feed por lotes que se vea de la siguiente manera:

{
  "@type": "MenuItemOffer",
  "@id": "menuitemoffer6680262",
  "sku": "offer-cola",
  "menuItemId": "menuitem896532",
  "price": 3.00,
  "priceCurrency": "USD"
}

Luego, tu actualización incremental a través de POST sería la siguiente:

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"
}

Cómo agregar una entidad

Para agregar entidades, evita usar las actualizaciones de inventario. En su lugar, usa el proceso de feeds por lotes como se describe para el esquema de inventario v2.

Cómo quitar una entidad

Para quitar entidades de nivel superior, usa un extremo ligeramente modificado y HTTP DELETE en lugar de HTTP POST en la solicitud.

Cómo borrar una entidad de nivel superior

Imagina una situación en la que quieres borrar un restaurante de un feed. También debes borrar sus servicios y menús.

Un extremo de muestra para una entidad de menú con el ID "provider/restaurant/menu/nr":

DELETE v2/apps/delivery-provider-id/entities/menu/provider%2Frestaurant%2Fmenu%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com

Un extremo de muestra para una entidad de restaurante con el ID "https://www.provider.com/restaurant/nr":

DELETE v2/apps/delivery-provider-id/entities/restaurant/provider%2Frestaurant%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com

Un extremo de muestra para una entidad de servicio con el ID "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
}

Cómo quitar subentidades

No uses HTTP DELETE para quitar una subentidad dentro de una entidad de nivel superior, como un elemento de menú dentro de un menú. En su lugar, trata la eliminación de subentidades como una actualización de una entidad de nivel superior en la que se quita la subentidad de la lista relevante o reverseReference.

Códigos de respuesta de la API

Una llamada correcta no significa que el feed sea válido o correcto, solo que se realizó la llamada a la API. Las llamadas correctas reciben un código de respuesta HTTP 200, junto con un cuerpo de respuesta vacío:

{}

En el caso de las fallas, el código de respuesta HTTP no será 200, y el cuerpo de la respuesta indicará qué salió mal.

Por ejemplo, si el usuario estableció el valor "vertical" en el sobre como FAKE_VERTICAL, recibirás el siguiente mensaje:

{
  "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\""
          }
        ]
      }
    ]
  }
}

Muestra de código

A continuación, se muestran algunos ejemplos de cómo usar la API de Incremental Update en varios idiomas. En estos ejemplos, se usan las bibliotecas de Google Auth y se supone que un feed usa el esquema de inventario v1. Si deseas obtener soluciones alternativas, consulta Cómo usar OAuth 2.0 para aplicaciones de servidor a servidor.

Actualiza entidades

Node.js

En este código, se usa la biblioteca de Google Auth para 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

Este código usa la biblioteca de autenticación de Google para 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

Este código usa la biblioteca de Google Auth para 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);
}

Cómo quitar entidades

Node.js

En este código, se usa la biblioteca de Google Auth para 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

Este código usa la biblioteca de autenticación de Google para 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

Este código usa la biblioteca de Google Auth para 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));
}

Casos de uso

Los siguientes casos de uso son ejemplos de actualizaciones incrementales, actualizaciones de feeds completas y el contenido a un nivel alto en la llamada a la API:

Situación Entidad que se actualizará Descripción y efectos
Inhabilita un servicio Service

Debes inhabilitar un servicio por un motivo imprevisto.

Actualizaciones incrementales: Actualiza la entidad Service en cuestión configurando su propiedad isDisabled en true, pero mantén las demás propiedades iguales.

Feeds completos: Asegúrate de actualizar la entidad de los feeds completos para que isDisabled esté configurado como true antes de la próxima recuperación de Google. De lo contrario, la entidad se volverá a habilitar.

Un artículo específico está agotado MenuItemOffer Actualizaciones incrementales: Envía la entidad MenuItemOffer encapsulada con inventoryLevel establecido en 0 para el MenuItem determinado y sin cambios en los demás datos.
Cambio de precio del elemento del menú MenuItemOffer Actualizaciones incrementales: Envía la entidad MenuItemOffer encapsulada con price establecida en el precio actualizado para el MenuItem determinado y sin cambios en los demás datos.

Agrega una nueva entidad de nivel superior

Solo se aplica a entidades de los tipos Menu, Restaurant y Service.

Menu, Restaurant, Service

Por ejemplo, debes agregar un menú nuevo a un restaurante.

Feeds completos: Agrega la entidad a tus feeds de datos y espera la transferencia por lotes.

Cómo borrar de forma permanente una entidad de nivel superior

Solo se aplica a entidades de los tipos Menu, Restaurant y Service.

Menu, Restaurant, Service

Actualizaciones incrementales: Envía una eliminación explícita.

Feeds completos: Asegúrate de quitar la entidad de los feeds completos antes de la próxima actualización de Google. De lo contrario, la entidad se volverá a agregar.

Cómo agregar una nueva área de entrega en un Service específico ServiceArea Feeds incrementales: Envía la entidad ServiceArea en cuestión con todos sus campos intactos, como lo harías normalmente en los feeds completos, con una nueva área de publicación especificada en polygon, geoRadius o postalCode.
Actualiza la hora de llegada estimada de la entrega en Service ServiceHours Feeds incrementales: Envía el ServiceHours igual que en los feeds, excepto que su leadTimeMin se actualiza según corresponda.
Actualizar los precios de entrega en Service Fee Feeds incrementales: Envía una publicación completa Fee con price actualizado.
Actualiza los horarios de entrega o para llevar en Service ServiceHours Feeds incrementales: Envía el ServiceHours de la misma manera que en los feeds, excepto que sus propiedades opens y closes se actualizan según corresponda.
Service (cambia el importe mínimo del pedido) Fee Feeds incrementales: Envía un Fee completo con minPrice actualizado.
Cómo borrar un MenuItem de forma permanente Menu Feeds incrementales: Envía el MenuItem igual que en los feeds, pero con parentMenuSectionId vacío.

SLO sobre el tiempo de procesamiento de trabajos por lotes y actualizaciones incrementales

Una entidad actualizada o borrada a través de un lote se procesará en un plazo de 2 horas en el modo de mayor esfuerzo, mientras que una entidad actualizada a través de una actualización incremental se procesará en 5 minutos. Una entidad inactiva se borra en 7 días.

Puedes enviar a Google lo siguiente:

  • Varios trabajos por lotes por día para mantener tu inventario actualizado
  • Una tarea por lotes por día y APIs incrementales para mantener tu inventario actualizado