이 섹션에서는 인벤토리 항목의 시간에 민감한 업데이트를 Google에 전송하는 방법을 설명합니다. 증분 업데이트 API를 사용하면 거의 실시간으로 샌드박스 또는 프로덕션 인벤토리의 업데이트를 푸시하고 항목을 삭제할 수 있습니다.
이 기능은 긴급 폐쇄와 같이 예상할 수 없는 업데이트를 주로 위해 마련되었습니다. 일반적으로 증분 업데이트 API를 통해 제출된 변경사항은 1시간 이내에 적용되어야 하는 변경사항이어야 합니다. 변경사항을 즉시 반영할 필요가 없는 경우 대신 일괄 처리를 사용하면 됩니다. 증분 업데이트는 5분 이내에 처리됩니다.
기본 요건
증분 업데이트를 구현하기 전에 다음 항목이 필요합니다.
- Actions 프로젝트에 대한 편집자 역할이 있는 서비스 계정이 생성됩니다. 자세한 내용은 프로젝트 만들기 및 설정을 참고하세요.
- 프로덕션 또는 샌드박스 데이터 피드가 호스팅되고 처리됩니다. 자세한 내용은 일괄 처리를 참고하세요.
- (선택사항이지만 권장됨) API를 호출할 때 OAuth 2.0을 쉽게 사용할 수 있도록 원하는 언어로 Google 클라이언트 라이브러리를 설치합니다. 아래에 포함된 코드 샘플은 이러한 라이브러리를 사용합니다. 그러지 않으면 OAuth 2.0을 사용하여 Google API에 액세스하기에 설명된 대로 토큰 교환을 수동으로 처리해야 합니다.
엔드포인트
아래 요청에서 다음을 바꿉니다.
- PROJECT_ID: 프로젝트 만들기 및 설정에서 만든 프로젝트와 연결된 Google Cloud 프로젝트 ID입니다.
- TYPE: 업데이트하려는 데이터 피드의 객체의 항목 유형 (
@type
속성)입니다. - ENTITY_ID (삭제 엔드포인트만 해당): 삭제할 항목의 ID입니다. 엔티티 ID를 URL 인코딩해야 합니다.
- DELETE_TIME (삭제 엔드포인트만 해당): 시스템에서 항목이 삭제된 시간을 나타내는 선택사항 필드입니다 (기본값은 요청이 수신된 시간). 시간 값은 미래일 수 없습니다. 증분 호출을 통해 항목을 전송할 때 항목 버전 관리는 삭제 호출의 경우에도
delete_time
필드를 사용합니다. 이 값의 형식을yyyy-mm-ddTHH:mm:ssZ
로 지정합니다.
엔드포인트 업데이트
항목을 수정하려면 다음 엔드포인트에 HTTP POST 요청을 실행하고 업데이트 및 추가 페이로드를 포함합니다. 단일 API 호출로 최대 1,000개의 항목을 업데이트할 수 있습니다.
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities:batchPush
예를 들어 ID가 'delivery-provider-id'인 프로젝트의 항목을 업데이트하려면 엔드포인트는 다음과 같습니다.
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities:batchpush
엔드포인트 삭제
인벤토리의 항목을 삭제하려면 다음 엔드포인트에 HTTP DELETE 요청을 보냅니다.
https://actions.googleapis.com/v2/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
예를 들어 'delivery-provider-id' 프로젝트에서 ID가 'menuSection_122'인 'MenuSection' 항목을 삭제하려면 다음에 HTTP DELETE API를 호출합니다.
https://actions.googleapis.com/v2/apps/delivery-provider-id/entities/MenuSection/menuSection_122?entity.vertical=FOODORDERING
샌드박스 환경
샌드박스 인벤토리에서 증분 업데이트 API를 사용하려면 위의 엔드포인트에 나온 안내를 따르되 /v2/apps/
대신 /v2/sandbox/apps/
에 요청합니다.
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities:batchPush
https://actions.googleapis.com/v2/sandbox/apps/PROJECT_ID/entities/TYPE/ENTITY_ID?entity.vertical=FOODORDERING&delete_time=DELETE_TIME
항목 업데이트
각 POST 요청에는 인벤토리 스키마에 나열된 모든 항목 유형의 정형 데이터가 포함된 JSON 페이로드와 함께 요청 매개변수가 포함되어야 합니다.
페이로드 업데이트
JSON은 일괄 피드와 동일하게 표시되지만 다음과 같은 차이점이 있습니다.
- 페이로드 본문의 크기는 5MB 이하여야 합니다. 일괄 피드와 마찬가지로 더 많은 데이터를 맞추기 위해 공백을 삭제하는 것이 좋습니다.
- 봉투는 다음과 같습니다.
{ "requests": [ { "entity": { "data":"ENTITY_DATA", "name": "apps/project_id>/entities/type/entity_id" }, "update_time":"UPDATE_TIMESTAMP" }, ], "vertical": "FOODORDERING" }
위의 페이로드에서 다음을 바꿉니다.
- ENTITY_DATA: JSON 형식의 항목으로 문자열로 직렬화됩니다. JSON-LD 항목은
data
필드에서 문자열로 전달되어야 합니다. - UPDATE_TIMESTAMP (선택사항): 시스템에서 항목이 업데이트된 타임스탬프입니다. 시간 값은 미래일 수 없습니다. 기본 타임스탬프는 Google에서 요청을 수신한 시점입니다. 증분 요청을 통해 항목을 전송할 때 항목 버전 관리는 추가/업데이트 요청의 경우에도
update_time
필드를 사용합니다.
예
예시 1: 음식점 업데이트
식당의 전화번호를 긴급하게 업데이트해야 한다고 가정해 보겠습니다. 업데이트에는 전체 레스토랑의 JSON이 포함됩니다.
다음과 같은 일괄 피드를 고려해 보겠습니다.
{ "@type": "Restaurant", "@id": "restaurant12345", "name": "Some Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501234567", "streetAddress": "345 Spear St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US", "latitude": 37.472842, "longitude": -122.217144 }
그러면 HTTP POST를 통한 증분 업데이트는 다음과 같습니다.
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant12345", "data": { "@type": "Restaurant", "@id": "restaurant12345", "name": "Some Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501235555", "streetAddress": "345 Spear St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US", "latitude": 37.472842, "longitude": -122.217144 } } } "vertical": "FOODORDERING" }
예 2: 여러 레스토랑 업데이트
단일 API 호출에서 레스토랑 항목 2개를 업데이트하려면 HTTP POST 요청이 다음과 같습니다.
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant12345", "data": { "@type": "Restaurant", "@id": "restaurant12345", "name": "Some Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501235555", "streetAddress": "345 Spear St", "addressLocality": "San Francisco", "addressRegion": "CA", "postalCode": "94105", "addressCountry": "US", "latitude": 37.472842, "longitude": -122.217144 } } }, { "entity": { "name": "apps/provider-project/entities/restaurant/restaurant123", "data": { "@type": "Restaurant", "@id": "restaurant123", "name": "Some Other Restaurant", "url": "https://www.provider.com/somerestaurant", "telephone": "+16501231235", "streetAddress": "385 Spear St", "addressLocality": "San Mateo", "addressRegion": "CA", "postalCode": "94115", "addressCountry": "US" } } } ] "vertical": "FOODORDERING" }
예시 3: 메뉴 항목 가격 업데이트
메뉴 항목의 가격을 변경해야 한다고 가정해 보겠습니다. 예 1과 같이 업데이트에는 전체 최상위 항목 (메뉴)의 JSON이 포함되어야 하며 피드는 v1 인벤토리 스키마를 사용합니다.
다음과 같은 일괄 피드를 고려해 보겠습니다.
{ "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 3.00, "priceCurrency": "USD" }
그러면 POST를 통한 증분 업데이트는 다음과 같습니다.
POST v2/sandbox/apps/provider-project/entities:batchPush Host: actions.googleapis.com Content-Type: application/ld+json { "requests": [ { "entity": { "name": "apps/provider-project/entities/menuitemoffer/menuitemoffer6680262", "data": { "@type": "MenuItemOffer", "@id": "menuitemoffer6680262", "sku": "offer-cola", "menuItemId": "menuitem896532", "price": 1.00, "priceCurrency": "USD" }, "vertical": "FOODORDERING" } } ] "vertical": "FOODORDERING" }
항목 추가
항목을 추가하려면 인벤토리 업데이트를 사용하지 마세요. 대신 v2 인벤토리 스키마에 설명된 대로 일괄 피드 프로세스를 사용하세요.
항목 삭제
최상위 항목을 삭제하려면 약간 수정된 엔드포인트를 사용하고 요청에서 HTTP POST 대신 HTTP DELETE를 사용합니다.
최상위 항목 삭제
피드에서 식당을 삭제하려는 상황을 가정해 보겠습니다. 서비스와 메뉴도 삭제해야 합니다.
ID가 'provider/restaurant/menu/nr'인 메뉴 항목의 샘플 엔드포인트는 다음과 같습니다.
DELETE v2/apps/delivery-provider-id/entities/menu/provider%2Frestaurant%2Fmenu%2Fnr?entity.vertical=FOODORDERING
Host: actions.googleapis.com
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
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
}
하위 항목 삭제
HTTP DELETE를 사용하여 메뉴 내 메뉴 항목과 같이 최상위 항목 내 하위 항목을 삭제하지 마세요. 대신 하위 항목 삭제를 관련 목록 또는 reverseReference에서 하위 항목이 삭제되는 최상위 항목 업데이트로 취급합니다.
API 응답 코드
호출에 성공했다고 해서 피드가 유효하거나 올바르다는 의미는 아닙니다. API 호출이 이루어졌음을 의미할 뿐입니다. 호출이 성공하면 HTTP 응답 코드 200과 빈 응답 본문이 수신됩니다.
{}
실패의 경우 HTTP 응답 코드가 200이 아니며 응답 본문에는 문제가 무엇인지 표시됩니다.
예를 들어 사용자가 봉투에서 'vertical' 값을 FAKE_VERTICAL
로 설정하면 아래 메시지가 표시됩니다.
{
"error": {
"code": 400,
"message": "Invalid value at 'entity.vertical' (TYPE_ENUM), \"FAKE_VERTICAL\"",
"status": "INVALID_ARGUMENT",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "entity.vertical",
"description": "Invalid value at 'entity.vertical' (TYPE_ENUM), \"FAKE_VERTICAL\""
}
]
}
]
}
}
코드 샘플
다음은 다양한 언어로 Incremental Update API를 사용하는 방법을 보여주는 샘플입니다. 이 샘플은 Google 인증 라이브러리를 사용하며 v1 인벤토리 스키마를 사용하는 피드를 가정합니다. 대체 솔루션은 서버 간 애플리케이션에 OAuth 2.0 사용을 참고하세요.
항목 업데이트
Node.js
이 코드는 Node.js용 Google 인증 라이브러리를 사용합니다.
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
이 코드는 Python용 Google 인증 라이브러리를 사용합니다.
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용 Google 인증 라이브러리를 사용합니다.
private static final String PROJECT_ID = "your-project-id"; private static final String ENTITY_ID = "type/your-entity-id"; /** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/assistant")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an incremental update to update or add an entity. * @param entityId The id of the entity to update. * @param entity the json of the entity to be updated. */ public void updateEntity(String entityId, JSONObject data) { String authToken = getAuthToken(); String endpoint = String.format("https://actions.googleapis.com/v2/apps/%s/entities/:batchPush", PROJECT_ID); JSONObject entity = new JSONObject(); entity.put("data", data.toString()); entity.put("name", String.format("apps/%s/entities/%s", PROJECT_ID, URLEncoder.encode(ENTITY_ID, "UTF-8"))); JSONObject request = new JSONObject(); request.put("entity", entity); JSONArray requestArray = new JSONArray(); requestArray.put(request); JSONObject payload = new JSONObject(); payload.put("requests", requestArray); payload.put("vertical", FOODORDERING); // Execute POST request executePostRequest(endpoint, authToken, payload); }
항목 삭제
Node.js
이 코드는 Node.js용 Google 인증 라이브러리를 사용합니다.
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
이 코드는 Python용 Google 인증 라이브러리를 사용합니다.
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용 Google 인증 라이브러리를 사용합니다.
private static final String PROJECT_ID = "your-project-id"; private static final String ENTITY_ID = "restaurant/http://www.provider.com/somerestaurant"; /** * Get the authorization token using a service account. */ private static String getAuthToken() { InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json"); ServiceAccountCredentials.Builder credentialsSimpleBuilder = ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder(); credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/assistant")); AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken(); return accessToken.getTokenValue(); } /** * Send an incremental update to delete an entity. * @param entityId The id of the entity to delete. */ public void deleteEntity(String entityId) { String authToken = getAuthToken(); String endpoint = String.format( "https://actions.googleapis.com/v2/apps/%s/entities/%s?entity.vertical=FOODORDERING", PROJECT_ID, URLEncoder.encode(entityId, "UTF-8")); // Execute DELETE request System.out.println(executeDeleteRequest(endpoint, authToken)); }
사용 사례
다음은 점진적 업데이트, 전체 피드 업데이트, API 호출의 대략적인 콘텐츠의 예시입니다.
시나리오 | 업데이트할 항목 | 설명 및 효과 |
---|---|---|
서비스 사용 중지 | Service |
예상치 못한 이유로 서비스를 사용 중지해야 합니다. 증분 업데이트: 전체 피드: Google에서 다음으로 가져오기 전에 전체 피드에서 항목을 업데이트하여 |
특정 상품 재고 없음 | MenuItemOffer |
증분 업데이트: 지정된 MenuItem 의 inventoryLevel 를 0으로 설정하고 다른 모든 데이터는 변경하지 않은 캡슐화 MenuItemOffer 항목을 전송합니다. |
메뉴 항목 가격 변경 | MenuItemOffer |
증분 업데이트: price 를 지정된 MenuItem 의 업데이트된 가격으로 설정하고 다른 모든 데이터는 변경하지 않은 채 캡슐화된 MenuItemOffer 항목을 전송합니다. |
새 최상위 항목 추가
|
Menu , Restaurant , Service |
예를 들어 식당에 새 메뉴를 추가해야 합니다. 전체 피드: 데이터 피드에 항목을 추가하고 일괄 처리를 기다립니다. |
최상위 항목 영구 삭제
|
Menu , Restaurant , Service |
증분 업데이트: 명시적 삭제를 전송합니다. 전체 피드: Google에서 다음에 가져오기 전에 전체 피드에서 항목을 삭제해야 합니다. 그러지 않으면 항목이 다시 추가됩니다. |
특정 Service 에 새 배송 지역 추가 |
ServiceArea |
증분 피드: 전체 피드 내에서 일반적으로 하는 것처럼 모든 필드를 그대로 유지하면서 문제의 ServiceArea 항목을 전송합니다. 이때 polygon , geoRadius 또는 postalCode 내에 새 전송 지역을 지정합니다. |
Service 에서 배송 예상 도착 시간을 업데이트합니다. |
ServiceHours |
증분 피드: ServiceHours 를 피드와 동일하게 전송합니다. 단, leadTimeMin 가 적절하게 업데이트됩니다. |
Service 의 배송비 업데이트 |
Fee |
증분 피드: price 가 업데이트된 전체 전송 Fee 를 전송합니다. |
Service 에서 배달 또는 테이크아웃 시간을 업데이트합니다. |
ServiceHours |
증분 피드: opens 및 closes 속성이 적절하게 업데이트되는 점을 제외하고 피드와 동일한 ServiceHours 를 전송합니다. |
Service (최소 주문 금액 변경) |
Fee |
증분 피드: 업데이트된 minPrice 와 함께 전체 Fee 전송 |
MenuItem 완전히 삭제 |
Menu |
증분 피드: 피드와 동일한 MenuItem 를 전송하지만 parentMenuSectionId 는 비워 둡니다.
|
일괄 작업 및 증분 업데이트 처리 시간에 관한 SLO
일괄 처리를 통해 업데이트되거나 삭제된 항목은 최선의 방식으로 2시간 이내에 처리되지만 증분 업데이트를 통해 업데이트된 항목은 5분 이내에 처리됩니다. 비활성 항목은 7일 후에 삭제됩니다.
Google에 다음 중 하나를 보낼 수 있습니다.
- 인벤토리를 최신 상태로 유지하기 위한 일일 일괄 작업 여러 개 또는
- 인벤토리를 최신 상태로 유지하기 위한 일일 일괄 작업 1개 및 증분 API