Советы по производительности

В этом документе описаны некоторые методы, которые можно использовать для повышения производительности вашего приложения. В некоторых случаях для иллюстрации представленных идей используются примеры из других API или универсальных API. Однако те же самые концепции применимы и к API Google Wallet.

Сжатие с использованием gzip

Простой и удобный способ уменьшить пропускную способность, необходимую для каждого запроса, — это включить сжатие gzip. Хотя это требует дополнительного процессорного времени для распаковки результатов, компромисс с сетевыми издержками обычно очень оправдан.

Для получения ответа в формате gzip необходимо выполнить два действия: установить заголовок Accept-Encoding и изменить пользовательский агент таким образом, чтобы он содержал строку gzip . Вот пример правильно сформированных HTTP-заголовков для включения сжатия gzip:

Accept-Encoding: gzip
User-Agent: my program (gzip)

Работа с ограниченными ресурсами

Ещё один способ повысить производительность вызовов API — отправлять и получать только ту часть данных, которая вас интересует. Это позволяет вашему приложению избегать передачи, анализа и хранения ненужных полей, что позволяет более эффективно использовать ресурсы, включая сеть, процессор и память.

Существует два типа частичных запросов:

  • Частичный ответ : Запрос, в котором вы указываете, какие поля следует включить в ответ (используйте параметр запроса fields ).
  • Patch : Запрос на обновление, в котором вы отправляете только те поля, которые хотите изменить (используйте HTTP-метод PATCH ).

Более подробная информация о подаче частичных запросов представлена ​​в следующих разделах.

Частичный ответ

По умолчанию сервер после обработки запросов отправляет полное представление ресурса. Для повышения производительности вы можете попросить сервер отправлять только необходимые поля и получать вместо этого частичный ответ .

Чтобы запросить частичный ответ, используйте параметр запроса fields , чтобы указать поля, которые вы хотите получить. Этот параметр можно использовать с любым запросом, который возвращает данные ответа.

Обратите внимание, что параметр fields влияет только на данные ответа; он не влияет на данные, которые вам необходимо отправить, если таковые имеются. Чтобы уменьшить объем данных, отправляемых при изменении ресурсов, используйте запрос на исправление (patch request).

Пример

В следующем примере показано использование параметра fields с универсальным (вымышленным) API "Demo".

Простой запрос: В этом HTTP GET запросе параметр fields отсутствует, и он возвращает полный ресурс.

https://www.googleapis.com/demo/v1

Полный ответ на запрос ресурса: Полные данные ресурса включают следующие поля, а также многие другие, которые были опущены для краткости.

{
  "kind": "demo",
  ...
  "items": [
  {
    "title": "First title",
    "comment": "First comment.",
    "characteristics": {
      "length": "short",
      "accuracy": "high",
      "followers": ["Jo", "Will"],
    },
    "status": "active",
    ...
  },
  {
    "title": "Second title",
    "comment": "Second comment.",
    "characteristics": {
      "length": "long",
      "accuracy": "medium"
      "followers": [ ],
    },
    "status": "pending",
    ...
  },
  ...
  ]
}

Запрос на частичный ответ: В следующем запросе к этому же ресурсу используется параметр fields , что значительно уменьшает объем возвращаемых данных.

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

Частичный ответ: В ответ на указанный выше запрос сервер отправляет ответ, содержащий только информацию о типе запроса, а также сокращенный массив элементов, включающий только HTML-заголовок и информацию о длине каждого элемента.

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

Обратите внимание, что ответ представляет собой JSON-объект, содержащий только выбранные поля и окружающие их родительские объекты.

Далее рассматриваются подробности форматирования параметра fields , а затем более подробно описывается, что именно возвращается в ответе.

Краткое описание синтаксиса параметров полей

