From eb0cb759dbc09e0bbfdbf9b4942ce303b72b6dec Mon Sep 17 00:00:00 2001 From: devchas Date: Tue, 7 Dec 2021 13:08:53 -0500 Subject: [PATCH 1/5] Add basic pmax campaign example Change-Id: I3e196f93febb498713d1ba9a46e713ffb5d71b61 --- .../AddPerformanceMaxCampaign.java | 543 ++++++++++++++++++ 1 file changed, 543 insertions(+) create mode 100644 google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java new file mode 100644 index 0000000000..c44717b0b2 --- /dev/null +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java @@ -0,0 +1,543 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.ads.googleads.examples.advancedoperations; + +import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime; + +import com.beust.jcommander.Parameter; +import com.google.ads.googleads.examples.utils.ArgumentNames; +import com.google.ads.googleads.examples.utils.CodeSampleParams; +import com.google.ads.googleads.lib.GoogleAdsClient; +import com.google.ads.googleads.v9.common.ImageAsset; +import com.google.ads.googleads.v9.common.LanguageInfo; +import com.google.ads.googleads.v9.common.LocationInfo; +import com.google.ads.googleads.v9.common.MaximizeConversionValue; +import com.google.ads.googleads.v9.common.TextAsset; +import com.google.ads.googleads.v9.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType; +import com.google.ads.googleads.v9.enums.AssetFieldTypeEnum.AssetFieldType; +import com.google.ads.googleads.v9.enums.AssetGroupStatusEnum.AssetGroupStatus; +import com.google.ads.googleads.v9.enums.BiddingStrategyTypeEnum.BiddingStrategyType; +import com.google.ads.googleads.v9.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod; +import com.google.ads.googleads.v9.enums.CampaignStatusEnum.CampaignStatus; +import com.google.ads.googleads.v9.errors.GoogleAdsError; +import com.google.ads.googleads.v9.errors.GoogleAdsException; +import com.google.ads.googleads.v9.resources.Asset; +import com.google.ads.googleads.v9.resources.AssetGroup; +import com.google.ads.googleads.v9.resources.AssetGroupAsset; +import com.google.ads.googleads.v9.resources.Campaign; +import com.google.ads.googleads.v9.resources.CampaignBudget; +import com.google.ads.googleads.v9.resources.CampaignCriterion; +import com.google.ads.googleads.v9.services.AssetGroupAssetOperation; +import com.google.ads.googleads.v9.services.AssetGroupOperation; +import com.google.ads.googleads.v9.services.AssetOperation; +import com.google.ads.googleads.v9.services.CampaignBudgetOperation; +import com.google.ads.googleads.v9.services.CampaignCriterionOperation; +import com.google.ads.googleads.v9.services.CampaignOperation; +import com.google.ads.googleads.v9.services.GoogleAdsServiceClient; +import com.google.ads.googleads.v9.services.MutateGoogleAdsResponse; +import com.google.ads.googleads.v9.services.MutateOperation; +import com.google.ads.googleads.v9.services.MutateOperationResponse; +import com.google.ads.googleads.v9.utils.ResourceNames; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import com.google.protobuf.ByteString; +import com.google.protobuf.Descriptors.FieldDescriptor; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import org.joda.time.DateTime; + +/** + * This example shows how to create a Performance Max campaign. + * + *

For more information about Performance Max campaigns, see + * https://developers.google.com/google-ads/api/docs/performance-max/overview + * + *

Prerequisites: - You must have at least one conversion action in the account. For more about + * conversion actions, see + * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions + */ +public class AddPerformanceMaxCampaign { + + // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always + // negative and unique within one mutate request. + // + //

See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further + // details. + // + //

These temporary IDs are fixed because they are used in multiple places. + private static final int BUDGET_TEMPORARY_ID = -1; + private static final int PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2; + private static final int ASSET_GROUP_TEMPORARY_ID = -3; + + // There are also entities that will be created in the same request but do not + // need to be fixed temporary IDs because they are referenced only once. + private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1; + + private static class AddPerformanceMaxCampaignParams extends CodeSampleParams { + + @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) + private Long customerId; + } + + public static void main(String[] args) throws IOException { + AddPerformanceMaxCampaignParams params = new AddPerformanceMaxCampaignParams(); + if (!params.parseArguments(args)) { + + // Either pass the required parameters for this example on the command line, or insert them + // into the code here. See the parameter class definition above for descriptions. + params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE"); + } + + GoogleAdsClient googleAdsClient = null; + try { + googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build(); + } catch (FileNotFoundException fnfe) { + System.err.printf( + "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe); + System.exit(1); + } catch (IOException ioe) { + System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe); + System.exit(1); + } + + try { + new AddPerformanceMaxCampaign().runExample(googleAdsClient, params.customerId); + } catch (GoogleAdsException gae) { + // GoogleAdsException is the base class for most exceptions thrown by an API request. + // Instances of this exception have a message and a GoogleAdsFailure that contains a + // collection of GoogleAdsErrors that indicate the underlying causes of the + // GoogleAdsException. + System.err.printf( + "Request ID %s failed due to GoogleAdsException. Underlying errors:%n", + gae.getRequestId()); + int i = 0; + for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) { + System.err.printf(" Error %d: %s%n", i++, googleAdsError); + } + System.exit(1); + } + } + + // [START add_performance_max_campaign] + /** + * Runs the example. + * + * @param googleAdsClient the Google Ads API client. + * @param customerId the client customer ID. + */ + private void runExample(GoogleAdsClient googleAdsClient, long customerId) throws IOException { + // [START add_performance_max_campaign_1] + // Performance Max campaigns require that repeated assets such as headlines + // and descriptions be created before the campaign. + // For the list of required assets for a Performance Max campaign, see + // https://developers.google.com/google-ads/api/docs/performance-max/assets + // + // Creates the headlines. + List headlines = ImmutableList.of("Travel", "Travel Reviews", "Book travel"); + List headlineAssetResourceNames = + createMultipleTextAssets(googleAdsClient, customerId, headlines); + // Creates the descriptions. + List descriptions = ImmutableList.of("Take to the air!", "Fly to the sky!"); + List descriptionAssetResourceNames = + createMultipleTextAssets(googleAdsClient, customerId, descriptions); + + // The below methods create and return MutateOperations that we later + // provide to the GoogleAdsService.Mutate method in order to create the + // entities in a single request. Since the entities for a Performance Max + // campaign are closely tied to one-another, it's considered a best practice + // to create them in a single Mutate request, so they all complete + // successfully or fail entirely, leaving no orphaned entities. See: + // https://developers.google.com/google-ads/api/docs/mutating/overview + List mutateOperations = new ArrayList<>(); + mutateOperations.add(createCampaignBudgetOperation(customerId)); + mutateOperations.add(createPerformanceMaxCampaignOperation(customerId)); + mutateOperations.addAll(createCampaignCriterionOperations(customerId)); + mutateOperations.addAll( + createAssetGroupOperations( + customerId, headlineAssetResourceNames, descriptionAssetResourceNames)); + + try (GoogleAdsServiceClient googleAdsServiceClient = + googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { + MutateGoogleAdsResponse response = + googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations); + printResponseDetails(response); + } + // [END add_performance_max_campaign_1] + } + + // [START add_performance_max_campaign_2] + /** Creates a MutateOperation that creates a new CampaignBudget. */ + private MutateOperation createCampaignBudgetOperation(long customerId) { + CampaignBudget campaignBudget = + CampaignBudget.newBuilder() + .setName("Performance Max campaign budget #" + getPrintableDateTime()) + // The budget period already defaults to DAILY. + .setAmountMicros(50_000_000) + .setDeliveryMethod(BudgetDeliveryMethod.STANDARD) + // A Performance Max campaign cannot use a shared campaign budget. + .setExplicitlyShared(false) + // Set a temporary ID in the budget's resource name, so it can be referenced + // by the campaign in later steps. + .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID)) + .build(); + + return MutateOperation.newBuilder() + .setCampaignBudgetOperation( + CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build()) + .build(); + } + // [END add_performance_max_campaign_2] + + // [START add_performance_max_campaign_3] + /** Creates a MutateOperation that creates a new Performance Max campaign. */ + private MutateOperation createPerformanceMaxCampaignOperation(long customerId) { + Campaign performanceMaxCampaign = + Campaign.newBuilder() + .setName("Performance Max campaign #" + getPrintableDateTime()) + // Sets the campaign status as PAUSED. The campaign is the only entity in + // the mutate request that should have its status set. + .setStatus(CampaignStatus.PAUSED) + // All Performance Max campaigns have an advertising_channel_type of + // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set. + .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX) + // Bidding strategy must be set directly on the campaign. + // Setting a portfolio bidding strategy by resource name is not supported. + // Max Conversion and Maximize Conversion Value are the only strategies + // supported for Performance Max campaigns. + // An optional ROAS (Return on Advertising Spend) can be set for + // maximize_conversion_value. The ROAS value must be specified as a ratio in + // the API. It is calculated by dividing "total value" by "total spend". + // For more information on Maximize Conversion Value, see the support + // article: http://support.google.com/google-ads/answer/7684216. + // A targetRoas of 3.5 corresponds to a 350% return on ad spend. + .setBiddingStrategyType(BiddingStrategyType.MAXIMIZE_CONVERSION_VALUE) + .setMaximizeConversionValue( + MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build()) + // Sets the Final URL expansion opt out. This flag is specific to + // Performance Max campaigns. If opted out (True), only the final URLs in + // the asset group or URLs specified in the advertiser's Google Merchant + // Center or business data feeds are targeted. + // If opted in (False), the entire domain will be targeted. For best + // results, set this value to false to opt in and allow URL expansions. You + // can optionally add exclusions to limit traffic to parts of your website. + .setUrlExpansionOptOut(false) + // Assigns the resource name with a temporary ID. + .setResourceName( + ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)) + // Sets the budget using the given budget resource name. + .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID)) + // Optional fields. + .setStartDate(new DateTime().plusDays(1).toString("yyyyMMdd")) + .setEndDate(new DateTime().plusDays(365).toString("yyyyMMdd")) + .build(); + + return MutateOperation.newBuilder() + .setCampaignOperation( + CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build()) + .build(); + } + // [END add_performance_max_campaign_3] + + // [START add_performance_max_campaign_4] + /** Creates a list of MutateOperations that create new campaign criteria. */ + private List createCampaignCriterionOperations(long customerId) { + String campaignResourceName = + ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID); + List campaignCriteria = new ArrayList<>(); + // Sets the LOCATION campaign criteria. + // Targets all of New York City except Brooklyn. + // Location IDs are listed here: + // https://developers.google.com/google-ads/api/reference/data/geotargets + // and they can also be retrieved using the GeoTargetConstantService as shown + // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting + // + // We will add one positive location target for New York City (ID=1023191) + // and one negative location target for Brooklyn (ID=1022762). + // First, adds the positive (negative = False) for New York City. + campaignCriteria.add( + CampaignCriterion.newBuilder() + .setCampaign(campaignResourceName) + .setLocation( + LocationInfo.newBuilder() + .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191)) + .build()) + .setNegative(false) + .build()); + // Next adds the negative target for Brooklyn. + campaignCriteria.add( + CampaignCriterion.newBuilder() + .setCampaign(campaignResourceName) + .setLocation( + LocationInfo.newBuilder() + .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762)) + .build()) + .setNegative(true) + .build()); + // Sets the LANGUAGE campaign criterion. + campaignCriteria.add( + CampaignCriterion.newBuilder() + .setCampaign(campaignResourceName) + // Sets the language. + // For a list of all language codes, see: + // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 + .setLanguage( + LanguageInfo.newBuilder() + .setLanguageConstant(ResourceNames.languageConstant(1000)) // English + .build()) + .build()); + // Returns a list of mutate operations with one operation per criterion. + return campaignCriteria.stream() + .map( + criterion -> + MutateOperation.newBuilder() + .setCampaignCriterionOperation( + CampaignCriterionOperation.newBuilder().setCreate(criterion).build()) + .build()) + .collect(Collectors.toList()); + } + // [END add_performance_max_campaign_4] + + // [START add_performance_max_campaign_5] + /** Creates multiple text assets and returns the list of resource names. */ + private List createMultipleTextAssets( + GoogleAdsClient googleAdsClient, long customerId, List texts) { + List mutateOperations = new ArrayList<>(); + for (String text : texts) { + Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build(); + AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); + mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); + } + + List assetResourceNames = new ArrayList<>(); + // Creates the service client. + try (GoogleAdsServiceClient googleAdsServiceClient = + googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { + // Sends the operations in a single Mutate request. + MutateGoogleAdsResponse response = + googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations); + for (MutateOperationResponse result : response.getMutateOperationResponsesList()) { + if (result.hasAssetResult()) { + assetResourceNames.add(result.getAssetResult().getResourceName()); + } + } + printResponseDetails(response); + } + return assetResourceNames; + } + // [END add_performance_max_campaign_5] + + // [START add_performance_max_campaign_6] + /** Creates a list of MutateOperations that create a new AssetGroup. */ + private List createAssetGroupOperations( + long customerId, + List headlineAssetResourceNames, + List descriptionAssetResourceNames) + throws IOException { + List mutateOperations = new ArrayList<>(); + String campaignResourceName = + ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID); + String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID); + // Creates the AssetGroup. + AssetGroup assetGroup = + AssetGroup.newBuilder() + .setName("Performance Max asset group #" + getPrintableDateTime()) + .setCampaign(campaignResourceName) + .addFinalUrls("http://www.example.com") + .addFinalMobileUrls("http://www.example.com") + .setStatus(AssetGroupStatus.PAUSED) + .setResourceName(assetGroupResourceName) + .build(); + AssetGroupOperation assetGroupOperation = + AssetGroupOperation.newBuilder().setCreate(assetGroup).build(); + mutateOperations.add( + MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build()); + + // For the list of required assets for a Performance Max campaign, see + // https://developers.google.com/google-ads/api/docs/performance-max/assets + + // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset + // and providing: + // the resource name of the AssetGroup + // the resource name of the Asset + // the field_type of the Asset in this AssetGroup. + + // To learn more about AssetGroups, see + // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups + + // Links the previously created multiple text assets. + + // Links the headline assets. + for (String resourceName : headlineAssetResourceNames) { + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(AssetFieldType.HEADLINE) + .setAssetGroup(assetGroupResourceName) + .setAsset(resourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder() + .setAssetGroupAssetOperation(assetGroupAssetOperation) + .build()); + } + + // Links the description assets. + for (String resourceName : descriptionAssetResourceNames) { + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(AssetFieldType.DESCRIPTION) + .setAssetGroup(assetGroupResourceName) + .setAsset(resourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder() + .setAssetGroupAssetOperation(assetGroupAssetOperation) + .build()); + } + + // Creates and links the long headline text asset. + List createAndLinkTextAssetOperations = + createAndLinkTextAsset(customerId, "Travel the World", AssetFieldType.LONG_HEADLINE); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the business name text asset. + createAndLinkTextAssetOperations = + createAndLinkTextAsset(customerId, "Interplanetary Cruises", AssetFieldType.BUSINESS_NAME); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the image assets. + + // Creates and links the Logo Asset. + createAndLinkTextAssetOperations = + createAndLinkImageAsset(customerId, "https://gaagl.page.link/bjYi", AssetFieldType.LOGO); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the Marketing Image Asset. + createAndLinkTextAssetOperations = + createAndLinkImageAsset( + customerId, "https://gaagl.page.link/Eit5", AssetFieldType.MARKETING_IMAGE); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the Square Marketing Image Asset. + createAndLinkTextAssetOperations = + createAndLinkImageAsset( + customerId, "https://gaagl.page.link/bjYi", AssetFieldType.SQUARE_MARKETING_IMAGE); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + return mutateOperations; + } + // [END add_performance_max_campaign_6] + + // [START add_performance_max_campaign_7] + /** Creates a list of MutateOperations that create a new linked text asset. */ + List createAndLinkTextAsset( + long customerId, String text, AssetFieldType assetFieldType) { + List mutateOperations = new ArrayList<>(); + String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId()); + // Creates the Text Asset. + Asset asset = + Asset.newBuilder() + .setResourceName(assetResourceName) + .setTextAsset(TextAsset.newBuilder().setText(text).build()) + .build(); + AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); + mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); + + // Creates an AssetGroupAsset to link the Asset to the AssetGroup. + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(assetFieldType) + .setAssetGroup(ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)) + .setAsset(assetResourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build()); + + return mutateOperations; + } + // [END add_performance_max_campaign_7] + + // [START add_performance_max_campaign_8] + /** Creates a list of MutateOperations that create a new linked text asset. */ + List createAndLinkImageAsset( + long customerId, String url, AssetFieldType assetFieldType) throws IOException { + List mutateOperations = new ArrayList<>(); + String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId()); + // Creates a media file. + byte[] assetBytes = ByteStreams.toByteArray(new URL(url).openStream()); + + // Creates the Image Asset. + Asset asset = + Asset.newBuilder() + .setResourceName(assetResourceName) + .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(assetBytes)).build()) + .build(); + AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); + mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); + + // Creates an AssetGroupAsset to link the Asset to the AssetGroup. + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(assetFieldType) + .setAssetGroup(ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)) + .setAsset(assetResourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build()); + + return mutateOperations; + } + // [END add_performance_max_campaign_8] + + /** + * Prints the details of a MutateGoogleAdsResponse. + * + *

Parses the "response" oneof field name and uses it to extract the new entity's name and + * resource name. + */ + private void printResponseDetails(MutateGoogleAdsResponse response) { + // Parses the Mutate response to print details about the entities that were created by the + // request. + String suffix = "_result"; + for (MutateOperationResponse result : response.getMutateOperationResponsesList()) { + for (Entry responseFields : result.getAllFields().entrySet()) { + String fieldName = responseFields.getKey().getName(); + String value = responseFields.getValue().toString().trim(); + if (fieldName.endsWith(suffix)) { + fieldName = fieldName.substring(0, fieldName.length() - suffix.length()); + } + System.out.printf("Created a(n) %s with %s.%n", fieldName, value); + } + } + } + + /** Returns the next temporary ID and decreases it by one. */ + private long getNextTemporaryId() { + return temporaryId--; + } + // [END add_performance_max_campaign] +} From 44ab70a9e23ffa9d0fa4ce89f54b69b1c805a88c Mon Sep 17 00:00:00 2001 From: devchas Date: Tue, 7 Dec 2021 17:33:01 -0500 Subject: [PATCH 2/5] Add performance max retail campaign example Change-Id: Icf24c6720c9e166b4b245b8912492fa748aa285e --- .../AddPerformanceMaxCampaign.java | 3 + .../AddPerformanceMaxRetailCampaign.java | 666 ++++++++++++++++++ 2 files changed, 669 insertions(+) create mode 100644 google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java index c44717b0b2..e5579f3c4d 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java @@ -72,6 +72,9 @@ *

Prerequisites: - You must have at least one conversion action in the account. For more about * conversion actions, see * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions + * + *

This example uses the default customer conversion goals. For an example of setting + * campaign-specific conversion goals, see {@link AddPerformanceMaxCampaign}. */ public class AddPerformanceMaxCampaign { diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java new file mode 100644 index 0000000000..587938549b --- /dev/null +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java @@ -0,0 +1,666 @@ +// Copyright 2021 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.ads.googleads.examples.shoppingads; + +import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime; + +import com.beust.jcommander.Parameter; +import com.google.ads.googleads.examples.utils.ArgumentNames; +import com.google.ads.googleads.examples.utils.CodeSampleParams; +import com.google.ads.googleads.lib.GoogleAdsClient; +import com.google.ads.googleads.lib.utils.FieldMasks; +import com.google.ads.googleads.v9.common.ImageAsset; +import com.google.ads.googleads.v9.common.LanguageInfo; +import com.google.ads.googleads.v9.common.LocationInfo; +import com.google.ads.googleads.v9.common.MaximizeConversionValue; +import com.google.ads.googleads.v9.common.TextAsset; +import com.google.ads.googleads.v9.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType; +import com.google.ads.googleads.v9.enums.AssetFieldTypeEnum.AssetFieldType; +import com.google.ads.googleads.v9.enums.AssetGroupStatusEnum.AssetGroupStatus; +import com.google.ads.googleads.v9.enums.BiddingStrategyTypeEnum.BiddingStrategyType; +import com.google.ads.googleads.v9.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod; +import com.google.ads.googleads.v9.enums.CampaignStatusEnum.CampaignStatus; +import com.google.ads.googleads.v9.enums.ConversionActionCategoryEnum.ConversionActionCategory; +import com.google.ads.googleads.v9.enums.ConversionOriginEnum.ConversionOrigin; +import com.google.ads.googleads.v9.errors.GoogleAdsError; +import com.google.ads.googleads.v9.errors.GoogleAdsException; +import com.google.ads.googleads.v9.resources.Asset; +import com.google.ads.googleads.v9.resources.AssetGroup; +import com.google.ads.googleads.v9.resources.AssetGroupAsset; +import com.google.ads.googleads.v9.resources.Campaign; +import com.google.ads.googleads.v9.resources.Campaign.ShoppingSetting; +import com.google.ads.googleads.v9.resources.CampaignBudget; +import com.google.ads.googleads.v9.resources.CampaignConversionGoal; +import com.google.ads.googleads.v9.resources.CampaignCriterion; +import com.google.ads.googleads.v9.resources.CustomerConversionGoal; +import com.google.ads.googleads.v9.services.AssetGroupAssetOperation; +import com.google.ads.googleads.v9.services.AssetGroupOperation; +import com.google.ads.googleads.v9.services.AssetOperation; +import com.google.ads.googleads.v9.services.CampaignBudgetOperation; +import com.google.ads.googleads.v9.services.CampaignConversionGoalOperation; +import com.google.ads.googleads.v9.services.CampaignCriterionOperation; +import com.google.ads.googleads.v9.services.CampaignOperation; +import com.google.ads.googleads.v9.services.GoogleAdsRow; +import com.google.ads.googleads.v9.services.GoogleAdsServiceClient; +import com.google.ads.googleads.v9.services.GoogleAdsServiceClient.SearchPagedResponse; +import com.google.ads.googleads.v9.services.MutateGoogleAdsResponse; +import com.google.ads.googleads.v9.services.MutateOperation; +import com.google.ads.googleads.v9.services.MutateOperationResponse; +import com.google.ads.googleads.v9.utils.ResourceNames; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import com.google.protobuf.ByteString; +import com.google.protobuf.Descriptors.FieldDescriptor; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import org.joda.time.DateTime; + +/** + * This example shows how to create a Performance Max retail campaign. + * + *

This will be created for "All products". + * + *

For more information about Performance Max retail campaigns, see + * https://developers.google.com/google-ads/api/docs/performance-max/retail + * + *

Prerequisites: - You need to have access to a Merchant Center account. You can find + * instructions to create a Merchant Center account here: + * https://support.google.com/merchants/answer/188924. This account must be linked to your Google + * Ads account. The integration instructions can be found at: + * https://developers.google.com/google-ads/api/docs/shopping-ads/merchant-center - You need your + * Google Ads account to track conversions. The different ways to track conversions can be found + * here: https://support.google.com/google-ads/answer/1722054. - You must have at least one + * conversion action in the account. For more about conversion actions, see + * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions + */ +public class AddPerformanceMaxRetailCampaign { + + // We specify temporary IDs that are specific to a single mutate request. Temporary IDs are always + // negative and unique within one mutate request. + // + //

See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further + // details. + // + //

These temporary IDs are fixed because they are used in multiple places. + private static final int BUDGET_TEMPORARY_ID = -1; + private static final int PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2; + private static final int ASSET_GROUP_TEMPORARY_ID = -3; + + // There are also entities that will be created in the same request but do not + // need to be fixed temporary IDs because they are referenced only once. + private static long temporaryId = ASSET_GROUP_TEMPORARY_ID - 1; + + private static class AddPerformanceMaxRetailCampaignParams extends CodeSampleParams { + + @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true) + private Long customerId; + + @Parameter( + names = ArgumentNames.MERCHANT_CENTER_ACCOUNT_ID, + required = true, + description = "The Merchant Center account ID.") + private long merchantCenterAccountId; + + @Parameter( + names = ArgumentNames.COUNTRY_CODE, + required = true, + description = "The sales country of products to include in the campaign.") + private String countryCode; + } + + public static void main(String[] args) throws IOException { + AddPerformanceMaxRetailCampaignParams params = new AddPerformanceMaxRetailCampaignParams(); + if (!params.parseArguments(args)) { + + // Either pass the required parameters for this example on the command line, or insert them + // into the code here. See the parameter class definition above for descriptions. + params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE"); + params.merchantCenterAccountId = Long.parseLong("INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE"); + params.countryCode = "INSERT_COUNTRY_CODE_HERE"; + } + + GoogleAdsClient googleAdsClient = null; + try { + googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build(); + } catch (FileNotFoundException fnfe) { + System.err.printf( + "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe); + System.exit(1); + } catch (IOException ioe) { + System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe); + System.exit(1); + } + + try { + new AddPerformanceMaxRetailCampaign() + .runExample( + googleAdsClient, + params.customerId, + params.merchantCenterAccountId, + params.countryCode); + } catch (GoogleAdsException gae) { + // GoogleAdsException is the base class for most exceptions thrown by an API request. + // Instances of this exception have a message and a GoogleAdsFailure that contains a + // collection of GoogleAdsErrors that indicate the underlying causes of the + // GoogleAdsException. + System.err.printf( + "Request ID %s failed due to GoogleAdsException. Underlying errors:%n", + gae.getRequestId()); + int i = 0; + for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) { + System.err.printf(" Error %d: %s%n", i++, googleAdsError); + } + System.exit(1); + } + } + + // [START add_performance_max_retail_campaign] + /** + * Runs the example. + * + * @param googleAdsClient the Google Ads API client. + * @param customerId the client customer ID. + * @param merchantCenterAccountId the Merchant Center account ID. + * @param countryCode sales country of products to include in the campaign. + */ + private void runExample( + GoogleAdsClient googleAdsClient, + long customerId, + long merchantCenterAccountId, + String countryCode) + throws IOException { + // [START add_performance_max_retail_campaign_1] + // This campaign will override the customer conversion goals. + // Retrieve the current list of customer conversion goals. + List customerConversionGoals = + getCustomerConversionGoals(googleAdsClient, customerId); + + // Performance Max campaigns require that repeated assets such as headlines + // and descriptions be created before the campaign. + // For the list of required assets for a Performance Max campaign, see + // https://developers.google.com/google-ads/api/docs/performance-max/assets + // + // Creates the headlines. + List headlines = ImmutableList.of("Travel", "Travel Reviews", "Book travel"); + List headlineAssetResourceNames = + createMultipleTextAssets(googleAdsClient, customerId, headlines); + // Creates the descriptions. + List descriptions = ImmutableList.of("Take to the air!", "Fly to the sky!"); + List descriptionAssetResourceNames = + createMultipleTextAssets(googleAdsClient, customerId, descriptions); + + // The below methods create and return MutateOperations that we later + // provide to the GoogleAdsService.Mutate method in order to create the + // entities in a single request. Since the entities for a Performance Max + // campaign are closely tied to one-another, it's considered a best practice + // to create them in a single Mutate request, so they all complete + // successfully or fail entirely, leaving no orphaned entities. See: + // https://developers.google.com/google-ads/api/docs/mutating/overview + List mutateOperations = new ArrayList<>(); + mutateOperations.add(createCampaignBudgetOperation(customerId)); + mutateOperations.add( + createPerformanceMaxCampaignOperation(customerId, merchantCenterAccountId, countryCode)); + mutateOperations.addAll(createCampaignCriterionOperations(customerId)); + mutateOperations.addAll( + createAssetGroupOperations( + customerId, headlineAssetResourceNames, descriptionAssetResourceNames)); + mutateOperations.addAll(createConversionGoalOperations(customerId, customerConversionGoals)); + + try (GoogleAdsServiceClient googleAdsServiceClient = + googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { + MutateGoogleAdsResponse response = + googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations); + printResponseDetails(response); + } + // [END add_performance_max_retail_campaign_1] + } + + // [START add_performance_max_retail_campaign_2] + /** Creates a MutateOperation that creates a new CampaignBudget. */ + private MutateOperation createCampaignBudgetOperation(long customerId) { + CampaignBudget campaignBudget = + CampaignBudget.newBuilder() + .setName("Performance Max retail campaign budget #" + getPrintableDateTime()) + // The budget period already defaults to DAILY. + .setAmountMicros(50_000_000) + .setDeliveryMethod(BudgetDeliveryMethod.STANDARD) + // A Performance Max campaign cannot use a shared campaign budget. + .setExplicitlyShared(false) + // Set a temporary ID in the budget's resource name, so it can be referenced + // by the campaign in later steps. + .setResourceName(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID)) + .build(); + + return MutateOperation.newBuilder() + .setCampaignBudgetOperation( + CampaignBudgetOperation.newBuilder().setCreate(campaignBudget).build()) + .build(); + } + // [END add_performance_max_retail_campaign_2] + + // [START add_performance_max_retail_campaign_3] + /** Creates a MutateOperation that creates a new Performance Max campaign. */ + private MutateOperation createPerformanceMaxCampaignOperation( + long customerId, long merchantCenterAccountId, String countryCode) { + Campaign performanceMaxCampaign = + Campaign.newBuilder() + .setName("Performance Max retail campaign #" + getPrintableDateTime()) + // Sets the campaign status as PAUSED. The campaign is the only entity in + // the mutate request that should have its status set. + .setStatus(CampaignStatus.PAUSED) + // All Performance Max campaigns have an advertising_channel_type of + // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set. + .setAdvertisingChannelType(AdvertisingChannelType.PERFORMANCE_MAX) + // Bidding strategy must be set directly on the campaign. + // Setting a portfolio bidding strategy by resource name is not supported. + // Max Conversion and Maximize Conversion Value are the only strategies + // supported for Performance Max campaigns. + // An optional ROAS (Return on Advertising Spend) can be set for + // maximize_conversion_value. The ROAS value must be specified as a ratio in + // the API. It is calculated by dividing "total value" by "total spend". + // For more information on Maximize Conversion Value, see the support + // article: http://support.google.com/google-ads/answer/7684216. + // A targetRoas of 3.5 corresponds to a 350% return on ad spend. + .setBiddingStrategyType(BiddingStrategyType.MAXIMIZE_CONVERSION_VALUE) + .setMaximizeConversionValue( + MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build()) + // Sets the shopping settings. + .setShoppingSetting( + ShoppingSetting.newBuilder() + .setMerchantId(merchantCenterAccountId) + .setSalesCountry(countryCode) + .build()) + // Sets the Final URL expansion opt out. This flag is specific to + // Performance Max campaigns. If opted out (True), only the final URLs in + // the asset group or URLs specified in the advertiser's Google Merchant + // Center or business data feeds are targeted. + // If opted in (False), the entire domain will be targeted. For best + // results, set this value to false to opt in and allow URL expansions. You + // can optionally add exclusions to limit traffic to parts of your website. + .setUrlExpansionOptOut(false) + // Assigns the resource name with a temporary ID. + .setResourceName( + ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)) + // Sets the budget using the given budget resource name. + .setCampaignBudget(ResourceNames.campaignBudget(customerId, BUDGET_TEMPORARY_ID)) + // Optional fields. + .setStartDate(new DateTime().plusDays(1).toString("yyyyMMdd")) + .setEndDate(new DateTime().plusDays(365).toString("yyyyMMdd")) + .build(); + + return MutateOperation.newBuilder() + .setCampaignOperation( + CampaignOperation.newBuilder().setCreate(performanceMaxCampaign).build()) + .build(); + } + // [END add_performance_max_retail_campaign_3] + + // [START add_performance_max_retail_campaign_4] + /** Creates a list of MutateOperations that create new campaign criteria. */ + private List createCampaignCriterionOperations(long customerId) { + String campaignResourceName = + ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID); + List campaignCriteria = new ArrayList<>(); + // Sets the LOCATION campaign criteria. + // Targets all of New York City except Brooklyn. + // Location IDs are listed here: + // https://developers.google.com/google-ads/api/reference/data/geotargets + // and they can also be retrieved using the GeoTargetConstantService as shown + // here: https://developers.google.com/google-ads/api/docs/targeting/location-targeting + // + // We will add one positive location target for New York City (ID=1023191) + // and one negative location target for Brooklyn (ID=1022762). + // First, adds the positive (negative = False) for New York City. + campaignCriteria.add( + CampaignCriterion.newBuilder() + .setCampaign(campaignResourceName) + .setLocation( + LocationInfo.newBuilder() + .setGeoTargetConstant(ResourceNames.geoTargetConstant(1023191)) + .build()) + .setNegative(false) + .build()); + // Next adds the negative target for Brooklyn. + campaignCriteria.add( + CampaignCriterion.newBuilder() + .setCampaign(campaignResourceName) + .setLocation( + LocationInfo.newBuilder() + .setGeoTargetConstant(ResourceNames.geoTargetConstant(1022762)) + .build()) + .setNegative(true) + .build()); + // Sets the LANGUAGE campaign criterion. + campaignCriteria.add( + CampaignCriterion.newBuilder() + .setCampaign(campaignResourceName) + // Sets the language. + // For a list of all language codes, see: + // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7 + .setLanguage( + LanguageInfo.newBuilder() + .setLanguageConstant(ResourceNames.languageConstant(1000)) // English + .build()) + .build()); + // Returns a list of mutate operations with one operation per criterion. + return campaignCriteria.stream() + .map( + criterion -> + MutateOperation.newBuilder() + .setCampaignCriterionOperation( + CampaignCriterionOperation.newBuilder().setCreate(criterion).build()) + .build()) + .collect(Collectors.toList()); + } + // [END add_performance_max_retail_campaign_4] + + // [START add_performance_max_retail_campaign_5] + /** Creates multiple text assets and returns the list of resource names. */ + private List createMultipleTextAssets( + GoogleAdsClient googleAdsClient, long customerId, List texts) { + List mutateOperations = new ArrayList<>(); + for (String text : texts) { + Asset asset = Asset.newBuilder().setTextAsset(TextAsset.newBuilder().setText(text)).build(); + AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); + mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); + } + + List assetResourceNames = new ArrayList<>(); + // Creates the service client. + try (GoogleAdsServiceClient googleAdsServiceClient = + googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { + // Sends the operations in a single Mutate request. + MutateGoogleAdsResponse response = + googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations); + for (MutateOperationResponse result : response.getMutateOperationResponsesList()) { + if (result.hasAssetResult()) { + assetResourceNames.add(result.getAssetResult().getResourceName()); + } + } + printResponseDetails(response); + } + return assetResourceNames; + } + // [END add_performance_max_retail_campaign_5] + + // [START add_performance_max_retail_campaign_6] + /** Creates a list of MutateOperations that create a new AssetGroup. */ + private List createAssetGroupOperations( + long customerId, + List headlineAssetResourceNames, + List descriptionAssetResourceNames) + throws IOException { + List mutateOperations = new ArrayList<>(); + String campaignResourceName = + ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID); + String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID); + // Creates the AssetGroup. + AssetGroup assetGroup = + AssetGroup.newBuilder() + .setName("Performance Max retail asset group #" + getPrintableDateTime()) + .setCampaign(campaignResourceName) + .addFinalUrls("http://www.example.com") + .addFinalMobileUrls("http://www.example.com") + .setStatus(AssetGroupStatus.PAUSED) + .setResourceName(assetGroupResourceName) + .build(); + AssetGroupOperation assetGroupOperation = + AssetGroupOperation.newBuilder().setCreate(assetGroup).build(); + mutateOperations.add( + MutateOperation.newBuilder().setAssetGroupOperation(assetGroupOperation).build()); + + // For the list of required assets for a Performance Max campaign, see + // https://developers.google.com/google-ads/api/docs/performance-max/assets + + // An AssetGroup is linked to an Asset by creating a new AssetGroupAsset + // and providing: + // the resource name of the AssetGroup + // the resource name of the Asset + // the field_type of the Asset in this AssetGroup. + + // To learn more about AssetGroups, see + // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups + + // Links the previously created multiple text assets. + + // Links the headline assets. + for (String resourceName : headlineAssetResourceNames) { + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(AssetFieldType.HEADLINE) + .setAssetGroup(assetGroupResourceName) + .setAsset(resourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder() + .setAssetGroupAssetOperation(assetGroupAssetOperation) + .build()); + } + + // Links the description assets. + for (String resourceName : descriptionAssetResourceNames) { + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(AssetFieldType.DESCRIPTION) + .setAssetGroup(assetGroupResourceName) + .setAsset(resourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder() + .setAssetGroupAssetOperation(assetGroupAssetOperation) + .build()); + } + + // Creates and links the long headline text asset. + List createAndLinkTextAssetOperations = + createAndLinkTextAsset(customerId, "Travel the World", AssetFieldType.LONG_HEADLINE); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the business name text asset. + createAndLinkTextAssetOperations = + createAndLinkTextAsset(customerId, "Interplanetary Cruises", AssetFieldType.BUSINESS_NAME); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the image assets. + + // Creates and links the Logo Asset. + createAndLinkTextAssetOperations = + createAndLinkImageAsset(customerId, "https://gaagl.page.link/bjYi", AssetFieldType.LOGO); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the Marketing Image Asset. + createAndLinkTextAssetOperations = + createAndLinkImageAsset( + customerId, "https://gaagl.page.link/Eit5", AssetFieldType.MARKETING_IMAGE); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + // Creates and links the Square Marketing Image Asset. + createAndLinkTextAssetOperations = + createAndLinkImageAsset( + customerId, "https://gaagl.page.link/bjYi", AssetFieldType.SQUARE_MARKETING_IMAGE); + mutateOperations.addAll(createAndLinkTextAssetOperations); + + return mutateOperations; + } + // [END add_performance_max_retail_campaign_6] + + // [START add_performance_max_retail_campaign_7] + /** Creates a list of MutateOperations that create a new linked text asset. */ + List createAndLinkTextAsset( + long customerId, String text, AssetFieldType assetFieldType) { + List mutateOperations = new ArrayList<>(); + String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId()); + // Creates the Text Asset. + Asset asset = + Asset.newBuilder() + .setResourceName(assetResourceName) + .setTextAsset(TextAsset.newBuilder().setText(text).build()) + .build(); + AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); + mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); + + // Creates an AssetGroupAsset to link the Asset to the AssetGroup. + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(assetFieldType) + .setAssetGroup(ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)) + .setAsset(assetResourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build()); + + return mutateOperations; + } + // [END add_performance_max_retail_campaign_7] + + // [START add_performance_max_retail_campaign_8] + /** Creates a list of MutateOperations that create a new linked text asset. */ + List createAndLinkImageAsset( + long customerId, String url, AssetFieldType assetFieldType) throws IOException { + List mutateOperations = new ArrayList<>(); + String assetResourceName = ResourceNames.asset(customerId, getNextTemporaryId()); + // Creates a media file. + byte[] assetBytes = ByteStreams.toByteArray(new URL(url).openStream()); + + // Creates the Image Asset. + Asset asset = + Asset.newBuilder() + .setResourceName(assetResourceName) + .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(assetBytes)).build()) + .build(); + AssetOperation assetOperation = AssetOperation.newBuilder().setCreate(asset).build(); + mutateOperations.add(MutateOperation.newBuilder().setAssetOperation(assetOperation).build()); + + // Creates an AssetGroupAsset to link the Asset to the AssetGroup. + AssetGroupAsset assetGroupAsset = + AssetGroupAsset.newBuilder() + .setFieldType(assetFieldType) + .setAssetGroup(ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)) + .setAsset(assetResourceName) + .build(); + AssetGroupAssetOperation assetGroupAssetOperation = + AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build(); + mutateOperations.add( + MutateOperation.newBuilder().setAssetGroupAssetOperation(assetGroupAssetOperation).build()); + + return mutateOperations; + } + // [END add_performance_max_retail_campaign_8] + + // [START add_performance_max_retail_campaign_9] + /** Retrieves the list of customer conversion goals. */ + private static List getCustomerConversionGoals( + GoogleAdsClient googleAdsClient, long customerId) { + String query = + "SELECT customer_conversion_goal.category, customer_conversion_goal.origin " + + "FROM customer_conversion_goal"; + + List customerConversionGoals = new ArrayList<>(); + try (GoogleAdsServiceClient googleAdsServiceClient = + googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) { + // The number of conversion goals is typically less than 50, so we use + // GoogleAdsService.search instead of search_stream. + SearchPagedResponse response = + googleAdsServiceClient.search(Long.toString(customerId), query); + for (GoogleAdsRow googleAdsRow : response.iterateAll()) { + customerConversionGoals.add(googleAdsRow.getCustomerConversionGoal()); + } + } + + return customerConversionGoals; + } + + /** Creates a list of MutateOperations that override customer conversion goals. */ + private static List createConversionGoalOperations( + long customerId, List customerConversionGoals) { + List mutateOperations = new ArrayList<>(); + // To override the customer conversion goals, we will change the + // biddability of each of the customer conversion goals so that only + // the desired conversion goal is biddable in this campaign. + for (CustomerConversionGoal customerConversionGoal : customerConversionGoals) { + ConversionActionCategory category = customerConversionGoal.getCategory(); + ConversionOrigin origin = customerConversionGoal.getOrigin(); + String campaignConversionGoalResourceName = + ResourceNames.campaignConversionGoal( + customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID, category, origin); + CampaignConversionGoal.Builder campaignConversionGoalBuilder = + CampaignConversionGoal.newBuilder().setResourceName(campaignConversionGoalResourceName); + // Change the biddability for the campaign conversion goal. + // Set biddability to True for the desired (category, origin). + // Set biddability to False for all other conversion goals. + // Note: + // 1- It is assumed that this Conversion Action + // (category=PURCHASE, origin=WEBSITE) exists in this account. + // 2- More than one goal can be biddable if desired. This example + // shows only one. + if (category == ConversionActionCategory.PURCHASE && origin == ConversionOrigin.WEBSITE) { + campaignConversionGoalBuilder.setBiddable(true); + } else { + campaignConversionGoalBuilder.setBiddable(false); + } + CampaignConversionGoal campaignConversionGoal = campaignConversionGoalBuilder.build(); + CampaignConversionGoalOperation campaignConversionGoalOperation = + CampaignConversionGoalOperation.newBuilder() + .setUpdate(campaignConversionGoal) + .setUpdateMask(FieldMasks.allSetFieldsOf(campaignConversionGoal)) + .build(); + mutateOperations.add( + MutateOperation.newBuilder() + .setCampaignConversionGoalOperation(campaignConversionGoalOperation) + .build()); + } + return mutateOperations; + } + // [END add_performance_max_retail_campaign_9] + + /** + * Prints the details of a MutateGoogleAdsResponse. + * + *

Parses the "response" oneof field name and uses it to extract the new entity's name and + * resource name. + */ + private void printResponseDetails(MutateGoogleAdsResponse response) { + // Parses the Mutate response to print details about the entities that were created by the + // request. + String suffix = "_result"; + for (MutateOperationResponse result : response.getMutateOperationResponsesList()) { + for (Entry responseFields : result.getAllFields().entrySet()) { + String fieldName = responseFields.getKey().getName(); + String value = responseFields.getValue().toString().trim(); + if (fieldName.endsWith(suffix)) { + fieldName = fieldName.substring(0, fieldName.length() - suffix.length()); + } + System.out.printf("Created a(n) %s with %s.%n", fieldName, value); + } + } + } + + /** Returns the next temporary ID and decreases it by one. */ + private long getNextTemporaryId() { + return temporaryId--; + } + // [END add_performance_max_retail_campaign] +} From 43a5bd3314292f071a79286b70b891ea90d7c461 Mon Sep 17 00:00:00 2001 From: devchas Date: Wed, 8 Dec 2021 13:22:57 -0500 Subject: [PATCH 3/5] Add clarifying docs Change-Id: Ibf0071b59ed13162fc6be404f5d427e721119f2c --- .../examples/shoppingads/AddPerformanceMaxRetailCampaign.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java index 587938549b..fb196b3d2f 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java @@ -187,7 +187,8 @@ private void runExample( String countryCode) throws IOException { // [START add_performance_max_retail_campaign_1] - // This campaign will override the customer conversion goals. + // This campaign will override the customer conversion goals. For more information see + // https://developers.google.com/google-ads/api/docs/conversions/goals/campaign-goals. // Retrieve the current list of customer conversion goals. List customerConversionGoals = getCustomerConversionGoals(googleAdsClient, customerId); From f24bb6ac7db40c27304c00b66e9a05f46664e84f Mon Sep 17 00:00:00 2001 From: devchas Date: Wed, 15 Dec 2021 08:52:16 -0500 Subject: [PATCH 4/5] Address David comments Change-Id: I9785a392721293df80b8a2771b10c8f09df092ec --- .../AddPerformanceMaxCampaign.java | 6 ++-- .../AddPerformanceMaxRetailCampaign.java | 32 ++++++++++++------- .../examples/utils/ArgumentNames.java | 1 + 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java index e5579f3c4d..1fd132cf8e 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java @@ -74,7 +74,8 @@ * https://developers.google.com/google-ads/api/docs/conversions/overview#conversion_actions * *

This example uses the default customer conversion goals. For an example of setting - * campaign-specific conversion goals, see {@link AddPerformanceMaxCampaign}. + * campaign-specific conversion goals, see {@link + * com.google.ads.googleads.examples.shoppingads.AddPerformanceMaxRetailCampaign}. */ public class AddPerformanceMaxCampaign { @@ -230,7 +231,6 @@ private MutateOperation createPerformanceMaxCampaignOperation(long customerId) { // For more information on Maximize Conversion Value, see the support // article: http://support.google.com/google-ads/answer/7684216. // A targetRoas of 3.5 corresponds to a 350% return on ad spend. - .setBiddingStrategyType(BiddingStrategyType.MAXIMIZE_CONVERSION_VALUE) .setMaximizeConversionValue( MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build()) // Sets the Final URL expansion opt out. This flag is specific to @@ -483,7 +483,7 @@ List createAndLinkTextAsset( // [END add_performance_max_campaign_7] // [START add_performance_max_campaign_8] - /** Creates a list of MutateOperations that create a new linked text asset. */ + /** Creates a list of MutateOperations that create a new linked image asset. */ List createAndLinkImageAsset( long customerId, String url, AssetFieldType assetFieldType) throws IOException { List mutateOperations = new ArrayList<>(); diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java index fb196b3d2f..4e36af2373 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java @@ -120,9 +120,13 @@ private static class AddPerformanceMaxRetailCampaignParams extends CodeSamplePar @Parameter( names = ArgumentNames.COUNTRY_CODE, - required = true, description = "The sales country of products to include in the campaign.") - private String countryCode; + private String countryCode = "US"; + + @Parameter( + names = ArgumentNames.FINAL_URL, + description = "The final URL for the asset group of the campaign.") + private String finalUrl = "http://www.example.com"; } public static void main(String[] args) throws IOException { @@ -133,7 +137,10 @@ public static void main(String[] args) throws IOException { // into the code here. See the parameter class definition above for descriptions. params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE"); params.merchantCenterAccountId = Long.parseLong("INSERT_MERCHANT_CENTER_ACCOUNT_ID_HERE"); - params.countryCode = "INSERT_COUNTRY_CODE_HERE"; + + // Optionally set custom values for the parameters below. + // params.countryCode = "INSERT_COUNTRY_CODE_HERE"; + // params.finalUrl = "INSERT_FINAL_URL_HERE"; } GoogleAdsClient googleAdsClient = null; @@ -154,7 +161,8 @@ public static void main(String[] args) throws IOException { googleAdsClient, params.customerId, params.merchantCenterAccountId, - params.countryCode); + params.countryCode, + params.finalUrl); } catch (GoogleAdsException gae) { // GoogleAdsException is the base class for most exceptions thrown by an API request. // Instances of this exception have a message and a GoogleAdsFailure that contains a @@ -179,12 +187,14 @@ public static void main(String[] args) throws IOException { * @param customerId the client customer ID. * @param merchantCenterAccountId the Merchant Center account ID. * @param countryCode sales country of products to include in the campaign. + * @param finalUrl final URL for the asset group of the campaign. */ private void runExample( GoogleAdsClient googleAdsClient, long customerId, long merchantCenterAccountId, - String countryCode) + String countryCode, + String finalUrl) throws IOException { // [START add_performance_max_retail_campaign_1] // This campaign will override the customer conversion goals. For more information see @@ -221,7 +231,7 @@ private void runExample( mutateOperations.addAll(createCampaignCriterionOperations(customerId)); mutateOperations.addAll( createAssetGroupOperations( - customerId, headlineAssetResourceNames, descriptionAssetResourceNames)); + customerId, headlineAssetResourceNames, descriptionAssetResourceNames, finalUrl)); mutateOperations.addAll(createConversionGoalOperations(customerId, customerConversionGoals)); try (GoogleAdsServiceClient googleAdsServiceClient = @@ -279,7 +289,6 @@ private MutateOperation createPerformanceMaxCampaignOperation( // For more information on Maximize Conversion Value, see the support // article: http://support.google.com/google-ads/answer/7684216. // A targetRoas of 3.5 corresponds to a 350% return on ad spend. - .setBiddingStrategyType(BiddingStrategyType.MAXIMIZE_CONVERSION_VALUE) .setMaximizeConversionValue( MaximizeConversionValue.newBuilder().setTargetRoas(3.5).build()) // Sets the shopping settings. @@ -406,7 +415,8 @@ private List createMultipleTextAssets( private List createAssetGroupOperations( long customerId, List headlineAssetResourceNames, - List descriptionAssetResourceNames) + List descriptionAssetResourceNames, + String finalUrl) throws IOException { List mutateOperations = new ArrayList<>(); String campaignResourceName = @@ -417,8 +427,8 @@ private List createAssetGroupOperations( AssetGroup.newBuilder() .setName("Performance Max retail asset group #" + getPrintableDateTime()) .setCampaign(campaignResourceName) - .addFinalUrls("http://www.example.com") - .addFinalMobileUrls("http://www.example.com") + .addFinalUrls(finalUrl) + .addFinalMobileUrls(finalUrl) .setStatus(AssetGroupStatus.PAUSED) .setResourceName(assetGroupResourceName) .build(); @@ -538,7 +548,7 @@ List createAndLinkTextAsset( // [END add_performance_max_retail_campaign_7] // [START add_performance_max_retail_campaign_8] - /** Creates a list of MutateOperations that create a new linked text asset. */ + /** Creates a list of MutateOperations that create a new linked image asset. */ List createAndLinkImageAsset( long customerId, String url, AssetFieldType assetFieldType) throws IOException { List mutateOperations = new ArrayList<>(); diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/utils/ArgumentNames.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/utils/ArgumentNames.java index 4da828dbd9..40c81e1622 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/utils/ArgumentNames.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/utils/ArgumentNames.java @@ -67,6 +67,7 @@ public final class ArgumentNames { public static final String FEED_ITEM_ID = "--feedItemId"; public static final String FEED_ITEM_IDS = "--feedItemIds"; public static final String FEED_ITEM_SET_ID = "--feedItemSetId"; + public static final String FINAL_URL = "--finalUrl"; public static final String FLIGHT_PLACEHOLDER_FIELD_NAME = "--flightPlaceholderFieldName"; public static final String FREEFORM_KEYWORD_TEXT = "--freeformKeywordText"; public static final String GCLID = "--gclid"; From 78463ad15756c8c1a0cb6f8f9ddb4d730fd14242 Mon Sep 17 00:00:00 2001 From: devchas Date: Wed, 15 Dec 2021 08:54:21 -0500 Subject: [PATCH 5/5] Rm unused import Change-Id: I9a41203262e231003430641d095768988a3b52fc --- .../examples/advancedoperations/AddPerformanceMaxCampaign.java | 1 - .../examples/shoppingads/AddPerformanceMaxRetailCampaign.java | 1 - 2 files changed, 2 deletions(-) diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java index 1fd132cf8e..3819f96b20 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/advancedoperations/AddPerformanceMaxCampaign.java @@ -28,7 +28,6 @@ import com.google.ads.googleads.v9.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType; import com.google.ads.googleads.v9.enums.AssetFieldTypeEnum.AssetFieldType; import com.google.ads.googleads.v9.enums.AssetGroupStatusEnum.AssetGroupStatus; -import com.google.ads.googleads.v9.enums.BiddingStrategyTypeEnum.BiddingStrategyType; import com.google.ads.googleads.v9.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod; import com.google.ads.googleads.v9.enums.CampaignStatusEnum.CampaignStatus; import com.google.ads.googleads.v9.errors.GoogleAdsError; diff --git a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java index 4e36af2373..6041fd53d0 100644 --- a/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java +++ b/google-ads-examples/src/main/java/com/google/ads/googleads/examples/shoppingads/AddPerformanceMaxRetailCampaign.java @@ -29,7 +29,6 @@ import com.google.ads.googleads.v9.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType; import com.google.ads.googleads.v9.enums.AssetFieldTypeEnum.AssetFieldType; import com.google.ads.googleads.v9.enums.AssetGroupStatusEnum.AssetGroupStatus; -import com.google.ads.googleads.v9.enums.BiddingStrategyTypeEnum.BiddingStrategyType; import com.google.ads.googleads.v9.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod; import com.google.ads.googleads.v9.enums.CampaignStatusEnum.CampaignStatus; import com.google.ads.googleads.v9.enums.ConversionActionCategoryEnum.ConversionActionCategory;