작동 방식

소개

제로터치 등록 API는 기기 리셀러가 통합을 자동화하는 데 도움이 됩니다. 조직의 영업 도구는 제로터치 등록을 기반으로 구축되어 사용자와 고객의 생산성을 높일 수 있습니다. API를 사용하면 사용자가 다음을 수행할 수 있습니다.

  • 구매한 기기를 고객의 제로터치 등록 계정에 할당합니다.
  • 고객의 제로터치 등록 계정을 만듭니다.
  • 조직의 전화번호 및 주문 메타데이터를 기기에 첨부합니다.
  • 고객에게 할당된 기기에 대한 보고서를 만듭니다.

이 문서에서는 API를 소개하고 패턴을 설명합니다. API를 직접 살펴보려면 자바, .NET 또는 Python의 빠른 시작을 참조하세요.

API 개념

고객과 기기는 API에서 사용하는 핵심 리소스입니다. 고객을 만들려면 create를 호출합니다. Claim API 메서드를 사용하여 기기를 만들 수 있습니다 (아래 참고). 조직에서는 제로터치 등록 포털을 사용하여 고객과 기기를 만들 수도 있습니다.

기기 및 고객 리소스 관계

고객
조직에서 기기를 판매하는 회사입니다. 고객에게는 nameID가 있습니다. 고객의 기기를 받거나 기기를 찾으려면 고객을 사용하세요. 자세한 내용은 Customer를 참고하세요.
기기
조직에서 고객에게 판매하는 제로터치 등록이 지원되는 Android 또는 ChromeOS 기기입니다. 기기에는 하드웨어 ID, 메타데이터, 고객 클레임이 있습니다. 기기는 API의 핵심이므로 거의 모든 메서드에서 사용합니다. 자세한 내용은 Device를 참고하세요.
DeviceIdentifier
IMEI 또는 MEID와 같은 하드웨어 ID를 캡슐화하여 제조된 기기를 식별합니다. DeviceIdentifier를 사용하여 찾거나 업데이트하거나 소유권을 주장하려는 기기를 타겟팅합니다. 자세한 내용은 식별자를 참고하세요.
DeviceMetadata
기기 메타데이터의 키-값 쌍을 저장합니다. DeviceMetadata를 사용하여 조직의 메타데이터를 저장합니다. 자세한 내용은 기기 메타데이터를 참고하세요.

앱에서 사용할 수 있는 모든 API 메서드와 리소스의 목록은 API 참조를 참고하세요.

고객 만들기

Android 기기의 경우 리셀러가 고객을 대신하여 고객 계정을 만들어야 합니다. 고객은 이 계정을 사용하여 제로터치 포털에 액세스하여 기기의 프로비저닝 설정을 구성합니다. 프로비저닝 설정을 구성하는 데 사용할 Google Workspace 계정이 이미 있는 ChromeOS 기기에는 이 설정이 필요하지 않습니다.

create API 메서드를 호출하여 제로터치 등록을 위한 고객 계정을 만들 수 있습니다. 고객의 제로터치 등록 포털에 회사 이름이 표시되므로 앱 사용자는 회사 이름이 올바른지 확인해야 합니다. 고객을 만든 후에는 고객 이름을 수정할 수 없습니다.

소유자가 되려면 Google 계정과 연결된 회사 이메일 주소를 하나 이상 포함해야 합니다. API와 함께 개인 Gmail 계정을 사용할 수 없습니다. 고객이 계정 연결과 관련하여 도움을 필요로 하는 경우 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.claim 또는 partners.devices.claimAsync 메서드를 호출합니다. 항상 sectionType의 값으로 SECTION_TYPE_ZERO_TOUCH를 제공합니다.

다른 고객을 위해 동일한 기기의 소유권을 주장하려면 먼저 고객의 기기의 소유권 주장을 취소해야 합니다 (아래 참조). 클레임 방법은 새 기기를 만들 때 IMEI 또는 MEID 또는 일련번호, 제조업체 이름 및 모델, ChromeOS 기기의 증명된 기기 ID를 포함한 DeviceIdentifier 필드의 validate을 처리합니다.

아래 스니펫은 기기의 소유권을 주장하는 방법을 보여줍니다.

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.unclaim 또는 partners.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 필드에 대한 작업을 확인합니다. done가 누락되었거나 false이면 작업이 여전히 실행 중인 것입니다.

응답

작업이 완료되면 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 메서드 (예: partners.devices.findByIdentifier)는 페이징된 결과를 지원합니다. 페이징된 결과를 사용하면 애플리케이션이 한 번에 한 페이지씩 큰 목록을 반복적으로 요청하고 처리할 수 있습니다.

API 메서드를 호출한 후 응답에 nextPageToken 값이 포함되어 있는지 확인합니다. nextPageTokennull가 아니면 앱에서 메서드를 다시 호출하여 다른 기기 페이지를 가져올 수 있습니다. 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의 작동 방식을 알게 되었으므로 자바, .NET 또는 Python의 빠른 시작으로 예시를 사용해 보세요.