adwords.axis.v201506.migration.MigrateToExtensionSettings.java Source code

Java tutorial

Introduction

Here is the source code for adwords.axis.v201506.migration.MigrateToExtensionSettings.java

Source

// Copyright 2014 Google Inc. All Rights Reserved.
//
// 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 adwords.axis.v201506.migration;

import com.google.api.ads.adwords.axis.factory.AdWordsServices;
import com.google.api.ads.adwords.axis.v201506.cm.AttributeFieldMapping;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignExtensionSetting;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignExtensionSettingOperation;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignExtensionSettingServiceInterface;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignFeed;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignFeedOperation;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignFeedPage;
import com.google.api.ads.adwords.axis.v201506.cm.CampaignFeedServiceInterface;
import com.google.api.ads.adwords.axis.v201506.cm.ConstantOperand;
import com.google.api.ads.adwords.axis.v201506.cm.ExtensionFeedItem;
import com.google.api.ads.adwords.axis.v201506.cm.ExtensionSetting;
import com.google.api.ads.adwords.axis.v201506.cm.ExtensionSettingPlatform;
import com.google.api.ads.adwords.axis.v201506.cm.Feed;
import com.google.api.ads.adwords.axis.v201506.cm.FeedItem;
import com.google.api.ads.adwords.axis.v201506.cm.FeedItemAttributeValue;
import com.google.api.ads.adwords.axis.v201506.cm.FeedItemOperation;
import com.google.api.ads.adwords.axis.v201506.cm.FeedItemPage;
import com.google.api.ads.adwords.axis.v201506.cm.FeedItemScheduling;
import com.google.api.ads.adwords.axis.v201506.cm.FeedItemServiceInterface;
import com.google.api.ads.adwords.axis.v201506.cm.FeedMapping;
import com.google.api.ads.adwords.axis.v201506.cm.FeedMappingPage;
import com.google.api.ads.adwords.axis.v201506.cm.FeedMappingServiceInterface;
import com.google.api.ads.adwords.axis.v201506.cm.FeedPage;
import com.google.api.ads.adwords.axis.v201506.cm.FeedServiceInterface;
import com.google.api.ads.adwords.axis.v201506.cm.FeedType;
import com.google.api.ads.adwords.axis.v201506.cm.Function;
import com.google.api.ads.adwords.axis.v201506.cm.FunctionArgumentOperand;
import com.google.api.ads.adwords.axis.v201506.cm.FunctionOperand;
import com.google.api.ads.adwords.axis.v201506.cm.FunctionOperator;
import com.google.api.ads.adwords.axis.v201506.cm.Operator;
import com.google.api.ads.adwords.axis.v201506.cm.RequestContextOperand;
import com.google.api.ads.adwords.axis.v201506.cm.RequestContextOperandContextType;
import com.google.api.ads.adwords.axis.v201506.cm.SitelinkFeedItem;
import com.google.api.ads.adwords.axis.v201506.cm.UrlList;
import com.google.api.ads.adwords.lib.client.AdWordsSession;
import com.google.api.ads.common.lib.auth.OfflineCredentials;
import com.google.api.ads.common.lib.auth.OfflineCredentials.Api;
import com.google.api.client.auth.oauth2.Credential;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * This code example migrates your feed based sitelinks at campaign level to use extension settings.
 * To learn more about extensionsettings, see
 * https://developers.google.com/adwords/api/docs/guides/extension-settings. To learn more about
 * migrating Feed based extensions to extension settings, see
 * https://developers.google.com/adwords/api/docs/guides/migrate-to-extension-settings.
 *
 * Tags: FeedService.query, FeedMappingService.query, FeedItemService.query 
 * Tags: CampaignExtensionSettingService.mutate, CampaignFeedService.query
 * Tags: CampaignFeedService.mutate
 *
 * Category: adx-exclude
 *
 * @author Josh Radcliff
 */
public class MigrateToExtensionSettings {

    private static final int PAGE_SIZE = 100;

