public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient<ICampaignManagementService>(authorizationData); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList<BMCStore> stores = await GetBMCStoresByCustomerIdAsync(); if (stores == null) { OutputStatusMessage( String.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } #region ManageCampaign /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var campaign = new Campaign { CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, }; var campaignIds = await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign }); OutputCampaignIdentifiers(campaignIds); /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var campaignCriterions = new CampaignCriterion[] { new CampaignCriterion() { CampaignId = campaignIds[0], BidAdjustment = null, // Reserved for future use Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, } }; var addCampaignCriterionsResponse = await (AddCampaignCriterionsAsync( campaignCriterions, CampaignCriterionType.ProductScope) ); #endregion ManageCampaign #region ManageAdGroup // Specify one or more ad groups. var adGroup = new AdGroup { Name = "Product Categories", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = 2016 }, Language = "English" }; var adGroupIds = (long[])await AddAdGroupsAsync(campaignIds[0], new[] { adGroup }); OutputAdGroupIdentifiers(adGroupIds); #region BidAllProducts var helper = new PartitionActionHelper(adGroupIds[0]); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); var adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(adGroupCriterions); /* * Let's update the bid of the root Unit we just added. */ BiddableAdGroupCriterion updatedRoot = new BiddableAdGroupCriterion { Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[0], CriterionBid = new FixedBid { Bid = new Bid { Amount = 0.45 } } }; helper = new PartitionActionHelper(adGroupIds[0]); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(adGroupCriterions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 5,000 nodes; however, * we will build it in steps to demonstrate how to use the results from ApplyProductPartitionActions to update the tree. * * For a list of validation rules, see the Bing Shopping Campaigns technical guide: * https://msdn.microsoft.com/en-US/library/bing-ads-campaign-management-bing-shopping-campaigns.aspx */ helper = new PartitionActionHelper(adGroupIds[0]); /* * Check whether a root node exists already. */ adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); var existingRoot = GetRootNode(adGroupCriterions); if (existingRoot != null) { helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null } ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://advertise.bingads.microsoft.com/en-us/WWDocs/user/search/en-us/Bing_Category_Taxonomy.txt */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" } ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" } ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); // To retrieve product partitions after they have been applied, call GetAdGroupCriterionsByAdGroupId. // The product partition with ParentCriterionId set to null is the root node. adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); /* * The product partition group tree now has 9 nodes. All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(adGroupCriterions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) */ helper = new PartitionActionHelper(adGroupIds[0]); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node), and was created at Index 1 of the previous ApplyProductPartitionActions call. * The node that we are replacing is Electronics (CategoryL1), and was created at Index 8. */ var rootId = applyProductPartitionActionsResponse.AdGroupCriterionIds[1]; electronics.Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[8]; helper.DeletePartition(electronics); var parent = new BiddableAdGroupCriterion() { Id = rootId }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" } ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(adGroupCriterions); #endregion UpdateTree #endregion ManageAdGroup #region ManageAds /* * Create a product ad. You must add at least one ProductAd to the corresponding ad group. * A ProductAd is not used directly for delivered ad copy. Instead, the delivery engine generates * product ads from the product details that it finds in your Bing Merchant Center store's product catalog. * The primary purpose of the ProductAd object is to provide promotional text that the delivery engine * adds to the product ads that it generates. For example, if the promotional text is set to * “Free shipping on $99 purchases”, the delivery engine will set the product ad’s description to * “Free shipping on $99 purchases.” */ var ads = new Ad[] { new ProductAd { PromotionalText = "Free shipping on $99 purchases." }, }; AddAdsResponse addAdsResponse = await AddAdsAsync(adGroupIds[0], ads); OutputAdResults(ads, addAdsResponse.AdIds, addAdsResponse.PartialErrors); #endregion ManageAds #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignIds[0] }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignIds[0])); #endregion CleanUp } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException<Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException<Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { Service = new ServiceClient <ICampaignManagementService>(authorizationData); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList <BMCStore> stores = await GetBMCStoresByCustomerIdAsync(); if (stores == null) { OutputStatusMessage( String.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } #region ManageCampaign /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var campaign = new Campaign { CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", BudgetType = BudgetLimitType.MonthlyBudgetSpendUntilDepleted, MonthlyBudget = 1000.00, TimeZone = "PacificTimeUSCanadaTijuana", DaylightSaving = true, }; var campaignIds = await AddCampaignsAsync(authorizationData.AccountId, new[] { campaign }); OutputCampaignIdentifiers(campaignIds); /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var campaignCriterions = new CampaignCriterion[] { new CampaignCriterion() { CampaignId = campaignIds[0], BidAdjustment = null, // Reserved for future use Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, } }; var addCampaignCriterionsResponse = await(AddCampaignCriterionsAsync( campaignCriterions, CampaignCriterionType.ProductScope) ); #endregion ManageCampaign #region ManageAdGroup // Specify one or more ad groups. var adGroup = new AdGroup { Name = "Product Categories", AdDistribution = AdDistribution.Search, BiddingModel = BiddingModel.Keyword, PricingModel = PricingModel.Cpc, StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = 2016 }, Language = "English" }; var adGroupIds = (long[]) await AddAdGroupsAsync(campaignIds[0], new[] { adGroup }); OutputAdGroupIdentifiers(adGroupIds); #region BidAllProducts var helper = new PartitionActionHelper(adGroupIds[0]); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); var adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(adGroupCriterions); /* * Let's update the bid of the root Unit we just added. */ BiddableAdGroupCriterion updatedRoot = new BiddableAdGroupCriterion { Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[0], CriterionBid = new FixedBid { Bid = new Bid { Amount = 0.45 } } }; helper = new PartitionActionHelper(adGroupIds[0]); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(adGroupCriterions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 5,000 nodes; however, * we will build it in steps to demonstrate how to use the results from ApplyProductPartitionActions to update the tree. * * For a list of validation rules, see the Bing Shopping Campaigns technical guide: * https://msdn.microsoft.com/en-US/library/bing-ads-campaign-management-bing-shopping-campaigns.aspx */ helper = new PartitionActionHelper(adGroupIds[0]); /* * Check whether a root node exists already. */ adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); var existingRoot = GetRootNode(adGroupCriterions); if (existingRoot != null) { helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null } ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://advertise.bingads.microsoft.com/en-us/WWDocs/user/search/en-us/Bing_Category_Taxonomy.txt */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" } ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" } ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); // To retrieve product partitions after they have been applied, call GetAdGroupCriterionsByAdGroupId. // The product partition with ParentCriterionId set to null is the root node. adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); /* * The product partition group tree now has 9 nodes. * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) | */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(adGroupCriterions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: * * Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) | */ helper = new PartitionActionHelper(adGroupIds[0]); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node), and was created at Index 1 of the previous ApplyProductPartitionActions call. * The node that we are replacing is Electronics (CategoryL1), and was created at Index 8. */ var rootId = applyProductPartitionActionsResponse.AdGroupCriterionIds[1]; electronics.Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[8]; helper.DeletePartition(electronics); var parent = new BiddableAdGroupCriterion() { Id = rootId }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" } ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyProductPartitionActionsResponse = await ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await GetAdGroupCriterionsByAdGroupIdAsync( adGroupIds[0], CriterionType.ProductPartition ); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) | */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(adGroupCriterions); #endregion UpdateTree #endregion ManageAdGroup #region ManageAds /* * Create a product ad. You must add at least one ProductAd to the corresponding ad group. * A ProductAd is not used directly for delivered ad copy. Instead, the delivery engine generates * product ads from the product details that it finds in your Bing Merchant Center store's product catalog. * The primary purpose of the ProductAd object is to provide promotional text that the delivery engine * adds to the product ads that it generates. For example, if the promotional text is set to * “Free shipping on $99 purchases”, the delivery engine will set the product ad’s description to * “Free shipping on $99 purchases.” */ var ads = new Ad[] { new ProductAd { PromotionalText = "Free shipping on $99 purchases." }, }; AddAdsResponse addAdsResponse = await AddAdsAsync(adGroupIds[0], ads); OutputAdResults(ads, addAdsResponse.AdIds, addAdsResponse.PartialErrors); #endregion ManageAds #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignIds[0] }); OutputStatusMessage(String.Format("Deleted CampaignId {0}\n", campaignIds[0])); #endregion CleanUp } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper( OutputStatusMessageDefault: this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>( authorizationData: authorizationData, environment: environment); // Get a list of all Bing Merchant Center stores associated with your CustomerId. OutputStatusMessage("-----\nGetBMCStoresByCustomerId:"); IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null || stores.Count <= 0) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.", authorizationData.CustomerId) ); return; } OutputStatusMessage("BMCStores:"); CampaignManagementExampleHelper.OutputArrayOfBMCStore(stores); // Create a Shopping campaign with product conditions. var campaigns = new[] { new Campaign { CampaignType = CampaignType.Shopping, Languages = new string[] { "All" }, Name = "Women's Shoes " + DateTime.UtcNow, Description = "Red shoes line.", DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, TimeZone = "PacificTimeUSCanadaTijuana", } }; OutputStatusMessage("-----\nAddCampaigns:"); AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync( accountId : authorizationData.AccountId, campaigns : campaigns, includeDynamicSearchAdsSource : false); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); OutputStatusMessage("CampaignIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); long campaignId = (long)campaignIds[0]; // Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. // You'll also be able to add more specific product conditions for each ad group. var campaignCriterions = new BiddableCampaignCriterion[] { new BiddableCampaignCriterion() { CampaignId = campaignId, CriterionBid = null, // Not applicable for product scope Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, Status = CampaignCriterionStatus.Active } }; OutputStatusMessage("-----\nAddCampaignCriterions:"); var addCampaignCriterionsResponse = await CampaignManagementExampleHelper.AddCampaignCriterionsAsync( campaignCriterions : campaignCriterions, criterionType : CampaignCriterionType.ProductScope); long?[] campaignCriterionIds = addCampaignCriterionsResponse.CampaignCriterionIds.ToArray(); OutputStatusMessage("CampaignCriterionIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(campaignCriterionIds); BatchErrorCollection[] campaignCriterionErrors = addCampaignCriterionsResponse.NestedPartialErrors.ToArray(); OutputStatusMessage("NestedPartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchErrorCollection(campaignCriterionErrors); // Create the ad group that will have the product partitions. var adGroups = new[] { new AdGroup { Name = "Women's Red Shoe Sale", StartDate = null, EndDate = new Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, CpcBid = new Bid { Amount = 0.09 }, } }; OutputStatusMessage("-----\nAddAdGroups:"); AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync( campaignId : (long)campaignIds[0], adGroups : adGroups, returnInheritedBidStrategyTypes : false); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdGroupIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); long adGroupId = (long)adGroupIds[0]; // Bid all products var helper = new PartitionActionHelper(adGroupId); var root = helper.AddUnit( parent: null, condition: new ProductCondition { Operand = "All", Attribute = null }, bidAmount: 0.35, isNegative: false); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage("Applying only the root as a Unit with a bid..."); var applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); var adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); // Let's update the bid of the root Unit we just added. BiddableAdGroupCriterion updatedRoot = new BiddableAdGroupCriterion { Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[0], CriterionBid = new FixedBid { Amount = 0.45 } }; helper = new PartitionActionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage("Updating the bid for the tree root node..."); await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); // Initialize and overwrite any existing tree root, and build a product partition group tree structure in multiple steps. // You could build the entire tree in a single call since there are less than 5,000 nodes; however, // we will build it in steps to demonstrate how to use the results from ApplyProductPartitionActions to update the tree. helper = new PartitionActionHelper(adGroupId); // Check whether a root node exists already. OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); var existingRoot = GetRootNode(adGroupCriterions?.AdGroupCriterions); if (existingRoot != null) { helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null } ); // The direct children of any node must have the same Operand. // For this example we will use CategoryL1 nodes as children of the root. // For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: // http://go.microsoft.com/fwlink?LinkId=507666 var animalsSubdivision = helper.AddSubdivision( parent: root, condition: new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" }); // If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. // In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. // For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. var petSuppliesSubdivision = helper.AddSubdivision( parent: animalsSubdivision, condition: new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" }); var brandA = helper.AddUnit( parent: petSuppliesSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, bidAmount: 0.35, isNegative: false); // If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. // The helper method will create a NegativeAdGroupCriterion and apply the condition. var brandB = helper.AddUnit( parent: petSuppliesSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, bidAmount: 0, isNegative: true); var otherBrands = helper.AddUnit( parent: petSuppliesSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = null }, bidAmount: 0.35, isNegative: false); var otherPetSupplies = helper.AddUnit( parent: animalsSubdivision, condition: new ProductCondition { Operand = "CategoryL2", Attribute = null }, bidAmount: 0.35, isNegative: false); var electronics = helper.AddUnit( parent: root, condition: new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, bidAmount: 0.35, isNegative: false); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false ); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage("Applying product partitions to the ad group..."); applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); // To retrieve product partitions after they have been applied, call GetAdGroupCriterionsByIds. // The product partition with ParentCriterionId set to null is the root node. OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); // The product partition group tree now has 9 nodes. //All other (Root Node) // | // +-- Animals & Pet Supplies (CategoryL1) // | | // | +-- Pet Supplies (CategoryL2) // | | | // | | +-- Brand A // | | | // | | +-- Brand B // | | | // | | +-- All other (Brand) // | | // | +-- All other (CategoryL2) // | // +-- Electronics (CategoryL1) // | // +-- All other (CategoryL1) OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); // Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that // has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: //Electronics (CategoryL1) //| //+-- Brand C (Brand) //| //+-- Brand D (Brand) //| //+-- All other (Brand) helper = new PartitionActionHelper(adGroupId); // To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node // we are replacing is All other (Root Node), and was created at Index 1 of the previous ApplyProductPartitionActions call. // The node that we are replacing is Electronics (CategoryL1), and was created at Index 8. var rootId = applyProductPartitionActionsResponse.AdGroupCriterionIds[1]; electronics.Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[8]; helper.DeletePartition(electronics); var parent = new BiddableAdGroupCriterion() { Id = rootId }; var electronicsSubdivision = helper.AddSubdivision( parent: parent, condition: new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" } ); var brandC = helper.AddUnit( parent: electronicsSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, bidAmount: 0.35, isNegative: false); var brandD = helper.AddUnit( parent: electronicsSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, bidAmount: 0.35, isNegative: false); var otherElectronicsBrands = helper.AddUnit( parent: electronicsSubdivision, condition: new ProductCondition { Operand = "Brand", Attribute = null }, bidAmount: 0.35, isNegative: false); OutputStatusMessage("-----\nApplyProductPartitionActions:"); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes..." ); applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync( criterionActions : helper.PartitionActions); OutputStatusMessage("-----\nGetAdGroupCriterionsByIds:"); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( adGroupCriterionIds : null, adGroupId : adGroupId, criterionType : AdGroupCriterionType.ProductPartition, returnAdditionalFields : AdGroupCriterionAdditionalField.FinalUrlSuffix); // The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): //All other (Root Node) // | // +-- Animals & Pet Supplies (CategoryL1) // | | // | +-- Pet Supplies (CategoryL2) // | | | // | | +-- Brand A // | | | // | | +-- Brand B // | | | // | | +-- All other (Brand) // | | // | +-- All other (CategoryL2) // | // +-- Electronics (CategoryL1) // | | // | +-- Brand C (Brand) // | | // | +-- Brand D (Brand) // | | // | +-- All other (Brand) // | // +-- All other (CategoryL1) OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); // Create a product ad. You must add at least one product ad to the ad group. // The product ad identifier can be used for reporting analytics. // Use Merchant Promotions if you want tags to appear at the bottom of your product ad // as "special offer" links, helping to increase customer engagement. For details // on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. var ads = new Ad[] { new ProductAd { } }; OutputStatusMessage("-----\nAddAds:"); AddAdsResponse addAdsResponse = await CampaignManagementExampleHelper.AddAdsAsync( adGroupId : (long)adGroupIds[0], ads : ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); OutputStatusMessage("AdIds:"); CampaignManagementExampleHelper.OutputArrayOfLong(adIds); OutputStatusMessage("PartialErrors:"); CampaignManagementExampleHelper.OutputArrayOfBatchError(adErrors); // Delete the campaign and everything it contains e.g., ad groups and ads. OutputStatusMessage("-----\nDeleteCampaigns:"); await CampaignManagementExampleHelper.DeleteCampaignsAsync( accountId : authorizationData.AccountId, campaignIds : new[] { (long)campaignIds[0] }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}", campaignIds[0])); } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }
public async override Task RunAsync(AuthorizationData authorizationData) { try { ApiEnvironment environment = ((OAuthDesktopMobileAuthCodeGrant)authorizationData.Authentication).Environment; CampaignManagementExampleHelper CampaignManagementExampleHelper = new CampaignManagementExampleHelper(this.OutputStatusMessage); CampaignManagementExampleHelper.CampaignManagementService = new ServiceClient <ICampaignManagementService>(authorizationData, environment); // Get a list of all Bing Merchant Center stores associated with your CustomerId IList <BMCStore> stores = (await CampaignManagementExampleHelper.GetBMCStoresByCustomerIdAsync())?.BMCStores; if (stores == null) { OutputStatusMessage( string.Format("You do not have any BMC stores registered for CustomerId {0}.\n", authorizationData.CustomerId) ); return; } #region ManageCampaign /* Add a new Bing Shopping campaign that will be associated with a ProductScope criterion. * - Set the CampaignType element of the Campaign to Shopping. * - Create a ShoppingSetting instance and set its Priority (0, 1, or 2), SalesCountryCode, and StoreId elements. * Add this shopping setting to the Settings list of the Campaign. */ var campaigns = new[] { new Campaign { CampaignType = CampaignType.Shopping, Settings = new[] { new ShoppingSetting() { Priority = 0, SalesCountryCode = "US", StoreId = (int)stores[0].Id } }, Name = "Bing Shopping Campaign " + DateTime.UtcNow, Description = "Bing Shopping Campaign Example.", // You must choose to set either the shared budget ID or daily amount. // You can set one or the other, but you may not set both. BudgetId = null, DailyBudget = 50, BudgetType = BudgetLimitType.DailyBudgetStandard, TimeZone = "PacificTimeUSCanadaTijuana", // Used with CustomParameters defined in lower level entities such as ad group criterion. TrackingUrlTemplate = "http://tracker.example.com/?season={_season}&promocode={_promocode}&u={lpurl}" } }; AddCampaignsResponse addCampaignsResponse = await CampaignManagementExampleHelper.AddCampaignsAsync(authorizationData.AccountId, campaigns); long?[] campaignIds = addCampaignsResponse.CampaignIds.ToArray(); BatchError[] campaignErrors = addCampaignsResponse.PartialErrors.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(campaignIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(campaignErrors); long campaignId = (long)campaignIds[0]; /* Optionally, you can create a ProductScope criterion that will be associated with your Bing Shopping campaign. * Use the product scope criterion to include a subset of your product catalog, for example a specific brand, * category, or product type. A campaign can only be associated with one ProductScope, which contains a list * of up to 7 ProductCondition. You'll also be able to specify more specific product conditions for each ad group. */ var campaignCriterions = new BiddableCampaignCriterion[] { new BiddableCampaignCriterion() { CampaignId = campaignId, CriterionBid = null, // Not applicable for product scope Criterion = new ProductScope() { Conditions = new ProductCondition[] { new ProductCondition { Operand = "Condition", Attribute = "New" }, new ProductCondition { Operand = "CustomLabel0", Attribute = "MerchantDefinedCustomLabel" }, } }, } }; var addCampaignCriterionsResponse = await(CampaignManagementExampleHelper.AddCampaignCriterionsAsync( campaignCriterions, CampaignCriterionType.ProductScope) ); #endregion ManageCampaign #region ManageAdGroup // Create the ad group that will have the product partitions. var adGroups = new[] { new AdGroup { Name = "Product Categories", StartDate = null, EndDate = new Microsoft.BingAds.V12.CampaignManagement.Date { Month = 12, Day = 31, Year = DateTime.UtcNow.Year + 1 }, Language = "English", CpcBid = new Bid { Amount = 0.090 } } }; AddAdGroupsResponse addAdGroupsResponse = await CampaignManagementExampleHelper.AddAdGroupsAsync((long)campaignId, adGroups, null); long?[] adGroupIds = addAdGroupsResponse.AdGroupIds.ToArray(); BatchError[] adGroupErrors = addAdGroupsResponse.PartialErrors.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(adGroupIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(adGroupErrors); long adGroupId = (long)adGroupIds[0]; #region BidAllProducts var helper = new PartitionActionHelper(adGroupId); var root = helper.AddUnit( null, new ProductCondition { Operand = "All", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying only the root as a Unit with a bid . . . \n"); var applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync(helper.PartitionActions); var adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( null, adGroupId, AdGroupCriterionType.ProductPartition ); OutputStatusMessage("The ad group's product partition only has a tree root node: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); /* * Let's update the bid of the root Unit we just added. */ BiddableAdGroupCriterion updatedRoot = new BiddableAdGroupCriterion { Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[0], CriterionBid = new FixedBid { Amount = 0.45 } }; helper = new PartitionActionHelper(adGroupId); helper.UpdatePartition(updatedRoot); OutputStatusMessage("Updating the bid for the tree root node . . . \n"); await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( null, adGroupId, AdGroupCriterionType.ProductPartition ); OutputStatusMessage("Updated the bid for the tree root node: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); #endregion BidAllProducts #region InitializeTree /* * Now we will overwrite any existing tree root, and build a product partition group tree structure in multiple steps. * You could build the entire tree in a single call since there are less than 5,000 nodes; however, * we will build it in steps to demonstrate how to use the results from ApplyProductPartitionActions to update the tree. * * For a list of validation rules, see the Product Ads technical guide: * https://docs.microsoft.com/en-us/bingads/guides/product-ads */ helper = new PartitionActionHelper(adGroupId); /* * Check whether a root node exists already. */ adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( null, adGroupId, AdGroupCriterionType.ProductPartition ); var existingRoot = GetRootNode(adGroupCriterions?.AdGroupCriterions); if (existingRoot != null) { helper.DeletePartition(existingRoot); } root = helper.AddSubdivision( null, new ProductCondition { Operand = "All", Attribute = null } ); /* * The direct children of any node must have the same Operand. * For this example we will use CategoryL1 nodes as children of the root. * For a list of valid CategoryL1 through CategoryL5 values, see the Bing Category Taxonomy: * http://go.microsoft.com/fwlink?LinkId=507666 */ var animalsSubdivision = helper.AddSubdivision( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Animals & Pet Supplies" } ); /* * If you use a CategoryL2 node, it must be a descendant (child or later) of a CategoryL1 node. * In other words you cannot have a CategoryL2 node as parent of a CategoryL1 node. * For this example we will a CategoryL2 node as child of the CategoryL1 Animals & Pet Supplies node. */ var petSuppliesSubdivision = helper.AddSubdivision( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = "Pet Supplies" } ); var brandA = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand A" }, 0.35, false ); /* * If you won't bid on Brand B, set the helper method's bidAmount to '0' and isNegative to true. * The helper method will create a NegativeAdGroupCriterion and apply the condition. */ var brandB = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand B" }, 0, true ); var otherBrands = helper.AddUnit( petSuppliesSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); var otherPetSupplies = helper.AddUnit( animalsSubdivision, new ProductCondition { Operand = "CategoryL2", Attribute = null }, 0.35, false ); var electronics = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" }, 0.35, false ); var otherCategoryL1 = helper.AddUnit( root, new ProductCondition { Operand = "CategoryL1", Attribute = null }, 0.35, false ); OutputStatusMessage("Applying product partitions to the ad group . . . \n"); applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync(helper.PartitionActions); // To retrieve product partitions after they have been applied, call GetAdGroupCriterionsByIds. // The product partition with ParentCriterionId set to null is the root node. adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( null, adGroupId, AdGroupCriterionType.ProductPartition ); /* * The product partition group tree now has 9 nodes. * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | +-- All other (CategoryL1) | */ OutputStatusMessage("The product partition group tree now has 9 nodes: \n"); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); #endregion InitializeTree #region UpdateTree /* * Let's replace the Electronics (CategoryL1) node created above with an Electronics (CategoryL1) node that * has children i.e. Brand C (Brand), Brand D (Brand), and All other (Brand) as follows: * * Electronics (CategoryL1) | +-- Brand C (Brand) | +-- Brand D (Brand) | +-- All other (Brand) | */ helper = new PartitionActionHelper(adGroupId); /* * To replace a node we must know its Id and its ParentCriterionId. In this case the parent of the node * we are replacing is All other (Root Node), and was created at Index 1 of the previous ApplyProductPartitionActions call. * The node that we are replacing is Electronics (CategoryL1), and was created at Index 8. */ var rootId = applyProductPartitionActionsResponse.AdGroupCriterionIds[1]; electronics.Id = applyProductPartitionActionsResponse.AdGroupCriterionIds[8]; helper.DeletePartition(electronics); var parent = new BiddableAdGroupCriterion() { Id = rootId }; var electronicsSubdivision = helper.AddSubdivision( parent, new ProductCondition { Operand = "CategoryL1", Attribute = "Electronics" } ); var brandC = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand C" }, 0.35, false ); var brandD = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = "Brand D" }, 0.35, false ); var otherElectronicsBrands = helper.AddUnit( electronicsSubdivision, new ProductCondition { Operand = "Brand", Attribute = null }, 0.35, false ); OutputStatusMessage( "Updating the product partition group to refine Electronics (CategoryL1) with 3 child nodes . . . \n" ); applyProductPartitionActionsResponse = await CampaignManagementExampleHelper.ApplyProductPartitionActionsAsync(helper.PartitionActions); adGroupCriterions = await CampaignManagementExampleHelper.GetAdGroupCriterionsByIdsAsync( null, adGroupId, AdGroupCriterionType.ProductPartition ); /* * The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): * * All other (Root Node) | +-- Animals & Pet Supplies (CategoryL1) | | | +-- Pet Supplies (CategoryL2) | | | | | +-- Brand A | | | | | +-- Brand B | | | | | +-- All other (Brand) | | | +-- All other (CategoryL2) | +-- Electronics (CategoryL1) | | | +-- Brand C (Brand) | | | +-- Brand D (Brand) | | | +-- All other (Brand) | +-- All other (CategoryL1) | */ OutputStatusMessage( "The product partition group tree now has 12 nodes, including the children of Electronics (CategoryL1): \n" ); OutputProductPartitions(adGroupCriterions?.AdGroupCriterions); #endregion UpdateTree #endregion ManageAdGroup #region ManageAds /* * Create a product ad. You must add at least one product ad to the ad group. * The product ad identifier can be used for reporting analytics. * Use Merchant Promotions if you want tags to appear at the bottom of your product ad * as "special offer" links, helping to increase customer engagement. For details * on Merchant Promotions see https://help.bingads.microsoft.com/#apex/3/en/56805/0. */ var ads = new Ad[] { new ProductAd { } }; AddAdsResponse addAdsResponse = await CampaignManagementExampleHelper.AddAdsAsync((long)adGroupIds[0], ads); long?[] adIds = addAdsResponse.AdIds.ToArray(); BatchError[] adErrors = addAdsResponse.PartialErrors.ToArray(); CampaignManagementExampleHelper.OutputArrayOfLong(adIds); CampaignManagementExampleHelper.OutputArrayOfBatchError(adErrors); #endregion ManageAds #region CleanUp /* Delete the campaign, ad group, criterion, and ad that were previously added. * You should remove this region if you want to view the added entities in the * Bing Ads web application or another tool. */ await CampaignManagementExampleHelper.DeleteCampaignsAsync(authorizationData.AccountId, new[] { campaignId }); OutputStatusMessage(string.Format("Deleted Campaign Id {0}\n", campaignId)); #endregion CleanUp } // Catch authentication exceptions catch (OAuthTokenRequestException ex) { OutputStatusMessage(string.Format("Couldn't get OAuth tokens. Error: {0}. Description: {1}", ex.Details.Error, ex.Details.Description)); } // Catch Campaign Management service exceptions catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.AdApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.Errors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.ApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (FaultException <Microsoft.BingAds.V12.CampaignManagement.EditorialApiFaultDetail> ex) { OutputStatusMessage(string.Join("; ", ex.Detail.OperationErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); OutputStatusMessage(string.Join("; ", ex.Detail.BatchErrors.Select(error => string.Format("{0}: {1}", error.Code, error.Message)))); } catch (Exception ex) { OutputStatusMessage(ex.Message); } }