<commit_before>#!/usr/bin/perl -w
#
# Copyright 2021, Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example shows how to create a Smart campaign.
#
# More details on Smart campaigns can be found here:
# https://support.google.com/google-ads/answer/7652860

use strict;
use warnings;
use utf8;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Google::Ads::GoogleAds::Client;
use Google::Ads::GoogleAds::Utils::GoogleAdsHelper;
use Google::Ads::GoogleAds::Utils::FieldMasks;
use Google::Ads::GoogleAds::V12::Resources::Ad;
use Google::Ads::GoogleAds::V12::Resources::AdGroup;
use Google::Ads::GoogleAds::V12::Resources::AdGroupAd;
use Google::Ads::GoogleAds::V12::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V12::Resources::Campaign;
use Google::Ads::GoogleAds::V12::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V12::Resources::PhoneNumber;
use Google::Ads::GoogleAds::V12::Resources::SmartCampaignSetting;
use Google::Ads::GoogleAds::V12::Common::AdScheduleInfo;
use Google::Ads::GoogleAds::V12::Common::KeywordThemeInfo;
use Google::Ads::GoogleAds::V12::Common::LocationInfo;
use Google::Ads::GoogleAds::V12::Common::SmartCampaignAdInfo;
use Google::Ads::GoogleAds::V12::Common::AdTextAsset;
use Google::Ads::GoogleAds::V12::Enums::AdGroupTypeEnum qw(SMART_CAMPAIGN_ADS);
use Google::Ads::GoogleAds::V12::Enums::AdTypeEnum      qw(SMART_CAMPAIGN_AD);
use Google::Ads::GoogleAds::V12::Enums::AdvertisingChannelTypeEnum qw(SMART);
use Google::Ads::GoogleAds::V12::Enums::AdvertisingChannelSubTypeEnum;
use Google::Ads::GoogleAds::V12::Enums::BudgetTypeEnum;
use Google::Ads::GoogleAds::V12::Enums::CampaignStatusEnum       qw(PAUSED);
use Google::Ads::GoogleAds::V12::Enums::DayOfWeekEnum            qw(MONDAY);
use Google::Ads::GoogleAds::V12::Enums::MinuteOfHourEnum         qw(ZERO);
use Google::Ads::GoogleAds::V12::Enums::ProximityRadiusUnitsEnum qw(MILES);
use Google::Ads::GoogleAds::V12::Services::AdGroupService::AdGroupOperation;
use Google::Ads::GoogleAds::V12::Services::AdGroupAdService::AdGroupAdOperation;
use
  Google::Ads::GoogleAds::V12::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V12::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V12::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V12::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V12::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSettingService::SmartCampaignSettingOperation;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::BusinessContext;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::KeywordTheme;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::LocationList;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestSmartCampaignAdRequest;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestSmartCampaignBudgetOptionsRequest;
use Google::Ads::GoogleAds::V12::Utils::ResourceNames;

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

# Geo target constant for New York City.
use constant GEO_TARGET_CONSTANT => 1023191;
# Country code is a two-letter ISO-3166 code, for a list of all codes see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16
use constant COUNTRY_CODE => "US";
# For a list of all language codes, see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
use constant LANGUAGE_CODE               => "en";
use constant LANDING_PAGE_URL            => "http://www.example.com";
use constant PHONE_NUMBER                => "555-555-5555";
use constant BUDGET_TEMPORARY_ID         => -1;
use constant SMART_CAMPAIGN_TEMPORARY_ID => -2;
use constant AD_GROUP_TEMPORARY_ID       => -3;
# These define the minimum number of headlines and descriptions that are
# required to create an AdGroupAd in a Smart campaign.
use constant REQUIRED_NUM_HEADLINES    => 3;
use constant REQUIRED_NUM_DESCRIPTIONS => 2;

# 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               = undef;
my $keyword_text              = undef;
my $free_form_keyword_text    = undef;
my $business_profile_location = undef;
my $business_name             = undef;

