運作方式

簡介

零接觸註冊機制 API 可協助裝置經銷商自動化整合作業。貴機構的銷售工具可在零接觸註冊機制中建構,讓使用者和客戶提升工作效率。運用 API 協助使用者:

  • 將購買的裝置指派給客戶的零接觸註冊機制帳戶。
  • 建立客戶的零接觸註冊機制帳戶。
  • 將貴機構的電話和訂單中繼資料附加至裝置。
  • 針對指派給客戶的裝置建立報表。

本文件將介紹這個 API,並說明這些模式。如果您想自行探索 API,請參考 Java.NETPython 快速入門導覽課程。

API 概念

客戶和裝置是您在 API 中使用的核心資源。如要建立客戶,請呼叫 create。您可以使用憑證附加資訊 API 方法建立裝置 (請見以下說明)。貴機構也可以使用零接觸註冊機制入口網站建立客戶和裝置。

裝置和客戶資源關係

客戶
貴機構販售裝置的公司。客戶擁有 nameID。想聲明擁有權或尋找他們的裝置時,請使用客戶。詳情請參閱 Customer
裝置
貴機構須使用零接觸註冊機制的 Android 或 ChromeOS 裝置,並販售給客戶。裝置具有硬體 ID、中繼資料和客戶憑證附加資訊。裝置是 API 的核心,因此幾乎所有方法都能使用。詳情請參閱 Device
DeviceIdentifier
封裝硬體 ID (例如 IMEI 或 MEID),以便識別製造裝置。使用 DeviceIdentifier 以指定要尋找、更新或聲明擁有權的裝置。詳情請參閱 ID
DeviceMetadata
儲存裝置中繼資料的鍵/值組合。使用 DeviceMetadata 儲存機構的中繼資料。詳情請參閱裝置中繼資料

如要列出應用程式可使用的所有 API 方法和資源,請參閱 API 參考資料

建立客戶

針對 Android 裝置,經銷商負責代表客戶建立客戶帳戶。客戶會使用這個帳戶存取零接觸入口網站,並調整裝置的佈建設定。ChromeOS 裝置已擁有 Google Workspace 帳戶,因此不需要進行這項設定,因為裝置會用來調整佈建設定。

您可以呼叫 create API 方法,為零接觸註冊機制建立客戶帳戶。由於客戶會在零接觸註冊機制入口網站中看到公司名稱,因此應用程式使用者應確認名稱正確無誤。客戶建立完成後,您就無法編輯客戶名稱。

您至少必須包含一個與 Google 帳戶相關聯的公司電子郵件地址,才能成為擁有者。您無法搭配個人 Gmail 帳戶使用 API,如果客戶需要連結帳戶方面的協助,請傳送將 Google 帳戶建立關聯的操作說明。

透過呼叫 API 建立客戶後,他們會管理員工的入口網站存取權,因此您無法透過 API 編輯客戶的使用者。以下程式碼片段說明如何建立客戶:

Java

// Provide the customer data as a Company type.
// The API requires a name and owners.
Company customer = new Company();
customer.setCompanyName("XYZ Corp");
customer.setOwnerEmails(Arrays.asList("liz@example.com", "darcy@example.com"));
customer.setAdminEmails(Collections.singletonList("jane@example.com"));

// Use our reseller ID for the parent resource name.
String parentResource = String.format("partners/%d", PARTNER_ID);

// Call the API to create the customer using the values in the company object.
CreateCustomerRequest body = new CreateCustomerRequest();
body.setCustomer(customer);
Company response = service.partners().customers().create(parentResource, body).execute();

.NET

// Provide the customer data as a Company type.
// The API requires a name and owners.
var customer = new Company
{
    CompanyName = "XYZ Corp",
    OwnerEmails = new String[] { "liz@example.com", "darcy@example.com" },
    AdminEmails = new String[] { "jane@example.com" }
};

// Use our reseller ID for the parent resource name.
var parentResource = String.Format("partners/{0}", PartnerId);

