The code samples below provide examples of common migration functions using the AdWords API. Client Library.
Migrate feed-based campaign level sitelinks to extension settings
// Copyright 2018 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 Google.Api.Ads.AdWords.Lib; using Google.Api.Ads.AdWords.v201809; using System; using System.Collections.Generic; namespace Google.Api.Ads.AdWords.Examples.CSharp.v201809 { /// <summary> /// 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. /// </summary> /// <remarks>This code example doesn't migrate scheduling or feeditem-level campaign, adgroup, /// keyword, or geo targeting settings.</remarks> public class MigrateToExtensionSettings : ExampleBase { /// <summary> /// The placeholder type for sitelinks. See /// https://developers.google.com/adwords/api/docs/appendix/placeholders for /// the list of all supported placeholder types. /// </summary> private const int PLACEHOLDER_TYPE_SITELINKS = 1; /// <summary> /// Holds the placeholder field IDs for sitelinks. See /// https://developers.google.com/adwords/api/docs/appendix/placeholders for /// the list of all supported placeholder types. /// </summary> private class SiteLinkFields { public const long TEXT = 1; public const long URL = 2; public const long LINE2 = 3; public const long LINE3 = 4; public const long FINAL_URLS = 5; public const long FINAL_MOBILE_URLS = 6; public const long TRACKING_URL_TEMPLATE = 7; }; /// <summary> /// A sitelink object read from a feed. /// </summary> private class SiteLinkFromFeed { /// <summary> /// Gets or sets the feed ID. /// </summary> public long FeedId { get; set; } /// <summary> /// Gets or sets the feed item ID. /// </summary> public long FeedItemId { get; set; } /// <summary> /// Gets or sets the sitelink text. /// </summary> public string Text { get; set; } /// <summary> /// Gets or sets the sitelink URL. /// </summary> public string Url { get; set; } /// <summary> /// Gets or sets the sitelink final URLs. /// </summary> public string[] FinalUrls { get; set; } /// <summary> /// Gets or sets the sitelink final Mobile URLs. /// </summary> public string[] FinalMobileUrls { get; set; } /// <summary> /// Gets or sets the sitelink tracking URL template. /// </summary> public string TrackingUrlTemplate { get; set; } /// <summary> /// Gets or sets the sitelink line2 description. /// </summary> public string Line2 { get; set; } /// <summary> /// Gets or sets the sitelink line3 description. /// </summary> public string Line3 { 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) { MigrateToExtensionSettings codeExample = new MigrateToExtensionSettings(); Console.WriteLine(codeExample.Description); try { codeExample.Run(new AdWordsUser()); } catch (Exception e) { Console.WriteLine("An exception occurred while running this code example. {0}", ExampleUtilities.FormatException(e)); } } /// <summary> /// Returns a description about the code example. /// </summary> public override string Description { get { return "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."; } } /// <summary> /// Runs the code example. /// </summary> /// <param name="user">The AdWords user.</param> public void Run(AdWordsUser user) { // Get all the feeds from the user account. Feed[] feeds = GetFeeds(user); foreach (Feed feed in feeds) { // Retrieve all the sitelinks from the current feed. Dictionary<long, SiteLinkFromFeed> feedItems = GetSiteLinksFromFeed(user, feed.id); // Get all the instances where a sitelink from this feed has been added // to a campaign. CampaignFeed[] campaignFeeds = GetCampaignFeeds(user, feed, PLACEHOLDER_TYPE_SITELINKS); if (campaignFeeds != null) { HashSet<long> allFeedItemsToDelete = new HashSet<long>(); foreach (CampaignFeed campaignFeed in campaignFeeds) { // Retrieve the sitelinks that have been associated with this // campaign. List<long> feedItemIds = GetFeedItemsForCampaign(campaignFeed); ExtensionSettingPlatform platformRestrictions = GetPlatformRestrictionsForCampaign(campaignFeed); if (feedItemIds.Count == 0) { Console.WriteLine( "Migration skipped for campaign feed with campaign ID {0} and " + "feed ID {1} because no mapped feed item IDs were found in the " + "campaign feed's matching function.", campaignFeed.campaignId, campaignFeed.feedId); } else { // Delete the campaign feed that associates the sitelinks from the // feed to the campaign. DeleteCampaignFeed(user, campaignFeed); // Create extension settings instead of sitelinks. CreateExtensionSetting(user, feedItems, campaignFeed.campaignId, feedItemIds, platformRestrictions); // Mark the sitelinks from the feed for deletion. allFeedItemsToDelete.UnionWith(feedItemIds); } } // Delete all the sitelinks from the feed. DeleteOldFeedItems(user, new List<long>(allFeedItemsToDelete), feed.id); } } } /// <summary> /// Gets the site links from a feed. /// </summary> /// <param name="user">The user that owns the feed.</param> /// <param name="feedId">The feed ID.</param> /// <returns>A dictionary of sitelinks from the feed, with key as the feed /// item ID, and value as the sitelink.</returns> private Dictionary<long, SiteLinkFromFeed> GetSiteLinksFromFeed(AdWordsUser user, long feedId) { Dictionary<long, SiteLinkFromFeed> siteLinks = new Dictionary<long, SiteLinkFromFeed>(); // Retrieve all the feed items from the feed. FeedItem[] feedItems = GetFeedItems(user, feedId); // Retrieve the feed's attribute mapping. Dictionary<long, HashSet<long>> feedMappings = GetFeedMapping(user, feedId, PLACEHOLDER_TYPE_SITELINKS); if (feedItems != null) { foreach (FeedItem feedItem in feedItems) { SiteLinkFromFeed sitelinkFromFeed = new SiteLinkFromFeed() { FeedId = feedItem.feedId, FeedItemId = feedItem.feedItemId }; foreach (FeedItemAttributeValue attributeValue in feedItem.attributeValues) { // This attribute hasn't been mapped to a field. if (!feedMappings.ContainsKey(attributeValue.feedAttributeId)) { continue; } // Get the list of all the fields to which this attribute has been mapped. foreach (long fieldId in feedMappings[attributeValue.feedAttributeId]) { // Read the appropriate value depending on the ID of the mapped // field. switch (fieldId) { case SiteLinkFields.TEXT: sitelinkFromFeed.Text = attributeValue.stringValue; break; case SiteLinkFields.URL: sitelinkFromFeed.Url = attributeValue.stringValue; break; case SiteLinkFields.FINAL_URLS: sitelinkFromFeed.FinalUrls = attributeValue.stringValues; break; case SiteLinkFields.FINAL_MOBILE_URLS: sitelinkFromFeed.FinalMobileUrls = attributeValue.stringValues; break; case SiteLinkFields.TRACKING_URL_TEMPLATE: sitelinkFromFeed.TrackingUrlTemplate = attributeValue.stringValue; break; case SiteLinkFields.LINE2: sitelinkFromFeed.Line2 = attributeValue.stringValue; break; case SiteLinkFields.LINE3: sitelinkFromFeed.Line3 = attributeValue.stringValue; break; } } } siteLinks.Add(feedItem.feedItemId, sitelinkFromFeed); } } return siteLinks; } /// <summary> /// Gets the feed mapping for a feed. /// </summary> /// <param name="user">The user that owns the feed.</param> /// <param name="feedId">The feed ID.</param> /// <param name="placeHolderType">Type of the place holder for which feed /// mappings should be retrieved.</param> /// <returns>A dictionary, with key as the feed attribute ID, and value as /// the set of all fields which the attribute has a mapping to.</returns> private Dictionary<long, HashSet<long>> GetFeedMapping(AdWordsUser user, long feedId, long placeHolderType) { using (FeedMappingService feedMappingService = (FeedMappingService) user.GetService(AdWordsService.v201809.FeedMappingService)) { FeedMappingPage page = feedMappingService.query(string.Format( "SELECT FeedMappingId, " + "AttributeFieldMappings where FeedId='{0}' and PlaceholderType={1} and " + "Status='ENABLED'", feedId, placeHolderType)); Dictionary<long, HashSet<long>> attributeMappings = new Dictionary<long, HashSet<long>>(); if (page.entries != null) { // Normally, a feed attribute is mapped only to one field. However, // you may map it to more than one field if needed. foreach (FeedMapping feedMapping in page.entries) { foreach (AttributeFieldMapping attributeMapping in feedMapping .attributeFieldMappings) { if (!attributeMappings.ContainsKey(attributeMapping.feedAttributeId)) { attributeMappings[attributeMapping.feedAttributeId] = new HashSet<long>(); } attributeMappings[attributeMapping.feedAttributeId] .Add(attributeMapping.fieldId); } } } return attributeMappings; } } /// <summary> /// Gets the feeds. /// </summary> /// <param name="user">The user for which feeds are retrieved.</param> /// <returns>The list of feeds.</returns> private Feed[] GetFeeds(AdWordsUser user) { using (FeedService feedService = (FeedService) user.GetService(AdWordsService.v201809.FeedService)) { FeedPage page = feedService.query("SELECT Id, Name, Attributes where " + "Origin='USER' and FeedStatus='ENABLED'"); return page.entries; } } /// <summary> /// Gets the feed items in a feed. /// </summary> /// <param name="user">The user that owns the feed.</param> /// <param name="feedId">The feed ID.</param> /// <returns>The list of feed items in the feed.</returns> private FeedItem[] GetFeedItems(AdWordsUser user, long feedId) { using (FeedItemService feedItemService = (FeedItemService) user.GetService(AdWordsService.v201809.FeedItemService)) { FeedItemPage page = feedItemService.query(string.Format( "Select FeedItemId, " + "AttributeValues where Status = 'ENABLED' and FeedId = '{0}'", feedId)); return page.entries; } } /// <summary> /// Deletes the old feed items for which extension settings have been /// created. /// </summary> /// <param name="user">The user that owns the feed items.</param> /// <param name="feedItemIds">IDs of the feed items to be removed.</param> /// <param name="feedId">ID of the feed that holds the feed items.</param> private void DeleteOldFeedItems(AdWordsUser user, List<long> feedItemIds, long feedId) { if (feedItemIds.Count == 0) { return; } List<FeedItemOperation> operations = new List<FeedItemOperation>(); foreach (long feedItemId in feedItemIds) { FeedItemOperation operation = new FeedItemOperation() { @operator = Operator.REMOVE, operand = new FeedItem() { feedItemId = feedItemId, feedId = feedId } }; operations.Add(operation); } using (FeedItemService feedItemService = (FeedItemService) user.GetService(AdWordsService.v201809.FeedItemService)) { feedItemService.mutate(operations.ToArray()); return; } } /// <summary> /// Creates the extension setting fo a list of feed items. /// </summary> /// <param name="user">The user for which extension settings are created. /// </param> /// <param name="feedItems">The list of all feed items.</param> /// <param name="campaignId">ID of the campaign to which extension settings /// are added.</param> /// <param name="feedItemIds">IDs of the feed items for which extension /// settings should be created.</param> /// <param name="platformRestrictions">The platform restrictions for the /// extension setting.</param> private static void CreateExtensionSetting(AdWordsUser user, Dictionary<long, SiteLinkFromFeed> feedItems, long campaignId, List<long> feedItemIds, ExtensionSettingPlatform platformRestrictions) { CampaignExtensionSetting extensionSetting = new CampaignExtensionSetting() { campaignId = campaignId, extensionType = FeedType.SITELINK, extensionSetting = new ExtensionSetting() { } }; List<ExtensionFeedItem> extensionFeedItems = new List<ExtensionFeedItem>(); foreach (long feedItemId in feedItemIds) { SiteLinkFromFeed feedItem = feedItems[feedItemId]; SitelinkFeedItem newFeedItem = new SitelinkFeedItem() { sitelinkText = feedItem.Text, sitelinkUrl = feedItem.Url, sitelinkFinalUrls = new UrlList() { urls = feedItem.FinalUrls }, sitelinkFinalMobileUrls = new UrlList() { urls = feedItem.FinalMobileUrls }, sitelinkTrackingUrlTemplate = feedItem.TrackingUrlTemplate, sitelinkLine2 = feedItem.Line2, sitelinkLine3 = feedItem.Line3 }; extensionFeedItems.Add(newFeedItem); } extensionSetting.extensionSetting.extensions = extensionFeedItems.ToArray(); extensionSetting.extensionSetting.platformRestrictions = platformRestrictions; extensionSetting.extensionType = FeedType.SITELINK; using (CampaignExtensionSettingService campaignExtensionSettingService = (CampaignExtensionSettingService) user.GetService(AdWordsService.v201809 .CampaignExtensionSettingService)) { CampaignExtensionSettingOperation operation = new CampaignExtensionSettingOperation() { operand = extensionSetting, @operator = Operator.ADD }; campaignExtensionSettingService.mutate(new CampaignExtensionSettingOperation[] { operation }); } return; } /// <summary> /// Deletes a campaign feed. /// </summary> /// <param name="user">The AdWords user.</param> /// <param name="campaignFeed">The campaign feed.</param> /// <returns></returns> private CampaignFeed DeleteCampaignFeed(AdWordsUser user, CampaignFeed campaignFeed) { using (CampaignFeedService campaignFeedService = (CampaignFeedService) user.GetService(AdWordsService.v201809.CampaignFeedService)) { CampaignFeedOperation operation = new CampaignFeedOperation() { operand = campaignFeed, @operator = Operator.REMOVE }; CampaignFeed retval = campaignFeedService.mutate(new CampaignFeedOperation[] { operation }).value[0]; return retval; } } /// <summary> /// Gets the platform restrictions for sitelinks in a campaign. /// </summary> /// <param name="campaignFeed">The campaign feed.</param> /// <returns>The platform restrictions.</returns> private ExtensionSettingPlatform GetPlatformRestrictionsForCampaign( CampaignFeed campaignFeed) { string platformRestrictions = "NONE"; if (campaignFeed.matchingFunction.@operator == FunctionOperator.AND) { foreach (FunctionArgumentOperand argument in campaignFeed.matchingFunction .lhsOperand) { // Check if matchingFunction is of the form EQUALS(CONTEXT.DEVICE, 'Mobile'). FunctionOperand operand = argument as FunctionOperand; if (operand?.value.@operator == FunctionOperator.EQUALS) { RequestContextOperand requestContextOperand = operand.value.lhsOperand[0] as RequestContextOperand; if (requestContextOperand != null && requestContextOperand.contextType == RequestContextOperandContextType.DEVICE_PLATFORM) { platformRestrictions = (operand.value.rhsOperand[0] as ConstantOperand) .stringValue; } } } } return (ExtensionSettingPlatform) Enum.Parse(typeof(ExtensionSettingPlatform), platformRestrictions, true); } /// <summary> /// Gets the list of feed items that are used by a campaign through a given /// campaign feed. /// </summary> /// <param name="campaignFeed">The campaign feed.</param> /// <returns>The list of feed items.</returns> private List<long> GetFeedItemsForCampaign(CampaignFeed campaignFeed) { List<long> feedItems = new List<long>(); switch (campaignFeed.matchingFunction.@operator) { case FunctionOperator.IN: // Check if matchingFunction is of the form IN(FEED_ITEM_ID,{xxx,xxx}). // Extract feedItems if applicable. feedItems.AddRange(GetFeedItemsFromArgument(campaignFeed.matchingFunction)); break; case FunctionOperator.AND: // Check each condition. foreach (FunctionArgumentOperand argument in campaignFeed.matchingFunction .lhsOperand) { // Check if matchingFunction is of the form IN(FEED_ITEM_ID,{xxx,xxx}). // Extract feedItems if applicable. if (argument is FunctionOperand) { FunctionOperand operand = (argument as FunctionOperand); if (operand.value.@operator == FunctionOperator.IN) { feedItems.AddRange(GetFeedItemsFromArgument(operand.value)); } } } break; default: // There are no other matching functions involving feeditem ids. break; } return feedItems; } private List<long> GetFeedItemsFromArgument(Function function) { List<long> feedItems = new List<long>(); if (function.lhsOperand.Length == 1) { RequestContextOperand requestContextOperand = function.lhsOperand[0] as RequestContextOperand; if (requestContextOperand != null && requestContextOperand.contextType == RequestContextOperandContextType.FEED_ITEM_ID) { foreach (ConstantOperand argument in function.rhsOperand) { feedItems.Add(argument.longValue); } } } return feedItems; } /// <summary> /// Gets the campaignfeeds that use a particular feed. /// </summary> /// <param name="user">The user that owns the feed.</param> /// <param name="feed">The feed for which campaign feeds should be /// retrieved.</param> /// <param name="placeholderType">The type of placeholder to restrict /// search.</param> /// <returns>The list of campaignfeeds.</returns> private CampaignFeed[] GetCampaignFeeds(AdWordsUser user, Feed feed, int placeholderType) { using (CampaignFeedService campaignFeedService = (CampaignFeedService) user.GetService(AdWordsService.v201809.CampaignFeedService)) { CampaignFeedPage page = campaignFeedService.query( string.Format( "SELECT CampaignId, MatchingFunction, PlaceholderTypes where " + "Status='ENABLED' and FeedId = '{0}' and PlaceholderTypes " + "CONTAINS_ANY[{1}]", feed.id, placeholderType)); return page.entries; } } } }