sub add_smart_campaign {
  my ($api_client, $customer_id, $keyword_text, $free_form_keyword_text,
    $business_profile_location, $business_name)
    = @_;

  # [START add_smart_campaign_12]
  # The SmartCampaignSuggestionInfo object acts as the basis for many of the
  # entities necessary to create a Smart campaign. It will be reused a number
  # of times to retrieve suggestions for keyword themes, budget amount,
  # ad creatives, and campaign criteria.
  my $suggestion_info =
    _get_smart_campaign_suggestion_info($business_profile_location,
    $business_name);

  # After creating a SmartCampaignSuggestionInfo object we first use it to
  # generate a list of keyword themes using the SuggestKeywordThemes method
  # on the SmartCampaignSuggestService. It is strongly recommended that you
  # use this strategy for generating keyword themes.
  my $keyword_themes =
    _get_keyword_theme_suggestions($api_client, $customer_id, $suggestion_info);

  # If a keyword text is given, retrieve keyword theme constant suggestions
  # from the KeywordThemeConstantService, map them to KeywordThemes, and
  # append them to the existing list. This logic should ideally only be used
  # if the suggestions from the get_keyword_theme_suggestions funtion are
  # insufficient.
  if (defined $keyword_text) {
    push @$keyword_themes,
      @{_get_keyword_text_auto_completions($api_client, $keyword_text)};
  }

  # Map the KeywordThemeConstants retrieved by the previous two steps to
  # KeywordThemeInfo instances.
  my $keyword_theme_infos = _map_keyword_themes_to_infos($keyword_themes);

  # If a free-form keyword text is given we create a KeywordThemeInfo instance
  # from it and add it to the existing list.
  if (defined $free_form_keyword_text) {
    push @$keyword_theme_infos,
      _get_free_form_keyword_theme_info($free_form_keyword_text);
  }

  # Now add the generated keyword themes to the suggestion info instance.
  $suggestion_info->{keywordThemes} = $keyword_theme_infos;
  # [END add_smart_campaign_12]

  # Retrieve a budget amount suggestion.
  my $suggested_budget_amount =
    _get_budget_suggestion($api_client, $customer_id, $suggestion_info);

  # Retrieve Smart campaign ad creative suggestions.
  my $ad_suggestions =
    _get_ad_suggestions($api_client, $customer_id, $suggestion_info);

  # [START add_smart_campaign_7]
  # The below methods create and return MutateOperations that we later provide to the
  # GoogleAdsService.Mutate method in order to create the entities in a single
  # request. Since the entities for a Smart campaign are closely tied to one-another
  # it's considered a best practice to create them in a single Mutate request; the
  # entities will either all complete successfully or fail entirely, leaving no
  # orphaned entities. See:
  # https://developers.google.com/google-ads/api/docs/mutating/overview
  my $campaign_budget_operation =
    _create_campaign_budget_operation($customer_id, $suggested_budget_amount);
  my $smart_campaign_operation = _create_smart_campaign_operation($customer_id);
  my $smart_campaign_setting_operation =
    _create_smart_campaign_setting_operation($customer_id,
    $business_profile_location, $business_name);
  my $campaign_criterion_operations =
    _create_campaign_criterion_operations($customer_id, $keyword_theme_infos,
    $suggestion_info);
  my $ad_group_operation = _create_ad_group_operation($customer_id);
  my $ad_group_ad_operation =
    _create_ad_group_ad_operation($customer_id, $ad_suggestions);

  # It's important to create these entities in this order because they depend on
  # each other. For example, the SmartCampaignSetting and ad group depend on the
  # campaign and the ad group ad depends on the ad group.
  my $mutate_operations = [
    $campaign_budget_operation, $smart_campaign_operation,
    $smart_campaign_setting_operation,
    # Expand the list of campaign criterion operations into the list of
    # other mutate operations.
    @$campaign_criterion_operations,
    $ad_group_operation, $ad_group_ad_operation
  ];

  # Send the operations in a single mutate request.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $mutate_operations
  });

  _print_response_details($mutate_google_ads_response);
  # [END add_smart_campaign_7]

  return 1;
}

# [START add_smart_campaign_11]
# Retrieves KeywordThemes using the given suggestion info.
# Here we use the SuggestKeywordThemes method, which uses all of the business
# details included in the given SmartCampaignSuggestionInfo instance to generate
# keyword theme suggestions. This is the recommended way to generate keyword themes
# because it uses detailed information about your business, its location, and
# website content to generate keyword themes.
sub _get_keyword_theme_suggestions {
  my ($api_client, $customer_id, $suggestion_info) = @_;

  my $response =
    $api_client->SmartCampaignSuggestService()->suggest_keyword_themes(
    Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest
      ->new({
        customerId     => $customer_id,
        suggestionInfo => $suggestion_info
      }));

  printf "Retrieved %d keyword theme suggestions from the SuggestKeywordThemes"
    . "method.\n",
    scalar @{$response->{keywordThemes}};

  return $response->{keywordThemes};
}
# [END add_smart_campaign_11]

