Add Performance Max Campaign

  • This content showcases code examples in multiple languages (Java, C#, PHP, Python, Ruby, Perl) for creating and managing Performance Max campaigns within Google Ads via the Google Ads API.

  • The code demonstrates the creation of campaign budgets, the Performance Max campaign itself, targeting criteria (location and language), and the setup of asset groups, including linking multiple text and image assets, along with business name, and long headlines.

  • It covers core concepts such as using Mutate Operations for batch requests, employing temporary IDs for entities created within the same request, and incorporating asset group signals like audience and search theme for enhanced targeting.

  • The code handles multiple operations like creating text assets (headlines, descriptions), creating image assets (logo, marketing images), and linking these assets to asset groups, all while setting specific campaign parameters and handling the API responses.

  • The code demonstrates the handling of responses from the API, detailing how to extract information about created entities and confirm the success of operations, as well as the creation of audience signals.

Java

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     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 static com.google.ads.googleads.v21.enums.EuPoliticalAdvertisingStatusEnum.EuPoliticalAdvertisingStatus.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING;

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.v21.common.AudienceInfo;
import com.google.ads.googleads.v21.common.ImageAsset;
import com.google.ads.googleads.v21.common.LanguageInfo;
import com.google.ads.googleads.v21.common.LocationInfo;
import com.google.ads.googleads.v21.common.MaximizeConversionValue;
import com.google.ads.googleads.v21.common.SearchThemeInfo;
import com.google.ads.googleads.v21.common.TextAsset;
import com.google.ads.googleads.v21.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;
import com.google.ads.googleads.v21.enums.AssetFieldTypeEnum.AssetFieldType;
import com.google.ads.googleads.v21.enums.AssetGroupStatusEnum.AssetGroupStatus;
import com.google.ads.googleads.v21.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;
import com.google.ads.googleads.v21.enums.CampaignStatusEnum.CampaignStatus;
import com.google.ads.googleads.v21.errors.GoogleAdsError;
import com.google.ads.googleads.v21.errors.GoogleAdsException;
import com.google.ads.googleads.v21.resources.Asset;
import com.google.ads.googleads.v21.resources.AssetGroup;
import com.google.ads.googleads.v21.resources.AssetGroupAsset;
import com.google.ads.googleads.v21.resources.AssetGroupSignal;
import com.google.ads.googleads.v21.resources.Campaign;
import com.google.ads.googleads.v21.resources.CampaignAsset;
import com.google.ads.googleads.v21.resources.CampaignBudget;
import com.google.ads.googleads.v21.resources.CampaignCriterion;
import com.google.ads.googleads.v21.services.AssetGroupAssetOperation;
import com.google.ads.googleads.v21.services.AssetGroupOperation;
import com.google.ads.googleads.v21.services.AssetGroupSignalOperation;
import com.google.ads.googleads.v21.services.AssetOperation;
import com.google.ads.googleads.v21.services.CampaignAssetOperation;
import com.google.ads.googleads.v21.services.CampaignBudgetOperation;
import com.google.ads.googleads.v21.services.CampaignCriterionOperation;
import com.google.ads.googleads.v21.services.CampaignOperation;
import com.google.ads.googleads.v21.services.GoogleAdsServiceClient;
import com.google.ads.googleads.v21.services.MutateGoogleAdsResponse;
import com.google.ads.googleads.v21.services.MutateOperation;
import com.google.ads.googleads.v21.services.MutateOperationResponse;
import com.google.ads.googleads.v21.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.
 *
 * <p>For more information about Performance Max campaigns, see
 * https://developers.google.com/google-ads/api/docs/performance-max/overview
 *
 * <p>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
 *
 * <p>This example uses the default customer conversion goals. For an example of setting
 * campaign-specific conversion goals, see {@link
 * com.google.ads.googleads.examples.shoppingads.AddPerformanceMaxRetailCampaign}.
 */
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.
  //
  // <p>See https://developers.google.com/google-ads/api/docs/mutating/best-practices for further
  // details.
  //
  // <p>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;

    @Parameter(
        names = ArgumentNames.AUDIENCE_ID,
        description =
            "An audience ID to use to improve the targeting of the Performance Max campaign")
    private Long audienceId;

    @Parameter(
        names = "--brandGuidelinesEnabled",
        arity = 1,
        description =
            "A boolean value indicating if the created campaign is enabled for brand guidelines")
    private boolean brandGuidelinesEnabled = false;
  }

  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");

      // Optional: Specify an audience ID.
      // params.audienceId = Long.parseLong("INSERT_AUDIENCE_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, params.audienceId, params.brandGuidelinesEnabled);
    } catch (GoogleAdsException gae) {
      // GoogleAdsException is the base class for most exceptions thrown by an API request.
      // Instances of this exception have a message and a GoogleAdsFailure that contains a
      // collection of GoogleAdsErrors that indicate the underlying causes of the
      // GoogleAdsException.
      System.err.printf(
          "Request ID %s failed due to GoogleAdsException. Underlying errors:%n",
          gae.getRequestId());
      int i = 0;
      for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {
        System.err.printf("  Error %d: %s%n", i++, googleAdsError);
      }
      System.exit(1);
    }
  }

  /**
   * Runs the example.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @param audienceId the optional audience ID.
   * @param brandGuidelinesEnabled indicates if the campaign is enabled for brand guidelines.
   */
  private void runExample(
      GoogleAdsClient googleAdsClient,
      long customerId,
      Long audienceId,
      boolean brandGuidelinesEnabled)
      throws IOException {
    // 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<String> headlines = ImmutableList.of("Travel", "Travel Reviews", "Book travel");
    List<String> headlineAssetResourceNames =
        createMultipleTextAssets(googleAdsClient, customerId, headlines);
    // Creates the descriptions.
    List<String> descriptions = ImmutableList.of("Take to the air!", "Fly to the sky!");
    List<String> 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<MutateOperation> mutateOperations = new ArrayList<>();
    mutateOperations.add(createCampaignBudgetOperation(customerId));
    mutateOperations.add(createPerformanceMaxCampaignOperation(customerId, brandGuidelinesEnabled));
    mutateOperations.addAll(createCampaignCriterionOperations(customerId));
    String assetGroupResourceName = ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID);
    mutateOperations.addAll(
        createAssetGroupOperations(
            customerId,
            assetGroupResourceName,
            headlineAssetResourceNames,
            descriptionAssetResourceNames,
            brandGuidelinesEnabled));
    mutateOperations.addAll(
        createAssetGroupSignalOperations(customerId, assetGroupResourceName, audienceId));

    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      MutateGoogleAdsResponse response =
          googleAdsServiceClient.mutate(Long.toString(customerId), mutateOperations);
      printResponseDetails(response);
    }
  }

  /** 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();
  }


  /** Creates a MutateOperation that creates a new Performance Max campaign. */
  private MutateOperation createPerformanceMaxCampaignOperation(
      long customerId, boolean brandGuidelinesEnabled) {
    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.
            .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)
            // Sets if the campaign is enabled for brand guidelines. For more information on brand
            // guidelines, see https://support.google.com/google-ads/answer/14934472.
            .setBrandGuidelinesEnabled(brandGuidelinesEnabled)
            // 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))
            // Declares whether this campaign serves political ads targeting the EU.
            .setContainsEuPoliticalAdvertising(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING)
            // 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();
  }


  /** Creates a list of MutateOperations that create new campaign criteria. */
  private List<MutateOperation> createCampaignCriterionOperations(long customerId) {
    String campaignResourceName =
        ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID);
    List<CampaignCriterion> 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());
  }


  /** Creates multiple text assets and returns the list of resource names. */
  private List<String> createMultipleTextAssets(
      GoogleAdsClient googleAdsClient, long customerId, List<String> texts) {
    List<MutateOperation> 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<String> 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;
  }


  /** Creates a list of MutateOperations that create a new AssetGroup. */
  private List<MutateOperation> createAssetGroupOperations(
      long customerId,
      String assetGroupResourceName,
      List<String> headlineAssetResourceNames,
      List<String> descriptionAssetResourceNames,
      boolean brandGuidelinesEnabled)
      throws IOException {
    List<MutateOperation> mutateOperations = new ArrayList<>();
    String campaignResourceName =
        ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_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) {
      mutateOperations.add(
          createAssetGroupAssetMutateOperation(
              AssetFieldType.HEADLINE, resourceName, assetGroupResourceName));
    }

    // Links the description assets.
    for (String resourceName : descriptionAssetResourceNames) {
      mutateOperations.add(
          createAssetGroupAssetMutateOperation(
              AssetFieldType.DESCRIPTION, resourceName, assetGroupResourceName));
    }

    // Creates and links the long headline text asset.
    List<MutateOperation> createAndLinkTextAssetOperations =
        createAndLinkTextAsset(customerId, "Travel the World", AssetFieldType.LONG_HEADLINE);
    mutateOperations.addAll(createAndLinkTextAssetOperations);

    // Creates and links the business name and logo assets.
    List<MutateOperation> createAndLinkBrandAssets =
        createAndLinkBrandAssets(
            customerId,
            brandGuidelinesEnabled,
            "Interplanetary Cruises",
            "https://gaagl.page.link/bjYi",
            "Marketing Logo");
    mutateOperations.addAll(createAndLinkBrandAssets);

    // Creates and links the image assets.

    // Creates and links the Marketing Image Asset.
    createAndLinkTextAssetOperations =
        createAndLinkImageAsset(
            customerId,
            "https://gaagl.page.link/Eit5",
            AssetFieldType.MARKETING_IMAGE,
            "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,
            "Square Marketing Image");
    mutateOperations.addAll(createAndLinkTextAssetOperations);

    return mutateOperations;
  }


  /** Creates a list of MutateOperations that create a new linked text asset. */
  List<MutateOperation> createAndLinkTextAsset(
      long customerId, String text, AssetFieldType assetFieldType) {
    List<MutateOperation> 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.
    mutateOperations.add(
        createAssetGroupAssetMutateOperation(
            assetFieldType,
            assetResourceName,
            ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));

    return mutateOperations;
  }


  /** Creates a list of MutateOperations that create a new linked image asset. */
  List<MutateOperation> createAndLinkImageAsset(
      long customerId, String url, AssetFieldType assetFieldType, String assetName)
      throws IOException {
    List<MutateOperation> 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())
            // Provides a unique friendly name to identify your asset. When there is an existing
            // image asset with the same content but a different name, the new name will be dropped
            // silently.
            .setName(assetName)
            .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.
    mutateOperations.add(
        createAssetGroupAssetMutateOperation(
            assetFieldType,
            assetResourceName,
            ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));

    return mutateOperations;
  }

  /** Creates a list of MutateOperations that create linked brand assets. */
  List<MutateOperation> createAndLinkBrandAssets(
      long customerId,
      boolean brandGuidelinesEnabled,
      String businessName,
      String logoUrl,
      String logoName)
      throws IOException {
    List<MutateOperation> mutateOperations = new ArrayList<>();

    // Creates the brand name text asset.
    String businessNameAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());
    Asset businessNameAsset =
        Asset.newBuilder()
            .setResourceName(businessNameAssetResourceName)
            .setTextAsset(TextAsset.newBuilder().setText(businessName).build())
            .build();
    AssetOperation businessNameAssetOperation =
        AssetOperation.newBuilder().setCreate(businessNameAsset).build();
    mutateOperations.add(
        MutateOperation.newBuilder().setAssetOperation(businessNameAssetOperation).build());

    // Creates the logo image asset.
    String logoAssetResourceName = ResourceNames.asset(customerId, getNextTemporaryId());
    // Creates a media file.
    byte[] logoBytes = ByteStreams.toByteArray(new URL(logoUrl).openStream());
    Asset logoAsset =
        Asset.newBuilder()
            .setResourceName(logoAssetResourceName)
            .setImageAsset(ImageAsset.newBuilder().setData(ByteString.copyFrom(logoBytes)).build())
            // Provides a unique friendly name to identify your asset. When there is an existing
            // image asset with the same content but a different name, the new name will be dropped
            // silently.
            .setName(logoName)
            .build();
    AssetOperation logoImageAssetOperation =
        AssetOperation.newBuilder().setCreate(logoAsset).build();
    mutateOperations.add(
        MutateOperation.newBuilder().setAssetOperation(logoImageAssetOperation).build());

    if (brandGuidelinesEnabled) {
      // Creates CampaignAsset resources to link the Asset resources to the Campaign.
      mutateOperations.add(
          createCampaignAssetMutateOperation(
              customerId, AssetFieldType.BUSINESS_NAME, businessNameAssetResourceName));
      mutateOperations.add(
          createCampaignAssetMutateOperation(
              customerId, AssetFieldType.LOGO, logoAssetResourceName));
    } else {
      // Creates an AssetGroupAsset to link the Asset to the AssetGroup.
      mutateOperations.add(
          createAssetGroupAssetMutateOperation(
              AssetFieldType.BUSINESS_NAME,
              businessNameAssetResourceName,
              ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));
      mutateOperations.add(
          createAssetGroupAssetMutateOperation(
              AssetFieldType.LOGO,
              logoAssetResourceName,
              ResourceNames.assetGroup(customerId, ASSET_GROUP_TEMPORARY_ID)));
    }

    return mutateOperations;
  }

  /** Creates a MutateOperation to add an AssetGroupAsset. */
  MutateOperation createAssetGroupAssetMutateOperation(
      AssetFieldType fieldType, String assetResourceName, String assetGroupResourceName) {
    AssetGroupAsset assetGroupAsset =
        AssetGroupAsset.newBuilder()
            .setFieldType(fieldType)
            .setAssetGroup(assetGroupResourceName)
            .setAsset(assetResourceName)
            .build();
    AssetGroupAssetOperation assetGroupAssetOperation =
        AssetGroupAssetOperation.newBuilder().setCreate(assetGroupAsset).build();
    return MutateOperation.newBuilder()
        .setAssetGroupAssetOperation(assetGroupAssetOperation)
        .build();
  }

  /** Creates a MutateOperation to add a CampaignAsset. */
  MutateOperation createCampaignAssetMutateOperation(
      long customerId, AssetFieldType fieldType, String assetResourceName) {
    CampaignAsset campaignAsset =
        CampaignAsset.newBuilder()
            .setFieldType(fieldType)
            .setCampaign(ResourceNames.campaign(customerId, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID))
            .setAsset(assetResourceName)
            .build();
    CampaignAssetOperation campaignAssetOperation =
        CampaignAssetOperation.newBuilder().setCreate(campaignAsset).build();
    return MutateOperation.newBuilder().setCampaignAssetOperation(campaignAssetOperation).build();
  }


  /**
   * Creates a list of MutateOperations that create {@link
   * com.google.ads.googleads.v21.resources.AssetGroupSignal} objects.
   */
  private List<MutateOperation> createAssetGroupSignalOperations(
      long customerId, String assetGroupResourceName, Long audienceId) {
    List<MutateOperation> mutateOperations = new ArrayList<>();

    if (audienceId != null) {
      // Creates an audience asset group signal.
      // To learn more about Audience Signals, see:
      // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#audiences
      AssetGroupSignal audienceSignal =
          AssetGroupSignal.newBuilder()
              .setAssetGroup(assetGroupResourceName)
              .setAudience(
                  AudienceInfo.newBuilder()
                      .setAudience(ResourceNames.audience(customerId, audienceId)))
              .build();

      mutateOperations.add(
          MutateOperation.newBuilder()
              .setAssetGroupSignalOperation(
                  AssetGroupSignalOperation.newBuilder().setCreate(audienceSignal))
              .build());
    }

    // Creates a search theme asset group signal.
    // To learn more about Search Themes Signals, see:
    // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes
    AssetGroupSignal searchThemeSignal =
        AssetGroupSignal.newBuilder()
            .setAssetGroup(assetGroupResourceName)
            .setSearchTheme(SearchThemeInfo.newBuilder().setText("travel").build())
            .build();

    mutateOperations.add(
        MutateOperation.newBuilder()
            .setAssetGroupSignalOperation(
                AssetGroupSignalOperation.newBuilder().setCreate(searchThemeSignal))
            .build());

    return mutateOperations;
  }

  /**
   * Prints the details of a MutateGoogleAdsResponse.
   *
   * <p>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<FieldDescriptor, Object> 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--;
  }
}

      

C#

// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using CommandLine;
using Google.Ads.Gax.Examples;
using Google.Ads.Gax.Util;
using Google.Ads.GoogleAds.Config;
using Google.Ads.GoogleAds.Lib;
using Google.Ads.GoogleAds.V21.Common;
using Google.Ads.GoogleAds.V21.Errors;
using Google.Ads.GoogleAds.V21.Resources;
using Google.Ads.GoogleAds.V21.Services;
using Google.Protobuf;
using System;
using System.Collections.Generic;
using System.Threading;
using static Google.Ads.GoogleAds.V21.Enums.AdvertisingChannelTypeEnum.Types;
using static Google.Ads.GoogleAds.V21.Enums.AssetFieldTypeEnum.Types;
using static Google.Ads.GoogleAds.V21.Enums.AssetGroupStatusEnum.Types;
using static Google.Ads.GoogleAds.V21.Enums.CampaignStatusEnum.Types;
using static Google.Ads.GoogleAds.V21.Enums.EuPoliticalAdvertisingStatusEnum.Types;

namespace Google.Ads.GoogleAds.Examples.V21
{
    /// <summary>
    /// 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
    ///
    /// This example uses the default customer conversion goals. For an example
    /// of setting campaign-specific conversion goals, see
    /// ShoppingAds/AddPerformanceMaxRetailCampaign.cs
    /// </summary>
    public class AddPerformanceMaxCampaign : ExampleBase
    {
        /// <summary>
        /// Command line options for running the <see cref="AddPerformanceMaxCampaign"/> example.
        /// </summary>
        public class Options : OptionsBase
        {
            /// <summary>
            /// The Google Ads customer ID.
            /// </summary>
            [Option("customerId", Required = true, HelpText =
                "The Google Ads customer ID.")]
            public long CustomerId { get; set; }

            /// <summary>
            /// Optional: An audience ID to use to improve the targeting of the Performance Max
            /// campaign.
            /// </summary>
            [Option("audienceId", Required = false, HelpText = "The ID of an audience.")]
            public long? AudienceId { get; set; }

            /// <summary>
            /// Optional: A boolean value indicating if the campaign is enabled for brand
            /// guidelines.
            /// </summary>
            [Option("brandGuidelinesEnabled", Required = false, HelpText =
                "A boolean value indicating if the campaign is enabled for brand guidelines.")]
            public bool BrandGuidelinesEnabled { get; set; }
        }

        /// <summary>
        /// Main method, to run this code example as a standalone application.
        /// </summary>
        /// <param name="args">The command line arguments.</param>
        public static void Main(string[] args)
        {
            Options options = ExampleUtilities.ParseCommandLine<Options>(args);

            AddPerformanceMaxCampaign codeExample = new AddPerformanceMaxCampaign();
            Console.WriteLine(codeExample.Description);
            codeExample.Run(
                new GoogleAdsClient(),
                options.CustomerId,
                options.AudienceId,
                options.BrandGuidelinesEnabled
            );
        }

        // 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 const int TEMPORARY_ID_BUDGET = -1;

        private const int TEMPORARY_ID_CAMPAIGN = -2;
        private const int TEMPORARY_ID_ASSET_GROUP = -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 class AssetTemporaryResourceNameGenerator
        {
            private long customerId;
            private long next;

            public AssetTemporaryResourceNameGenerator(long customerId, long assetGroupId)
            {
                this.customerId = customerId;
                this.next = assetGroupId - 1;
            }

            public string Next()
            {
                long i = next;
                Interlocked.Decrement(ref next);
                return ResourceNames.Asset(customerId, i);
            }
        }

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description =>
            "This example shows how to create a Performance Max campaign.";

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID.</param>
        /// <param name="audienceId">The optional audience ID.</param>
        /// <param name="brandGuidelinesEnabled">Whether or not to enable brand guidelines.</param>
        public void Run(GoogleAdsClient client, long customerId, long? audienceId,
            bool brandGuidelinesEnabled)
        {
            try
            {
                GoogleAdsServiceClient googleAdsServiceClient =
                    client.GetService(Services.V21.GoogleAdsService);

                // 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
                //
                // Create the headlines.
                List<string> headlineAssetResourceNames = CreateMultipleTextAssets(
                    client,
                    customerId,
                    new[] {
                    "Travel",
                    "Travel Reviews",
                    "Book travel"
                    }
                );

                // Create the descriptions.
                List<string> descriptionAssetResourceNames = CreateMultipleTextAssets(
                    client,
                    customerId,
                    new[] {
                    "Take to the air!",
                    "Fly to the sky!"
                    }
                );

                string tempResourceNameCampaignBudget = ResourceNames.CampaignBudget(
                    customerId,
                    TEMPORARY_ID_BUDGET
                );

                // 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 is 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
                MutateOperation campaignBudgetOperation = CreateCampaignBudgetOperation(
                    tempResourceNameCampaignBudget
                );

                string tempResourceNameCampaign = ResourceNames.Campaign(
                    customerId,
                    TEMPORARY_ID_CAMPAIGN
                );

                MutateOperation performanceMaxCampaignOperation =
                    CreatePerformanceMaxCampaignOperation(
                        tempResourceNameCampaign,
                        tempResourceNameCampaignBudget,
                        brandGuidelinesEnabled
                    );

                List<MutateOperation> campaignCriterionOperations =
                    CreateCampaignCriterionOperations(tempResourceNameCampaign);

                List<MutateOperation> assetGroupOperations =
                    CreateAssetGroupOperations(
                        tempResourceNameCampaign,
                        ResourceNames.AssetGroup(customerId, TEMPORARY_ID_ASSET_GROUP),
                        headlineAssetResourceNames,
                        descriptionAssetResourceNames,
                        new AssetTemporaryResourceNameGenerator(
                            customerId,
                            TEMPORARY_ID_ASSET_GROUP
                        ),
                        client.Config,
                        brandGuidelinesEnabled
                    );

                List<MutateOperation> assetGroupSignalOperations =
                    CreateAssetGroupSignalOperations(
                        customerId,
                        ResourceNames.AssetGroup(customerId, TEMPORARY_ID_ASSET_GROUP),
                        audienceId
                    );

                MutateGoogleAdsRequest request = new MutateGoogleAdsRequest
                {
                    CustomerId = customerId.ToString()
                };

                // It's important to create these entities in this order because they depend on
                // each other.
                //
                // Additionally, we take several lists of operations and flatten them into one
                // large list.
                request.MutateOperations.Add(campaignBudgetOperation);
                request.MutateOperations.Add(performanceMaxCampaignOperation);
                request.MutateOperations.AddRange(campaignCriterionOperations);
                request.MutateOperations.AddRange(assetGroupOperations);
                request.MutateOperations.AddRange(assetGroupSignalOperations);

                MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);

                PrintResponseDetails(response);
            }
            catch (GoogleAdsException e)
            {
                Console.WriteLine("Failure:");
                Console.WriteLine($"Message: {e.Message}");
                Console.WriteLine($"Failure: {e.Failure}");
                Console.WriteLine($"Request ID: {e.RequestId}");
                throw;
            }
        }

        /// <summary>
        /// Creates a MutateOperation that creates a new CampaignBudget.
        ///
        /// A temporary ID will be assigned to this campaign budget so that it can be
        /// referenced by other objects being created in the same Mutate request.
        /// </summary>
        /// <param name="budgetResourceName">The temporary resource name of the budget to
        /// create.</param>
        /// <returns>A MutateOperation that creates a CampaignBudget.</returns>
        private MutateOperation CreateCampaignBudgetOperation(string budgetResourceName)
        {
            MutateOperation operation = new MutateOperation
            {
                CampaignBudgetOperation = new CampaignBudgetOperation
                {
                    Create = new CampaignBudget
                    {
                        Name = "Performance Max campaign budget #"
                          + ExampleUtilities.GetRandomString(),

                        // The budget period already defaults to DAILY.
                        AmountMicros = 50000000,

                        // A Performance Max campaign cannot use a shared campaign budget.
                        ExplicitlyShared = false,

                        // Set a temporary ID in the budget's resource name so it can be referenced
                        // by the campaign in later steps.
                        ResourceName = budgetResourceName
                    }
                }
            };

            return operation;
        }


        /// Creates a MutateOperation that creates a new Performance Max campaign.
        /// <param name="campaignResourceName">The campaign resource name.</param>
        /// <param name="campaignBudgetResourceName">The campaign budget resource name.</param>
        /// <param name="brandGuidelinesEnabled">Whether or not to enable brand guidelines.</param>
        /// <returns>A MutateOperations that will create this new campaign.</returns>
        private MutateOperation CreatePerformanceMaxCampaignOperation(
            string campaignResourceName,
            string campaignBudgetResourceName,
            bool brandGuidelinesEnabled)
        {
            MutateOperation operation = new MutateOperation()
            {
                CampaignOperation = new CampaignOperation()
                {
                    Create = new Campaign()
                    {
                        Name = "Performance Max campaign #" + ExampleUtilities.GetRandomString(),

                        // Set the campaign status as PAUSED. The campaign is the only entity in
                        // the mutate request that should have its status set.
                        Status = CampaignStatus.Paused,

                        // All Performance Max campaigns have an AdvertisingChannelType of
                        // PerformanceMax. The AdvertisingChannelSubType should not be set.
                        AdvertisingChannelType = AdvertisingChannelType.PerformanceMax,

                        // 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. BiddingStrategyType is
                        // read-only and cannot be set by the API. An optional ROAS (Return on
                        // Advertising Spend) can be set to enable the MaximizeConversionValue
                        // bidding strategy. 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 target_roas of 3.5 corresponds to a 350% return on ad spend.
                        MaximizeConversionValue = new MaximizeConversionValue()
                        {
                            TargetRoas = 3.5
                        },

                        // Set 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.
                        UrlExpansionOptOut = false,

                        // Use the temporary resource name created earlier
                        ResourceName = campaignResourceName,

                        // Set the budget using the given budget resource name.
                        CampaignBudget = campaignBudgetResourceName,

                        // Set if the campaign is enabled for brand guidelines. For more information
                        // on brand guidelines, see https://support.google.com/google-ads/answer/14934472.
                        BrandGuidelinesEnabled = brandGuidelinesEnabled,

                        // Declare whether or not this campaign contains political ads targeting the EU.
                        ContainsEuPoliticalAdvertising = EuPoliticalAdvertisingStatus.DoesNotContainEuPoliticalAdvertising,

                        // Optional fields
                        StartDate = DateTime.Now.AddDays(1).ToString("yyyyMMdd"),
                        EndDate = DateTime.Now.AddDays(365).ToString("yyyyMMdd")
                    }
                }
            };

            return operation;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create new campaign criteria.
        /// </summary>
        /// <param name="campaignResourceName">The campaign resource name.</param>
        /// <returns>A list of MutateOperations that create new campaign criteria.</returns>
        private List<MutateOperation> CreateCampaignCriterionOperations(
            string campaignResourceName)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // Set the LOCATION campaign criteria.
            // Target 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, add the positive (negative = False) for New York City.
            MutateOperation operation1 = new MutateOperation()
            {
                CampaignCriterionOperation = new CampaignCriterionOperation()
                {
                    Create = new CampaignCriterion()
                    {
                        Campaign = campaignResourceName,
                        Location = new LocationInfo()
                        {
                            GeoTargetConstant = ResourceNames.GeoTargetConstant(1023191)
                        },

                        Negative = false
                    }
                }
            };

            operations.Add(operation1);

            // Next add the negative target for Brooklyn.
            MutateOperation operation2 = new MutateOperation()
            {
                CampaignCriterionOperation = new CampaignCriterionOperation()
                {
                    Create = new CampaignCriterion()
                    {
                        Campaign = campaignResourceName,
                        Location = new LocationInfo()
                        {
                            GeoTargetConstant = ResourceNames.GeoTargetConstant(1022762)
                        },

                        Negative = true
                    }
                }
            };

            operations.Add(operation2);

            // Set the LANGUAGE campaign criterion.
            MutateOperation operation3 = new MutateOperation()
            {
                CampaignCriterionOperation = new CampaignCriterionOperation()
                {
                    Create = new CampaignCriterion()
                    {
                        Campaign = campaignResourceName,

                        // Set the language.
                        // For a list of all language codes, see:
                        // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
                        Language = new LanguageInfo()
                        {
                            LanguageConstant = ResourceNames.LanguageConstant(1000) // English
                        },
                    }
                }
            };

            operations.Add(operation3);

            return operations;
        }


        /// <summary>
        /// Creates multiple text assets and returns the list of resource names.
        /// </summary>
        /// <param name="client">The Google Ads Client.</param>
        /// <param name="customerId">The customer's ID.</param>
        /// <param name="texts">The texts to add.</param>
        /// <returns>A list of asset resource names.</returns>
        private List<string> CreateMultipleTextAssets(
            GoogleAdsClient client,
            long customerId,
            string[] texts)
        {
            // Get the GoogleAdsService.
            GoogleAdsServiceClient googleAdsServiceClient =
                client.GetService(Services.V21.GoogleAdsService);

            MutateGoogleAdsRequest request = new MutateGoogleAdsRequest()
            {
                CustomerId = customerId.ToString()
            };

            foreach (string text in texts)
            {
                request.MutateOperations.Add(
                    new MutateOperation()
                    {
                        AssetOperation = new AssetOperation()
                        {
                            Create = new Asset()
                            {
                                TextAsset = new TextAsset()
                                {
                                    Text = text
                                }
                            }
                        }
                    }
                );
            }

            // Send the operations in a single Mutate request.
            MutateGoogleAdsResponse response = googleAdsServiceClient.Mutate(request);

            List<string> assetResourceNames = new List<string>();

            foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)
            {
                MutateAssetResult assetResult = operationResponse.AssetResult;
                assetResourceNames.Add(assetResult.ResourceName);
            }

            PrintResponseDetails(response);

            return assetResourceNames;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create a new asset_group.
        /// </summary>
        /// <param name="campaignResourceName">The campaign resource name.</param>
        /// <param name="assetGroupResourceName">The asset group resource name.</param>
        /// <param name="headlineAssetResourceNames">The headline asset resource names.</param>
        /// <param name="descriptionAssetResourceNames">The description asset resource
        /// names.</param>
        /// <param name="resourceNameGenerator">A generator for unique temporary ID's.</param>
        /// <param name="config">The Google Ads config.</param>
        /// <param name="brandGuidelinesEnabled">Whether or not to enable brand guidelines.</param>
        /// <returns>A list of MutateOperations that create the new asset group.</returns>
        private List<MutateOperation> CreateAssetGroupOperations(
            string campaignResourceName,
            string assetGroupResourceName,
            List<string> headlineAssetResourceNames,
            List<string> descriptionAssetResourceNames,
            AssetTemporaryResourceNameGenerator resourceNameGenerator,
            GoogleAdsConfig config,
            bool brandGuidelinesEnabled)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // Create the AssetGroup
            operations.Add(
                new MutateOperation()
                {
                    AssetGroupOperation = new AssetGroupOperation()
                    {
                        Create = new AssetGroup()
                        {
                            Name = "Performance Max asset group #" +
                                ExampleUtilities.GetRandomString(),
                            Campaign = campaignResourceName,
                            FinalUrls = { "http://www.example.com" },
                            FinalMobileUrls = { "http://www.example.com" },
                            Status = AssetGroupStatus.Paused,
                            ResourceName = assetGroupResourceName
                        }
                    }
                }
            );

            // 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

            // Link the previously created multiple text assets.

            // Link the headline assets.
            foreach (string resourceName in headlineAssetResourceNames)
            {
                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupAssetOperation = new AssetGroupAssetOperation()
                        {
                            Create = new AssetGroupAsset()
                            {
                                FieldType = AssetFieldType.Headline,
                                AssetGroup = assetGroupResourceName,
                                Asset = resourceName
                            }
                        }
                    }
                );
            }

            // Link the description assets.
            foreach (string resourceName in descriptionAssetResourceNames)
            {
                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupAssetOperation = new AssetGroupAssetOperation()
                        {
                            Create = new AssetGroupAsset()
                            {
                                FieldType = AssetFieldType.Description,
                                AssetGroup = assetGroupResourceName,
                                Asset = resourceName
                            }
                        }
                    }
                );
            }

            // Create and link the brand assets.
            operations.AddRange(
                CreateAndLinkBrandAssets(
                    assetGroupResourceName,
                    campaignResourceName,
                    resourceNameGenerator,
                    "Interplanetary Cruises",
                    "https://gaagl.page.link/bjYi",
                    "Marketing Logo",
                    config,
                    brandGuidelinesEnabled
                )
            );

            // Create and link the long headline text asset.
            operations.AddRange(
                CreateAndLinkTextAsset(
                    assetGroupResourceName,
                    resourceNameGenerator.Next(),
                    "Travel the World",
                    AssetFieldType.LongHeadline
                )
            );

            // Create and link the image assets.

            // Create and link the Marketing Image Asset.
            operations.AddRange(
                CreateAndLinkImageAsset(
                    assetGroupResourceName,
                    resourceNameGenerator.Next(),
                    "https://gaagl.page.link/Eit5",
                    AssetFieldType.MarketingImage,
                    "Marketing Image",
                    config
                )
            );

            // Create and link the Square Marketing Image Asset.
            operations.AddRange(
                CreateAndLinkImageAsset(
                    assetGroupResourceName,
                    resourceNameGenerator.Next(),
                    "https://gaagl.page.link/bjYi",
                    AssetFieldType.SquareMarketingImage,
                    "Square Marketing Image",
                    config
                )
            );

            return operations;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create a new linked text asset.
        /// </summary>
        /// <param name="assetGroupResourceName">The resource name of the asset group to be
        /// created.</param>
        /// <param name="assetResourceName">The resource name of the text asset to be
        /// created.</param>
        /// <param name="text">The text of the asset to be created.</param>
        /// <param name="fieldType">The field type of the asset to be created.</param>
        /// <returns>A list of MutateOperations that create the new linked text asset.</returns>
        private List<MutateOperation> CreateAndLinkTextAsset(
            string assetGroupResourceName,
            string assetResourceName,
            string text,
            AssetFieldType fieldType)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // Create the Text Asset.
            operations.Add(
                new MutateOperation()
                {
                    AssetOperation = new AssetOperation()
                    {
                        Create = new Asset()
                        {
                            ResourceName = assetResourceName,
                            TextAsset = new TextAsset()
                            {
                                Text = text
                            }
                        }
                    }
                }
            );

            // Create an AssetGroupAsset to link the Asset to the AssetGroup.
            operations.Add(
                new MutateOperation()
                {
                    AssetGroupAssetOperation = new AssetGroupAssetOperation()
                    {
                        Create = new AssetGroupAsset()
                        {
                            FieldType = fieldType,
                            AssetGroup = assetGroupResourceName,
                            Asset = assetResourceName
                        }
                    }
                }
            );

            return operations;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create a new linked image asset.
        /// </summary>
        /// <param name="assetGroupResourceName">The resource name of the asset group to be
        /// created.</param>
        /// <param name="assetResourceName">The resource name of the text asset to be
        /// created.</param>
        /// <param name="url">The url of the image to be retrieved and put into an asset.</param>
        /// <param name="fieldType">The field type of the asset to be created.</param>
        /// <param name="assetName">The asset name.</param>
        /// <param name="config">The Google Ads Config.</param>
        /// <returns>A list of MutateOperations that create a new linked image asset.</returns>
        private List<MutateOperation> CreateAndLinkImageAsset(
            string assetGroupResourceName,
            string assetResourceName,
            string url,
            AssetFieldType fieldType,
            string assetName, GoogleAdsConfig config)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            // Create the Image Asset.
            operations.Add(
                new MutateOperation()
                {
                    AssetOperation = new AssetOperation()
                    {
                        Create = new Asset()
                        {
                            ResourceName = assetResourceName,
                            ImageAsset = new ImageAsset()
                            {
                                Data =
                                    ByteString.CopyFrom(
                                        MediaUtilities.GetAssetDataFromUrl(url, config)
                                    )
                            },
                            // Provide a unique friendly name to identify your asset.
                            // When there is an existing image asset with the same content but a
                            // different name, the new name will be dropped silently.
                            Name = assetName
                        }
                    }
                }
            );

            // Create an AssetGroupAsset to link the Asset to the AssetGroup.
            operations.Add(
                new MutateOperation()
                {
                    AssetGroupAssetOperation = new AssetGroupAssetOperation()
                    {
                        Create = new AssetGroupAsset()
                        {
                            FieldType = fieldType,
                            AssetGroup = assetGroupResourceName,
                            Asset = assetResourceName
                        }
                    }
                }
            );

            return operations;
        }


        /// <summary>
        /// Creates a list of MutateOperations that create and link the brand assets.
        /// </summary>
        /// <param name="assetGroupResourceName">The resource name of the asset group to link assets
        /// to.</param>
        /// <param name="campaignResourceName">The resource name of the campaign to link assets
        /// to.</param>
        /// <param name="assetResourceNameGenerator">The resource name generator of the assets to be
        /// created.</param>
        /// <param name="businessName">The business name text to be put into an asset.</param>
        /// <param name="logoUrl">The url of the logo to be retrieved and put into an asset.</param>
        /// <param name="logoName">The asset name of the logo.</param>
        /// <param name="config">The Google Ads Config.</param>
        /// <param name="brandGuidelinesEnabled">Whether or not to enable brand guidelines.</param>
        /// <returns>A list of MutateOperations that create a new linked image asset.</returns>
        private List<MutateOperation> CreateAndLinkBrandAssets(
            string assetGroupResourceName,
            string campaignResourceName,
            AssetTemporaryResourceNameGenerator assetResourceNameGenerator,
            string businessName,
            string logoUrl,
            string logoName,
            GoogleAdsConfig config,
            bool brandGuidelinesEnabled)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            string logoAssetResourceName = assetResourceNameGenerator.Next();
            string businessNameAssetResourceName = assetResourceNameGenerator.Next();

            // Create the Image Asset.
            operations.Add(
                new MutateOperation()
                {
                    AssetOperation = new AssetOperation()
                    {
                        Create = new Asset()
                        {
                            ResourceName = logoAssetResourceName,
                            ImageAsset = new ImageAsset()
                            {
                                Data =
                                    ByteString.CopyFrom(
                                        MediaUtilities.GetAssetDataFromUrl(logoUrl, config)
                                    )
                            },
                            // Provide a unique friendly name to identify your asset.
                            // When there is an existing image asset with the same content but a
                            // different name, the new name will be dropped silently.
                            Name = logoName
                        }
                    }
                }
            );

            // Create the business name asset.
            operations.Add(
                new MutateOperation()
                {
                    AssetOperation = new AssetOperation()
                    {
                        Create = new Asset()
                        {
                            ResourceName = businessNameAssetResourceName,
                            TextAsset = new TextAsset()
                            {
                                Text = businessName,
                            }
                        }
                    }
                }
            );

            if (brandGuidelinesEnabled)
            {
                // Create CampaignAssets to link the Assets to the Campaign.
                operations.Add(
                    new MutateOperation()
                    {
                        CampaignAssetOperation = new CampaignAssetOperation()
                        {
                            Create = new CampaignAsset()
                            {
                                FieldType = AssetFieldType.Logo,
                                Campaign = campaignResourceName,
                                Asset = logoAssetResourceName
                            }
                        }
                    }
                );

                operations.Add(
                    new MutateOperation()
                    {
                        CampaignAssetOperation = new CampaignAssetOperation()
                        {
                            Create = new CampaignAsset()
                            {
                                FieldType = AssetFieldType.BusinessName,
                                Campaign = campaignResourceName,
                                Asset = businessNameAssetResourceName
                            }
                        }
                    }
                );
            } else {
                // Create AssetGroupAssets to link the Assets to the AssetGroup.
                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupAssetOperation = new AssetGroupAssetOperation()
                        {
                            Create = new AssetGroupAsset()
                            {
                                FieldType = AssetFieldType.Logo,
                                AssetGroup = assetGroupResourceName,
                                Asset = logoAssetResourceName
                            }
                        }
                    }
                );

                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupAssetOperation = new AssetGroupAssetOperation()
                        {
                            Create = new AssetGroupAsset()
                            {
                                FieldType = AssetFieldType.BusinessName,
                                AssetGroup = assetGroupResourceName,
                                Asset = businessNameAssetResourceName
                            }
                        }
                    }
                );

            }


            return operations;
        }

        /// <summary>
        /// Creates a list of MutateOperations that may create AssetGroupSignals
        /// </summary>
        /// <param name="customerId">The customer ID.</param>
        /// <param name="assetGroupResourceName">The resource name of the asset group to be
        /// created.</param>
        /// <param name="audienceId">The optional audience ID.</param>
        /// <returns>A list of MutateOperations that create may create AssetGroupSignals.</returns>
        private List<MutateOperation> CreateAssetGroupSignalOperations(
            long customerId,
            string assetGroupResourceName,
            long? audienceId)
        {
            List<MutateOperation> operations = new List<MutateOperation>();

            if (audienceId.HasValue)
            {
                // Create an audience asset group signal.
                // To learn more about Audience Signals, see
                // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals
                operations.Add(
                    new MutateOperation()
                    {
                        AssetGroupSignalOperation = new AssetGroupSignalOperation()
                        {
                            Create = new AssetGroupSignal()
                            {
                                AssetGroup = assetGroupResourceName,
                                Audience = new AudienceInfo()
                                {
                                    Audience = ResourceNames.Audience(customerId, audienceId.Value)
                                }
                            }
                        }
                    }
                );
            }

            // Create a search theme asset group signal.
            // To learn more about Search Themes Signals, see:
            // https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes
            operations.Add(
                new MutateOperation()
                {
                    AssetGroupSignalOperation = new AssetGroupSignalOperation()
                    {
                        Create = new AssetGroupSignal()
                        {
                            AssetGroup = assetGroupResourceName,
                            SearchTheme = new SearchThemeInfo()
                            {
                                Text = "travel"
                            }
                        }
                    }
                }
            );

            return operations;
        }

        /// <summary>
        /// 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.
        /// </summary>
        /// <param name="response">A MutateGoogleAdsResponse instance.</param>
        private void PrintResponseDetails(MutateGoogleAdsResponse response)
        {
            // Parse the Mutate response to print details about the entities that were created
            // in the request.
            foreach (MutateOperationResponse operationResponse in response.MutateOperationResponses)
            {
                string entityName = operationResponse.ResponseCase.ToString();
                // Trim the substring "Result" from the end of the entity name.
                entityName = entityName.Remove(entityName.Length - 6);

                string resourceName;
                switch (operationResponse.ResponseCase)
                {
                    case MutateOperationResponse.ResponseOneofCase.AdGroupResult:
                        resourceName = operationResponse.AdGroupResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AdGroupAdResult:
                        resourceName = operationResponse.AdGroupAdResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignResult:
                        resourceName = operationResponse.CampaignResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignBudgetResult:
                        resourceName = operationResponse.CampaignBudgetResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.CampaignCriterionResult:
                        resourceName = operationResponse.CampaignCriterionResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.SmartCampaignSettingResult:
                        resourceName = operationResponse.SmartCampaignSettingResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetResult:
                        resourceName = operationResponse.AssetResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetGroupResult:
                        resourceName = operationResponse.AssetGroupResult.ResourceName;
                        break;

                    case MutateOperationResponse.ResponseOneofCase.AssetGroupAssetResult:
                        resourceName = operationResponse.AssetGroupAssetResult.ResourceName;
                        break;

                    default:
                        resourceName = "<not found>";
                        break;
                }

                Console.WriteLine(
                    $"Created a(n) {entityName} with resource name: '{resourceName}'.");
            }
        }
    }
}
      

PHP

<?php

/**
 * Copyright 2021 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace Google\Ads\GoogleAds\Examples\AdvancedOperations;

require __DIR__ . '/../../vendor/autoload.php';

use GetOpt\GetOpt;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentNames;
use Google\Ads\GoogleAds\Examples\Utils\ArgumentParser;
use Google\Ads\GoogleAds\Examples\Utils\Helper;
use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
use Google\Ads\GoogleAds\Lib\V21\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V21\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V21\GoogleAdsException;
use Google\Ads\GoogleAds\Util\V21\ResourceNames;
use Google\Ads\GoogleAds\V21\Common\AudienceInfo;
use Google\Ads\GoogleAds\V21\Common\ImageAsset;
use Google\Ads\GoogleAds\V21\Common\LanguageInfo;
use Google\Ads\GoogleAds\V21\Common\LocationInfo;
use Google\Ads\GoogleAds\V21\Common\MaximizeConversionValue;
use Google\Ads\GoogleAds\V21\Common\TextAsset;
use Google\Ads\GoogleAds\V21\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType;
use Google\Ads\GoogleAds\V21\Enums\AssetFieldTypeEnum\AssetFieldType;
use Google\Ads\GoogleAds\V21\Enums\AssetGroupStatusEnum\AssetGroupStatus;
use Google\Ads\GoogleAds\V21\Enums\BudgetDeliveryMethodEnum\BudgetDeliveryMethod;
use Google\Ads\GoogleAds\V21\Enums\CampaignStatusEnum\CampaignStatus;
use Google\Ads\GoogleAds\V21\Enums\EuPoliticalAdvertisingStatusEnum\EuPoliticalAdvertisingStatus;
use Google\Ads\GoogleAds\V21\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V21\Resources\Asset;
use Google\Ads\GoogleAds\V21\Resources\AssetGroup;
use Google\Ads\GoogleAds\V21\Resources\AssetGroupAsset;
use Google\Ads\GoogleAds\V21\Resources\AssetGroupSignal;
use Google\Ads\GoogleAds\V21\Resources\Campaign;
use Google\Ads\GoogleAds\V21\Resources\CampaignAsset;
use Google\Ads\GoogleAds\V21\Resources\CampaignBudget;
use Google\Ads\GoogleAds\V21\Resources\CampaignCriterion;
use Google\Ads\GoogleAds\V21\Services\AssetGroupAssetOperation;
use Google\Ads\GoogleAds\V21\Services\AssetGroupOperation;
use Google\Ads\GoogleAds\V21\Services\AssetGroupSignalOperation;
use Google\Ads\GoogleAds\V21\Services\AssetOperation;
use Google\Ads\GoogleAds\V21\Services\CampaignAssetOperation;
use Google\Ads\GoogleAds\V21\Services\CampaignBudgetOperation;
use Google\Ads\GoogleAds\V21\Services\CampaignCriterionOperation;
use Google\Ads\GoogleAds\V21\Services\CampaignOperation;
use Google\Ads\GoogleAds\V21\Services\MutateGoogleAdsRequest;
use Google\Ads\GoogleAds\V21\Services\MutateGoogleAdsResponse;
use Google\Ads\GoogleAds\V21\Services\MutateOperation;
use Google\Ads\GoogleAds\V21\Services\MutateOperationResponse;
use Google\ApiCore\ApiException;
use Google\ApiCore\Serializer;

/**
 * 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.
 *
 * This example uses the default customer conversion goals. For an example of setting
 * campaign-specific conversion goals, see ShoppingAds/AddPerformanceMaxRetailCampaign.php.
 */
