Add Complete Campaigns Using Batch Job

Java

// Copyright 2020 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.campaignmanagement;

import static com.google.ads.googleads.examples.utils.CodeSampleHelper.getPrintableDateTime;

import com.beust.jcommander.Parameter;
import com.google.ads.googleads.examples.utils.ArgumentNames;
import com.google.ads.googleads.examples.utils.CodeSampleParams;
import com.google.ads.googleads.lib.GoogleAdsClient;
import com.google.ads.googleads.v17.common.ExpandedTextAdInfo;
import com.google.ads.googleads.v17.common.KeywordInfo;
import com.google.ads.googleads.v17.common.ManualCpc;
import com.google.ads.googleads.v17.enums.AdGroupAdStatusEnum.AdGroupAdStatus;
import com.google.ads.googleads.v17.enums.AdGroupCriterionStatusEnum.AdGroupCriterionStatus;
import com.google.ads.googleads.v17.enums.AdGroupTypeEnum.AdGroupType;
import com.google.ads.googleads.v17.enums.AdvertisingChannelTypeEnum.AdvertisingChannelType;
import com.google.ads.googleads.v17.enums.BudgetDeliveryMethodEnum.BudgetDeliveryMethod;
import com.google.ads.googleads.v17.enums.CampaignStatusEnum.CampaignStatus;
import com.google.ads.googleads.v17.enums.KeywordMatchTypeEnum.KeywordMatchType;
import com.google.ads.googleads.v17.errors.GoogleAdsError;
import com.google.ads.googleads.v17.errors.GoogleAdsException;
import com.google.ads.googleads.v17.resources.Ad;
import com.google.ads.googleads.v17.resources.AdGroup;
import com.google.ads.googleads.v17.resources.AdGroupAd;
import com.google.ads.googleads.v17.resources.AdGroupCriterion;
import com.google.ads.googleads.v17.resources.BatchJob;
import com.google.ads.googleads.v17.resources.Campaign;
import com.google.ads.googleads.v17.resources.CampaignBudget;
import com.google.ads.googleads.v17.resources.CampaignCriterion;
import com.google.ads.googleads.v17.services.AdGroupAdOperation;
import com.google.ads.googleads.v17.services.AdGroupCriterionOperation;
import com.google.ads.googleads.v17.services.AdGroupOperation;
import com.google.ads.googleads.v17.services.AddBatchJobOperationsRequest;
import com.google.ads.googleads.v17.services.AddBatchJobOperationsResponse;
import com.google.ads.googleads.v17.services.BatchJobOperation;
import com.google.ads.googleads.v17.services.BatchJobResult;
import com.google.ads.googleads.v17.services.BatchJobServiceClient;
import com.google.ads.googleads.v17.services.BatchJobServiceClient.ListBatchJobResultsPagedResponse;
import com.google.ads.googleads.v17.services.CampaignBudgetOperation;
import com.google.ads.googleads.v17.services.CampaignCriterionOperation;
import com.google.ads.googleads.v17.services.CampaignOperation;
import com.google.ads.googleads.v17.services.ListBatchJobResultsRequest;
import com.google.ads.googleads.v17.services.MutateOperation;
import com.google.ads.googleads.v17.services.MutateOperationResponse.ResponseCase;
import com.google.ads.googleads.v17.utils.ResourceNames;
import com.google.api.gax.longrunning.OperationFuture;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * This example adds complete campaigns including campaign budgets, campaigns, ad groups and
 * keywords using BatchJobService.
 */
public class AddCompleteCampaignsUsingBatchJob {
  private static final int NUMBER_OF_CAMPAIGNS_TO_ADD = 2;

  private static final int NUMBER_OF_AD_GROUPS_TO_ADD = 2;

  private static final int NUMBER_OF_KEYWORDS_TO_ADD = 4;

  private static final int MAX_TOTAL_POLL_INTERVAL_SECONDS = 60;

  private static final int PAGE_SIZE = 1000;

  /** The negative temporary ID used in mutate job operations. */
  private static long temporaryId = -1;

  private static class AddCompleteCampaignsUsingBatchJobParams extends CodeSampleParams {

    @Parameter(names = ArgumentNames.CUSTOMER_ID, required = true)
    private Long customerId;
  }

  public static void main(String[] args) throws IOException {
    AddCompleteCampaignsUsingBatchJobParams params = new AddCompleteCampaignsUsingBatchJobParams();
    if (!params.parseArguments(args)) {

      // Either pass the required parameters for this example on the command line, or insert them
      // into the code here. See the parameter class definition above for descriptions.
      params.customerId = Long.parseLong("INSERT_CUSTOMER_ID_HERE");
    }

    GoogleAdsClient googleAdsClient = null;
    try {
      googleAdsClient = GoogleAdsClient.newBuilder().fromPropertiesFile().build();
    } catch (FileNotFoundException fnfe) {
      System.err.printf(
          "Failed to load GoogleAdsClient configuration from file. Exception: %s%n", fnfe);
      System.exit(1);
    } catch (IOException ioe) {
      System.err.printf("Failed to create GoogleAdsClient. Exception: %s%n", ioe);
      System.exit(1);
    }

    try {
      new AddCompleteCampaignsUsingBatchJob().runExample(googleAdsClient, params.customerId);
    } catch (GoogleAdsException gae) {
      // GoogleAdsException is the base class for most exceptions thrown by an API request.
      // Instances of this exception have a message and a GoogleAdsFailure that contains a
      // collection of GoogleAdsErrors that indicate the underlying causes of the
      // GoogleAdsException.
      System.err.printf(
          "Request ID %s failed due to GoogleAdsException. Underlying errors:%n",
          gae.getRequestId());
      int i = 0;
      for (GoogleAdsError googleAdsError : gae.getGoogleAdsFailure().getErrorsList()) {
        System.err.printf("  Error %d: %s%n", i++, googleAdsError);
      }
    }
  }

  /**
   * Runs the example.
   *
   * @param googleAdsClient the Google Ads API client.
   * @param customerId the client customer ID.
   * @throws GoogleAdsException if an API request failed with one or more service errors.
   */
  private void runExample(GoogleAdsClient googleAdsClient, long customerId) {
    try (BatchJobServiceClient batchJobServiceClient =
        googleAdsClient.getLatestVersion().createBatchJobServiceClient()) {
      String batchJobResourceName = createBatchJob(batchJobServiceClient, customerId);
      addAllBatchJobOperations(batchJobServiceClient, customerId, batchJobResourceName);
      OperationFuture operationResponse = runBatchJob(batchJobServiceClient, batchJobResourceName);
      pollBatchJob(operationResponse);
      fetchAndPrintResults(batchJobServiceClient, batchJobResourceName);
    }
  }

  /**
   * Creates a new mutate job for the specified customer ID.
   *
   * @param batchJobServiceClient the mutate job service client.
   * @param customerId the client customer ID.
   * @return the resource name of the created mutate job.
   */
  private String createBatchJob(BatchJobServiceClient batchJobServiceClient, long customerId) {
    BatchJobOperation operation =
        BatchJobOperation.newBuilder().setCreate(BatchJob.newBuilder().build()).build();
    String batchJobResourceName =
        batchJobServiceClient
            .mutateBatchJob(Long.toString(customerId), operation)
            .getResult()
            .getResourceName();
    System.out.printf("Created a mutate job with resource name: '%s'.%n", batchJobResourceName);

    return batchJobResourceName;
  }

  /**
   * Adds all mutate job operations to the mutate job. As this is the first time for this mutate
   * job, the sequence token is not set. The response will contain the next sequence token that you
   * can use to upload more operations in the future.
   *
   * @param batchJobServiceClient the mutate job service client.
   * @param customerId the client customer ID.
   * @param batchJobResourceName the resource name of mutate job to which the mutate job operations
   *     will be added.
   */
  private void addAllBatchJobOperations(
      BatchJobServiceClient batchJobServiceClient, long customerId, String batchJobResourceName) {
    AddBatchJobOperationsResponse response =
        batchJobServiceClient.addBatchJobOperations(
            AddBatchJobOperationsRequest.newBuilder()
                .setResourceName(batchJobResourceName)
                .addAllMutateOperations(buildAllOperations(customerId))
                .build());
    System.out.printf(
        "%d mutate operations have been added so far.%n", response.getTotalOperations());

    // You can use this next sequence token for calling addBatchJobOperations() next time.
    System.out.printf(
        "Next sequence token for adding next operations is '%s'.%n",
        response.getNextSequenceToken());
  }

  /**
   * Requests the API to run the mutate job for executing all uploaded mutate job operations.
   *
   * @param batchJobServiceClient the mutate job service client.
   * @param batchJobResourceName the resource name of mutate job to be run.
   * @return the operation response from running mutate job.
   */
  private OperationFuture runBatchJob(
      BatchJobServiceClient batchJobServiceClient, String batchJobResourceName) {
    OperationFuture operationResponse =
        batchJobServiceClient.runBatchJobAsync(batchJobResourceName);

    // BEWARE! The above call returns an OperationFuture. The execution of that future depends on
    // the thread pool which is owned by batchJobServiceClient. If you use this future, you *must*
    // keep the service client in scope too.
    // See https://developers.google.com/google-ads/api/docs/client-libs/java/lro for more detail.

    System.out.printf(
        "Mutate job with resource name '%s' has been executed.%n", batchJobResourceName);

    return operationResponse;
  }

  /**
   * Polls the server until the mutate job execution finishes by setting the total time to wait
   * before time-out.
   *
   * @param operationResponse the operation response used to poll the server.
   */
  private void pollBatchJob(OperationFuture operationResponse) {
    try {
      operationResponse.get(MAX_TOTAL_POLL_INTERVAL_SECONDS, TimeUnit.SECONDS);
    } catch (InterruptedException | ExecutionException | TimeoutException e) {
      System.err.printf("Failed polling the mutate job. Exception: %s%n", e);
      System.exit(1);
    }
  }

  /**
   * Prints all the results from running the mutate job.
   *
   * @param batchJobServiceClient the mutate job service client.
   * @param batchJobResourceName the resource name of mutate job to get its results.
   */
  private void fetchAndPrintResults(
      BatchJobServiceClient batchJobServiceClient, String batchJobResourceName) {
    System.out.printf(
        "Mutate job with resource name '%s' has finished. Now, printing its results...%n",
        batchJobResourceName);
    // Gets all the results from running mutate job and prints their information.
    ListBatchJobResultsPagedResponse batchJobResults =
        batchJobServiceClient.listBatchJobResults(
            ListBatchJobResultsRequest.newBuilder()
                .setResourceName(batchJobResourceName)
                .setPageSize(PAGE_SIZE)
                .build());
    for (BatchJobResult batchJobResult : batchJobResults.iterateAll()) {
      System.out.printf(
          "Mutate job #%d has a status '%s' and response of type '%s'.%n",
          batchJobResult.getOperationIndex(),
          batchJobResult.getStatus().getMessage().isEmpty()
              ? "N/A"
              : batchJobResult.getStatus().getMessage(),
          batchJobResult
                  .getMutateOperationResponse()
                  .getResponseCase()
                  .equals(ResponseCase.RESPONSE_NOT_SET)
              ? "N/A"
              : batchJobResult.getMutateOperationResponse().getResponseCase());
    }
  }

  /**
   * Builds all operations for creating a complete campaign and return an array of their
   * corresponding mutate operations.
   *
   * @param customerId the client customer ID.
   * @return the mutate operations to be added to a mutate job.
   */
  private List<MutateOperation> buildAllOperations(long customerId) {
    List<MutateOperation> mutateOperations = new ArrayList<>();

    // Creates a new campaign budget operation and adds it to the array of mutate operations.
    CampaignBudgetOperation campaignBudgetOperation = buildCampaignBudgetOperation(customerId);
    mutateOperations.add(
        MutateOperation.newBuilder().setCampaignBudgetOperation(campaignBudgetOperation).build());

    // Creates new campaign operations and adds them to the array of mutate operations.
    List<CampaignOperation> campaignOperations =
        buildCampaignOperations(customerId, campaignBudgetOperation.getCreate().getResourceName());
    for (CampaignOperation campaignOperation : campaignOperations) {
      mutateOperations.add(
          MutateOperation.newBuilder().setCampaignOperation(campaignOperation).build());
    }

    // Creates new campaign criterion operations and adds them to the array of mutate operations.
    List<CampaignCriterionOperation> campaignCriterionOperations =
        buildCampaignCriterionOperations(campaignOperations);
    for (CampaignCriterionOperation campaignCriterionOperation : campaignCriterionOperations) {
      mutateOperations.add(
          MutateOperation.newBuilder()
              .setCampaignCriterionOperation(campaignCriterionOperation)
              .build());
    }

    // Creates new ad group operations and adds them to the array of mutate operations.
    List<AdGroupOperation> adGroupOperations =
        buildAdGroupOperations(customerId, campaignOperations);
    for (AdGroupOperation adGroupOperation : adGroupOperations) {
      mutateOperations.add(
          MutateOperation.newBuilder().setAdGroupOperation(adGroupOperation).build());
    }

    // Creates new ad group criterion operations and adds them to the array of mutate operations.
    List<AdGroupCriterionOperation> adGroupCriterionOperations =
        buildAdGroupCriterionOperations(adGroupOperations);
    for (AdGroupCriterionOperation adGroupCriterionOperation : adGroupCriterionOperations) {
      mutateOperations.add(
          MutateOperation.newBuilder()
              .setAdGroupCriterionOperation(adGroupCriterionOperation)
              .build());
    }

    // Creates new ad group ad operations and adds them to the array of mutate operations.
    List<AdGroupAdOperation> adGroupAdOperations = buildAdGroupAdOperations(adGroupOperations);
    for (AdGroupAdOperation adGroupAdOperation : adGroupAdOperations) {
      mutateOperations.add(
          MutateOperation.newBuilder().setAdGroupAdOperation(adGroupAdOperation).build());
    }

    return mutateOperations;
  }