# [START add_smart_campaign]
# Retrieves KeywordThemeConstants for the given keyword text.
# These KeywordThemeConstants are derived from autocomplete data for the given
# keyword text. They are mapped to KeywordThemes before being returned.
sub _get_keyword_text_auto_completions {
  my ($api_client, $keyword_text) = @_;

  my $response = $api_client->KeywordThemeConstantService()->suggest(
    Google::Ads::GoogleAds::V12::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest
      ->new({
        queryText    => $keyword_text,
        countryCode  => COUNTRY_CODE,
        languageCode => LANGUAGE_CODE
      }));

  printf "Retrieved %d keyword theme constants using the keyword '%s'.\n",
    scalar @{$response->{keywordThemeConstants}}, $keyword_text;

  # Map the keyword theme constants to KeywordTheme instances for consistency
  # with the response from SmartCampaignSuggestService.SuggestKeywordThemes.
  my $keyword_themes = [];
  foreach my $keyword_theme_constant (@{response->{keywordThemeConstants}}) {
    push @$keyword_themes,
      Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::KeywordTheme
      ->new({
        keywordThemeConstant => $keyword_theme_constant
      });
  }

  return $keyword_themes;
}
# [END add_smart_campaign]

# [START add_smart_campaign_13]
# Creates a KeywordInfo instance using the given free-form keyword text.
sub _get_free_form_keyword_theme_info {
  my ($free_form_keyword_text) = @_;

  return Google::Ads::GoogleAds::V12::Common::KeywordThemeInfo->new({
    freeFormKeywordTheme => $free_form_keyword_text
  });
}
# [END add_smart_campaign_13]

# Maps a list of KeywordThemes to KeywordThemeInfos.<commit_msg>Fix typo in add_smart_campaign.pl (#92)<commit_after>#!/usr/bin/perl -w
#
# Copyright 2021, Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This example shows how to create a Smart campaign.
#
# More details on Smart campaigns can be found here:
# https://support.google.com/google-ads/answer/7652860

use strict;
use warnings;
use utf8;

use FindBin qw($Bin);
use lib "$Bin/../../lib";
use Google::Ads::GoogleAds::Client;
use Google::Ads::GoogleAds::Utils::GoogleAdsHelper;
use Google::Ads::GoogleAds::Utils::FieldMasks;
use Google::Ads::GoogleAds::V12::Resources::Ad;
use Google::Ads::GoogleAds::V12::Resources::AdGroup;
use Google::Ads::GoogleAds::V12::Resources::AdGroupAd;
use Google::Ads::GoogleAds::V12::Resources::CampaignBudget;
use Google::Ads::GoogleAds::V12::Resources::Campaign;
use Google::Ads::GoogleAds::V12::Resources::CampaignCriterion;
use Google::Ads::GoogleAds::V12::Resources::PhoneNumber;
use Google::Ads::GoogleAds::V12::Resources::SmartCampaignSetting;
use Google::Ads::GoogleAds::V12::Common::AdScheduleInfo;
use Google::Ads::GoogleAds::V12::Common::KeywordThemeInfo;
use Google::Ads::GoogleAds::V12::Common::LocationInfo;
use Google::Ads::GoogleAds::V12::Common::SmartCampaignAdInfo;
use Google::Ads::GoogleAds::V12::Common::AdTextAsset;
use Google::Ads::GoogleAds::V12::Enums::AdGroupTypeEnum qw(SMART_CAMPAIGN_ADS);
use Google::Ads::GoogleAds::V12::Enums::AdTypeEnum      qw(SMART_CAMPAIGN_AD);
use Google::Ads::GoogleAds::V12::Enums::AdvertisingChannelTypeEnum qw(SMART);
use Google::Ads::GoogleAds::V12::Enums::AdvertisingChannelSubTypeEnum;
use Google::Ads::GoogleAds::V12::Enums::BudgetTypeEnum;
use Google::Ads::GoogleAds::V12::Enums::CampaignStatusEnum       qw(PAUSED);
use Google::Ads::GoogleAds::V12::Enums::DayOfWeekEnum            qw(MONDAY);
use Google::Ads::GoogleAds::V12::Enums::MinuteOfHourEnum         qw(ZERO);
use Google::Ads::GoogleAds::V12::Enums::ProximityRadiusUnitsEnum qw(MILES);
use Google::Ads::GoogleAds::V12::Services::AdGroupService::AdGroupOperation;
use Google::Ads::GoogleAds::V12::Services::AdGroupAdService::AdGroupAdOperation;
use
  Google::Ads::GoogleAds::V12::Services::CampaignBudgetService::CampaignBudgetOperation;