class AddPerformanceMaxCampaign
{
    private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';
    // Optional: An audience ID to use to improve the targeting of the Performance Max campaign.
    private const AUDIENCE_ID = null;
    // Optional: Indicates whether the created campaign is enabled for brand guidelines.
    private const BRAND_GUIDELINES_ENABLED = false;

    // 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 const BUDGET_TEMPORARY_ID = -1;
    private const PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = -2;
    private const 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.
    /** @var int the negative temporary ID used in bulk mutates. */
    private static $nextTempId = self::ASSET_GROUP_TEMPORARY_ID - 1;

    public static function main()
    {
        // Either pass the required parameters for this example on the command line, or insert them
        // into the constants above.
        $options = (new ArgumentParser())->parseCommandArguments([
            ArgumentNames::CUSTOMER_ID => GetOpt::REQUIRED_ARGUMENT,
            ArgumentNames::AUDIENCE_ID => GetOpt::OPTIONAL_ARGUMENT,
            ArgumentNames::BRAND_GUIDELINES_ENABLED => GetOpt::OPTIONAL_ARGUMENT
        ]);

        // Generate a refreshable OAuth2 credential for authentication.
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Construct a Google Ads client configured from a properties file and the
        // OAuth2 credentials above.
        $googleAdsClient = (new GoogleAdsClientBuilder())
            ->fromFile()
            ->withOAuth2Credential($oAuth2Credential)
            ->build();

        try {
            self::runExample(
                $googleAdsClient,
                $options[ArgumentNames::CUSTOMER_ID] ?: self::CUSTOMER_ID,
                $options[ArgumentNames::AUDIENCE_ID] ?: self::AUDIENCE_ID,
                filter_var(
                    $options[ArgumentNames::BRAND_GUIDELINES_ENABLED]
                        ?: self::BRAND_GUIDELINES_ENABLED,
                    FILTER_VALIDATE_BOOLEAN
                )
            );
        } catch (GoogleAdsException $googleAdsException) {
            printf(
                "Request with ID '%s' has failed.%sGoogle Ads failure details:%s",
                $googleAdsException->getRequestId(),
                PHP_EOL,
                PHP_EOL
            );
            foreach ($googleAdsException->getGoogleAdsFailure()->getErrors() as $error) {
                /** @var GoogleAdsError $error */
                printf(
                    "\t%s: %s%s",
                    $error->getErrorCode()->getErrorCode(),
                    $error->getMessage(),
                    PHP_EOL
                );
            }
            exit(1);
        } catch (ApiException $apiException) {
            printf(
                "ApiException was thrown with message '%s'.%s",
                $apiException->getMessage(),
                PHP_EOL
            );
            exit(1);
        }
    }