Формат значения параметра запроса fields в общих чертах основан на синтаксисе XPath. Поддерживаемый синтаксис кратко описан ниже, а дополнительные примеры приведены в следующем разделе.

  • Для выбора нескольких полей используйте список, разделенный запятыми.
  • Используйте a/b для выбора поля b , вложенного в поле a ; используйте a/b/c для выбора поля c вложенного в b .

    Исключение: Для ответов API, использующих обертки "data", где ответ вложен в объект data , имеющий вид data: { ... } , не включайте " data " в спецификацию fields . Включение объекта данных со спецификацией полей, например, data/a/b приводит к ошибке. Вместо этого используйте спецификацию fields , например, a/b .

  • Используйте подселектор для запроса набора определенных подполей массивов или объектов, заключая выражения в скобки " ( ) ".

    Например: fields=items(id,author/email) возвращает только идентификатор элемента и адрес электронной почты автора для каждого элемента в массиве items. Вы также можете указать одно подполе, где fields=items(id) эквивалентно fields=items/id .

  • При необходимости используйте символы подстановки при выборе полей.

    Например: fields=items/pagemap/* выбирает все объекты в карте страниц.

Дополнительные примеры использования параметра fields

Приведенные ниже примеры описывают, как значение параметра fields влияет на ответ.

Примечание: Как и все значения параметров запроса, значение параметра fields должно быть закодировано в формате URL. Для лучшей читаемости в примерах этого документа кодирование не используется.

Укажите поля, которые вы хотите получить в результате, или выберите нужные поля .
Значение параметра запроса fields представляет собой список полей, разделенных запятыми, и каждое поле указывается относительно корня ответа. Таким образом, если вы выполняете операцию со списком , ответ представляет собой коллекцию и обычно включает массив ресурсов. Если вы выполняете операцию, которая возвращает один ресурс, поля указываются относительно этого ресурса. Если выбранное вами поле является (или является частью) массива, сервер возвращает выбранную часть всех элементов массива.

Вот несколько примеров на уровне коллекций:
Примеры Эффект
items Возвращает все элементы массива items, включая все поля каждого элемента, но не другие поля.
etag,items Возвращает как поле etag , так и все элементы массива items.
items/title Возвращает только поле title для всех элементов в массиве items.

При возврате вложенного поля ответ включает в себя окружающие его родительские объекты. Родительские поля не включают в себя другие дочерние поля, если они также не выбраны явно.
context/facets/label Возвращает только поле label для всех элементов массива facets , который, в свою очередь, вложен в объект context .
items/pagemap/*/title Для каждого элемента в массиве items возвращает только поле title (если оно присутствует) всех объектов, являющихся дочерними элементами pagemap .