  /**
   * Builds a new campaign budget operation for the specified customer ID.
   *
   * @param customerId the client customer ID.
   * @return the campaign budget operation.
   */
  private CampaignBudgetOperation buildCampaignBudgetOperation(long customerId) {
    // Creates a campaign budget.
    CampaignBudget budget =
        CampaignBudget.newBuilder()
            // Creates a resource name using the temporary ID.
            .setResourceName(ResourceNames.campaignBudget(customerId, getNextTemporaryId()))
            .setName("Interplanetary Cruise Budget #" + getPrintableDateTime())
            .setDeliveryMethod(BudgetDeliveryMethod.STANDARD)
            .setAmountMicros(5_000_000)
            .build();

    // Creates a campaign budget operation.
    return CampaignBudgetOperation.newBuilder().setCreate(budget).build();
  }

  /**
   * Builds new campaign operations for the specified customer ID.
   *
   * @param customerId the client customer ID.
   * @param campaignBudgetResourceName the resource name of campaign budget to be used to create
   *     campaigns.
   * @return the campaign operations.
   */
  private List<CampaignOperation> buildCampaignOperations(
      long customerId, String campaignBudgetResourceName) {
    List<CampaignOperation> operations = new ArrayList<>();

    for (int i = 0; i < NUMBER_OF_CAMPAIGNS_TO_ADD; i++) {
      // Creates a campaign.
      long campaignId = getNextTemporaryId();
      Campaign campaign =
          Campaign.newBuilder()
              // Creates a resource name using the temporary ID.
              .setResourceName(ResourceNames.campaign(customerId, campaignId))
              .setName("Mutate job campaign #" + getPrintableDateTime() + "." + campaignId)
              .setAdvertisingChannelType(AdvertisingChannelType.SEARCH)
              // Recommendation: Set the campaign to PAUSED when creating it to prevent
              // the ads from immediately serving. Set to ENABLED once you've added
              // targeting and the ads are ready to serve.
              .setStatus(CampaignStatus.PAUSED)
              // Sets the bidding strategy and budget.
              .setManualCpc(ManualCpc.newBuilder().build())
              .setCampaignBudget(campaignBudgetResourceName)
              .build();

      // Creates a campaign operation and adds it to the operations list.
      CampaignOperation op = CampaignOperation.newBuilder().setCreate(campaign).build();
      operations.add(op);
    }

    return operations;
  }

  /**
   * Builds new campaign criterion operations for creating negative campaign criteria (as keywords).
   *
   * @param campaignOperations the campaign operations to be used to create campaign criteria.
   * @return the campaign criterion operations.
   */
  private List<CampaignCriterionOperation> buildCampaignCriterionOperations(
      List<CampaignOperation> campaignOperations) {
    List<CampaignCriterionOperation> operations = new ArrayList<>();

    for (CampaignOperation campaignOperation : campaignOperations) {
      // Creates a campaign criterion.
      CampaignCriterion campaignCriterion =
          CampaignCriterion.newBuilder()
              .setKeyword(
                  KeywordInfo.newBuilder()
                      .setText("venus")
                      .setMatchType(KeywordMatchType.BROAD)
                      .build())
              // Sets the campaign criterion as a negative criterion.
              .setNegative(Boolean.TRUE)
              .setCampaign(campaignOperation.getCreate().getResourceName())
              .build();

      // Creates a campaign criterion operation and adds it to the operations list.
      CampaignCriterionOperation op =
          CampaignCriterionOperation.newBuilder().setCreate(campaignCriterion).build();
      operations.add(op);
    }

    return operations;
  }

  /**
   * Builds new ad group operations for the specified customer ID.
   *
   * @param customerId the client customer ID.
   * @param campaignOperations the campaign operations to be used to create ad groups.
   * @return the ad group operations.
   */
  private List<AdGroupOperation> buildAdGroupOperations(
      long customerId, List<CampaignOperation> campaignOperations) {
    List<AdGroupOperation> operations = new ArrayList<>();

    for (CampaignOperation campaignOperation : campaignOperations) {
      for (int i = 0; i < NUMBER_OF_AD_GROUPS_TO_ADD; i++) {
        // Creates an ad group.
        long adGroupId = getNextTemporaryId();
        AdGroup adGroup =
            AdGroup.newBuilder()
                // Creates a resource name using the temporary ID.
                .setResourceName(ResourceNames.adGroup(customerId, adGroupId))
                .setName("Mutate job ad group #" + getPrintableDateTime() + "." + adGroupId)
                .setCampaign(campaignOperation.getCreate().getResourceName())
                .setType(AdGroupType.SEARCH_STANDARD)
                .setCpcBidMicros(10_000_000)
                .build();

        // Creates an ad group operation and adds it to the operations list.
        AdGroupOperation op = AdGroupOperation.newBuilder().setCreate(adGroup).build();
        operations.add(op);
      }
    }

    return operations;
  }

  /**
   * Builds new ad group criterion operations for creating keywords. 50% of keywords are created
   * with some invalid characters to demonstrate how BatchJobService returns information about such
   * errors.
   *
   * @param adGroupOperations the ad group operations to be used to create ad group criteria.
   * @return the ad group criterion operations.
   */
  private List<AdGroupCriterionOperation> buildAdGroupCriterionOperations(
      List<AdGroupOperation> adGroupOperations) {
    List<AdGroupCriterionOperation> operations = new ArrayList<>();

    for (AdGroupOperation adGroupOperation : adGroupOperations) {
      for (int i = 0; i < NUMBER_OF_KEYWORDS_TO_ADD; i++) {
        // Creates a keyword text by making 50% of keywords invalid to demonstrate error handling.
        String keywordText = "mars" + i;
        if (i % 2 == 0) {
          keywordText += "!!!";
        }
        // Creates an ad group criterion using the created keyword text.
        AdGroupCriterion adGroupCriterion =
            AdGroupCriterion.newBuilder()
                .setKeyword(
                    KeywordInfo.newBuilder()
                        .setText(keywordText)
                        .setMatchType(KeywordMatchType.BROAD)
                        .build())
                .setAdGroup(adGroupOperation.getCreate().getResourceName())
                .setStatus(AdGroupCriterionStatus.ENABLED)
                .build();

        // Creates an ad group criterion operation and adds it to the operations list.
        AdGroupCriterionOperation op =
            AdGroupCriterionOperation.newBuilder().setCreate(adGroupCriterion).build();
        operations.add(op);
      }
    }

    return operations;
  }

  /**
   * Builds new ad group ad operations.
   *
   * @param adGroupOperations the ad group operations to be used to create ad group ads.
   * @return the ad group ad operations.
   */
  private List<AdGroupAdOperation> buildAdGroupAdOperations(
      List<AdGroupOperation> adGroupOperations) {
    List<AdGroupAdOperation> operations = new ArrayList<>();

    for (AdGroupOperation adGroupOperation : adGroupOperations) {
      // Creates an ad group ad.
      AdGroupAd adGroupAd =
          AdGroupAd.newBuilder()
              // Creates the expanded text ad info.
              .setAd(
                  Ad.newBuilder()
                      // Sets the expanded text ad info on an ad.
                      .setExpandedTextAd(
                          ExpandedTextAdInfo.newBuilder()
                              .setHeadlinePart1("Cruise to Mars #" + getPrintableDateTime())
                              .setHeadlinePart2("Best Space Cruise Line")
                              .setDescription("Buy your tickets now!")
                              .build())
                      .addFinalUrls("http://www.example.com")
                      .build())
              .setAdGroup(adGroupOperation.getCreate().getResourceName())
              .setStatus(AdGroupAdStatus.PAUSED)
              .build();

      // Creates an ad group ad operation and adds it to the operations list.
      AdGroupAdOperation op = AdGroupAdOperation.newBuilder().setCreate(adGroupAd).build();
      operations.add(op);
    }

    return operations;
  }

  /**
   * Returns the next temporary ID and decreases it by one.
   *
   * @return the next temporary ID.
   */
  private long getNextTemporaryId() {
    return temporaryId--;
  }
}

      

C#

// Copyright 2020 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.GoogleAds.Lib;
using Google.Ads.GoogleAds.V17.Common;
using Google.Ads.GoogleAds.V17.Errors;
using Google.Ads.GoogleAds.V17.Resources;
using Google.Ads.GoogleAds.V17.Services;
using Google.Api.Gax;
using Google.LongRunning;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Collections.Generic;
using static Google.Ads.GoogleAds.V17.Enums.AdGroupAdStatusEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AdGroupCriterionStatusEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AdGroupTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.AdvertisingChannelTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.BudgetDeliveryMethodEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.CampaignStatusEnum.Types;
using static Google.Ads.GoogleAds.V17.Enums.KeywordMatchTypeEnum.Types;
using static Google.Ads.GoogleAds.V17.Resources.BatchJob.Types;

namespace Google.Ads.GoogleAds.Examples.V17
{
    /// <summary>
    /// This code example adds complete campaigns including campaign budgets, campaigns, ad groups
    /// and keywords using BatchJobService.
    /// </summary>
    public class AddCompleteCampaignsUsingBatchJob : ExampleBase
    {
        /// <summary>
        /// Command line options for running the <see cref="AddCompleteCampaignsUsingBatchJob"/>
        /// example.
        /// </summary>
        public class Options : OptionsBase
        {
            /// <summary>
            /// The Google Ads customer ID for which the call is made.
            /// </summary>
            [Option("customerId", Required = true, HelpText =
                "The Google Ads customer ID for which the call is made.")]
            public long CustomerId { 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);

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

        /// <summary>
        /// The number of campaigns to add.
        /// </summary>
        private const int NUMBER_OF_CAMPAIGNS_TO_ADD = 2;

        /// <summary>
        /// The number of ad groups per campaign to add.
        /// </summary>
        private const int NUMBER_OF_AD_GROUPS_TO_ADD = 2;

        /// <summary>
        /// The number of keywords per ad group to add.
        /// </summary>
        private const int NUMBER_OF_KEYWORDS_TO_ADD = 4;

        /// <summary>
        /// The maximum total poll interval in seconds.
        /// </summary>
        private const int MAX_TOTAL_POLL_INTERVAL_SECONDS = 60;

        /// <summary>
        /// The page size for retrieving results.
        /// </summary>
        private const int PAGE_SIZE = 1000;

        /// <summary>
        /// The negative temporary ID used in batch job operations.
        /// </summary>
        private static long temporaryId = -1;