    /**
     * Runs the example.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param int|null $audienceId the audience ID
     * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand
     *     guidelines
     */
    public static function runExample(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        ?int $audienceId,
        bool $brandGuidelinesEnabled
    ) {
        // 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.
        $headlineAssetResourceNames = self::createMultipleTextAssets(
            $googleAdsClient,
            $customerId,
            ["Travel", "Travel Reviews", "Book travel"]
        );
        // Creates the descriptions.
        $descriptionAssetResourceNames = self::createMultipleTextAssets(
            $googleAdsClient,
            $customerId,
            ["Take to the air!", "Fly to the sky!"]
        );

        // It's important to create the below entities in this order because they depend on
        // each other.
        $operations = [];
        // 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.
        $operations[] = self::createCampaignBudgetOperation($customerId);
        $operations[] =
            self::createPerformanceMaxCampaignOperation($customerId, $brandGuidelinesEnabled);
        $operations =
            array_merge($operations, self::createCampaignCriterionOperations($customerId));
        $operations = array_merge($operations, self::createAssetGroupOperations(
            $customerId,
            $headlineAssetResourceNames,
            $descriptionAssetResourceNames,
            $brandGuidelinesEnabled
        ));
        $operations = array_merge($operations, self::createAssetGroupSignalOperations(
            $customerId,
            ResourceNames::forAssetGroup($customerId, self::ASSET_GROUP_TEMPORARY_ID),
            $audienceId
        ));

        // Issues a mutate request to create everything and prints its information.
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        $response = $googleAdsServiceClient->mutate(MutateGoogleAdsRequest::build(
            $customerId,
            $operations
        ));

        self::printResponseDetails($response);
    }