Вот несколько примеров на уровне ресурсов:
Примеры Эффект
title Возвращает поле title запрошенного ресурса.
author/uri Возвращает подполе uri объекта author в запрошенном ресурсе.
links/*/href
Возвращает поле href всех объектов, являющихся дочерними элементами links .
Запрашивайте только части определенных полей, используя подзапросы .
По умолчанию, если ваш запрос указывает на определенные поля, сервер возвращает объекты или элементы массива целиком. Вы можете указать ответ, включающий только определенные подполя. Для этого используется синтаксис подвыбора " ( ) ", как в примере ниже.
Пример Эффект
items(title,author/uri) Возвращает только значения title и uri автора для каждого элемента в массиве items.

Обработка частичных ответов

После обработки сервером корректного запроса, включающего параметр запроса fields , он отправляет обратно код состояния HTTP 200 OK вместе с запрошенными данными. Если параметр запроса fields содержит ошибку или является недействительным по какой-либо другой причине, сервер возвращает код состояния HTTP 400 Bad Request вместе с сообщением об ошибке, указывающим пользователю на ошибку в выборе полей (например, "Invalid field selection a/b" ).

Вот пример частичного ответа, показанный во вводной части выше. В запросе используется параметр fields для указания того, какие поля следует вернуть.

https://www.googleapis.com/demo/v1?fields=kind,items(title,characteristics/length)

Частичный ответ выглядит следующим образом:

200 OK
{
  "kind": "demo",
  "items": [{
    "title": "First title",
    "characteristics": {
      "length": "short"
    }
  }, {
    "title": "Second title",
    "characteristics": {
      "length": "long"
    }
  },
  ...
  ]
}

Примечание: Для API, поддерживающих параметры запроса для постраничной обработки данных (например, maxResults и nextPageToken ), используйте эти параметры, чтобы уменьшить размер результатов каждого запроса до приемлемого уровня. В противном случае, преимущества в производительности, достигаемые за счет частичного ответа, могут быть не реализованы.

Патч (частичное обновление)

Также можно избежать отправки ненужных данных при изменении ресурсов. Чтобы отправлять обновленные данные только для конкретных полей, которые вы изменяете, используйте HTTP-метод PATCH . Семантика обновления, описанная в этом документе, отличается (и проще), чем та, что использовалась в более старой реализации частичного обновления в GData.

Приведённый ниже короткий пример показывает, как использование команды patch минимизирует объём данных, необходимых для внесения небольшого обновления.

Пример

В этом примере показан простой запрос на изменение только заголовка общего (вымышленного) ресурса API "Demo". Ресурс также содержит комментарий, набор характеристик, статус и множество других полей, но этот запрос отправляет только поле title , поскольку изменяется только это поле:

PATCH https://www.googleapis.com/demo/v1/324
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "title": "New title"
}

Ответ:

200 OK
{
  "title": "New title",
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "accuracy": "high",
    "followers": ["Jo", "Will"],
  },
  "status": "active",
  ...
}

Сервер возвращает код состояния 200 OK , а также полное представление обновлённого ресурса. Поскольку в запрос на обновление было включено только поле title , это единственное значение, отличающееся от предыдущего.

Примечание: Использование параметра fields частичного ответа в сочетании с параметром patch позволяет еще больше повысить эффективность запросов на обновление. Запрос patch уменьшает только размер запроса. Частичный ответ уменьшает размер ответа. Поэтому, чтобы уменьшить объем данных, передаваемых в обоих направлениях, используйте запрос patch с параметром fields .

Семантика запроса на исправление

Тело запроса на изменение содержит только те поля ресурса, которые вы хотите изменить. При указании поля необходимо включить все родительские объекты, содержащие эти объекты, так же как и в частичном ответе возвращаются родительские объекты. Измененные данные, которые вы отправляете, объединяются с данными родительского объекта, если таковой имеется.

  • Добавить: Чтобы добавить поле, которого еще нет, укажите новое поле и его значение.
  • Изменить: Чтобы изменить значение существующего поля, укажите поле и задайте ему новое значение.
  • Удаление: Чтобы удалить поле, укажите его и установите значение null . Например, "comment": null . Вы также можете удалить весь объект (если он изменяемый), установив его значение null . Если вы используете клиентскую библиотеку Java API , используйте вместо этого Data.NULL_STRING ; подробности см. в разделе JSON null .

Примечание о массивах: Запросы на изменение, содержащие массивы, заменяют существующий массив предоставленным вами. Вы не можете изменять, добавлять или удалять элементы в массиве по частям.

Использование патча в цикле чтение-изменение-запись

Полезно начать с получения частичного ответа с данными, которые вы хотите изменить. Это особенно важно для ресурсов, использующих ETags, поскольку для успешного обновления ресурса необходимо указать текущее значение ETag в заголовке HTTP If-Match . После получения данных вы можете изменить нужные значения и отправить измененное частичное представление обратно с запросом на исправление. Вот пример, предполагающий, что ресурс Demo использует ETags:

GET https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token

Это частичный ответ:

200 OK
{
  "etag": "ETagString"
  "title": "New title"
  "comment": "First comment.",
  "characteristics": {
    "length": "short",
    "level": "5",
    "followers": ["Jo", "Will"],
  }
}

Следующий запрос на исправление основан на этом ответе. Как показано ниже, он также использует параметр fields для ограничения данных, возвращаемых в ответе на запрос на исправление:

PATCH https://www.googleapis.com/demo/v1/324?fields=etag,title,comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json
If-Match: "ETagString"
{
  "etag": "ETagString"
  "title": "",                  /* Clear the value of the title by setting it to the empty string. */
  "comment": null,              /* Delete the comment by replacing its value with null. */
  "characteristics": {
    "length": "short",
    "level": "10",              /* Modify the level value. */
    "followers": ["Jo", "Liz"], /* Replace the followers array to delete Will and add Liz. */
    "accuracy": "high"          /* Add a new characteristic. */
  },
}

Сервер отвечает HTTP-статусом 200 OK и частичным представлением обновленного ресурса:

200 OK
{
  "etag": "newETagString"
  "title": "",                 /* Title is cleared; deleted comment field is missing. */
  "characteristics": {
    "length": "short",
    "level": "10",             /* Value is updated.*/
    "followers": ["Jo" "Liz"], /* New follower Liz is present; deleted Will is missing. */
    "accuracy": "high"         /* New characteristic is present. */
  }
}

Создание запроса на исправление напрямую

Для некоторых запросов на внесение изменений необходимо основываться на ранее полученных данных. Например, если вы хотите добавить элемент в массив и не хотите потерять ни один из существующих элементов массива, сначала необходимо получить существующие данные. Аналогично, если API использует ETags, для успешного обновления ресурса необходимо отправить предыдущее значение ETag вместе с запросом.