// Call the API to create the customer using the values in the company object.
var body = new CreateCustomerRequest
{
    Customer = customer
};
var request = service.Partners.Customers.Create(body, parentResource);
var response = request.Execute();

Python

# Provide the customer data as a Company type. The API requires
# a name and at least one owner.
company = {'companyName':'XYZ Corp', \
  'ownerEmails':['liz@example.com', 'darcy@example.com'], \
  'adminEmails':['jane@example.com']}

# Use our reseller ID for the parent resource name.
parent_resource = 'partners/{0}'.format(PARTNER_ID)

# Call the API to create the customer using the values in the company object.
response = service.partners().customers().create(parent=parent_resource,
    body={'customer':company}).execute()

如要進一步瞭解客戶員工的擁有者和管理員角色,請參閱入口網站使用者

為客戶領取裝置

客戶購買裝置後,就需要在帳戶中為這些裝置指定佈建設定。聲明裝置擁有權後,裝置就會新增至零接觸註冊機制,讓客戶可以調整佈建設定。

裝置的佈建記錄有一個「零接觸註冊機制」部分。您可以為客戶聲明記錄的零接觸註冊機制部分,藉此指派裝置。呼叫 partners.devices.claimpartners.devices.claimAsync 方法,做為引數提供給客戶。請一律提供 SECTION_TYPE_ZERO_TOUCH 做為 sectionType 的值。

您必須先取消認領客戶的裝置 (詳見下文),才能為其他客戶領取同一部裝置。這種聲明方法會在建立新裝置時validate DeviceIdentifier 欄位,包括 IMEI 或 MEID,或是序號、製造商名稱和型號,以及 ChromeOS 裝置通過認證的裝置 ID。

以下程式碼片段說明如何認領裝置:

Java

// Identify the device to claim.
DeviceIdentifier identifier = new DeviceIdentifier();
// The manufacturer value is optional but recommended for cellular devices
identifier.setManufacturer("Google");
identifier.setImei("098765432109875");

// Create the body to connect the customer with the device.
ClaimDeviceRequest body = new ClaimDeviceRequest();
body.setDeviceIdentifier(identifier);
body.setCustomerId(customerId);
body.setSectionType("SECTION_TYPE_ZERO_TOUCH");

// Claim the device.
ClaimDeviceResponse response = service.partners().devices().claim(PARTNER_ID, body).execute();

.NET

// Identify the device to claim.
var deviceIdentifier = new DeviceIdentifier
{
    // The manufacturer value is optional but recommended for cellular devices
    Manufacturer = "Google",
    Imei = "098765432109875"
};

// Create the body to connect the customer with the device.
ClaimDeviceRequest body = new ClaimDeviceRequest
{
    DeviceIdentifier = deviceIdentifier,
    CustomerId = CustomerId,
    SectionType = "SECTION_TYPE_ZERO_TOUCH"
};

// Claim the device.
var response = service.Partners.Devices.Claim(body, PartnerId).Execute();

Python

# Identify the device to claim.
# The manufacturer value is optional but recommended for cellular devices
device_identifier = {'manufacturer':'Google', 'imei':'098765432109875'}

# Create the body to connect the customer with the device.
request_body = {'deviceIdentifier':device_identifier, \
    'customerId':customer_id, \
    'sectionType':'SECTION_TYPE_ZERO_TOUCH'}

# Claim the device.
response = service.partners().devices().claim(partnerId=PARTNER_ID,
    body=request_body).execute()

取消認領裝置

貴機構可以取消客戶裝置的擁有權。取消註冊裝置後,該裝置就會從零接觸註冊機制中移除。經銷商可能會為想要遷移至其他帳戶、退回或不慎認領的裝置取消擁有權。呼叫 partners.devices.unclaimpartners.devices.unclaimAsync 方法,以便從客戶取消裝置擁有權。

供應商

您可以使用供應商來代表您的經銷商網路中的經銷商合作夥伴,或是全球經銷商網路內的當地營運商,或者會代表您販售裝置的任何機構。供應商可協助您區分使用者、客戶和裝置:

  • 您建立的供應商無法查看您的零接觸註冊機制帳戶或彼此的帳戶。
  • 您可以查看供應商的客戶和裝置,也可以取消註冊供應商的裝置。但您無法將裝置指派給供應商的客戶。

