Atualizações de inventário incremental da v2

Nesta seção, descrevemos como enviar atualizações urgentes das suas entidades de inventário ao Google. Com a API Incremental Update, é possível enviar atualizações e excluir entidades no seu inventário do sandbox ou de produção quase em tempo real.

Essa funcionalidade é destinada principalmente a atualizações que não podem ser previstas, como fechamentos de emergência. Como regra geral, qualquer alteração enviada por meio da API Incremental Update precisa ser uma alteração que precise ser publicada em menos de uma hora. Se a mudança não precisar ser refletida imediatamente, use a ingestão em lote. As atualizações incrementais são processadas em no máximo cinco minutos.

Pré-requisitos

Os itens a seguir são obrigatórios antes de você implementar as atualizações incrementais:

  1. Uma conta de serviço é criada com o papel de editor no seu projeto do Actions. Para mais detalhes, consulte Criar e configurar um projeto.
  2. Os feeds de dados de produção ou de sandbox são hospedados e ingeridos. Para mais detalhes, consulte Ingestão em lote.
  3. (Opcional, mas recomendado) Instale a biblioteca de cliente do Google na linguagem de sua escolha para facilitar o uso do OAuth 2.0 ao chamar a API. As amostras de código incluídas abaixo usam essas bibliotecas. Caso contrário, você precisará processar trocas de tokens manualmente, conforme descrito em Como usar o OAuth 2.0 para acessar as APIs do Google.

Endpoints

Nas solicitações abaixo, substitua o seguinte:

  • PROJECT_ID: ID do projeto do Google Cloud associado ao projeto criado em Criar e configurar um projeto.
  • TYPE: o tipo de entidade (propriedade @type) do objeto no seu feed de dados que você quer atualizar.
  • ENTITY_ID (excluir endpoint): o ID da entidade a ser excluída. Codifique o código da entidade no URL.
  • DELETE_TIME (apenas excluir endpoint): campo opcional para indicar o horário em que a entidade foi excluída nos seus sistemas. O padrão é quando a solicitação é recebida. O valor de tempo não pode estar no futuro. Ao enviar uma entidade usando uma chamada incremental, o controle de versão da entidade também usa o campo delete_time no caso de uma chamada de exclusão. Formate esse valor como yyyy-mm-ddTHH:mm:ssZ

Atualizar endpoint

Para modificar uma entidade, faça uma solicitação HTTP POST para o endpoint a seguir e inclua um payload de atualizações e adições. É possível fazer atualizações de até mil entidades em uma única chamada de API.

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

Por exemplo, se você quiser atualizar entidades em um projeto com um ID "delivery-provider-id" o endpoint será:

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

Excluir endpoint

Para excluir uma entidade no seu inventário, faça uma solicitação HTTP DELETE para o seguinte endpoint.

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

Por exemplo, para excluir uma entidade "MenuSection" com ID "menuSection_122" do seu projeto "delivery-provider-id" você faria uma chamada da API HTTP DELETE para:

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

Ambiente de sandbox

Para usar a API Incremental Update no inventário do sandbox, siga as orientações nos Endpoints acima, mas faça solicitações para /v2/sandbox/apps/ em vez de /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

Como atualizar entidades

Cada solicitação POST precisa incluir os parâmetros de solicitação junto com o payload JSON que contém os dados estruturados de qualquer tipo de entidade listado no esquema do inventário.

Atualizar payload

O JSON será exibido da mesma forma que no feed de lotes, com as seguintes diferenças:

  • O corpo do payload não pode exceder 5 MB. Assim como nos feeds em lote, sugerimos a remoção de espaços em branco com o objetivo de ajustar mais dados.
  • O envelope é o seguinte:
{
  "requests": [
    {
      "entity": {
        "data":"ENTITY_DATA",
        "name": "apps/project_id>/entities/type/entity_id"
      },
      "update_time":"UPDATE_TIMESTAMP"
    },
  ],
  "vertical": "FOODORDERING"
}

No payload acima, substitua o seguinte:

  • ENTITY_DATA: entidade no formato JSON serializado como uma string. A entidade JSON-LD precisa ser transmitida como uma string no campo data.
  • UPDATE_TIMESTAMP (opcional): carimbo de data/hora em que a entidade foi atualizada nos sistemas. O valor de tempo não pode estar no futuro. O carimbo de data/hora padrão é quando o Google recebe a solicitação. Ao enviar uma entidade por meio de uma solicitação incremental, o controle de versão da entidade também usa o campo update_time no caso de uma solicitação de adição/atualização.

Exemplos

Exemplo 1: atualização de um restaurante

Suponha que você precise atualizar urgentemente o número de telefone de um restaurante. Sua atualização contém o JSON de todo o restaurante.

Considere um feed em lote com a seguinte aparência:

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

A atualização incremental por POST HTTP seria assim:

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

Exemplo 2: atualização de vários restaurantes

Para atualizar duas entidades de restaurante em uma única chamada de API, a solicitação HTTP POST seria a seguinte:

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

Exemplo 3: atualização do preço de um item do cardápio

Imagine que você precise mudar o preço de um item do cardápio. Como no exemplo 1, a atualização precisa conter o JSON de toda a entidade de nível superior (o menu) e o feed usa o esquema de inventário v1.

Considere um feed em lote com a seguinte aparência:

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

Dessa forma, sua atualização incremental via POST seria a seguinte:

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

Adicionar uma entidade