Примечание: Вы можете использовать HTTP-заголовок "If-Match: *" , чтобы принудительно отправить патч, когда используются ETags. В этом случае вам не нужно будет выполнять чтение перед записью.

Однако в других ситуациях вы можете создать запрос на исправление напрямую, без предварительного получения существующих данных. Например, вы можете легко настроить запрос на исправление, который обновляет поле новым значением или добавляет новое поле. Вот пример:

PATCH https://www.googleapis.com/demo/v1/324?fields=comment,characteristics
Authorization: Bearer your_auth_token
Content-Type: application/json

{
  "comment": "A new comment",
  "characteristics": {
    "volume": "loud",
    "accuracy": null
  }
}

При таком запросе, если поле комментария уже имеет значение, новое значение перезаписывает его; в противном случае ему присваивается новое значение. Аналогично, если существовала характеристика объема, ее значение перезаписывается; если нет, она создается. Поле точности, если оно задано, удаляется.

Обработка ответа на обновление

После обработки действительного запроса на внесение изменений API возвращает HTTP-ответ с кодом 200 OK а также полное описание измененного ресурса. Если API использует ETags, сервер обновляет значения ETags после успешной обработки запроса на внесение изменений, как и в случае с PUT .

Запрос на исправление возвращает полное представление ресурса, если только вы не используете параметр fields для уменьшения объема возвращаемых данных.

Если запрос на изменение приводит к созданию нового состояния ресурса, которое является синтаксически или семантически некорректным, сервер возвращает HTTP-код состояния 400 Bad Request или 422 Unprocessable Entity , и состояние ресурса остается неизменным. Например, если вы попытаетесь удалить значение для обязательного поля, сервер вернет ошибку.

Альтернативная запись, если HTTP-метод PATCH не поддерживается.

Если ваш брандмауэр не разрешает HTTP-запросы PATCH , выполните HTTP-запрос POST и установите заголовок переопределения в PATCH , как показано ниже:

POST https://www.googleapis.com/...
X-HTTP-Method-Override: PATCH
...

Разница между патчем и обновлением.

На практике, при отправке данных в запросе на обновление с использованием HTTP PUT , вам нужно отправлять только те поля, которые являются обязательными или необязательными; если вы отправляете значения для полей, которые задаются сервером, они игнорируются. Хотя это может показаться еще одним способом частичного обновления, у этого подхода есть некоторые ограничения. При обновлениях с использованием HTTP PUT запрос завершается с ошибкой, если вы не указываете обязательные параметры, и очищает ранее установленные данные, если вы не указываете необязательные параметры.

По этой причине использовать функцию patch гораздо безопаснее. Вы указываете данные только для тех полей, которые хотите изменить; поля, которые вы пропускаете, не очищаются. Единственное исключение из этого правила касается повторяющихся элементов или массивов: если вы пропускаете все из них, они остаются как есть; если вы указываете хотя бы один из них, весь набор заменяется тем набором, который вы указали.

Пакетные запросы к Google Кошелек

API Google Wallet поддерживает пакетную обработку вызовов API для уменьшения количества соединений, которые должен установить клиент. Дополнительную информацию о структуре пакетных запросов и ответов см. в разделе «Подробности пакетной обработки» .

Приведенный ниже пример кода демонстрирует пакетную обработку запросов. В примерах на Java и PHP используются библиотеки Google Wallet для упрощения создания классов и объектов.

Java

Чтобы начать интеграцию на Java, ознакомьтесь с полными примерами кода на GitHub .

/**
 * Batch create Google Wallet objects from an existing class.
 *
 * @param issuerId The issuer ID being used for this request.
 * @param classSuffix Developer-defined unique ID for this pass class.
 */