使用入口網站為您的機構建立供應商;您無法使用 API。您的帳戶角色必須是擁有者才能建立新的供應商。如果貴機構有供應商,您可以呼叫 partners.vendors.list 列出供應商;並呼叫 partners.vendors.customers.list 來取得供應商的客戶。以下範例使用這兩種方法列印報表,顯示供應商客戶的《服務條款》狀態:

Java

// First, get the organization's vendors.
String parentResource = String.format("partners/%d", PARTNER_ID);
ListVendorsResponse results = service.partners().vendors().list(parentResource).execute();
if (results.getVendors() == null) {
  return;
}

// For each vendor, report the company name and a maximum 5 customers.
for (Company vendor: results.getVendors()) {
  System.out.format("\n%s customers\n", vendor.getCompanyName());
  System.out.println("---");
  // Use the vendor's API resource name as the parent resource.
  AndroidProvisioningPartner.Partners.Vendors.Customers.List customerRequest =
      service.partners().vendors().customers().list(vendor.getName());
  customerRequest.setPageSize(5);
  ListVendorCustomersResponse customerResponse = customerRequest.execute();

  List<Company> customers = customerResponse.getCustomers();
  if (customers == null) {
    System.out.println("No customers");
    break;
  } else {
    for (Company customer: customers) {
      System.out.format("%s: %s\n",
          customer.getCompanyName(),
          customer.getTermsStatus());
    }
  }
}

.NET

// First, get the organization's vendors.
var parentResource = String.Format("partners/{0}", PartnerId);
var results = service.Partners.Vendors.List(parentResource).Execute();
if (results.Vendors == null)
{
    return;
}

// For each vendor, report the company name and a maximum 5 customers.
foreach (Company vendor in results.Vendors)
{
    Console.WriteLine("\n{0} customers", vendor);
    Console.WriteLine("---");
    // Use the vendor's API resource name as the parent resource.
    PartnersResource.VendorsResource.CustomersResource.ListRequest customerRequest =
        service.Partners.Vendors.Customers.List(vendor.Name);
    customerRequest.PageSize = 5;
    var customerResponse = customerRequest.Execute();

    IList<Company> customers = customerResponse.Customers;
    if (customers == null)
    {
        Console.WriteLine("No customers");
        break;
    }
    else
    {
        foreach (Company customer in customers)
        {
            Console.WriteLine("{0}: {1}", customer.Name, customer.TermsStatus);
        }
    }
}

Python

# First, get the organization's vendors.
parent_resource = 'partners/{0}'.format(PARTNER_ID)
vendor_response = service.partners().vendors().list(
    parent=parent_resource).execute()
if 'vendors' not in vendor_response:
  return

# For each vendor, report the company name and a maximum 5 customers.
for vendor in vendor_response['vendors']:
  print '\n{0} customers'.format(vendor['companyName'])
  print '---'
  # Use the vendor's API resource name as the parent resource.
  customer_response = service.partners().vendors().customers().list(
      parent=vendor['name'], pageSize=5).execute()
  if 'customers' not in customer_response:
    print 'No customers'
    break
  for customer in customer_response['customers']:
    print '  {0}: {1}'.format(customer['name'], customer['termsStatus'])

如有多個裝置,您可能需要知道聲明裝置擁有權的經銷商或供應商。如要取得數字經銷商 ID,請檢查裝置憑證附加記錄中 resellerId 欄位的值。

貴機構可以取消供應商認領裝置的擁有權。對於會修改裝置的其他 API 呼叫,您應先檢查貴機構是否已聲明裝置擁有權,再呼叫 API 方法。以下範例說明如何執行此操作:

Java

// Get the devices claimed for two customers: one of our organization's
// customers and one of our vendor's customers.
FindDevicesByOwnerRequest body = new FindDevicesByOwnerRequest();
body.setSectionType("SECTION_TYPE_ZERO_TOUCH");
body.setCustomerId(Arrays.asList(resellerCustomerId, vendorCustomerId));
body.setLimit(MAX_PAGE_SIZE);
FindDevicesByOwnerResponse response =
    service.partners().devices().findByOwner(PARTNER_ID, body).execute();