        /// <summary>
        /// Returns a description about the code example.
        /// </summary>
        public override string Description =>
            "This code example adds complete campaigns including campaign budgets, campaigns, " +
            "ad groups and keywords using BatchJobService.";

        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="client">The Google Ads client.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        public void Run(GoogleAdsClient client, long customerId)
        {
            // Gets the BatchJobService.
            BatchJobServiceClient batchJobService =
                client.GetService(Services.V17.BatchJobService);

            try
            {
                string batchJobResourceName = CreateBatchJob(batchJobService, customerId);
                AddAllBatchJobOperations(batchJobService, customerId, batchJobResourceName);
                Operation<Empty, BatchJobMetadata> operationResponse =
                    RunBatchJob(batchJobService, batchJobResourceName);
                PollBatchJob(operationResponse);
                FetchAndPrintResults(batchJobService, batchJobResourceName);
            }
            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 the batch job.
        /// </summary>
        /// <param name="batchJobService">The batch job service.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <returns>The resource name of the created batch job.</returns>
        private static string CreateBatchJob(BatchJobServiceClient batchJobService,
            long customerId)
        {
            BatchJobOperation operation = new BatchJobOperation()
            {
                Create = new BatchJob()
                {
                }
            };
            string batchJobResourceName =
                batchJobService.MutateBatchJob(customerId.ToString(), operation)
                .Result.ResourceName;
            Console.WriteLine($"Created a batch job with resource name: " +
                $"'{batchJobResourceName}'.");

            return batchJobResourceName;
        }

        /// <summary>
        /// Adds all batch job operations to the batch job. As this is the first time for this
        /// batch job, the sequence token is not set. The response will contain the next sequence
        /// token that you can use to upload more operations in the future.
        /// </summary>
        /// <param name="batchJobService">The batch job service.</param>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="batchJobResourceName">The resource name of batch job to which the batch
        /// job operations will be added.
        /// </param>
        private static void AddAllBatchJobOperations(BatchJobServiceClient batchJobService,
            long customerId, string batchJobResourceName)
        {
            AddBatchJobOperationsResponse response =
                batchJobService.AddBatchJobOperations(
                    new AddBatchJobOperationsRequest()
                    {
                        ResourceName = batchJobResourceName,
                        MutateOperations = { BuildAllOperations(customerId) }
                    });
            Console.WriteLine($"{response.TotalOperations} mutate operations have been added" +
                $" so far.");

            // You can use this next sequence token for calling AddBatchJobOperations() next time.
            Console.WriteLine($"Next sequence token for adding next operations is " +
                $"'{response.NextSequenceToken}'.");
        }

        /// <summary>
        /// Requests the API to run the batch job for executing all uploaded batch job
        /// operations.
        /// </summary>
        /// <param name="batchJobService">The batch job service client.</param>
        /// <param name="batchJobResourceName">The resource name of batch job to be run.</param>
        /// <returns>The operation response from running batch job.</returns>
        private Operation<Empty, BatchJobMetadata> RunBatchJob(
            BatchJobServiceClient batchJobService, string batchJobResourceName)
        {
            Operation<Empty, BatchJobMetadata> operationResponse =
                batchJobService.RunBatchJob(batchJobResourceName);
            Console.WriteLine($"Batch job with resource name '{batchJobResourceName}' has been " +
                $"executed.");

            return operationResponse;
        }

        /// <summary>
        /// Polls the server until the batch job execution finishes by setting the total
        /// time to wait before time-out.
        /// </summary>
        /// <param name="operationResponse">The operation response used to poll the server.</param>
        private static void PollBatchJob(Operation<Empty, BatchJobMetadata> operationResponse)
        {
            PollSettings pollSettings = new PollSettings(
                Expiration.FromTimeout(TimeSpan.FromSeconds(MAX_TOTAL_POLL_INTERVAL_SECONDS)),
                TimeSpan.FromSeconds(1));
            operationResponse.PollUntilCompleted(pollSettings);
        }

        /// <summary>
        /// Fetches and prints all the results from running the batch job.
        /// </summary>
        /// <param name="batchJobService">The batch job service.</param>
        /// <param name="batchJobResourceName">The resource name of batch job to get its results.
        /// </param>
        private static void FetchAndPrintResults(BatchJobServiceClient batchJobService,
            string batchJobResourceName)
        {
            Console.WriteLine($"batch job with resource name '{batchJobResourceName}' has " +
                $"finished. Now, printing its results...");

            ListBatchJobResultsRequest request = new ListBatchJobResultsRequest()
            {
                ResourceName = batchJobResourceName,
                PageSize = PAGE_SIZE,
            };
            ListBatchJobResultsResponse resp = new ListBatchJobResultsResponse();
            // Gets all the results from running batch job and prints their information.
            foreach (BatchJobResult batchJobResult in
                batchJobService.ListBatchJobResults(request))
            {
                if (!batchJobResult.IsFailed)
                {
                    Console.WriteLine($"batch job result #{batchJobResult.OperationIndex} is " +
                        $"successful and response is of type " +
                        $"'{batchJobResult.MutateOperationResponse.ResponseCase}'.");
                }
                else
                {
                    Console.WriteLine($"batch job result #{batchJobResult.OperationIndex} " +
                        $"failed with error message {batchJobResult.Status.Message}.");

                    foreach (GoogleAdsError error in batchJobResult.Failure.Errors)
                    {
                        Console.WriteLine($"Error found: {error}.");
                    }
                }
            }
        }

        /// <summary>
        /// Builds all operations for creating a complete campaign and return an array of
        /// their corresponding mutate operations.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <returns>The mutate operations to be added to a batch job.</returns>
        private static List<MutateOperation> BuildAllOperations(long customerId)
        {
            List<MutateOperation> mutateOperations = new List<MutateOperation>();

            // Creates a new campaign budget operation and adds it to the array of mutate operations.
            CampaignBudgetOperation campaignBudgetOperation =
                BuildCampaignBudgetOperation(customerId);
            mutateOperations.Add(
                new MutateOperation()
                {
                    CampaignBudgetOperation = campaignBudgetOperation
                }
            );

            // Creates new campaign operations and adds them to the array of mutate operations.
            List<CampaignOperation> campaignOperations =
                BuildCampaignOperations(customerId, campaignBudgetOperation.Create.ResourceName);
            foreach (CampaignOperation campaignOperation in campaignOperations)
            {
                mutateOperations.Add(
                    new MutateOperation()
                    {
                        CampaignOperation = campaignOperation
                    }
                );
            }

            // Creates new campaign criterion operations and adds them to the array of mutate
            // operations.
            List<CampaignCriterionOperation> campaignCriterionOperations =
                BuildCampaignCriterionOperations(campaignOperations);
            foreach (CampaignCriterionOperation campaignCriterionOperation in
                campaignCriterionOperations)
            {
                mutateOperations.Add(
                    new MutateOperation()
                    {
                        CampaignCriterionOperation = campaignCriterionOperation
                    }
                );
            }

            // Creates new ad group operations and adds them to the array of mutate operations.
            List<AdGroupOperation> adGroupOperations = BuildAdGroupOperations(customerId,
                campaignOperations);
            foreach (AdGroupOperation adGroupOperation in adGroupOperations)
            {
                mutateOperations.Add(
                    new MutateOperation()
                    {
                        AdGroupOperation = adGroupOperation
                    }
                );
            }

            // Creates new ad group criterion operations and adds them to the array of mutate
            // operations.
            List<AdGroupCriterionOperation> adGroupCriterionOperations =
                BuildAdGroupCriterionOperations(adGroupOperations);
            foreach (AdGroupCriterionOperation adGroupCriterionOperation in
                adGroupCriterionOperations)
            {
                mutateOperations.Add(
                    new MutateOperation()
                    {
                        AdGroupCriterionOperation = adGroupCriterionOperation
                    }
                );
            }

            // Creates new ad group ad operations and adds them to the array of mutate operations.
            List<AdGroupAdOperation> adGroupAdOperations =
                BuildAdGroupAdOperations(adGroupOperations);
            foreach (AdGroupAdOperation adGroupAdOperation in adGroupAdOperations)
            {
                mutateOperations.Add(
                    new MutateOperation()
                    {
                        AdGroupAdOperation = adGroupAdOperation
                    }
                );
            }

            return mutateOperations;
        }

        /// <summary>
        /// Builds a new campaign budget operation for the specified customer ID.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <returns>The campaign budget operation.</returns>
        private static CampaignBudgetOperation BuildCampaignBudgetOperation(long customerId)
        {
            // Creates a campaign budget.
            CampaignBudget budget = new CampaignBudget()
            {
                ResourceName = ResourceNames.CampaignBudget(customerId, GetNextTemporaryId()),
                Name = "batch job Budget #" + ExampleUtilities.GetRandomString(),
                DeliveryMethod = BudgetDeliveryMethod.Standard,
                AmountMicros = 5_000_000
            };
            // Creates a campaign budget operation.
            return new CampaignBudgetOperation()
            {
                Create = budget
            };
        }

        /// <summary>
        /// Builds new campaign operations for the specified customer ID.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="campaignBudgetResourceName">The resource name of campaign budget to be
        /// used to create campaigns.</param>
        /// <returns>The campaign operations.</returns>
        private static List<CampaignOperation> BuildCampaignOperations(long customerId,
            string campaignBudgetResourceName)
        {
            List<CampaignOperation> operations = new List<CampaignOperation>();

            for (int i = 0; i < NUMBER_OF_CAMPAIGNS_TO_ADD; i++)
            {
                // Creates a campaign.
                long campaignId = GetNextTemporaryId();
                Campaign campaign = new Campaign()
                {
                    ResourceName = ResourceNames.Campaign(customerId, campaignId),
                    Name = "batch job campaign #" + ExampleUtilities.GetRandomString(),
                    AdvertisingChannelType = AdvertisingChannelType.Search,

                    // Recommendation: Set the campaign to PAUSED when creating it to prevent
                    // the ads from immediately serving. Set to ENABLED once you've added
                    // targeting and the ads are ready to serve.
                    Status = CampaignStatus.Paused,

                    // Sets the bidding strategy and budget.
                    ManualCpc = new ManualCpc(),
                    CampaignBudget = campaignBudgetResourceName
                };

                // Creates a campaign operation and adds it to the operations list.
                CampaignOperation op = new CampaignOperation()
                {
                    Create = campaign
                };
                operations.Add(op);
            }

            return operations;
        }

        /// <summary>
        /// Builds new campaign criterion operations for creating negative campaign criteria
        /// (as keywords).
        /// </summary>
        /// <param name="campaignOperations">The campaign operations to be used to create
        /// campaign criteria.</param>
        /// <returns>The campaign criterion operations.</returns>
        private static List<CampaignCriterionOperation> BuildCampaignCriterionOperations(
                    List<CampaignOperation> campaignOperations)
        {
            List<CampaignCriterionOperation> operations =
                new List<CampaignCriterionOperation>();

            foreach (CampaignOperation campaignOperation in campaignOperations)
            {
                // Creates a campaign criterion.
                CampaignCriterion campaignCriterion = new CampaignCriterion()
                {
                    Keyword = new KeywordInfo()
                    {
                        Text = "venus",
                        MatchType = KeywordMatchType.Broad
                    },
                    // Sets the campaign criterion as a negative criterion.
                    Negative = true,
                    Campaign = campaignOperation.Create.ResourceName
                };

                // Creates a campaign criterion operation and adds it to the operations list.
                CampaignCriterionOperation op = new CampaignCriterionOperation()
                {
                    Create = campaignCriterion
                };
                operations.Add(op);
            }

            return operations;
        }

        /// <summary>
        /// Builds new ad group operations for the specified customer ID.
        /// </summary>
        /// <param name="customerId">The Google Ads customer ID for which the call is made.</param>
        /// <param name="campaignOperations">The campaign operations to be used to create ad
        /// groups.</param>
        /// <returns>The ad group operations.</returns>
        private static List<AdGroupOperation> BuildAdGroupOperations(
                    long customerId, List<CampaignOperation> campaignOperations)
        {
            List<AdGroupOperation> operations = new List<AdGroupOperation>();

            foreach (CampaignOperation campaignOperation in campaignOperations)
            {
                for (int i = 0; i < NUMBER_OF_AD_GROUPS_TO_ADD; i++)
                {
                    // Creates an ad group.
                    long adGroupId = GetNextTemporaryId();
                    AdGroup adGroup = new AdGroup()
                    {
                        ResourceName = ResourceNames.AdGroup(customerId, adGroupId),
                        Name = "batch job ad group #" + ExampleUtilities.GetShortRandomString(),
                        Campaign = campaignOperation.Create.ResourceName,
                        Type = AdGroupType.SearchStandard,
                        CpcBidMicros = 10_000_000
                    };

                    // Creates an ad group operation and adds it to the operations list.
                    AdGroupOperation op = new AdGroupOperation()
                    {
                        Create = adGroup
                    };
                    operations.Add(op);
                }
            }

            return operations;
        }

        /// <summary>
        /// Builds new ad group criterion operations for creating keywords. 50% of keywords are
        /// created some invalid characters to demonstrate how BatchJobService returns information
        /// about such errors.
        /// </summary>
        /// <param name="adGroupOperations">The ad group operations to be used to create ad group
        /// criteria.</param>
        /// <returns>The ad group criterion operations.</returns>
        private static List<AdGroupCriterionOperation> BuildAdGroupCriterionOperations(
                    List<AdGroupOperation> adGroupOperations)
        {
            List<AdGroupCriterionOperation> operations = new List<AdGroupCriterionOperation>();

            foreach (AdGroupOperation adGroupOperation in adGroupOperations)
            {
                for (int i = 0; i < NUMBER_OF_KEYWORDS_TO_ADD; i++)
                {
                    // Creates a keyword text by making 50% of keywords invalid to demonstrate
                    // error handling.
                    string keywordText = "mars" + i;
                    if (i % 2 == 0)
                    {
                        keywordText += "!!!";
                    }
                    // Creates an ad group criterion using the created keyword text.
                    AdGroupCriterion adGroupCriterion = new AdGroupCriterion()
                    {
                        Keyword = new KeywordInfo()
                        {
                            Text = keywordText,
                            MatchType = KeywordMatchType.Broad,
                        },
                        AdGroup = adGroupOperation.Create.ResourceName,
                        Status = AdGroupCriterionStatus.Paused
                    };

                    // Creates an ad group criterion operation and adds it to the operations list.
                    AdGroupCriterionOperation op = new AdGroupCriterionOperation()
                    {
                        Create = adGroupCriterion
                    };
                    operations.Add(op);
                }
            }

            return operations;
        }

        /// <summary>
        /// Builds the ad group ad operations.
        /// </summary>
        /// <param name="adGroupOperations">The ad group operations to be used to create ad
        /// group ads.</param>
        /// <returns>The ad group ad operations.</returns>
        private static List<AdGroupAdOperation> BuildAdGroupAdOperations(
                    List<AdGroupOperation> adGroupOperations)
        {
            List<AdGroupAdOperation> operations = new List<AdGroupAdOperation>();

            foreach (AdGroupOperation adGroupOperation in adGroupOperations)
            {
                // Creates an ad group ad.
                AdGroupAd adGroupAd = new AdGroupAd()
                {
                    Ad = new Ad
                    {
                        FinalUrls = { "http://www.example.com/" },

                        // Sets the expanded text ad info on an ad.
                        ExpandedTextAd = new ExpandedTextAdInfo
                        {
                            HeadlinePart1 = "Cruise #" + ExampleUtilities.GetShortRandomString() +
                            " to Mars",
                            HeadlinePart2 = "Best Space Cruise Line",
                            Description = "Buy your tickets now!",
                        },
                    },
                    AdGroup = adGroupOperation.Create.ResourceName,
                    // Optional: Set the status.
                    Status = AdGroupAdStatus.Paused,
                };

                // Creates an ad group ad operation and adds it to the operations list.
                AdGroupAdOperation op = new AdGroupAdOperation()
                {
                    Create = adGroupAd
                };
                operations.Add(op);
            }

            return operations;
        }

        /// <summary>
        /// Returns the next temporary ID and decreases it by one.
        /// </summary>
        /// <returns>The next temporary ID.</returns>
        private static long GetNextTemporaryId()
        {
            return temporaryId--;
        }
    }
}

      

PHP

<?php

/**
 * Copyright 2019 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\CampaignManagement;

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\V17\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V17\GoogleAdsException;
use Google\Ads\GoogleAds\Util\V17\ResourceNames;
use Google\Ads\GoogleAds\V17\Common\ExpandedTextAdInfo;
use Google\Ads\GoogleAds\V17\Common\KeywordInfo;
use Google\Ads\GoogleAds\V17\Common\ManualCpc;
use Google\Ads\GoogleAds\V17\Enums\AdGroupAdStatusEnum\AdGroupAdStatus;
use Google\Ads\GoogleAds\V17\Enums\AdGroupCriterionStatusEnum\AdGroupCriterionStatus;
use Google\Ads\GoogleAds\V17\Enums\AdGroupTypeEnum\AdGroupType;
use Google\Ads\GoogleAds\V17\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType;
use Google\Ads\GoogleAds\V17\Enums\BudgetDeliveryMethodEnum\BudgetDeliveryMethod;
use Google\Ads\GoogleAds\V17\Enums\CampaignStatusEnum\CampaignStatus;
use Google\Ads\GoogleAds\V17\Enums\KeywordMatchTypeEnum\KeywordMatchType;
use Google\Ads\GoogleAds\V17\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V17\Resources\Ad;
use Google\Ads\GoogleAds\V17\Resources\AdGroup;
use Google\Ads\GoogleAds\V17\Resources\AdGroupAd;
use Google\Ads\GoogleAds\V17\Resources\AdGroupCriterion;
use Google\Ads\GoogleAds\V17\Resources\BatchJob;
use Google\Ads\GoogleAds\V17\Resources\Campaign;
use Google\Ads\GoogleAds\V17\Resources\CampaignBudget;
use Google\Ads\GoogleAds\V17\Resources\CampaignCriterion;
use Google\Ads\GoogleAds\V17\Services\AddBatchJobOperationsRequest;
use Google\Ads\GoogleAds\V17\Services\AdGroupAdOperation;
use Google\Ads\GoogleAds\V17\Services\AdGroupCriterionOperation;
use Google\Ads\GoogleAds\V17\Services\AdGroupOperation;
use Google\Ads\GoogleAds\V17\Services\BatchJobOperation;
use Google\Ads\GoogleAds\V17\Services\BatchJobResult;
use Google\Ads\GoogleAds\V17\Services\CampaignBudgetOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignCriterionOperation;
use Google\Ads\GoogleAds\V17\Services\CampaignOperation;
use Google\Ads\GoogleAds\V17\Services\Client\BatchJobServiceClient;
use Google\Ads\GoogleAds\V17\Services\ListBatchJobResultsRequest;
use Google\Ads\GoogleAds\V17\Services\MutateBatchJobRequest;
use Google\Ads\GoogleAds\V17\Services\MutateOperation;
use Google\Ads\GoogleAds\V17\Services\RunBatchJobRequest;
use Google\ApiCore\ApiException;
use Google\ApiCore\OperationResponse;

/**
 * This example adds complete campaigns including campaign budgets, campaigns, ad groups and
 * keywords using BatchJobService.
 */
class AddCompleteCampaignsUsingBatchJob
{
    private const CUSTOMER_ID = 'INSERT_CUSTOMER_ID_HERE';

