Mises à jour incrémentielles de l'inventaire (v2)

Cette section explique comment envoyer à Google des mises à jour urgentes des entités d'inventaire. L'API de mise à jour incrémentielle vous permet d'envoyer des mises à jour et de supprimer des entités de votre inventaire de bac à sable ou de production en temps quasi réel.

Cette fonctionnalité est principalement destinée aux mises à jour que vous ne pouvez pas prévoir, comme les fermetures d'urgence. En règle générale, toute modification envoyée via l'API incrémentielle Mise à jour doit être mise en ligne en moins d'une heure. Si votre modification n'a pas besoin d'être répercutée immédiatement, vous pouvez utiliser l'ingestion par lots. Le traitement des mises à jour incrémentielles ne prend pas plus de cinq minutes.

Conditions préalables

Les éléments suivants sont requis avant d'implémenter des mises à jour incrémentielles:

  1. Un compte de service est créé avec le rôle Éditeur pour votre projet Actions. Pour en savoir plus, consultez Créer et configurer un projet.
  2. Les flux de données de production ou de bac à sable sont hébergés et ingérés. Pour en savoir plus, consultez la page Ingestion par lot.
  3. (Facultatif, mais recommandé) Installez la bibliothèque cliente Google dans le langage de votre choix pour faciliter l'utilisation d'OAuth 2.0 lorsque vous appelez l'API. Les exemples de code ci-dessous utilisent ces bibliothèques. Sinon, vous devrez gérer les échanges de jetons manuellement, comme décrit dans la section Utiliser OAuth 2.0 pour accéder aux API Google.

Points de terminaison

Dans les requêtes ci-dessous, remplacez les éléments suivants:

  • PROJECT_ID: ID du projet Google Cloud associé au projet que vous avez créé dans la section Créer et configurer un projet.
  • TYPE: type d'entité (propriété @type) de l'objet de votre flux de données que vous souhaitez mettre à jour.
  • ENTITY_ID (supprimer le point de terminaison uniquement): ID de l'entité à supprimer. Veillez à encoder votre ID d'entité au format URL.
  • DELETE_TIME (supprimer le point de terminaison uniquement): champ facultatif pour indiquer l'heure à laquelle l'entité a été supprimée de vos systèmes (la valeur par défaut correspond à la réception de la requête). La valeur de l'heure ne doit pas se situer dans le futur. Lors de l'envoi d'une entité via un appel incrémentiel, la gestion des versions d'entité utilise également le champ delete_time dans le cas d'un appel de suppression. Mettez en forme cette valeur au format yyyy-mm-ddTHH:mm:ssZ.

Mettre à jour le point de terminaison

Pour modifier une entité, envoyez une requête HTTP POST au point de terminaison suivant et incluez une charge utile de mises à jour et ajouts. Vous pouvez mettre à jour jusqu'à 1 000 entités dans un seul appel d'API.

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

Par exemple, si vous souhaitez mettre à jour les entités d'un projet ayant l'ID "delivery-provider-id", le point de terminaison se présente comme suit:

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

Supprimer un point de terminaison

Pour supprimer une entité de votre inventaire, envoyez une requête HTTP DELETE au point de terminaison ci-dessous.

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

Par exemple, pour supprimer une entité "MenuSection" portant l'ID "menuSection_122" de votre projet "delivery-provider-id", vous devez effectuer un appel d'API HTTP DELETE pour:

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

Environnement de bac à sable

Pour utiliser l'API de mise à jour incrémentielle dans l'inventaire de votre bac à sable, suivez les instructions de la section Points de terminaison ci-dessus, mais envoyez des requêtes à /v2/sandbox/apps/ au lieu 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

Mettre à jour des entités

Chaque requête POST doit inclure les paramètres de requête ainsi que la charge utile JSON contenant les données structurées de tout type d'entité répertorié dans le schéma d'inventaire.

Mettre à jour la charge utile

