Actualizaciones de inventario incrementales v1

En esta sección, se describe cómo enviar a Google actualizaciones de tus feeds con plazos definidos. La API de actualizaciones incrementales te permite actualizar y borrar entidades de tus feeds casi en tiempo real.

Esta función está diseñada principalmente para actualizaciones que no puedes prever, como cierres de emergencia. Como regla, cualquier cambio enviado a través de la API de actualizaciones incrementales debe ser un cambio que debe publicarse en no más de una semana. Si el cambio no necesita reflejarse de inmediato, puedes utilizar una actualización por lotes. Las actualizaciones incrementales se procesan en no más de cinco minutos.

Configuración

Para implementar actualizaciones incrementales, haz lo siguiente:

  1. Sigue los pasos descritos en Crea y configura un proyecto para crear un proyecto.
  2. Sigue los pasos descritos en Configura una cuenta de servicio para crear una. Ten en cuenta que debes ser “propietario” del proyecto para agregar un rol de “editor” a la cuenta de servicio.
  3. (Opcional, pero recomendado) Instala la biblioteca cliente de Google en el lenguaje que elijas para facilitar el uso de OAuth 2.0 cuando llames a la API. Las muestras de código que se incluyen a continuación usan estas bibliotecas. De lo contrario, deberás controlar los intercambios de tokens de forma manual, como se describe en Usa OAuth 2.0 para acceder a las APIs de Google.

Endpoint

Para notificar a Google sobre una actualización, realiza una solicitud HTTP POST a la API de actualizaciones incrementales y, luego, incluye una carga útil de actualizaciones y adiciones. El esquema de inventario que usas determina a qué extremo realizar la solicitud:

inventario de v2

https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID:push

inventario v1

https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/ENTITY_ID:push

Para quitar una entidad, realiza una solicitud HTTP DELETE al siguiente extremo que corresponda al esquema de inventario que uses:

inventario de v2

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

inventario v1

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

En las solicitudes anteriores, reemplaza la siguiente información:

  • PROJECT_ID: Es el ID del proyecto de Google Cloud asociado con el proyecto que creaste en Crea y configura un proyecto.
  • TYPE (solo esquema de inventario v2): Es el tipo de entidad (propiedad @type) del objeto en tu feed de datos que deseas actualizar.
  • ENTITY_ID: Es el ID de la entidad incluida en la carga útil. Asegúrate de codificar en formato URL el ID de tu entidad.
  • DELETE_TIME (solo borrar extremo): campo opcional para indicar la hora en que se borró la entidad en tus sistemas (el valor predeterminado es cuando se recibe la solicitud). El valor del tiempo 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. Aplica el formato de yyyy-mm-ddTHH:mm:ssZ a este valor

Por ejemplo, tienes un proyecto con un ID de “delivery-provider-id” que usa el esquema de inventario v2. Deseas realizar cambios en el restaurante con un tipo de entidad de restaurante de "MenuSection" y un ID de entidad de "menuSection_122". El extremo para las actualizaciones de tus datos sería el siguiente:

https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122:push

Para quitar esta misma entidad, tendrías que realizar esta llamada a la API de HTTP DELETE:

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

Solicitudes de la zona de pruebas

Para las solicitudes de zona de pruebas, sigue las instrucciones de Extremo anteriores, pero realiza solicitudes a /v2/sandbox/apps/ en lugar de a /v2/apps/. Por ejemplo, una solicitud de eliminación de la zona de pruebas para el esquema de inventario v2 se estructura de la siguiente manera:

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

Actualizaciones y adiciones

Tus feeds por lotes diarios también deben contener cualquier cambio enviado a través de esta API. De lo contrario, las actualizaciones por lotes reemplazarán los cambios incrementales.

Carga útil

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

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 de tamaño. Al igual que con los feeds por lotes, te sugerimos que quites los espacios en blanco para agregar más datos.
  • El sobre es el siguiente:
{
  "entity": {
    "data":"ENTITY_DATA",
    "vertical":"FOODORDERING"
  },
  "update_time":"UPDATE_TIMESTAMP"
}

En la carga útil anterior, reemplaza lo siguiente:

  • ENTITY_DATA: Entidad en formato JSON serializada como una string. La entidad JSON-LD se debe pasar como una cadena en el campo data.
  • UPDATE_TIMESTAMP (opcional): Es la marca de tiempo del momento en que se actualizó la entidad en tus sistemas. El valor del tiempo 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 la entidad también usa el campo update_time en el caso de una solicitud de adición o actualización.

Cómo actualizar una entidad

Ejemplo 1: Actualización de un restaurante

Supongamos que necesitas actualizar el número de teléfono de un restaurante de forma urgente. Tu actualización contiene el archivo JSON para 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
}

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

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

Ejemplo 2: Actualiza el precio de un producto de menú