if (response.getDevices() == null) {
  return;
}

for (Device device: response.getDevices()) {
  // Confirm the device was claimed by our reseller and not a vendor before
  // updating metadata in another method.
  for (DeviceClaim claim: device.getClaims()) {
    if (claim.getResellerId() == PARTNER_ID) {
      updateDeviceMetadata(device.getDeviceId());
      break;
    }
  }
}

.NET

// Get the devices claimed for two customers: one of our organization's
// customers and one of our vendor's customers.
FindDevicesByOwnerRequest body = new FindDevicesByOwnerRequest
{
    Limit = MaxPageSize,
    SectionType = "SECTION_TYPE_ZERO_TOUCH",
    CustomerId = new List<long?>
    {
        resellerCustomerId,
        vendorCustomerId
    }
};
var response = service.Partners.Devices.FindByOwner(body, PartnerId).Execute();
if (response.Devices == null)
{
    return;
}

foreach (Device device in response.Devices)
{
    // Confirm the device was claimed by our reseller and not a vendor before
    // updating metadata in another method.
    foreach (DeviceClaim claim in device.Claims)
    {
        if (claim.ResellerId == PartnerId)
        {
            UpdateDeviceMetadata(device.DeviceId);
            break;
        }
    }
}

Python

# Get the devices claimed for two customers: one of our organization's
# customers and one of our vendor's customers.
request_body = {'limit':MAX_PAGE_SIZE, \
  'pageToken':None, \
  'customerId':[reseller_customer_id, vendor_customer_id], \
  'sectionType':'SECTION_TYPE_ZERO_TOUCH'}
response = service.partners().devices().findByOwner(partnerId=PARTNER_ID,
    body=request_body).execute()

for device in response['devices']:
  # Confirm the device was claimed by our reseller and not a vendor before
  # updating metadata in another method.
  for claim in device['claims']:
    if claim['resellerId'] == PARTNER_ID:
      update_device_metadata(device['deviceId'])
      break

長時間執行的批次作業

API 包含裝置方法的非同步版本。這些方法可批次處理多種裝置,同時同步方法會為每個 API 要求處理一部裝置。非同步方法名稱有「Async」後置字串,例如 claimAsync

非同步 API 方法會在處理完成之前傳回結果。非同步方法還能讓應用程式 (或工具) 在等待長時間執行的作業完成時,對使用者保持回應。您的應用程式應定期檢查作業狀態。

作業套件

您可以使用 Operation 追蹤長時間執行的批次作業。若呼叫非同步方法成功,會在回應中傳回作業的參照。下列 JSON 程式碼片段顯示呼叫 updateMetadataAsync 後的一般回應:

{
  "name": "operations/apibatchoperation/1234567890123476789"
}

每項作業都包含個別工作的清單。呼叫 operations.get 以瞭解作業中工作的狀態和結果。下列程式碼片段說明執行此操作的方法。在您的應用程式中,您需要處理所有錯誤。

Java

// Build out the request body to apply the same order number to a customer's
// purchase of 2 devices.
UpdateMetadataArguments firstUpdate = new UpdateMetadataArguments();
firstUpdate.setDeviceMetadata(metadata);
firstUpdate.setDeviceId(firstTargetDeviceId);

UpdateMetadataArguments secondUpdate = new UpdateMetadataArguments();
secondUpdate.setDeviceMetadata(metadata);
secondUpdate.setDeviceId(firstTargetDeviceId);

// Start the device metadata update.
UpdateDeviceMetadataInBatchRequest body = new UpdateDeviceMetadataInBatchRequest();
body.setUpdates(Arrays.asList(firstUpdate, secondUpdate));
Operation response = service
    .partners()
    .devices()
    .updateMetadataAsync(PARTNER_ID, body)
    .execute();

// Assume the metadata update started, so get the Operation for the update.
Operation operation = service.operations().get(response.getName()).execute();

