Советы по повышению эффективности

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

Сжатие с помощью GZIP

Сжатие с помощью GZIP – это простой и удобный способ уменьшить объем запросов. На распаковку ответов требуется дополнительное процессорное время, однако экономия сетевых ресурсов того стоит.

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

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

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

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

Частичные запросы бывают двух типов:

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

Далее подробно описывается, как выполнять частичные запросы обоих типов.

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

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

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

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

Пример

В следующем примере показано, как используется параметр 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)

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

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

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

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

Краткая информация о синтаксисе параметра fields

Формат значения параметра запроса fields основан на синтаксисе XPath. Его основные принципы перечислены ниже. В следующем разделе также представлены дополнительные примеры.

  • Чтобы выбрать несколько полей, перечислите их через запятую.
  • Формат a/b позволяет выбрать поле b, вложенное в поле a, а формат a/b/c – поле c, вложенное в поле b.

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

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

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

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

    Пример: fields=items/pagemap/* выбирает все объекты, содержащиеся в данных формата PageMap.

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

Из следующих примеров видно, как значение параметра fields влияет на ответ.

Примечание. Как и все остальные значения параметров запроса, значение fields должно быть закодировано. В целях наглядности в приведенных здесь примерах кодировка не используется.

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

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

При возврате вложенного поля в ответ включаются также его родительские объекты. Если для родительского объекта явным образом не выбраны другие дочерние поля, они не возвращаются.
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), можно использовать такие параметры для сокращения числа результатов по каждому запросу. В остальных случаях применение частичных ответов может не дать никаких преимуществ.

Метод patch (частичное обновление)

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

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

Пример

Допустим, нам нужно обновить только заголовок гипотетического ресурса 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 и полное представление обновленного ресурса. Поскольку в запрос PATCH было включено только поле title, изменилось только его значение.

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

Семантика PATCH-запроса

Тело запроса PATCH содержит только поля ресурса, которые вы хотите изменить. Вместе с полем необходимо указать любые включающие его родительские объекты (аналогично тому, как при частичном ответе возвращаются все включающие родительские объекты). Новые данные, которые вы отправляете, войдут в данные для родительского объекта, если таковой имеется.

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

Примечание относительно массивов. Массив из PATCH-запроса полностью замещает существующий массив. Изменять, добавлять и удалять элементы массива по отдельности нельзя.

Использование PATCH-запроса в цикле "Чтение-изменение-запись"

Прежде всего имеет смысл получить частичный ответ с данными, которые вы хотите изменить. Это особенно важно для ресурсов, использующих ETag, поскольку для успешного обновления ресурса необходимо указать текущее значение ETag в HTTP-заголовке If-Match. Получив данные, вы можете изменить нужные значения и отправить измененное частичное представление обратно с помощью запроса patch. Вот пример, в котором предполагается, что ресурс 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"],
  }
}

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

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. */
  },
}

Сервер возвращает код статуса 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" g>"Liz"], /* New follower Liz is present; deleted Will is missing. */
    "accuracy": "high"         /* New characteristic is present. */
  }
}

PATCH-запрос без предварительного ответа

Для выполнения определенных PATCH-запросов вам понадобятся данные, полученные ранее. Например, если вы хотите добавить в массив новый элемент, не потеряв при этом ни один из старых, то сначала нужно будет получить существующие данные. Точно так же, чтобы обновить ресурс с идентификаторами ETag, в запросе необходимо будет указать предыдущее значение ETag.

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

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

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

Если в поле комментария уже есть значение, оно будет перезаписано, а если нет – будет задано новое значение. Точно так же и с полем volume: если в нем имеется значение, оно будет заменено на новое, а если нет – создано. Значение поля accuracy будет удалено.