    // See the Placeholder reference page for a list of all the placeholder types and fields.
    // https://developers.google.com/adwords/api/docs/appendix/placeholders
    private static final int PLACEHOLDER_SITELINKS = 1;

    // See the Placeholder reference page for a list of all the placeholder types and fields.
    // https://developers.google.com/adwords/api/docs/appendix/placeholders
    private static final int PLACEHOLDER_FIELD_SITELINK_LINK_TEXT = 1;
    private static final int PLACEHOLDER_FIELD_SITELINK_URL = 2;
    private static final int PLACEHOLDER_FIELD_LINE_2_TEXT = 3;
    private static final int PLACEHOLDER_FIELD_LINE_3_TEXT = 4;
    private static final int PLACEHOLDER_FIELD_FINAL_URLS = 5;
    private static final int PLACEHOLDER_FIELD_FINAL_MOBILE_URLS = 6;
    private static final int PLACEHOLDER_FIELD_TRACKING_URL_TEMPLATE = 7;

    public static void main(String[] args) throws Exception {
        // Generate a refreshable OAuth2 credential similar to a ClientLogin token
        // that can be used in place of a service account.
        Credential oAuth2Credential = new OfflineCredentials.Builder().forApi(Api.ADWORDS).fromFile().build()
                .generateCredential();

        // Construct an AdWordsSession.
        AdWordsSession session = new AdWordsSession.Builder().fromFile().withOAuth2Credential(oAuth2Credential)
                .build();

        AdWordsServices adWordsServices = new AdWordsServices();

        runExample(adWordsServices, session);
    }

    public static void runExample(AdWordsServices adWordsServices, AdWordsSession session) throws Exception {
        // Get all of the feeds for the session's account.
        List<Feed> feeds = getFeeds(adWordsServices, session);

        for (Feed feed : feeds) {
            // Retrieve all the sitelinks from the current feed.
            Map<Long, SiteLinkFromFeed> feedItems = getSiteLinksFromFeed(adWordsServices, session, feed);

            // Get all the instances where a sitelink from this feed has been added to a campaign.
            List<CampaignFeed> campaignFeeds = getCampaignFeeds(adWordsServices, session, feed,
                    PLACEHOLDER_SITELINKS);

            Set<Long> allFeedItemsToDelete = Sets.newHashSet();
            for (CampaignFeed campaignFeed : campaignFeeds) {
                // Retrieve the sitelinks that have been associated with this campaign.
                Set<Long> feedItemIds = getFeedItemIdsForCampaign(campaignFeed);

                ExtensionSettingPlatform platformRestrictions = getPlatformRestictionsForCampaign(campaignFeed);

                if (feedItemIds.isEmpty()) {
                    System.out.printf(
                            "Migration skipped for campaign feed with campaign ID %d "
                                    + "and feed ID %d because no mapped feed item IDs were found in the "
                                    + "campaign feed's matching function.%n",
                            campaignFeed.getCampaignId(), campaignFeed.getFeedId());
                } else {
                    // Delete the campaign feed that associates the sitelinks from the feed to the campaign.
                    deleteCampaignFeed(adWordsServices, session, campaignFeed);

                    // Create extension settings instead of sitelinks.
                    createExtensionSetting(adWordsServices, session, feedItems, campaignFeed, feedItemIds,
                            platformRestrictions);

                    // Mark the sitelinks from the feed for deletion.
                    allFeedItemsToDelete.addAll(feedItemIds);
                }
            }

            // Delete all the sitelinks from the feed.
            deleteOldFeedItems(adWordsServices, session, allFeedItemsToDelete, feed);
        }
    }

