Java
// Copyright 2021 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. package com.google.ads.googleads.examples.extensions; 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.v14.common.PromotionAsset; import com.google.ads.googleads.v14.common.PromotionFeedItem; import com.google.ads.googleads.v14.enums.AssetFieldTypeEnum.AssetFieldType; import com.google.ads.googleads.v14.errors.GoogleAdsError; import com.google.ads.googleads.v14.errors.GoogleAdsException; import com.google.ads.googleads.v14.resources.AdGroup; import com.google.ads.googleads.v14.resources.AdGroupAsset; import com.google.ads.googleads.v14.resources.AdGroupExtensionSetting; import com.google.ads.googleads.v14.resources.Asset; import com.google.ads.googleads.v14.resources.Campaign; import com.google.ads.googleads.v14.resources.CampaignAsset; import com.google.ads.googleads.v14.resources.CampaignExtensionSetting; import com.google.ads.googleads.v14.resources.ExtensionFeedItem; import com.google.ads.googleads.v14.resources.FeedItem; import com.google.ads.googleads.v14.services.AdGroupAssetOperation; import com.google.ads.googleads.v14.services.AdGroupAssetServiceClient; import com.google.ads.googleads.v14.services.AssetOperation; import com.google.ads.googleads.v14.services.AssetServiceClient; import com.google.ads.googleads.v14.services.CampaignAssetOperation; import com.google.ads.googleads.v14.services.CampaignAssetServiceClient; import com.google.ads.googleads.v14.services.GoogleAdsRow; import com.google.ads.googleads.v14.services.GoogleAdsServiceClient; import com.google.ads.googleads.v14.services.MutateAdGroupAssetResult; import com.google.ads.googleads.v14.services.MutateAdGroupAssetsResponse; import com.google.ads.googleads.v14.services.MutateAssetsResponse; import com.google.ads.googleads.v14.services.MutateCampaignAssetResult; import com.google.ads.googleads.v14.services.MutateCampaignAssetsResponse; import com.google.ads.googleads.v14.services.SearchGoogleAdsStreamRequest; import com.google.ads.googleads.v14.services.SearchGoogleAdsStreamResponse; import com.google.ads.googleads.v14.utils.ResourceNames; import com.google.api.gax.rpc.ServerStream; import com.google.common.collect.ImmutableList; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.StreamSupport; /** * Retrieves the full details of a Promotion Feed-based extension and creates a matching Promotion * asset-based extension. The new Asset-based extension will then be associated with the same * campaigns and ad groups as the original Feed-based extension. Finally, the Feed-based extension * will be mutated to no longer serve. * Once copied, you should remove the Feed-based extension; see * RemoveEntireSitelinkCampaignExtensionSetting.java for an example. */ public class MigratePromotionFeedToAsset { private static class MigratePromotionFeedToAssetParams extends CodeSampleParams { @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) private Long customerId; @Parameter(names = ArgumentNames.FEED_ITEM_ID, required = true) private Long extensionFeedItemId; } public static void main(String[] args) { MigratePromotionFeedToAssetParams params = new MigratePromotionFeedToAssetParams(); 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.extensionFeedItemId = Long.parseLong("INSERT_EXTENSION_FEED_ITEM_ID_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 MigratePromotionFeedToAsset() .runExample(googleAdsClient, params.customerId, params.extensionFeedItemId); } 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. */ private void runExample( GoogleAdsClient googleAdsClient, Long customerId, Long extensionFeedItemId) { try (GoogleAdsServiceClient client = googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { String extensionFeedItemResourceName = ResourceNames.extensionFeedItem(customerId, extensionFeedItemId); ExtensionFeedItem extensionFeedItem = getExtensionFeedItem(client, customerId, extensionFeedItemId); // Gets all campaign IDs associated with the extension feed item. List<Long> campaignIds = getTargetedCampaignIds(client, customerId, extensionFeedItemResourceName); // Gets all ad group IDs associated with the extension feed item. List<Long> adGroupIds = getTargetedAdGroupIds(client, customerId, extensionFeedItemResourceName); // Creates a new Promotion asset that matches the target extension feed item. String promotionAssetResourceName = createPromotionAssetFromFeed(googleAdsClient, customerId, extensionFeedItem); // Associates the new Promotion asset with the same campaigns as the original. associateAssetWithCampaigns( googleAdsClient, customerId, promotionAssetResourceName, campaignIds); // Associates the new Promotion asset with the same ad groups as the original. associateAssetWithAdGroups( googleAdsClient, customerId, promotionAssetResourceName, adGroupIds); } } /** * Gets the requested Promotion-type extension feed item. * * <p>Note that extensions can also be created via Feed, FeedItem, etc. If you have this type of * extensions you will need to alter the queries and return types to match your feed type. To do * so you will need to read the Feed Item via the Feed Mapping and create an asset from the * contents. */ private ExtensionFeedItem getExtensionFeedItem( GoogleAdsServiceClient client, Long customerId, Long extensionFeedItemId) { // Creates a query that will retrieve the requested Promotion-type extension feed item and // ensures that all fields are populated. String extensionFeedItemQuery = "SELECT" + " extension_feed_item.id," + " extension_feed_item.ad_schedules," + " extension_feed_item.device," + " extension_feed_item.status," + " extension_feed_item.start_date_time," + " extension_feed_item.end_date_time," + " extension_feed_item.targeted_campaign," + " extension_feed_item.targeted_ad_group," + " extension_feed_item.promotion_feed_item.discount_modifier," + " extension_feed_item.promotion_feed_item.final_mobile_urls," + " extension_feed_item.promotion_feed_item.final_url_suffix," + " extension_feed_item.promotion_feed_item.final_urls," + " extension_feed_item.promotion_feed_item.language_code," + " extension_feed_item.promotion_feed_item.money_amount_off.amount_micros," + " extension_feed_item.promotion_feed_item.money_amount_off.currency_code," + " extension_feed_item.promotion_feed_item.occasion," + " extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros," + " extension_feed_item.promotion_feed_item.orders_over_amount.currency_code," + " extension_feed_item.promotion_feed_item.percent_off," + " extension_feed_item.promotion_feed_item.promotion_code," + " extension_feed_item.promotion_feed_item.promotion_end_date," + " extension_feed_item.promotion_feed_item.promotion_start_date," + " extension_feed_item.promotion_feed_item.promotion_target," + " extension_feed_item.promotion_feed_item.tracking_url_template " + "FROM extension_feed_item " + "WHERE " + " extension_feed_item.extension_type = 'PROMOTION' " + " AND extension_feed_item.id = " + extensionFeedItemId + " LIMIT 1"; ServerStream<SearchGoogleAdsStreamResponse> serverStream = client .searchStreamCallable() .call( SearchGoogleAdsStreamRequest.newBuilder() .setCustomerId(String.valueOf(customerId)) .setQuery(extensionFeedItemQuery) .build()); Optional<ExtensionFeedItem> extensionFeedItemResult = StreamSupport.stream(serverStream.spliterator(), false) .flatMap(r -> r.getResultsList().stream()) .map(r -> r.getExtensionFeedItem()) .findFirst(); if (!extensionFeedItemResult.isPresent()) { throw new RuntimeException( "Unable to find extension feed item in customer ID " + customerId + " with ID " + extensionFeedItemId); } ExtensionFeedItem extensionFeedItem = extensionFeedItemResult.get(); System.out.println("Retrieved details for ad extension with ID " + extensionFeedItemId); // Creates a query to retrieve any URL customer parameters attached to the feed item. String urlParametersQuery = "SELECT feed_item.url_custom_parameters " + "FROM feed_item " + "WHERE feed_item.id = " + extensionFeedItemId; // Issues a search request to get any URL custom parameters. serverStream = client .searchStreamCallable() .call( SearchGoogleAdsStreamRequest.newBuilder() .setCustomerId(String.valueOf(customerId)) .setQuery(urlParametersQuery) .build()); Optional<FeedItem> urlCustomParametersResult = StreamSupport.stream(serverStream.spliterator(), false) .flatMap(r -> r.getResultsList().stream()) .map(r -> r.getFeedItem()) .findFirst(); // Checks for URL custom parameters and adds to the extension feed item if present. if (urlCustomParametersResult.isPresent()) { FeedItem feedItem = urlCustomParametersResult.get(); // Converts the ExtensionFeedItem to a builder and populates the URL custom parameters. ExtensionFeedItem.Builder builder = extensionFeedItem.toBuilder(); builder .getPromotionFeedItemBuilder() .addAllUrlCustomParameters(feedItem.getUrlCustomParametersList()); extensionFeedItem = builder.build(); System.out.println( "Retrieved " + feedItem.getUrlCustomParametersCount() + " attached URL custom parameters."); } return extensionFeedItem; } /** * Finds and returns all of the campaigns that are associated with the specified Promotion * extension feed item. */ private List<Long> getTargetedCampaignIds( GoogleAdsServiceClient client, Long customerId, String extensionFeedItemResourceName) { String query = "SELECT campaign.id, campaign_extension_setting.extension_feed_items " + "FROM campaign_extension_setting " + "WHERE campaign_extension_setting.extension_type = 'PROMOTION' " + " AND campaign.status != 'REMOVED'"; ServerStream<SearchGoogleAdsStreamResponse> serverStream = client .searchStreamCallable() .call( SearchGoogleAdsStreamRequest.newBuilder() .setCustomerId(String.valueOf(customerId)) .setQuery(query) .build()); List<Long> campaignIds = new ArrayList<>(); for (SearchGoogleAdsStreamResponse response : serverStream) { for (GoogleAdsRow row : response.getResultsList()) { Campaign campaign = row.getCampaign(); CampaignExtensionSetting extensionSetting = row.getCampaignExtensionSetting(); // Adds the campaign ID to the list of IDs if the extension feed item is // associated with this extension setting. if (extensionSetting.getExtensionFeedItemsList().contains(extensionFeedItemResourceName)) { campaignIds.add(campaign.getId()); System.out.println("Found matching campaign with ID " + campaign.getId()); } } } return campaignIds; } /** * Finds and returns all of the ad groups that are associated with the specified Promotion * extension feed item. */ private List<Long> getTargetedAdGroupIds( GoogleAdsServiceClient client, Long customerId, String extensionFeedItemResourceName) { String query = "SELECT ad_group.id, ad_group_extension_setting.extension_feed_items " + "FROM ad_group_extension_setting " + "WHERE ad_group_extension_setting.extension_type = 'PROMOTION' " + " AND ad_group.status != 'REMOVED'"; ServerStream<SearchGoogleAdsStreamResponse> serverStream = client .searchStreamCallable() .call( SearchGoogleAdsStreamRequest.newBuilder() .setCustomerId(String.valueOf(customerId)) .setQuery(query) .build()); List<Long> adGroupIds = new ArrayList<>(); for (SearchGoogleAdsStreamResponse response : serverStream) { for (GoogleAdsRow row : response.getResultsList()) { AdGroup adGroup = row.getAdGroup(); AdGroupExtensionSetting extensionSetting = row.getAdGroupExtensionSetting(); // Adds the ad group ID to the list of IDs if the extension feed item is // associated with this extension setting. if (extensionSetting.getExtensionFeedItemsList().contains(extensionFeedItemResourceName)) { adGroupIds.add(adGroup.getId()); System.out.println("Found matching ad group with ID " + adGroup.getId()); } } } return adGroupIds; } /** Creates a Promotion asset that copies values from the specified extension feed item. */ private String createPromotionAssetFromFeed( GoogleAdsClient googleAdsClient, Long customerId, ExtensionFeedItem extensionFeedItem) { PromotionFeedItem promotionFeedItem = extensionFeedItem.getPromotionFeedItem(); // Creates the Promotion asset. Asset.Builder asset = Asset.newBuilder() .setName("Migrated from feed item " + extensionFeedItem.getId()) .setTrackingUrlTemplate(promotionFeedItem.getTrackingUrlTemplate()) .setFinalUrlSuffix(promotionFeedItem.getFinalUrlSuffix()) .setPromotionAsset( PromotionAsset.newBuilder() .setPromotionTarget(promotionFeedItem.getPromotionTarget()) .setDiscountModifier(promotionFeedItem.getDiscountModifier()) .setRedemptionEndDate(promotionFeedItem.getPromotionStartDate()) .setRedemptionEndDate(promotionFeedItem.getPromotionEndDate()) .setOccasion(promotionFeedItem.getOccasion()) .setLanguageCode(promotionFeedItem.getLanguageCode()) .addAllAdScheduleTargets(extensionFeedItem.getAdSchedulesList())) .addAllFinalUrls(promotionFeedItem.getFinalUrlsList()) .addAllFinalMobileUrls(promotionFeedItem.getFinalMobileUrlsList()) .addAllUrlCustomParameters(promotionFeedItem.getUrlCustomParametersList()); // Either PercentOff or MoneyAmountOff must be set. if (promotionFeedItem.getPercentOff() > 0) { // Adjusts the percent off scale when copying. asset.getPromotionAssetBuilder().setPercentOff(promotionFeedItem.getPercentOff() / 100); } else { asset.getPromotionAssetBuilder().setMoneyAmountOff(promotionFeedItem.getMoneyAmountOff()); } // Either PromotionCode or OrdersOverAmount must be set. if (promotionFeedItem.getPromotionCode() != null && promotionFeedItem.getPromotionCode().length() > 0) { asset.getPromotionAssetBuilder().setPromotionCode(promotionFeedItem.getPromotionCode()); } else { asset.getPromotionAssetBuilder().setOrdersOverAmount(promotionFeedItem.getOrdersOverAmount()); } // Sets the start and end dates if set in the existing extension. if (extensionFeedItem.hasStartDateTime()) { asset.getPromotionAssetBuilder().setStartDate(extensionFeedItem.getStartDateTime()); } if (extensionFeedItem.hasEndDateTime()) { asset.getPromotionAssetBuilder().setEndDate(extensionFeedItem.getEndDateTime()); } // Builds an operation to create the Promotion asset. AssetOperation operation = AssetOperation.newBuilder().setCreate(asset).build(); // Gets the Asset Service client. try (AssetServiceClient assetServiceClient = googleAdsClient.getLatestVersion().createAssetServiceClient()) { // Issues the request and returns the resource name of the new Promotion asset. MutateAssetsResponse response = assetServiceClient.mutateAssets(String.valueOf(customerId), ImmutableList.of(operation)); String resourceName = response.getResults(0).getResourceName(); System.out.println("Created Promotion asset with resource name " + resourceName); return resourceName; } } /** Associates the specified Promotion asset with the specified campaigns. */ private void associateAssetWithCampaigns( GoogleAdsClient googleAdsClient, Long customerId, String promotionAssetResourceName, List<Long> campaignIds) { if (campaignIds.isEmpty()) { System.out.println("Asset was not associated with any campaigns."); return; } // Constructs an operation to associate the asset with each campaign. List<CampaignAssetOperation> campaignAssetOperations = campaignIds.stream() .map( id -> CampaignAssetOperation.newBuilder() .setCreate( CampaignAsset.newBuilder() .setAsset(promotionAssetResourceName) .setFieldType(AssetFieldType.PROMOTION) .setCampaign(ResourceNames.campaign(customerId, id))) .build()) .collect(Collectors.toList()); // Creates a service client. try (CampaignAssetServiceClient campaignAssetServiceClient = googleAdsClient.getLatestVersion().createCampaignAssetServiceClient()) { // Issues the mutate request. MutateCampaignAssetsResponse response = campaignAssetServiceClient.mutateCampaignAssets( String.valueOf(customerId), campaignAssetOperations); // Prints some information about the result. for (MutateCampaignAssetResult result : response.getResultsList()) { System.out.println("Created campaign asset with resource name " + result.getResourceName()); } } } /** Associates the specified Promotion asset with the specified ad groups. */ private void associateAssetWithAdGroups( GoogleAdsClient googleAdsClient, Long customerId, String promotionAssetResourceName, List<Long> adGroupIds) { if (adGroupIds.isEmpty()) { System.out.println("Asset was not associated with any ad groups."); return; } // Creates an operation to associate the asset with each ad group. List<AdGroupAssetOperation> adGroupAssetOperations = adGroupIds.stream() .map( id -> AdGroupAssetOperation.newBuilder() .setCreate( AdGroupAsset.newBuilder() .setAsset(promotionAssetResourceName) .setFieldType(AssetFieldType.PROMOTION) .setAdGroup(ResourceNames.adGroup(customerId, id))) .build()) .collect(Collectors.toList()); // Creates a service client. try (AdGroupAssetServiceClient adGroupAssetServiceClient = googleAdsClient.getLatestVersion().createAdGroupAssetServiceClient()) { // Issues the mutate request. MutateAdGroupAssetsResponse response = adGroupAssetServiceClient.mutateAdGroupAssets( String.valueOf(customerId), adGroupAssetOperations); // Prints some information about the result. for (MutateAdGroupAssetResult result : response.getResultsList()) { System.out.println("Created campaign asset with resource name " + result.getResourceName()); } } } }
C#
// Copyright 2021 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.V14.Common; using Google.Ads.GoogleAds.V14.Enums; using Google.Ads.GoogleAds.V14.Errors; using Google.Ads.GoogleAds.V14.Resources; using Google.Ads.GoogleAds.V14.Services; using Google.Protobuf.Collections; using System; using System.Collections.Generic; using System.Linq; namespace Google.Ads.GoogleAds.Examples.V14 { /// <summary> /// This code example retrieves the full details of a Promotion Feed-based extension and /// creates a matching Promotion asset-based extension. The new Asset-based extension will /// then be associated with the same campaigns and ad groups as the original Feed-based /// extension. /// Once copied, you should remove the Feed-based extension; see /// RemoveEntireSitelinkCampaignExtensionSetting.cs for an example. /// </summary> public class MigratePromotionFeedToAsset : ExampleBase { /// <summary> /// Command line options for running the <see cref="MigratePromotionFeedToAsset"/> 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> /// ID of the extension feed item to migrate. /// </summary> [Option("feedItemId", Required = true, HelpText = "ID of the extension feed item to migrate.")] public long FeedItemId { 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); MigratePromotionFeedToAsset codeExample = new MigratePromotionFeedToAsset(); Console.WriteLine(codeExample.Description); codeExample.Run(new GoogleAdsClient(), options.CustomerId, options.FeedItemId); } /// <summary> /// Returns a description about the code example. /// </summary> public override string Description => "This code example retrieves the full details of a Promotion Feed-based extension " + "and creates a matching Promotion asset-based extension. The new Asset-based " + "extension will then be associated with the same campaigns and ad groups as the " + "original Feed-based extension.\n" + "Once copied, you should remove the Feed-based extension; see " + "RemoveEntireSitelinkCampaignExtensionSetting.cs for an example."; /// <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="feedItemId">ID of the extension feed item to migrate.</param> public void Run(GoogleAdsClient client, long customerId, long feedItemId) { // Get the GoogleAdsService client. GoogleAdsServiceClient googleAdsServiceClient = client.GetService(Services.V14.GoogleAdsService); string extensionFeedItemResourceName = ResourceNames.ExtensionFeedItem(customerId, feedItemId); try { // Get the target extension feed item. ExtensionFeedItem extensionFeedItem = GetExtensionFeedItem(googleAdsServiceClient, customerId, feedItemId); // Get all campaign IDs associated with the extension feed item. List<long> campaignIds = GetTargetedCampaignIds(googleAdsServiceClient, customerId, extensionFeedItemResourceName); // Get all ad group IDs associated with the extension feed item. List<long> adGroupIds = GetTargetedAdGroupIds(googleAdsServiceClient, customerId, extensionFeedItemResourceName); // Create a new Promotion asset that matches the target extension feed item. string promotionAssetResourceName = CreatePromotionAssetFromFeed(client, customerId, extensionFeedItem); // Associate the new Promotion asset with the same campaigns as the original. AssociateAssetWithCampaigns(client, customerId, promotionAssetResourceName, campaignIds); // Associate the new Promotion asset with the same ad groups as the original. AssociateAssetWithAdGroups(client, customerId, promotionAssetResourceName, adGroupIds); } catch (GoogleAdsException e) { Console.WriteLine("Failure:"); Console.WriteLine($"Message: {e.Message}"); Console.WriteLine($"Failure: {e.Failure}"); Console.WriteLine($"Request ID: {e.RequestId}"); throw; } catch (Exception e) { Console.WriteLine(e.Message); Console.WriteLine(e.StackTrace); throw; } } /// <summary> /// Gets the requested Promotion-type extension feed item. /// /// Note that extension feed items pertain to feeds that were created by Google. Use /// FeedService to instead retrieve a user-created Feed. /// </summary> /// <param name="googleAdsServiceClient">An initialized Google Ads API Service /// client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="feedItemId">ID of the extension feed item to migrate.</param> /// <returns>The requested ExtensionFeedItem, or null if no matching item was /// found.</returns> private ExtensionFeedItem GetExtensionFeedItem( GoogleAdsServiceClient googleAdsServiceClient, long customerId, long feedItemId) { // Create a query that will retrieve the requested Promotion-type extension feed item // and ensure that all fields are populated. string extensionFeedItemQuery = $@" SELECT extension_feed_item.id, extension_feed_item.ad_schedules, extension_feed_item.device, extension_feed_item.status, extension_feed_item.start_date_time, extension_feed_item.end_date_time, extension_feed_item.targeted_campaign, extension_feed_item.targeted_ad_group, extension_feed_item.promotion_feed_item.discount_modifier, extension_feed_item.promotion_feed_item.final_mobile_urls, extension_feed_item.promotion_feed_item.final_url_suffix, extension_feed_item.promotion_feed_item.final_urls, extension_feed_item.promotion_feed_item.language_code, extension_feed_item.promotion_feed_item.money_amount_off.amount_micros, extension_feed_item.promotion_feed_item.money_amount_off.currency_code, extension_feed_item.promotion_feed_item.occasion, extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros, extension_feed_item.promotion_feed_item.orders_over_amount.currency_code, extension_feed_item.promotion_feed_item.percent_off, extension_feed_item.promotion_feed_item.promotion_code, extension_feed_item.promotion_feed_item.promotion_end_date, extension_feed_item.promotion_feed_item.promotion_start_date, extension_feed_item.promotion_feed_item.promotion_target, extension_feed_item.promotion_feed_item.tracking_url_template FROM extension_feed_item WHERE extension_feed_item.extension_type = 'PROMOTION' AND extension_feed_item.id = {feedItemId} LIMIT 1"; ExtensionFeedItem fetchedExtensionFeedItem = null; // Issue a search request to get the extension feed item contents. googleAdsServiceClient.SearchStream(customerId.ToString(), extensionFeedItemQuery, delegate (SearchGoogleAdsStreamResponse response) { fetchedExtensionFeedItem = response.Results.First().ExtensionFeedItem; } ); Console.WriteLine( $"Retrieved details for ad extension with ID {fetchedExtensionFeedItem.Id}."); // Create a query to retrieve any URL customer parameters attached to the feed item. string urlCustomParametersQuery = $@" SELECT feed_item.url_custom_parameters FROM feed_item WHERE feed_item.id = {feedItemId}"; // Issue a search request to get any URL custom parameters. googleAdsServiceClient.SearchStream(customerId.ToString(), urlCustomParametersQuery, delegate (SearchGoogleAdsStreamResponse response) { RepeatedField<CustomParameter> urlCustomParameters = response.Results.First().FeedItem.UrlCustomParameters; Console.WriteLine( $"Retrieved {urlCustomParameters.Count} attached URL custom parameters."); if (urlCustomParameters.Count > 0) { fetchedExtensionFeedItem.PromotionFeedItem.UrlCustomParameters.Add( urlCustomParameters); } } ); return fetchedExtensionFeedItem; } /// <summary> /// Finds and returns all of the campaigns that are associated with the specified Promotion /// extension feed item. /// </summary> /// <param name="googleAdsServiceClient">An initialized Google Ads API Service /// client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="extensionFeedResourceName">ID of the extension feed item to /// migrate.</param> /// <returns>A list of campaign IDs.</returns> private List<long> GetTargetedCampaignIds(GoogleAdsServiceClient googleAdsServiceClient, long customerId, string extensionFeedResourceName) { List<long> campaignIds = new List<long>(); string query = @" SELECT campaign.id, campaign_extension_setting.extension_feed_items FROM campaign_extension_setting WHERE campaign_extension_setting.extension_type = 'PROMOTION' AND campaign.status != 'REMOVED'"; googleAdsServiceClient.SearchStream(customerId.ToString(), query, delegate (SearchGoogleAdsStreamResponse response) { foreach (GoogleAdsRow googleAdsRow in response.Results) { // Add the campaign ID to the list of IDs if the extension feed item is // associated with this extension setting. if (googleAdsRow.CampaignExtensionSetting.ExtensionFeedItems.Contains( extensionFeedResourceName)) { Console.WriteLine( $"Found matching campaign with ID {googleAdsRow.Campaign.Id}."); campaignIds.Add(googleAdsRow.Campaign.Id); } } } ); return campaignIds; } /// <summary> /// Finds and returns all of the ad groups that are associated with the specified Promotion /// extension feed item. /// </summary> /// <param name="googleAdsServiceClient">An initialized Google Ads API Service /// client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="extensionFeedItemResourceName">Resource name of the extension feed item to /// migrate.</param> /// <returns>A list of ad group IDs.</returns> private List<long> GetTargetedAdGroupIds( GoogleAdsServiceClient googleAdsServiceClient, long customerId, string extensionFeedItemResourceName) { List<long> adGroupIds = new List<long>(); string query = @" SELECT ad_group.id, ad_group_extension_setting.extension_feed_items FROM ad_group_extension_setting WHERE ad_group_extension_setting.extension_type = 'PROMOTION' AND ad_group.status != 'REMOVED'"; googleAdsServiceClient.SearchStream(customerId.ToString(), query, delegate (SearchGoogleAdsStreamResponse response) { foreach (GoogleAdsRow googleAdsRow in response.Results) { // Add the ad group ID to the list of IDs if the extension feed item is // associated with this extension setting. if (googleAdsRow.AdGroupExtensionSetting.ExtensionFeedItems.Contains( extensionFeedItemResourceName)) { Console.WriteLine( $"Found matching ad group with ID {googleAdsRow.AdGroup.Id}."); adGroupIds.Add(googleAdsRow.AdGroup.Id); } } } ); return adGroupIds; } /// <summary> /// Create a Promotion asset that copies values from the specified extension feed item. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="extensionFeedItem">The extension feed item to be migrated.</param> /// <returns>The resource name of the newly created Promotion asset.</returns> private string CreatePromotionAssetFromFeed(GoogleAdsClient client, long customerId, ExtensionFeedItem extensionFeedItem) { // Get the Asset Service client. AssetServiceClient assetServiceClient = client.GetService(Services.V14.AssetService); PromotionFeedItem promotionFeedItem = extensionFeedItem.PromotionFeedItem; // Create the Promotion asset. Asset asset = new Asset { // Name field is optional. Name = $"Migrated from feed item #{extensionFeedItem.Id}", PromotionAsset = new PromotionAsset { PromotionTarget = promotionFeedItem.PromotionTarget, DiscountModifier = promotionFeedItem.DiscountModifier, RedemptionStartDate = promotionFeedItem.PromotionStartDate, RedemptionEndDate = promotionFeedItem.PromotionEndDate, Occasion = promotionFeedItem.Occasion, LanguageCode = promotionFeedItem.LanguageCode, }, TrackingUrlTemplate = promotionFeedItem.TrackingUrlTemplate, FinalUrlSuffix = promotionFeedItem.FinalUrlSuffix }; // Either PercentOff or MoneyAmountOff must be set. if (promotionFeedItem.PercentOff > 0) { // Adjust the percent off scale when copying. asset.PromotionAsset.PercentOff = promotionFeedItem.PercentOff / 100; } else { asset.PromotionAsset.MoneyAmountOff = new Money { AmountMicros = promotionFeedItem.MoneyAmountOff.AmountMicros, CurrencyCode = promotionFeedItem.MoneyAmountOff.CurrencyCode }; } // Either PromotionCode or OrdersOverAmount must be set. if (!string.IsNullOrEmpty(promotionFeedItem.PromotionCode)) { asset.PromotionAsset.PromotionCode = promotionFeedItem.PromotionCode; } else { asset.PromotionAsset.OrdersOverAmount = new Money { AmountMicros = promotionFeedItem.OrdersOverAmount.AmountMicros, CurrencyCode = promotionFeedItem.OrdersOverAmount.CurrencyCode }; } // Set the start and end dates if set in the existing extension. if (extensionFeedItem.HasStartDateTime) { asset.PromotionAsset.StartDate = DateTime.Parse(extensionFeedItem.StartDateTime) .ToString("yyyy-MM-dd"); } if (extensionFeedItem.HasEndDateTime) { asset.PromotionAsset.EndDate = DateTime.Parse(extensionFeedItem.EndDateTime) .ToString("yyyy-MM-dd"); } asset.PromotionAsset.AdScheduleTargets.Add(extensionFeedItem.AdSchedules); asset.FinalUrls.Add(promotionFeedItem.FinalUrls); asset.FinalMobileUrls.Add(promotionFeedItem.FinalMobileUrls); asset.UrlCustomParameters.Add(promotionFeedItem.UrlCustomParameters); // Build an operation to create the Promotion asset. AssetOperation operation = new AssetOperation { Create = asset }; // Issue the request and return the resource name of the new Promotion asset. MutateAssetsResponse response = assetServiceClient.MutateAssets( customerId.ToString(), new[] { operation }); Console.WriteLine("Created Promotion asset with resource name " + $"{response.Results.First().ResourceName}"); return response.Results.First().ResourceName; } /// <summary> /// Associates the specified Promotion asset with the specified campaigns. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="promotionAssetResourceName">The string resource name of the Promotion /// Asset.</param> /// <param name="campaignIds">A list of campaign IDs with which the Asset should be /// associated.</param> private void AssociateAssetWithCampaigns(GoogleAdsClient client, long customerId, string promotionAssetResourceName, List<long> campaignIds) { if (campaignIds.Count == 0) { Console.WriteLine("Asset was not associated with any campaigns."); return; } CampaignAssetServiceClient campaignAssetServiceClient = client.GetService(Services.V14 .CampaignAssetService); List<CampaignAssetOperation> operations = new List<CampaignAssetOperation>(); foreach (long campaignId in campaignIds) { operations.Add(new CampaignAssetOperation { Create = new CampaignAsset { Asset = promotionAssetResourceName, FieldType = AssetFieldTypeEnum.Types.AssetFieldType.Promotion, Campaign = ResourceNames.Campaign(customerId, campaignId), } }); } MutateCampaignAssetsResponse response = campaignAssetServiceClient.MutateCampaignAssets( customerId.ToString(), operations); foreach (MutateCampaignAssetResult result in response.Results) { Console.WriteLine($"Created campaign asset with resource name " + $"{result.ResourceName}."); } } /// <summary> /// Associates the specified Promotion asset with the specified ad groups. /// </summary> /// <param name="client">The Google Ads client.</param> /// <param name="customerId">The customer ID for which the call is made.</param> /// <param name="promotionAssetResourceName">The string resource name of the Promotion /// Asset.</param> /// <param name="adGroupIds">A list of ad group IDs with which the Asset should be /// associated.</param> private void AssociateAssetWithAdGroups(GoogleAdsClient client, long customerId, string promotionAssetResourceName, List<long> adGroupIds) { if (adGroupIds.Count == 0) { Console.WriteLine("Asset was not associated with any ad groups."); return; } AdGroupAssetServiceClient adGroupAssetServiceClient = client.GetService(Services.V14 .AdGroupAssetService); List<AdGroupAssetOperation> operations = new List<AdGroupAssetOperation>(); foreach (long adGroupId in adGroupIds) { operations.Add(new AdGroupAssetOperation { Create = new AdGroupAsset { Asset = promotionAssetResourceName, FieldType = AssetFieldTypeEnum.Types.AssetFieldType.Promotion, AdGroup = ResourceNames.AdGroup(customerId, adGroupId), } }); } MutateAdGroupAssetsResponse response = adGroupAssetServiceClient.MutateAdGroupAssets( customerId.ToString(), operations); foreach (MutateAdGroupAssetResult result in response.Results) { Console.WriteLine($"Created ad group asset with resource name " + $"{result.ResourceName}."); } } } }
PHP
<?php /** * Copyright 2021 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\Lib\OAuth2TokenBuilder; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsClient; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsClientBuilder; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsException; use Google\Ads\GoogleAds\Lib\V14\GoogleAdsServerStreamDecorator; use Google\Ads\GoogleAds\Util\V14\ResourceNames; use Google\Ads\GoogleAds\V14\Common\Money; use Google\Ads\GoogleAds\V14\Common\PromotionAsset; use Google\Ads\GoogleAds\V14\Enums\AssetFieldTypeEnum\AssetFieldType; use Google\Ads\GoogleAds\V14\Errors\GoogleAdsError; use Google\Ads\GoogleAds\V14\Resources\AdGroupAsset; use Google\Ads\GoogleAds\V14\Resources\Asset; use Google\Ads\GoogleAds\V14\Resources\CampaignAsset; use Google\Ads\GoogleAds\V14\Resources\ExtensionFeedItem; use Google\Ads\GoogleAds\V14\Resources\FeedItem; use Google\Ads\GoogleAds\V14\Services\AdGroupAssetOperation; use Google\Ads\GoogleAds\V14\Services\AssetOperation; use Google\Ads\GoogleAds\V14\Services\CampaignAssetOperation; use Google\Ads\GoogleAds\V14\Services\GoogleAdsRow; use Google\Ads\GoogleAds\V14\Services\MutateAdGroupAssetsRequest; use Google\Ads\GoogleAds\V14\Services\MutateAssetsRequest; use Google\Ads\GoogleAds\V14\Services\MutateCampaignAssetsRequest; use Google\Ads\GoogleAds\V14\Services\SearchGoogleAdsStreamRequest; use Google\ApiCore\ApiException; /** * This code example retrieves the full details of a Promotion Feed-based extension and * creates a matching Promotion asset-based extension. The new Asset-based extension will * then be associated with the same campaigns and ad groups as the original Feed-based * extension. * Once copied, you should remove the Feed-based extension; see * RemoveEntireSitelinkCampaignExtensionSetting.php for an example. */ class MigratePromotionFeedToAsset { private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE'; // The ID of the extension feed item to migrate. private const FEED_ITEM_ID = 'INSERT_FEED_ITEM_ID_HERE'; 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::FEED_ITEM_ID => 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) // We set this value to true to show how to use GAPIC v2 source code. You can remove the // below line if you wish to use the old-style source code. Note that in that case, you // probably need to modify some parts of the code below to make it work. // For more information, see // https://developers.devsite.corp.google.com/google-ads/api/docs/client-libs/php/gapic. ->usingGapicV2Source(true) ->build(); try { self::runExample( $googleAdsClient, $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID, $options[ArgumentNames::FEED_ITEM_ID] ?: self::FEED_ITEM_ID ); } 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 int $feedItemId ID of the extension feed item to migrate */ public static function runExample( GoogleAdsClient $googleAdsClient, int $customerId, int $feedItemId ) { $extensionFeedItemResourceName = ResourceNames::forExtensionFeedItem($customerId, $feedItemId); // Get the target extension feed item. $extensionFeedItem = self::getExtensionFeedItem($googleAdsClient, $customerId, $feedItemId); // Get all campaign IDs associated with the extension feed item. $campaignIds = self::getTargetedCampaignIds( $googleAdsClient, $customerId, $extensionFeedItemResourceName ); // Get all ad group IDs associated with the extension feed item. $adGroupIds = self::getTargetedAdGroupIds( $googleAdsClient, $customerId, $extensionFeedItemResourceName ); // Create a new Promotion asset that matches the target extension feed item. $promotionAssetResourceName = self::createPromotionAssetFromFeed($googleAdsClient, $customerId, $extensionFeedItem); // Associate the new Promotion asset with the same campaigns as the original. self::associateAssetWithCampaigns( $googleAdsClient, $customerId, $promotionAssetResourceName, $campaignIds ); // Associate the new Promotion asset with the same ad groups as the original. self::associateAssetWithAdGroups( $googleAdsClient, $customerId, $promotionAssetResourceName, $adGroupIds ); } /** * Gets the requested Promotion-type extension feed item. * * Note that extension feed items pertain to feeds that were created by Google. Use * FeedService to instead retrieve a user-created Feed. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the client customer ID * @param int $feedItemId the ID of the extension feed item to fetch * @return ExtensionFeedItem|null the requested extension feed item or null if no matching * extension feed item was found */ private static function getExtensionFeedItem( GoogleAdsClient $googleAdsClient, int $customerId, string $feedItemId ): ?ExtensionFeedItem { $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); // Create a query that will retrieve the requested Promotion-type extension feed item // and ensure that all fields are populated. $query = "SELECT extension_feed_item.id, extension_feed_item.ad_schedules, extension_feed_item.device, extension_feed_item.status, extension_feed_item.start_date_time, extension_feed_item.end_date_time, extension_feed_item.targeted_campaign, extension_feed_item.targeted_ad_group, extension_feed_item.promotion_feed_item.discount_modifier, extension_feed_item.promotion_feed_item.final_mobile_urls, extension_feed_item.promotion_feed_item.final_url_suffix, extension_feed_item.promotion_feed_item.final_urls, extension_feed_item.promotion_feed_item.language_code, extension_feed_item.promotion_feed_item.money_amount_off.amount_micros, extension_feed_item.promotion_feed_item.money_amount_off.currency_code, extension_feed_item.promotion_feed_item.occasion, extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros, extension_feed_item.promotion_feed_item.orders_over_amount.currency_code, extension_feed_item.promotion_feed_item.percent_off, extension_feed_item.promotion_feed_item.promotion_code, extension_feed_item.promotion_feed_item.promotion_end_date, extension_feed_item.promotion_feed_item.promotion_start_date, extension_feed_item.promotion_feed_item.promotion_target, extension_feed_item.promotion_feed_item.tracking_url_template FROM extension_feed_item WHERE extension_feed_item.extension_type = 'PROMOTION' AND extension_feed_item.id = $feedItemId LIMIT 1"; $fetchedExtensionFeedItem = null; // Issue a search request to get the extension feed item contents. /** @var GoogleAdsServerStreamDecorator $stream */ $stream = $googleAdsServiceClient->searchStream( SearchGoogleAdsStreamRequest::build($customerId, $query) ); $currentElement = $stream->iterateAllElements()->current(); if (!is_null($currentElement)) { /** @var ExtensionFeedItem $fetchedExtensionFeedItem */ $fetchedExtensionFeedItem = $currentElement->getExtensionFeedItem(); printf( "Retrieved details for extension feed item with ID %d.%s", $fetchedExtensionFeedItem->getId(), PHP_EOL ); } else { // No need to get URL custom parameters if the extension feed item wasn't found. return null; } // Create a query to retrieve any URL customer parameters attached to the feed item. $urlCustomParametersQuery = "SELECT feed_item.url_custom_parameters FROM feed_item " . "WHERE feed_item.id = $feedItemId"; // Issue a search request to get any URL custom parameters. /** @var GoogleAdsServerStreamDecorator $stream */ $stream = $googleAdsServiceClient->searchStream( SearchGoogleAdsStreamRequest::build($customerId, $urlCustomParametersQuery) ); /** @var FeedItem $fetchedFeedItem */ $fetchedFeedItem = $stream->iterateAllElements()->current()->getFeedItem(); $urlCustomParameters = $fetchedFeedItem->getUrlCustomParameters(); printf( "Retrieved %d attached URL custom parameters.%s", count($urlCustomParameters), PHP_EOL ); if (!empty($urlCustomParameters)) { $fetchedExtensionFeedItem->getPromotionFeedItem()->setUrlCustomParameters( $urlCustomParameters ); } return $fetchedExtensionFeedItem; } /** * Finds and returns all of the campaigns that are associated with the specified Promotion * extension feed item. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the client customer ID * @param int $extensionFeedItemResourceName the resource name of the extension feed item to * migrate * @return int[] the list of campaign IDs associated with the specified extension feed item */ private static function getTargetedCampaignIds( GoogleAdsClient $googleAdsClient, int $customerId, string $extensionFeedItemResourceName ): array { $campaignIds = []; $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); // Create a query that will retrieve the campaign extension settings. $query = "SELECT campaign.id, campaign_extension_setting.extension_feed_items " . "FROM campaign_extension_setting " . "WHERE campaign_extension_setting.extension_type = 'PROMOTION' " . "AND campaign.status != 'REMOVED'"; // Issue a search request to get the campaign extension settings. /** @var GoogleAdsServerStreamDecorator $stream */ $stream = $googleAdsServiceClient->searchStream( SearchGoogleAdsStreamRequest::build($customerId, $query) ); foreach ($stream->iterateAllElements() as $googleAdsRow) { /** @var GoogleAdsRow $googleAdsRow */ // Add the campaign ID to the list of IDs if the extension feed item is // associated with this extension setting. if ( in_array( $extensionFeedItemResourceName, iterator_to_array( $googleAdsRow->getCampaignExtensionSetting()->getExtensionFeedItems() ) ) ) { printf( "Found matching campaign with ID %d.%s", $googleAdsRow->getCampaign()->getId(), PHP_EOL ); $campaignIds[] = $googleAdsRow->getCampaign()->getId(); } } return $campaignIds; } /** * Finds and returns all of the ad groups that are associated with the specified Promotion * extension feed item. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the client customer ID * @param int $extensionFeedItemResourceName the resource name of the extension feed item to * migrate * @return int[] the list of ad group IDs associated with the specified extension feed item */ private static function getTargetedAdGroupIds( GoogleAdsClient $googleAdsClient, int $customerId, string $extensionFeedItemResourceName ): array { $adGroupIds = []; $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient(); // Create a query that will retrieve the ad group extension settings. $query = "SELECT ad_group.id, ad_group_extension_setting.extension_feed_items " . "FROM ad_group_extension_setting " . "WHERE ad_group_extension_setting.extension_type = 'PROMOTION' " . "AND ad_group.status != 'REMOVED'"; // Issue a search request to get the ad group extension settings. /** @var GoogleAdsServerStreamDecorator $stream */ $stream = $googleAdsServiceClient->searchStream( SearchGoogleAdsStreamRequest::build($customerId, $query) ); foreach ($stream->iterateAllElements() as $googleAdsRow) { /** @var GoogleAdsRow $googleAdsRow */ // Add the ad group ID to the list of IDs if the extension feed item is // associated with this extension setting. if ( in_array( $extensionFeedItemResourceName, iterator_to_array( $googleAdsRow->getAdGroupExtensionSetting()->getExtensionFeedItems() ) ) ) { printf( "Found matching ad group with ID %d.%s", $googleAdsRow->getAdGroup()->getId(), PHP_EOL ); $adGroupIds[] = $googleAdsRow->getAdGroup()->getId(); } } return $adGroupIds; } /** * Create a Promotion asset that copies values from the specified extension feed item. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param ExtensionFeedItem $extensionFeedItem the extension feed item to be migrated * @return string the resource name of the newly created Promotion asset */ private static function createPromotionAssetFromFeed( GoogleAdsClient $googleAdsClient, int $customerId, ExtensionFeedItem $extensionFeedItem ): string { $promotionFeedItem = $extensionFeedItem->getPromotionFeedItem(); // Creates the Promotion asset. $asset = new Asset([ // Name field is optional. 'name' => 'Migrated from feed item #' . $extensionFeedItem->getId(), 'promotion_asset' => new PromotionAsset([ 'promotion_target' => $promotionFeedItem->getPromotionTarget(), 'discount_modifier' => $promotionFeedItem->getDiscountModifier(), 'redemption_start_date' => $promotionFeedItem->getPromotionStartDate(), 'redemption_end_date' => $promotionFeedItem->getPromotionEndDate(), 'occasion' => $promotionFeedItem->getOccasion(), 'language_code' => $promotionFeedItem->getLanguageCode(), 'ad_schedule_targets' => $extensionFeedItem->getAdSchedules() ]), 'tracking_url_template' => $promotionFeedItem->getTrackingUrlTemplate(), 'url_custom_parameters' => $promotionFeedItem->getUrlCustomParameters(), 'final_urls' => $promotionFeedItem->getFinalUrls(), 'final_mobile_urls' => $promotionFeedItem->getFinalMobileUrls(), 'final_url_suffix' => $promotionFeedItem->getFinalUrlSuffix(), ]); // Either percent off or money amount off must be set. if ($promotionFeedItem->getPercentOff() > 0) { // Adjust the percent off scale when copying. $asset->getPromotionAsset()->setPercentOff($promotionFeedItem->getPercentOff() / 100); } else { $money = new Money([ 'amount_micros' => $promotionFeedItem->getMoneyAmountOff()->getAmountMicros(), 'currency_code' => $promotionFeedItem->getMoneyAmountOff()->getCurrencyCode() ]); $asset->getPromotionAsset()->setMoneyAmountOff($money); } // Either promotion code or orders over amount must be set. if (!empty($promotionFeedItem->getPromotionCode())) { $asset->getPromotionAsset()->setPromotionCode($promotionFeedItem->getPromotionCode()); } else { $money = new Money([ 'amount_micros' => $promotionFeedItem->getOrdersOverAmount()->getAmountMicros(), 'currency_code' => $promotionFeedItem->getOrdersOverAmount()->getCurrencyCode() ]); $asset->getPromotionAsset()->setOrdersOverAmount($money); } if ($extensionFeedItem->hasStartDateTime()) { $startDateTime = new \DateTime($extensionFeedItem->getStartDateTime()); $asset->getPromotionAsset()->setStartDate($startDateTime->format('yyyy-MM-dd')); } if ($extensionFeedItem->hasEndDateTime()) { $endDateTime = new \DateTime($extensionFeedItem->getEndDateTime()); $asset->getPromotionAsset()->setEndDate($endDateTime->format('yyyy-MM-dd')); } // Creates an operation to add the Promotion asset. $assetOperation = new AssetOperation(); $assetOperation->setCreate($asset); // Issues a mutate request to add the Promotion asset and prints its information. $assetServiceClient = $googleAdsClient->getAssetServiceClient(); $response = $assetServiceClient->mutateAssets( MutateAssetsRequest::build($customerId, [$assetOperation]) ); $assetResourceName = $response->getResults()[0]->getResourceName(); printf( "Created the Promotion asset with resource name: '%s'.%s", $assetResourceName, PHP_EOL ); return $assetResourceName; } /** * Associates the specified Promotion asset with the specified campaigns. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param string $promotionAssetResourceName the resource name of the Promotion asset * @param int[] $campaignIds the IDs of the campaigns with which assets should be associated */ private static function associateAssetWithCampaigns( GoogleAdsClient $googleAdsClient, int $customerId, string $promotionAssetResourceName, array $campaignIds ) { if (empty($campaignIds)) { print 'Asset was not associated with any campaigns.' . PHP_EOL; return; } $operations = []; foreach ($campaignIds as $campaignId) { $operations[] = new CampaignAssetOperation([ 'create' => new CampaignAsset([ 'asset' => $promotionAssetResourceName, 'field_type' => AssetFieldType::PROMOTION, 'campaign' => ResourceNames::forCampaign($customerId, $campaignId) ]) ]); } // Issues a mutate request to add the campaign assets and prints their information. $campaignAssetServiceClient = $googleAdsClient->getCampaignAssetServiceClient(); $response = $campaignAssetServiceClient->mutateCampaignAssets( MutateCampaignAssetsRequest::build($customerId, $operations) ); foreach ($response->getResults() as $addedCampaignAsset) { /** @var CampaignAsset $addedCampaignAsset */ printf( "Created campaign asset with resource name: '%s'.%s", $addedCampaignAsset->getResourceName(), PHP_EOL ); } } /** * Associates the specified Promotion asset with the specified ad groups. * * @param GoogleAdsClient $googleAdsClient the Google Ads API client * @param int $customerId the customer ID * @param string $promotionAssetResourceName the resource name of the Promotion asset * @param int[] $adGroupIds the IDs of the ad groups with which assets should be associated */ private static function associateAssetWithAdGroups( GoogleAdsClient $googleAdsClient, int $customerId, string $promotionAssetResourceName, array $adGroupIds ) { if (empty($adGroupIds)) { print 'Asset was not associated with any ad groups.' . PHP_EOL; return; } $operations = []; foreach ($adGroupIds as $adGroupId) { $operations[] = new AdGroupAssetOperation([ 'create' => new AdGroupAsset([ 'asset' => $promotionAssetResourceName, 'field_type' => AssetFieldType::PROMOTION, 'ad_group' => ResourceNames::forAdGroup($customerId, $adGroupId) ]) ]); } // Issues a mutate request to add the ad group assets and prints their information. $adGroupAssetServiceClient = $googleAdsClient->getAdGroupAssetServiceClient(); $response = $adGroupAssetServiceClient->mutateAdGroupAssets( MutateAdGroupAssetsRequest::build($customerId, $operations) ); foreach ($response->getResults() as $addedAdGroupAsset) { /** @var AdGroupAsset $addedAdGroupAsset */ printf( "Created ad group asset with resource name: '%s'.%s", $addedAdGroupAsset->getResourceName(), PHP_EOL ); } } } MigratePromotionFeedToAsset::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. """Migrates a feed-based promotion extension to an asset-based extension. The new asset-based extension will then be associated with the same campaigns and ad groups as the original feed-based extension. Finally, the old feed-based extension will be mutated so it no longer serves. """ import argparse import sys from uuid import uuid4 from google.ads.googleads.client import GoogleAdsClient from google.ads.googleads.errors import GoogleAdsException def main(client, customer_id, feed_item_id): """The main method that creates all necessary entities for the example. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. feed_item_id: an extension feed item ID. """ extension_feed_item_service = client.get_service("ExtensionFeedItemService") resource_name = extension_feed_item_service.extension_feed_item_path( customer_id, feed_item_id ) # Get the target extension feed item extension_feed_item = get_extension_feed_item( client, customer_id, feed_item_id ) # Get all campaign IDs associated with the extension feed item. campaign_ids = get_targeted_campaign_ids(client, customer_id, resource_name) # Get all ad group IDs associated with the extension feed item. ad_group_ids = get_targeted_ad_group_ids(client, customer_id, resource_name) # Create a new Promotion asset that matches the target extension feed item. promotion_asset_resource_name = create_promotion_asset_from_feed( client, customer_id, extension_feed_item ) # Associate the new Promotion asset with the same campaigns as the original. associate_asset_with_campaigns( client, customer_id, promotion_asset_resource_name, campaign_ids ) # Associate the new Promotion asset with the same ad groups as the original. associate_asset_with_ad_groups( client, customer_id, promotion_asset_resource_name, ad_group_ids ) def get_extension_feed_item(client, customer_id, feed_item_id): """Gets the requested Promotion-type extension feed item. Note that extension feed items pertain to feeds that were created by Google. Use FeedService to instead retrieve a user-created Feed. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. feed_item_id: an extension feed item ID. Returns: an ExtensionFeedItem instance. """ ga_service = client.get_service("GoogleAdsService") query = f""" SELECT extension_feed_item.id, extension_feed_item.ad_schedules, extension_feed_item.device, extension_feed_item.status, extension_feed_item.start_date_time, extension_feed_item.end_date_time, extension_feed_item.targeted_campaign, extension_feed_item.targeted_ad_group, extension_feed_item.promotion_feed_item.discount_modifier, extension_feed_item.promotion_feed_item.final_mobile_urls, extension_feed_item.promotion_feed_item.final_url_suffix, extension_feed_item.promotion_feed_item.final_urls, extension_feed_item.promotion_feed_item.language_code, extension_feed_item.promotion_feed_item.money_amount_off.amount_micros, extension_feed_item.promotion_feed_item.money_amount_off.currency_code, extension_feed_item.promotion_feed_item.occasion, extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros, extension_feed_item.promotion_feed_item.orders_over_amount.currency_code, extension_feed_item.promotion_feed_item.percent_off, extension_feed_item.promotion_feed_item.promotion_code, extension_feed_item.promotion_feed_item.promotion_end_date, extension_feed_item.promotion_feed_item.promotion_start_date, extension_feed_item.promotion_feed_item.promotion_target, extension_feed_item.promotion_feed_item.tracking_url_template FROM extension_feed_item WHERE extension_feed_item.extension_type = 'PROMOTION' AND extension_feed_item.id = {feed_item_id} LIMIT 1""" # Issue a search request to get the extension feed item contents. stream = ga_service.search_stream(customer_id=customer_id, query=query) try: stream_response = next(stream) except StopIteration: print(f"Error: No ExtensionFeedItem found with ID {feed_item_id}.") sys.exit(1) extension_feed_item = stream_response.results[0].extension_feed_item print( "Retrieved details for ad extension with ID: {extension_feed_item.id}." ) # Create a query to retrieve any URL customer parameters attached to the # extension feed item. url_custom_params_query = f""" SELECT feed_item.url_custom_parameters FROM feed_item WHERE feed_item.id = {extension_feed_item.id}""" # Issue a search request to get any URL custom parameters. stream = ga_service.search_stream( customer_id=customer_id, query=url_custom_params_query ) try: url_stream_response = next(stream) except StopIteration: print(f"Error: No FeedItems found with ID {feed_item_id}.") sys.exit(1) feed_item = url_stream_response.results[0].feed_item parameters = feed_item.url_custom_parameters num_params = len(parameters) print(f"Retrieved {num_params} attached URL custom parameters.") if num_params > 0: extension_feed_item.promotion_feed_item.url_custom_parameters.extend( parameters ) return extension_feed_item def get_targeted_campaign_ids(client, customer_id, resource_name): """Retrieves all campaigns associated with the given FeedItem resource name. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. resource_name: an extension feed item resource name. Returns: a list of campaign IDs. """ ga_service = client.get_service("GoogleAdsService") query = """ SELECT campaign.id, campaign_extension_setting.extension_feed_items FROM campaign_extension_setting WHERE campaign_extension_setting.extension_type = 'PROMOTION' AND campaign.status != 'REMOVED'""" stream = ga_service.search_stream(customer_id=customer_id, query=query) campaign_ids = [] for batch in stream: for row in batch.results: feed_items = row.campaign_extension_setting.extension_feed_items if resource_name in feed_items: print(f"Found matching campaign with ID: '{row.campaign.id}'") campaign_ids.append(row.campaign.id) return campaign_ids def get_targeted_ad_group_ids(client, customer_id, resource_name): """Retrieves all ad groups associated with the given FeedItem resource name. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. resource_name: an extension feed item resource name. Returns: a list of ad group IDs. """ ga_service = client.get_service("GoogleAdsService") query = """ SELECT ad_group.id, ad_group_extension_setting.extension_feed_items FROM ad_group_extension_setting WHERE ad_group_extension_setting.extension_type = 'PROMOTION' AND ad_group.status != 'REMOVED'""" stream = ga_service.search_stream(customer_id=customer_id, query=query) ad_group_ids = [] for batch in stream: for row in batch.results: feed_items = row.ad_group_extension_setting.extension_feed_items if resource_name in feed_items: print(f"Found matching ad group with ID: '{row.ad_group.id}'") ad_group_ids.append(row.ad_group.id) return ad_group_ids def create_promotion_asset_from_feed(client, customer_id, extension_feed_item): """Retrieves all campaigns associated with the given FeedItem resource name. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. extension_feed_item: an extension feed item. Returns: the resource name of a newly created promotion asset. """ asset_service = client.get_service("AssetService") promotion_feed_item = extension_feed_item.promotion_feed_item # Create an asset operation to start building the new promotion asset using # data from the given extension feed item. asset_operation = client.get_type("AssetOperation") asset = asset_operation.create asset.name = f"Migrated from feed item ID '{extension_feed_item.id}'" asset.tracking_url_template = promotion_feed_item.tracking_url_template asset.final_url_suffix = promotion_feed_item.final_url_suffix asset.final_urls.extend(promotion_feed_item.final_urls) asset.final_mobile_urls.extend(promotion_feed_item.final_mobile_urls) promotion_asset = asset.promotion_asset promotion_asset.promotion_target = promotion_feed_item.promotion_target promotion_asset.discount_modifier = promotion_feed_item.discount_modifier promotion_asset.redemption_start_date = ( promotion_feed_item.promotion_start_date ) promotion_asset.redemption_end_date = promotion_feed_item.promotion_end_date promotion_asset.occasion = promotion_feed_item.occasion promotion_asset.language_code = promotion_feed_item.language_code promotion_asset.ad_schedule_targets.extend(extension_feed_item.ad_schedules) # Either percent_off or money_amount_off must be set. if promotion_feed_item.percent_off > 0: # Adjust the percent off scale after copying. Extension feed items # interpret 1,000,000 as 1% and assets interpret 1,000,000 as 100% so # to migrate the correct discount value we must divide it by 100. promotion_asset.percent_off = int(promotion_feed_item.percent_off / 100) else: # If percent_off is not set then copy money_amount_off. This field is # an instance of Money in both cases, so setting the field with # copy_from is possible. Using regular assignment is also valid here. client.copy_from( promotion_asset.money_amount_off, promotion_feed_item.money_amount_off, ) # Check if promotion_code field is set if promotion_feed_item.promotion_code: promotion_asset.promotion_code = promotion_feed_item.promotion_code else: # If promotion_code is not set then copy orders_over_amount. This field # is an instance of Money in both cases, so setting the field with # copy_from is possible. Using regular assignment is also valid here. client.copy_from( promotion_asset.orders_over_amount, promotion_feed_item.orders_over_amount, ) # Set the start and end dates if set in the existing extension. if promotion_feed_item.promotion_start_date: promotion_asset.start_date = promotion_feed_item.promotion_start_date if promotion_feed_item.promotion_end_date: promotion_asset.end_date = promotion_feed_item.promotion_end_date response = asset_service.mutate_assets( customer_id=customer_id, operations=[asset_operation] ) resource_name = response.results[0].resource_name print(f"Created promotion asset with resource name: '{resource_name}'") return resource_name def associate_asset_with_campaigns( client, customer_id, promotion_asset_resource_name, campaign_ids ): """Associates the specified promotion asset with the specified campaigns. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. promotion_asset_resource_name: the resource name for a promotion asset. campaign_ids: a list of campaign IDs. """ if len(campaign_ids) == 0: print(f"Asset was not associated with any campaigns.") return campaign_service = client.get_service("CampaignService") campaign_asset_service = client.get_service("CampaignAssetService") operations = [] for campaign_id in campaign_ids: operation = client.get_type("CampaignAssetOperation") campaign_asset = operation.create campaign_asset.asset = promotion_asset_resource_name campaign_asset.field_type = client.enums.AssetFieldTypeEnum.PROMOTION campaign_asset.campaign = campaign_service.campaign_path( customer_id, campaign_id ) operations.append(operation) response = campaign_asset_service.mutate_campaign_assets( customer_id=customer_id, operations=operations ) for result in response.results: print( "Created campaign asset with resource name: " f"'{result.resource_name}'" ) def associate_asset_with_ad_groups( client, customer_id, promotion_asset_resource_name, ad_group_ids ): """Associates the specified promotion asset with the specified campaigns. Args: client: an initialized GoogleAdsClient instance. customer_id: a client customer ID. promotion_asset_resource_name: the resource name for a promotion asset. ad_groups_ids: a list of ad group IDs. """ if len(ad_group_ids) == 0: print(f"Asset was not associated with any ad groups.") return ad_group_service = client.get_service("AdGroupService") ad_group_asset_service = client.get_service("AdGroupAssetService") operations = [] for ad_group_id in ad_group_ids: operation = client.get_type("AdGroupAssetOperation") ad_group_asset = operation.create ad_group_asset.asset = promotion_asset_resource_name ad_group_asset.field_type = client.enums.AssetFieldTypeEnum.PROMOTION ad_group_asset.ad_group = ad_group_service.ad_group_path( customer_id, ad_group_id ) operations.append(operation) response = ad_group_asset_service.mutate_ad_group_assets( customer_id=customer_id, operations=operations ) for result in response.results: print( "Created ad group asset with resource name: " f"'{result.resource_name}'" ) 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="v14") parser = argparse.ArgumentParser( description="Migrates a feed-based promotion extension to an " "asset-based extension." ) # 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( "-f", "--feed_item_id", type=str, required=True, help="The ID of the ExtensionFeedItem to migrate.", ) args = parser.parse_args() try: main(googleads_client, args.customer_id, args.feed_item_id) 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 2021 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 code example retrieves the full details of a Promotion Feed-based extension and # creates a matching Promotion asset-based extension. The new Asset-based extension will # then be associated with the same campaigns and ad groups as the original Feed-based # extension. # # Once copied, you should remove the Feed-based extension; see # remove_entire_sitelink_campaign_extension_setting.rb.cs for an example. require 'optparse' require 'google/ads/google_ads' def migrate_promotion_feed_to_asset(customer_id, feed_item_id) # GoogleAdsClient will read a config file from # ENV['HOME']/google_ads_config.rb when called without parameters client = Google::Ads::GoogleAds::GoogleAdsClient.new resource_name = client.path.extension_feed_item(customer_id, feed_item_id) # Get the target extension feed item. extension_feed_item = get_extension_feed_item(client, customer_id, feed_item_id) # Get all campaign IDs associated with the extension feed item. campaign_ids = get_targeted_campaign_ids(client, customer_id, resource_name) # Get all ad group IDs associated with the extension feed item. ad_group_ids = get_targeted_ad_group_ids(client, customer_id, resource_name) # Create a new Promotion asset that matches the target extension feed item. promotion_asset_resource_name = create_promotion_asset_from_feed(client, customer_id, extension_feed_item) # Associate the new Promotion asset with the same campaigns as the original. associate_asset_with_campaigns(client, customer_id, promotion_asset_resource_name, campaign_ids) # Associate the new Promotion asset with the same ad groups as the original. associate_asset_with_ad_groups(client, customer_id, promotion_asset_resource_name, ad_group_ids) end def get_extension_feed_item(client, customer_id, feed_item_id) # Gets the requested Promotion-type extension feed item. # # Note that extension feed items pertain to feeds that were created by Google. Use # FeedService to instead retrieve a user-created Feed. google_ads_service = client.service.google_ads query = <<~QUERY SELECT extension_feed_item.id, extension_feed_item.ad_schedules, extension_feed_item.device, extension_feed_item.status, extension_feed_item.start_date_time, extension_feed_item.end_date_time, extension_feed_item.targeted_campaign, extension_feed_item.targeted_ad_group, extension_feed_item.promotion_feed_item.discount_modifier, extension_feed_item.promotion_feed_item.final_mobile_urls, extension_feed_item.promotion_feed_item.final_url_suffix, extension_feed_item.promotion_feed_item.final_urls, extension_feed_item.promotion_feed_item.language_code, extension_feed_item.promotion_feed_item.money_amount_off.amount_micros, extension_feed_item.promotion_feed_item.money_amount_off.currency_code, extension_feed_item.promotion_feed_item.occasion, extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros, extension_feed_item.promotion_feed_item.orders_over_amount.currency_code, extension_feed_item.promotion_feed_item.percent_off, extension_feed_item.promotion_feed_item.promotion_code, extension_feed_item.promotion_feed_item.promotion_end_date, extension_feed_item.promotion_feed_item.promotion_start_date, extension_feed_item.promotion_feed_item.promotion_target, extension_feed_item.promotion_feed_item.tracking_url_template FROM extension_feed_item WHERE extension_feed_item.extension_type = 'PROMOTION' AND extension_feed_item.id = #{feed_item_id} LIMIT 1 QUERY # Issue a search request to get the extension feed item contents. response = google_ads_service.search(customer_id: customer_id, query: query) extension_feed_item = response.first&.extension_feed_item if extension_feed_item.nil? raise "Error: No ExtensionFeedItem found with ID '#{feed_item_id}'." end puts "Retrieved details for ad extension with ID '#{extension_feed_item.id}'." # Create a query to retrieve any URL customer parameters attached to the # extension feed item. query = <<~QUERY SELECT feed_item.url_custom_parameters FROM feed_item WHERE feed_item.id = #{extension_feed_item.id} LIMIT 1 QUERY # Issue a search request to get any URL custom parameters. response = google_ads_service.search(customer_id: customer_id, query: query) feed_item = response.first&.feed_item if feed_item.nil? raise "Error: No FeedItem found with ID '#{feed_item_id}'." end parameters = feed_item.url_custom_parameters puts "Retrieved #{parameters.count} attached URL custom parameters." extension_feed_item.promotion_feed_item.url_custom_parameters += parameters extension_feed_item end def get_targeted_campaign_ids(client, customer_id, resource_name) # Finds and returns all of the campaigns that are associated with the specified # Promotion extension feed item. query = <<~QUERY SELECT campaign.id, campaign_extension_setting.extension_feed_items FROM campaign_extension_setting WHERE campaign_extension_setting.extension_type = 'PROMOTION' AND campaign.status != 'REMOVED' QUERY responses = client.service.google_ads.search_stream(customer_id: customer_id, query: query) campaign_ids = [] responses.each do |response| response.results.each do |row| feed_items = row.campaign_extension_setting.extension_feed_items if feed_items.include?(resource_name) puts "Found matching campaign with ID '#{row.campaign.id}'." campaign_ids << row.campaign.id end end end campaign_ids end def get_targeted_ad_group_ids(client, customer_id, resource_name) # Finds and returns all of the ad groups that are associated with the specified # Promotion extension feed item. query = <<~QUERY SELECT ad_group.id, ad_group_extension_setting.extension_feed_items FROM ad_group_extension_setting WHERE ad_group_extension_setting.extension_type = 'PROMOTION' AND ad_group.status != 'REMOVED' QUERY responses = client.service.google_ads.search_stream(customer_id: customer_id, query: query) ad_group_ids = [] responses.each do |response| response.results.each do |row| feed_items = row.ad_group_extension_setting.extension_feed_items if feed_items.include?(resource_name) puts "Found matching ad group with ID: '#{row.ad_group.id}'" ad_group_ids << row.ad_group.id end end end ad_group_ids end def create_promotion_asset_from_feed(client, customer_id, extension_feed_item) # Create a Promotion asset that copies values from the specified extension feed item. asset_service = client.service.asset promotion_feed_item = extension_feed_item.promotion_feed_item # Create an asset operation to start building the new promotion asset using # data from the given extension feed item. asset_operation = client.operation.create_resource.asset do |asset| asset.name = "Migrated from feed item ID '#{extension_feed_item.id}'" asset.tracking_url_template = promotion_feed_item.tracking_url_template asset.final_url_suffix = promotion_feed_item.final_url_suffix asset.final_urls += promotion_feed_item.final_urls asset.final_mobile_urls += promotion_feed_item.final_mobile_urls # Create the Promotion asset. asset.promotion_asset = client.resource.promotion_asset do |pa| pa.promotion_target = promotion_feed_item.promotion_target pa.discount_modifier = promotion_feed_item.discount_modifier pa.redemption_start_date = promotion_feed_item.promotion_start_date pa.redemption_end_date = promotion_feed_item.promotion_end_date pa.occasion = promotion_feed_item.occasion pa.language_code = promotion_feed_item.language_code pa.ad_schedule_targets += extension_feed_item.ad_schedules # Either percent_off or money_amount_off must be set. if promotion_feed_item.percent_off.positive? # Adjust the percent off scale after copying. pa.percent_off = int(promotion_feed_item.percent_off / 100) else # If percent_off is not set then copy money_amount_off. This field is # an instance of Money in both cases, so setting the field with # copy_from is possible. Using regular assignment is also valid here. pa.money_amount_off = promotion_feed_item.money_amount_off end # Either promotion_code or orders_over_amount must be set. if promotion_feed_item.promotion_code.empty? pa.orders_over_amount = promotion_feed_item.orders_over_amount else pa.promotion_code = promotion_feed_item.promotion_code end # Set the start and end dates if set in the existing extension. unless promotion_feed_item.promotion_start_date.empty? pa.start_date = promotion_feed_item.promotion_start_date end unless promotion_feed_item.promotion_end_date.empty? pa.end_date = promotion_feed_item.promotion_end_date end end end response = asset_service.mutate_assets(customer_id: customer_id, operations: [asset_operation]) resource_name = response.results.first.resource_name puts "Created promotion asset with resource name: '#{resource_name}'" resource_name end def associate_asset_with_campaigns(client, customer_id, promotion_asset_resource_name, campaign_ids) # Associates the specified promotion asset with the specified campaigns. if campaign_ids.empty? puts 'Asset was not associated with any campaigns.' return end operations = campaign_ids.map do |campaign_id| client.operation.create_resource.campaign_asset do |ca| ca.asset = promotion_asset_resource_name ca.field_type = :PROMOTION ca.campaign = client.path.campaign(customer_id, campaign_id) end end response = client.service.campaign_asset.mutate_campaign_assets( customer_id: customer_id, operations: operations, ) response.results.each do |result| puts "Created campaign asset with resource name '#{result.resource_name}'." end end def associate_asset_with_ad_groups(client, customer_id, promotion_asset_resource_name, ad_group_ids) # Associates the specified promotion asset with the specified ad groups. if ad_group_ids.empty? puts 'Asset was not associated with any ad groups.' return end operations = ad_group_ids.map do |ad_group_id| client.operation.create_resource.ad_group_asset do |aga| aga.asset = promotion_asset_resource_name aga.field_type = :PROMOTION aga.ad_group = client.path.ad_group(customer_id, ad_group_id) end end response = client.service.ad_group_asset.mutate_ad_group_assets( customer_id: customer_id, operations: operations, ) response.results.each do |result| puts "Created ad group asset with resource name '#{result.resource_name}'." end end if __FILE__ == $0 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[:feed_item_id] = 'INSERT_FEED_ITEM_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('-f', '--feed-item-id FEED-ITEM-ID', String, 'Feed Item ID') do |v| options[:feed_item_id] = v end opts.separator '' opts.separator 'Help:' opts.on_tail('-h', '--help', 'Show this message') do puts opts exit end end.parse! begin migrate_promotion_feed_to_asset(options.fetch(:customer_id).tr('-', ''), options.fetch(:feed_item_id)) 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 2021, 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 code example retrieves the full details of a Promotion Feed-based # extension and creates a matching Promotion asset-based extension. The new # Asset-based extension will then be associated with the same campaigns and ad # groups as the original Feed-based extension. # # Once copied, you should remove the Feed-based extension; see # remove_entire_sitelink_campaign_extension_setting.pl for an example. use strict; use warnings; use utf8; use FindBin qw($Bin); use lib "$Bin/../../lib"; use Google::Ads::GoogleAds::Client; use Google::Ads::GoogleAds::Utils::GoogleAdsHelper; use Google::Ads::GoogleAds::Utils::SearchStreamHandler; use Google::Ads::GoogleAds::V14::Resources::AdGroupAsset; use Google::Ads::GoogleAds::V14::Resources::Asset; use Google::Ads::GoogleAds::V14::Resources::CampaignAsset; use Google::Ads::GoogleAds::V14::Common::Money; use Google::Ads::GoogleAds::V14::Common::PromotionAsset; use Google::Ads::GoogleAds::V14::Enums::ExtensionTypeEnum qw(PROMOTION); use Google::Ads::GoogleAds::V14::Services::AdGroupAssetService::AdGroupAssetOperation; use Google::Ads::GoogleAds::V14::Services::AssetService::AssetOperation; use Google::Ads::GoogleAds::V14::Services::CampaignAssetService::CampaignAssetOperation; use Google::Ads::GoogleAds::V14::Services::GoogleAdsService::SearchGoogleAdsStreamRequest; use Google::Ads::GoogleAds::V14::Utils::ResourceNames; use Getopt::Long qw(:config auto_help); use Pod::Usage; use Cwd qw(abs_path); # 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 $feed_item_id = "INSERT_FEED_ITEM_ID_HERE"; sub migrate_promotion_feed_to_asset { my ($api_client, $customer_id, $feed_item_id) = @_; # Get the GoogleAdsService client. my $google_ads_service = $api_client->GoogleAdsService(); my $extension_feed_item_resource_name = Google::Ads::GoogleAds::V14::Utils::ResourceNames::extension_feed_item( $customer_id, $feed_item_id); # Get the target extension feed item. my $extension_feed_item = get_extension_feed_item($google_ads_service, $customer_id, $feed_item_id); # Get all campaign IDs associated with the extension feed item. my @campaign_ids = get_targeted_campaign_ids($google_ads_service, $customer_id, $extension_feed_item_resource_name); # Get all ad group IDs associated with the extension feed item. my @ad_group_ids = get_targeted_ad_group_ids($google_ads_service, $customer_id, $extension_feed_item_resource_name); # Create a new Promotion asset that matches the target extension feed item. my $promotion_asset_resource_name = create_promotion_asset_from_feed($api_client, $customer_id, $extension_feed_item); # Associate the new Promotion asset with the same campaigns as the original. associate_asset_with_campaigns($api_client, $customer_id, $promotion_asset_resource_name, @campaign_ids); # Associate the new Promotion asset with the same ad groups as the original. associate_asset_with_ad_groups($api_client, $customer_id, $promotion_asset_resource_name, @ad_group_ids); return 1; } # Gets the requested Promotion-type extension feed item. # # Note that extension feed items pertain to feeds that were created by Google. # Use FeedService to instead retrieve a user-created Feed. sub get_extension_feed_item { my ($google_ads_service, $customer_id, $feed_item_id) = @_; # Create a query that will retrieve the requested Promotion-type extension # feed item and ensure that all fields are populated. my $extension_feed_item_query = " SELECT extension_feed_item.id, extension_feed_item.ad_schedules, extension_feed_item.device, extension_feed_item.status, extension_feed_item.start_date_time, extension_feed_item.end_date_time, extension_feed_item.targeted_campaign, extension_feed_item.targeted_ad_group, extension_feed_item.promotion_feed_item.discount_modifier, extension_feed_item.promotion_feed_item.final_mobile_urls, extension_feed_item.promotion_feed_item.final_url_suffix, extension_feed_item.promotion_feed_item.final_urls, extension_feed_item.promotion_feed_item.language_code, extension_feed_item.promotion_feed_item.money_amount_off.amount_micros, extension_feed_item.promotion_feed_item.money_amount_off.currency_code, extension_feed_item.promotion_feed_item.occasion, extension_feed_item.promotion_feed_item.orders_over_amount.amount_micros, extension_feed_item.promotion_feed_item.orders_over_amount.currency_code, extension_feed_item.promotion_feed_item.percent_off, extension_feed_item.promotion_feed_item.promotion_code, extension_feed_item.promotion_feed_item.promotion_end_date, extension_feed_item.promotion_feed_item.promotion_start_date, extension_feed_item.promotion_feed_item.promotion_target, extension_feed_item.promotion_feed_item.tracking_url_template FROM extension_feed_item WHERE extension_feed_item.extension_type = 'PROMOTION' AND extension_feed_item.id = $feed_item_id LIMIT 1"; my $fetched_extension_feed_item; # Issue a search request to get the extension feed item contents. my $search_stream_request = Google::Ads::GoogleAds::V14::Services::GoogleAdsService::SearchGoogleAdsStreamRequest ->new({ customerId => $customer_id, query => $extension_feed_item_query }); my $search_stream_handler = Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({ service => $google_ads_service, request => $search_stream_request }); $search_stream_handler->process_contents( sub { my $google_ads_row = shift; $fetched_extension_feed_item = $google_ads_row->{extensionFeedItem}; }); # Create a query to retrieve any URL customer parameters attached to the # feed item. my $url_custom_parameters_query = " SELECT feed_item.url_custom_parameters FROM feed_item WHERE feed_item.id = $feed_item_id"; # Issue a search request to get any URL custom parameters. $search_stream_request = Google::Ads::GoogleAds::V14::Services::GoogleAdsService::SearchGoogleAdsStreamRequest ->new({ customerId => $customer_id, query => $url_custom_parameters_query }); $search_stream_handler = Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({ service => $google_ads_service, request => $search_stream_request }); $search_stream_handler->process_contents( sub { my $google_ads_row = shift; push @{$fetched_extension_feed_item->{promotionFeedItem}{urlCustomParameters} }, @{$google_ads_row->{feedItem}{urlCustomParameters}}; }); printf "Retrieved details for ad extension with ID %d.\n", $fetched_extension_feed_item->{id}; return $fetched_extension_feed_item; } # Finds and returns all of the campaigns that are associated with the specified # Promotion extension feed item. sub get_targeted_campaign_ids { my ($google_ads_service, $customer_id, $extension_feed_item_resource_name) = @_; my @campaign_ids; my $query = " SELECT campaign.id, campaign_extension_setting.extension_feed_items FROM campaign_extension_setting WHERE campaign_extension_setting.extension_type = 'PROMOTION' AND campaign.status != 'REMOVED'"; my $search_stream_request = Google::Ads::GoogleAds::V14::Services::GoogleAdsService::SearchGoogleAdsStreamRequest ->new({ customerId => $customer_id, query => $query }); my $search_stream_handler = Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({ service => $google_ads_service, request => $search_stream_request }); $search_stream_handler->process_contents( sub { my $google_ads_row = shift; # Add the campaign ID to the list of IDs if the extension feed item # is associated with this extension setting. if (grep { $_ eq $extension_feed_item_resource_name } @{$google_ads_row->{campaignExtensionSetting}{extensionFeedItems}}) { printf "Found matching campaign with ID $google_ads_row->{campaign}{id}.\n"; push @campaign_ids, $google_ads_row->{campaign}{id}; } }); return @campaign_ids; } # Finds and returns all of the ad groups that are associated with the specified # Promotion extension feed item. sub get_targeted_ad_group_ids { my ($google_ads_service, $customer_id, $extension_feed_item_resource_name) = @_; my @ad_group_ids; my $query = " SELECT ad_group.id, ad_group_extension_setting.extension_feed_items FROM ad_group_extension_setting WHERE ad_group_extension_setting.extension_type = 'PROMOTION' AND ad_group.status != 'REMOVED'"; my $search_stream_request = Google::Ads::GoogleAds::V14::Services::GoogleAdsService::SearchGoogleAdsStreamRequest ->new({ customerId => $customer_id, query => $query }); my $search_stream_handler = Google::Ads::GoogleAds::Utils::SearchStreamHandler->new({ service => $google_ads_service, request => $search_stream_request }); $search_stream_handler->process_contents( sub { my $google_ads_row = shift; # Add the ad group ID to the list of IDs if the extension feed item # is associated with this extension setting. if (grep { $_ eq $extension_feed_item_resource_name } @{$google_ads_row->{adGroupExtensionSetting}{extensionFeedItems}}) { printf "Found matching ad group with ID $google_ads_row->{adGroup}{id}.\n"; push @ad_group_ids, $google_ads_row->{adGroup}{id}; } }); return @ad_group_ids; } # Create a Promotion asset that copies values from the specified extension feed # item. sub create_promotion_asset_from_feed { my ($api_client, $customer_id, $extension_feed_item) = @_; my $promotion_feed_item = $extension_feed_item->{promotionFeedItem}; # Create the Promotion asset. my $asset = Google::Ads::GoogleAds::V14::Resources::Asset->new({ name => "Migrated from feed item #" . $extension_feed_item->{id}, trackingUrlTemplate => $promotion_feed_item->{trackingUrlTemplate}, finalUrlSuffix => $promotion_feed_item->{finalUrlSuffix}, promotionAsset => Google::Ads::GoogleAds::V14::Common::PromotionAsset->new({ promotionTarget => $promotion_feed_item->{promotionTarget}, discountModifier => $promotion_feed_item->{discountModifier}, redemptionStartDate => $promotion_feed_item->{promotionStartDate}, redemptionEndDate => $promotion_feed_item->{promotionEndDate}, occasion => $promotion_feed_item->{occasion}, languageCode => $promotion_feed_item->{languageCode}})}); push @{$asset->{finalUrls}}, @{$promotion_feed_item->{finalUrls}}; # Copy optional fields if present in the existing extension. if (defined($extension_feed_item->{adSchedules})) { push @{$asset->{promotionAsset}{adScheduleTargets}}, @{$extension_feed_item->{adSchedules}}; } if (defined($promotion_feed_item->{finalMobileUrls})) { push @{$asset->{finalMobileUrls}}, @{$promotion_feed_item->{finalMobileUrls}}; } if (defined($promotion_feed_item->{urlCustomParameters})) { push @{$asset->{urlCustomParameters}}, @{$promotion_feed_item->{urlCustomParameters}}; } # Either percentOff or moneyAmountOff must be set. if (defined($promotion_feed_item->{percentOff})) { # Adjust the percent off scale when copying. $asset->{promotionAsset}{percentOff} = $promotion_feed_item->{percentOff} / 100; } else { $asset->{promotionAsset}{moneyAmountOff} = Google::Ads::GoogleAds::V14::Common::Money->new({ amountMicros => $promotion_feed_item->{moneyAmountOff}{amountMicros}, currencyCode => $promotion_feed_item->{moneyAmountOff}{currencyCode}}); } # Either promotionCode or ordersOverAmount must be set. if (defined($promotion_feed_item->{promotionCode})) { $asset->{promotionAsset}{promotionCode} = $promotion_feed_item->{promotionCode}; } else { $asset->{promotionAsset}{ordersOverAmount} = Google::Ads::GoogleAds::V14::Common::Money->new({ amountMicros => $promotion_feed_item->{ordersOverAmount}{amountMicros}, currencyCode => $promotion_feed_item->{ordersOverAmount}{currencyCode}} ); } # Set the start and end dates if set in the existing extension. if (defined($extension_feed_item->{startDateTime})) { $asset->{promotionAsset}{startDate} = substr($extension_feed_item->{startDateTime}, 0, index($extension_feed_item->{startDateTime}, ' ')); } if (defined($extension_feed_item->{endDateTime})) { $asset->{promotionAsset}{endDate} = substr($extension_feed_item->{endDateTime}, 0, index($extension_feed_item->{endDateTime}, ' ')); } # Build an operation to create the Promotion asset. my $operation = Google::Ads::GoogleAds::V14::Services::AssetService::AssetOperation->new({ create => $asset }); # Issue the request and return the resource name of the new Promotion asset. my $response = $api_client->AssetService()->mutate({ customerId => $customer_id, operations => [$operation]}); printf "Created Promotion asset with resource name '%s'.\n", $response->{results}[0]{resourceName}; return $response->{results}[0]{resourceName}; } # Associates the specified Promotion asset with the specified campaigns. sub associate_asset_with_campaigns { my ($api_client, $customer_id, $promotion_asset_resource_name, @campaign_ids) = @_; if (scalar(@campaign_ids) == 0) { printf "Asset was not associated with any campaigns.\n"; return (); } my $operations = []; foreach my $campaign_id (@campaign_ids) { my $campaign_asset = Google::Ads::GoogleAds::V14::Resources::CampaignAsset->new({ asset => $promotion_asset_resource_name, fieldType => PROMOTION, campaign => Google::Ads::GoogleAds::V14::Utils::ResourceNames::campaign( $customer_id, $campaign_id )}); my $operation = Google::Ads::GoogleAds::V14::Services::CampaignAssetService::CampaignAssetOperation ->new({ create => $campaign_asset }); push @$operations, $operation; } my $response = $api_client->CampaignAssetService()->mutate({ customerId => $customer_id, operations => $operations }); foreach my $result (@{$response->{results}}) { printf "Created campaign asset with resource name '%s'.\n", $result->{resourceName}; } } # Associates the specified Promotion asset with the specified ad groups. sub associate_asset_with_ad_groups { my ($api_client, $customer_id, $promotion_asset_resource_name, @ad_group_ids) = @_; if (scalar(@ad_group_ids) == 0) { printf "Asset was not associated with any ad groups.\n"; return (); } my $operations = []; foreach my $ad_group_id (@ad_group_ids) { my $ad_group_asset = Google::Ads::GoogleAds::V14::Resources::AdGroupAsset->new({ asset => $promotion_asset_resource_name, fieldType => PROMOTION, adGroup => Google::Ads::GoogleAds::V14::Utils::ResourceNames::ad_group( $customer_id, $ad_group_id )}); my $operation = Google::Ads::GoogleAds::V14::Services::AdGroupAssetService::AdGroupAssetOperation ->new({ create => $ad_group_asset }); push @$operations, $operation; } my $response = $api_client->AdGroupAssetService()->mutate({ customerId => $customer_id, operations => $operations }); foreach my $result (@{$response->{results}}) { printf "Created ad group asset with resource name '%s'.\n", $result->{resourceName}; } } # 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, "feed_item_id=i" => \$feed_item_id ); # 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, $feed_item_id); # Call the example. migrate_promotion_feed_to_asset($api_client, $customer_id =~ s/-//gr, $feed_item_id); =pod =head1 NAME migrate_promotion_feed_to_asset =head1 DESCRIPTION This code example retrieves the full details of a Promotion Feed-based extension and creates a matching Promotion asset-based extension. The new Asset-based extension will then be associated with the same campaigns and ad groups as the original Feed-based extension. Once copied, you should remove the Feed-based extension; see remove_entire_sitelink_campaign_extension_setting.pl for an example. =head1 SYNOPSIS migrate_promotion_feed_to_asset.pl [options] -help Show the help message. -customer_id The Google Ads customer ID. -feed_item_id ID of the ExtensionFeedItem to migrate. =cut