Supongamos que necesitas cambiar el precio de un producto del menú. Al igual que en el ejemplo 1, tu actualización debe contener el archivo JSON para toda la entidad de nivel superior (el menú), y el feed usa el esquema de inventario v1.

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

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

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

Agrega una entidad

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

Quita una entidad

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

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 cambio, trata la eliminación de las subentidades como una actualización a una entidad de nivel superior en la que la subentidad se quita de la lista o el parámetro relevantes.

Ejemplo 1: Borra una entidad de nivel superior

Considera una situación en la que deseas borrar un restaurante de un feed que usa el esquema de inventario v1. También debes borrar sus servicios y menús.

Un extremo de muestra para una entidad de menú con el ID “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

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/https%3A%2F%2Fwww.provider.com%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/https%3A%2F%2Fwww.provider.com%2Frestaurant%2Fservice%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
}

Ejemplo 2: Quita subentidades

Para quitar una subentidad dentro de una entidad de nivel superior, envía la entidad de nivel superior sin quitar la subentidad del campo correspondiente. En el siguiente ejemplo, se supone que el feed usa el esquema de inventario v1.

Por ejemplo, para quitar un área de servicio, actualiza el servicio con el área de servicio quitada de la lista 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"
  }
}

Códigos de respuesta de la API

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

{}

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

Por ejemplo, si el usuario estableció el valor "vertical" del sobre en 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 actualizaciones incrementales en varios lenguajes. En estas muestras, se usan las bibliotecas de Google Auth y se supone un feed con el esquema de inventario v1. Si quieres ver soluciones alternativas, consulta Usa OAuth 2.0 para aplicaciones de servidor a servidor.

Actualiza entidades

Node.js

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

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 = '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

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

Cómo quitar entidades

Node.js

Este código usa la biblioteca de autenticación de Google 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 autenticación de Google 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 feed completo y contenido en un nivel alto en la llamada a la API:

Situación Entidad de nivel superior Descripción y efectos
Inhabilita un servicio DisabledService

Debes inhabilitar un servicio por una razón imprevista.

Actualizaciones incrementales: Envía la entidad Service en cuestión con @type cambiado a DisabledService, pero mantén las demás propiedades como están.

Feeds completos: Asegúrate de actualizar la entidad de los feeds completos para que @type tenga el valor DisabledService antes de la próxima recuperación que realice Google. De lo contrario, se volverá a habilitar la entidad.

El artículo específico está agotado Menu Actualizaciones incrementales: Envía la entidad Menu de encapsulación con offer.inventoryLevel configurado en 0 para el MenuItem determinado y todos los demás datos sin cambios.
Cambio de precio del elemento de menú Menu Actualizaciones incrementales: Envía la entidad Menu encapsulada con offer.price establecido en el precio actualizado para el MenuItem determinado y todos los demás datos sin cambios.

Agregar nueva entidad de nivel superior

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

Menu, Restaurant y Service

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

Actualizaciones incrementales: Envía la nueva entidad de menú, junto con la entidad de restaurante con su campo hasMenu, se actualiza en consecuencia.

Borrar la entidad de nivel superior de forma permanente

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

Menu, Restaurant y 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 recuperación que realice Google. De lo contrario, se volverá a agregar.

Agrega una nueva área de entrega a un Service específico Service Feeds incrementales: Envía la entidad Service en cuestión con todos sus campos intactos, como lo harías normalmente en los feeds completos, con el área de entrega nueva especificada en areaServed de Service.
Actualiza la hora estimada de llegada a Service Service Feeds incrementales: Envía el Service igual que en los feeds, excepto que su hoursAvailable.deliveryHours se actualiza en consecuencia.
Actualiza los precios de entrega en Service Service Feeds incrementales: Envía el valor Service completo con offers.priceSpecification.price actualizado.
Actualiza los horarios de comida para llevar o entrega a domicilio en Service Service Feeds incrementales: Envía el Service igual que en los feeds, excepto que su hoursAvailable se actualiza en consecuencia.
Service (cambiar el importe mínimo del pedido) Service Feeds incrementales: Se debe enviar Service completo con Service.offers.priceSpecification.eligibleTransactionVolume actualizado.
Borrar MenuItem de forma permanente Menu Feeds incrementales: Envía el Menu igual que en los feeds, pero con este MenuItem quitado de la lista hasMenuItems.

SLO de tiempo de procesamiento para trabajos por lotes y actualizaciones incrementales

Una entidad que se agregue mediante una actualización por lotes o incremental se procesará en 1 o 2 días. Una entidad que se actualice o se borre mediante un lote se procesará en 2 horas, mientras que una entidad que se actualice mediante una actualización incremental se procesará en 5 minutos. Una entidad inactiva se borra dentro de 7 días.

Puedes enviarle a Google lo siguiente:

  • Varios trabajos por lotes por día para mantener tu inventario actualizado
  • Un trabajo por lotes por día y APIs incrementales para mantener tu inventario actualizado.