.NET

// Build out the request body to apply the same order number to a customer's
// purchase of 2 devices.
var updates = new List<UpdateMetadataArguments>
{
    new UpdateMetadataArguments
    {
        DeviceMetadata = metadata,
        DeviceId = firstTargetDeviceId
    },
    new UpdateMetadataArguments
    {
        DeviceMetadata = metadata,
        DeviceId = secondTargetDeviceId
    }
};

// Start the device metadata update.
UpdateDeviceMetadataInBatchRequest body = new UpdateDeviceMetadataInBatchRequest
{
    Updates = updates
};
var response = service.Partners.Devices.UpdateMetadataAsync(body, PartnerId).Execute();

// Assume the metadata update started, so get the Operation for the update.
Operation operation = service.Operations.Get(response.Name).Execute();

Python

# Build out the request body to apply the same order number to a customer's
# purchase of 2 devices.
updates = [{'deviceMetadata':metadata,'deviceId':first_target_device_id},
    {'deviceMetadata':metadata,'deviceId':second_target_device_id}]

# Start the device metadata update.
response = service.partners().devices().updateMetadataAsync(
    partnerId=PARTNER_ID, body={'updates':updates}).execute()

# Assume the metadata update started, so get the Operation for the update.
operation = service.operations().get(name=response['name']).execute()

如要確認作業是否已完成,請查看作業中是否有值為 truedone 欄位。如果缺少 donefalse,表示作業仍在執行中。

回應

作業完成後,即使所有個別工作全部或完全沒有成功,API 仍會根據結果更新作業。response 欄位是 DevicesLongRunningOperationResponse 物件,用於詳細說明作業中每個裝置的處理方式。

檢查 successCount 欄位可有效得知是否有任何工作失敗,並避免反覆查看大型結果清單。DevicesLongRunningOperationResponseperDeviceStatus 欄位是 OperationPerDevice 例項清單,其中詳細列出作業中的每部裝置。清單順序與原始要求中的工作相符。

每項 OperationPerDevice 工作都包含 result 欄位,以及伺服器收到要求的提醒摘要。請使用 result 欄位檢查工作是否成功或失敗。

下列 JSON 程式碼片段顯示呼叫 updateMetadataAsync 後,從作業的一般回應中:

"response": {
  "perDeviceStatus": [
    {
      "result": {
        "deviceId": "12345678901234567",
        "status": "SINGLE_DEVICE_STATUS_SUCCESS"
      },
      "updateMetadata": {
        "deviceId": "12345678901234567",
        "deviceMetadata": {
          "entries": {
            "phonenumber": "+1 (800) 555-0100"
          }
        }
      }
    }
  ],
  "successCount": 1
}

曲目播放進度

如果應用程式需要追蹤進度,建議您定期重新擷取作業。metadata 欄位包含 DevicesLongRunningOperationMetadata 執行個體,可協助應用程式檢查執行中作業的最新進度。請使用下表中列出的 DevicesLongRunningOperationMetadata 欄位追蹤作業進度:

欄位 常見用途
processingStatus 隨著作業進度,從 BATCH_PROCESS_PENDING 變更為 BATCH_PROCESS_IN_PROGRESS,然後變更為 BATCH_PROCESS_PROCESSED
progress 已處理的更新百分比。您的應用程式可以使用這個時間估算完成時間。由於 progress 值可能是 100,因此在作業結束時,請檢查作業的 done 欄位,確認該作業是否已完成,並有結果。
devicesCount 顯示作業中的更新次數。如果 API 無法剖析部分更新項目,這可能和要求中的更新數量不同。

以下簡化的範例說明應用程式如何使用進度中繼資料設定輪詢間隔。在應用程式中,您可能需要更複雜的工作執行器來進行輪詢。此外,您也必須新增錯誤處理機制。

Java

// Milliseconds between polling the API.
private static long MIN_INTERVAL = 2000;
private static long MAX_INTERVAL = 10000;

// ...
// Start the device metadata update.
Operation response = service
    .partners()
    .devices()
    .updateMetadataAsync(PARTNER_ID, body)
    .execute();