Le fichier JSON doit être identique à celui du flux par lot, avec les différences suivantes:

  • La taille du corps de la charge utile ne doit pas dépasser 5 Mo. Comme pour les flux par lot, nous vous suggérons de supprimer les espaces blancs afin d'ajuster plus de données.
  • L'enveloppe se présente comme suit:
{
  "requests": [
    {
      "entity": {
        "data":"ENTITY_DATA",
        "name": "apps/project_id>/entities/type/entity_id"
      },
      "update_time":"UPDATE_TIMESTAMP"
    },
  ],
  "vertical": "FOODORDERING"
}

Dans la charge utile ci-dessus, remplacez ce qui suit:

  • ENTITY_DATA: entité au format JSON sérialisée en tant que chaîne. L'entité JSON-LD doit être transmise sous forme de chaîne dans le champ data.
  • UPDATE_TIMESTAMP (facultatif): code temporel de la mise à jour de l'entité dans vos systèmes. La valeur de l'heure ne doit pas se situer dans le futur. L'horodatage par défaut correspond au moment où Google reçoit la requête. Lors de l'envoi d'une entité via une requête incrémentielle, la gestion des versions d'entité utilise également le champ update_time dans le cas d'une requête d'ajout/de mise à jour.

Exemples

Exemple 1: Mettre à jour un restaurant

Supposons que vous ayez besoin de mettre à jour en urgence le numéro de téléphone d'un restaurant. Votre mise à jour contient le fichier JSON de l'ensemble du restaurant.

Prenons l'exemple d'un flux groupé semblable à celui-ci:

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

La mise à jour incrémentielle par HTTP POST se présenterait alors comme suit:

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

Exemple 2: Mettre à jour plusieurs restaurants

Pour mettre à jour deux entités de restaurant dans un seul appel d'API, la requête HTTP POST se présenterait comme suit:

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

Exemple 3: Mettre à jour le prix d'un élément de menu

Supposons que vous deviez modifier le prix d'un élément de menu. Comme dans l'exemple 1, votre mise à jour doit contenir le code JSON de l'entité de premier niveau entière (le menu), et le flux utilise le schéma d'inventaire v1.

Prenons l'exemple d'un flux groupé semblable à celui-ci:

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

La mise à jour incrémentielle via POST se présenterait alors comme suit:

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

Ajouter une entité

Pour ajouter des entités, évitez d'utiliser les mises à jour d'inventaire. Utilisez plutôt le processus de flux par lot comme décrit dans le schéma d'inventaire v2.

Supprimer une entité

Pour supprimer les entités de niveau supérieur, utilisez un point de terminaison légèrement modifié, et utilisez HTTP DELETE au lieu de HTTP POST dans la requête.

Supprimer une entité de niveau supérieur

Imaginons que vous souhaitiez supprimer un restaurant dans un flux. Vous devez également supprimer ses services et menus.

Exemple de point de terminaison pour une entité de menu avec l'ID "provider/restaurant/menu/nr":

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

Exemple de point de terminaison pour une entité de restaurant ayant l'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

Exemple de point de terminaison pour une entité de service ayant l'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
}

Suppression de sous-entités

N'utilisez pas HTTP DELETE pour supprimer une sous-entité d'une entité de niveau supérieur, comme un élément de menu dans un menu. Traitez plutôt la suppression des sous-entités comme une mise à jour d'une entité de niveau supérieur dans laquelle la sous-entité est supprimée de la liste ou de la valeur reverseReference correspondantes.

Codes de réponse de l'API

Un appel réussi ne signifie pas que le flux est valide ou correct, mais uniquement que l'appel d'API a été effectué. Les appels réussis reçoivent un code de réponse HTTP 200, ainsi qu'un corps de réponse vide:

{}

En cas d'échec, le code de réponse HTTP ne sera pas 200 et le corps de la réponse indiquera le problème.

Par exemple, si l'utilisateur a défini la valeur "verticale" dans l'enveloppe sur FAKE_VERTICAL, le message ci-dessous s'affiche:

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

Exemple de code

Vous trouverez ci-dessous des exemples d'utilisation de l'API incrémentalité de mises à jour dans différents langages. Ces exemples utilisent les bibliothèques Google Auth et supposent la présence d'un flux à l'aide du schéma d'inventaire v1. Pour trouver d'autres solutions, reportez-vous à la page Utiliser OAuth 2.0 pour l'authentification serveur à serveur.