    private const NUMBER_OF_CAMPAIGNS_TO_ADD = 2;
    private const NUMBER_OF_AD_GROUPS_TO_ADD = 2;
    private const NUMBER_OF_KEYWORDS_TO_ADD = 4;
    private const POLL_FREQUENCY_SECONDS = 1;
    private const MAX_TOTAL_POLL_INTERVAL_SECONDS = 60;

    private const PAGE_SIZE = 1000;

    /** @var int the negative temporary ID used in batch job operations. */
    private static $temporaryId = -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,
        ]);

        // 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
            );
        } 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
     */
    public static function runExample(GoogleAdsClient $googleAdsClient, int $customerId)
    {
        $batchJobServiceClient = $googleAdsClient->getBatchJobServiceClient();

        $batchJobResourceName = self::createBatchJob($batchJobServiceClient, $customerId);
        self::addAllBatchJobOperations(
            $batchJobServiceClient,
            $customerId,
            $batchJobResourceName
        );
        $operationResponse = self::runBatchJob($batchJobServiceClient, $batchJobResourceName);
        self::pollBatchJob($operationResponse);
        self::fetchAndPrintResults($batchJobServiceClient, $batchJobResourceName);
    }

    /**
     * Creates a new batch job for the specified customer ID.
     *
     * @param BatchJobServiceClient $batchJobServiceClient the batch job service client
     * @param int $customerId the customer ID
     * @return string the resource name of the created batch job
     */
    private static function createBatchJob(
        BatchJobServiceClient $batchJobServiceClient,
        int $customerId
    ): string {
        // Creates a batch job operation to create a new batch job.
        $batchJobOperation = new BatchJobOperation();
        $batchJobOperation->setCreate(new BatchJob());

        // Issues a request to the API and get the batch job's resource name.
        $batchJobResourceName = $batchJobServiceClient->mutateBatchJob(
            MutateBatchJobRequest::build($customerId, $batchJobOperation)
        )->getResult()->getResourceName();
        printf(
            "Created a batch job with resource name: '%s'.%s",
            $batchJobResourceName,
            PHP_EOL
        );
        return $batchJobResourceName;
    }

    /**
     * Adds all batch job operations to the batch job. As this is the first time for this
     * batch job, pass null as a sequence token. The response will contain the next sequence token
     * that you can use to upload more operations in the future.
     *
     * @param BatchJobServiceClient $batchJobServiceClient the batch job service client
     * @param int $customerId the customer ID
     * @param string $batchJobResourceName the resource name of batch job to which the batch job
     *     operations will be added
     */
    private static function addAllBatchJobOperations(
        BatchJobServiceClient $batchJobServiceClient,
        int $customerId,
        string $batchJobResourceName
    ): void {
        $response = $batchJobServiceClient->addBatchJobOperations(
            AddBatchJobOperationsRequest::build(
                $batchJobResourceName,
                '',
                self::buildAllOperations($customerId)
            )
        );
        printf(
            "%d mutate operations have been added so far.%s",
            $response->getTotalOperations(),
            PHP_EOL
        );
        // You can use this next sequence token for calling addBatchJobOperations() next time.
        printf(
            "Next sequence token for adding next operations is '%s'.%s",
            $response->getNextSequenceToken(),
            PHP_EOL
        );
    }

    /**
     * Requests the API to run the batch job for executing all uploaded batch job operations.
     *
     * @param BatchJobServiceClient $batchJobServiceClient the batch job service client
     * @param string $batchJobResourceName the resource name of batch job to be run
     * @return OperationResponse the operation response from running batch job
     */
    private static function runBatchJob(
        BatchJobServiceClient $batchJobServiceClient,
        string $batchJobResourceName
    ): OperationResponse {
        $operationResponse =
            $batchJobServiceClient->runBatchJob(RunBatchJobRequest::build($batchJobResourceName));
        printf(
            "Batch job with resource name '%s' has been executed.%s",
            $batchJobResourceName,
            PHP_EOL
        );
        return $operationResponse;
    }

    /**
     * Polls the server until the batch job execution finishes by setting the initial poll
     * delay time and the total time to wait before time-out.
     *
     * @param OperationResponse $operationResponse the operation response used to poll the server
     */
    private static function pollBatchJob(OperationResponse $operationResponse): void
    {
        $operationResponse->pollUntilComplete([
            'initialPollDelayMillis' => self::POLL_FREQUENCY_SECONDS * 1000,
            'totalPollTimeoutMillis' => self::MAX_TOTAL_POLL_INTERVAL_SECONDS * 1000
        ]);
    }

    /**
     * Prints all the results from running the batch job.
     *
     * @param BatchJobServiceClient $batchJobServiceClient the batch job service client
     * @param string $batchJobResourceName the resource name of batch job to get its results
     */
    private static function fetchAndPrintResults(
        BatchJobServiceClient $batchJobServiceClient,
        string $batchJobResourceName
    ): void {
        printf(
            "Batch job with resource name '%s' has finished. Now, printing its results...%s",
            $batchJobResourceName,
            PHP_EOL
        );
        // Gets all the results from running batch job and print their information.
        $batchJobResults = $batchJobServiceClient->listBatchJobResults(
            ListBatchJobResultsRequest::build($batchJobResourceName)->setPageSize(self::PAGE_SIZE)
        );
        foreach ($batchJobResults->iterateAllElements() as $batchJobResult) {
            /** @var BatchJobResult $batchJobResult */
            printf(
                "Batch job #%d has a status '%s' and response of type '%s'.%s",
                $batchJobResult->getOperationIndex(),
                $batchJobResult->getStatus()
                    ? $batchJobResult->getStatus()->getMessage() : 'N/A',
                $batchJobResult->getMutateOperationResponse()
                    ? $batchJobResult->getMutateOperationResponse()->getResponse()
                    : 'N/A',
                PHP_EOL
            );
        }
    }

    /**
     * Builds all operations for creating a complete campaign and return an array of their
     * corresponding mutate operations.
     *
     * @param int $customerId the customer ID
     * @return MutateOperation[] the mutate operations to be added to a batch job
     */
    private static function buildAllOperations(int $customerId): array
    {
        $mutateOperations = [];

        // Creates a new campaign budget operation and add it to the array of mutate operations.
        $campaignBudgetOperation = self::buildCampaignBudgetOperation($customerId);
        $mutateOperations[] =
            new MutateOperation(['campaign_budget_operation' => $campaignBudgetOperation]);

        // Creates new campaign operations and adds them to the array of mutate operations.
        $campaignOperations = self::buildCampaignOperations(
            $customerId,
            $campaignBudgetOperation->getCreate()->getResourceName()
        );
        $mutateOperations = array_merge($mutateOperations, array_map(
            function (CampaignOperation $campaignOperation) {
                return new MutateOperation(['campaign_operation' => $campaignOperation]);
            },
            $campaignOperations
        ));

        // Creates new campaign criterion operations and adds them to the array of mutate
        // operations.
        $mutateOperations = array_merge($mutateOperations, array_map(
            function (CampaignCriterionOperation $campaignCriterionOperation) {
                return new MutateOperation(
                    ['campaign_criterion_operation' => $campaignCriterionOperation]
                );
            },
            self::buildCampaignCriterionOperations($campaignOperations)
        ));

        // Creates new ad group operations and adds them to the array of mutate operations.
        $adGroupOperations = self::buildAdGroupOperations($customerId, $campaignOperations);
        $mutateOperations = array_merge($mutateOperations, array_map(
            function (AdGroupOperation $adGroupOperation) {
                return new MutateOperation(['ad_group_operation' => $adGroupOperation]);
            },
            $adGroupOperations
        ));

        // Creates new ad group criterion operations and adds them to the array of mutate
        // operations.
        $mutateOperations = array_merge($mutateOperations, array_map(
            function (AdGroupCriterionOperation $adGroupCriterionOperation) {
                return new MutateOperation(
                    ['ad_group_criterion_operation' => $adGroupCriterionOperation]
                );
            },
            self::buildAdGroupCriterionOperations($adGroupOperations)
        ));

        // Creates new ad group ad operations and adds them to the array of mutate operations.
        $mutateOperations = array_merge($mutateOperations, array_map(
            function (AdGroupAdOperation $adGroupAdOperation) {
                return new MutateOperation(['ad_group_ad_operation' => $adGroupAdOperation]);
            },
            self::buildAdGroupAdOperations($adGroupOperations)
        ));

        return $mutateOperations;
    }

    /**
     * Builds a new campaign budget operation for the specified customer ID.
     *
     * @param int $customerId the customer ID
     * @return CampaignBudgetOperation the campaign budget operation
     */
    private static function buildCampaignBudgetOperation(int $customerId): CampaignBudgetOperation
    {
        // Creates a campaign budget operation.
        return new CampaignBudgetOperation([
            'create' => new CampaignBudget([
                // Creates a resource name using the temporary ID.
                'resource_name' => ResourceNames::forCampaignBudget(
                    $customerId,
                    self::getNextTemporaryId()
                ),
                'name' => 'Interplanetary Cruise Budget #' . Helper::getPrintableDatetime(),
                'delivery_method' => BudgetDeliveryMethod::STANDARD,
                'amount_micros' => 5000000
            ])
        ]);
    }

    /**
     * Builds new campaign operations for the specified customer ID.
     *
     * @param int $customerId the customer ID
     * @param string $campaignBudgetResourceName the resource name of campaign budget to be used
     *     to create campaigns
     * @return CampaignOperation[] the campaign operations
     */
    private static function buildCampaignOperations(
        int $customerId,
        string $campaignBudgetResourceName
    ): array {
        $operations = [];
        for ($i = 0; $i < self::NUMBER_OF_CAMPAIGNS_TO_ADD; $i++) {
            // Creates a campaign.
            $campaignId = self::getNextTemporaryId();
            $campaign = new Campaign([
                // Creates a resource name using the temporary ID.
                'resource_name' => ResourceNames::forCampaign($customerId, $campaignId),
                'name' => sprintf(
                    'Mutate job campaign #%s.%d',
                    Helper::getPrintableDatetime(),
                    $campaignId
                ),
                'advertising_channel_type' => AdvertisingChannelType::SEARCH,
                // Recommendation: Set the campaign to PAUSED when creating it to prevent
                // the ads from immediately serving. Set to ENABLED once you've added
                // targeting and the ads are ready to serve.
                'status' => CampaignStatus::PAUSED,
                // Sets the bidding strategy and budget.
                'manual_cpc' => new ManualCpc(),
                'campaign_budget' => $campaignBudgetResourceName,
            ]);

            // Creates a campaign operation and add it to the operations list.
            $operations[] = new CampaignOperation(['create' => $campaign]);
        }

        return $operations;
    }

    /**
     * Builds new campaign criterion operations for creating negative campaign criteria
     * (as keywords).
     *
     * @param CampaignOperation[] $campaignOperations the campaign operations to be used to create
     *     campaign criteria
     * @return CampaignCriterionOperation[] the campaign criterion operations
     */
    private static function buildCampaignCriterionOperations(array $campaignOperations): array
    {
        $operations = [];
        foreach ($campaignOperations as $campaignOperation) {
            // Creates a campaign criterion.
            $campaignCriterion = new CampaignCriterion([
                'keyword' => new KeywordInfo([
                    'text' => 'venus',
                    'match_type' => KeywordMatchType::BROAD
                ]),
                // Sets the campaign criterion as a negative criterion.
                'negative' => true,
                'campaign' => $campaignOperation->getCreate()->getResourceName()
            ]);

            // Creates a campaign criterion operation and add it to the operations list.
            $operations[] = new CampaignCriterionOperation(['create' => $campaignCriterion]);
        }
        return $operations;
    }

    /**
     * Builds new ad group operations for the specified customer ID.
     *
     * @param int $customerId the customer ID
     * @param CampaignOperation[] $campaignOperations the campaign operations to be used to create
     *     ad groups
     * @return AdGroupOperation[] the ad group operations
     */
    private static function buildAdGroupOperations(
        int $customerId,
        array $campaignOperations
    ): array {
        $operations = [];
        foreach ($campaignOperations as $campaignOperation) {
            for ($i = 0; $i < self::NUMBER_OF_AD_GROUPS_TO_ADD; $i++) {
                // Creates an ad group.
                $adGroupId = self::getNextTemporaryId();
                $adGroup = new AdGroup([
                    // Creates a resource name using the temporary ID.
                    'resource_name' => ResourceNames::forAdGroup($customerId, $adGroupId),
                    'name' => sprintf(
                        'Mutate job ad group #%s.%d',
                        Helper::getPrintableDatetime(),
                        $adGroupId
                    ),
                    'campaign' => $campaignOperation->getCreate()->getResourceName(),
                    'type' => AdGroupType::SEARCH_STANDARD,
                    'cpc_bid_micros' => 10000000
                ]);

                // Creates an ad group operation and add it to the operations list.
                $operations[] = new AdGroupOperation(['create' => $adGroup]);
            }
        }
        return $operations;
    }

    /**
     * Builds new ad group criterion operations for creating keywords. 50% of keywords are created
     * with some invalid characters to demonstrate how BatchJobService returns information about
     * such errors.
     *
     * @param AdGroupOperation[] $adGroupOperations the ad group operations to be used to create
     *     ad group criteria
     * @return AdGroupCriterionOperation[] the ad group criterion operations
     */
    private static function buildAdGroupCriterionOperations(array $adGroupOperations): array
    {
        $operations = [];
        foreach ($adGroupOperations as $adGroupOperation) {
            for ($i = 0; $i < self::NUMBER_OF_KEYWORDS_TO_ADD; $i++) {
                // Create a keyword text by making 50% of keywords invalid to demonstrate error
                // handling.
                $keywordText = sprintf('mars%d', $i);
                if ($i % 2 == 0) {
                    $keywordText = $keywordText . '!!!';
                }
                // Creates an ad group criterion using the created keyword text.
                $adGroupCriterion = new AdGroupCriterion([
                    'keyword' => new KeywordInfo([
                        'text' => $keywordText,
                        'match_type' => KeywordMatchType::BROAD
                    ]),
                    'ad_group' => $adGroupOperation->getCreate()->getResourceName(),
                    'status' => AdGroupCriterionStatus::ENABLED,
                ]);

                // Creates an ad group criterion operation and add it to the operations list.
                $operations[] = new AdGroupCriterionOperation(['create' => $adGroupCriterion]);
            }
        }
        return $operations;
    }

    /**
     * Builds new ad group ad operations.
     *
     * @param AdGroupOperation[] $adGroupOperations the ad group operations to be used to create
     *     ad group ads
     * @return AdGroupAdOperation[] the ad group ad operations
     */
    private static function buildAdGroupAdOperations(array $adGroupOperations): array
    {
        $operations = [];
        foreach ($adGroupOperations as $adGroupOperation) {
            // Creates an ad group ad.
            $adGroupAd = new AdGroupAd([
                // Creates the expanded text ad info.
                'ad' => new Ad([
                    // Sets the expanded text ad info on an ad.
                    'expanded_text_ad' => new ExpandedTextAdInfo([
                        'headline_part1' => 'Cruise to Mars #' . Helper::getPrintableDatetime(),
                        'headline_part2' => 'Best Space Cruise Line',
                        'description' => 'Buy your tickets now!'
                    ]),
                    'final_urls' => ['http://www.example.com']
                ]),
                'ad_group' => $adGroupOperation->getCreate()->getResourceName(),
                'status' => AdGroupAdStatus::PAUSED,
            ]);

            // Creates an ad group ad operation and add it to the operations list.
            $operations[] = new AdGroupAdOperation(['create' => $adGroupAd]);
        }
        return $operations;
    }

    /**
     * Returns the next temporary ID and decrease it by one.
     *
     * @return int the next temporary ID
     */
    private static function getNextTemporaryId(): int
    {
        return self::$temporaryId--;
    }
}

