/// <summary>
        /// Cancels the job.
        /// </summary>
        /// <param name="batchJobService">The batch job service.</param>
        /// <param name="batchJob">The batch job.</param>
        private BatchJob CancelJob(BatchJobService batchJobService, BatchJob batchJob)
        {
            try {
                batchJob.status = BatchJobStatus.CANCELING;
                BatchJobOperation batchJobSetOperation = new BatchJobOperation()
                {
                    @operator = Operator.SET,
                    operand   = batchJob
                };

                batchJob = batchJobService.mutate(
                    new BatchJobOperation[] { batchJobSetOperation }).value[0];
                Console.WriteLine("Requested cancellation of batch job with ID {0}.", batchJob.id);
            } catch (AdWordsApiException e) {
                ApiException innerException = e.ApiException as ApiException;
                if (innerException == null)
                {
                    // This means that the API call failed, but not due to an error on
                    // the operations. You can still examine the innerException property
                    // of the original exception to get more details.
                    throw new Exception("Failed to retrieve ApiError. See inner exception for more " +
                                        "details.", e);
                }

                // Examine each ApiError received from the server.
                foreach (ApiError apiError in innerException.errors)
                {
                    if (apiError is BatchJobError)
                    {
                        BatchJobError batchJobError = (BatchJobError)apiError;
                        if (batchJobError.reason == BatchJobErrorReason.INVALID_STATE_CHANGE)
                        {
                            Console.WriteLine("Attempt to cancel batch job with ID {0} was rejected because " +
                                              "the job already completed or was canceled.", batchJob.id);
                            continue;
                        }
                    }
                }
                throw;
            }
            return(batchJob);
        }
        /// <summary>
        /// Runs the code example.
        /// </summary>
        /// <param name="user">The AdWords user.</param>
        /// <param name="adGroupId">Id of the ad groups to which keywords are
        /// added.</param>
        public void Run(AdWordsUser user, long adGroupId)
        {
            using (BatchJobService batchJobService = (BatchJobService)user.GetService(
                       AdWordsService.v201708.BatchJobService)) {
                BatchJobOperation addOp = new BatchJobOperation()
                {
                    @operator = Operator.ADD,
                    operand   = new BatchJob()
                };

                try {
                    BatchJob batchJob = batchJobService.mutate(new BatchJobOperation[] { addOp }).value[0];

                    Console.WriteLine("Created BatchJob with ID {0}, status '{1}' and upload URL {2}.",
                                      batchJob.id, batchJob.status, batchJob.uploadUrl.url);

                    List <AdGroupCriterionOperation> operations = CreateOperations(adGroupId);

                    // Create a BatchJobUtilities instance for uploading operations. Use a
                    // chunked upload.
                    BatchJobUtilities batchJobUploadHelper = new BatchJobUtilities(user, true, CHUNK_SIZE);

                    // Create a resumable Upload URL to upload the operations.
                    string resumableUploadUrl = batchJobUploadHelper.GetResumableUploadUrl(
                        batchJob.uploadUrl.url);

                    // Use the BatchJobUploadHelper to upload all operations.
                    batchJobUploadHelper.Upload(resumableUploadUrl, operations.ToArray());

                    // A flag to determine if the job was requested to be cancelled. This
                    // typically comes from the user.
                    bool wasCancelRequested = false;

                    bool isComplete = batchJobUploadHelper.WaitForPendingJob(batchJob.id,
                                                                             TIME_TO_WAIT_FOR_COMPLETION, delegate(BatchJob waitBatchJob, long timeElapsed) {
                        Console.WriteLine("[{0} seconds]: Batch job ID {1} has status '{2}'.",
                                          timeElapsed / 1000, waitBatchJob.id, waitBatchJob.status);
                        batchJob = waitBatchJob;
                        return(wasCancelRequested);
                    });

                    // Optional: Cancel the job if it has not completed after waiting for
                    // TIME_TO_WAIT_FOR_COMPLETION.
                    bool shouldWaitForCancellation = false;
                    if (!isComplete && wasCancelRequested)
                    {
                        BatchJobError cancellationError = null;
                        try {
                            batchJobUploadHelper.TryToCancelJob(batchJob.id);
                        } catch (AdWordsApiException e) {
                            cancellationError = GetBatchJobError(e);
                        }
                        if (cancellationError == null)
                        {
                            Console.WriteLine("Successfully requested job cancellation.");
                            shouldWaitForCancellation = true;
                        }
                        else
                        {
                            Console.WriteLine("Job cancellation failed. Error says: {0}.",
                                              cancellationError.reason);
                        }

                        if (shouldWaitForCancellation)
                        {
                            isComplete = batchJobUploadHelper.WaitForPendingJob(batchJob.id,
                                                                                TIME_TO_WAIT_FOR_COMPLETION, delegate(BatchJob waitBatchJob, long timeElapsed) {
                                Console.WriteLine("[{0} seconds]: Batch job ID {1} has status '{2}'.",
                                                  timeElapsed / 1000, waitBatchJob.id, waitBatchJob.status);
                                batchJob = waitBatchJob;
                                return(false);
                            });
                        }
                    }

                    if (!isComplete)
                    {
                        throw new TimeoutException("Job is still in pending state after waiting for " +
                                                   TIME_TO_WAIT_FOR_COMPLETION + " seconds.");
                    }

                    if (batchJob.processingErrors != null)
                    {
                        foreach (BatchJobProcessingError processingError in batchJob.processingErrors)
                        {
                            Console.WriteLine("  Processing error: {0}, {1}, {2}, {3}, {4}",
                                              processingError.ApiErrorType, processingError.trigger,
                                              processingError.errorString, processingError.fieldPath,
                                              processingError.reason);
                        }
                    }

                    if (batchJob.downloadUrl != null && batchJob.downloadUrl.url != null)
                    {
                        BatchJobMutateResponse mutateResponse = batchJobUploadHelper.Download(
                            batchJob.downloadUrl.url);
                        Console.WriteLine("Downloaded results from {0}.", batchJob.downloadUrl.url);
                        foreach (MutateResult mutateResult in mutateResponse.rval)
                        {
                            String outcome = mutateResult.errorList == null ? "SUCCESS" : "FAILURE";
                            Console.WriteLine("  Operation [{0}] - {1}", mutateResult.index, outcome);
                        }
                    }
                    else
                    {
                        Console.WriteLine("No results available for download.");
                    }
                } catch (Exception e) {
                    throw new System.ApplicationException("Failed to create keywords using batch job.", e);
                }
            }
        }