Przenieś plik danych o promocjach do zasobu

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.v11.common.PromotionAsset;
import com.google.ads.googleads.v11.common.PromotionFeedItem;
import com.google.ads.googleads.v11.enums.AssetFieldTypeEnum.AssetFieldType;
import com.google.ads.googleads.v11.errors.GoogleAdsError;
import com.google.ads.googleads.v11.errors.GoogleAdsException;
import com.google.ads.googleads.v11.resources.AdGroup;
import com.google.ads.googleads.v11.resources.AdGroupAsset;
import com.google.ads.googleads.v11.resources.AdGroupExtensionSetting;
import com.google.ads.googleads.v11.resources.Asset;
import com.google.ads.googleads.v11.resources.Campaign;
import com.google.ads.googleads.v11.resources.CampaignAsset;
import com.google.ads.googleads.v11.resources.CampaignExtensionSetting;
import com.google.ads.googleads.v11.resources.ExtensionFeedItem;
import com.google.ads.googleads.v11.resources.FeedItem;
import com.google.ads.googleads.v11.services.AdGroupAssetOperation;
import com.google.ads.googleads.v11.services.AdGroupAssetServiceClient;
import com.google.ads.googleads.v11.services.AssetOperation;
import com.google.ads.googleads.v11.services.AssetServiceClient;
import com.google.ads.googleads.v11.services.CampaignAssetOperation;
import com.google.ads.googleads.v11.services.CampaignAssetServiceClient;
import com.google.ads.googleads.v11.services.GoogleAdsRow;
import com.google.ads.googleads.v11.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v11.services.MutateAdGroupAssetResult;
import com.google.ads.googleads.v11.services.MutateAdGroupAssetsResponse;
import com.google.ads.googleads.v11.services.MutateAssetsResponse;
import com.google.ads.googleads.v11.services.MutateCampaignAssetResult;
import com.google.ads.googleads.v11.services.MutateCampaignAssetsResponse;
import com.google.ads.googleads.v11.services.SearchGoogleAdsStreamRequest;
import com.google.ads.googleads.v11.services.SearchGoogleAdsStreamResponse;
import com.google.ads.googleads.v11.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.V11.Common;
using Google.Ads.GoogleAds.V11.Enums;
using Google.Ads.GoogleAds.V11.Errors;
using Google.Ads.GoogleAds.V11.Resources;
using Google.Ads.GoogleAds.V11.Services;
using Google.Protobuf.Collections;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Google.Ads.GoogleAds.Examples.V11
{
    /// <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.V11.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.V11.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.V11
                .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.V11
                .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\V11\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V11\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V11\GoogleAdsException;
use Google\Ads\GoogleAds\Lib\V11\GoogleAdsServerStreamDecorator;
use Google\Ads\GoogleAds\Util\V11\ResourceNames;
use Google\Ads\GoogleAds\V11\Common\Money;
use Google\Ads\GoogleAds\V11\Common\PromotionAsset;
use Google\Ads\GoogleAds\V11\Enums\AssetFieldTypeEnum\AssetFieldType;
use Google\Ads\GoogleAds\V11\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V11\Resources\AdGroupAsset;
use Google\Ads\GoogleAds\V11\Resources\Asset;
use Google\Ads\GoogleAds\V11\Resources\CampaignAsset;
use Google\Ads\GoogleAds\V11\Resources\ExtensionFeedItem;
use Google\Ads\GoogleAds\V11\Resources\FeedItem;
use Google\Ads\GoogleAds\V11\Services\AdGroupAssetOperation;
use Google\Ads\GoogleAds\V11\Services\AssetOperation;
use Google\Ads\GoogleAds\V11\Services\CampaignAssetOperation;
use Google\Ads\GoogleAds\V11\Services\GoogleAdsRow;
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)
            ->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($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($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($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($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($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($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($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="v11")

    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::V11::Resources::AdGroupAsset;
use Google::Ads::GoogleAds::V11::Resources::Asset;
use Google::Ads::GoogleAds::V11::Resources::CampaignAsset;
use Google::Ads::GoogleAds::V11::Common::Money;
use Google::Ads::GoogleAds::V11::Common::PromotionAsset;
use Google::Ads::GoogleAds::V11::Enums::ExtensionTypeEnum qw(PROMOTION);
use
  Google::Ads::GoogleAds::V11::Services::AdGroupAssetService::AdGroupAssetOperation;
use Google::Ads::GoogleAds::V11::Services::AssetService::AssetOperation;
use
  Google::Ads::GoogleAds::V11::Services::CampaignAssetService::CampaignAssetOperation;
use
  Google::Ads::GoogleAds::V11::Services::GoogleAdsService::SearchGoogleAdsStreamRequest;
use Google::Ads::GoogleAds::V11::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::V11::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::V11::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::V11::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::V11::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::V11::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::V11::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::V11::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::V11::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::V11::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::V11::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::V11::Resources::CampaignAsset->new({
        asset     => $promotion_asset_resource_name,
        fieldType => PROMOTION,
        campaign => Google::Ads::GoogleAds::V11::Utils::ResourceNames::campaign(
          $customer_id, $campaign_id
        )});

    my $operation =
      Google::Ads::GoogleAds::V11::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::V11::Resources::AdGroupAsset->new({
        asset     => $promotion_asset_resource_name,
        fieldType => PROMOTION,
        adGroup => Google::Ads::GoogleAds::V11::Utils::ResourceNames::ad_group(
          $customer_id, $ad_group_id
        )});

    my $operation =
      Google::Ads::GoogleAds::V11::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