AddCompleteCampaignsUsingBatchJob::main();

      

Python

#!/usr/bin/env python
# Copyright 2020 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.
"""Adds complete campaigns using BatchJobService.

Complete campaigns include campaign budgets, campaigns, ad groups and keywords.
"""


import argparse
import asyncio
import sys
from uuid import uuid4

from google.ads.googleads.client import GoogleAdsClient
from google.ads.googleads.errors import GoogleAdsException

NUMBER_OF_CAMPAIGNS_TO_ADD = 2
NUMBER_OF_AD_GROUPS_TO_ADD = 2
NUMBER_OF_KEYWORDS_TO_ADD = 4

_temporary_id = 0


def get_next_temporary_id():
    """Returns the next temporary ID to use in batch job operations.

    Decrements the temporary ID by one before returning it. The first value
    returned for the ID is -1.

    Returns: an int of the next temporary ID.
    """
    global _temporary_id
    _temporary_id -= 1
    return _temporary_id


def build_mutate_operation(client, operation_type, operation):
    """Builds a mutate operation with the given operation type and operation.

    Args:
        client: an initialized GoogleAdsClient instance.
        operation_type: a str of the operation type corresponding to a field on
            the MutateOperation message class.
        operation: an operation instance.

    Returns: a MutateOperation instance
    """
    mutate_operation = client.get_type("MutateOperation")
    # Retrieve the nested operation message instance using getattr then copy the
    # contents of the given operation into it using the client.copy_from method.
    client.copy_from(getattr(mutate_operation, operation_type), operation)
    return mutate_operation


async def main(client, customer_id):
    """Main function that runs the example.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a str of a customer ID.
    """
    batch_job_service = client.get_service("BatchJobService")
    batch_job_operation = create_batch_job_operation(client)
    resource_name = create_batch_job(
        batch_job_service, customer_id, batch_job_operation
    )
    operations = build_all_operations(client, customer_id)
    add_all_batch_job_operations(batch_job_service, operations, resource_name)
    operations_response = run_batch_job(batch_job_service, resource_name)

    # Create an asyncio.Event instance to control execution during the
    # asynchronous steps in _poll_batch_job. Note that this is not important
    # for polling asynchronously, it simply helps with execution control, so we
    # can run _fetch_and_print_results after the asynchronous operations have
    # completed.
    done_event = asyncio.Event()
    poll_batch_job(operations_response, done_event)
    # Execution will stop here and wait for the asynchronous steps in
    # _poll_batch_job to complete before proceeding.
    await done_event.wait()

    fetch_and_print_results(client, batch_job_service, resource_name)


def create_batch_job_operation(client):
    """Created a BatchJobOperation and sets an empty BatchJob instance to
    the "create" property in order to tell the Google Ads API that we're
    creating a new BatchJob.

    Args:
        client: an initialized GoogleAdsClient instance.

    Returns: a BatchJobOperation with a BatchJob instance set in the "create"
        property.
    """
    batch_job_operation = client.get_type("BatchJobOperation")
    batch_job = client.get_type("BatchJob")
    client.copy_from(batch_job_operation.create, batch_job)
    return batch_job_operation


def create_batch_job(batch_job_service, customer_id, batch_job_operation):
    """Creates a batch job for the specified customer ID.

    Args:
        batch_job_service: an instance of the BatchJobService message class.
        customer_id: a str of a customer ID.
        batch_job_operation: a BatchJobOperation instance set to "create"

    Returns: a str of a resource name for a batch job.
    """
    try:
        response = batch_job_service.mutate_batch_job(
            customer_id=customer_id, operation=batch_job_operation
        )
        resource_name = response.result.resource_name
        print(f'Created a batch job with resource name "{resource_name}"')
        return resource_name
    except GoogleAdsException as exception:
        handle_googleads_exception(exception)


def add_all_batch_job_operations(batch_job_service, operations, resource_name):
    """Adds all mutate operations to the batch job.

    As this is the first time for this batch job, we pass null as a sequence
    token. The response will contain the next sequence token that we can use
    to upload more operations in the future.

    Args:
        batch_job_service: an instance of the BatchJobService message class.
        operations: a list of a mutate operations.
        resource_name: a str of a resource name for a batch job.
    """
    try:
        response = batch_job_service.add_batch_job_operations(
            resource_name=resource_name,
            sequence_token=None,
            mutate_operations=operations,
        )

        print(
            f"{response.total_operations} mutate operations have been "
            "added so far."
        )

        # You can use this next sequence token for calling
        # add_batch_job_operations() next time.
        print(
            "Next sequence token for adding next operations is "
            f"{response.next_sequence_token}"
        )
    except GoogleAdsException as exception:
        handle_googleads_exception(exception)


def build_all_operations(client, customer_id):
    """Builds all operations for creating a complete campaign.

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

    Returns: a list of operations of various types.
    """
    operations = []

    # Creates a new campaign budget operation and adds it to the list of
    # mutate operations.
    campaign_budget_op = build_campaign_budget_operation(client, customer_id)
    operations.append(
        build_mutate_operation(
            client, "campaign_budget_operation", campaign_budget_op
        )
    )

    # Creates new campaign operations and adds them to the list of
    # mutate operations.
    campaign_operations = build_campaign_operations(
        client, customer_id, campaign_budget_op.create.resource_name
    )
    operations = operations + [
        build_mutate_operation(client, "campaign_operation", operation)
        for operation in campaign_operations
    ]

    # Creates new campaign criterion operations and adds them to the list of
    # mutate operations.
    campaign_criterion_operations = build_campaign_criterion_operations(
        client, campaign_operations
    )
    operations = operations + [
        build_mutate_operation(
            client, "campaign_criterion_operation", operation
        )
        for operation in campaign_criterion_operations
    ]

    # Creates new ad group operations and adds them to the list of
    # mutate operations.
    ad_group_operations = build_ad_group_operations(
        client, customer_id, campaign_operations
    )
    operations = operations + [
        build_mutate_operation(client, "ad_group_operation", operation)
        for operation in ad_group_operations
    ]

    # Creates new ad group criterion operations and add them to the list of
    # mutate operations.
    ad_group_criterion_operations = build_ad_group_criterion_operations(
        client, ad_group_operations
    )
    operations = operations + [
        build_mutate_operation(
            client, "ad_group_criterion_operation", operation
        )
        for operation in ad_group_criterion_operations
    ]

    # Creates new ad group ad operations and adds them to the list of
    # mutate operations.
    ad_group_ad_operations = build_ad_group_ad_operations(
        client, ad_group_operations
    )
    operations = operations + [
        build_mutate_operation(client, "ad_group_ad_operation", operation)
        for operation in ad_group_ad_operations
    ]

    return operations