    /**
     * Gets the site links from a feed.
     *
     * @return a map of feed item ID to SiteLinkFromFeed
     */
    private static Map<Long, SiteLinkFromFeed> getSiteLinksFromFeed(AdWordsServices adWordsServices,
            AdWordsSession session, Feed feed) throws Exception {
        // Retrieve the feed's attribute mapping.
        Multimap<Long, Integer> feedMappings = getFeedMapping(adWordsServices, session, feed,
                PLACEHOLDER_SITELINKS);

        Map<Long, SiteLinkFromFeed> feedItems = Maps.newHashMap();

        for (FeedItem feedItem : getFeedItems(adWordsServices, session, feed)) {
            SiteLinkFromFeed siteLinkFromFeed = new SiteLinkFromFeed();

            for (FeedItemAttributeValue attributeValue : feedItem.getAttributeValues()) {
                // Skip this attribute if it hasn't been mapped to a field.
                if (!feedMappings.containsKey(attributeValue.getFeedAttributeId())) {
                    continue;
                }

                for (Integer fieldId : feedMappings.get(attributeValue.getFeedAttributeId())) {
                    switch (fieldId) {
                    case PLACEHOLDER_FIELD_SITELINK_LINK_TEXT:
                        siteLinkFromFeed.text = attributeValue.getStringValue();
                        break;
                    case PLACEHOLDER_FIELD_SITELINK_URL:
                        siteLinkFromFeed.url = attributeValue.getStringValue();
                        break;
                    case PLACEHOLDER_FIELD_FINAL_URLS:
                        siteLinkFromFeed.finalUrls = attributeValue.getStringValues();
                        break;
                    case PLACEHOLDER_FIELD_FINAL_MOBILE_URLS:
                        siteLinkFromFeed.finalMobileUrls = attributeValue.getStringValues();
                        break;
                    case PLACEHOLDER_FIELD_TRACKING_URL_TEMPLATE:
                        siteLinkFromFeed.trackingUrlTemplate = attributeValue.getStringValue();
                        break;
                    case PLACEHOLDER_FIELD_LINE_2_TEXT:
                        siteLinkFromFeed.line2 = attributeValue.getStringValue();
                        break;
                    case PLACEHOLDER_FIELD_LINE_3_TEXT:
                        siteLinkFromFeed.line3 = attributeValue.getStringValue();
                        break;
                    default:
                        // Ignore attributes that do not map to a predefined placeholder field.
                        break;
                    }
                }
            }
            siteLinkFromFeed.scheduling = feedItem.getScheduling();

            feedItems.put(feedItem.getFeedItemId(), siteLinkFromFeed);
        }

        return feedItems;
    }

    /**
     * Gets the feed mapping for a feed.
     *
     * @return a multimap from feed attribute ID to the set of field IDs mapped to the attribute
     */
    private static Multimap<Long, Integer> getFeedMapping(AdWordsServices adWordsServices, AdWordsSession session,
            Feed feed, long placeholderType) throws Exception {
        // Get the FeedMappingService.
        FeedMappingServiceInterface feedMappingService = adWordsServices.get(session,
                FeedMappingServiceInterface.class);
        String query = String
                .format("SELECT FeedMappingId, AttributeFieldMappings WHERE FeedId = %d and PlaceholderType = %d "
                        + "AND Status = 'ENABLED'", feed.getId(), placeholderType);

        Multimap<Long, Integer> attributeMappings = HashMultimap.create();
        int offset = 0;
        FeedMappingPage feedMappingPage;

        do {
            String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
            feedMappingPage = feedMappingService.query(pageQuery);
            if (feedMappingPage.getEntries() != null) {
                // Normally, a feed attribute is mapped only to one field. However, you may map it to more
                // than one field if needed.
                for (FeedMapping feedMapping : feedMappingPage.getEntries()) {
                    for (AttributeFieldMapping attributeMapping : feedMapping.getAttributeFieldMappings()) {
                        attributeMappings.put(attributeMapping.getFeedAttributeId(), attributeMapping.getFieldId());
                    }
                }
            }
            offset += PAGE_SIZE;
        } while (offset < feedMappingPage.getTotalNumEntries());

        return attributeMappings;
    }