String operationName = response.getName();

// Start polling for completion.
long startTime = new Date().getTime();
while (true) {

  // Get the latest update on the operation's progress using the API.
  Operation operation = service.operations().get(operationName).execute();

  if (operation.get("done") != null && operation.getDone()) {
    // The operation is finished. Print the status.
    System.out.format("Operation complete: %s of %s successful device updates\n",
        operation.getResponse().get("successCount"),
        operation.getMetadata().get("devicesCount"));
    break;

  } else {
    // Estimate how long the operation *should* take - within min and max value.
    BigDecimal opProgress = (BigDecimal) operation.getMetadata().get("progress");
    double progress = opProgress.longValue();
    long interval = MAX_INTERVAL;
    if (progress > 0) {
      interval = (long) ((new Date().getTime() - startTime) *
          ((100.0 - progress) / progress));
    }
    interval = Math.max(MIN_INTERVAL, Math.min(interval, MAX_INTERVAL));

    // Sleep until the operation should be complete.
    Thread.sleep(interval);
  }
}

.NET

// Milliseconds between polling the API.
private static double MinInterval = 2000;
private static double MaxInterval = 10000;

// ...
// Start the device metadata update.
var response = service.Partners.Devices.UpdateMetadataAsync(body, PartnerId).Execute();
var operationName = response.Name;

// Start polling for completion.
var startTime = DateTime.Now;
while (true)
{

    // Get the latest update on the operation's progress using the API.
    Operation operation = service.Operations.Get(operationName).Execute();

    if (operation.Done == true)
    {
        // The operation is finished. Print the status.
        Console.WriteLine("Operation complete: {0} of {1} successful device updates",
                          operation.Response["successCount"],
                          operation.Metadata["devicesCount"]);
        break;
    }
    else
    {
        // Estimate how long the operation *should* take - within min and max value.
        double progress = (double)(long)operation.Metadata["progress"];
        double interval = MaxInterval;
        if (progress > 0)
        {
            interval = DateTime.Now.Subtract(startTime).TotalMilliseconds *
                                     ((100.0 - progress) / progress);
        }
        interval = Math.Max(MinInterval, Math.Min(interval, MaxInterval));

        // Sleep until the operation should be complete.
        System.Threading.Thread.Sleep((int)interval);
    }
}

Python

# Seconds between polling the API.
MIN_INTERVAL = 2;
MAX_INTERVAL = 10;

# ...
# Start the device metadata update
response = service.partners().devices().updateMetadataAsync(
  partnerId=PARTNER_ID, body={'updates':updates}).execute()

op_name = response['name']
start_time = time.time()

# Start polling for completion
while True:
  # Get the latest update on the operation's progress using the API
  op = service.operations().get(name=op_name).execute()

  if 'done' in op and op['done']:
    # The operation is finished. Print the status.
    print('Operation complete: {0} of {1} successful device updates'.format(
      op['response']['successCount'], op['metadata']['devicesCount']
    ))
    break
  else:
    # Estimate how long the operation *should* take - within min and max.
    progress = op['metadata']['progress']
    interval = MIN_INTERVAL
    if progress > 0:
      interval = (time.time() - start_time) * ((100.0 - progress) / progress)
    interval = max(MIN_INTERVAL, min(interval, MAX_INTERVAL))

    # Sleep until the operation should be complete.
    time.sleep(interval)

選擇最適合應用程式使用者的輪詢方式。有些應用程式使用者可能會為了等待程序完成而定期更新進度。

分頁結果

partners.devices.findByOwner API 方法可能會傳回非常龐大的裝置清單。為了縮減回應大小,此 API 及其他 API 方法 (例如 partners.devices.findByIdentifier) 支援分頁結果。使用分頁結果時,應用程式可以反覆要求及處理一個頁面的大清單。

呼叫 API 方法後,請檢查回應是否包含 nextPageToken 值。如果 nextPageToken 不是 null,應用程式可再次呼叫該方法,以使用其擷取另一頁。您必須在 limit 參數中設定裝置數量上限。如果 nextPageTokennull,表示應用程式要求最後一頁。