def build_campaign_budget_operation(client, customer_id):
    """Builds a new campaign budget operation for the given customer ID.

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

    Returns: a CampaignBudgetOperation instance.
    """
    campaign_budget_service = client.get_service("CampaignBudgetService")
    campaign_budget_operation = client.get_type("CampaignBudgetOperation")
    campaign_budget = campaign_budget_operation.create
    resource_name = campaign_budget_service.campaign_budget_path(
        customer_id, get_next_temporary_id()
    )
    campaign_budget.resource_name = resource_name
    campaign_budget.name = f"Interplanetary Cruise Budget #{uuid4()}"
    campaign_budget.delivery_method = (
        client.enums.BudgetDeliveryMethodEnum.STANDARD
    )
    campaign_budget.amount_micros = 5000000

    return campaign_budget_operation


def build_campaign_operations(
    client, customer_id, campaign_budget_resource_name
):
    """Builds new campaign operations for the specified customer ID.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a str of a customer ID.
        campaign_budget_resource_name: a str resource name for a campaign
            budget.

    Returns: a list of CampaignOperation instances.
    """
    return [
        build_campaign_operation(
            client, customer_id, campaign_budget_resource_name
        )
        for i in range(NUMBER_OF_CAMPAIGNS_TO_ADD)
    ]


def build_campaign_operation(
    client, customer_id, campaign_budget_resource_name
):
    """Builds new campaign operation for the specified customer ID.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a str of a customer ID.
        campaign_budget_resource_name: a str resource name for a campaign
            budget.

    Returns: a CampaignOperation instance.
    """
    campaign_operation = client.get_type("CampaignOperation")
    campaign_service = client.get_service("CampaignService")
    # Creates a campaign.
    campaign = campaign_operation.create
    campaign_id = get_next_temporary_id()
    # Creates a resource name using the temporary ID.
    campaign.resource_name = campaign_service.campaign_path(
        customer_id, campaign_id
    )
    campaign.name = f"Batch job campaign #{customer_id}.{campaign_id}"
    campaign.advertising_channel_type = (
        client.enums.AdvertisingChannelTypeEnum.SEARCH
    )
    # Recommendation: Set the campaign to PAUSED when creating it to prevent
    # the ads from immediately serving. Set to ENABLED once you've added
    # targeting and the ads are ready to serve.
    campaign.status = client.enums.CampaignStatusEnum.PAUSED
    # Set the bidding strategy and type by setting manual_cpc equal to an empty
    # ManualCpc instance.
    client.copy_from(campaign.manual_cpc, client.get_type("ManualCpc"))
    campaign.campaign_budget = campaign_budget_resource_name

    return campaign_operation


def build_campaign_criterion_operations(client, campaign_operations):
    """Builds new campaign criterion operations for negative keyword criteria.

    Args:
        client: an initialized GoogleAdsClient instance.
        campaign_operations: a list of CampaignOperation instances.

    Returns: a list of CampaignCriterionOperation instances.
    """
    return [
        build_campaign_criterion_operation(client, campaign_operation)
        for campaign_operation in campaign_operations
    ]


def build_campaign_criterion_operation(client, campaign_operation):
    """Builds a new campaign criterion operation for negative keyword criterion.

    Args:
        client: an initialized GoogleAdsClient instance.
        campaign_operation: a CampaignOperation instance.

    Returns: a CampaignCriterionOperation instance.
    """
    campaign_criterion_operation = client.get_type("CampaignCriterionOperation")
    # Creates a campaign criterion.
    campaign_criterion = campaign_criterion_operation.create
    campaign_criterion.keyword.text = "venus"
    campaign_criterion.keyword.match_type = (
        client.enums.KeywordMatchTypeEnum.BROAD
    )
    # Sets the campaign criterion as a negative criterion.
    campaign_criterion.negative = True
    campaign_criterion.campaign = campaign_operation.create.resource_name

    return campaign_criterion_operation


def build_ad_group_operations(client, customer_id, campaign_operations):
    """Builds new ad group operations for the specified customer ID.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a str of a customer ID.
        campaign_operations: a list of CampaignOperation instances.

    Return: a list of AdGroupOperation instances.
    """
    operations = []

    for campaign_operation in campaign_operations:
        for i in range(NUMBER_OF_AD_GROUPS_TO_ADD):
            operations.append(
                build_ad_group_operation(
                    client, customer_id, campaign_operation
                )
            )

    return operations


def build_ad_group_operation(client, customer_id, campaign_operation):
    """Builds a new ad group operation for the specified customer ID.

    Args:
        client: an initialized GoogleAdsClient instance.
        customer_id: a str of a customer ID.
        campaign_operation: a CampaignOperation instance.

    Return: an AdGroupOperation instance.
    """
    ad_group_operation = client.get_type("AdGroupOperation")
    ad_group_service = client.get_service("AdGroupService")
    # Creates an ad group.
    ad_group = ad_group_operation.create
    ad_group_id = get_next_temporary_id()
    # Creates a resource name using the temporary ID.
    ad_group.resource_name = ad_group_service.ad_group_path(
        customer_id, ad_group_id
    )
    ad_group.name = f"Batch job ad group #{uuid4()}.{ad_group_id}"
    ad_group.campaign = campaign_operation.create.resource_name
    ad_group.type_ = client.enums.AdGroupTypeEnum.SEARCH_STANDARD
    ad_group.cpc_bid_micros = 10000000

    return ad_group_operation


def build_ad_group_criterion_operations(client, ad_group_operations):
    """Builds new ad group criterion operations for creating keywords.

    50% of keywords are created with some invalid characters to demonstrate
    how BatchJobService returns information about such errors.

    Args:
        client: an initialized GoogleAdsClient instance.
        ad_group_operations: a list of AdGroupOperation instances.

    Returns a list of AdGroupCriterionOperation instances.
    """
    operations = []

    for ad_group_operation in ad_group_operations:
        for i in range(NUMBER_OF_KEYWORDS_TO_ADD):
            operations.append(
                build_ad_group_criterion_operation(
                    # Create a keyword text by making 50% of keywords invalid
                    # to demonstrate error handling.
                    client,
                    ad_group_operation,
                    i,
                    i % 2 == 0,
                )
            )

    return operations


def build_ad_group_criterion_operation(
    client, ad_group_operation, number, is_valid=True
):
    """Builds new ad group criterion operation for creating keywords.

    Takes an optional param that dictates whether the keyword text should
    intentionally generate an error with invalid characters.

    Args:
        client: an initialized GoogleAdsClient instance.
        ad_group_operation: an AdGroupOperation instance.
        number: an int of the number to assign to the name of the criterion.
        is_valid: a bool of whether the keyword text should be invalid.

    Returns: an AdGroupCriterionOperation instance.
    """
    ad_group_criterion_operation = client.get_type("AdGroupCriterionOperation")
    # Creates an ad group criterion.
    ad_group_criterion = ad_group_criterion_operation.create
    ad_group_criterion.keyword.text = f"mars{number}"

    # If keyword should be invalid we add exclamation points, which will
    # generate errors when sent to the API.
    if not is_valid:
        ad_group_criterion.keyword.text += "!!!"

    ad_group_criterion.keyword.match_type = (
        client.enums.KeywordMatchTypeEnum.BROAD
    )
    ad_group_criterion.ad_group = ad_group_operation.create.resource_name
    ad_group_criterion.status = client.enums.AdGroupCriterionStatusEnum.ENABLED

    return ad_group_criterion_operation


def build_ad_group_ad_operations(client, ad_group_operations):
    """Builds new ad group ad operations.

    Args:
        client: an initialized GoogleAdsClient instance.
        ad_group_operations: a list of AdGroupOperation instances.

    Returns: a list of AdGroupAdOperation instances.
    """
    return [
        build_ad_group_ad_operation(client, ad_group_operation)
        for ad_group_operation in ad_group_operations
    ]


def build_ad_group_ad_operation(client, ad_group_operation):
    """Builds a new ad group ad operation.

    Args:
        client: an initialized GoogleAdsClient instance.
        ad_group_operation: an AdGroupOperation instance.

    Returns: an AdGroupAdOperation instance.
    """
    ad_group_ad_operation = client.get_type("AdGroupAdOperation")
    # Creates an ad group ad.
    ad_group_ad = ad_group_ad_operation.create
    # Creates the expanded text ad info.
    text_ad = ad_group_ad.ad.expanded_text_ad
    text_ad.headline_part1 = f"Cruise to Mars #{uuid4()}"
    text_ad.headline_part2 = "Best Space Cruise Line"
    text_ad.description = "Buy your tickets now!"

    ad_group_ad.ad.final_urls.append("http://www.example.com")
    ad_group_ad.ad_group = ad_group_operation.create.resource_name
    ad_group_ad.status = client.enums.AdGroupAdStatusEnum.PAUSED

    return ad_group_ad_operation


def run_batch_job(batch_job_service, resource_name):
    """Runs the batch job for executing all uploaded mutate operations.

    Args:
        batch_job_service: an instance of the BatchJobService message class.
        resource_name: a str of a resource name for a batch job.

    Returns: a google.api_core.operation.Operation instance.
    """
    try:
        response = batch_job_service.run_batch_job(resource_name=resource_name)
        print(
            f'Batch job with resource name "{resource_name}" has been '
            "executed."
        )
        return response
    except GoogleAdsException as exception:
        handle_googleads_exception(exception)


def poll_batch_job(operations_response, event):
    """Polls the server until the batch job execution finishes.

    Sets the initial poll delay time and the total time to wait before time-out.

    Args:
        operations_response: a google.api_core.operation.Operation instance.
        event: an instance of asyncio.Event to invoke once the operations have
            completed, alerting the awaiting calling code that it can proceed.
    """
    loop = asyncio.get_event_loop()

    def done_callback(future):
        # The operations_response object will call callbacks from a daemon
        # thread so we must use a threadsafe method of setting the event here
        # otherwise it will not trigger the awaiting code.
        loop.call_soon_threadsafe(event.set)

    # operations_response represents a Long-Running Operation or LRO. The class
    # provides an interface for polling the API to check when the operation is
    # complete. Below we use the asynchronous interface, but there's also a
    # synchronous interface that uses the Operation.result method.
    # See: https://googleapis.dev/python/google-api-core/latest/operation.html
    operations_response.add_done_callback(done_callback)


def fetch_and_print_results(client, batch_job_service, resource_name):
    """Prints all the results from running the batch job.

    Args:
        client: an initialized GoogleAdsClient instance.
        batch_job_service: an instance of the BatchJobService message class.
        resource_name: a str of a resource name for a batch job.
    """
    print(
        f'Batch job with resource name "{resource_name}" has finished. '
        "Now, printing its results..."
    )

    list_results_request = client.get_type("ListBatchJobResultsRequest")
    list_results_request.resource_name = resource_name
    list_results_request.page_size = 1000
    # Gets all the results from running batch job and prints their information.
    batch_job_results = batch_job_service.list_batch_job_results(
        request=list_results_request
    )

    for batch_job_result in batch_job_results:
        status = batch_job_result.status.message
        status = status if status else "N/A"
        result = batch_job_result.mutate_operation_response
        result = result or "N/A"
        print(
            f"Batch job #{batch_job_result.operation_index} "
            f'has a status "{status}" and response type "{result}"'
        )


def handle_googleads_exception(exception):
    """Prints the details of a GoogleAdsException object.

    Args:
        exception: an instance of GoogleAdsException.
    """
    print(
        f'Request with ID "{exception.request_id}" failed with status '
        f'"{exception.error.code().name}" and includes the following errors:'
    )
    for error in exception.failure.errors:
        print(f'\tError 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)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description=(
            "Adds complete campaigns, including campaign budgets, "
            "campaigns, ad groups and keywords for the given "
            "customer ID using BatchJobService."
        )
    )

    # 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.",
    )

    args = parser.parse_args()

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

    asyncio.run(main(googleads_client, args.customer_id))

      

Ruby

#!/usr/bin/env ruby
# Encoding: utf-8
#
# Copyright 2020 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 adds complete campaigns including campaign budgets, campaigns,
# ad groups and keywords using BatchJobService.