use Google::Ads::GoogleAds::V12::Services::CampaignService::CampaignOperation;
use
  Google::Ads::GoogleAds::V12::Services::CampaignCriterionService::CampaignCriterionOperation;
use Google::Ads::GoogleAds::V12::Services::GoogleAdsService::MutateOperation;
use
  Google::Ads::GoogleAds::V12::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSettingService::SmartCampaignSettingOperation;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::BusinessContext;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::KeywordTheme;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::LocationList;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SmartCampaignSuggestionInfo;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestSmartCampaignAdRequest;
use
  Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestSmartCampaignBudgetOptionsRequest;
use Google::Ads::GoogleAds::V12::Utils::ResourceNames;

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

# Geo target constant for New York City.
use constant GEO_TARGET_CONSTANT => 1023191;
# Country code is a two-letter ISO-3166 code, for a list of all codes see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-16
use constant COUNTRY_CODE => "US";
# For a list of all language codes, see:
# https://developers.google.com/google-ads/api/reference/data/codes-formats#expandable-7
use constant LANGUAGE_CODE               => "en";
use constant LANDING_PAGE_URL            => "http://www.example.com";
use constant PHONE_NUMBER                => "555-555-5555";
use constant BUDGET_TEMPORARY_ID         => -1;
use constant SMART_CAMPAIGN_TEMPORARY_ID => -2;
use constant AD_GROUP_TEMPORARY_ID       => -3;
# These define the minimum number of headlines and descriptions that are
# required to create an AdGroupAd in a Smart campaign.
use constant REQUIRED_NUM_HEADLINES    => 3;
use constant REQUIRED_NUM_DESCRIPTIONS => 2;

# 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               = undef;
my $keyword_text              = undef;
my $free_form_keyword_text    = undef;
my $business_profile_location = undef;
my $business_name             = undef;

sub add_smart_campaign {
  my ($api_client, $customer_id, $keyword_text, $free_form_keyword_text,
    $business_profile_location, $business_name)
    = @_;

  # [START add_smart_campaign_12]
  # The SmartCampaignSuggestionInfo object acts as the basis for many of the
  # entities necessary to create a Smart campaign. It will be reused a number
  # of times to retrieve suggestions for keyword themes, budget amount,
  # ad creatives, and campaign criteria.
  my $suggestion_info =
    _get_smart_campaign_suggestion_info($business_profile_location,
    $business_name);

  # After creating a SmartCampaignSuggestionInfo object we first use it to
  # generate a list of keyword themes using the SuggestKeywordThemes method
  # on the SmartCampaignSuggestService. It is strongly recommended that you
  # use this strategy for generating keyword themes.
  my $keyword_themes =
    _get_keyword_theme_suggestions($api_client, $customer_id, $suggestion_info);

  # If a keyword text is given, retrieve keyword theme constant suggestions
  # from the KeywordThemeConstantService, map them to KeywordThemes, and
  # append them to the existing list. This logic should ideally only be used
  # if the suggestions from the get_keyword_theme_suggestions funtion are
  # insufficient.
  if (defined $keyword_text) {
    push @$keyword_themes,
      @{_get_keyword_text_auto_completions($api_client, $keyword_text)};
  }

  # Map the KeywordThemeConstants retrieved by the previous two steps to
  # KeywordThemeInfo instances.
  my $keyword_theme_infos = _map_keyword_themes_to_infos($keyword_themes);

  # If a free-form keyword text is given we create a KeywordThemeInfo instance
  # from it and add it to the existing list.
  if (defined $free_form_keyword_text) {
    push @$keyword_theme_infos,
      _get_free_form_keyword_theme_info($free_form_keyword_text);
  }

  # Now add the generated keyword themes to the suggestion info instance.
  $suggestion_info->{keywordThemes} = $keyword_theme_infos;
  # [END add_smart_campaign_12]

  # Retrieve a budget amount suggestion.
  my $suggested_budget_amount =
    _get_budget_suggestion($api_client, $customer_id, $suggestion_info);

  # Retrieve Smart campaign ad creative suggestions.
  my $ad_suggestions =
    _get_ad_suggestions($api_client, $customer_id, $suggestion_info);

  # [START add_smart_campaign_7]
  # The below methods create and return MutateOperations that we later provide to the
  # GoogleAdsService.Mutate method in order to create the entities in a single
  # request. Since the entities for a Smart campaign are closely tied to one-another
  # it's considered a best practice to create them in a single Mutate request; the
  # entities will either all complete successfully or fail entirely, leaving no
  # orphaned entities. See:
  # https://developers.google.com/google-ads/api/docs/mutating/overview
  my $campaign_budget_operation =
    _create_campaign_budget_operation($customer_id, $suggested_budget_amount);
  my $smart_campaign_operation = _create_smart_campaign_operation($customer_id);
  my $smart_campaign_setting_operation =
    _create_smart_campaign_setting_operation($customer_id,
    $business_profile_location, $business_name);
  my $campaign_criterion_operations =
    _create_campaign_criterion_operations($customer_id, $keyword_theme_infos,
    $suggestion_info);
  my $ad_group_operation = _create_ad_group_operation($customer_id);
  my $ad_group_ad_operation =
    _create_ad_group_ad_operation($customer_id, $ad_suggestions);

  # It's important to create these entities in this order because they depend on
  # each other. For example, the SmartCampaignSetting and ad group depend on the
  # campaign and the ad group ad depends on the ad group.
  my $mutate_operations = [
    $campaign_budget_operation, $smart_campaign_operation,
    $smart_campaign_setting_operation,
    # Expand the list of campaign criterion operations into the list of
    # other mutate operations.
    @$campaign_criterion_operations,
    $ad_group_operation, $ad_group_ad_operation
  ];

  # Send the operations in a single mutate request.
  my $mutate_google_ads_response = $api_client->GoogleAdsService()->mutate({
    customerId       => $customer_id,
    mutateOperations => $mutate_operations
  });

  _print_response_details($mutate_google_ads_response);
  # [END add_smart_campaign_7]

  return 1;
}