Para adicionar entidades, evite usar atualizações de inventário. Em vez disso, use o processo de feeds em lote conforme descrito no esquema de inventário v2.

Remover uma entidade

Para remover entidades de nível superior, use um endpoint ligeiramente modificado e use HTTP DELETE em vez de HTTP POST na solicitação.

Como excluir uma entidade de nível superior

Considere uma situação em que você quer excluir um restaurante em um feed. Você também precisa excluir os serviços e menus dele.

Um endpoint de amostra para uma entidade de menu com ID "provider/restaurante/menu/nr":

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

Um endpoint de exemplo para uma entidade de restaurante com ID "https://www.provider.com/restaurante/nr":

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

Um endpoint de exemplo para uma entidade de serviço com 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
}

Como remover subentidades

Não use HTTP DELETE para remover uma subentidade dentro de uma entidade de nível superior, como um item de menu no cardápio. Em vez disso, trate a remoção de subentidades como uma atualização de uma entidade de nível superior em que a subentidade é removida da lista relevante ou reverseReference.

Códigos de resposta da API

Uma chamada bem-sucedida não significa que o feed seja válido ou correto, apenas que a chamada de API foi feita. As chamadas bem-sucedidas recebem um código de resposta HTTP 200 com um corpo de resposta vazio:

{}

Em caso de falhas, o código de resposta HTTP não será 200, e o corpo da resposta indica o que deu errado.

Por exemplo, se o usuário tiver definido o valor "vertical" no envelope como FAKE_VERTICAL, você receberá a mensagem abaixo:

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

Amostra de código

Veja abaixo alguns exemplos de como usar a API Incremental Update em várias linguagens. Essas amostras usam as bibliotecas do Google Auth e presumem um feed usando o esquema de inventário v1. Para ver soluções alternativas, consulte Como usar o OAuth 2.0 para aplicativos de servidor para servidor.

Como atualizar entidades

Node.js

Esse código usa a biblioteca de autenticação do 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 = '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

Esse código usa a Biblioteca de autenticação do 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

Esse código usa a biblioteca de autenticação do Google 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);
}

Remover entidades

Node.js

Esse código usa a biblioteca de autenticação do 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

Esse código usa a Biblioteca de autenticação do 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

Esse código usa a biblioteca de autenticação do 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

Os casos de uso abaixo são exemplos de atualizações incrementais, atualizações completas do feed e o conteúdo em alto nível na chamada de API:

Cenário Entidade a ser atualizada Descrição e efeitos
Como desativar um serviço Service

Você precisa desativar um serviço por um motivo imprevisível.

Atualizações incrementais: atualize a entidade Service em questão definindo a propriedade isDisabled como true, mas mantenha as outras propriedades iguais.

Feeds completos: atualize a entidade dos feeds completos para que isDisabled seja definido como true antes da próxima busca pelo Google. Caso contrário, a entidade será reativada.

Item específico esgotado MenuItemOffer Atualizações incrementais: envie a entidade MenuItemOffer de encapsulamento com inventoryLevel definido como 0 para o determinado MenuItem e todos os outros dados inalterados.
Mudança no preço do item de menu MenuItemOffer Atualizações incrementais:envie a entidade MenuItemOffer de encapsulamento com price definido como o preço atualizado para o MenuItem especificado, e todos os outros dados inalterados.

Adicionar nova entidade de nível superior

Aplicável apenas a entidades dos tipos Menu, Restaurant e Service.

Menu, Restaurant, Service

Por exemplo, é necessário adicionar um novo cardápio a um restaurante.

Feeds completos:adicione a entidade nos seus feeds de dados e aguarde a ingestão em lote.

Excluir entidade de nível superior permanentemente

Aplicável apenas a entidades dos tipos Menu, Restaurant e Service.

Menu, Restaurant, Service

Atualizações incrementais: envie uma exclusão explícita.

Feeds completos: remova a entidade dos feeds completos antes da próxima busca do Google. Caso contrário, a entidade será adicionada novamente.

Adicionar uma nova área de entrega em uma Service específica ServiceArea Feeds incrementais: envie a entidade ServiceArea em questão com todos os campos intactos, como você faria normalmente nos feeds completos, com a nova área de entrega especificada em polygon, geoRadius ou postalCode.
Atualizar o horário estimado de chegada em Service ServiceHours Feeds incrementais:envie o ServiceHours da mesma forma que nos feeds, exceto que o leadTimeMin é atualizado de acordo com isso.
Atualizar preços de entrega em Service Fee Feeds incrementais: envie entrega completa Fee com price atualizado.
Atualize o horário de entrega ou para viagem em Service ServiceHours Feeds incrementais: envie ServiceHours da mesma maneira que nos feeds, exceto que as propriedades opens e closes são atualizadas adequadamente.
Service (alterar valor mínimo do pedido) Fee Feeds incrementais: envie Fee completa com minPrice atualizado
Excluir um MenuItem permanentemente Menu Feeds incrementais: envie MenuItem da mesma forma que nos feeds, mas com parentMenuSectionId vazio.

SLO no tempo de processamento de jobs em lote e atualizações incrementais

Uma entidade atualizada ou excluída por meio de um lote será processada em até duas horas no modo de melhor esforço, enquanto uma entidade atualizada por uma atualização incremental será processada em cinco minutos. Uma entidade desatualizada é excluída em sete dias.

Você pode enviar ao Google o seguinte:

  • vários jobs em lote por dia para manter seu inventário atualizado; OU
  • Um job em lote por dia e APIs incrementais para manter seu inventário atualizado.