require 'optparse'
require 'google/ads/google_ads'

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

  batch_job_service = client.service.batch_job

  batch_job_resource_name = create_batch_job(
    client,
    batch_job_service,
    customer_id,
  )

  add_all_batch_job_operations(
    client,
    batch_job_service,
    customer_id,
    batch_job_resource_name,
  )

  operation_response = run_batch_job(
    batch_job_service,
    batch_job_resource_name,
  )

  poll_batch_job(operation_response)

  fetch_and_print_results(batch_job_service, batch_job_resource_name)
end

# Creates a new batch job for the specified customer ID.
def create_batch_job(client, batch_job_service, customer_id)
  # Creates a batch job operation to create a new batch job.
  operation = client.operation.create_resource.batch_job

  # Issues a request to the API and get the batch job's resource name.
  response = batch_job_service.mutate_batch_job(
    customer_id: customer_id,
    operation: operation
  )

  batch_job_resource_name = response.result.resource_name
  puts "Created a batch job with resource name: '#{batch_job_resource_name}'"

  batch_job_resource_name
end

# Adds all batch job operations to the batch job. As this is the first time
# for this batch job, pass null as a sequence token. The response will contain
# the next sequence token that you can use to upload more operations in the
# future.
def add_all_batch_job_operations(
  client,
  batch_job_service,
  customer_id,
  batch_job_resource_name)
  response = batch_job_service.add_batch_job_operations(
    resource_name: batch_job_resource_name,
    mutate_operations: build_all_operations(client, customer_id),
  )
  puts "#{response.total_operations} mutate operations have been added so far."

  # You can use this next sequence token for calling
  # add_all_batch_job_operations() next time
  puts "Next sequence token for adding next operations is " \
    "'#{response.next_sequence_token}'"
end

# Requests the API to run the batch job for executing all uploaded batch job
# operations.
def run_batch_job(batch_job_service, batch_job_resource_name)
  operation_response = batch_job_service.run_batch_job(
    resource_name: batch_job_resource_name,
  )
  puts "Batch job with resource name '#{batch_job_resource_name}' " \
    "has been executed."
  operation_response
end

# Polls the server until the batch job execution finishes by setting the initial
# poll delay time and the total time to wait before time-out.
def poll_batch_job(operation_response)
  operation_response.wait_until_done!
end

# Prints all the results from running the batch job.
def fetch_and_print_results(batch_job_service, batch_job_resource_name)
  puts "Batch job with resource name '#{batch_job_resource_name}' has " \
    "finished. Now, printing its results..." \

  # Gets all the results from running batch job and print their information.
  batch_job_results = batch_job_service.list_batch_job_results(
    resource_name: batch_job_resource_name,
    page_size: PAGE_SIZE,
  )
  batch_job_results.each do |result|
    puts "Batch job ##{result.operation_index} has a status " \
      "#{result.status ? result.status.message : 'N/A'} and response of type " \
      "#{result.mutate_operation_response ? result.mutate_operation_response.response : 'N/A'}"
  end
end

# Builds all operations for creating a complete campaign and return an array of
# their corresponding mutate operations.
def build_all_operations(client, customer_id)
  mutate_operations = []

  # Creates a new campaign budget operation and add it to the array of mutate
  # operations.
  campaign_budget_operation = build_campaign_budget_operation(client, customer_id)
  mutate_operations << client.operation.mutate do |mutate_op|
    mutate_op.campaign_budget_operation = campaign_budget_operation
  end

  # Creates new campaign operations and adds them to the array of mutate
  # operations.
  campaign_operations = build_campaign_operations(
    client, customer_id, campaign_budget_operation.create.resource_name)
  campaign_operations.each do |op|
    mutate_operations << client.operation.mutate do |mutate_op|
      mutate_op.campaign_operation = op
    end
  end

  # Creates new campaign criterion operations and adds them to the array of
  # mutate operations.
  campaign_criterion_operations = build_campaign_criterion_operations(
    client, campaign_operations)
  campaign_criterion_operations.each do |op|
    mutate_operations << client.operation.mutate do |mutate_op|
      mutate_op.campaign_criterion_operation = op
    end
  end

  # Creates new ad group operations and adds them to the array of mutate
  # operations.
  ad_group_operations = build_ad_group_operations(
    client, customer_id, campaign_operations)
  ad_group_operations.each do |op|
    mutate_operations << client.operation.mutate do |mutate_op|
      mutate_op.ad_group_operation = op
    end
  end

  # Creates new ad group criterion operations and adds them to the array of
  # mutate operations.
  ad_group_criterion_operations = build_ad_group_criterion_operations(
    client, ad_group_operations)
  ad_group_criterion_operations.each do |op|
    mutate_operations << client.operation.mutate do |mutate_op|
      mutate_op.ad_group_criterion_operation = op
    end
  end

  # Creates new ad group ad operations and adds them to the array of mutate
  # operations.
  ad_group_ad_operations = build_ad_group_ad_operations(
    client, ad_group_operations)
  ad_group_ad_operations.each do |op|
    mutate_operations << client.operation.mutate do |mutate_op|
      mutate_op.ad_group_ad_operation = op
    end
  end

  mutate_operations
end

# Builds a new campaign budget operation for the specified customer ID.
def build_campaign_budget_operation(client, customer_id)
  # Creates a campaign budget operation.
  operation = client.operation.create_resource.campaign_budget do |b|
    # Creates a resource name using the temporary ID.
    b.resource_name = client.path.campaign_budget(
      customer_id, get_next_temporary_id)
    b.name = "Interplanetary Cruise Budget ##{(Time.new.to_f * 1000).to_i}"
    b.delivery_method = :STANDARD
    b.amount_micros = 5_000_000
  end

  operation
end

# Builds new campaign operations for the specified customer ID.
def build_campaign_operations(client, customer_id, campaign_budget_resource_name)
  operations = []
  for i in 0..NUMBER_OF_CAMPAIGNS_TO_ADD-1
    # Creates a campaign.
    campaign_id = get_next_temporary_id
    operations << client.operation.create_resource.campaign do |c|
      c.resource_name = client.path.campaign(customer_id, campaign_id)
      c.name = "Mutate job campaign ##{(Time.new.to_f * 1000).to_i}.#{campaign_id}"
      c.advertising_channel_type = :SEARCH
      # Recommendation: Set the campaign to PAUSED when creating it to prevent
      # the ads from immediately serving. Set to ENABLED once you've added
      # targeting and the ads are ready to serve.
      c.status = :PAUSED
      # Sets the bidding strategy and budget.
      c.manual_cpc = client.resource.manual_cpc
      c.campaign_budget = campaign_budget_resource_name
    end
  end

  operations
end

# Builds new campaign criterion operations for creating negative campaign
# criteria (as keywords).
def build_campaign_criterion_operations(client, campaign_operations)
  operations = []
  campaign_operations.each do |op|
    operations << client.operation.create_resource.campaign_criterion do |cc|
      cc.keyword = client.resource.keyword_info do |k|
        k.text = "venus"
        k.match_type = :BROAD
      end
      cc.negative = true
      cc.campaign = op.create.resource_name
    end
  end

  operations
end

# Builds new ad group operations for the specified customer ID.
def build_ad_group_operations(client, customer_id, campaign_operations)
  operations = []
  campaign_operations.each do |op|
    for i in 0..NUMBER_OF_AD_GROUPS_TO_ADD-1
      # Creates an ad group.
      ad_group_id = get_next_temporary_id
      operations << client.operation.create_resource.ad_group do |ag|
        # Creates a resource name using the temporary ID.
        ag.resource_name = client.path.ad_group(customer_id, ad_group_id)
        ag.name = "Mutate job ad group ##{(Time.new.to_f * 1000).to_i}.#{ad_group_id}"
        ag.campaign = op.create.resource_name
        ag.type = :SEARCH_STANDARD
        ag.cpc_bid_micros = 10_000_000
      end
    end
  end

  operations
end

# Builds new ad group criterion operations for creating keywords. 50% of
# keywords are created with some invalid characters to demonstrate how
# BatchJobService returns information about such errors.
def build_ad_group_criterion_operations(client, ad_group_operations)
  operations = []
  ad_group_operations.each do |op|
    for i in 0..NUMBER_OF_KEYWORDS_TO_ADD-1
      # Create a keyword text by making 50% of keywords invalid to demonstrate
      # error handling.
      keyword_text = "mars#{i}"
      if i % 2 == 0
        keyword_text += "!!!"
      end
      # Creates an ad group criterion using the created keyword text.
      operations << client.operation.create_resource.ad_group_criterion do |agc|
        agc.keyword = client.resource.keyword_info do |k|
          k.text = keyword_text
          k.match_type = :BROAD
        end
        agc.ad_group = op.create.resource_name
        agc.status = :ENABLED
      end
    end
  end

  operations
end

# Builds new ad group ad operations.
def build_ad_group_ad_operations(client, ad_group_operations)
  operations = []
  ad_group_operations.each do |op|
    operations << client.operation.create_resource.ad_group_ad do |aga|
      aga.ad = client.resource.ad do |ad|
        ad.expanded_text_ad = client.resource.expanded_text_ad_info do |eta|
          eta.headline_part1 = "Cruise to Mars ##{(Time.new.to_f * 1000).to_i}"
          eta.headline_part2 = "Best Space Cruise Line"
          eta.description = "Buy your tickets now!"
        end
        ad.final_urls << "http://www.example.com"
      end
      aga.ad_group = op.create.resource_name
      aga.status = :PAUSED
    end
  end

  operations
end

# Returns the next temporary ID and decrease it by one.
def get_next_temporary_id
  @temporary_id ||= 0
  @temporary_id -= 1
end

if __FILE__ == $0
  NUMBER_OF_CAMPAIGNS_TO_ADD = 2;
  NUMBER_OF_AD_GROUPS_TO_ADD = 2;
  NUMBER_OF_KEYWORDS_TO_ADD = 4;

  PAGE_SIZE = 1000

  options = {}

  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.separator ''
    opts.separator 'Help:'

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

  begin
    add_complete_campaigns_using_batch_job(
      options.fetch(:customer_id).tr("-", ""))
  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 2019, 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 adds complete campaigns including campaign budgets, campaigns,
# ad groups and keywords using BatchJobService.

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::V17::Resources::BatchJob;
use Google::Ads::GoogleAds::V17::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V17::Resources::Campaign;
use Google::Ads::GoogleAds::V17::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V17::Resources::AdGroup;
use Google::Ads::GoogleAds::V17::Resources::AdGroupCriterion;
use Google::Ads::GoogleAds::V17::Resources::AdGroupAd;
use Google::Ads::GoogleAds::V17::Resources::Ad;
use Google::Ads::GoogleAds::V17::Common::ManualCpc;
use Google::Ads::GoogleAds::V17::Common::KeywordInfo;
use Google::Ads::GoogleAds::V17::Common::ExpandedTextAdInfo;
use Google::Ads::GoogleAds::V17::Enums::BudgetDeliveryMethodEnum   qw(STANDARD);
use Google::Ads::GoogleAds::V17::Enums::AdvertisingChannelTypeEnum qw(SEARCH);
use Google::Ads::GoogleAds::V17::Enums::CampaignStatusEnum;
use Google::Ads::GoogleAds::V17::Enums::KeywordMatchTypeEnum qw(BROAD);
use Google::Ads::GoogleAds::V17::Enums::AdGroupTypeEnum qw(SEARCH_STANDARD);
use Google::Ads::GoogleAds::V17::Enums::AdGroupCriterionStatusEnum;
use Google::Ads::GoogleAds::V17::Enums::AdGroupAdStatusEnum;
use Google::Ads::GoogleAds::V17::Services::BatchJobService::BatchJobOperation;
use Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V17::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V17::Services::AdGroupService::AdGroupOperation;
use
  Google::Ads::GoogleAds::V17::Services::AdGroupCriterionService::AdGroupCriterionOperation;
use Google::Ads::GoogleAds::V17::Services::AdGroupAdService::AdGroupAdOperation;
use Google::Ads::GoogleAds::V17::Utils::ResourceNames;

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

use constant NUMBER_OF_CAMPAIGNS_TO_ADD => 2;
use constant NUMBER_OF_AD_GROUPS_TO_ADD => 2;
use constant NUMBER_OF_KEYWORDS_TO_ADD  => 4;
use constant POLL_FREQUENCY_SECONDS     => 1;
use constant POLL_TIMEOUT_SECONDS       => 60;

use constant PAGE_SIZE => 1000;

# 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.
my $customer_id = "INSERT_CUSTOMER_ID_HERE";

sub add_complete_campaigns_using_batch_job {
  my ($api_client, $customer_id) = @_;

  my $batch_job_service = $api_client->BatchJobService();
  my $operation_service = $api_client->OperationService();

  my $batch_job_resource_name =
    create_batch_job($batch_job_service, $customer_id);

  add_all_batch_job_operations($batch_job_service, $customer_id,
    $batch_job_resource_name);

  my $batch_job_lro =
    run_batch_job($batch_job_service, $batch_job_resource_name);

  poll_batch_job($operation_service, $batch_job_lro);

  fetch_and_print_results($batch_job_service, $batch_job_resource_name);

  return 1;
}