public void batchCreateObjects(String issuerId, String classSuffix) throws IOException {
  // Create the batch request client
  BatchRequest batch = service.batch(new HttpCredentialsAdapter(credentials));

  // The callback will be invoked for each request in the batch
  JsonBatchCallback<GiftCardObject> callback =
      new JsonBatchCallback<GiftCardObject>() {
        // Invoked if the request was successful
        public void onSuccess(GiftCardObject response, HttpHeaders responseHeaders) {
          System.out.println("Batch insert response");
          System.out.println(response.toString());
        }

        // Invoked if the request failed
        public void onFailure(GoogleJsonError e, HttpHeaders responseHeaders) {
          System.out.println("Error Message: " + e.getMessage());
        }
      };

  // Example: Generate three new pass objects
  for (int i = 0; i < 3; i++) {
    // Generate a random object suffix
    String objectSuffix = UUID.randomUUID().toString().replaceAll("[^\\w.-]", "_");

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
    GiftCardObject batchObject =
        new GiftCardObject()
            .setId(String.format("%s.%s", issuerId, objectSuffix))
            .setClassId(String.format("%s.%s", issuerId, classSuffix))
            .setState("ACTIVE")
            .setHeroImage(
                new Image()
                    .setSourceUri(
                        new ImageUri()
                            .setUri(
                                "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"))
                    .setContentDescription(
                        new LocalizedString()
                            .setDefaultValue(
                                new TranslatedString()
                                    .setLanguage("en-US")
                                    .setValue("Hero image description"))))
            .setTextModulesData(
                    List.of(
                            new TextModuleData()
                                    .setHeader("Text module header")
                                    .setBody("Text module body")
                                    .setId("TEXT_MODULE_ID")))
            .setLinksModuleData(
                new LinksModuleData()
                    .setUris(
                        Arrays.asList(
                            new Uri()
                                .setUri("http://maps.google.com/")
                                .setDescription("Link module URI description")
                                .setId("LINK_MODULE_URI_ID"),
                            new Uri()
                                .setUri("tel:6505555555")
                                .setDescription("Link module tel description")
                                .setId("LINK_MODULE_TEL_ID"))))
            .setImageModulesData(
                    List.of(
                            new ImageModuleData()
                                    .setMainImage(
                                            new Image()
                                                    .setSourceUri(
                                                            new ImageUri()
                                                                    .setUri(
                                                                            "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"))
                                                    .setContentDescription(
                                                            new LocalizedString()
                                                                    .setDefaultValue(
                                                                            new TranslatedString()
                                                                                    .setLanguage("en-US")
                                                                                    .setValue("Image module description"))))
                                    .setId("IMAGE_MODULE_ID")))
            .setBarcode(new Barcode().setType("QR_CODE").setValue("QR code value"))
            .setLocations(
                    List.of(
                            new LatLongPoint()
                                    .setLatitude(37.424015499999996)
                                    .setLongitude(-122.09259560000001)))
            .setCardNumber("Card number")
            .setPin("1234")
            .setBalance(new Money().setMicros(20000000L).setCurrencyCode("USD"))
            .setBalanceUpdateTime(new DateTime().setDate("2020-04-12T16:20:50.52-04:00"));

    service.giftcardobject().insert(batchObject).queue(batch, callback);
  }

  // Invoke the batch API calls
  batch.execute();
}

PHP

Чтобы начать интеграцию на PHP, ознакомьтесь с полными примерами кода на GitHub .

/**
 * Batch create Google Wallet objects from an existing class.
 *
 * @param string $issuerId The issuer ID being used for this request.
 * @param string $classSuffix Developer-defined unique ID for the pass class.
 */
public function batchCreateObjects(string $issuerId, string $classSuffix)
{
  // Update the client to enable batch requests
  $this->client->setUseBatch(true);
  $batch = $this->service->createBatch();

  // Example: Generate three new pass objects
  for ($i = 0; $i < 3; $i++) {
    // Generate a random object suffix
    $objectSuffix = preg_replace('/[^\w.-]/i', '_', uniqid());

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
    $batchObject = new GiftCardObject([
      'id' => "{$issuerId}.{$objectSuffix}",
      'classId' => "{$issuerId}.{$classSuffix}",
      'state' => 'ACTIVE',
      'heroImage' => new Image([
        'sourceUri' => new ImageUri([
          'uri' => 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg'
        ]),
        'contentDescription' => new LocalizedString([
          'defaultValue' => new TranslatedString([
            'language' => 'en-US',
            'value' => 'Hero image description'
          ])
        ])
      ]),
      'textModulesData' => [
        new TextModuleData([
          'header' => 'Text module header',
          'body' => 'Text module body',
          'id' => 'TEXT_MODULE_ID'
        ])
      ],
      'linksModuleData' => new LinksModuleData([
        'uris' => [
          new Uri([
            'uri' => 'http://maps.google.com/',
            'description' => 'Link module URI description',
            'id' => 'LINK_MODULE_URI_ID'
          ]),
          new Uri([
            'uri' => 'tel:6505555555',
            'description' => 'Link module tel description',
            'id' => 'LINK_MODULE_TEL_ID'
          ])
        ]
      ]),
      'imageModulesData' => [
        new ImageModuleData([
          'mainImage' => new Image([
            'sourceUri' => new ImageUri([
              'uri' => 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg'
            ]),
            'contentDescription' => new LocalizedString([
              'defaultValue' => new TranslatedString([
                'language' => 'en-US',
                'value' => 'Image module description'
              ])
            ])
          ]),
          'id' => 'IMAGE_MODULE_ID'
        ])
      ],
      'barcode' => new Barcode([
        'type' => 'QR_CODE',
        'value' => 'QR code value'
      ]),
      'locations' => [
        new LatLongPoint([
          'latitude' => 37.424015499999996,
          'longitude' =>  -122.09259560000001
        ])
      ],
      'cardNumber' => 'Card number',
      'pin' => '1234',
      'balance' => new Money([
        'micros' => 20000000,
        'currencyCode' => 'USD'
      ]),
      'balanceUpdateTime' => new DateTime([
        'date' => '2020-04-12T16:20:50.52-04:00'
      ])
    ]);

    $batch->add($this->service->giftcardobject->insert($batchObject));
  }

  // Make the batch request
  $batchResponse = $batch->execute();

  print "Batch insert response\n";
  foreach ($batchResponse as $key => $value) {
    if ($value instanceof Google_Service_Exception) {
      print_r($value->getErrors());
      continue;
    }
    print "{$value->getId()}\n";
  }
}

