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

В этом документе рассматриваются некоторые методы, которые можно использовать для повышения производительности вашего приложения. В некоторых случаях для иллюстрации представленных идей используются примеры из других 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 влияет только на данные ответа; он не влияет на данные, которые необходимо отправить, если таковые имеются. Чтобы сократить объём данных, отправляемых при изменении ресурсов, используйте запрос на исправление .

Пример

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

Простой запрос: этот 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 автора для каждого элемента в массиве элементов.

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

После обработки сервером корректного запроса, включающего параметр запроса 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.

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

Пример

В этом примере показан простой запрос на обновление только заголовка универсального (вымышленного) ресурса 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 , это единственное значение, которое отличается от предыдущего.

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

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

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

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

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

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

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

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 использует ETag, для успешного обновления ресурса необходимо отправить предыдущее значение ETag вместе с запросом.

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

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

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 использует ETag, сервер обновляет значения ETag после успешной обработки запроса на исправление, как это происходит с 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 Wallet

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

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

Ява

Чтобы начать интеграцию в 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<FlightObject> callback =
      new JsonBatchCallback<FlightObject>() {
        // Invoked if the request was successful
        public void onSuccess(FlightObject 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/tickets/boarding-passes/rest/v1/flightobject
    FlightObject batchObject =
        new FlightObject()
            .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)))
            .setPassengerName("Passenger name")
            .setBoardingAndSeatingInfo(
                new BoardingAndSeatingInfo().setBoardingGroup("B").setSeatNumber("42"))
            .setReservationInfo(new ReservationInfo().setConfirmationCode("Confirmation code"));

    service.flightobject().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/tickets/boarding-passes/rest/v1/flightobject
    $batchObject = new FlightObject([
      '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
        ])
      ],
      'passengerName' => 'Passenger name',
      'boardingAndSeatingInfo' => new BoardingAndSeatingInfo([
        'boardingGroup' => 'B',
        'seatNumber' => '42'
      ]),
      'reservationInfo' => new ReservationInfo([
        'confirmationCode' => 'Confirmation code'
      ])
    ]);

    $batch->add($this->service->flightobject->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, обратитесь к нашим полным примерам кода на 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/tickets/boarding-passes/rest/v1/flightobject
        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
            }],
            'passengerName': 'Passenger name',
            'boardingAndSeatingInfo': {
                'boardingGroup': 'B',
                'seatNumber': '42'
            },
            'reservationInfo': {
                'confirmationCode': 'Confirmation code'
            }
        }

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

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

    print('Batch complete')

С#

Чтобы начать интеграцию в 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/tickets/boarding-passes/rest/v1/flightobject
    FlightObject batchObject = new FlightObject
    {
      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
        }
      },
      PassengerName = "Passenger name",
      BoardingAndSeatingInfo = new BoardingAndSeatingInfo
      {
        BoardingGroup = "B",
        SeatNumber = "42"
      },
      ReservationInfo = new ReservationInfo
      {
        ConfirmationCode = "Confirmation code"
      }
    };

    data += "--batch_createobjectbatch\n";
    data += "Content-Type: application/json\n\n";
    data += "POST /walletobjects/v1/flightObject/\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/tickets/boarding-passes/rest/v1/flightobject
    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
        }
      ],
      'passengerName': 'Passenger name',
      'boardingAndSeatingInfo': {
        'boardingGroup': 'B',
        'seatNumber': '42'
      },
      'reservationInfo': {
        'confirmationCode': 'Confirmation code'
      }
    };

    data += '--batch_createobjectbatch\n';
    data += 'Content-Type: application/json\n\n';
    data += 'POST /walletobjects/v1/flightObject\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. Примеры кода на Github .

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

		flightObject := new(walletobjects.FlightObject)
		flightObject.Id = fmt.Sprintf("%s.%s", issuerId, objectSuffix)
		flightObject.ClassId = fmt.Sprintf("%s.%s", issuerId, classSuffix)
		flightObject.State = "ACTIVE"
		flightObject.PassengerName = "Passenger name"
		flightObject.ReservationInfo = &walletobjects.ReservationInfo{
			ConfirmationCode: "Confirmation code",
		}

		flightJson, _ := json.Marshal(flightObject)
		batchObject := fmt.Sprintf("%s", flightJson)

		data += "--batch_createobjectbatch\n"
		data += "Content-Type: application/json\n\n"
		data += "POST /walletobjects/v1/flightObject\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)
	}
}