Mettre à jour des entités

Node.js

Ce code utilise la bibliothèque d'authentification Google pour 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

Ce code utilise la bibliothèque d'authentification Google pour 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

Ce code utilise la bibliothèque d'authentification Google pour 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);
}

Suppression d'entités

Node.js

Ce code utilise la bibliothèque d'authentification Google pour 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

Ce code utilise la bibliothèque d'authentification Google pour 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

Ce code utilise la bibliothèque d'authentification Google pour 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));
}

Cas d'utilisation

Les cas d'utilisation suivants sont des exemples de mises à jour incrémentielles, de mises à jour de flux complètes et du contenu au haut niveau dans l'appel d'API:

Scénario Entité à mettre à jour Description et effets
Désactiver un service Service

Vous devez désactiver un service pour une raison imprévue.

Mises à jour incrémentielles:mettez à jour l'entité Service en question en définissant sa propriété isDisabled sur true, mais conservez les autres propriétés.

Flux complets:veillez à mettre à jour l'entité à partir des flux complets en définissant isDisabled sur true avant la prochaine récupération effectuée par Google. Sinon, l'entité sera réactivée.

Un article spécifique n'est pas disponible MenuItemOffer Mises à jour incrémentielles:envoie l'entité MenuItemOffer d'encapsulation avec inventoryLevel défini sur 0 pour le MenuItem donné, et toutes les autres données inchangées.
Changement de prix d'un élément de menu MenuItemOffer Mises à jour incrémentielles:envoie l'entité MenuItemOffer encapsulée avec price défini sur le prix mis à jour pour le MenuItem donné, et toutes les autres données non modifiées.

Ajouter une entité de niveau supérieur

Ne s'applique qu'aux entités de types Menu, Restaurant et Service.

Menu, Restaurant et Service

Par exemple, vous devez ajouter un nouveau menu à un restaurant.

Flux complets:ajoutez l'entité à vos flux de données et attendez l'ingestion par lot.

Supprimer définitivement l'entité de niveau supérieur

Ne s'applique qu'aux entités de types Menu, Restaurant et Service.

Menu, Restaurant et Service

Mises à jour incrémentielles:envoyez une suppression explicite.

Flux complets:veillez à supprimer l'entité des flux complets avant la prochaine extraction par Google, sinon elle sera de nouveau ajoutée.

Ajouter une zone de livraison dans un Service spécifique ServiceArea Flux incrémentiels:envoyez l'entité ServiceArea en question avec tous ses champs intacts, comme vous le feriez normalement dans les flux complets, avec une nouvelle zone de livraison spécifiée entre polygon, geoRadius ou postalCode.
Modifier l'heure d'arrivée prévue à Service ServiceHours Flux incrémentiels:envoyez l'élément ServiceHours de la même manière que dans les flux, sauf que son leadTimeMin est mis à jour en conséquence.
Modifier les prix de livraison (Service) Fee Flux incrémentiels:envoyez le Fee à diffusion complète avec la valeur price mise à jour.
Modifier les horaires de livraison ou de vente à emporter dans Service ServiceHours Flux incrémentiels:envoyez l'élément ServiceHours de la même manière que dans les flux, sauf que ses propriétés opens et closes sont mises à jour en conséquence.
Service (modifier le montant minimal de commande) Fee Flux incrémentiels:envoyer la version Fee complète avec la valeur minPrice mise à jour
Supprimer définitivement un MenuItem Menu Flux incrémentiels:envoyez l'élément MenuItem de la même manière que dans les flux, mais avec le champ parentMenuSectionId vide.

SLO concernant le temps de traitement des jobs par lot et des mises à jour incrémentielles

Une entité mise à jour ou supprimée via un lot est traitée en deux heures en mode d'optimisation, tandis qu'une entité mise à jour par le biais d'une mise à jour incrémentielle est traitée en cinq minutes. Une entité obsolète est supprimée au bout de sept jours.

Vous pouvez envoyer à Google:

  • plusieurs jobs par lot par jour pour maintenir votre inventaire à jour, OU
  • Une tâche par lot par jour et des API incrémentielles pour maintenir votre inventaire à jour.