# [START add_smart_campaign_11]
# Retrieves KeywordThemes using the given suggestion info.
# Here we use the SuggestKeywordThemes method, which uses all of the business
# details included in the given SmartCampaignSuggestionInfo instance to generate
# keyword theme suggestions. This is the recommended way to generate keyword themes
# because it uses detailed information about your business, its location, and
# website content to generate keyword themes.
sub _get_keyword_theme_suggestions {
  my ($api_client, $customer_id, $suggestion_info) = @_;

  my $response =
    $api_client->SmartCampaignSuggestService()->suggest_keyword_themes(
    Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::SuggestKeywordThemesRequest
      ->new({
        customerId     => $customer_id,
        suggestionInfo => $suggestion_info
      }));

  printf "Retrieved %d keyword theme suggestions from the SuggestKeywordThemes"
    . "method.\n",
    scalar @{$response->{keywordThemes}};

  return $response->{keywordThemes};
}
# [END add_smart_campaign_11]

# [START add_smart_campaign]
# Retrieves KeywordThemeConstants for the given keyword text.
# These KeywordThemeConstants are derived from autocomplete data for the given
# keyword text. They are mapped to KeywordThemes before being returned.
sub _get_keyword_text_auto_completions {
  my ($api_client, $keyword_text) = @_;

  my $response = $api_client->KeywordThemeConstantService()->suggest(
    Google::Ads::GoogleAds::V12::Services::KeywordThemeConstantService::SuggestKeywordThemeConstantsRequest
      ->new({
        queryText    => $keyword_text,
        countryCode  => COUNTRY_CODE,
        languageCode => LANGUAGE_CODE
      }));

  printf "Retrieved %d keyword theme constants using the keyword '%s'.\n",
    scalar @{$response->{keywordThemeConstants}}, $keyword_text;

  # Map the keyword theme constants to KeywordTheme instances for consistency
  # with the response from SmartCampaignSuggestService.SuggestKeywordThemes.
  my $keyword_themes = [];
  foreach my $keyword_theme_constant (@{$response->{keywordThemeConstants}}) {
    push @$keyword_themes,
      Google::Ads::GoogleAds::V12::Services::SmartCampaignSuggestService::KeywordTheme
      ->new({
        keywordThemeConstant => $keyword_theme_constant
      });
  }

  return $keyword_themes;
}
# [END add_smart_campaign]

# [START add_smart_campaign_13]
# Creates a KeywordInfo instance using the given free-form keyword text.
sub _get_free_form_keyword_theme_info {
  my ($free_form_keyword_text) = @_;

  return Google::Ads::GoogleAds::V12::Common::KeywordThemeInfo->new({
    freeFormKeywordTheme => $free_form_keyword_text
  });
}
# [END add_smart_campaign_13]

# Maps a list of KeywordThemes to KeywordThemeInfos.
{'fraction_non_alphanumeric': 0.09869071634824572, 'fraction_numerical': 0.011471065226850204, 'mean_word_length': 5.821910415542364, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 71, 'https://': 8, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1364438', 'n_tokens_mistral': 7958, 'n_tokens_neox': 7541, 'n_words': 1945}