    /**
     * Returns a list of all enabled feeds.
     */
    private static List<Feed> getFeeds(AdWordsServices adWordsServices, AdWordsSession session) throws Exception {
        FeedServiceInterface feedService = adWordsServices.get(session, FeedServiceInterface.class);
        String query = "SELECT Id, Name, Attributes WHERE Origin = 'USER' AND FeedStatus = 'ENABLED'";

        List<Feed> feeds = Lists.newArrayList();
        int offset = 0;
        FeedPage feedPage;

        do {
            String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
            feedPage = feedService.query(pageQuery);
            if (feedPage.getEntries() != null) {
                feeds.addAll(Arrays.asList(feedPage.getEntries()));
            }
            offset += PAGE_SIZE;
        } while (offset < feedPage.getTotalNumEntries());

        return feeds;
    }

    /**
     * Returns the feed items for a feed.
     */
    private static List<FeedItem> getFeedItems(AdWordsServices adWordsServices, AdWordsSession session, Feed feed)
            throws Exception {
        // Get the FeedItemService.
        FeedItemServiceInterface feedItemService = adWordsServices.get(session, FeedItemServiceInterface.class);
        String query = String.format(
                "SELECT FeedItemId, AttributeValues, Scheduling WHERE Status = 'ENABLED' AND FeedId = %d",
                feed.getId());

        List<FeedItem> feedItems = Lists.newArrayList();
        int offset = 0;
        FeedItemPage feedItemPage;

        do {
            String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
            feedItemPage = feedItemService.query(pageQuery);
            if (feedItemPage.getEntries() != null) {
                feedItems.addAll(Arrays.asList(feedItemPage.getEntries()));
            }
            offset += PAGE_SIZE;
        } while (offset < feedItemPage.getTotalNumEntries());

        return feedItems;
    }

    /**
     * Deletes the old feed items for which extension settings have been created.
     */
    private static void deleteOldFeedItems(AdWordsServices adWordsServices, AdWordsSession session,
            Set<Long> feedItemIds, Feed feed) throws Exception {
        // Get the FeedItemService.
        FeedItemServiceInterface feedItemService = adWordsServices.get(session, FeedItemServiceInterface.class);

        if (feedItemIds.isEmpty()) {
            return;
        }

        List<FeedItemOperation> operations = Lists.newArrayList();
        for (Long feedItemId : feedItemIds) {
            FeedItemOperation operation = new FeedItemOperation();

            FeedItem feedItem = new FeedItem();
            feedItem.setFeedId(feed.getId());
            feedItem.setFeedItemId(feedItemId);

            operation.setOperand(feedItem);
            operation.setOperator(Operator.REMOVE);

            operations.add(operation);
        }

        feedItemService.mutate(operations.toArray(new FeedItemOperation[operations.size()]));
    }