以下範例方法顯示應用程式如何輸出裝置清單,一次一個頁面:

Java

private static long MAX_PAGE_SIZE = 10;

// ...
/**
 * Demonstrates how to loop through paginated lists of devices.
 * @param pageToken       The token specifying which result page to return.
 * @throws IOException    If the zero-touch API call fails.
 */
private void printDevices(String pageToken) throws IOException {

  // Create the request body to find the customer's devices.
  FindDevicesByOwnerRequest body = new FindDevicesByOwnerRequest();
  body.setLimit(MAX_PAGE_SIZE);
  body.setSectionType("SECTION_TYPE_ZERO_TOUCH");
  body.setCustomerId(Collections.singletonList(targetCustomerId));

  // Call the API to get a page of Devices. Send a page token from the method
  // argument (might be None). If the page token is None, the API returns the first page.
  FindDevicesByOwnerResponse response =
      service.partners().devices().findByOwner(PARTNER_ID, body).execute();
  if (response.getDevices() == null) {
    return;
  }

  // Print the devices included in this page of results.
  for (Device device: response.getDevices()) {
    System.out.format("Device %s\n", device.getName());
  }
  System.out.println("---");

  // Check to see if another page of devices is available. If yes,
  // fetch and print the devices.
  if (response.getNextPageToken() != null) {
    this.printDevices(response.getNextPageToken());
  }
}

// ...
// Pass null to start printing the first page of devices.
printDevices(null);

.NET

private static int MaxPageSize = 10;

// ...
/// <summary>Demonstrates how to loop through paginated lists of devices.</summary>
/// <param name="pageToken">The token specifying which result page to return.</param>
private void PrintDevices(string pageToken)
{
    // Create the request body to find the customer's devices.
    FindDevicesByOwnerRequest body = new FindDevicesByOwnerRequest
    {
        PageToken = pageToken,
        Limit = MaxPageSize,
        SectionType = "SECTION_TYPE_ZERO_TOUCH",
        CustomerId = new List<long?>
        {
            targetCustomerId
        }
    };

    // Call the API to get a page of Devices. Send a page token from the method
    // argument (might be None). If the page token is None, the API returns the first page.
    var response = service.Partners.Devices.FindByOwner(body, PartnerId).Execute();
    if (response.Devices == null)
    {
        return;
    }

    // Print the devices included in this page of results.
    foreach (Device device in response.Devices)
    {
        Console.WriteLine("Device: {0}", device.Name);
    }
    Console.WriteLine("---");

    // Check to see if another page of devices is available. If yes,
    // fetch and print the devices.
    if (response.NextPageToken != null)
    {
        this.PrintDevices(response.NextPageToken);
    }
}

// ...
// Pass null to start printing the first page of devices.
PrintDevices(null);

Python

MAX_PAGE_SIZE = 10;

# ...
def print_devices(page_token):
  """Demonstrates how to loop through paginated lists of devices.

  Args:
    page_token: The token specifying which result page to return.
  """

   # Create the body to find the customer's devices.
  request_body = {'limit':MAX_PAGE_SIZE, \
    'pageToken':page_token, \
    'customerId':[target_customer_id], \
    'sectionType':'SECTION_TYPE_ZERO_TOUCH'}

  # Call the API to get a page of Devices. Send a page token from the method
  # argument (might be None). If the page token is None,
  # the API returns the first page.
  response = service.partners().devices().findByOwner(partnerId=PARTNER_ID,
    body=request_body).execute()

  # Print the devices included in this page of results.
  for device in response['devices']:
    print 'Device: {0}'.format(device['name'])
  print '---'

  # Check to see if another page of devices is available. If yes,
  # fetch and print the devices.
  if 'nextPageToken' in response:
    print_devices(response['nextPageToken'])

# ...
# Pass None to start printing the first page of devices.
print_devices(None);

後續步驟

現在您已經瞭解 API 的運作方式,不妨試試 Java.NETPython 快速入門導覽課程的範例。