Python

Чтобы начать интеграцию на Python, ознакомьтесь с полными примерами кода на GitHub .

def batch_create_objects(self, issuer_id: str, class_suffix: str):
    """Batch create Google Wallet objects from an existing class.

    The request body will be a multiline string. See below for information.

    https://cloud.google.com/compute/docs/api/how-tos/batch#example

    Args:
        issuer_id (str): The issuer ID being used for this request.
        class_suffix (str): Developer-defined unique ID for this pass class.
    """
    batch = self.client.new_batch_http_request()

    # Example: Generate three new pass objects
    for _ in range(3):
        # Generate a random object suffix
        object_suffix = str(uuid.uuid4()).replace('[^\\w.-]', '_')

        # See link below for more information on required properties
        # https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
        batch_object = {
            'id': f'{issuer_id}.{object_suffix}',
            'classId': f'{issuer_id}.{class_suffix}',
            'state': 'ACTIVE',
            'heroImage': {
                'sourceUri': {
                    'uri':
                        'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg'
                },
                'contentDescription': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': 'Hero image description'
                    }
                }
            },
            'textModulesData': [{
                'header': 'Text module header',
                'body': 'Text module body',
                'id': 'TEXT_MODULE_ID'
            }],
            'linksModuleData': {
                'uris': [{
                    'uri': 'http://maps.google.com/',
                    'description': 'Link module URI description',
                    'id': 'LINK_MODULE_URI_ID'
                }, {
                    'uri': 'tel:6505555555',
                    'description': 'Link module tel description',
                    'id': 'LINK_MODULE_TEL_ID'
                }]
            },
            'imageModulesData': [{
                'mainImage': {
                    'sourceUri': {
                        'uri':
                            'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg'
                    },
                    'contentDescription': {
                        'defaultValue': {
                            'language': 'en-US',
                            'value': 'Image module description'
                        }
                    }
                },
                'id': 'IMAGE_MODULE_ID'
            }],
            'barcode': {
                'type': 'QR_CODE',
                'value': 'QR code'
            },
            'locations': [{
                'latitude': 37.424015499999996,
                'longitude': -122.09259560000001
            }],
            'cardNumber': 'Card number',
            'pin': '1234',
            'balance': {
                'micros': 20000000,
                'currencyCode': 'USD'
            },
            'balanceUpdateTime': {
                'date': '2020-04-12T16:20:50.52-04:00'
            }
        }

        batch.add(self.client.giftcardobject().insert(body=batch_object))

    # Invoke the batch API calls
    response = batch.execute()

    print('Batch complete')

C#

Чтобы начать интеграцию на C#, ознакомьтесь с полными примерами кода на GitHub .