    /**
     * Creates the extension setting for a list of feed items.
     *
     * @param adWordsServices the AdWordsServices
     * @param session the AdWordsSession
     * @param feedItems the list of all feed items
     * @param campaignFeed the original campaign feed
     * @param feedItemIds IDs of the feed items for which extension settings should be created
     * @param platformRestrictions the platform restrictions for the new campaign extension setting
     */
    private static void createExtensionSetting(AdWordsServices adWordsServices, AdWordsSession session,
            Map<Long, SiteLinkFromFeed> feedItems, CampaignFeed campaignFeed, Set<Long> feedItemIds,
            ExtensionSettingPlatform platformRestrictions) throws Exception {
        // Get the CampaignExtensionSettingService.
        CampaignExtensionSettingServiceInterface campaignExtensionSettingService = adWordsServices.get(session,
                CampaignExtensionSettingServiceInterface.class);

        CampaignExtensionSetting campaignExtensionSetting = new CampaignExtensionSetting();
        campaignExtensionSetting.setCampaignId(campaignFeed.getCampaignId());
        campaignExtensionSetting.setExtensionType(FeedType.SITELINK);

        ExtensionSetting extensionSetting = new ExtensionSetting();

        List<ExtensionFeedItem> extensionFeedItems = Lists.newArrayList();

        for (Long feedItemId : feedItemIds) {
            SiteLinkFromFeed siteLinkFromFeed = feedItems.get(feedItemId);
            SitelinkFeedItem siteLinkFeedItem = new SitelinkFeedItem();
            siteLinkFeedItem.setSitelinkText(siteLinkFromFeed.text);

            if (siteLinkFromFeed.finalUrls != null && siteLinkFromFeed.finalUrls.length > 0) {
                siteLinkFeedItem.setSitelinkFinalUrls(new UrlList(siteLinkFromFeed.finalUrls));
                if (siteLinkFromFeed.finalMobileUrls != null && siteLinkFromFeed.finalMobileUrls.length > 0) {
                    siteLinkFeedItem.setSitelinkFinalMobileUrls(new UrlList(siteLinkFromFeed.finalMobileUrls));
                }
                siteLinkFeedItem.setSitelinkTrackingUrlTemplate(siteLinkFromFeed.trackingUrlTemplate);
            } else {
                siteLinkFeedItem.setSitelinkUrl(siteLinkFromFeed.url);
            }

            siteLinkFeedItem.setSitelinkLine2(siteLinkFromFeed.line2);
            siteLinkFeedItem.setSitelinkLine3(siteLinkFromFeed.line3);
            siteLinkFeedItem.setScheduling(siteLinkFromFeed.scheduling);

            extensionFeedItems.add(siteLinkFeedItem);
        }

        extensionSetting
                .setExtensions(extensionFeedItems.toArray(new ExtensionFeedItem[extensionFeedItems.size()]));

        extensionSetting.setPlatformRestrictions(platformRestrictions);

        campaignExtensionSetting.setExtensionSetting(extensionSetting);

        CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation();
        operation.setOperand(campaignExtensionSetting);
        operation.setOperator(Operator.ADD);

        campaignExtensionSettingService.mutate(new CampaignExtensionSettingOperation[] { operation });
    }

    /**
     * Deletes a campaign feed.
     */
    private static CampaignFeed deleteCampaignFeed(AdWordsServices adWordsServices, AdWordsSession session,
            CampaignFeed campaignFeed) throws Exception {
        // Get the CampaignFeedService.
        CampaignFeedServiceInterface campaignFeedService = adWordsServices.get(session,
                CampaignFeedServiceInterface.class);

        CampaignFeedOperation operation = new CampaignFeedOperation();
        operation.setOperand(campaignFeed);
        operation.setOperator(Operator.REMOVE);

        return campaignFeedService.mutate(new CampaignFeedOperation[] { operation }).getValue(0);
    }

    /**
     * Gets the platform restrictions for sitelinks in a campaign.
     */
    private static ExtensionSettingPlatform getPlatformRestictionsForCampaign(CampaignFeed campaignFeed) {
        String platformRestrictions = ExtensionSettingPlatform.NONE.getValue();

        if (FunctionOperator.AND.equals(campaignFeed.getMatchingFunction().getOperator())) {
            for (FunctionArgumentOperand argument : campaignFeed.getMatchingFunction().getLhsOperand()) {
                if (argument instanceof FunctionOperand) {
                    FunctionOperand operand = (FunctionOperand) argument;
                    if (FunctionOperator.EQUALS.equals(operand.getValue().getOperator())
                            && (operand.getValue().getLhsOperand(0) instanceof RequestContextOperand)) {
                        RequestContextOperand requestContextOperand = (RequestContextOperand) operand.getValue()
                                .getLhsOperand(0);
                        if (RequestContextOperandContextType.DEVICE_PLATFORM
                                .equals(requestContextOperand.getContextType())) {
                            platformRestrictions = ((ConstantOperand) operand.getValue().getRhsOperand(0))
                                    .getStringValue();
                        }
                    }
                }
            }
        }
        return ExtensionSettingPlatform.fromString(platformRestrictions.toUpperCase());
    }