    /**
     * Creates a MutateOperation that creates a new CampaignBudget.
     *
     * A temporary ID will be assigned to this campaign budget so that it can be
     * referenced by other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation the mutate operation that creates a campaign budget
     */
    private static function createCampaignBudgetOperation(int $customerId): MutateOperation
    {
        // Creates a mutate operation that creates a campaign budget operation.
        return new MutateOperation([
            'campaign_budget_operation' => new CampaignBudgetOperation([
                'create' => new CampaignBudget([
                    // Sets a temporary ID in the budget's resource name so it can be referenced
                    // by the campaign in later steps.
                    'resource_name' => ResourceNames::forCampaignBudget(
                        $customerId,
                        self::BUDGET_TEMPORARY_ID
                    ),
                    'name' => 'Performance Max campaign budget #' . Helper::getPrintableDatetime(),
                    // The budget period already defaults to DAILY.
                    'amount_micros' => 50000000,
                    'delivery_method' => BudgetDeliveryMethod::STANDARD,
                    // A Performance Max campaign cannot use a shared campaign budget.
                    'explicitly_shared' => false
                ])
            ])
        ]);
    }

    /**
     * Creates a MutateOperation that creates a new Performance Max campaign.
     *
     * A temporary ID will be assigned to this campaign so that it can
     * be referenced by other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand
     *     guidelines
     * @return MutateOperation the mutate operation that creates the campaign
     */
    private static function createPerformanceMaxCampaignOperation(
        int $customerId,
        bool $brandGuidelinesEnabled
    ): MutateOperation {
        // Creates a mutate operation that creates a campaign operation.
        return new MutateOperation([
            'campaign_operation' => new CampaignOperation([
                'create' => new Campaign([
                    'name' => 'Performance Max campaign #' . Helper::getPrintableDatetime(),
                    // Assigns the resource name with a temporary ID.
                    'resource_name' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    // Sets the budget using the given budget resource name.
                    'campaign_budget' => ResourceNames::forCampaignBudget(
                        $customerId,
                        self::BUDGET_TEMPORARY_ID
                    ),
                    // The campaign is the only entity in the mutate request that should have its
                    // status set.
                    // Recommendation: Set the campaign to PAUSED when creating it to prevent
                    // the ads from immediately serving.
                    'status' => CampaignStatus::PAUSED,
                    // All Performance Max campaigns have an advertising_channel_type of
                    // PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
                    'advertising_channel_type' => 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 target_roas of 3.5 corresponds to a 350% return on ad spend.
                    'maximize_conversion_value' => new MaximizeConversionValue([
                        'target_roas' => 3.5
                    ]),

                    // 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.
                    'url_expansion_opt_out' => false,

                    // Sets if the campaign is enabled for brand guidelines. For more information
                    // on brand guidelines, see
                    // https://support.google.com/google-ads/answer/14934472.
                    'brand_guidelines_enabled' => $brandGuidelinesEnabled,

                    // Declare whether or not this campaign serves political ads targeting the EU.
                    'contains_eu_political_advertising' =>
                        EuPoliticalAdvertisingStatus::DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING,

                    // Optional fields.
                    'start_date' => date('Ymd', strtotime('+1 day')),
                    'end_date' => date('Ymd', strtotime('+365 days'))
                ])
            ])
        ]);
    }

    /**
     * Creates a list of MutateOperations that create new campaign criteria.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation[] a list of MutateOperations that create the new campaign criteria
     */
    private static function createCampaignCriterionOperations(int $customerId): array
    {
        $operations = [];
        // Set the LOCATION campaign criteria.
        // Target 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.
        $operations[] = new MutateOperation([
            'campaign_criterion_operation' => new CampaignCriterionOperation([
                'create' => new CampaignCriterion([
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    'location' => new LocationInfo([
                        'geo_target_constant' => ResourceNames::forGeoTargetConstant(1023191)
                    ]),
                    'negative' => false
                ])
            ])
        ]);

        // Next adds the negative target for Brooklyn.
        $operations[] = new MutateOperation([
            'campaign_criterion_operation' => new CampaignCriterionOperation([
                'create' => new CampaignCriterion([
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    'location' => new LocationInfo([
                        'geo_target_constant' => ResourceNames::forGeoTargetConstant(1022762)
                    ]),
                    'negative' => true
                ])
            ])
        ]);

        // Sets the LANGUAGE campaign criterion.
        $operations[] = new MutateOperation([
            'campaign_criterion_operation' => new CampaignCriterionOperation([
                'create' => new CampaignCriterion([
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    // Set the language.
                    // For a list of all language codes, see:
                    // https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
                    'language' => new LanguageInfo([
                        'language_constant' => ResourceNames::forLanguageConstant(1000)  // English
                    ])
                ])
            ])
        ]);

        return $operations;
    }

    /**
     * Creates multiple text assets and returns the list of resource names.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param string[] $texts a list of strings, each of which will be used to create a text asset
     * @return string[] a list of asset resource names
     */
    private static function createMultipleTextAssets(
        GoogleAdsClient $googleAdsClient,
        int $customerId,
        array $texts
    ): array {
        // Here again, we use the GoogleAdService to create multiple text assets in a single
        // request.
        $operations = [];
        foreach ($texts as $text) {
            // Creates a mutate operation for a text asset.
            $operations[] = new MutateOperation([
                'asset_operation' => new AssetOperation([
                    'create' => new Asset(['text_asset' => new TextAsset(['text' => $text])])
                ])
            ]);
        }

        // Issues a mutate request to add all assets.
        $googleAdsService = $googleAdsClient->getGoogleAdsServiceClient();
        /** @var MutateGoogleAdsResponse $mutateGoogleAdsResponse */
        $mutateGoogleAdsResponse =
            $googleAdsService->mutate(MutateGoogleAdsRequest::build($customerId, $operations));

        $assetResourceNames = [];
        foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {
            /** @var MutateOperationResponse $response */
            $assetResourceNames[] = $response->getAssetResult()->getResourceName();
        }
        self::printResponseDetails($mutateGoogleAdsResponse);

        return $assetResourceNames;
    }