Как обрабатывать ответ на исправление

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

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

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

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

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

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

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

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

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

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

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

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

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<EventTicketObject> callback =
      new JsonBatchCallback<EventTicketObject>() {
        // Invoked if the request was successful
        public void onSuccess(EventTicketObject 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/events/rest/v1/eventticketobject
    EventTicketObject batchObject =
        new EventTicketObject()
            .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)))
            .setSeatInfo(
                new EventSeat()
                    .setSeat(
                        new LocalizedString()
                            .setDefaultValue(
                                new TranslatedString().setLanguage("en-US").setValue("42")))
                    .setRow(
                        new LocalizedString()
                            .setDefaultValue(
                                new TranslatedString().setLanguage("en-US").setValue("G3")))
                    .setSection(
                        new LocalizedString()
                            .setDefaultValue(
                                new TranslatedString().setLanguage("en-US").setValue("5")))
                    .setGate(
                        new LocalizedString()
                            .setDefaultValue(
                                new TranslatedString().setLanguage("en-US").setValue("A"))))
            .setTicketHolderName("Ticket holder name")
            .setTicketNumber("Ticket number");

    service.eventticketobject().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/events/rest/v1/eventticketobject
    $batchObject = new EventTicketObject([
      '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
        ])
      ],
      'seatInfo' => new EventSeat([
        'seat' => new LocalizedString([
          'defaultValue' => new TranslatedString([
            'language' => 'en-US',
            'value' => '42'
          ])
        ]),
        'row' => new LocalizedString([
          'defaultValue' => new TranslatedString([
            'language' => 'en-US',
            'value' => 'G3'
          ])
        ]),
        'section' => new LocalizedString([
          'defaultValue' => new TranslatedString([
            'language' => 'en-US',
            'value' => '5'
          ])
        ]),
        'gate' => new LocalizedString([
          'defaultValue' => new TranslatedString([
            'language' => 'en-US',
            'value' => 'A'
          ])
        ])
      ]),
      'ticketHolderName' => 'Ticket holder name',
      'ticketNumber' => 'Ticket number'
    ]);

    $batch->add($this->service->eventticketobject->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/tickets/events/rest/v1/eventticketobject
        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
            }],
            'seatInfo': {
                'seat': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': '42'
                    }
                },
                'row': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': 'G3'
                    }
                },
                'section': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': '5'
                    }
                },
                'gate': {
                    'defaultValue': {
                        'language': 'en-US',
                        'value': 'A'
                    }
                }
            },
            'ticketHolderName': 'Ticket holder name',
            'ticketNumber': 'Ticket number'
        }

        batch.add(self.client.eventticketobject().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/tickets/events/rest/v1/eventticketobject
    EventTicketObject batchObject = new EventTicketObject
    {
      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
        }
      },
      SeatInfo = new EventSeat
      {
        Seat = new LocalizedString
        {
          DefaultValue = new TranslatedString
          {
            Language = "en-US",
            Value = "42"
          }
        },
        Row = new LocalizedString
        {
          DefaultValue = new TranslatedString
          {
            Language = "en-US",
            Value = "G3"
          }
        },
        Section = new LocalizedString
        {
          DefaultValue = new TranslatedString
          {
            Language = "en-US",
            Value = "5"
          }
        },
        Gate = new LocalizedString
        {
          DefaultValue = new TranslatedString
          {
            Language = "en-US",
            Value = "A"
          }
        }
      },
      TicketHolderName = "Ticket holder name",
      TicketNumber = "Ticket number"
    };

    data += "--batch_createobjectbatch\n";
    data += "Content-Type: application/json\n\n";
    data += "POST /walletobjects/v1/eventTicketObject/\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/events/rest/v1/eventticketobject
    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
        }
      ],
      'seatInfo': {
        'seat': {
          'defaultValue': {
            'language': 'en-US',
            'value': '42'
          }
        },
        'row': {
          'defaultValue': {
            'language': 'en-US',
            'value': 'G3'
          }
        },
        'section': {
          'defaultValue': {
            'language': 'en-US',
            'value': '5'
          }
        },
        'gate': {
          'defaultValue': {
            'language': 'en-US',
            'value': 'A'
          }
        },
      },
      'ticketHolderName': 'Ticket holder name',
      'ticketNumber': 'Ticket number'
    };

    data += '--batch_createobjectbatch\n';
    data += 'Content-Type: application/json\n\n';
    data += 'POST /walletobjects/v1/eventTicketObject\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 *demoEventticket) batchCreateObjects(issuerId, classSuffix string) {
	data := ""
	for i := 0; i < 3; i++ {
		objectSuffix := strings.ReplaceAll(uuid.New().String(), "-", "_")

		eventticketObject := new(walletobjects.EventTicketObject)
		eventticketObject.Id = fmt.Sprintf("%s.%s", issuerId, objectSuffix)
		eventticketObject.ClassId = fmt.Sprintf("%s.%s", issuerId, classSuffix)
		eventticketObject.TicketHolderName = "Ticket holder name"
		eventticketObject.TicketNumber = "Ticket number"
		eventticketObject.State = "ACTIVE"

		eventticketJson, _ := json.Marshal(eventticketObject)
		batchObject := fmt.Sprintf("%s", eventticketJson)

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