/// <summary>
/// Batch create Google Wallet objects from an existing class.
/// </summary>
/// <param name="issuerId">The issuer ID being used for this request.</param>
/// <param name="classSuffix">Developer-defined unique ID for this pass class.</param>
public async void BatchCreateObjects(string issuerId, string classSuffix)
{
  // The request body will be a multiline string
  // See below for more information
  // https://cloud.google.com/compute/docs/api/how-tos/batch//example
  string data = "";

  HttpClient httpClient = new HttpClient();
  httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
    "Bearer",
    credentials.GetAccessTokenForRequestAsync().Result
  );

  // Example: Generate three new pass objects
  for (int i = 0; i < 3; i++)
  {
    // Generate a random object suffix
    string objectSuffix = Regex.Replace(Guid.NewGuid().ToString(), "[^\\w.-]", "_");

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
    GiftCardObject batchObject = new GiftCardObject
    {
      Id = $"{issuerId}.{objectSuffix}",
      ClassId = $"{issuerId}.{classSuffix}",
      State = "ACTIVE",
      HeroImage = new Image
      {
        SourceUri = new ImageUri
        {
          Uri = "https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg"
        },
        ContentDescription = new LocalizedString
        {
          DefaultValue = new TranslatedString
          {
            Language = "en-US",
            Value = "Hero image description"
          }
        }
      },
      TextModulesData = new List<TextModuleData>
      {
        new TextModuleData
        {
          Header = "Text module header",
          Body = "Text module body",
          Id = "TEXT_MODULE_ID"
        }
      },
      LinksModuleData = new LinksModuleData
      {
        Uris = new List<Google.Apis.Walletobjects.v1.Data.Uri>
        {
          new Google.Apis.Walletobjects.v1.Data.Uri
          {
            UriValue = "http://maps.google.com/",
            Description = "Link module URI description",
            Id = "LINK_MODULE_URI_ID"
          },
          new Google.Apis.Walletobjects.v1.Data.Uri
          {
            UriValue = "tel:6505555555",
            Description = "Link module tel description",
            Id = "LINK_MODULE_TEL_ID"
          }
        }
      },
      ImageModulesData = new List<ImageModuleData>
      {
        new ImageModuleData
        {
          MainImage = new Image
          {
            SourceUri = new ImageUri
            {
              Uri = "http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg"
            },
            ContentDescription = new LocalizedString
            {
              DefaultValue = new TranslatedString
              {
                Language = "en-US",
                Value = "Image module description"
              }
            }
          },
          Id = "IMAGE_MODULE_ID"
        }
      },
      Barcode = new Barcode
      {
        Type = "QR_CODE",
        Value = "QR code"
      },
      Locations = new List<LatLongPoint>
      {
        new LatLongPoint
        {
          Latitude = 37.424015499999996,
          Longitude = -122.09259560000001
        }
      },
      CardNumber = "Card number",
      Pin = "1234",
      Balance = new Money
      {
        Micros = 20000000,
        CurrencyCode = "USD"
      },
      BalanceUpdateTime = new Google.Apis.Walletobjects.v1.Data.DateTime
      {
        Date = "2020-04-12T16:20:50.52-04:00"
      }
    };

    data += "--batch_createobjectbatch\n";
    data += "Content-Type: application/json\n\n";
    data += "POST /walletobjects/v1/giftCardObject/\n\n";

    data += JsonConvert.SerializeObject(batchObject) + "\n\n";
  }
  data += "--batch_createobjectbatch--";

  // Invoke the batch API calls
  HttpRequestMessage batchObjectRequest = new HttpRequestMessage(
      HttpMethod.Post,
      "https://walletobjects.googleapis.com/batch");

  batchObjectRequest.Content = new StringContent(data);
  batchObjectRequest.Content.Headers.ContentType = new MediaTypeHeaderValue(
      "multipart/mixed");
  // `boundary` is the delimiter between API calls in the batch request
  batchObjectRequest.Content.Headers.ContentType.Parameters.Add(
      new NameValueHeaderValue("boundary", "batch_createobjectbatch"));

  HttpResponseMessage batchObjectResponse = httpClient.Send(
      batchObjectRequest);

  string batchObjectContent = await batchObjectResponse
      .Content
      .ReadAsStringAsync();

  Console.WriteLine("Batch insert response");
  Console.WriteLine(batchObjectContent);
}

Node.js

Чтобы начать интеграцию в Node, ознакомьтесь с полными примерами кода на GitHub .

/**
 * Batch create Google Wallet objects from an existing class.
 *
 * @param {string} issuerId The issuer ID being used for this request.
 * @param {string} classSuffix Developer-defined unique ID for this pass class.
 */