    /**
     * Creates a list of MutateOperations that create a new asset group.
     *
     * A temporary ID will be assigned to this asset group so that it can
     * be referenced by other objects being created in the same Mutate request.
     *
     * @param int $customerId the customer ID
     * @param string[] $headlineAssetResourceNames a list of headline resource names
     * @param string[] $descriptionAssetResourceNames a list of description resource names
     * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand
     *     guidelines
     * @return MutateOperation[] a list of MutateOperations that create new asset group
     */
    private static function createAssetGroupOperations(
        int $customerId,
        array $headlineAssetResourceNames,
        array $descriptionAssetResourceNames,
        bool $brandGuidelinesEnabled
    ): array {
        $operations = [];
        // Creates a new mutate operation that creates an asset group operation.
        $operations[] = new MutateOperation([
            'asset_group_operation' => new AssetGroupOperation([
                'create' => new AssetGroup([
                    'resource_name' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    'name' => 'Performance Max asset group #' . Helper::getPrintableDatetime(),
                    'campaign' => ResourceNames::forCampaign(
                        $customerId,
                        self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                    ),
                    'final_urls' => ['http://www.example.com'],
                    'final_mobile_urls' => ['http://www.example.com'],
                    'status' => AssetGroupStatus::PAUSED
                ])
            ])
        ]);

        // 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.
        foreach ($headlineAssetResourceNames as $resourceName) {
            $operations[] = new MutateOperation([
                'asset_group_asset_operation' => new AssetGroupAssetOperation([
                    'create' => new AssetGroupAsset([
                        'asset' => $resourceName,
                        'asset_group' => ResourceNames::forAssetGroup(
                            $customerId,
                            self::ASSET_GROUP_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::HEADLINE
                    ])
                ])
            ]);
        }
        // Links the description assets.
        foreach ($descriptionAssetResourceNames as $resourceName) {
            $operations[] = new MutateOperation([
                'asset_group_asset_operation' => new AssetGroupAssetOperation([
                    'create' => new AssetGroupAsset([
                        'asset' => $resourceName,
                        'asset_group' => ResourceNames::forAssetGroup(
                            $customerId,
                            self::ASSET_GROUP_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::DESCRIPTION
                    ])
                ])
            ]);
        }

        // Creates and links the long headline text asset.
        $operations = array_merge($operations, self::createAndLinkTextAsset(
            $customerId,
            'Travel the World',
            AssetFieldType::LONG_HEADLINE
        ));
        // Creates and links the business name text asset.
        $operations = array_merge($operations, self::createAndLinkBrandAssets(
            $customerId,
            $brandGuidelinesEnabled,
            'Interplanetary Cruises',
            'https://gaagl.page.link/bjYi',
            'Marketing Logo'
        ));

        // Creates and links the image assets.

        // Creates and links the Marketing Image Asset.
        $operations = array_merge($operations, self::createAndLinkImageAsset(
            $customerId,
            'https://gaagl.page.link/Eit5',
            AssetFieldType::MARKETING_IMAGE,
            'Marketing Image'
        ));
        // Creates and links the Square Marketing Image Asset.
        $operations = array_merge($operations, self::createAndLinkImageAsset(
            $customerId,
            'https://gaagl.page.link/bjYi',
            AssetFieldType::SQUARE_MARKETING_IMAGE,
            'Square Marketing Image'
        ));

        return $operations;
    }

    /**
     * Creates a list of MutateOperations that create a new linked text asset.
     *
     * @param int $customerId the customer ID
     * @param string $text the text of the asset to be created
     * @param int $fieldType the field type of the new asset in the AssetGroupAsset
     * @return MutateOperation[] a list of MutateOperations that create a new linked text asset
     */
    private static function createAndLinkTextAsset(
        int $customerId,
        string $text,
        int $fieldType
    ): array {
        $operations = [];
        // Creates a new mutate operation that creates a text asset.
        $operations[] = new MutateOperation([
            'asset_operation' => new AssetOperation([
                'create' => new Asset([
                    'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    'text_asset' => new TextAsset(['text' => $text])
                ])
            ])
        ]);

        // Creates an asset group asset to link the asset to the asset group.
        $operations[] = new MutateOperation([
            'asset_group_asset_operation' => new AssetGroupAssetOperation([
                'create' => new AssetGroupAsset([
                    'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    'asset_group' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    'field_type' => $fieldType
                ])
            ])
        ]);
        self::$nextTempId--;

        return $operations;
    }

    /**
     * Creates a list of MutateOperations that create a new linked image asset.
     *
     * @param int $customerId the customer ID
     * @param string $url the URL of the image to be retrieved and put into an asset
     * @param int $fieldType the field type of the new asset in the AssetGroupAsset
     * @param string $assetName the asset name
     * @return MutateOperation[] a list of MutateOperations that create a new linked image asset
     */
    private static function createAndLinkImageAsset(
        int $customerId,
        string $url,
        int $fieldType,
        string $assetName
    ): array {
        $operations = [];
        // Creates a new mutate operation that creates an image asset.
        $operations[] = new MutateOperation([
            'asset_operation' => new AssetOperation([
                'create' => new Asset([
                    'resource_name' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    // Provide a unique friendly name to identify your asset.
                    // When there is an existing image asset with the same content but a different
                    // name, the new name will be dropped silently.
                    'name' => $assetName,
                    'image_asset' => new ImageAsset(['data' => file_get_contents($url)])
                ])
            ])
        ]);

        // Creates an asset group asset to link the asset to the asset group.
        $operations[] = new MutateOperation([
            'asset_group_asset_operation' => new AssetGroupAssetOperation([
                'create' => new AssetGroupAsset([
                    'asset' => ResourceNames::forAsset($customerId, self::$nextTempId),
                    'asset_group' => ResourceNames::forAssetGroup(
                        $customerId,
                        self::ASSET_GROUP_TEMPORARY_ID
                    ),
                    'field_type' => $fieldType
                ])
            ])
        ]);
        self::$nextTempId--;

        return $operations;
    }

    /**
     * Creates a list of MutateOperations that create linked brand assets.
     *
     * @param int $customerId the customer ID
     * @param bool $brandGuidelinesEnabled whether the created campaign will be enabled for brand
     *     guidelines
     * @param string $businessName the business name text to be put into an asset
     * @param string $logoUrl the URL of the logo to be retrieved and put into an asset
     * @param string $logoName the asset name of the logo
     * @return MutateOperation[] a list of MutateOperations that create a new linked text asset
     */
    private static function createAndLinkBrandAssets(
        int $customerId,
        bool $brandGuidelinesEnabled,
        string $businessName,
        string $logoUrl,
        string $logoName
    ): array {
        $operations = [];
        // Creates a new mutate operation that creates a text asset.
        $businessNameTempId = self::$nextTempId--;
        $operations[] = new MutateOperation([
            'asset_operation' => new AssetOperation([
                'create' => new Asset([
                    'resource_name' => ResourceNames::forAsset($customerId, $businessNameTempId),
                    'text_asset' => new TextAsset(['text' => $businessName])
                ])
            ])
        ]);

        $logoTempId = self::$nextTempId--;
        // Creates a new mutate operation that creates an image asset.
        $operations[] = new MutateOperation([
            'asset_operation' => new AssetOperation([
                'create' => new Asset([
                    'resource_name' => ResourceNames::forAsset($customerId, $logoTempId),
                    // Provide a unique friendly name to identify your asset.
                    // When there is an existing image asset with the same content but a different
                    // name, the new name will be dropped silently.
                    'name' => $logoName,
                    'image_asset' => new ImageAsset(['data' => file_get_contents($logoUrl)])
                ])
            ])
        ]);

        if ($brandGuidelinesEnabled) {
            // Creates a campaign asset to link the business name and logo assets to the campaign.
            $operations[] = new MutateOperation([
                'campaign_asset_operation' => new CampaignAssetOperation([
                    'create' => new CampaignAsset([
                        'asset' => ResourceNames::forAsset($customerId, $businessNameTempId),
                        'campaign' => ResourceNames::forCampaign(
                            $customerId,
                            self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::BUSINESS_NAME
                    ])
                ])
            ]);
            $operations[] = new MutateOperation([
                'campaign_asset_operation' => new CampaignAssetOperation([
                    'create' => new CampaignAsset([
                        'asset' => ResourceNames::forAsset($customerId, $logoTempId),
                        'campaign' => ResourceNames::forCampaign(
                            $customerId,
                            self::PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::LOGO
                    ])
                ])
            ]);
        } else {
            // Creates an asset group asset to link the business name and logo assets to the asset
            // group.
            $operations[] = new MutateOperation([
                'asset_group_asset_operation' => new AssetGroupAssetOperation([
                    'create' => new AssetGroupAsset([
                        'asset' => ResourceNames::forAsset($customerId, $businessNameTempId),
                        'asset_group' => ResourceNames::forAssetGroup(
                            $customerId,
                            self::ASSET_GROUP_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::BUSINESS_NAME
                    ])
                ])
            ]);
            $operations[] = new MutateOperation([
                'asset_group_asset_operation' => new AssetGroupAssetOperation([
                    'create' => new AssetGroupAsset([
                        'asset' => ResourceNames::forAsset($customerId, $logoTempId),
                        'asset_group' => ResourceNames::forAssetGroup(
                            $customerId,
                            self::ASSET_GROUP_TEMPORARY_ID
                        ),
                        'field_type' => AssetFieldType::LOGO
                    ])
                ])
            ]);
        }

        return $operations;
    }

    /**
     * Creates a list of MutateOperations that may create asset group signals.
     *
     * @param int $customerId the customer ID
     * @param string $assetGroupResourceName the resource name of the asset group
     * @param int|null $audienceId the audience ID
     * @return MutateOperation[] a list of MutateOperations that may create asset group signals
     */
    private static function createAssetGroupSignalOperations(
        int $customerId,
        string $assetGroupResourceName,
        ?int $audienceId
    ): array {
        $operations = [];
        if (is_null($audienceId)) {
            return $operations;
        }

        $operations[] = new MutateOperation([
            'asset_group_signal_operation' => new AssetGroupSignalOperation([
                // To learn more about Audience Signals, see
                // https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals.
                'create' => new AssetGroupSignal([
                    'asset_group' => $assetGroupResourceName,
                    'audience' => new AudienceInfo([
                        'audience' => ResourceNames::forAudience($customerId, $audienceId)
                    ])
                ])
            ])
        ]);

        return $operations;
    }

    /**
     * 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.
     *
     * @param MutateGoogleAdsResponse $mutateGoogleAdsResponse the mutate Google Ads response
     */
    private static function printResponseDetails(
        MutateGoogleAdsResponse $mutateGoogleAdsResponse
    ): void {
        foreach ($mutateGoogleAdsResponse->getMutateOperationResponses() as $response) {
            /** @var MutateOperationResponse $response */
            $getter = Serializer::getGetter($response->getResponse());
            printf(
                "Created a(n) %s with '%s'.%s",
                preg_replace(
                    '/Result$/',
                    '',
                    ucfirst(Serializer::toCamelCase($response->getResponse()))
                ),
                $response->$getter()->getResourceName(),
                PHP_EOL
            );
        }
    }
}

AddPerformanceMaxCampaign::main();

      

Python

#!/usr/bin/env python
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This 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

This example uses the default customer conversion goals. For an example
of setting campaign-specific conversion goals, see
shopping_ads/add_performance_max_retail_campaign.py
"""


import argparse
from datetime import datetime, timedelta
import sys
from typing import List, Optional, Iterable
from uuid import uuid4

from examples.utils.example_helpers import get_image_bytes_from_url
from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException
from google.ads.googleads.util import convert_snake_case_to_upper_case
from google.ads.googleads.v21.enums.types.asset_field_type import (
    AssetFieldTypeEnum,
)
from google.ads.googleads.v21.resources.types.asset import Asset
from google.ads.googleads.v21.resources.types.asset_group import AssetGroup
from google.ads.googleads.v21.resources.types.asset_group_asset import (
    AssetGroupAsset,
)
from google.ads.googleads.v21.resources.types.asset_group_signal import (
    AssetGroupSignal,
)
from google.ads.googleads.v21.resources.types.campaign import Campaign
from google.ads.googleads.v21.resources.types.campaign_asset import (
    CampaignAsset,
)
from google.ads.googleads.v21.resources.types.campaign_budget import (
    CampaignBudget,
)
from google.ads.googleads.v21.resources.types.campaign_criterion import (
    CampaignCriterion,
)
from google.ads.googleads.v21.services.services.asset_group_service import (
    AssetGroupServiceClient,
)
from google.ads.googleads.v21.services.services.asset_service import (
    AssetServiceClient,
)
from google.ads.googleads.v21.services.services.campaign_service import (
    CampaignServiceClient,
)
from google.ads.googleads.v21.services.services.geo_target_constant_service import (
    GeoTargetConstantServiceClient,
)
from google.ads.googleads.v21.services.services.google_ads_service import (
    GoogleAdsServiceClient,
)
from google.ads.googleads.v21.services.types.campaign_budget_service import (
    CampaignBudgetOperation,
)
from google.ads.googleads.v21.services.types.google_ads_service import (
    MutateGoogleAdsResponse,
    MutateOperation,
    MutateOperationResponse,
)


# 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.
_BUDGET_TEMPORARY_ID = "-1"
_PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = "-2"
_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.
next_temp_id = int(_ASSET_GROUP_TEMPORARY_ID) - 1


def main(
    client: GoogleAdsClient,
    customer_id: str,
    audience_id: Optional[str],
    brand_guidelines_enabled: bool,
) -> None:
    """The main method that creates all necessary entities for the example.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        audience_id: an optional audience ID.
        brand_guidelines_enabled: a boolean value indicating if the campaign is
          enabled for brand guidelines.
    """
    googleads_service: GoogleAdsServiceClient = client.get_service(
        "GoogleAdsService"
    )

    # 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
    #
    # Create the headlines.
    headline_asset_resource_names: List[str] = create_multiple_text_assets(
        client,
        customer_id,
        [
            "Travel",
            "Travel Reviews",
            "Book travel",
        ],
    )
    # Create the descriptions.
    description_asset_resource_names: List[str] = create_multiple_text_assets(
        client,
        customer_id,
        [
            "Take to the air!",
            "Fly to the sky!",
        ],
    )

    # 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
    campaign_budget_operation: MutateOperation = (
        create_campaign_budget_operation(
            client,
            customer_id,
        )
    )
    performance_max_campaign_operation: MutateOperation = (
        create_performance_max_campaign_operation(
            client,
            customer_id,
            brand_guidelines_enabled,
        )
    )
    campaign_criterion_operations: List[MutateOperation] = (
        create_campaign_criterion_operations(
            client,
            customer_id,
        )
    )
    asset_group_operations: List[MutateOperation] = (
        create_asset_group_operation(
            client,
            customer_id,
            headline_asset_resource_names,
            description_asset_resource_names,
            brand_guidelines_enabled,
        )
    )
    asset_group_signal_operations: List[MutateOperation] = (
        create_asset_group_signal_operations(client, customer_id, audience_id)
    )

    mutate_operations: List[MutateOperation] = [
        # It's important to create these entities in this order because
        # they depend on each other.
        campaign_budget_operation,
        performance_max_campaign_operation,
        # Expand the list of multiple operations into the list of
        # other mutate operations
        *campaign_criterion_operations,
        *asset_group_operations,
        *asset_group_signal_operations,
    ]

    # Send the operations in a single Mutate request.
    response: MutateGoogleAdsResponse = googleads_service.mutate(
        customer_id=customer_id, mutate_operations=mutate_operations
    )

    print_response_details(response)


def create_campaign_budget_operation(
    client: GoogleAdsClient,
    customer_id: str,
) -> MutateOperation:
    """Creates a MutateOperation that creates a new CampaignBudget.

    A temporary ID will be assigned to this campaign budget so that it can be
    referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a MutateOperation that creates a CampaignBudget.
    """
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    campaign_budget_operation: CampaignBudgetOperation = (
        mutate_operation.campaign_budget_operation
    )
    campaign_budget: CampaignBudget = campaign_budget_operation.create
    campaign_budget.name = f"Performance Max campaign budget #{uuid4()}"
    # The budget period already defaults to DAILY.
    campaign_budget.amount_micros = 50000000
    campaign_budget.delivery_method = (
        client.enums.BudgetDeliveryMethodEnum.STANDARD
    )
    # A Performance Max campaign cannot use a shared campaign budget.
    campaign_budget.explicitly_shared = False

    # Set a temporary ID in the budget's resource name so it can be referenced
    # by the campaign in later steps.
    campaign_budget.resource_name = client.get_service(
        "CampaignBudgetService"
    ).campaign_budget_path(customer_id, _BUDGET_TEMPORARY_ID)

    return mutate_operation


def create_performance_max_campaign_operation(
    client: GoogleAdsClient,
    customer_id: str,
    brand_guidelines_enabled: bool,
) -> MutateOperation:
    """Creates a MutateOperation that creates a new Performance Max campaign.

    A temporary ID will be assigned to this campaign so that it can
    be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        brand_guidelines_enabled: a boolean value indicating if the campaign is
          enabled for brand guidelines.

    Returns:
        a MutateOperation that creates a campaign.
    """
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    campaign: Campaign = mutate_operation.campaign_operation.create
    campaign.name = f"Performance Max campaign #{uuid4()}"
    # Set the campaign status as PAUSED. The campaign is the only entity in
    # the mutate request that should have its status set.
    campaign.status = client.enums.CampaignStatusEnum.PAUSED
    # All Performance Max campaigns have an advertising_channel_type of
    # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
    campaign.advertising_channel_type = (
        client.enums.AdvertisingChannelTypeEnum.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 target_roas of 3.5 corresponds to a 350% return on ad spend.
    campaign.bidding_strategy_type = (
        client.enums.BiddingStrategyTypeEnum.MAXIMIZE_CONVERSION_VALUE
    )
    campaign.maximize_conversion_value.target_roas = 3.5

    # Set 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.
    campaign.url_expansion_opt_out = False

    # Set if the campaign is enabled for brand guidelines. For more information
    # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.
    campaign.brand_guidelines_enabled = brand_guidelines_enabled

    # Assign the resource name with a temporary ID.
    campaign_service: CampaignServiceClient = client.get_service(
        "CampaignService"
    )
    campaign.resource_name = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    # Set the budget using the given budget resource name.
    campaign.campaign_budget = campaign_service.campaign_budget_path(
        customer_id, _BUDGET_TEMPORARY_ID
    )

    # Declare whether or not this campaign serves political ads targeting the
    # EU. Valid values are:
    #   CONTAINS_EU_POLITICAL_ADVERTISING
    #   DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING
    campaign.contains_eu_political_advertising = (
        client.enums.EuPoliticalAdvertisingStatusEnum.DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING
    )

    # Optional fields
    campaign.start_date = (datetime.now() + timedelta(1)).strftime("%Y%m%d")
    campaign.end_date = (datetime.now() + timedelta(365)).strftime("%Y%m%d")

    return mutate_operation


def create_campaign_criterion_operations(
    client: GoogleAdsClient,
    customer_id: str,
) -> List[MutateOperation]:
    """Creates a list of MutateOperations that create new campaign criteria.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.

    Returns:
        a list of MutateOperations that create new campaign criteria.
    """
    campaign_service: CampaignServiceClient = client.get_service(
        "CampaignService"
    )
    geo_target_constant_service: GeoTargetConstantServiceClient = (
        client.get_service("GeoTargetConstantService")
    )
    googleads_service: GoogleAdsServiceClient = client.get_service(
        "GoogleAdsService"
    )

    operations: List[MutateOperation] = []
    # Set the LOCATION campaign criteria.
    # Target 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, add the positive (negative = False) for New York City.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    campaign_criterion: CampaignCriterion = (
        mutate_operation.campaign_criterion_operation.create
    )
    campaign_criterion.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    campaign_criterion.location.geo_target_constant = (
        geo_target_constant_service.geo_target_constant_path("1023191")
    )
    campaign_criterion.negative = False
    operations.append(mutate_operation)

    # Next add the negative target for Brooklyn.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    campaign_criterion: CampaignCriterion = (
        mutate_operation.campaign_criterion_operation.create
    )
    campaign_criterion.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    campaign_criterion.location.geo_target_constant = (
        geo_target_constant_service.geo_target_constant_path("1022762")
    )
    campaign_criterion.negative = True
    operations.append(mutate_operation)

    # Set the LANGUAGE campaign criterion.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    campaign_criterion: CampaignCriterion = (
        mutate_operation.campaign_criterion_operation.create
    )
    campaign_criterion.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    # Set the language.
    # For a list of all language codes, see:
    # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
    campaign_criterion.language.language_constant = (
        googleads_service.language_constant_path("1000")
    )  # English
    operations.append(mutate_operation)

    return operations


def create_multiple_text_assets(
    client: GoogleAdsClient, customer_id: str, texts: List[str]
) -> List[str]:
    """Creates multiple text assets and returns the list of resource names.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        texts: a list of strings, each of which will be used to create a text
          asset.

    Returns:
        asset_resource_names: a list of asset resource names.
    """
    # Here again we use the GoogleAdService to create multiple text
    # assets in a single request.
    googleads_service: GoogleAdsServiceClient = client.get_service(
        "GoogleAdsService"
    )

    operations: List[MutateOperation] = []
    for text in texts:
        mutate_operation: MutateOperation = client.get_type("MutateOperation")
        asset: Asset = mutate_operation.asset_operation.create
        asset.text_asset.text = text
        operations.append(mutate_operation)

    # Send the operations in a single Mutate request.
    response: MutateGoogleAdsResponse = googleads_service.mutate(
        customer_id=customer_id,
        mutate_operations=operations,
    )
    asset_resource_names: List[str] = []
    for result in response.mutate_operation_responses:
        if result._pb.HasField("asset_result"):
            asset_resource_names.append(result.asset_result.resource_name)
    print_response_details(response)
    return asset_resource_names


def create_asset_group_operation(
    client: GoogleAdsClient,
    customer_id: str,
    headline_asset_resource_names: List[str],
    description_asset_resource_names: List[str],
    brand_guidelines_enabled: bool,
) -> List[MutateOperation]:
    """Creates a list of MutateOperations that create a new asset_group.

    A temporary ID will be assigned to this asset group so that it can
    be referenced by other objects being created in the same Mutate request.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        headline_asset_resource_names: a list of headline resource names.
        description_asset_resource_names: a list of description resource names.
        brand_guidelines_enabled: a boolean value indicating if the campaign is
          enabled for brand guidelines.

    Returns:
        MutateOperations that create a new asset group and related assets.
    """
    asset_group_service: AssetGroupServiceClient = client.get_service(
        "AssetGroupService"
    )
    campaign_service: CampaignServiceClient = client.get_service(
        "CampaignService"
    )

    operations: List[MutateOperation] = []

    # Create the AssetGroup
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    asset_group: AssetGroup = mutate_operation.asset_group_operation.create
    asset_group.name = f"Performance Max asset group #{uuid4()}"
    asset_group.campaign = campaign_service.campaign_path(
        customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
    )
    asset_group.final_urls.append("http://www.example.com")
    asset_group.final_mobile_urls.append("http://www.example.com")
    asset_group.status = client.enums.AssetGroupStatusEnum.PAUSED
    asset_group.resource_name = asset_group_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )
    operations.append(mutate_operation)

    # 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

    # Link the previously created multiple text assets.

    # Link the headline assets.
    for resource_name in headline_asset_resource_names:
        mutate_operation: MutateOperation = client.get_type("MutateOperation")
        asset_group_asset: AssetGroupAsset = (
            mutate_operation.asset_group_asset_operation.create
        )
        asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.HEADLINE
        asset_group_asset.asset_group = asset_group_service.asset_group_path(
            customer_id,
            _ASSET_GROUP_TEMPORARY_ID,
        )
        asset_group_asset.asset = resource_name
        operations.append(mutate_operation)

    #  Link the description assets.
    for resource_name in description_asset_resource_names:
        mutate_operation: MutateOperation = client.get_type("MutateOperation")
        asset_group_asset: AssetGroupAsset = (
            mutate_operation.asset_group_asset_operation.create
        )
        asset_group_asset.field_type = (
            client.enums.AssetFieldTypeEnum.DESCRIPTION
        )
        asset_group_asset.asset_group = asset_group_service.asset_group_path(
            customer_id,
            _ASSET_GROUP_TEMPORARY_ID,
        )
        asset_group_asset.asset = resource_name
        operations.append(mutate_operation)

    # Create and link the long headline text asset.
    mutate_operations: List[MutateOperation] = create_and_link_text_asset(
        client,
        customer_id,
        "Travel the World",
        client.enums.AssetFieldTypeEnum.LONG_HEADLINE,
    )
    operations.extend(mutate_operations)

    # Create and link the business name and logo asset.
    mutate_operations: List[MutateOperation] = create_and_link_brand_assets(
        client,
        customer_id,
        brand_guidelines_enabled,
        "Interplanetary Cruises",
        "https://gaagl.page.link/bjYi",
        "Marketing Logo",
    )
    operations.extend(mutate_operations)

    # Create and link the image assets.

    # Create and link the Marketing Image Asset.
    mutate_operations: List[MutateOperation] = create_and_link_image_asset(
        client,
        customer_id,
        "https://gaagl.page.link/Eit5",
        client.enums.AssetFieldTypeEnum.MARKETING_IMAGE,
        "Marketing Image",
    )
    operations.extend(mutate_operations)

    # Create and link the Square Marketing Image Asset.
    mutate_operations: List[MutateOperation] = create_and_link_image_asset(
        client,
        customer_id,
        "https://gaagl.page.link/bjYi",
        client.enums.AssetFieldTypeEnum.SQUARE_MARKETING_IMAGE,
        "Square Marketing Image",
    )
    operations.extend(mutate_operations)
    return operations


def create_and_link_text_asset(
    client: GoogleAdsClient,
    customer_id: str,
    text: str,
    field_type: AssetFieldTypeEnum.AssetFieldType,
) -> List[MutateOperation]:
    """Creates a list of MutateOperations that create a new linked text asset.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        text: the text of the asset to be created.
        field_type: the field_type of the new asset in the AssetGroupAsset.

    Returns:
        MutateOperations that create a new linked text asset.
    """
    global next_temp_id
    operations: List[MutateOperation] = []
    asset_service: AssetServiceClient = client.get_service("AssetService")
    asset_group_service: AssetGroupServiceClient = client.get_service(
        "AssetGroupService"
    )

    # Create the Text Asset.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    asset: Asset = mutate_operation.asset_operation.create
    asset.resource_name = asset_service.asset_path(customer_id, next_temp_id)
    asset.text_asset.text = text
    operations.append(mutate_operation)

    # Create an AssetGroupAsset to link the Asset to the AssetGroup.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    asset_group_asset: AssetGroupAsset = (
        mutate_operation.asset_group_asset_operation.create
    )
    asset_group_asset.field_type = field_type
    asset_group_asset.asset_group = asset_group_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )
    asset_group_asset.asset = asset_service.asset_path(
        customer_id, next_temp_id
    )
    operations.append(mutate_operation)

    next_temp_id -= 1
    return operations


def create_and_link_image_asset(
    client: GoogleAdsClient,
    customer_id: str,
    url: str,
    field_type: AssetFieldTypeEnum.AssetFieldType,
    asset_name: str,
) -> List[MutateOperation]:
    """Creates a list of MutateOperations that create a new linked image asset.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        url: the url of the image to be retrieved and put into an asset.
        field_type: the field_type of the new asset in the AssetGroupAsset.
        asset_name: the asset name.

    Returns:
        MutateOperations that create a new linked image asset.
    """
    global next_temp_id
    operations: List[MutateOperation] = []
    asset_service: AssetServiceClient = client.get_service("AssetService")
    asset_group_service: AssetGroupServiceClient = client.get_service(
        "AssetGroupService"
    )

    # Create the Image Asset.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    asset: Asset = mutate_operation.asset_operation.create
    asset.resource_name = asset_service.asset_path(customer_id, next_temp_id)
    # Provide a unique friendly name to identify your asset.
    # When there is an existing image asset with the same content but a different
    # name, the new name will be dropped silently.
    asset.name = asset_name
    asset.type_ = client.enums.AssetTypeEnum.IMAGE
    asset.image_asset.data = get_image_bytes_from_url(url)
    operations.append(mutate_operation)

    # Create an AssetGroupAsset to link the Asset to the AssetGroup.
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    asset_group_asset: AssetGroupAsset = (
        mutate_operation.asset_group_asset_operation.create
    )
    asset_group_asset.field_type = field_type
    asset_group_asset.asset_group = asset_group_service.asset_group_path(
        customer_id,
        _ASSET_GROUP_TEMPORARY_ID,
    )
    asset_group_asset.asset = asset_service.asset_path(
        customer_id, next_temp_id
    )
    operations.append(mutate_operation)

    next_temp_id -= 1
    return operations


def create_and_link_brand_assets(
    client: GoogleAdsClient,
    customer_id: str,
    brand_guidelines_enabled: bool,
    business_name: str,
    logo_url: str,
    logo_name: str,
) -> List[MutateOperation]:
    """Creates a list of MutateOperations that create linked brand assets.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        brand_guidelines_enabled: a boolean value indicating if the campaign is
          enabled for brand guidelines.
        business_name: the business name text to be put into an asset.
        logo_url: the url of the logo to be retrieved and put into an asset.
        logo_name: the asset name of the logo.

    Returns:
        MutateOperations that create linked brand assets.
    """
    global next_temp_id
    operations: List[MutateOperation] = []
    asset_service: AssetServiceClient = client.get_service("AssetService")

    # Create the Text Asset.
    text_asset_temp_id = next_temp_id
    next_temp_id -= 1

    text_mutate_operation = client.get_type("MutateOperation")
    text_asset: Asset = text_mutate_operation.asset_operation.create
    text_asset.resource_name = asset_service.asset_path(
        customer_id, text_asset_temp_id
    )
    text_asset.text_asset.text = business_name
    operations.append(text_mutate_operation)

    # Create the Image Asset.
    image_asset_temp_id = next_temp_id
    next_temp_id -= 1

    image_mutate_operation = client.get_type("MutateOperation")
    image_asset: Asset = image_mutate_operation.asset_operation.create
    image_asset.resource_name = asset_service.asset_path(
        customer_id, image_asset_temp_id
    )
    # Provide a unique friendly name to identify your asset.
    # When there is an existing image asset with the same content but a different
    # name, the new name will be dropped silently.
    image_asset.name = logo_name
    image_asset.type_ = client.enums.AssetTypeEnum.IMAGE
    image_asset.image_asset.data = get_image_bytes_from_url(logo_url)
    operations.append(image_mutate_operation)

    if brand_guidelines_enabled:
        # Create CampaignAsset resources to link the Asset resources to the Campaign.
        campaign_service: CampaignServiceClient = client.get_service(
            "CampaignService"
        )

        business_name_mutate_operation: MutateOperation = client.get_type(
            "MutateOperation"
        )
        business_name_campaign_asset: CampaignAsset = (
            business_name_mutate_operation.campaign_asset_operation.create
        )
        business_name_campaign_asset.field_type = (
            client.enums.AssetFieldTypeEnum.BUSINESS_NAME
        )
        business_name_campaign_asset.campaign = campaign_service.campaign_path(
            customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
        )
        business_name_campaign_asset.asset = asset_service.asset_path(
            customer_id, text_asset_temp_id
        )
        operations.append(business_name_mutate_operation)

        logo_mutate_operation: MutateOperation = client.get_type(
            "MutateOperation"
        )
        logo_campaign_asset: CampaignAsset = (
            logo_mutate_operation.campaign_asset_operation.create
        )
        logo_campaign_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO
        logo_campaign_asset.campaign = campaign_service.campaign_path(
            customer_id, _PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
        )
        logo_campaign_asset.asset = asset_service.asset_path(
            customer_id, image_asset_temp_id
        )
        operations.append(logo_mutate_operation)

    else:
        # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.
        asset_group_service: AssetGroupServiceClient = client.get_service(
            "AssetGroupService"
        )

        business_name_mutate_operation: MutateOperation = client.get_type(
            "MutateOperation"
        )
        business_name_asset_group_asset: AssetGroupAsset = (
            business_name_mutate_operation.asset_group_asset_operation.create
        )
        business_name_asset_group_asset.field_type = (
            client.enums.AssetFieldTypeEnum.BUSINESS_NAME
        )
        business_name_asset_group_asset.asset_group = (
            asset_group_service.asset_group_path(
                customer_id,
                _ASSET_GROUP_TEMPORARY_ID,
            )
        )
        business_name_asset_group_asset.asset = asset_service.asset_path(
            customer_id, text_asset_temp_id
        )
        operations.append(business_name_mutate_operation)

        logo_mutate_operation: MutateOperation = client.get_type(
            "MutateOperation"
        )
        logo_asset_group_asset: AssetGroupAsset = (
            logo_mutate_operation.asset_group_asset_operation.create
        )
        logo_asset_group_asset.field_type = client.enums.AssetFieldTypeEnum.LOGO
        logo_asset_group_asset.asset_group = (
            asset_group_service.asset_group_path(
                customer_id,
                _ASSET_GROUP_TEMPORARY_ID,
            )
        )
        logo_asset_group_asset.asset = asset_service.asset_path(
            customer_id, image_asset_temp_id
        )
        operations.append(logo_mutate_operation)

    return operations


def print_response_details(response: MutateGoogleAdsResponse) -> None:
    """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.

    Args:
        response: a MutateGoogleAdsResponse object.
    """
    # Parse the Mutate response to print details about the entities that
    # were created by the request.
    results: Iterable[MutateOperation] = response.mutate_operation_responses
    suffix = "_result"
    for result in results:
        for field_descriptor, value in result._pb.ListFields():
            if field_descriptor.name.endswith(suffix):
                name = field_descriptor.name[: -len(suffix)]
            else:
                name = field_descriptor.name
            print(
                f"Created a(n) {convert_snake_case_to_upper_case(name)} with "
                f"{str(value).strip()}."
            )


def create_asset_group_signal_operations(
    client: GoogleAdsClient, customer_id: str, audience_id: Optional[str]
) -> List[MutateOperation]:
    """Creates a list of MutateOperations that may create asset group signals.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a client customer ID.
        audience_id: an optional audience ID.

    Returns:
        MutateOperations that create new asset group signals.
    """
    googleads_service: GoogleAdsServiceClient = client.get_service(
        "GoogleAdsService"
    )
    asset_group_resource_name: str = googleads_service.asset_group_path(
        customer_id, _ASSET_GROUP_TEMPORARY_ID
    )

    operations: List[MutateOperation] = []

    if audience_id:
        # Create an audience asset group signal.
        # To learn more about Audience Signals, see:
        # https://developers.google.com/google-ads/api/performance-max/asset-group-signals#audiences
        mutate_operation: MutateOperation = client.get_type("MutateOperation")
        operation: AssetGroupSignal = (
            mutate_operation.asset_group_signal_operation.create
        )
        operation.asset_group = asset_group_resource_name
        operation.audience.audience = googleads_service.audience_path(
            customer_id, audience_id
        )
        operations.append(mutate_operation)

    # Create a search theme asset group signal.
    # To learn more about Search Themes Signals, see:
    # https://developers.google.com/google-ads/api/performance-max/asset-group-signals#search_themes
    mutate_operation: MutateOperation = client.get_type("MutateOperation")
    operation: AssetGroupSignal = (
        mutate_operation.asset_group_signal_operation.create
    )
    operation.asset_group = asset_group_resource_name
    operation.search_theme.text = "travel"
    operations.append(mutate_operation)

    return operations



if __name__ == "__main__":
    parser: argparse.ArgumentParser = argparse.ArgumentParser(
        description=("Creates a Performance Max campaign.")
    )
    # The following argument(s) should be provided to run the example.
    parser.add_argument(
        "-c",
        "--customer_id",
        type=str,
        required=True,
        help="The Google Ads customer ID.",
    )
    parser.add_argument(
        "-a",
        "--audience_id",
        type=str,
        help="The ID of an audience.",
    )
    parser.add_argument(
        "-b",
        "--brand_guidelines_enabled",
        type=bool,
        default=False,
        help=(
            "A boolean value indicating if the created campaign is enabled "
            "for brand guidelines."
        ),
    )

    args: argparse.Namespace = parser.parse_args()

    # GoogleAdsClient will read the google-ads.yaml configuration file in the
    # home directory if none is specified.
    googleads_client: GoogleAdsClient = GoogleAdsClient.load_from_storage(
        version="v21"
    )

    try:
        main(
            googleads_client,
            args.customer_id,
            args.audience_id,
            args.brand_guidelines_enabled,
        )
    except GoogleAdsException as ex:
        print(
            f'Request with ID "{ex.request_id}" failed with status '
            f'"{ex.error.code().name}" and includes the following errors:'
        )
        for error in ex.failure.errors:
            print(f'Error with message "{error.message}".')
            if error.location:
                for field_path_element in error.location.field_path_elements:
                    print(f"\t\tOn field: {field_path_element.field_name}")
        sys.exit(1)

      

Ruby

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This 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
#
# This example uses the default customer conversion goals. For an example
# of setting campaign-specific conversion goals, see
# shopping_ads/add_performance_max_retail_campaign.rb

require 'optparse'
require 'date'
require 'open-uri'
require 'google/ads/google_ads'

# 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.
BUDGET_TEMPORARY_ID = "-1"
PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID = "-2"
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.
def next_temp_id
  @id ||= ASSET_GROUP_TEMPORARY_ID.to_i
  @id -= 1
end

def add_performance_max_campaign(
    customer_id,
    audience_id,
    brand_guidelines_enabled)
  # GoogleAdsClient will read a config file from
  # ENV['HOME']/google_ads_config.rb when called without parameters
  client = Google::Ads::GoogleAds::GoogleAdsClient.new

  # 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
  #
  # Create the headlines.
  headline_asset_resource_names = create_multiple_text_assets(
    client,
    customer_id,
    [
      "Travel",
      "Travel Reviews",
      "Book travel",
    ])
  # Create the descriptions.
  description_asset_resource_names = create_multiple_text_assets(
    client,
    customer_id,
    [
      "Take to the air!",
      "Fly to the sky!",
    ])

  # 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
  campaign_budget_operation = create_campaign_budget_operation(
    client,
    customer_id,
  )
  performance_max_campaign_operation = create_performance_max_campaign_operation(
    client,
    customer_id,
    brand_guidelines_enabled,
  )
  campaign_criterion_operations = create_campaign_criterion_operations(
    client,
    customer_id,
  )
  asset_group_operations = create_asset_group_operation(
    client,
    customer_id,
    headline_asset_resource_names,
    description_asset_resource_names,
    brand_guidelines_enabled,
  )
  asset_group_signal_operations = create_asset_group_signal_operations(
    client,
    customer_id,
    audience_id,
  )

  # Send the operations in a single Mutate request.
  response = client.service.google_ads.mutate(
    customer_id: customer_id,
    mutate_operations: [
      # It's important to create these entities in this order because
      # they depend on each other.
      campaign_budget_operation,
      performance_max_campaign_operation,
      # Expand the list of multiple operations into the list of
      # other mutate operations
      campaign_criterion_operations,
      asset_group_operations,
      asset_group_signal_operations,
    ].flatten)

  print_response_details(response)
end

# Creates a MutateOperation that creates a new CampaignBudget.
#
# A temporary ID will be assigned to this campaign budget so that it can be
# referenced by other objects being created in the same Mutate request.
def create_campaign_budget_operation(client, customer_id)
  client.operation.mutate do |m|
    m.campaign_budget_operation = client.operation.create_resource.campaign_budget do |cb|
      cb.name = "Performance Max campaign budget #{SecureRandom.uuid}"
      # The budget period already defaults to DAILY.
      cb.amount_micros = 50_000_000
      cb.delivery_method = :STANDARD
        # A Performance Max campaign cannot use a shared campaign budget.
        cb.explicitly_shared = false

      # Set a temporary ID in the budget's resource name so it can be referenced
      # by the campaign in later steps.
      cb.resource_name = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)
    end
  end
end

# Creates a MutateOperation that creates a new Performance Max campaign.
#
# A temporary ID will be assigned to this campaign so that it can
# be referenced by other objects being created in the same Mutate request.
def create_performance_max_campaign_operation(
    client,
    customer_id,
    brand_guidelines_enabled)
  client.operation.mutate do |m|
    m.campaign_operation = client.operation.create_resource.campaign do |c|
      c.name = "Performance Max campaign #{SecureRandom.uuid}"
      # Set the campaign status as PAUSED. The campaign is the only entity in
      # the mutate request that should have its status set.
      c.status = :PAUSED
      # All Performance Max campaigns have an advertising_channel_type of
      # PERFORMANCE_MAX. The advertising_channel_sub_type should not be set.
      c.advertising_channel_type = :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 target_roas of 3.5 corresponds to a 350% return on ad spend.
      c.bidding_strategy_type = :MAXIMIZE_CONVERSION_VALUE
      c.maximize_conversion_value = client.resource.maximize_conversion_value do |mcv|
        mcv.target_roas = 3.5
      end
      # Set 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.
      c.url_expansion_opt_out = false

      # Set if the campaign is enabled for brand guidelines. For more
      # information on brand guidelines, see
      # https://support.google.com/google-ads/answer/14934472.
      c.brand_guidelines_enabled = brand_guidelines_enabled

      # Assign the resource name with a temporary ID.
      c.resource_name = client.path.campaign(customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      # Set the budget using the given budget resource name.
      c.campaign_budget = client.path.campaign_budget(customer_id, BUDGET_TEMPORARY_ID)

      # Declare whether or not this campaign serves political ads targeting the EU.
      # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and
      # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.
      c.contains_eu_political_advertising = :DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING

      # Optional fields
      c.start_date = DateTime.parse((Date.today + 1).to_s).strftime('%Y%m%d')
      c.end_date = DateTime.parse(Date.today.next_year.to_s).strftime('%Y%m%d')
    end
  end
end

# Creates a list of MutateOperations that create new campaign criteria.
def create_campaign_criterion_operations(client, customer_id)
  operations = []

  # Set the LOCATION campaign criteria.
  # Target 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, add the positive (negative = false) for New York City.
  operations << client.operation.mutate do |m|
    m.campaign_criterion_operation =
      client.operation.create_resource.campaign_criterion do |cc|
      cc.campaign = client.path.campaign(
        customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      cc.location = client.resource.location_info do  |li|
        li.geo_target_constant = client.path.geo_target_constant("1023191")
      end
      cc.negative = false
    end
  end

  # Next add the negative target for Brooklyn.
  operations << client.operation.mutate do |m|
    m.campaign_criterion_operation =
      client.operation.create_resource.campaign_criterion do |cc|
      cc.campaign = client.path.campaign(
        customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      cc.location = client.resource.location_info do  |li|
        li.geo_target_constant = client.path.geo_target_constant("1022762")
      end
      cc.negative = true
    end
  end

  # Set the LANGUAGE campaign criterion.
  operations << client.operation.mutate do |m|
    m.campaign_criterion_operation =
      client.operation.create_resource.campaign_criterion do |cc|
      cc.campaign = client.path.campaign(
        customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      # Set the language.
      # For a list of all language codes, see:
      # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
      cc.language = client.resource.language_info do |li|
        li.language_constant = client.path.language_constant("1000")  # English
      end
    end
  end

  operations
end

# Creates multiple text assets and returns the list of resource names.
def create_multiple_text_assets(client, customer_id, texts)
  operations = texts.map do |text|
    client.operation.mutate do |m|
      m.asset_operation = client.operation.create_resource.asset do |asset|
        asset.text_asset = client.resource.text_asset do |text_asset|
          text_asset.text = text
        end
      end
    end
  end

  # Send the operations in a single Mutate request.
  response = client.service.google_ads.mutate(
    customer_id: customer_id,
    mutate_operations: operations,
  )

  asset_resource_names = []
  response.mutate_operation_responses.each do |result|
    if result.asset_result
      asset_resource_names.append(result.asset_result.resource_name)
    end
  end
  print_response_details(response)
  asset_resource_names
end

# Creates a list of MutateOperations that create a new asset_group.
#
# A temporary ID will be assigned to this asset group so that it can
# be referenced by other objects being created in the same Mutate request.
def create_asset_group_operation(
    client,
    customer_id,
    headline_asset_resource_names,
    description_asset_resource_names,
    brand_guidelines_enabled)
  operations = []

  # Create the AssetGroup
  operations << client.operation.mutate do |m|
    m.asset_group_operation = client.operation.create_resource.asset_group do |ag|
      ag.name = "Performance Max asset group #{SecureRandom.uuid}"
      ag.campaign = client.path.campaign(
        customer_id,
        PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID)
      ag.final_urls << "http://www.example.com"
      ag.final_mobile_urls << "http://www.example.com"
      ag.status = :PAUSED
      ag.resource_name = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID)
    end
  end

  # 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

  # Link the previously created multiple text assets.

  # Link the headline assets.
  headline_asset_resource_names.each do |resource_name|
    operations << client.operation.mutate do |m|
      m.asset_group_asset_operation = client.operation.create_resource
          .asset_group_asset do |aga|
        aga.field_type = :HEADLINE
        aga.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID)
        aga.asset = resource_name
      end
    end
  end

  #  Link the description assets.
  description_asset_resource_names.each do |resource_name|
    operations << client.operation.mutate do |m|
      m.asset_group_asset_operation = client.operation.create_resource
          .asset_group_asset do |aga|
        aga.field_type = :DESCRIPTION
        aga.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID)
        aga.asset = resource_name
      end
    end
  end

  # Create and link the long headline text asset.
  operations += create_and_link_text_asset(
    client,
    customer_id,
    "Travel the World",
    :LONG_HEADLINE)

  # Create and link the business name and logo asset.
  operations += create_and_link_brand_assets(
    client,
    customer_id,
    brand_guidelines_enabled,
    "Interplanetary Cruises",
    "https://gaagl.page.link/bjYi",
    "Marketing Logo")

  # Create and link the image assets.

  # Create and link the Marketing Image Asset.
  operations += create_and_link_image_asset(
    client,
    customer_id,
    "https://gaagl.page.link/Eit5",
    :MARKETING_IMAGE,
    "Marketing Image")

  # Create and link the Square Marketing Image Asset.
  operations += create_and_link_image_asset(
    client,
    customer_id,
    "https://gaagl.page.link/bjYi",
    :SQUARE_MARKETING_IMAGE,
    "Square Marketing Image")

  operations
end

# Creates a list of MutateOperations that create a new linked text asset.
def create_and_link_text_asset(client, customer_id, text, field_type)
  operations = []
  temp_id = next_temp_id

  # Create the Text Asset.
  operations << client.operation.mutate do |m|
    m.asset_operation = client.operation.create_resource.asset do |a|
      a.resource_name = client.path.asset(customer_id, temp_id)
      a.text_asset = client.resource.text_asset do |text_asset|
        text_asset.text = text
      end
    end
  end

  # Create an AssetGroupAsset to link the Asset to the AssetGroup.
  operations << client.operation.mutate do |m|
    m.asset_group_asset_operation = client.operation.create_resource
        .asset_group_asset do |aga|
      aga.field_type = field_type
      aga.asset_group = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID)
      aga.asset = client.path.asset(customer_id, temp_id)
    end
  end

  operations
end

# Creates a list of MutateOperations that create a new linked image asset.
def create_and_link_image_asset(client, customer_id, url, field_type, asset_name)
  operations = []
  temp_id = next_temp_id

  # Create the Image Asset.
  operations << client.operation.mutate do |m|
    m.asset_operation = client.operation.create_resource.asset do |a|
      a.resource_name = client.path.asset(customer_id, temp_id)
      # Provide a unique friendly name to identify your asset.
      # When there is an existing image asset with the same content but a different
      # name, the new name will be dropped silently.
      a.name = asset_name
      a.type = :IMAGE
      a.image_asset = client.resource.image_asset do |image_asset|
        image_asset.data = get_image_bytes(url)
      end
    end
  end

  # Create an AssetGroupAsset to link the Asset to the AssetGroup.
  operations << client.operation.mutate do |m|
    m.asset_group_asset_operation = client.operation.create_resource.
        asset_group_asset do |aga|
      aga.field_type = field_type
      aga.asset_group = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID,
      )
      aga.asset = client.path.asset(customer_id, temp_id)
    end
  end

  operations
end

# Creates a list of MutateOperations that create linked brand assets.
def create_and_link_brand_assets(
    client,
    customer_id,
    brand_guidelines_enabled,
    business_name,
    logo_url,
    logo_name)
  operations = []

  # Create the Text Asset.
  text_asset_temp_id = next_temp_id
  operations << client.operation.mutate do |m|
    m.asset_operation = client.operation.create_resource.asset do |a|
      a.resource_name = client.path.asset(customer_id, text_asset_temp_id)
      a.text_asset = client.resource.text_asset do |text_asset|
        text_asset.text = business_name
      end
    end
  end

  # Create the Image Asset.
  image_asset_temp_id = next_temp_id
  operations << client.operation.mutate do |m|
    m.asset_operation = client.operation.create_resource.asset do |a|
      a.resource_name = client.path.asset(customer_id, image_asset_temp_id)
      # Provide a unique friendly name to identify your asset.
      # When there is an existing image asset with the same content but a different
      # name, the new name will be dropped silently.
      a.name = logo_name
      a.type = :IMAGE
      a.image_asset = client.resource.image_asset do |image_asset|
        image_asset.data = get_image_bytes(logo_url)
      end
    end
  end

  if brand_guidelines_enabled
    # Create CampaignAsset resources to link the Asset resources to the Campaign.
    operations << client.operation.mutate do |m|
      m.campaign_asset_operation = client.operation.create_resource.
          campaign_asset do |ca|
        ca.field_type = :BUSINESS_NAME
        ca.campaign = client.path.campaign(
          customer_id,
          PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,
        )
        ca.asset = client.path.asset(customer_id, text_asset_temp_id)
      end
    end

    operations << client.operation.mutate do |m|
      m.campaign_asset_operation = client.operation.create_resource.
          campaign_asset do |ca|
        ca.field_type = :LOGO
        ca.campaign = client.path.campaign(
          customer_id,
          PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID,
        )
        ca.asset = client.path.asset(customer_id, image_asset_temp_id)
      end
    end
  else
    # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.
    operations << client.operation.mutate do |m|
      m.asset_group_asset_operation = client.operation.create_resource.
          asset_group_asset do |aga|
        aga.field_type = :BUSINESS_NAME
        aga.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID,
        )
        aga.asset = client.path.asset(customer_id, text_asset_temp_id)
      end
    end

    operations << client.operation.mutate do |m|
      m.asset_group_asset_operation = client.operation.create_resource.
          asset_group_asset do |aga|
        aga.field_type = :LOGO
        aga.asset_group = client.path.asset_group(
          customer_id,
          ASSET_GROUP_TEMPORARY_ID,
        )
        aga.asset = client.path.asset(customer_id, image_asset_temp_id)
      end
    end
  end

  operations
end

# Create a list of MutateOperations that create AssetGroupSignals.
def create_asset_group_signal_operations(client, customer_id, audience_id)
  operations = []
  return operations if audience_id.nil?

  operations << client.operation.mutate do |m|
    m.asset_group_signal_operation = client.operation.create_resource.
        asset_group_signal do |ags|
      ags.asset_group = client.path.asset_group(
        customer_id,
        ASSET_GROUP_TEMPORARY_ID,
      )
      ags.audience = client.resource.audience_info do |ai|
        ai.audience = client.path.audience(customer_id, audience_id)
      end
    end
  end

  operations
end

# Loads image data from a URL.
def get_image_bytes(url)
  URI.open(url).read
end

# Prints the details of a MutateGoogleAdsResponse.
def print_response_details(response)
  # Parse the mutate response to print details about the entities that
  # were created by the request.
  suffix = "_result"
  response.mutate_operation_responses.each do |result|
    result.to_h.select {|k, v| v }.each do |name, value|
      if name.to_s.end_with?(suffix)
        name = name.to_s.delete_suffix(suffix)
      end

      puts "Created a(n) #{::Google::Ads::GoogleAds::Utils.camelize(name)} " \
        "with #{value.to_s.strip}."
    end
  end
end

if __FILE__ == $0
  options = {}

  # The following parameter(s) should be provided to run the example. You can
  # either specify these by changing the INSERT_XXX_ID_HERE values below, or on
  # the command line.
  #
  # Parameters passed on the command line will override any parameters set in
  # code.
  #
  # Running the example with -h will print the command line usage.
  options[:customer_id] = 'INSERT_CUSTOMER_ID_HERE'
  options[:audience_id] = nil
  options[:brand_guidelines_enabled] = false

  OptionParser.new do |opts|
    opts.banner = sprintf('Usage: %s [options]', File.basename(__FILE__))

    opts.separator ''
    opts.separator 'Options:'

    opts.on('-C', '--customer-id CUSTOMER-ID', String, 'Customer ID') do |v|
      options[:customer_id] = v
    end

    opts.on('-D', '--audience-id AUDIENCE-ID', String, 'Audience ID (optional)') do |v|
      options[:audience_id] = v
    end

    opts.on('-B', '--brand-guidelines-enabled', 'Enable brand guidelines (optional)') do
      options[:brand_guidelines_enabled] = true
    end

    opts.separator ''
    opts.separator 'Help:'

    opts.on_tail('-h', '--help', 'Show this message') do
      puts opts
      exit
    end
  end.parse!

  begin
    add_performance_max_campaign(
      options.fetch(:customer_id).tr("-", ""),
      options[:audience_id],
      options[:brand_guidelines_enabled]
    )
  rescue Google::Ads::GoogleAds::Errors::GoogleAdsError => e
    e.failure.errors.each do |error|
      STDERR.printf("Error with message: %s\n", error.message)
      if error.location
        error.location.field_path_elements.each do |field_path_element|
          STDERR.printf("\tOn field: %s\n", field_path_element.field_name)
        end
      end
      error.error_code.to_h.each do |k, v|
        next if v == :UNSPECIFIED
        STDERR.printf("\tType: %s\n\tCode: %s\n", k, v)
      end
    end
    raise
  end
end

      

Perl

#!/usr/bin/perl -w
#
# Copyright 2021, Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This 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.
#
# This example uses the default customer conversion goals. For an example of
# setting campaign-specific conversion goals, see
# shopping_ads/add_performance_max_retail_campaign.pl.

use strict;
use warnings;
use utf8;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Google::Ads::GoogleAds::Client;
use Google::Ads::GoogleAds::Utils::GoogleAdsHelper;
use Google::Ads::GoogleAds::Utils::MediaUtils;
use Google::Ads::GoogleAds::V21::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V21::Resources::Campaign;
use Google::Ads::GoogleAds::V21::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V21::Resources::CampaignAsset;
use Google::Ads::GoogleAds::V21::Resources::Asset;
use Google::Ads::GoogleAds::V21::Resources::AssetGroup;
use Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset;
use Google::Ads::GoogleAds::V21::Resources::AssetGroupSignal;
use Google::Ads::GoogleAds::V21::Common::MaximizeConversionValue;
use Google::Ads::GoogleAds::V21::Common::LocationInfo;
use Google::Ads::GoogleAds::V21::Common::LanguageInfo;
use Google::Ads::GoogleAds::V21::Common::TextAsset;
use Google::Ads::GoogleAds::V21::Common::ImageAsset;
use Google::Ads::GoogleAds::V21::Common::AudienceInfo;
use Google::Ads::GoogleAds::V21::Enums::BudgetDeliveryMethodEnum qw(STANDARD);
use Google::Ads::GoogleAds::V21::Enums::CampaignStatusEnum;
use Google::Ads::GoogleAds::V21::Enums::AdvertisingChannelTypeEnum
  qw(PERFORMANCE_MAX);
use Google::Ads::GoogleAds::V21::Enums::AssetGroupStatusEnum;
use Google::Ads::GoogleAds::V21::Enums::AssetFieldTypeEnum
  qw(HEADLINE DESCRIPTION LONG_HEADLINE BUSINESS_NAME LOGO MARKETING_IMAGE SQUARE_MARKETING_IMAGE);
use Google::Ads::GoogleAds::V21::Enums::EuPoliticalAdvertisingStatusEnum
  qw(DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING);
use Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V21::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V21::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V21::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V21::Services::AssetService::AssetOperation;
use
  Google::Ads::GoogleAds::V21::Services::AssetGroupService::AssetGroupOperation;
use
  Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation;
use
  Google::Ads::GoogleAds::V21::Services::AssetGroupSignalService::AssetGroupSignalOperation;
use
  Google::Ads::GoogleAds::V21::Services::CampaignAssetService::CampaignAssetOperation;
use Google::Ads::GoogleAds::V21::Utils::ResourceNames;

use Getopt::Long qw(:config auto_help);
use Pod::Usage;
use Cwd          qw(abs_path);
use Data::Uniqid qw(uniqid);
use POSIX        qw(strftime);

# 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.
use constant BUDGET_TEMPORARY_ID                   => -1;
use constant PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID => -2;
use constant 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.
our $next_temp_id = ASSET_GROUP_TEMPORARY_ID - 1;

sub add_performance_max_campaign {
  my ($api_client, $customer_id, $audience_id, $brand_guidelines_enabled) = @_;

  # 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.
  #
  # Create the headlines.
  my $headline_asset_resource_names =
    create_multiple_text_assets($api_client, $customer_id,
    ["Travel", "Travel Reviews", "Book travel"]);
  # Create the descriptions.
  my $description_asset_resource_names =
    create_multiple_text_assets($api_client, $customer_id,
    ["Take to the air!", "Fly to the sky!"]);

  # It's important to create the below entities in this order because they depend
  # on each other.
  my $operations = [];
  # 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.
  push @$operations, create_campaign_budget_operation($customer_id);
  push @$operations,
    create_performance_max_campaign_operation($customer_id,
    $brand_guidelines_enabled);
  push @$operations, @{create_campaign_criterion_operations($customer_id)};
  push @$operations,
    @{
    create_asset_group_operations(
      $customer_id,                      $headline_asset_resource_names,
      $description_asset_resource_names, $brand_guidelines_enabled
    )};
  push @$operations,
    @{create_asset_group_signal_operations($customer_id, $audience_id)};

  # Issue a mutate request to create everything and print its information.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $operations
  });

  print_response_details($mutate_google_ads_response);

  return 1;
}

# Creates a MutateOperation that creates a new CampaignBudget.
#
# A temporary ID will be assigned to this campaign budget so that it can be
# referenced by other objects being created in the same mutate request.
sub create_campaign_budget_operation {
  my ($customer_id) = @_;

  # Create a mutate operation that creates a campaign budget operation.
  return
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      campaignBudgetOperation =>
        Google::Ads::GoogleAds::V21::Services::CampaignBudgetService::CampaignBudgetOperation
        ->new({
          create => Google::Ads::GoogleAds::V21::Resources::CampaignBudget->new(
            {
              # Set a temporary ID in the budget's resource name so it can be
              # referenced by the campaign in later steps.
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign_budget(
                $customer_id, BUDGET_TEMPORARY_ID
                ),
              name => "Performance Max campaign budget #" . uniqid(),
              # The budget period already defaults to DAILY.
              amountMicros   => 50000000,
              deliveryMethod => STANDARD,
              # A Performance Max campaign cannot use a shared campaign budget.
              explicitlyShared => "false",
            })})});
}

# Creates a MutateOperation that creates a new Performance Max campaign.
#
# A temporary ID will be assigned to this campaign so that it can be referenced
# by other objects being created in the same mutate request.
sub create_performance_max_campaign_operation {
  my ($customer_id, $brand_guidelines_enabled) = @_;

  # Create a mutate operation that creates a campaign operation.
  return
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      campaignOperation =>
        Google::Ads::GoogleAds::V21::Services::CampaignService::CampaignOperation
        ->new({
          create => Google::Ads::GoogleAds::V21::Resources::Campaign->new({
              # Assign the resource name with a temporary ID.
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              name => "Performance Max campaign #" . uniqid(),
              # Set the budget using the given budget resource name.
              campaignBudget =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign_budget(
                $customer_id, BUDGET_TEMPORARY_ID
                ),
              # Set the campaign status as PAUSED. The campaign is the only entity in
              # the mutate request that should have its status set.
              status =>
                Google::Ads::GoogleAds::V21::Enums::CampaignStatusEnum::PAUSED,
              # All Performance Max campaigns have an advertisingChannelType of
              # PERFORMANCE_MAX. The advertisingChannelSubType should not be set.
              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
              # maximizeConversionValue. 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.
              maximizeConversionValue =>
                Google::Ads::GoogleAds::V21::Common::MaximizeConversionValue->
                new({
                  targetRoas => 3.5
                }
                ),

              # Set 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.
              urlExpansionOptOut => "false",

              # Set if the campaign is enabled for brand guidelines. For more information
              # on brand guidelines, see https://support.google.com/google-ads/answer/14934472.
              brandGuidelinesEnabled => $brand_guidelines_enabled,

              # Optional fields.
              startDate => strftime("%Y%m%d", localtime(time + 60 * 60 * 24)),
              endDate   =>
                strftime("%Y%m%d", localtime(time + 60 * 60 * 24 * 365)),

              # Declare whether or not this campaign serves political ads targeting the EU.
              # Valid values are CONTAINS_EU_POLITICAL_ADVERTISING and
              # DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING.
              containsEuPoliticalAdvertising =>
                DOES_NOT_CONTAIN_EU_POLITICAL_ADVERTISING
            })})});
}

# Creates a list of MutateOperations that create new campaign criteria.
sub create_campaign_criterion_operations {
  my ($customer_id) = @_;

  my $operations = [];
  # Set the LOCATION campaign criteria.
  # Target 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, add the positive (negative = false) for New York City.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      campaignCriterionOperation =>
        Google::Ads::GoogleAds::V21::Services::CampaignCriterionService::CampaignCriterionOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V21::Resources::CampaignCriterion->new({
              campaign =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              location =>
                Google::Ads::GoogleAds::V21::Common::LocationInfo->new({
                  geoTargetConstant =>
                    Google::Ads::GoogleAds::V21::Utils::ResourceNames::geo_target_constant(
                    1023191)}
                ),
              negative => "false"
            })})});

  # Next add the negative target for Brooklyn.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      campaignCriterionOperation =>
        Google::Ads::GoogleAds::V21::Services::CampaignCriterionService::CampaignCriterionOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V21::Resources::CampaignCriterion->new({
              campaign =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              location =>
                Google::Ads::GoogleAds::V21::Common::LocationInfo->new({
                  geoTargetConstant =>
                    Google::Ads::GoogleAds::V21::Utils::ResourceNames::geo_target_constant(
                    1022762)}
                ),
              negative => "true"
            })})});

  # Set the LANGUAGE campaign criterion.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      campaignCriterionOperation =>
        Google::Ads::GoogleAds::V21::Services::CampaignCriterionService::CampaignCriterionOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V21::Resources::CampaignCriterion->new({
              campaign =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              # Set the language.
              # For a list of all language codes, see:
              # https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7.
              language =>
                Google::Ads::GoogleAds::V21::Common::LanguageInfo->new({
                  languageConstant =>
                    Google::Ads::GoogleAds::V21::Utils::ResourceNames::language_constant(
                    1000)    # English
                })})})});

  return $operations;
}

# Creates multiple text assets and returns the list of resource names.
sub create_multiple_text_assets {
  my ($api_client, $customer_id, $texts) = @_;

  # Here again we use the GoogleAdService to create multiple text assets in a
  # single request.
  my $operations = [];
  foreach my $text (@$texts) {
    # Create a mutate operation for a text asset.
    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        assetOperation =>
          Google::Ads::GoogleAds::V21::Services::AssetService::AssetOperation->
          new({
            create => Google::Ads::GoogleAds::V21::Resources::Asset->new({
                textAsset =>
                  Google::Ads::GoogleAds::V21::Common::TextAsset->new({
                    text => $text
                  })})})});
  }

  # Issue a mutate request to add all assets.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $operations
  });

  my $asset_resource_names = [];
  foreach
    my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})
  {
    push @$asset_resource_names, $response->{assetResult}{resourceName};
  }
  print_response_details($mutate_google_ads_response);

  return $asset_resource_names;
}

# Creates a list of MutateOperations that create a new asset group.
#
# A temporary ID will be assigned to this asset group so that it can be referenced
# by other objects being created in the same mutate request.
sub create_asset_group_operations {
  my (
    $customer_id,
    $headline_asset_resource_names,
    $description_asset_resource_names,
    $brand_guidelines_enabled
  ) = @_;

  my $operations = [];
  # Create a mutate operation that creates an asset group operation.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetGroupService::AssetGroupOperation
        ->new({
          create => Google::Ads::GoogleAds::V21::Resources::AssetGroup->new({
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              name     => "Performance Max asset group #" . uniqid(),
              campaign =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                ),
              finalUrls       => ["http://www.example.com"],
              finalMobileUrls => ["http://www.example.com"],
              status          =>
                Google::Ads::GoogleAds::V21::Enums::AssetGroupStatusEnum::PAUSED
            })})});

  # 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 fieldType of the Asset in this AssetGroup
  #
  # To learn more about AssetGroups, see
  # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups.

  # Link the previously created multiple text assets.

  # Link the headline assets.
  foreach my $resource_name (@$headline_asset_resource_names) {
    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        assetGroupAssetOperation =>
          Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset->new({
                asset      => $resource_name,
                assetGroup =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                  $customer_id, ASSET_GROUP_TEMPORARY_ID
                  ),
                fieldType => HEADLINE
              })})});
  }

  # Link the description assets.
  foreach my $resource_name (@$description_asset_resource_names) {
    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        assetGroupAssetOperation =>
          Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset->new({
                asset      => $resource_name,
                assetGroup =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                  $customer_id, ASSET_GROUP_TEMPORARY_ID
                  ),
                fieldType => DESCRIPTION
              })})});
  }

  # Create and link the long headline text asset.
  push @$operations,
    @{create_and_link_text_asset($customer_id, "Travel the World",
      LONG_HEADLINE)};

  # Create and link the business name and logo asset.
  push @$operations,
    @{
    create_and_link_brand_assets(
      $customer_id,             $brand_guidelines_enabled,
      "Interplanetary Cruises", "https://gaagl.page.link/bjYi",
      "Marketing Logo"
    )};

  # Create and link the image assets.

  # Create and link the marketing image asset.
  push @$operations,
    @{
    create_and_link_image_asset(
      $customer_id,    "https://gaagl.page.link/Eit5",
      MARKETING_IMAGE, "Marketing Image"
    )};

  # Create and link the square marketing image asset.
  push @$operations,
    @{
    create_and_link_image_asset(
      $customer_id,           "https://gaagl.page.link/bjYi",
      SQUARE_MARKETING_IMAGE, "Square Marketing Image"
    )};

  return $operations;
}

# Creates a list of MutateOperations that create a new linked text asset.
sub create_and_link_text_asset {
  my ($customer_id, $text, $field_type) = @_;

  my $operations = [];
  # Create a new mutate operation for a text asset.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetService::AssetOperation->
        new({
          create => Google::Ads::GoogleAds::V21::Resources::Asset->new({
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
                ),
              textAsset => Google::Ads::GoogleAds::V21::Common::TextAsset->new({
                  text => $text
                })})})});

  # Create an asset group asset to link the asset to the asset group.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupAssetOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset->new({
              asset => Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
              ),
              assetGroup =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              fieldType => $field_type
            })})});

  $next_temp_id--;
  return $operations;
}

# Creates a list of MutateOperations that create a new linked image asset.
sub create_and_link_image_asset {
  my ($customer_id, $url, $field_type, $asset_name) = @_;

  my $operations = [];
  # Create a new mutate operation for an image asset.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetService::AssetOperation->
        new({
          create => Google::Ads::GoogleAds::V21::Resources::Asset->new({
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
                ),
              # Provide a unique friendly name to identify your asset.
              # When there is an existing image asset with the same content but a different
              # name, the new name will be dropped silently.
              name       => $asset_name,
              imageAsset =>
                Google::Ads::GoogleAds::V21::Common::ImageAsset->new({
                  data => get_base64_data_from_url($url)})})})});

  # Create an asset group asset to link the asset to the asset group.
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupAssetOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation
        ->new({
          create =>
            Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset->new({
              asset => Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                $customer_id, $next_temp_id
              ),
              assetGroup =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              fieldType => $field_type
            })})});

  $next_temp_id--;
  return $operations;
}

# Creates a list of MutateOperations that create linked brand assets.
sub create_and_link_brand_assets {
  my ($customer_id, $brand_guidelines_enabled, $business_name, $logo_url,
    $logo_name)
    = @_;

  my $operations = [];

  # Create the text asset.
  my $text_asset_temp_id = $next_temp_id--;
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetService::AssetOperation->
        new({
          create => Google::Ads::GoogleAds::V21::Resources::Asset->new({
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                $customer_id, $text_asset_temp_id
                ),
              textAsset => Google::Ads::GoogleAds::V21::Common::TextAsset->new({
                  text => $business_name
                })})})});

  # Create the image asset.
  my $image_asset_temp_id = $next_temp_id--;
  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetService::AssetOperation->
        new({
          create => Google::Ads::GoogleAds::V21::Resources::Asset->new({
              resourceName =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                $customer_id, $image_asset_temp_id
                ),
              # Provide a unique friendly name to identify your asset.
              # When there is an existing image asset with the same content but a different
              # name, the new name will be dropped silently.
              name       => $logo_name,
              imageAsset =>
                Google::Ads::GoogleAds::V21::Common::ImageAsset->new({
                  data => get_base64_data_from_url($logo_url)})})})});

  if ($brand_guidelines_enabled) {
    # Create CampaignAsset resources to link the Asset resources to the Campaign.
    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        campaignAssetOperation =>
          Google::Ads::GoogleAds::V21::Services::CampaignAssetService::CampaignAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V21::Resources::CampaignAsset->new({
                fieldType => BUSINESS_NAME,
                campaign  =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                  $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                  ),
                asset =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                  $customer_id, $text_asset_temp_id
                  )})})});

    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        campaignAssetOperation =>
          Google::Ads::GoogleAds::V21::Services::CampaignAssetService::CampaignAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V21::Resources::CampaignAsset->new({
                fieldType => LOGO,
                campaign  =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::campaign(
                  $customer_id, PERFORMANCE_MAX_CAMPAIGN_TEMPORARY_ID
                  ),
                asset =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                  $customer_id, $image_asset_temp_id
                  )})})});
  } else {
    # Create AssetGroupAsset resources to link the Asset resources to the AssetGroup.
    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        assetGroupAssetOperation =>
          Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset->new({
                asset =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                  $customer_id, $text_asset_temp_id
                  ),
                assetGroup =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                  $customer_id, ASSET_GROUP_TEMPORARY_ID
                  ),
                fieldType => BUSINESS_NAME
              })})});

    push @$operations,
      Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation
      ->new({
        assetGroupAssetOperation =>
          Google::Ads::GoogleAds::V21::Services::AssetGroupAssetService::AssetGroupAssetOperation
          ->new({
            create =>
              Google::Ads::GoogleAds::V21::Resources::AssetGroupAsset->new({
                asset =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset(
                  $customer_id, $image_asset_temp_id
                  ),
                assetGroup =>
                  Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                  $customer_id, ASSET_GROUP_TEMPORARY_ID
                  ),
                fieldType => LOGO
              })})});
  }

  return $operations;
}

# Creates a list of MutateOperations that create asset group signals.
sub create_asset_group_signal_operations {
  my ($customer_id, $audience_id) = @_;

  my $operations = [];
  return $operations if not defined $audience_id;

  push @$operations,
    Google::Ads::GoogleAds::V21::Services::GoogleAdsService::MutateOperation->
    new({
      assetGroupSignalOperation =>
        Google::Ads::GoogleAds::V21::Services::AssetGroupSignalService::AssetGroupSignalOperation
        ->new({
          # To learn more about Audience Signals, see:
          # https://developers.google.com/google-ads/api/docs/performance-max/asset-groups#audience_signals
          create =>
            Google::Ads::GoogleAds::V21::Resources::AssetGroupSignal->new({
              assetGroup =>
                Google::Ads::GoogleAds::V21::Utils::ResourceNames::asset_group(
                $customer_id, ASSET_GROUP_TEMPORARY_ID
                ),
              audience =>
                Google::Ads::GoogleAds::V21::Common::AudienceInfo->new({
                  audience =>
                    Google::Ads::GoogleAds::V21::Utils::ResourceNames::audience(
                    $customer_id, $audience_id
                    )})})})});
  return $operations;
}

# 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.
sub print_response_details {
  my ($mutate_google_ads_response) = @_;

  foreach
    my $response (@{$mutate_google_ads_response->{mutateOperationResponses}})
  {
    my $result_type = [keys %$response]->[0];

    printf "Created a(n) %s with '%s'.\n",
      ucfirst $result_type =~ s/Result$//r,
      $response->{$result_type}{resourceName};
  }
}

# Don't run the example if the file is being included.
if (abs_path($0) ne abs_path(__FILE__)) {
  return 1;
}

# Get Google Ads Client, credentials will be read from ~/googleads.properties.
my $api_client = Google::Ads::GoogleAds::Client->new();

# By default examples are set to die on any server returned fault.
$api_client->set_die_on_faults(1);

my $customer_id              = undef;
my $audience_id              = undef;
my $brand_guidelines_enabled = "false";

# Parameters passed on the command line will override any parameters set in code.
GetOptions(
  "customer_id=s"              => \$customer_id,
  "audience_id=i"              => \$audience_id,
  "brand_guidelines_enabled=s" => \$brand_guidelines_enabled
);

# Print the help message if the parameters are not initialized in the code nor
# in the command line.
pod2usage(2) if not check_params($customer_id);

# Call the example.
add_performance_max_campaign(
  $api_client,  $customer_id =~ s/-//gr,
  $audience_id, $brand_guidelines_enabled
);

=pod

=head1 NAME

add_performance_max_campaign

=head1 DESCRIPTION

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.

This example uses the default customer conversion goals. For an example of
setting campaign-specific conversion goals, see
shopping_ads/add_performance_max_retail_campaign.pl.

=head1 SYNOPSIS

add_performance_max_campaign.pl [options]

    -help                       Show the help message.
    -customer_id                The Google Ads customer ID.
    -audience_id                [optional] An audience ID to use to improve the
                                targeting of the Performance Max campaign.
    -brand_guidelines_enabled	[optional] A boolean value indicating if the campaign is enabled for brand guidelines. Defaults to false.

=cut