# Creates a new batch job for the specified customer ID.
sub create_batch_job {
  my ($batch_job_service, $customer_id) = @_;

  # Create a batch job operation.
  my $batch_job_operation =
    Google::Ads::GoogleAds::V17::Services::BatchJobService::BatchJobOperation->
    new({create => Google::Ads::GoogleAds::V17::Resources::BatchJob->new({})});

  my $batch_job_resource_name = $batch_job_service->mutate({
      customerId => $customer_id,
      operation  => $batch_job_operation
    })->{result}{resourceName};

  printf
    "Created a batch job with resource name: '%s'.\n",
    $batch_job_resource_name;

  return $batch_job_resource_name;
}

# Adds all batch job operations to the batch job. As this is the first time for
# this batch job, pass null as a sequence token. The response will contain the
# next sequence token that you can use to upload more operations in the future.
sub add_all_batch_job_operations {
  my ($batch_job_service, $customer_id, $batch_job_resource_name) = @_;

  my $add_batch_job_operations_response = $batch_job_service->add_operations({
      resourceName     => $batch_job_resource_name,
      sequenceToken    => undef,
      mutateOperations => build_all_operations($customer_id)});

  printf
    "%d batch operations have been added so far.\n",
    $add_batch_job_operations_response->{totalOperations};

  # You can use this next sequence token for calling add_operations() next time.
  printf
    "Next sequence token for adding next operations is '%s'.\n",
    $add_batch_job_operations_response->{nextSequenceToken};
}

# Requests the API to run the batch job for executing all uploaded batch job
# operations.
sub run_batch_job {
  my ($batch_job_service, $batch_job_resource_name) = @_;

  my $batch_job_lro =
    $batch_job_service->run({resourceName => $batch_job_resource_name});

  printf
    "Batch job with resource name '%s' has been executed.\n",
    $batch_job_resource_name;

  return $batch_job_lro;
}

# Polls the server until the batch job execution finishes by setting the initial
# poll delay time and the total time to wait before time-out.
sub poll_batch_job {
  my ($operation_service, $batch_job_lro) = @_;

  $operation_service->poll_until_done({
    name                 => $batch_job_lro->{name},
    pollFrequencySeconds => POLL_FREQUENCY_SECONDS,
    pollTimeoutSeconds   => POLL_TIMEOUT_SECONDS
  });
}

# Prints all the results from running the batch job.
sub fetch_and_print_results {
  my ($batch_job_service, $batch_job_resource_name) = @_;

  printf "Batch job with resource name '%s' has finished. " .
    "Now, printing its results...\n", $batch_job_resource_name;

  # Get all the results from running batch job and print their information.
  my $list_batch_job_results_response = $batch_job_service->list_results({
    resourceName => $batch_job_resource_name,
    pageSize     => PAGE_SIZE
  });

  foreach my $batch_job_result (@{$list_batch_job_results_response->{results}})
  {
    printf
      "Batch job #%d has a status '%s' and response of type '%s'.\n",
      $batch_job_result->{operationIndex},
      $batch_job_result->{status} ? $batch_job_result->{status}{message}
      : "N/A",
      $batch_job_result->{mutateOperationResponse}
      ? [keys %{$batch_job_result->{mutateOperationResponse}}]->[0]
      : "N/A";
  }
}

# Builds all operations for creating a complete campaign and return an array of
# their corresponding mutate operations.
sub build_all_operations {
  my $customer_id = shift;

  my $mutate_operations = [];

  # Create a new campaign budget operation and add it to the array of mutate operations.
  my $campaign_budget_operation = build_campaign_budget_operation($customer_id);
  push @$mutate_operations,
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
    new({
      campaignBudgetOperation => $campaign_budget_operation
    });

  # Create new campaign operations and add them to the array of mutate operations.
  my $campaign_operations = build_campaign_operations($customer_id,
    $campaign_budget_operation->{create}{resourceName});
  push @$mutate_operations, map {
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
      new({
        campaignOperation => $_
      })
  } @$campaign_operations;

  # Create new campaign criterion operations and add them to the array of mutate
  # operations.
  my $campaign_criterion_operations =
    build_campaign_criterion_operations($campaign_operations);
  push @$mutate_operations, map {
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
      new({
        campaignCriterionOperation => $_
      })
  } @$campaign_criterion_operations;

  # Create new ad group operations and add them to the array of mutate operations.
  my $ad_group_operations =
    build_ad_group_operations($customer_id, $campaign_operations);
  push @$mutate_operations, map {
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
      new({
        adGroupOperation => $_
      })
  } @$ad_group_operations;

  # Create new ad group criterion operations and add them to the array of mutate
  # operations.
  my $ad_group_criterion_operations =
    build_ad_group_criterion_operations($ad_group_operations);
  push @$mutate_operations, map {
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
      new({
        adGroupCriterionOperation => $_
      })
  } @$ad_group_criterion_operations;

  # Create new ad group ad operations and add them to the array of mutate operations.
  my $ad_group_ad_operations =
    build_ad_group_ad_operations($ad_group_operations);
  push @$mutate_operations, map {
    Google::Ads::GoogleAds::V17::Services::GoogleAdsService::MutateOperation->
      new({
        adGroupAdOperation => $_
      })
  } @$ad_group_ad_operations;

  return $mutate_operations;
}

# Builds a new campaign budget operation for the specified customer ID.
sub build_campaign_budget_operation {
  my $customer_id = shift;

  # Create a campaign budget operation.
  return
    Google::Ads::GoogleAds::V17::Services::CampaignBudgetService::CampaignBudgetOperation
    ->new({
      create => Google::Ads::GoogleAds::V17::Resources::CampaignBudget->new({
          # Create a resource name using the temporary ID.
          resourceName =>
            Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign_budget(
            $customer_id, next_temporary_id()
            ),
          name           => "Interplanetary Cruise Budget #" . uniqid(),
          deliveryMethod => STANDARD,
          amountMicros   => 5000000
        })});
}

# Builds new campaign operations for the specified customer ID.
sub build_campaign_operations {
  my ($customer_id, $campaign_budget_resource_name) = @_;

  my $campaign_operations = [];
  for (my $i = 0 ; $i < NUMBER_OF_CAMPAIGNS_TO_ADD ; $i++) {
    # Create a campaign.
    my $campaign_id = next_temporary_id();
    my $campaign    = Google::Ads::GoogleAds::V17::Resources::Campaign->new({
        # Create a resource name using the temporary ID.
        resourceName =>
          Google::Ads::GoogleAds::V17::Utils::ResourceNames::campaign(
          $customer_id, $campaign_id
          ),
        name => sprintf("Batch job campaign #%s.%d", uniqid(), $campaign_id),
        advertisingChannelType => SEARCH,
        # Recommendation: Set the campaign to PAUSED when creating it to prevent
        # the ads from immediately serving. Set to ENABLED once you've added
        # targeting and the ads are ready to serve.
        status =>
          Google::Ads::GoogleAds::V17::Enums::CampaignStatusEnum::PAUSED,
        # Set the bidding strategy and budget.
        manualCpc      => Google::Ads::GoogleAds::V17::Common::ManualCpc->new(),
        campaignBudget => $campaign_budget_resource_name,
      });

    # Create a campaign operation and add it to the operations list.
    push @$campaign_operations,
      Google::Ads::GoogleAds::V17::Services::CampaignService::CampaignOperation
      ->new({
        create => $campaign
      });
  }

  return $campaign_operations;
}

# Builds new campaign criterion operations for creating negative campaign criteria
# (as keywords).
sub build_campaign_criterion_operations {
  my $campaign_operations = shift;

  my $campaign_criterion_operations = [];
  foreach my $campaign_operation (@$campaign_operations) {
    # Create a campaign criterion.
    my $campaign_criterion =
      Google::Ads::GoogleAds::V17::Resources::CampaignCriterion->new({
        keyword => Google::Ads::GoogleAds::V17::Common::KeywordInfo->new({
            text      => "venus",
            matchType => BROAD
          }
        ),
        # Set the campaign criterion as a negative criterion.
        negative => "true",
        campaign => $campaign_operation->{create}{resourceName}});

    # Create a campaign criterion operation and add it to the operations list.
    push @$campaign_criterion_operations,
      Google::Ads::GoogleAds::V17::Services::CampaignCriterionService::CampaignCriterionOperation
      ->new({
        create => $campaign_criterion
      });
  }

  return $campaign_criterion_operations;
}

# Builds new ad group operations for the specified customer ID.
sub build_ad_group_operations {
  my ($customer_id, $campaign_operations) = @_;

  my $ad_group_operations = [];
  foreach my $campaign_operation (@$campaign_operations) {
    for (my $i = 0 ; $i < NUMBER_OF_AD_GROUPS_TO_ADD ; $i++) {
      # Create an ad group.
      my $ad_group_id = next_temporary_id();
      my $ad_group    = Google::Ads::GoogleAds::V17::Resources::AdGroup->new({
          # Create a resource name using the temporary ID.
          resourceName =>
            Google::Ads::GoogleAds::V17::Utils::ResourceNames::ad_group(
            $customer_id, $ad_group_id
            ),
          name => sprintf("Batch job ad group #%s.%d", uniqid(), $ad_group_id),
          campaign     => $campaign_operation->{create}{resourceName},
          type         => SEARCH_STANDARD,
          cpcBidMicros => 10000000
        });

      # Create an ad group operation and add it to the operations list.
      push @$ad_group_operations,
        Google::Ads::GoogleAds::V17::Services::AdGroupService::AdGroupOperation
        ->new({
          create => $ad_group
        });
    }
  }

  return $ad_group_operations;
}

# Builds new ad group criterion operations for creating keywords. 50% of keywords
# are created with some invalid characters to demonstrate how BatchJobService
# returns information about such errors.
sub build_ad_group_criterion_operations {
  my $ad_group_operations = shift;

  my $ad_group_criterion_operations = [];
  foreach my $ad_group_operation (@$ad_group_operations) {
    for (my $i = 0 ; $i < NUMBER_OF_KEYWORDS_TO_ADD ; $i++) {
      # Create a keyword text by making 50% of keywords invalid to demonstrate
      # error handling.
      my $keyword_text = "mars$i";
      if ($i % 2 == 0) {
        $keyword_text = $keyword_text . '!!!';
      }

      # Create an ad group criterion using the created keyword text.
      my $ad_group_criterion =
        Google::Ads::GoogleAds::V17::Resources::AdGroupCriterion->new({
          keyword => Google::Ads::GoogleAds::V17::Common::KeywordInfo->new({
              text      => $keyword_text,
              matchType => BROAD
            }
          ),
          adGroup => $ad_group_operation->{create}{resourceName},
          status  =>
            Google::Ads::GoogleAds::V17::Enums::AdGroupCriterionStatusEnum::ENABLED
        });

      # Create an ad group criterion operation and add it to the operations list.
      push @$ad_group_criterion_operations,
        Google::Ads::GoogleAds::V17::Services::AdGroupCriterionService::AdGroupCriterionOperation
        ->new({
          create => $ad_group_criterion
        });
    }
  }

  return $ad_group_criterion_operations;
}

# Builds new ad group ad operations.
sub build_ad_group_ad_operations {
  my $ad_group_operations = shift;

  my $ad_group_ad_operations = [];
  foreach my $ad_group_operation (@$ad_group_operations) {
    # Create an ad group ad.
    my $ad_group_ad = Google::Ads::GoogleAds::V17::Resources::AdGroupAd->new({
        # Create the expanded text ad info.
        ad => Google::Ads::GoogleAds::V17::Resources::Ad->new({
            # Set the expanded text ad info on an ad.
            expandedTextAd =>
              Google::Ads::GoogleAds::V17::Common::ExpandedTextAdInfo->new({
                headlinePart1 => "Cruise to Mars #" . uniqid(),
                headlinePart2 => "Best Space Cruise Line",
                description   => "Buy your tickets now!"
              }
              ),
            finalUrls => "http://www.example.com",
          }
        ),
        adGroup => $ad_group_operation->{create}{resourceName},
        status  =>
          Google::Ads::GoogleAds::V17::Enums::AdGroupAdStatusEnum::PAUSED
      });

    # Create an ad group ad operation and add it to the operations list.
    push @$ad_group_ad_operations,
      Google::Ads::GoogleAds::V17::Services::AdGroupAdService::AdGroupAdOperation
      ->new({
        create => $ad_group_ad
      });
  }

  return $ad_group_ad_operations;
}

# Specifies a decreasing negative number for temporary IDs.
# Returns -1, -2, -3, etc. on subsequent calls.
sub next_temporary_id {
  our $temporary_id ||= 0;
  $temporary_id -= 1;
}

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

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

# 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_complete_campaigns_using_batch_job($api_client, $customer_id =~ s/-//gr);

=pod

=head1 NAME

add_complete_campaigns_using_batch_job

=head1 DESCRIPTION

This example adds complete campaigns including campaign budgets, campaigns, ad groups
and keywords using BatchJobService.

=head1 SYNOPSIS

add_complete_campaigns_using_batch_job.pl [options]

    -help                       Show the help message.
    -customer_id                The Google Ads customer ID.

=cut