async batchCreateObjects(issuerId, classSuffix) {
  // See below for more information
  // https://cloud.google.com/compute/docs/api/how-tos/batch#example
  let data = '';
  let batchObject;
  let objectSuffix;

  // Example: Generate three new pass objects
  for (let i = 0; i < 3; i++) {
    // Generate a random object suffix
    objectSuffix = uuidv4().replace('[^\w.-]', '_');

    // See link below for more information on required properties
    // https://developers.google.com/wallet/retail/gift-cards/rest/v1/giftcardobject
    batchObject = {
      'id': `${issuerId}.${objectSuffix}`,
      'classId': `${issuerId}.${classSuffix}`,
      'state': 'ACTIVE',
      'heroImage': {
        'sourceUri': {
          'uri': 'https://farm4.staticflickr.com/3723/11177041115_6e6a3b6f49_o.jpg'
        },
        'contentDescription': {
          'defaultValue': {
            'language': 'en-US',
            'value': 'Hero image description'
          }
        }
      },
      'textModulesData': [
        {
          'header': 'Text module header',
          'body': 'Text module body',
          'id': 'TEXT_MODULE_ID'
        }
      ],
      'linksModuleData': {
        'uris': [
          {
            'uri': 'http://maps.google.com/',
            'description': 'Link module URI description',
            'id': 'LINK_MODULE_URI_ID'
          },
          {
            'uri': 'tel:6505555555',
            'description': 'Link module tel description',
            'id': 'LINK_MODULE_TEL_ID'
          }
        ]
      },
      'imageModulesData': [
        {
          'mainImage': {
            'sourceUri': {
              'uri': 'http://farm4.staticflickr.com/3738/12440799783_3dc3c20606_b.jpg'
            },
            'contentDescription': {
              'defaultValue': {
                'language': 'en-US',
                'value': 'Image module description'
              }
            }
          },
          'id': 'IMAGE_MODULE_ID'
        }
      ],
      'barcode': {
        'type': 'QR_CODE',
        'value': 'QR code'
      },
      'locations': [
        {
          'latitude': 37.424015499999996,
          'longitude': -122.09259560000001
        }
      ],
      'cardNumber': 'Card number',
      'pin': '1234',
      'balance': {
        'micros': 20000000,
        'currencyCode': 'USD'
      },
      'balanceUpdateTime': {
        'date': '2020-04-12T16:20:50.52-04:00'
      }
    };

    data += '--batch_createobjectbatch\n';
    data += 'Content-Type: application/json\n\n';
    data += 'POST /walletobjects/v1/giftCardObject\n\n';

    data += JSON.stringify(batchObject) + '\n\n';
  }
  data += '--batch_createobjectbatch--';

  // Invoke the batch API calls
  let response = await this.client.context._options.auth.request({
    url: 'https://walletobjects.googleapis.com/batch',
    method: 'POST',
    data: data,
    headers: {
      // `boundary` is the delimiter between API calls in the batch request
      'Content-Type': 'multipart/mixed; boundary=batch_createobjectbatch'
    }
  });

  console.log('Batch insert response');
  console.log(response);
}

Идти

Чтобы начать интеграцию на Go, обратитесь к нашим полным примерам кода на GitHub .

// Batch create Google Wallet objects from an existing class.
func (d *demoGiftcard) batchCreateObjects(issuerId, classSuffix string) {
	data := ""
	for i := 0; i < 3; i++ {
		objectSuffix := strings.ReplaceAll(uuid.New().String(), "-", "_")

		giftcardObject := new(walletobjects.GiftCardObject)
		giftcardObject.Id = fmt.Sprintf("%s.%s", issuerId, objectSuffix)
		giftcardObject.ClassId = fmt.Sprintf("%s.%s", issuerId, classSuffix)
		giftcardObject.State = "ACTIVE"
		giftcardObject.CardNumber = "Card number"

		giftcardJson, _ := json.Marshal(giftcardObject)
		batchObject := fmt.Sprintf("%s", giftcardJson)

		data += "--batch_createobjectbatch\n"
		data += "Content-Type: application/json\n\n"
		data += "POST /walletobjects/v1/giftCardObject\n\n"
		data += batchObject + "\n\n"
	}
	data += "--batch_createobjectbatch--"

	res, err := d.credentials.Client(oauth2.NoContext).Post("https://walletobjects.googleapis.com/batch", "multipart/mixed; boundary=batch_createobjectbatch", bytes.NewBuffer([]byte(data)))

	if err != nil {
		fmt.Println(err)
	} else {
		b, _ := io.ReadAll(res.Body)
		fmt.Printf("Batch insert response:\n%s\n", b)
	}
}