From 9f6c0bb4f9ccf28bf00f040d5125d265339748fd Mon Sep 17 00:00:00 2001 From: devchas Date: Tue, 20 Oct 2020 08:58:23 -0400 Subject: [PATCH 1/4] Update messaging in AddCustomerMatchUserList example Change-Id: I9cec5b2a44d3bb4808f552af19fe714a0f382eb7 --- .../examples/remarketing/AddCustomerMatchUserList.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java index d69aca96c9..23f3f24beb 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java @@ -256,6 +256,8 @@ private void addUsersToCustomerMatchUserList( System.out.println("Waiting until operation completes."); // The polling future implements a default back-off policy for retrying. + // Note: Retrieving the results of the asynchronous request is for demonstrative purposes only + // as it may take several hours for the user list to be populated with the users. runFuture.getPollingFuture().get(MAX_TOTAL_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS); System.out.printf( "Offline user data job with resource name '%s' has finished.%n", From 224d9beeb76f8463d5112bb038123fc94f119e3f Mon Sep 17 00:00:00 2001 From: devchas Date: Tue, 20 Oct 2020 09:09:27 -0400 Subject: [PATCH 2/4] Update note Change-Id: I387057f002c96be24f82b8690b48737079b67e43 --- .../examples/remarketing/AddCustomerMatchUserList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java index 23f3f24beb..9658e23e43 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java @@ -257,7 +257,7 @@ private void addUsersToCustomerMatchUserList( // The polling future implements a default back-off policy for retrying. // Note: Retrieving the results of the asynchronous request is for demonstrative purposes only - // as it may take several hours for the user list to be populated with the users. + // as the job may not complete for several hours. runFuture.getPollingFuture().get(MAX_TOTAL_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS); System.out.printf( "Offline user data job with resource name '%s' has finished.%n", From 677a029e53996068a7f46837c5a78b71622b0a90 Mon Sep 17 00:00:00 2001 From: devchas Date: Tue, 20 Oct 2020 12:05:48 -0400 Subject: [PATCH 3/4] Update example to check status Change-Id: Ia6322cf443253e488e2701d8aed72f141e9afd8e --- .../remarketing/AddCustomerMatchUserList.java | 149 +++++++++++------- 1 file changed, 91 insertions(+), 58 deletions(-) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java index 9658e23e43..6f1b5466bd 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java @@ -24,6 +24,7 @@ import com.google.ads.googleads.v5.common.UserData; import com.google.ads.googleads.v5.common.UserIdentifier; import com.google.ads.googleads.v5.enums.CustomerMatchUploadKeyTypeEnum.CustomerMatchUploadKeyType; +import com.google.ads.googleads.v5.enums.OfflineUserDataJobStatusEnum.OfflineUserDataJobStatus; import com.google.ads.googleads.v5.enums.OfflineUserDataJobTypeEnum.OfflineUserDataJobType; import com.google.ads.googleads.v5.errors.GoogleAdsError; import com.google.ads.googleads.v5.errors.GoogleAdsException; @@ -41,11 +42,9 @@ import com.google.ads.googleads.v5.services.SearchGoogleAdsStreamResponse; import com.google.ads.googleads.v5.services.UserListOperation; import com.google.ads.googleads.v5.services.UserListServiceClient; -import com.google.api.gax.longrunning.OperationFuture; import com.google.api.gax.rpc.ServerStream; import com.google.common.collect.ImmutableList; import com.google.protobuf.BoolValue; -import com.google.protobuf.Empty; import com.google.protobuf.Int64Value; import com.google.protobuf.StringValue; import java.io.FileNotFoundException; @@ -56,9 +55,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; /** * Creates a user list (a.k.a. audience) and uploads members to populate the list. @@ -76,8 +72,6 @@ */ public class AddCustomerMatchUserList { - private static final long MAX_TOTAL_POLL_INTERVAL_SECONDS = 60L; - private static class AddCustomerMatchUserListParams extends CodeSampleParams { @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) @@ -85,8 +79,7 @@ private static class AddCustomerMatchUserListParams extends CodeSampleParams { } public static void main(String[] args) - throws InterruptedException, ExecutionException, TimeoutException, - UnsupportedEncodingException { + throws UnsupportedEncodingException { AddCustomerMatchUserListParams params = new AddCustomerMatchUserListParams(); if (!params.parseArguments(args)) { @@ -133,16 +126,12 @@ public static void main(String[] args) * @throws GoogleAdsException if an API request failed with one or more service errors. */ private void runExample(GoogleAdsClient googleAdsClient, long customerId) - throws InterruptedException, ExecutionException, TimeoutException, - UnsupportedEncodingException { + throws UnsupportedEncodingException { // Creates a Customer Match user list. String userListResourceName = createCustomerMatchUserList(googleAdsClient, customerId); // Adds members to the user list. addUsersToCustomerMatchUserList(googleAdsClient, customerId, userListResourceName); - - // Prints information about the user list. - printCustomerMatchUserListInfo(googleAdsClient, customerId, userListResourceName); } /** @@ -193,13 +182,12 @@ private String createCustomerMatchUserList(GoogleAdsClient googleAdsClient, long * * @param googleAdsClient the Google Ads API client. * @param customerId the client customer ID. - * @param userListResourceName the resource name of the Customer Match user list to add members + * @param userListResourceName the resource name of the Customer Match user list to add members. * to. */ private void addUsersToCustomerMatchUserList( GoogleAdsClient googleAdsClient, long customerId, String userListResourceName) - throws InterruptedException, ExecutionException, TimeoutException, - UnsupportedEncodingException { + throws UnsupportedEncodingException { try (OfflineUserDataJobServiceClient offlineUserDataJobServiceClient = googleAdsClient.getLatestVersion().createOfflineUserDataJobServiceClient()) { // Creates a new offline user data job. @@ -245,23 +233,14 @@ private void addUsersToCustomerMatchUserList( System.out.printf( "Successfully added %d operations to the offline user data job.%n", userDataJobOperations.size()); - } - - // Issues an asynchronous request to run the offline user data job for executing all added - // operations. - OperationFuture runFuture = - offlineUserDataJobServiceClient.runOfflineUserDataJobAsync( - offlineUserDataJobResourceName); - System.out.println("Asynchronous request to execute the added operations started."); - System.out.println("Waiting until operation completes."); - // The polling future implements a default back-off policy for retrying. - // Note: Retrieving the results of the asynchronous request is for demonstrative purposes only - // as the job may not complete for several hours. - runFuture.getPollingFuture().get(MAX_TOTAL_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS); - System.out.printf( - "Offline user data job with resource name '%s' has finished.%n", - offlineUserDataJobResourceName); + // Offline user data jobs may take up to 24 hours to complete, so instead of waiting for the + // job to complete, retrieves and displays the job status once. If the job is completed + // successfully, prints information about the user list. Otherwise, prints the query to use + // to check the job again later. + checkJobStatus( + googleAdsClient, customerId, offlineUserDataJobResourceName, userListResourceName); + } } } @@ -312,6 +291,85 @@ private List buildOfflineUserDataJobOperations() return operations; } + /** + * Returns the result of normalizing and then hashing the string using the provided digest. + * Private customer data must be hashed during upload, as described at + * https://support.google.com/google-ads/answer/7474263. + * + * @param digest the digest to use to hash the normalized string. + * @param s the string to normalize and hash. + */ + private String normalizeAndHash(MessageDigest digest, String s) + throws UnsupportedEncodingException { + // Normalizes by removing leading and trailing whitespace and converting all characters to + // lower case. + String normalized = s.trim().toLowerCase(); + // Hashes the normalized string using the hashing algorithm. + byte[] hash = digest.digest(normalized.getBytes("UTF-8")); + StringBuilder result = new StringBuilder(); + for (byte b : hash) { + result.append(String.format("%02x", b)); + } + + return result.toString(); + } + + /** + * Retrieves, checks, and prints the status of the offline user data job. + * + * @param googleAdsClient the Google Ads API client. + * @param customerId the client customer ID. + * @param offlineUserDataJobResourceName the resource name of the OfflineUserDataJob to get the + * status for. + * @param userListResourceName the resource name of the Customer Match user list. + */ + private void checkJobStatus( + GoogleAdsClient googleAdsClient, + long customerId, + String offlineUserDataJobResourceName, + String userListResourceName) { + try (GoogleAdsServiceClient googleAdsServiceClient = + googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { + String query = + String.format( + "SELECT offline_user_data_job.resource_name, " + + "offline_user_data_job.id, " + + "offline_user_data_job.status, " + + "offline_user_data_job.type, " + + "offline_user_data_job.failure_reason " + + "FROM offline_user_data_job " + + "WHERE offline_user_data_job.resource_name = '%s'", + offlineUserDataJobResourceName); + // Issues the query and gets the GoogleAdsRow containing the job from the response. + GoogleAdsRow googleAdsRow = + googleAdsServiceClient + .search(Long.toString(customerId), query) + .iterateAll() + .iterator() + .next(); + OfflineUserDataJob offlineUserDataJob = googleAdsRow.getOfflineUserDataJob(); + System.out.printf( + "Offline user data job ID %d with type '%s' has status: %s%n", + offlineUserDataJob.getId().getValue(), + offlineUserDataJob.getType(), + offlineUserDataJob.getStatus()); + OfflineUserDataJobStatus jobStatus = offlineUserDataJob.getStatus(); + if (OfflineUserDataJobStatus.SUCCESS == jobStatus) { + // Prints information about the user list. + printCustomerMatchUserListInfo(googleAdsClient, customerId, userListResourceName); + } else if (OfflineUserDataJobStatus.FAILED == jobStatus) { + System.out.printf(" Failure reason: %s%n", offlineUserDataJob.getFailureReason()); + } else if (OfflineUserDataJobStatus.PENDING == jobStatus + || OfflineUserDataJobStatus.RUNNING == jobStatus) { + System.out.println(); + System.out.printf( + "To check the status of the job periodically, use the following GAQL query with" + + " GoogleAdsService.search:%n%s%n", + query); + } + } + } + /** * Prints information about the Customer Match user list. * @@ -350,31 +408,6 @@ private void printCustomerMatchUserListInfo( userList.getResourceName(), userList.getSizeForDisplay().getValue(), userList.getSizeForSearch().getValue()); - System.out.println( - "Reminder: It may take several hours for the user list to be populated with the users."); } } - - /** - * Returns the result of normalizing and then hashing the string using the provided digest. - * Private customer data must be hashed during upload, as described at - * https://support.google.com/google-ads/answer/7474263. - * - * @param digest the digest to use to hash the normalized string. - * @param s the string to normalize and hash. - */ - private String normalizeAndHash(MessageDigest digest, String s) - throws UnsupportedEncodingException { - // Normalizes by removing leading and trailing whitespace and converting all characters to - // lower case. - String normalized = s.trim().toLowerCase(); - // Hashes the normalized string using the hashing algorithm. - byte[] hash = digest.digest(normalized.getBytes("UTF-8")); - StringBuilder result = new StringBuilder(); - for (byte b : hash) { - result.append(String.format("%02x", b)); - } - - return result.toString(); - } } From 24577f6c89b50c3b2511ffe472c0a8fd7fd0d04e Mon Sep 17 00:00:00 2001 From: devchas Date: Wed, 21 Oct 2020 14:40:21 -0400 Subject: [PATCH 4/4] Change to allowlisted Change-Id: I9a3affa7cc8a6bf9fc55554d98bba265562e0ddd --- .../examples/remarketing/AddCustomerMatchUserList.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java index 6f1b5466bd..a28990f5e6 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/remarketing/AddCustomerMatchUserList.java @@ -62,7 +62,7 @@ *

Notes: * *

    - *
  • This feature is only available to whitelisted accounts. See + *
  • This feature is only available to allowlisted accounts. See * https://support.google.com/adspolicy/answer/6299717 for more details. *
  • It may take up to several hours for the list to be populated with members. *
  • Email addresses must be associated with a Google account.