    /**
     * Returns the list of feed item IDs that are used by a campaign through a given campaign feed.
     */
    private static Set<Long> getFeedItemIdsForCampaign(CampaignFeed campaignFeed) throws Exception {
        Set<Long> feedItemIds = Sets.newHashSet();

        FunctionOperator functionOperator = campaignFeed.getMatchingFunction().getOperator();

        if (FunctionOperator.IN.equals(functionOperator)) {
            // Check if matchingFunction is of the form IN(FEED_ITEM_ID,{xxx,xxx}).
            // Extract feed items if applicable.
            feedItemIds.addAll(getFeedItemIdsFromArgument(campaignFeed.getMatchingFunction()));
        } else if (FunctionOperator.AND.equals(functionOperator)) {
            for (FunctionArgumentOperand argument : campaignFeed.getMatchingFunction().getLhsOperand()) {
                // Check if matchingFunction is of the form IN(FEED_ITEM_ID,{xxx,xxx}).
                // Extract feed items if applicable.
                if (argument instanceof FunctionOperand) {
                    FunctionOperand operand = (FunctionOperand) argument;
                    if (FunctionOperator.IN.equals(operand.getValue().getOperator())) {
                        feedItemIds.addAll(getFeedItemIdsFromArgument(operand.getValue()));
                    }
                }
            }

        } else {
            // There are no other matching functions involving feed item IDs.
        }

        return feedItemIds;
    }

    /**
     * Gets the set of feed item IDs from the function if it is of the form:
     * {@code IN(FEED_ITEM_ID,{xxx,xxx})}. Otherwise, returns an empty set.
     */
    private static Set<Long> getFeedItemIdsFromArgument(Function function) {
        Set<Long> feedItemIds = Sets.newHashSet();

        if (function.getLhsOperand().length == 1 && function.getLhsOperand(0) instanceof RequestContextOperand) {
            RequestContextOperand requestContextOperand = (RequestContextOperand) function.getLhsOperand(0);
            if (RequestContextOperandContextType.FEED_ITEM_ID.equals(requestContextOperand.getContextType())
                    && FunctionOperator.IN.equals(function.getOperator())) {
                for (FunctionArgumentOperand argument : function.getRhsOperand()) {
                    if (argument instanceof ConstantOperand) {
                        feedItemIds.add(((ConstantOperand) argument).getLongValue());
                    }
                }
            }
        }

        return feedItemIds;
    }

    /**
     * Returns the campaign feeds that use a particular feed for a particular placeholder type.
     */
    private static List<CampaignFeed> getCampaignFeeds(AdWordsServices adWordsServices, AdWordsSession session,
            Feed feed, int placeholderType) throws Exception {
        // Get the CampaignFeedService.
        CampaignFeedServiceInterface campaignFeedService = adWordsServices.get(session,
                CampaignFeedServiceInterface.class);

        String query = String
                .format("SELECT CampaignId, MatchingFunction, PlaceholderTypes WHERE Status = 'ENABLED' "
                        + "AND FeedId = %d AND PlaceholderTypes CONTAINS_ANY [%d]", feed.getId(), placeholderType);

        List<CampaignFeed> campaignFeeds = Lists.newArrayList();
        int offset = 0;
        CampaignFeedPage campaignFeedPage;

        do {
            String pageQuery = String.format(query + " LIMIT %d, %d", offset, PAGE_SIZE);
            campaignFeedPage = campaignFeedService.query(pageQuery);
            if (campaignFeedPage.getEntries() != null) {
                campaignFeeds.addAll(Arrays.asList(campaignFeedPage.getEntries()));
            }
            offset += PAGE_SIZE;
        } while (offset < campaignFeedPage.getTotalNumEntries());

        return campaignFeeds;
    }

    /**
     * A sitelink object read from a feed item.
     */
    private static class SiteLinkFromFeed {
        private String text;
        private String url;
        private String[] finalUrls;
        private String[] finalMobileUrls;
        private String trackingUrlTemplate;
        private String line2;
        private String line3;
        private FeedItemScheduling scheduling;
    }
}