Java
// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.ads.googleads.examples.extensions; import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime; import com.beust.jcommander.Parameter; import com.google.ads.googleads.examples.utils.ArgumentNames; import com.google.ads.googleads.examples.utils.CodeSampleParams; import com.google.ads.googleads.lib.GoogleAdsClient; import com.google.ads.googleads.v13.common.MatchingFunction; import com.google.ads.googleads.v13.common.Operand; import com.google.ads.googleads.v13.common.Operand.ConstantOperand; import com.google.ads.googleads.v13.enums.FeedOriginEnum.FeedOrigin; import com.google.ads.googleads.v13.enums.MatchingFunctionOperatorEnum.MatchingFunctionOperator; import com.google.ads.googleads.v13.enums.PlaceholderTypeEnum.PlaceholderType; import com.google.ads.googleads.v13.errors.GoogleAdsError; import com.google.ads.googleads.v13.errors.GoogleAdsException; import com.google.ads.googleads.v13.resources.CustomerFeed; import com.google.ads.googleads.v13.resources.Feed; import com.google.ads.googleads.v13.resources.Feed.PlacesLocationFeedData; import com.google.ads.googleads.v13.resources.Feed.PlacesLocationFeedData.OAuthInfo; import com.google.ads.googleads.v13.services.CustomerFeedOperation; import com.google.ads.googleads.v13.services.CustomerFeedServiceClient; import com.google.ads.googleads.v13.services.FeedOperation; import com.google.ads.googleads.v13.services.FeedServiceClient; import com.google.ads.googleads.v13.services.MutateCustomerFeedsResponse; import com.google.ads.googleads.v13.services.MutateFeedsResponse; import com.google.common.collect.ImmutableList; import java.io.FileNotFoundException; import java.io.IOException; /** * Adds a feed that syncs feed items from a Business Profile account and associates the feed with a * customer. */ public class AddBusinessProfileLocationExtensions { // The required scope for setting the OAuth info. private final String GOOGLE_ADS_SCOPE = "https://www.googleapis.com/auth/adwords"; // The maximum number of CustomerFeed ADD operation attempts to make before throwing an exception. private static final int MAX_CUSTOMER_FEED_ADD_ATTEMPTS = 10; private static class AddBusinessProfileLocationExtensionsParams extends CodeSampleParams { @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) private Long customerId; @Parameter(names = ArgumentNames.BUSINESS_PROFILE_EMAIL_ADDRESS, required = true) private String businessProfileEmailAddress; @Parameter(names = ArgumentNames.BUSINESS_ACCOUNT_IDENTIFIER) private String businessAccountIdentifier; @Parameter(names = ArgumentNames.BUSINESS_PROFILE_ACCESS_TOKEN, required = true) private String businessProfileAccessToken; } public static void main(String[] args) throws InterruptedException, IOException { AddBusinessProfileLocationExtensionsParams params = new AddBusinessProfileLocationExtensionsParams(); if (!params.parseArguments(args)) { // Either pass the required parameters for this example on the command line, or insert them // into the code here. See the parameter class definition above for descriptions. params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE"); params.businessProfileEmailAddress = "INSERT_BUSINESS_PROFILE_EMAIL_ADDRESS_HERE"; params.businessAccountIdentifier = "INSERT_BUSINESS_ACCOUNT_IDENTIFIER_HERE"; params.businessProfileAccessToken = "INSERT_BUSINESS_PROFILE_ACCESS_TOKEN_HERE"; } GoogleAdsClient googleAdsClient = null; try { googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build(); } catch (FileNotFoundException fnfe) { System.err.printf( "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe); System.exit(1); } catch (IOException ioe) { System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe); System.exit(1); } try { new AddBusinessProfileLocationExtensions() .runExample( googleAdsClient, params.customerId, params.businessProfileEmailAddress, params.businessAccountIdentifier, params.businessProfileAccessToken); } catch (GoogleAdsException gae) { // GoogleAdsException is the base class for most exceptions thrown by an API request. // Instances of this exception have a message and a GoogleAdsFailure that contains a // collection of GoogleAdsErrors that indicate the underlying causes of the // GoogleAdsException. System.err.printf( "Request ID %s failed due to GoogleAdsException. Underlying errors:%n", gae.getRequestId()); int i = 0; for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) { System.err.printf(" Error %d: %s%n", i++, googleAdsError); } System.exit(1); } } /** * Runs the example. * * @param googleAdsClient the Google Ads API client. * @param customerId the client customer ID. * @param businessProfileEmailAddress email address associated with the Business Profile account. * @param businessAccountIdentifier the account number of the Business Profile account (optional). * @param businessProfileAccessToken the access token created using the 'AdWords' scope and the * client ID and client secret of with the Cloud project associated with the Business Profile * account. * @throws GoogleAdsException if an API request failed with one or more service errors. * @throws InterruptedException if the Thread.sleep operation is interrupted. */ private void runExample( GoogleAdsClient googleAdsClient, long customerId, String businessProfileEmailAddress, String businessAccountIdentifier, String businessProfileAccessToken) throws InterruptedException { // Creates a PlacesLocationFeedData object to identify the Business Profile account, specify // location filters, and provide authorization for Google Ads to retrieve locations from the // account on behalf of the user identified by businessProfileEmailAddress. PlacesLocationFeedData.Builder placesLocationFeedData = PlacesLocationFeedData.newBuilder() .setEmailAddress(businessProfileEmailAddress) // Used to filter Business Profile listings by labels. If entries exist in // label_filters, only listings that have at least one of the labels set are // candidates to be synchronized into FeedItems. If no entries exist in // label_filters, then all listings are candidates for syncing. .addLabelFilters("Stores in New York") // Sets the authentication info to be able to connect Google Ads to the Business Profile // account. .setOauthInfo( OAuthInfo.newBuilder() .setHttpMethod("GET") .setHttpRequestUrl(GOOGLE_ADS_SCOPE) .setHttpAuthorizationHeader("Bearer " + businessProfileAccessToken) .build()); if (businessAccountIdentifier != null) { placesLocationFeedData.setBusinessAccountId(businessAccountIdentifier); } // Creates a feed that will sync to the Business Profile account. Do not add FeedAttributes to // this object as Google Ads will add them automatically because this will be a system generated // feed. Feed.Builder businessProfileFeed = Feed.newBuilder() .setName("Business Profile feed #" + getPrintableDateTime()) // Configures the location feed populated from Business Profile Locations. .setPlacesLocationFeedData(placesLocationFeedData) // Since this feed's feed items will be managed by Google, // you must set its origin to GOOGLE. .setOrigin(FeedOrigin.GOOGLE); FeedOperation operation = FeedOperation.newBuilder().setCreate(businessProfileFeed).build(); try (FeedServiceClient feedServiceClient = googleAdsClient.getLatestVersion().createFeedServiceClient()) { // Adds the feed. Since it is a system generated feed, Google Ads will automatically: // 1. Set up the FeedAttributes on the feed. // 2. Set up a FeedMapping that associates the FeedAttributes of the feed // with the placeholder fields of the LOCATION placeholder type. MutateFeedsResponse response = feedServiceClient.mutateFeeds(Long.toString(customerId), ImmutableList.of(operation)); String businessProfileFeedResourceName = response.getResults(0).getResourceName(); System.out.printf( "Business Profile feed created with resource name: %s%n", businessProfileFeedResourceName); // Adds a CustomerFeed that associates the feed with this customer for // the LOCATION placeholder type. CustomerFeed customerFeed = CustomerFeed.newBuilder() .setFeed(businessProfileFeedResourceName) .addPlaceholderTypes(PlaceholderType.LOCATION) // Creates a matching function that will always evaluate to true. .setMatchingFunction( MatchingFunction.newBuilder() .addLeftOperands( Operand.newBuilder() .setConstantOperand( ConstantOperand.newBuilder().setBooleanValue(true).build()) .build()) .setFunctionString("IDENTITY(true)") .setOperator(MatchingFunctionOperator.IDENTITY) .build()) .build(); CustomerFeedOperation customerFeedOperation = CustomerFeedOperation.newBuilder().setCreate(customerFeed).build(); try (CustomerFeedServiceClient customerFeedServiceClient = googleAdsClient.getLatestVersion().createCustomerFeedServiceClient()) { // After the completion of the Feed ADD operation above the added feed will not be available // for usage in a CustomerFeed until the sync between the Google Ads and Business Profile // accounts // completes. The loop below will retry adding the CustomerFeed up to ten times with an // exponential back-off policy. String addedCustomerFeed = null; int numberOfAttempts = 0; do { numberOfAttempts++; try { MutateCustomerFeedsResponse customerFeedsResponse = customerFeedServiceClient.mutateCustomerFeeds( Long.toString(customerId), ImmutableList.of(customerFeedOperation)); addedCustomerFeed = customerFeedsResponse.getResults(0).getResourceName(); System.out.printf("Customer feed created with resource name: %s%n", addedCustomerFeed); } catch (GoogleAdsException gae) { // Waits using exponential backoff policy. long sleepSeconds = (long) Math.scalb(5, numberOfAttempts); // Exits the loop early if sleepSeconds grows too large in the event that // MAX_CUSTOMER_FEED_ADD_ATTEMPTS is set too high. if (sleepSeconds > (long) Math.scalb(5, 10)) { break; } System.out.printf( "Attempt #%d to add the CustomerFeed was not successful. " + "Waiting %d seconds before trying again.%n", numberOfAttempts, sleepSeconds); Thread.sleep(sleepSeconds * 1000); } } while (numberOfAttempts < MAX_CUSTOMER_FEED_ADD_ATTEMPTS && addedCustomerFeed == null); if (addedCustomerFeed == null) { throw new RuntimeException( "Could not create the CustomerFeed after " + MAX_CUSTOMER_FEED_ADD_ATTEMPTS + " attempts. Please retry " + "the CustomerFeed ADD operation later."); } // OPTIONAL: Create a CampaignFeed to specify which FeedItems to use at the Campaign // level. // OPTIONAL: Create an AdGroupFeed for even more fine grained control over // which feed items are used at the AdGroup level. } } } }
C#
// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. using CommandLine; using Google.Ads.Gax.Examples; using Google.Ads.GoogleAds.Lib; using Google.Ads.GoogleAds.V13.Common; using Google.Ads.GoogleAds.V13.Errors; using Google.Ads.GoogleAds.V13.Resources; using Google.Ads.GoogleAds.V13.Services; using Google.Api.Gax; using Grpc.Core; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using static Google.Ads.GoogleAds.V13.Common.Operand.Types; using static Google.Ads.GoogleAds.V13.Enums.FeedOriginEnum.Types; using static Google.Ads.GoogleAds.V13.Enums.MatchingFunctionOperatorEnum.Types; using static Google.Ads.GoogleAds.V13.Enums.PlaceholderTypeEnum.Types; using static Google.Ads.GoogleAds.V13.Resources.Feed.Types; using static Google.Ads.GoogleAds.V13.Resources.Feed.Types.PlacesLocationFeedData.Types; namespace Google.Ads.GoogleAds.Examples.V13 { /// <summary> /// This code example adds a feed that syncs feed items from a Business Profile account /// and associates the feed with a customer. /// </summary> public class AddBusinessProfileLocationExtensions : ExampleBase { /// <summary> /// Command line options for running the <see cref="AddBusinessProfileLocationExtensions"/> /// example. /// </summary> public class Options : OptionsBase { /// <summary> /// The customer ID for which the call is made. /// </summary> [Option("customerId", Required = true, HelpText = "The customer ID for which the call is made.")] public long CustomerId { get; set; } /// <summary> /// The Business Profile login email address. /// </summary> [Option("businessProfileEmailAddress", Required = true, HelpText = "The Business Profile login email address.")] public string BusinessProfileEmailAddress { get; set; } /// <summary> /// The Business Profile account identifier. /// </summary> [Option("businessAccountId", Required = false, HelpText = "The Business Profile account identifier.")] public string BusinessAccountId { get; set; } /// <summary> /// The OAuth2 access token for the Business Profile account. /// </summary> [Option("businessProfileAccessToken", Required = false, HelpText = "The OAuth2 access token for the Business Profile account.")] public string BusinessProfileAccessToken { get; set; } } /// <summary> /// Main method, to run this code example as a standalone application. /// </summary> /// <param name="args">The command line arguments.</param> public static void Main(string[] args) { Options options = ExampleUtilities.ParseCommandLine<Options>(args); AddBusinessProfileLocationExtensions codeExample = new AddBusinessProfileLocationExtensions(); Console.WriteLine(codeExample.Description); codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.BusinessProfileEmailAddress, options.BusinessAccountId, options.BusinessProfileAccessToken); } // The required scope for setting the OAuth info. private const string GOOGLE_ADS_SCOPE = "https://www.googleapis.com/auth/adwords"; // The maximum number of attempts to make to retrieve the FeedMappings before throwing an // exception. private const int MAX_FEEDMAPPING_RETRIEVAL_ATTEMPTS = 10; /// <summary> /// Returns a description about the code example. /// </summary> public override string Description => "This code example adds a feed that syncs feed items from a Business Profile " + "account and associates the feed with a customer."; /// <summary> /// Runs the code example. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="businessProfileEmailAddress">The Business Profile login email address. /// </param> /// <param name="businessAccountId">The Business Profile account identifier.</param> /// <param name="businessProfileAccessToken">The OAuth2 access token for the Business /// Profile account.</param> public void Run(GoogleAdsClient client, long customerId, string businessProfileEmailAddress, string businessAccountId, string businessProfileAccessToken) { try { if (string.IsNullOrEmpty(businessProfileAccessToken)) { businessProfileAccessToken = client.Config.OAuth2AccessToken; } string businessProfileFeedResourceName = CreateBusinessProfileFeed(client, customerId, businessProfileEmailAddress, businessAccountId, businessProfileAccessToken); // After the completion of the Feed ADD operation above the added feed will not be // available for usage in a CustomerFeed until the FeedMappings are created. // We will wait with an exponential back-off policy until the feedmappings have // been created. WaitForBusinessProfileFeedToBeReady(client, customerId, businessProfileFeedResourceName); CreateCustomerFeed(client, customerId, businessProfileFeedResourceName); } catch (GoogleAdsException e) { Console.WriteLine("Failure:"); Console.WriteLine($"Message: {e.Message}"); Console.WriteLine($"Failure: {e.Failure}"); Console.WriteLine($"Request ID: {e.RequestId}"); throw; } } /// <summary> /// Creates the Business Profile feed. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="businessProfileEmailAddress">The Business Profile login email address. /// </param> /// <param name="businessAccountId">The Business Profile account ID.</param> /// <param name="businessProfileAccessToken">The OAuth2 access token for the Business /// Profile account.</param> /// <returns>ID of the newly created Business Profile feed.</returns> private static string CreateBusinessProfileFeed(GoogleAdsClient client, long customerId, string businessProfileEmailAddress, string businessAccountId, string businessProfileAccessToken) { // Optional: Delete all existing location extension feeds. This is an optional step, // and is required for this code example to run correctly more than once. // 1. Google Ads only allows one location extension feed per email address. // 2. A Google Ads account cannot have a location extension feed and an affiliate // location extension feed at the same time. DeleteLocationExtensionFeeds(client, customerId); // Get the FeedServiceClient. FeedServiceClient feedService = client.GetService(Services.V13.FeedService); // Creates a feed that will sync to the Business Profile account specified by // businessProfileEmailAddress. Do not add FeedAttributes to this object as Google Ads // will add them automatically because this will be a system generated feed. Feed businessProfileFeed = new Feed() { Name = "Business Profile feed #" + ExampleUtilities.GetRandomString(), PlacesLocationFeedData = new PlacesLocationFeedData() { EmailAddress = businessProfileEmailAddress, // If the EmailAddress is for a Business Profile manager instead of the // Business Profile account owner, then set BusinessAccountId to the Google+ // Page ID of a location for which the manager has access. This information is // available through the Business Profile API. See // https://developers.google.com/my-business/reference/rest/v4/accounts.locations#locationkey // for details. BusinessAccountId = string.IsNullOrEmpty(businessAccountId) ? null : businessAccountId, // Used to filter Business Profile listings by labels. If entries exist in // label_filters, only listings that have at least one of the labels set are // candidates to be synchronized into FeedItems. If no entries exist in // label_filters, then all listings are candidates for syncing. LabelFilters = { "Stores in New York" }, // Sets the authentication info to be able to connect Google Ads to the // Business Profile account. OauthInfo = new OAuthInfo() { HttpMethod = "GET", HttpRequestUrl = GOOGLE_ADS_SCOPE, HttpAuthorizationHeader = $"Bearer {businessProfileAccessToken}" }, }, // Since this feed's feed items will be managed by Google, // you must set its origin to GOOGLE. Origin = FeedOrigin.Google }; FeedOperation operation = new FeedOperation() { Create = businessProfileFeed }; // Adds the feed. MutateFeedsResponse response = feedService.MutateFeeds(customerId.ToString(), new[] { operation }); // Displays the results. string businessProfileFeedResourceName = response.Results[0].ResourceName; Console.WriteLine($"Business Profile feed created with resource name: " + $"{businessProfileFeedResourceName}."); return businessProfileFeedResourceName; } /// <summary> /// Deletes the old location extension feeds. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> private static void DeleteLocationExtensionFeeds(GoogleAdsClient client, long customerId) { // To delete a location extension feed, you need to // 1. Delete the CustomerFeed so that the location extensions from the feed stop // serving. // 2. Delete the feed so that Google Ads will no longer sync from the Business Profile // account. CustomerFeed[] oldCustomerFeeds = GetLocationExtensionCustomerFeeds(client, customerId); if (oldCustomerFeeds.Length != 0) { DeleteCustomerFeeds(client, customerId, oldCustomerFeeds); } Feed[] feeds = GetLocationExtensionFeeds(client, customerId); if (feeds.Length != 0) { RemoveFeeds(client, customerId, feeds); } } /// <summary> /// Gets the location extension feeds. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <returns>The list of location extension feeds.</returns> private static Feed[] GetLocationExtensionFeeds(GoogleAdsClient client, long customerId) { List<Feed> feeds = new List<Feed>(); GoogleAdsServiceClient googleAdsService = client.GetService( Services.V13.GoogleAdsService); // Create the query. string query = $"SELECT feed.resource_name, feed.status, " + $"feed.places_location_feed_data.email_address, " + $"feed.affiliate_location_feed_data.chain_ids " + $" from feed where feed.status = ENABLED"; PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> result = googleAdsService.Search(customerId.ToString(), query); foreach (GoogleAdsRow row in result) { // A location extension feed can be identified by checking whether the // PlacesLocationFeedData field is set (Location extensions feeds) or // AffiliateLocationFeedData field is set (Affiliate location extension feeds) Feed feed = row.Feed; if (feed.PlacesLocationFeedData != null || feed.AffiliateLocationFeedData != null) { feeds.Add(feed); } } return feeds.ToArray(); } /// <summary> /// Removes the feeds. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="feeds">The list of feeds to remove.</param> private static void RemoveFeeds(GoogleAdsClient client, long customerId, Feed[] feeds) { List<FeedOperation> operations = new List<FeedOperation>(); foreach (Feed feed in feeds) { FeedOperation operation = new FeedOperation() { Remove = feed.ResourceName, }; operations.Add(operation); } FeedServiceClient feedService = client.GetService( Services.V13.FeedService); feedService.MutateFeeds(customerId.ToString(), operations.ToArray()); } private static CustomerFeed[] GetLocationExtensionCustomerFeeds(GoogleAdsClient client, long customerId) { List<CustomerFeed> customerFeeds = new List<CustomerFeed>(); GoogleAdsServiceClient googleAdsService = client.GetService( Services.V13.GoogleAdsService); // Create the query. A location extension customer feed can be identified by filtering // for placeholder_types=LOCATION (location extension feeds) or // placeholder_types =AFFILIATE_LOCATION (affiliate location extension feeds) string query = $"SELECT customer_feed.resource_name, customer_feed.feed, " + $"customer_feed.status, customer_feed.matching_function.function_string from " + $"customer_feed " + $"WHERE customer_feed.placeholder_types CONTAINS " + $"ANY(LOCATION, AFFILIATE_LOCATION) and customer_feed.status=ENABLED"; PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> result = googleAdsService.Search(customerId.ToString(), query); foreach (GoogleAdsRow row in result) { customerFeeds.Add(row.CustomerFeed); } return customerFeeds.ToArray(); } /// <summary> /// Deletes the customer feeds. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="customerFeeds">The customer feeds to delete.</param> private static void DeleteCustomerFeeds(GoogleAdsClient client, long customerId, CustomerFeed[] customerFeeds) { List<CustomerFeedOperation> operations = new List<CustomerFeedOperation>(); foreach (CustomerFeed customerFeed in customerFeeds) { CustomerFeedOperation operation = new CustomerFeedOperation() { Remove = customerFeed.ResourceName, }; operations.Add(operation); } CustomerFeedServiceClient feedService = client.GetService( Services.V13.CustomerFeedService); feedService.MutateCustomerFeeds(customerId.ToString(), operations.ToArray()); } /// <summary> /// Gets the Business Profile feed mapping. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="businessProfileFeedResourceName">The Business Profile feed resource name. /// </param> /// <returns>The newly created feed mapping.</returns> private static FeedMapping GetBusinessProfileFeedMapping(GoogleAdsClient client, long customerId, string businessProfileFeedResourceName) { // Get the GoogleAdsService. GoogleAdsServiceClient googleAdsService = client.GetService( Services.V13.GoogleAdsService); // Create the query. string query = $"SELECT feed_mapping.resource_name, feed_mapping.status FROM " + $"feed_mapping WHERE feed_mapping.feed = '{businessProfileFeedResourceName}' and " + $"feed_mapping.status = ENABLED and feed_mapping.placeholder_type = LOCATION" + $" LIMIT 1"; // Issue a search request. PagedEnumerable<SearchGoogleAdsResponse, GoogleAdsRow> result = googleAdsService.Search(customerId.ToString(), query); // Display the results. GoogleAdsRow googleAdsRow = result.FirstOrDefault(); return (googleAdsRow == null) ? null : googleAdsRow.FeedMapping; } /// <summary> /// Waits for the Business Profile feed to be ready. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="businessProfileFeedResourceName">The Business Profile feed resource name. /// </param> private static void WaitForBusinessProfileFeedToBeReady(GoogleAdsClient client, long customerId, string businessProfileFeedResourceName) { int numAttempts = 0; while (numAttempts < MAX_FEEDMAPPING_RETRIEVAL_ATTEMPTS) { // Once you create a feed, Google's servers will setup the feed by creating feed // attributes and feedmapping. Once the feedmapping is created, it is ready to be // used for creating customer feed. // This process is asynchronous, so we wait until the feed mapping is created, // peforming exponential backoff. FeedMapping feedMapping = GetBusinessProfileFeedMapping(client, customerId, businessProfileFeedResourceName); if (feedMapping == null) { numAttempts++; int sleepSeconds = (int)(5 * Math.Pow(2, numAttempts)); Console.WriteLine($"Checked: #{numAttempts} time(s). Business Profile feed " + $"is not ready yet. Waiting {sleepSeconds} seconds before trying again."); Thread.Sleep(sleepSeconds * 1000); } else { Console.WriteLine($"Business Profile Feed {businessProfileFeedResourceName} " + $"is now ready."); return; } } throw new RpcException(new Status(StatusCode.DeadlineExceeded, $"Business Profile Feed is not ready after {MAX_FEEDMAPPING_RETRIEVAL_ATTEMPTS} " + $"retries.")); } /// <summary> /// Creates the customer feed. /// </summary> /// <param name="client">The client.</param> /// <param name="customerId">The customer identifier.</param> /// <param name="businessProfileFeedResourceName">The Business Profile feed resource name. /// </param> private static void CreateCustomerFeed(GoogleAdsClient client, long customerId, string businessProfileFeedResourceName) { // Get the CustomerFeedService. CustomerFeedServiceClient customerFeedService = client.GetService( Services.V13.CustomerFeedService); // Adds a CustomerFeed that associates the feed with this customer for // the LOCATION placeholder type. CustomerFeed customerFeed = new CustomerFeed() { Feed = businessProfileFeedResourceName, PlaceholderTypes = { PlaceholderType.Location }, MatchingFunction = new MatchingFunction() { LeftOperands = { new Operand() { ConstantOperand = new ConstantOperand() { BooleanValue = true } } }, // Specify the function string as IDENTITY(true) to mark this feed as enabled. FunctionString = "IDENTITY(true)", Operator = MatchingFunctionOperator.Identity }, }; CustomerFeedOperation operation = new CustomerFeedOperation() { Create = customerFeed }; MutateCustomerFeedsResponse customerFeedsResponse = customerFeedService.MutateCustomerFeeds( customerId.ToString(), new[] { operation }); // Displays the result. string addedCustomerFeed = customerFeedsResponse.Results[0].ResourceName; Console.WriteLine($"Customer feed created with resource name: {addedCustomerFeed}."); return; } } }
PHP
<?php /** * Copyright 2019 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ namespace Google\Ads\GoogleAds\Examples\Extensions; require __DIR__ . '/../../vendor/autoload.php'; use GetOpt\GetOpt; use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames; use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser; use Google\Ads\GoogleAds\Examples\Utils\Helper; use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder; use Google\Ads\GoogleAds\Lib\V13\GoogleAdsClient; use Google\Ads\GoogleAds\Lib\V13\GoogleAdsClientBuilder; use Google\Ads\GoogleAds\Lib\V13\GoogleAdsException; use Google\Ads\GoogleAds\V13\Common\MatchingFunction; use Google\Ads\GoogleAds\V13\Common\Operand; use Google\Ads\GoogleAds\V13\Common\Operand\ConstantOperand; use Google\Ads\GoogleAds\V13\Enums\FeedOriginEnum\FeedOrigin; use Google\Ads\GoogleAds\V13\Enums\MatchingFunctionOperatorEnum\MatchingFunctionOperator; use Google\Ads\GoogleAds\V13\Enums\PlaceholderTypeEnum\PlaceholderType; use Google\Ads\GoogleAds\V13\Errors\GoogleAdsError; use Google\Ads\GoogleAds\V13\Resources\CustomerFeed; use Google\Ads\GoogleAds\V13\Resources\Feed; use Google\Ads\GoogleAds\V13\Resources\Feed\PlacesLocationFeedData; use Google\Ads\GoogleAds\V13\Resources\Feed\PlacesLocationFeedData\OAuthInfo; use Google\Ads\GoogleAds\V13\Services\CustomerFeedOperation; use Google\Ads\GoogleAds\V13\Services\FeedOperation; use Google\ApiCore\ApiException; /** * This example adds a feed that syncs feed items from a Business Profile account * and associates the feed with a customer. */ class AddBusinessProfileLocationExtensions { private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE'; private const BUSINESS_PROFILE_EMAIL = 'INSERT_BUSINESS_PROFILE_EMAIL_HERE'; private const BUSINESS_PROFILE_ACCESS_TOKEN = 'INSERT_BUSINESS_PROFILE_ACCESS_TOKEN_HERE'; private const BUSINESS_ACCOUNT_IDENTIFIER = 'INSERT_BUSINESS_ACCOUNT_IDENTIFIER_HERE'; // The required scope for setting the OAuth info. private const GOOGLE_ADS_SCOPE = 'https://www.googleapis.com/auth/adwords'; // The maximum number of customer feed ADD operation attempts to make before throwing an // exception. private const MAX_CUSTOMER_FEED_ADD_ATTEMPTS = 10; private const POLL_FREQUENCY_SECONDS = 5; public static function main() { // Either pass the required parameters for this example on the command line, or insert them // into the constants above. $options = (new ArgumentParser())->parseCommandArguments([ ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::BUSINESS_PROFILE_EMAIL => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::BUSINESS_PROFILE_ACCESS_TOKEN => GetOpt::REQUIRED_ARGUMENT, ArgumentNames::BUSINESS_ACCOUNT_IDENTIFIER => GetOpt::REQUIRED_ARGUMENT ]); // Generate a refreshable OAuth2 credential for authentication. $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build(); // Construct a Google Ads client configured from a properties file and the // OAuth2 credentials above. $googleAdsClient = (new GoogleAdsClientBuilder())->fromFile() ->withOAuth2Credential($oAuth2Credential) ->build(); try { self::runExample( $googleAdsClient, $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID, $options[ArgumentNames::BUSINESS_PROFILE_EMAIL] ?: self::BUSINESS_PROFILE_EMAIL, $options[ArgumentNames::BUSINESS_PROFILE_ACCESS_TOKEN] ?: self::BUSINESS_PROFILE_ACCESS_TOKEN, $options[ArgumentNames::BUSINESS_ACCOUNT_IDENTIFIER] ?: self::BUSINESS_ACCOUNT_IDENTIFIER ); } catch (GoogleAdsException $googleAdsException) { printf( "Request with ID '%s' has failed.%sGoogle Ads failure details:%s", $googleAdsException->getRequestId(), PHP_EOL, PHP_EOL ); foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) { /** @var GoogleAdsError $error */ printf( "\t%s: %s%s", $error->getErrorCode()->getErrorCode(), $error->getMessage(), PHP_EOL ); } exit(1); } catch (ApiException $apiException) { printf( "ApiException was thrown with message '%s'.%s", $apiException->getMessage(), PHP_EOL ); exit(1); } } /** * Runs the example. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the client customer ID * @param string $businessProfileEmail the email address associated with the Business * Profile account * @param string $businessProfileAccessToken the access token created using the 'AdWords' scope * and the client ID and client secret of with the Cloud project associated with the * Business Profile account * @param string $businessAccountIdentifier the account number of the Business Profile account */ public static function runExample( GoogleAdsClient $googleAdsClient, int $customerId, string $businessProfileEmail, string $businessProfileAccessToken, string $businessAccountIdentifier ) { $businessProfileFeedResourceName = self::createFeed( $googleAdsClient, $customerId, $businessProfileEmail, $businessProfileAccessToken, $businessAccountIdentifier ); self::createCustomerFeed($googleAdsClient, $customerId, $businessProfileFeedResourceName); // OPTIONAL: Create a campaign feed to specify which feed items to use at the campaign // level. // OPTIONAL: Create an ad group feed for even more fine grained control over which feed // items are used at the ad group level. } /** * Creates a location feed that will sync to the Business Profile account specified by * `$businessProfileEmailAddress`. Do not add feed attributes to this object as Google Ads will * add them automatically because this will be a system generated feed. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the client customer ID * @param string $businessProfileEmail the email address associated with the Business * Profile account * @param string $businessProfileAccessToken the access token created using the 'AdWords' scope * and the client ID and client secret of with the Cloud project associated with the * Business Profile account * @param string $businessAccountIdentifier the account number of the Business Profile account * @return string the feed's resource name */ private static function createFeed( GoogleAdsClient $googleAdsClient, int $customerId, string $businessProfileEmail, string $businessProfileAccessToken, string $businessAccountIdentifier ) { $businessProfileFeed = new Feed([ 'name' => 'Business Profile feed #' . Helper::getPrintableDatetime(), 'origin' => FeedOrigin::GOOGLE, 'places_location_feed_data' => new PlacesLocationFeedData([ 'email_address' => $businessProfileEmail, 'business_account_id' => $businessAccountIdentifier, // Used to filter Business Profile listings by labels. If entries exist in // label_filters, only listings that have at least one of the labels set are // candidates to be synchronized into FeedItems. If no entries exist in // label_filters, then all listings are candidates for syncing. 'label_filters' => ['Stores in New York'], // Sets the authentication info to be able to connect Google Ads to the Business // Profile account. 'oauth_info' => new OAuthInfo([ 'http_method' => 'GET', 'http_request_url' => self::GOOGLE_ADS_SCOPE, 'http_authorization_header' => 'Bearer ' . $businessProfileAccessToken ]) ]) ]); // Creates a feed operation. $feedOperation = new FeedOperation(); $feedOperation->setCreate($businessProfileFeed); // Issues a mutate request to add the feed and print its information. // Since it is a system generated feed, Google Ads will automatically: // 1. Set up the feed attributes on the feed. // 2. Set up a feed mapping that associates the feed attributes of the feed with the // placeholder fields of the LOCATION placeholder type. $feedServiceClient = $googleAdsClient->getFeedServiceClient(); $response = $feedServiceClient->mutateFeeds( $customerId, [$feedOperation] ); $businessProfileFeedResourceName = $response->getResults()[0]->getResourceName(); printf( "Business Profile feed created with resource name: '%s'.%s", $businessProfileFeedResourceName, PHP_EOL ); return $businessProfileFeedResourceName; } /** * Creates a customer feed to attach the previously created Business Profile feed to the * specified customer ID. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the client customer ID * @param string $businessProfileFeedResourceName the feed's resource name to be used to create * a customer feed */ private static function createCustomerFeed( GoogleAdsClient $googleAdsClient, int $customerId, string $businessProfileFeedResourceName ) { // Creates a customer feed that associates the feed with this customer for the LOCATION // placeholder type. $customerFeed = new CustomerFeed([ 'feed' => $businessProfileFeedResourceName, 'placeholder_types' => [PlaceholderType::LOCATION], // Creates a matching function that will always evaluate to true. 'matching_function' => new MatchingFunction([ 'left_operands' => [new Operand([ 'constant_operand' => new ConstantOperand(['boolean_value' => true]) ])], 'function_string' => 'IDENTITY(true)', 'operator' => MatchingFunctionOperator::IDENTITY ]) ]); // Creates a customer feed operation. $customerFeedOperation = new CustomerFeedOperation(); $customerFeedOperation->setCreate($customerFeed); // After the completion of the feed ADD operation above the added feed will not be available // for usage in a customer feed until the sync between the Google Ads and Business Profile // accounts completes. The loop below will retry adding the customer feed up to ten times // with an exponential back-off policy. $numberOfAttempts = 0; $addedCustomerFeed = null; $customerFeedServiceClient = $googleAdsClient->getCustomerFeedServiceClient(); do { $numberOfAttempts++; try { // Issues a mutate request to add a customer feed and print its information if the // request succeeded. $addedCustomerFeed = $customerFeedServiceClient->mutateCustomerFeeds( $customerId, [$customerFeedOperation] ); printf( "Customer feed created with resource name: '%s'.%s", $addedCustomerFeed->getResults()[0]->getResourceName(), PHP_EOL ); } catch (GoogleAdsException $googleAdsException) { // Waits using exponential backoff policy. $sleepSeconds = self::POLL_FREQUENCY_SECONDS * pow(2, $numberOfAttempts); // Exits the loop early if $sleepSeconds grows too large in the event that // MAX_CUSTOMER_FEED_ADD_ATTEMPTS is set too high. if ( $sleepSeconds > self::POLL_FREQUENCY_SECONDS * pow(2, self::MAX_CUSTOMER_FEED_ADD_ATTEMPTS) ) { break; } printf( "Attempt #%d to add the customer feed was not successful." . " Waiting %d seconds before trying again.%s", $numberOfAttempts, $sleepSeconds, PHP_EOL ); sleep($sleepSeconds); } } while ( $numberOfAttempts < self::MAX_CUSTOMER_FEED_ADD_ATTEMPTS && is_null($addedCustomerFeed) ); if (is_null($addedCustomerFeed)) { throw new \RuntimeException( 'Could not create the customer feed after ' . self::MAX_CUSTOMER_FEED_ADD_ATTEMPTS . ' attempts. Please retry the customer feed ADD operation later.' ); } } } AddBusinessProfileLocationExtensions::main();
Python
#!/usr/bin/env python # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Adds a feed that syncs feed items from a Business Profile account. The feed will also be associated with a customer. """ import argparse import sys import time from uuid import uuid4 from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException MAX_CUSTOMER_FEED_ADD_ATTEMPTS = 9 DEFAULT_OAUTH2_SCOPE = "https://www.googleapis.com/auth/adwords" def main( client, customer_id, business_profile_email, business_account_id, business_profile_access_token, ): """Adds a feed that syncs feed items from a Business Profile account. The feed will also be associated with a customer. Args: client: An initialized GoogleAdsClient instance. customer_id: The Google Ads customer ID. business_profile_email: The email address associated with the Business Profile account. business_account_id: The account ID of the managed business. business_profile_access_token: The access token created using the 'AdWords' scope and the client ID and client secret of with the Cloud project associated with the Business Profile account. """ # Get the FeedService and CustomerFeedService clients. feed_service = client.get_service("FeedService") customer_feed_service = client.get_service("CustomerFeedService") # Create a feed operation and configure the new feed. # The feed will sync to the Business Profile account specified by # business_profile_email. Do not add FeedAttributes to this object as Google Ads # will add them automatically because this will be a system generated feed. # See here for more details: # https://developers.google.com/google-ads/api/docs/location-extensions/google-ads-location-extensions feed_operation = client.get_type("FeedOperation") business_profile_feed = feed_operation.create business_profile_feed.name = f"Business Profile Feed #{uuid4()}" # Configure the location feed populated from Business Profile Locations. business_profile_feed.places_location_feed_data.email_address = ( business_profile_email ) if business_account_id is not None: business_profile_feed.places_location_feed_data.business_account_id = ( business_account_id ) # Used to filter Business Profile listings by labels. If entries exist in # label_filters, only listings that have at least one of the labels set are # candidates to be synchronized into FeedItems. If no entries exist in # label_filters, then all listings are candidates for syncing. business_profile_feed.places_location_feed_data.label_filters.append( "Stores in New York" ) # Set the authentication info to be able to connect Google Ads to the # Business Profile account. business_profile_feed.places_location_feed_data.oauth_info.http_method = ( "GET" ) business_profile_feed.places_location_feed_data.oauth_info.http_request_url = ( DEFAULT_OAUTH2_SCOPE ) business_profile_feed.places_location_feed_data.oauth_info.http_authorization_header = ( f"Bearer {business_profile_access_token}" ) # Since this feed's feed items will be managed by Google, you must set its # origin to GOOGLE. business_profile_feed.origin = client.enums.FeedOriginEnum.GOOGLE # Optional: Delete all existing location extension feeds. This is an # optional step, and is required for this code example to run correctly # more than once; Google Ads only allows one location extension feed # per email address, and a Google Ads account cannot have a location # extension feed and an affiliate location extension feed at the same # time. delete_location_extension_feeds(client, customer_id) # Add the feed. Since it is a system generated feed, Google Ads will # automatically: # 1. Set up the FeedAttributes on the feed. # 2. Set up a FeedMapping that associates the FeedAttributes of the feed # with the placeholder fields of the LOCATION placeholder type. feed_response = feed_service.mutate_feeds( customer_id=customer_id, operations=[feed_operation] ) feed_resource_name = feed_response.results[0].resource_name print( "Business Profile feed created with resource name " f"'{feed_resource_name}'." ) # After the completion of the Feed ADD operation above the added feed # will not be available for usage in a CustomerFeed until the sync # between the Google Ads and Business Profile accounts completes. # This process is asynchronous, so we wait until the feed mapping is # created, performing exponential backoff. customer_feed_resource_name = None number_of_attempts = 0 while number_of_attempts < MAX_CUSTOMER_FEED_ADD_ATTEMPTS: feed_mapping = get_business_profile_feed_mapping( client, customer_id, feed_resource_name ) if feed_mapping is None: number_of_attempts += 1 sleep_seconds = 5 * (2 ** number_of_attempts) print( f"Attempt #{number_of_attempts} was not successful. " f"Waiting {sleep_seconds}s before trying again." ) time.sleep(sleep_seconds) else: customer_feed_resource_name = feed_mapping.resource_name print(f"Business Profile feed {feed_resource_name} is now ready.") break if customer_feed_resource_name is None: print( "Could not create the CustomerFeed after " f"{MAX_CUSTOMER_FEED_ADD_ATTEMPTS} attempts. Please retry " "the CustomerFeed ADD operation later." ) sys.exit(1) else: # Create a CustomerFeed operation and configure the CustomerFeed to # associate the feed with this customer for the LOCATION placeholder # type. # OPTIONAL: Create a CampaignFeed to specify which FeedItems to use at # the Campaign level. # OPTIONAL: Create an AdGroupFeed for even more fine grained control # over which feed items are used at the AdGroup level. customer_feed_operation = client.get_type("CustomerFeedOperation") customer_feed = customer_feed_operation.create customer_feed.feed = feed_resource_name customer_feed.placeholder_types.append( client.enums.PlaceholderTypeEnum.LOCATION ) # The function string "IDENTITY(true)" will enable this feed. true_operand = client.get_type("Operand") true_operand.constant_operand.boolean_value = True customer_feed.matching_function.left_operands.append(true_operand) customer_feed.matching_function.function_string = "IDENTITY(true)" customer_feed.matching_function.operator = ( client.enums.MatchingFunctionOperatorEnum.IDENTITY ) customer_feed_response = customer_feed_service.mutate_customer_feeds( customer_id=customer_id, operations=[customer_feed_operation] ) print( "Customer feed created with resource name " f"'{customer_feed_response.results[0].resource_name}'." ) def delete_location_extension_feeds(client, customer_id): """Deletes the existing location extension feeds. Args: client: An initialized Google Ads API client. customer_id: The Google Ads customer ID. """ # To delete a location extension feed, you need to: # 1. Delete the CustomerFeed so that the location extensions from the feed # stop serving. # 2. Delete the feed so that Google Ads will no longer sync from the # Business Profile account. old_customer_feeds = get_location_extension_customer_feeds( client, customer_id ) if old_customer_feeds: delete_customer_feeds(client, customer_id, old_customer_feeds) old_feeds = get_location_extension_feeds(client, customer_id) if old_feeds: delete_feeds(client, customer_id, old_feeds) def get_location_extension_customer_feeds(client, customer_id): """Gets the existing location extension customer feeds. Args: client: An initialized Google Ads API client. customer_id: The Google Ads customer ID. Returns: A list of location extension feeds. """ googleads_service = client.get_service("GoogleAdsService") # Create the query. A location extension customer feed can be identified by # filtering for placeholder_types=LOCATION (location extension feeds) or # placeholder_types=AFFILIATE_LOCATION (affiliate location extension feeds). query = """ SELECT customer_feed.resource_name, customer_feed.feed, customer_feed.status, customer_feed.matching_function.function_string FROM customer_feed WHERE customer_feed.placeholder_types CONTAINS ANY(LOCATION, AFFILIATE_LOCATION) AND customer_feed.status = ENABLED""" result = googleads_service.search(customer_id=customer_id, query=query) return [row.customer_feed for row in result] def get_location_extension_feeds(client, customer_id): """Gets the existing location extension feeds. Args: client: An initialized Google Ads API client. customer_id: The Google Ads customer ID. Returns: A list of location extension feeds. """ googleads_service = client.get_service("GoogleAdsService") # Create the query. query = """ SELECT feed.resource_name, feed.status, feed.places_location_feed_data.email_address, feed.affiliate_location_feed_data.chain_ids FROM feed WHERE feed.status = ENABLED""" result = googleads_service.search(customer_id=customer_id, query=query) # A location extension feed can be identified by checking whether the # places_location_feed_data field is set or the # affiliate_location_feed_data field is set. return [ row.feed for row in result if row.feed.places_location_feed_data or row.feed.affiliate_location_feed_data ] def delete_customer_feeds(client, customer_id, old_customer_feeds): """Removes the customer feeds. Args: client: An initialized Google Ads API client. customer_id: The Google Ads customer ID. old_customer_feeds: The list of customer feeds to delete. """ operations = [] customer_feed_service = client.get_service("CustomerFeedService") for customer_feed in old_customer_feeds: operation = client.get_type("CustomerFeedOperation") operation.remove = customer_feed.resource_name operations.append(operation) customer_feed_service.mutate_customer_feeds( customer_id=customer_id, operations=operations ) def delete_feeds(client, customer_id, old_feeds): """Removes the specified feeds. Args: client: An initialized Google Ads API client. customer_id: The Google Ads customer ID. old_feeds: The list of feeds to delete. """ operations = [] feed_service = client.get_service("FeedService") for feed in old_feeds: operation = client.get_type("FeedOperation") operation.remove = feed.resource_name operations.append(operation) feed_service.mutate_feeds(customer_id=customer_id, operations=operations) def get_business_profile_feed_mapping(client, customer_id, feed_resource_name): """Gets a Business Profile Feed mapping. Args: client: An initialized Google Ads client. customer_id: The customer ID for which the call is made. feed_resource_name: The string Business Profile feed resource name. Returns: The requested FeedMapping, or None if it is not available. """ googleads_service = client.get_service("GoogleAdsService") query = f""" SELECT feed_mapping.resource_name, feed_mapping.status FROM feed_mapping WHERE feed_mapping.feed = '{feed_resource_name}' AND feed_mapping.status = ENABLED AND feed_mapping.placeholder_type = LOCATION LIMIT 1""" result = googleads_service.search(customer_id=customer_id, query=query) try: return next(iter(result)).feed_mapping except StopIteration: return None if __name__ == "__main__": # GoogleAdsClient will read the google-ads.yaml configuration file in the # home directory if none is specified. googleads_client = GoogleAdsClient.load_from_storage(version="v13") parser = argparse.ArgumentParser( description="Adds a feed that syncs feed items from a Business Profile " "account." ) # The following argument(s) should be provided to run the example. parser.add_argument( "-c", "--customer_id", type=str, required=True, help="The Google Ads customer ID.", ) parser.add_argument( "-e", "--business_profile_email", type=str, required=True, help="The email address associated with the Business Profile account.", ) parser.add_argument( "-b", "--business_account_id", type=str, required=False, help="The account ID of the managed business.\n" "If the email_address is for a Business Profile manager instead of the " "Business Profile account owner, then set business_account_id to the" "Google+ Page ID of a location for which the manager has access. This " "information is available through the Business Profile API. See " "https://developers.google.com/my-business/reference/rest/v4/accounts.locations#locationkey" "for details.", ) parser.add_argument( "-t", "--business_profile_access_token", type=str, required=False, default=googleads_client.credentials.token, help="If the business_profile_email above is the same user you used to " "generate your Google Ads API refresh token, do not pass a value to " "this argument.\nOtherwise, to obtain an access token for your " "Business Profile account, run the " "authenticate_in_standalone_application code example while logged in " "as the same user as business_profile_email. Pass the " "Access Token value to this argument.", ) args = parser.parse_args() try: main( googleads_client, args.customer_id, args.business_profile_email, args.business_account_id, args.business_profile_access_token, ) except GoogleAdsException as ex: print( f"Request with ID '{ex.request_id}' failed with status " f"'{ex.error.code().name}' and includes the following errors:" ) for error in ex.failure.errors: print(f"\tError with message '{error.message}'.") if error.location: for field_path_element in error.location.field_path_elements: print(f"\t\tOn field: {field_path_element.field_name}") sys.exit(1)
Ruby
#!/usr/bin/env ruby # Encoding: utf-8 # # Copyright 2020 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This example adds a feed that syncs feed items from a Business Profile # account and associates the feed with a customer. require 'optparse' require 'google/ads/google_ads' require 'date' def add_business_profile_location_extensions( customer_id, business_profile_email_address, business_profile_access_token, business_account_identifier) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new business_profile_feed_resource_name = create_feed( client, customer_id, business_profile_email_address, business_profile_access_token, business_account_identifier, ) create_customer_feed(client, customer_id, business_profile_feed_resource_name) end def create_feed( client, customer_id, business_profile_email_address, business_profile_access_token, business_account_identifier) # Creates a feed operation. operation = client.operation.create_resource.feed do |feed| feed.name = "Business Profile feed #{(Time.new.to_f * 1000).to_i}" feed.origin = :GOOGLE feed.places_location_feed_data = client.resource.places_location_feed_data do |data| data.email_address = business_profile_email_address data.business_account_id = business_account_identifier data.label_filters << "Stores in New York" data.oauth_info = client.resource.o_auth_info do |oauth| oauth.http_method = "GET" oauth.http_request_url = "https://www.googleapis.com/auth/adwords" oauth.http_authorization_header = "Bearer #{business_profile_access_token}" end end end # Issues a mutate request to add the feed and print its information. # Since it is a system generated feed, Google Ads will automatically: # 1. Set up the feed attributes on the feed. # 2. Set up a feed mapping that associates the feed attributes of the feed with the # placeholder fields of the LOCATION placeholder type. response = client.service.feed.mutate_feeds( customer_id: customer_id, operations: [operation], ) # Prints out the Business Profile feed resource name. business_profile_feed_resource_name = response.results.first.resource_name puts "Business Profile feed created with resource name: #{business_profile_feed_resource_name}" business_profile_feed_resource_name end def create_customer_feed( client, customer_id, business_profile_feed_resource_name) # Creates a customer feed operation. operation = client.operation.create_resource.customer_feed do |cf| cf.feed = business_profile_feed_resource_name cf.placeholder_types << :LOCATION cf.matching_function = client.resource.matching_function do |m| m.left_operands << client.resource.operand do |op| op.constant_operand = client.resource.constant_operand do |co| co.boolean_value = true end end m.function_string = "IDENTITY(true)" m.operator = :IDENTITY end end # After the completion of the feed ADD operation above the added feed will # not be available for usage in a customer feed until the sync between the # Google Ads and Business Profile accounts completes. The loop below will # retry adding the customer feed up to ten times with an exponential back-off # policy. number_of_attempts = 0 added_customer_feed = nil customer_feed_service_client = client.service.customer_feed loop do number_of_attempts += 1 begin # Issues a mutate request to add a customer feed and print its information # if the request succeeded. response = customer_feed_service_client.mutate_customer_feeds( customer_id: customer_id, operations: [operation] ) puts "Customer feed created with resource name: " \ "#{response.results.first.resource_name}" rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e # Waits using exponential backoff policy sleep_seconds = POLL_FREQUENCY_SECONDS * (2 ** number_of_attempts) puts "Attempt #{number_of_attempts} to add the customer feed was " \ "not successful. Waiting #{sleep_seconds} seconds before trying again." sleep sleep_seconds end break if number_of_attempts >= MAX_CUSTOMER_FEED_ADD_ATTEMPTS || added_customer_feed end if added_customer_feed.nil? raise "Could not create the customer feed after #{MAX_CUSTOMER_FEED_ADD_ATTEMPTS} " \ "attempts. Please retry the customer feed ADD operation later." end end if __FILE__ == $0 # The maximum number of customer feed ADD operation attempts to make before # throwing an exception. MAX_CUSTOMER_FEED_ADD_ATTEMPTS = 10 POLL_FREQUENCY_SECONDS = 5 options = {} # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE' options[:campaign_id] = 'INSERT_CAMPAIGN_ID_HERE' OptionParser.new do |opts| opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__)) opts.separator '' opts.separator 'Options:' opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v| options[:customer_id] = v end opts.on('-E', '--business-profile-email-address BUSINESS-PROFILE-EMAIL-ADDRESS', String, 'Business Profile Email Address') do |v| options[:business_profile_email_address] = v end opts.on('-T', '--business-profile-access-token BUSINESS-PROFILE-ACCESS-TOKEN', String, 'Business Profile Access Token') do |v| options[:business_profile_access_token] = v end opts.on('-B', '--business-account-identifier BUSINESS-ACCOUNT-IDENTIFIER', String, 'Business Account Identifier') do |v| options[:business_account_identifier] = v end opts.separator '' opts.separator 'Help:' opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end end.parse! begin add_business_profile_location_extensions( options.fetch(:customer_id).tr("-", ""), options.fetch(:business_profile_email_address), options.fetch(:business_profile_access_token), options.fetch(:business_account_identifier), ) rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e e.failure.errors.each do |error| STDERR.printf("Error with message: %s\n", error.message) if error.location error.location.field_path_elements.each do |field_path_element| STDERR.printf("\tOn field: %s\n", field_path_element.field_name) end end error.error_code.to_h.each do |k, v| next if v == :UNSPECIFIED STDERR.printf("\tType: %s\n\tCode: %s\n", k, v) end end raise end end
Perl
#!/usr/bin/perl -w # # Copyright 2019, Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # This example adds a feed that syncs feed items from a Business Profile account # and associates the feed with a customer. use strict; use warnings; use utf8; use FindBin qw($Bin); use lib "$Bin/../../lib"; use Google::Ads::GoogleAds::Constants; use Google::Ads::GoogleAds::Client; use Google::Ads::GoogleAds::Utils::GoogleAdsHelper; use Google::Ads::GoogleAds::V13::Resources::Feed; use Google::Ads::GoogleAds::V13::Resources::PlacesLocationFeedData; use Google::Ads::GoogleAds::V13::Resources::OAuthInfo; use Google::Ads::GoogleAds::V13::Resources::CustomerFeed; use Google::Ads::GoogleAds::V13::Common::MatchingFunction; use Google::Ads::GoogleAds::V13::Common::Operand; use Google::Ads::GoogleAds::V13::Common::ConstantOperand; use Google::Ads::GoogleAds::V13::Enums::FeedOriginEnum qw(GOOGLE); use Google::Ads::GoogleAds::V13::Enums::PlaceholderTypeEnum qw(LOCATION); use Google::Ads::GoogleAds::V13::Enums::MatchingFunctionOperatorEnum qw(IDENTITY); use Google::Ads::GoogleAds::V13::Services::FeedService::FeedOperation; use Google::Ads::GoogleAds::V13::Services::CustomerFeedService::CustomerFeedOperation; use Getopt::Long qw(:config auto_help); use Pod::Usage; use Cwd qw(abs_path); use Data::Uniqid qw(uniqid); use Time::HiRes qw(sleep); use constant MAX_CUSTOMER_FEED_ADD_ATTEMPTS => 10; # The following parameter(s) should be provided to run the example. You can # either specify these by changing the INSERT_XXX_ID_HERE values below, or on # the command line. # # Parameters passed on the command line will override any parameters set in # code. # # Running the example with -h will print the command line usage. my $customer_id = "INSERT_CUSTOMER_ID_HERE"; my $business_profile_email = "INSERT_BUSINESS_PROFILE_EMAIL_HERE"; my $business_profile_account_id = "INSERT_BUSINESS_PROFILE_ACCOUNT_ID_HERE"; my $business_profile_access_token = "INSERT_BUSINESS_PROFILE_ACCESS_TOKEN_HERE"; sub add_business_profile_location_extensions { my ($api_client, $customer_id, $business_profile_email, $business_profile_account_id, $business_profile_access_token) = @_; # Create a feed that will sync to the Business Profile account specified by # $business_profile_email. Do not add FeedAttributes to this object as Google Ads # will add them automatically because this will be a system generated feed. my $business_profile_feed = Google::Ads::GoogleAds::V13::Resources::Feed->new( { name => "Business Profile feed #" . uniqid(), # Configure the location feed populated from Business Profile Locations. placesLocationFeedData => Google::Ads::GoogleAds::V13::Resources::PlacesLocationFeedData->new({ emailAddress => $business_profile_email, businessAccountId => $business_profile_account_id, # Used to filter Business Profile listings by labels. If entries exist in # label_filters, only listings that have at least one of the labels set are # candidates to be synchronized into FeedItems. If no entries exist in # label_filters, then all listings are candidates for syncing. labelFilters => ["Stores in New York"], # Set the authentication info to be able to connect Google Ads to the # Business Profile account. oauthInfo => Google::Ads::GoogleAds::V13::Resources::OAuthInfo->new({ httpMethod => "GET", httpRequestUrl => Google::Ads::GoogleAds::Constants::DEFAULT_OAUTH2_SCOPE, httpAuthorizationHeader => "Bearer " . $business_profile_access_token })} ), # Since this feed's feed items will be managed by Google, you must set its # origin to GOOGLE. origin => GOOGLE }); # Create a feed operation. my $feed_operation = Google::Ads::GoogleAds::V13::Services::FeedService::FeedOperation->new( {create => $business_profile_feed}); # Add the feed. Since it is a system generated feed, Google Ads will automatically: # 1. Set up the FeedAttributes on the feed. # 2. Set up a FeedMapping that associates the FeedAttributes of the feed with the # placeholder fields of the LOCATION placeholder type. my $feeds_response = $api_client->FeedService()->mutate({ customerId => $customer_id, operations => [$feed_operation]}); my $feed_resource_name = $feeds_response->{results}[0]{resourceName}; printf "Business Profile feed created with resource name: '%s'.\n", $feed_resource_name; # Add a CustomerFeed that associates the feed with this customer for the LOCATION # placeholder type. my $customer_feed = Google::Ads::GoogleAds::V13::Resources::CustomerFeed->new( { feed => $feed_resource_name, placeholderTypes => LOCATION, # Create a matching function that will always evaluate to true. matchingFunction => Google::Ads::GoogleAds::V13::Common::MatchingFunction->new({ leftOperands => [ Google::Ads::GoogleAds::V13::Common::Operand->new({ constantOperand => Google::Ads::GoogleAds::V13::Common::ConstantOperand->new({ booleanValue => "true" })}) ], functionString => "IDENTITY(true)", operator => IDENTITY })}); # Create a customer feed operation. my $customer_feed_operation = Google::Ads::GoogleAds::V13::Services::CustomerFeedService::CustomerFeedOperation ->new({create => $customer_feed}); # After the completion of the Feed ADD operation above the added feed will not be available # for usage in a CustomerFeed until the sync between the Google Ads and Business Profile # accounts completes. The loop below will retry adding the CustomerFeed up to ten times with an # exponential back-off policy. my $customer_feed_service = $api_client->CustomerFeedService(); my $customer_feed_resource_name = undef; my $number_of_attempts = 0; while ($number_of_attempts < MAX_CUSTOMER_FEED_ADD_ATTEMPTS) { $number_of_attempts++; my $customer_feeds_response = eval { $customer_feed_service->mutate({ customerId => $customer_id, operations => [$customer_feed_operation], }); }; if ($@) { # Wait using exponential backoff policy. my $sleep_seconds = 5 * (2**$number_of_attempts); # Exit the loop early if $sleep_seconds grows too large in the event that # MAX_CUSTOMER_FEED_ADD_ATTEMPTS is set too high. if ($sleep_seconds > 5 * (2**10)) { last; } printf "Attempt #%d to add the CustomerFeed was not successful. " . "Waiting %d seconds before trying again.\n", $number_of_attempts, $sleep_seconds; sleep($sleep_seconds); } else { $customer_feed_resource_name = $customer_feeds_response->{results}[0]{resourceName}; printf "Customer feed created with resource name: '%s'.\n", $customer_feed_resource_name; last; } } printf "Could not create the CustomerFeed after %d attempts. " . "Please retry the CustomerFeed ADD operation later.", MAX_CUSTOMER_FEED_ADD_ATTEMPTS if not $customer_feed_resource_name; # OPTIONAL: Create a CampaignFeed to specify which FeedItems to use at the Campaign level. # OPTIONAL: Create an AdGroupFeed for even more fine grained control over which feed items # are used at the AdGroup level. return 1; } # Don't run the example if the file is being included. if (abs_path($0) ne abs_path(__FILE__)) { return 1; } # Get Google Ads Client, credentials will be read from ~/googleads.properties. my $api_client = Google::Ads::GoogleAds::Client->new(); # By default examples are set to die on any server returned fault. $api_client->set_die_on_faults(1); # Parameters passed on the command line will override any parameters set in code. GetOptions( "customer_id=s" => \$customer_id, "business_profile_email=s" => \$business_profile_email, "business_profile_account_id=s" => \$business_profile_account_id, "business_profile_access_token=s" => \$business_profile_access_token, ); # Print the help message if the parameters are not initialized in the code nor # in the command line. pod2usage(2) if not check_params($customer_id, $business_profile_email, $business_profile_account_id, $business_profile_access_token); # Call the example. add_business_profile_location_extensions($api_client, $customer_id =~ s/-//gr, $business_profile_email, $business_profile_account_id, $business_profile_access_token); =pod =head1 NAME add_business_profile_location_extensions =head1 DESCRIPTION This example adds a feed that syncs feed items from a Business Profile account and associates the feed with a customer. =head1 SYNOPSIS add_business_profile_location_extensions.pl [options] -help Show the help message. -customer_id The Google Ads customer ID. -business_profile_email The email address associated with the Business Profile ` account. -business_profile_account_id The account ID of the managed business. -business_profile_access_token The access token created using the 'AdWords' scope and the client ID and client secret of with the Cloud project associated with the Business Profile account. =cut