google-adwords-api 0.15.1 → 0.15.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. checksums.yaml +8 -8
  2. data/ChangeLog +4 -0
  3. data/examples/v201502/extensions/add_google_my_business_location_extensions.rb +4 -4
  4. data/examples/v201506/account_management/create_account.rb +92 -0
  5. data/examples/v201506/account_management/get_account_changes.rb +143 -0
  6. data/examples/v201506/account_management/get_account_hierarchy.rb +98 -0
  7. data/examples/v201506/advanced_operations/add_ad_customizers.rb +189 -0
  8. data/examples/v201506/advanced_operations/add_ad_group_bid_modifier.rb +105 -0
  9. data/examples/v201506/advanced_operations/add_click_to_download_ad.rb +137 -0
  10. data/examples/v201506/advanced_operations/add_text_ad_with_upgraded_urls.rb +138 -0
  11. data/examples/v201506/advanced_operations/create_and_attach_shared_keyword_set.rb +137 -0
  12. data/examples/v201506/advanced_operations/find_and_remove_criteria_from_shared_set.rb +171 -0
  13. data/examples/v201506/advanced_operations/get_ad_group_bid_modifiers.rb +106 -0
  14. data/examples/v201506/advanced_operations/upload_offline_conversions.rb +117 -0
  15. data/examples/v201506/advanced_operations/use_shared_bidding_strategy.rb +152 -0
  16. data/examples/v201506/basic_operations/add_ad_groups.rb +144 -0
  17. data/examples/v201506/basic_operations/add_campaigns.rb +143 -0
  18. data/examples/v201506/basic_operations/add_keywords.rb +118 -0
  19. data/examples/v201506/basic_operations/add_text_ads.rb +113 -0
  20. data/examples/v201506/basic_operations/get_ad_groups.rb +106 -0
  21. data/examples/v201506/basic_operations/get_campaigns.rb +101 -0
  22. data/examples/v201506/basic_operations/get_campaigns_with_awql.rb +93 -0
  23. data/examples/v201506/basic_operations/get_keywords.rb +112 -0
  24. data/examples/v201506/basic_operations/get_text_ads.rb +114 -0
  25. data/examples/v201506/basic_operations/pause_ad.rb +92 -0
  26. data/examples/v201506/basic_operations/remove_ad.rb +93 -0
  27. data/examples/v201506/basic_operations/remove_ad_group.rb +89 -0
  28. data/examples/v201506/basic_operations/remove_campaign.rb +91 -0
  29. data/examples/v201506/basic_operations/remove_keyword.rb +98 -0
  30. data/examples/v201506/basic_operations/update_ad_group.rb +89 -0
  31. data/examples/v201506/basic_operations/update_campaign.rb +90 -0
  32. data/examples/v201506/basic_operations/update_keyword.rb +110 -0
  33. data/examples/v201506/campaign_management/add_campaign_labels.rb +86 -0
  34. data/examples/v201506/campaign_management/add_experiment.rb +166 -0
  35. data/examples/v201506/campaign_management/add_keywords_in_bulk.rb +158 -0
  36. data/examples/v201506/campaign_management/get_all_disapproved_ads.rb +101 -0
  37. data/examples/v201506/campaign_management/get_all_disapproved_ads_with_awql.rb +93 -0
  38. data/examples/v201506/campaign_management/get_campaigns_by_label.rb +112 -0
  39. data/examples/v201506/campaign_management/promote_experiment.rb +85 -0
  40. data/examples/v201506/campaign_management/set_ad_parameters.rb +122 -0
  41. data/examples/v201506/campaign_management/set_criterion_bid_modifier.rb +108 -0
  42. data/examples/v201506/campaign_management/validate_text_ad.rb +114 -0
  43. data/examples/v201506/error_handling/handle_partial_failures.rb +134 -0
  44. data/examples/v201506/error_handling/handle_policy_violation_error.rb +145 -0
  45. data/examples/v201506/extensions/add_google_my_business_location_extensions.rb +190 -0
  46. data/examples/v201506/extensions/add_site_links.rb +168 -0
  47. data/examples/v201506/extensions/add_site_links_using_feeds.rb +276 -0
  48. data/examples/v201506/migration/migrate_to_extension_settings.rb +392 -0
  49. data/examples/v201506/migration/upgrade_ad_url.rb +97 -0
  50. data/examples/v201506/misc/create_ad_words_session_without_properties_file.rb +94 -0
  51. data/examples/v201506/misc/get_all_images_and_videos.rb +108 -0
  52. data/examples/v201506/misc/setup_oauth2.rb +88 -0
  53. data/examples/v201506/misc/upload_image.rb +97 -0
  54. data/examples/v201506/misc/use_oauth2_jwt.rb +97 -0
  55. data/examples/v201506/optimization/estimate_keyword_traffic.rb +150 -0
  56. data/examples/v201506/optimization/get_keyword_bid_simulations.rb +99 -0
  57. data/examples/v201506/optimization/get_keyword_ideas.rb +130 -0
  58. data/examples/v201506/remarketing/add_audience.rb +122 -0
  59. data/examples/v201506/remarketing/add_conversion_tracker.rb +104 -0
  60. data/examples/v201506/remarketing/add_rule_based_user_lists.rb +171 -0
  61. data/examples/v201506/reporting/download_criteria_report.rb +94 -0
  62. data/examples/v201506/reporting/download_criteria_report_with_awql.rb +95 -0
  63. data/examples/v201506/reporting/get_report_fields.rb +79 -0
  64. data/examples/v201506/reporting/parallel_report_download.rb +168 -0
  65. data/examples/v201506/shopping_campaigns/add_product_partition_tree.rb +269 -0
  66. data/examples/v201506/shopping_campaigns/add_product_scope.rb +133 -0
  67. data/examples/v201506/shopping_campaigns/add_shopping_campaign.rb +133 -0
  68. data/examples/v201506/shopping_campaigns/get_product_category_taxonomy.rb +117 -0
  69. data/examples/v201506/targeting/add_campaign_targeting_criteria.rb +184 -0
  70. data/examples/v201506/targeting/add_demographic_targeting_criteria.rb +116 -0
  71. data/examples/v201506/targeting/get_campaign_targeting_criteria.rb +110 -0
  72. data/examples/v201506/targeting/get_targetable_languages_and_carriers.rb +94 -0
  73. data/examples/v201506/targeting/lookup_location.rb +112 -0
  74. data/lib/adwords_api.rb +9 -0
  75. data/lib/adwords_api/api_config.rb +90 -4
  76. data/lib/adwords_api/report_header_handler.rb +5 -0
  77. data/lib/adwords_api/v201506/account_label_service.rb +38 -0
  78. data/lib/adwords_api/v201506/account_label_service_registry.rb +46 -0
  79. data/lib/adwords_api/v201506/ad_customizer_feed_service.rb +38 -0
  80. data/lib/adwords_api/v201506/ad_customizer_feed_service_registry.rb +46 -0
  81. data/lib/adwords_api/v201506/ad_group_ad_service.rb +50 -0
  82. data/lib/adwords_api/v201506/ad_group_ad_service_registry.rb +46 -0
  83. data/lib/adwords_api/v201506/ad_group_bid_modifier_service.rb +42 -0
  84. data/lib/adwords_api/v201506/ad_group_bid_modifier_service_registry.rb +46 -0
  85. data/lib/adwords_api/v201506/ad_group_criterion_service.rb +46 -0
  86. data/lib/adwords_api/v201506/ad_group_criterion_service_registry.rb +46 -0
  87. data/lib/adwords_api/v201506/ad_group_extension_setting_service.rb +42 -0
  88. data/lib/adwords_api/v201506/ad_group_extension_setting_service_registry.rb +46 -0
  89. data/lib/adwords_api/v201506/ad_group_feed_service.rb +42 -0
  90. data/lib/adwords_api/v201506/ad_group_feed_service_registry.rb +46 -0
  91. data/lib/adwords_api/v201506/ad_group_service.rb +46 -0
  92. data/lib/adwords_api/v201506/ad_group_service_registry.rb +46 -0
  93. data/lib/adwords_api/v201506/ad_param_service.rb +38 -0
  94. data/lib/adwords_api/v201506/ad_param_service_registry.rb +46 -0
  95. data/lib/adwords_api/v201506/adwords_user_list_service.rb +38 -0
  96. data/lib/adwords_api/v201506/adwords_user_list_service_registry.rb +46 -0
  97. data/lib/adwords_api/v201506/bidding_strategy_service.rb +42 -0
  98. data/lib/adwords_api/v201506/bidding_strategy_service_registry.rb +46 -0
  99. data/lib/adwords_api/v201506/budget_order_service.rb +42 -0
  100. data/lib/adwords_api/v201506/budget_order_service_registry.rb +46 -0
  101. data/lib/adwords_api/v201506/budget_service.rb +42 -0
  102. data/lib/adwords_api/v201506/budget_service_registry.rb +46 -0
  103. data/lib/adwords_api/v201506/campaign_criterion_service.rb +42 -0
  104. data/lib/adwords_api/v201506/campaign_criterion_service_registry.rb +46 -0
  105. data/lib/adwords_api/v201506/campaign_extension_setting_service.rb +42 -0
  106. data/lib/adwords_api/v201506/campaign_extension_setting_service_registry.rb +46 -0
  107. data/lib/adwords_api/v201506/campaign_feed_service.rb +42 -0
  108. data/lib/adwords_api/v201506/campaign_feed_service_registry.rb +46 -0
  109. data/lib/adwords_api/v201506/campaign_service.rb +46 -0
  110. data/lib/adwords_api/v201506/campaign_service_registry.rb +46 -0
  111. data/lib/adwords_api/v201506/campaign_shared_set_service.rb +42 -0
  112. data/lib/adwords_api/v201506/campaign_shared_set_service_registry.rb +46 -0
  113. data/lib/adwords_api/v201506/constant_data_service.rb +66 -0
  114. data/lib/adwords_api/v201506/constant_data_service_registry.rb +46 -0
  115. data/lib/adwords_api/v201506/conversion_tracker_service.rb +42 -0
  116. data/lib/adwords_api/v201506/conversion_tracker_service_registry.rb +46 -0
  117. data/lib/adwords_api/v201506/customer_extension_setting_service.rb +42 -0
  118. data/lib/adwords_api/v201506/customer_extension_setting_service_registry.rb +46 -0
  119. data/lib/adwords_api/v201506/customer_feed_service.rb +42 -0
  120. data/lib/adwords_api/v201506/customer_feed_service_registry.rb +46 -0
  121. data/lib/adwords_api/v201506/customer_service.rb +38 -0
  122. data/lib/adwords_api/v201506/customer_service_registry.rb +46 -0
  123. data/lib/adwords_api/v201506/customer_sync_service.rb +34 -0
  124. data/lib/adwords_api/v201506/customer_sync_service_registry.rb +47 -0
  125. data/lib/adwords_api/v201506/data_service.rb +54 -0
  126. data/lib/adwords_api/v201506/data_service_registry.rb +46 -0
  127. data/lib/adwords_api/v201506/experiment_service.rb +38 -0
  128. data/lib/adwords_api/v201506/experiment_service_registry.rb +46 -0
  129. data/lib/adwords_api/v201506/feed_item_service.rb +42 -0
  130. data/lib/adwords_api/v201506/feed_item_service_registry.rb +46 -0
  131. data/lib/adwords_api/v201506/feed_mapping_service.rb +42 -0
  132. data/lib/adwords_api/v201506/feed_mapping_service_registry.rb +46 -0
  133. data/lib/adwords_api/v201506/feed_service.rb +42 -0
  134. data/lib/adwords_api/v201506/feed_service_registry.rb +46 -0
  135. data/lib/adwords_api/v201506/geo_location_service.rb +34 -0
  136. data/lib/adwords_api/v201506/geo_location_service_registry.rb +46 -0
  137. data/lib/adwords_api/v201506/label_service.rb +42 -0
  138. data/lib/adwords_api/v201506/label_service_registry.rb +46 -0
  139. data/lib/adwords_api/v201506/location_criterion_service.rb +38 -0
  140. data/lib/adwords_api/v201506/location_criterion_service_registry.rb +46 -0
  141. data/lib/adwords_api/v201506/managed_customer_service.rb +54 -0
  142. data/lib/adwords_api/v201506/managed_customer_service_registry.rb +46 -0
  143. data/lib/adwords_api/v201506/media_service.rb +42 -0
  144. data/lib/adwords_api/v201506/media_service_registry.rb +46 -0
  145. data/lib/adwords_api/v201506/mutate_job_service.rb +42 -0
  146. data/lib/adwords_api/v201506/mutate_job_service_registry.rb +46 -0
  147. data/lib/adwords_api/v201506/offline_conversion_feed_service.rb +34 -0
  148. data/lib/adwords_api/v201506/offline_conversion_feed_service_registry.rb +46 -0
  149. data/lib/adwords_api/v201506/report_definition_service.rb +34 -0
  150. data/lib/adwords_api/v201506/report_definition_service_registry.rb +46 -0
  151. data/lib/adwords_api/v201506/shared_criterion_service.rb +42 -0
  152. data/lib/adwords_api/v201506/shared_criterion_service_registry.rb +46 -0
  153. data/lib/adwords_api/v201506/shared_set_service.rb +42 -0
  154. data/lib/adwords_api/v201506/shared_set_service_registry.rb +46 -0
  155. data/lib/adwords_api/v201506/targeting_idea_service.rb +34 -0
  156. data/lib/adwords_api/v201506/targeting_idea_service_registry.rb +46 -0
  157. data/lib/adwords_api/v201506/traffic_estimator_service.rb +34 -0
  158. data/lib/adwords_api/v201506/traffic_estimator_service_registry.rb +46 -0
  159. data/lib/adwords_api/version.rb +1 -1
  160. data/test/adwords_api/test_adwords_api.rb +1 -1
  161. data/test/adwords_api/test_api_config.rb +8 -8
  162. data/test/adwords_api/test_choices.rb +2 -2
  163. data/test/adwords_api/test_report_utils.rb +1 -1
  164. data/test/templates/v201506/basic_operations_get_campaigns.def +116 -0
  165. data/test/templates/v201506/misc_use_oauth2_jwt.def +131 -0
  166. metadata +156 -2
@@ -0,0 +1,276 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: utf-8
3
+ #
4
+ # Author:: api.dklimkin@gmail.com (Danial Klimkin)
5
+ #
6
+ # Copyright:: Copyright 2013, Google Inc. All Rights Reserved.
7
+ #
8
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17
+ # implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ # This example adds a sitelinks feed and associates it with a campaign.
22
+ #
23
+ # Tags: CampaignFeedService.mutate, FeedItemService.mutate
24
+ # Tags: FeedMappingService.mutate, FeedService.mutate
25
+
26
+ require 'adwords_api'
27
+
28
+ def add_site_links(campaign_id)
29
+ # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
30
+ # when called without parameters.
31
+ adwords = AdwordsApi::Api.new
32
+
33
+ # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
34
+ # the configuration file or provide your own logger:
35
+ # adwords.logger = Logger.new('adwords_xml.log')
36
+
37
+ feed_srv = adwords.service(:FeedService, API_VERSION)
38
+ feed_item_srv = adwords.service(:FeedItemService, API_VERSION)
39
+ feed_mapping_srv = adwords.service(:FeedMappingService, API_VERSION)
40
+ campaign_feed_srv = adwords.service(:CampaignFeedService, API_VERSION)
41
+
42
+ sitelinks_data = {}
43
+
44
+ # Create site links feed first.
45
+ site_links_feed = {
46
+ :name => 'Feed For Site Links',
47
+ :attributes => [
48
+ {:type => 'STRING', :name => 'Link Text'},
49
+ {:type => 'URL_LIST', :name => 'Final URLs'},
50
+ {:type => 'STRING', :name => 'Line 1 Description'},
51
+ {:type => 'STRING', :name => 'Line 2 Description'}
52
+ ]
53
+ }
54
+
55
+ response = feed_srv.mutate([
56
+ {:operator => 'ADD', :operand => site_links_feed}
57
+ ])
58
+ if response and response[:value]
59
+ feed = response[:value].first
60
+ # Attribute of type STRING.
61
+ link_text_feed_attribute_id = feed[:attributes][0][:id]
62
+ # Attribute of type URL_LIST.
63
+ final_url_feed_attribute_id = feed[:attributes][1][:id]
64
+ # Attribute of type STRING.
65
+ line_1_feed_attribute_id = feed[:attributes][2][:id]
66
+ #Attribute of type STRING.
67
+ line_2_feed_attribute_id = feed[:attributes][3][:id]
68
+ puts "Feed with name '%s' and ID %d was added with" %
69
+ [feed[:name], feed[:id]]
70
+ puts "\tText attribute ID %d and Final URLs attribute ID %d " +
71
+ "and Line 1 attribute ID %d and Line 2 attribute ID %d." % [
72
+ link_text_feed_attribute_id,
73
+ final_url_feed_attribute_id,
74
+ line_1_feed_attribute_id,
75
+ line_2_feed_attribute_id
76
+ ]
77
+
78
+ sitelinks_data[:feed_id] = feed[:id]
79
+ sitelinks_data[:link_text_feed_id] = link_text_feed_attribute_id
80
+ sitelinks_data[:final_url_feed_id] = final_url_feed_attribute_id
81
+ sitelinks_data[:line_1_feed_id] = line_1_feed_attribute_id
82
+ sitelinks_data[:line_2_feed_id] = line_2_feed_attribute_id
83
+ else
84
+ raise new StandardError, 'No feeds were added.'
85
+ end
86
+
87
+ # Create site links feed items.
88
+ items_data = [
89
+ {
90
+ :text => 'Home',
91
+ :final_urls => ['http://www.example.com'],
92
+ :line_1 => 'Home line 1',
93
+ :line_2 => 'Home line 2'
94
+ },
95
+ {
96
+ :text => 'Stores',
97
+ :final_urls => ['http://www.example.com/stores'],
98
+ :line_1 => 'Stores line 1',
99
+ :line_2 => 'Stores line 2'
100
+ },
101
+ {
102
+ :text => 'On Sale',
103
+ :final_urls => ['http://www.example.com/sale'],
104
+ :line_1 => 'On Sale line 1',
105
+ :line_2 => 'On Sale line 2'
106
+ },
107
+ {
108
+ :text => 'Support',
109
+ :final_urls => ['http://www.example.com/support'],
110
+ :line_1 => 'Support line 1',
111
+ :line_2 => 'Support line 2'
112
+ },
113
+ {
114
+ :text => 'Products',
115
+ :final_urls => ['http://www.example.com/products'],
116
+ :line_1 => 'Products line 1',
117
+ :line_2 => 'Products line 2'
118
+ },
119
+ {
120
+ :text => 'About',
121
+ :final_urls => ['http://www.example.com/about'],
122
+ :line_1 => 'About line 1',
123
+ :line_2 => 'About line 2'
124
+ }
125
+ ]
126
+
127
+ feed_items = items_data.map do |item|
128
+ {
129
+ :feed_id => sitelinks_data[:feed_id],
130
+ :attribute_values => [
131
+ {
132
+ :feed_attribute_id => sitelinks_data[:link_text_feed_id],
133
+ :string_value => item[:text]
134
+ },
135
+ {
136
+ :feed_attribute_id => sitelinks_data[:final_url_feed_id],
137
+ :string_values => item[:final_urls]
138
+ },
139
+ {
140
+ :feed_attribute_id => sitelinks_data[:line_1_feed_id],
141
+ :string_value => item[:line_1]
142
+ },
143
+ {
144
+ :feed_attribute_id => sitelinks_data[:line_2_feed_id],
145
+ :string_value => item[:line_2]
146
+ }
147
+ ]
148
+ }
149
+ end
150
+
151
+ feed_items_operations = feed_items.map do |item|
152
+ {:operator => 'ADD', :operand => item}
153
+ end
154
+
155
+ response = feed_item_srv.mutate(feed_items_operations)
156
+ if response and response[:value]
157
+ sitelinks_data[:feed_item_ids] = []
158
+ response[:value].each do |feed_item|
159
+ puts 'Feed item with ID %d was added.' % feed_item[:feed_item_id]
160
+ sitelinks_data[:feed_item_ids] << feed_item[:feed_item_id]
161
+ end
162
+ else
163
+ raise new StandardError, 'No feed items were added.'
164
+ end
165
+
166
+ # Create site links feed mapping.
167
+ feed_mapping = {
168
+ :placeholder_type => PLACEHOLDER_SITELINKS,
169
+ :feed_id => sitelinks_data[:feed_id],
170
+ :attribute_field_mappings => [
171
+ {
172
+ :feed_attribute_id => sitelinks_data[:link_text_feed_id],
173
+ :field_id => PLACEHOLDER_FIELD_SITELINK_LINK_TEXT
174
+ },
175
+ {
176
+ :feed_attribute_id => sitelinks_data[:final_url_feed_id],
177
+ :field_id => PLACEHOLDER_FIELD_SITELINK_FINAL_URLS
178
+ },
179
+ {
180
+ :feed_attribute_id => sitelinks_data[:line_1_feed_id],
181
+ :field_id => PLACEHOLDER_FIELD_SITELINK_LINE_1_TEXT
182
+ },
183
+ {
184
+ :feed_attribute_id => sitelinks_data[:line_2_feed_id],
185
+ :field_id => PLACEHOLDER_FIELD_SITELINK_LINE_2_TEXT
186
+ }
187
+ ]
188
+ }
189
+
190
+ response = feed_mapping_srv.mutate([
191
+ {:operator => 'ADD', :operand => feed_mapping}
192
+ ])
193
+ if response and response[:value]
194
+ feed_mapping = response[:value].first
195
+ puts ('Feed mapping with ID %d and placeholder type %d was saved for feed' +
196
+ ' with ID %d.') % [
197
+ feed_mapping[:feed_mapping_id],
198
+ feed_mapping[:placeholder_type],
199
+ feed_mapping[:feed_id]
200
+ ]
201
+ else
202
+ raise new StandardError, 'No feed mappings were added.'
203
+ end
204
+
205
+ # Construct a matching function that associates the sitelink feeditems to the
206
+ # campaign, and set the device preference to Mobile. See the matching function
207
+ # guide at:
208
+ # https://developers.google.com/adwords/api/docs/guides/feed-matching-functions
209
+ # for more details.
210
+ matching_function_string =
211
+ "AND(IN(FEED_ITEM_ID, {%s}), EQUALS(CONTEXT.DEVICE, 'Mobile'))" %
212
+ sitelinks_data[:feed_item_ids].join(',')
213
+
214
+ # Create site links campaign feed.
215
+ campaign_feed = {
216
+ :feed_id => sitelinks_data[:feed_id],
217
+ :campaign_id => campaign_id,
218
+ :matching_function => {:function_string => matching_function_string},
219
+ # Specifying placeholder types on the CampaignFeed allows the same feed
220
+ # to be used for different placeholders in different Campaigns.
221
+ :placeholder_types => [PLACEHOLDER_SITELINKS]
222
+ }
223
+
224
+ response = campaign_feed_srv.mutate([
225
+ {:operator => 'ADD', :operand => campaign_feed}
226
+ ])
227
+ if response and response[:value]
228
+ campaign_feed = response[:value].first
229
+ puts 'Campaign with ID %d was associated with feed with ID %d.' %
230
+ [campaign_feed[:campaign_id], campaign_feed[:feed_id]]
231
+ else
232
+ raise new StandardError, 'No campaign feeds were added.'
233
+ end
234
+ end
235
+
236
+ if __FILE__ == $0
237
+ API_VERSION = :v201506
238
+
239
+ # See the Placeholder reference page for a list of all the placeholder types
240
+ # and fields, see:
241
+ # https://developers.google.com/adwords/api/docs/appendix/placeholders
242
+ PLACEHOLDER_SITELINKS = 1
243
+ PLACEHOLDER_FIELD_SITELINK_LINK_TEXT = 1
244
+ PLACEHOLDER_FIELD_SITELINK_FINAL_URLS = 5
245
+ PLACEHOLDER_FIELD_SITELINK_LINE_1_TEXT = 3
246
+ PLACEHOLDER_FIELD_SITELINK_LINE_2_TEXT = 4
247
+
248
+ begin
249
+ # Campaign ID to add site link to.
250
+ campaign_id = 'INSERT_CAMPAIGN_ID_HERE'.to_i
251
+ add_site_links(campaign_id)
252
+
253
+ # Authorization error.
254
+ rescue AdsCommon::Errors::OAuth2VerificationRequired => e
255
+ puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
256
+ "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
257
+ "to retrieve and store OAuth2 tokens."
258
+ puts "See this wiki page for more details:\n\n " +
259
+ 'http://code.google.com/p/google-api-ads-ruby/wiki/OAuth2'
260
+
261
+ # HTTP errors.
262
+ rescue AdsCommon::Errors::HttpError => e
263
+ puts "HTTP Error: %s" % e
264
+
265
+ # API errors.
266
+ rescue AdwordsApi::Errors::ApiException => e
267
+ puts "Message: %s" % e.message
268
+ puts 'Errors:'
269
+ e.errors.each_with_index do |error, index|
270
+ puts "\tError [%d]:" % (index + 1)
271
+ error.each do |field, value|
272
+ puts "\t\t%s: %s" % [field, value]
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,392 @@
1
+ #!/usr/bin/env ruby
2
+ # Encoding: utf-8
3
+ #
4
+ # Author:: api.mcloonan@gmail.com (Michael Cloonan)
5
+ #
6
+ # Copyright:: Copyright 2015, Google Inc. All Rights Reserved.
7
+ #
8
+ # License:: Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
17
+ # implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ # This example migrates your feed based sitelinks at campaign level to use
22
+ # extension settings. To learn more about extensionsettings, see
23
+ # https://developers.google.com/adwords/api/docs/guides/extension-settings.
24
+ # To learn more about migrating Feed based extensions to extension settings,
25
+ # see
26
+ # https://developers.google.com/adwords/api/docs/guides/migrate-to-extension-settings.
27
+ #
28
+ # Tags: FeedService.query, FeedMappingService.query, FeedItemService.query
29
+ # Tags: CampaignExtensionSettingService.mutate, CampaignFeedService.query
30
+ # Tags: CampaignFeedService.mutate
31
+
32
+ require 'adwords_api'
33
+ require 'set'
34
+
35
+ def migrate_to_extension_settings()
36
+ # AdwordsApi::Api will read a config file from ENV['HOME']/adwords_api.yml
37
+ # when called without parameters.
38
+ adwords = AdwordsApi::Api.new
39
+
40
+ # To enable logging of SOAP requests, set the log_level value to 'DEBUG' in
41
+ # the configuration file or provide your own logger:
42
+ # adwords.logger = Logger.new('adwords_xml.log')
43
+
44
+ # Get all of the feeds for the current user.
45
+ feeds = get_feeds(adwords)
46
+
47
+ feeds.each do |feed|
48
+ # Retrieve all the sitelinks from the current feed.
49
+ feed_items = get_site_links_from_feed(adwords, feed)
50
+
51
+ # Get all the instances where a sitelink from this feed has been added
52
+ # to a campaign.
53
+ campaign_feeds = get_campaign_feeds(adwords, feed, PLACEHOLDER_SITELINKS)
54
+
55
+ all_feed_items_to_delete = campaign_feeds.map do |campaign_feed|
56
+ # Retrieve the sitelinks that have been associated with this campaign.
57
+ feed_item_ids = get_feed_item_ids_for_campaign(campaign_feed)
58
+
59
+ if feed_item_ids.empty?
60
+ puts(("Migration skipped for campaign feed with campaign ID %d " +
61
+ "and feed ID %d because no mapped feed item IDs were found in " +
62
+ "the campaign feed's matching function.") %
63
+ [campaign_feed[:campaign_id], campaign_feed[:feed_id]])
64
+ next
65
+ end
66
+
67
+ platform_restrictions = get_platform_restrictions(campaign_feed)
68
+
69
+ # Delete the campaign feed that associates the sitelinks from the
70
+ # feed to the campaign.
71
+ delete_campaign_feed(adwords, campaign_feed)
72
+
73
+ # Create extension settings instead of sitelinks.
74
+ create_extension_setting(adwords, feed_items, campaign_feed,
75
+ feed_item_ids, platform_restrictions)
76
+
77
+ # Mark the sitelinks from the feed for deletion.
78
+ feed_item_ids
79
+ end.flatten.to_set.reject {|id| id.nil?}
80
+
81
+ # Delete all the sitelinks from the feed.
82
+ delete_old_feed_items(adwords, all_feed_items_to_delete, feed)
83
+ end
84
+ end
85
+
86
+ def get_site_links_from_feed(adwords, feed)
87
+ # Retrieve the feed's attribute mapping.
88
+ feed_mappings = get_feed_mapping(adwords, feed, PLACEHOLDER_SITELINKS)
89
+
90
+ feed_items = {}
91
+
92
+ get_feed_items(adwords, feed).each do |feed_item|
93
+ site_link_from_feed = {}
94
+
95
+ feed_item[:attribute_values].each do |attribute_value|
96
+ # Skip this attribute if it hasn't been mapped to a field.
97
+ next unless feed_mappings.has_key?(
98
+ attribute_value[:feed_attribute_id])
99
+
100
+ feed_mappings[attribute_value[:feed_attribute_id]].each do |field_id|
101
+ case field_id
102
+ when PLACEHOLDER_FIELD_SITELINK_LINK_TEXT
103
+ site_link_from_feed[:text] = attribute_value[:string_value]
104
+ when PLACEHOLDER_FIELD_SITELINK_URL
105
+ site_link_from_feed[:url] = attribute_value[:string_value]
106
+ when PLACEHOLDER_FIELD_FINAL_URLS
107
+ site_link_from_feed[:final_urls] = attribute_value[:string_values]
108
+ when PLACEHOLDER_FIELD_FINAL_MOBILE_URLS
109
+ site_link_from_feed[:final_mobile_urls] =
110
+ attribute_value[:string_values]
111
+ when PLACEHOLDER_FIELD_TRACKING_URL_TEMPLATE
112
+ site_link_from_feed[:tracking_url_template] =
113
+ attribute_value[:string_value]
114
+ when PLACEHOLDER_FIELD_LINE_2_TEXT
115
+ site_link_from_feed[:line2] = attribute_value[:string_value]
116
+ when PLACEHOLDER_FIELD_LINE_3_TEXT
117
+ site_link_from_feed[:line3] = attribute_value[:string_value]
118
+ end
119
+ end
120
+ end
121
+ site_link_from_feed[:scheduling] = feed_item[:scheduling]
122
+
123
+ feed_items[feed_item[:feed_item_id]] = site_link_from_feed
124
+ end
125
+ return feed_items
126
+ end
127
+
128
+ def get_feed_mapping(adwords, feed, placeholder_type)
129
+ feed_mapping_srv = adwords.service(:FeedMappingService, API_VERSION)
130
+ query = ("SELECT FeedMappingId, AttributeFieldMappings " +
131
+ "WHERE FeedId = %d AND PlaceholderType = %d AND Status = 'ENABLED'") %
132
+ [feed[:id], placeholder_type]
133
+
134
+ attribute_mappings = {}
135
+ offset = 0
136
+
137
+ begin
138
+ page_query = (query + " LIMIT %d, %d") % [offset, PAGE_SIZE]
139
+ page = feed_mapping_srv.query(page_query)
140
+
141
+ unless page[:entries].nil?
142
+ # Normally, a feed attribute is mapped only to one field. However, you
143
+ # may map it to more than one field if needed.
144
+ page[:entries].each do |feed_mapping|
145
+ feed_mapping[:attribute_field_mappings].each do |attribute_mapping|
146
+ # Since attribute_mappings can have multiple values for each key,
147
+ # we set up an array to store the values.
148
+ if attribute_mappings.has_key?(attribute_mapping[:feed_attribute_id])
149
+ attribute_mappings[attribute_mapping[:feed_attribute_id]] <<
150
+ attribute_mapping[:field_id]
151
+ else
152
+ attribute_mappings[attribute_mapping[:feed_attribute_id]] =
153
+ [attribute_mapping[:field_id]]
154
+ end
155
+ end
156
+ end
157
+ end
158
+ offset += PAGE_SIZE
159
+ end while page[:total_num_entries] > offset
160
+
161
+ return attribute_mappings
162
+ end
163
+
164
+ def get_feeds(adwords)
165
+ feed_srv = adwords.service(:FeedService, API_VERSION)
166
+ query = "SELECT Id, Name, Attributes " +
167
+ "WHERE Origin = 'USER' AND FeedStatus = 'ENABLED'"
168
+
169
+ feeds = []
170
+ offset = 0
171
+
172
+ begin
173
+ page_query = (query + " LIMIT %d, %d") % [offset, PAGE_SIZE]
174
+ page = feed_srv.query(page_query)
175
+
176
+ unless page[:entries].nil?
177
+ feeds += page[:entries]
178
+ end
179
+ offset += PAGE_SIZE
180
+ end while page[:total_num_entries] > offset
181
+
182
+ return feeds
183
+ end
184
+
185
+ def get_feed_items(adwords, feed)
186
+ feed_item_srv = adwords.service(:FeedItemService, API_VERSION)
187
+ query = ("SELECT FeedItemId, AttributeValues, Scheduling " +
188
+ "WHERE Status = 'ENABLED' AND FeedId = %d") % feed[:id]
189
+
190
+ feed_items = []
191
+ offset = 0
192
+
193
+ begin
194
+ page_query = (query + " LIMIT %d, %d") % [offset, PAGE_SIZE]
195
+ page = feed_item_srv.query(page_query)
196
+
197
+ unless page[:entries].nil?
198
+ feed_items += page[:entries]
199
+ end
200
+ offset += PAGE_SIZE
201
+ end while page[:total_num_entries] > offset
202
+
203
+ return feed_items
204
+ end
205
+
206
+ def get_platform_restrictions(campaign_feed)
207
+ platform_restrictions = nil
208
+
209
+ if campaign_feed[:matching_function][:operator] == 'AND'
210
+ campaign_feed[:matching_function][:lhs_operand].each do |argument|
211
+ # Check if matchingFunction is EQUALS(CONTEXT.DEVICE, 'Mobile')
212
+ if argument[:value][:operator] == 'EQUALS'
213
+ request_context_operand = argument[:value][:lhs_operand].first()
214
+ if request_context_operand &&
215
+ request_context_operand == 'DEVICE_PLATFORM'
216
+ platform_restrictions =
217
+ argument[:value][:rhs_operand].first().upcase()
218
+ break
219
+ end
220
+ end
221
+ end
222
+ end
223
+ return platform_restrictions
224
+ end
225
+
226
+ def delete_old_feed_items(adwords, feed_item_ids, feed)
227
+ return if feed_item_ids.empty?
228
+
229
+ feed_item_srv = adwords.service(:FeedItemService, API_VERSION)
230
+
231
+ operations = feed_item_ids.map do |feed_item_id|
232
+ {
233
+ :operator => 'REMOVE',
234
+ :operand => {
235
+ :feed_id => feed[:id],
236
+ :feed_item_id => feed_item_id
237
+ }
238
+ }
239
+ end
240
+
241
+ feed_item_srv.mutate(operations)
242
+ end
243
+
244
+ def create_extension_setting(
245
+ adwords, feed_items, campaign_feed, feed_item_ids, platform_restrictions)
246
+ campaign_extension_setting_srv = adwords.service(
247
+ :CampaignExtensionSettingService, API_VERSION)
248
+
249
+ extension_feed_items = feed_item_ids.map do |feed_item_id|
250
+ site_link_from_feed = feed_items[:feed_item_id]
251
+ site_link_feed_item = {
252
+ :sitelink_text => site_link_from_feed[:text],
253
+ :sitelink_line2 => site_link_from_feed[:line2],
254
+ :sitelink_line3 => site_link_from_feed[:line3],
255
+ :scheduling => site_link_from_feed[:scheduling]
256
+ }
257
+ if !site_link_from_feed.final_urls.nil? &&
258
+ site_link_from_feed[:final_urls].length > 0
259
+ site_link_feed_item[:sitelink_final_urls] = {
260
+ :urls => site_link_from_feed[:final_urls]
261
+ }
262
+ unless site_link_from_feed[:final_mobile_urls].nil?
263
+ site_link_feed_item[:sitelink_final_mobile_urls] = {
264
+ :urls => site_link_from_feed[:final_mobile_urls]
265
+ }
266
+ end
267
+ site_link_feed_item[:sitelink_tracking_url_template] =
268
+ site_link_from_feed[:tracking_url_template]
269
+ else
270
+ site_link_feed_item[:sitelink_url] = site_link_from_feed[:url]
271
+ end
272
+
273
+ site_link_feed_item
274
+ end
275
+
276
+ extension_setting = {
277
+ :extensions => extension_feed_items
278
+ }
279
+
280
+ unless platform_restrictions.nil?
281
+ extension_setting[:platform_restrictions] = platform_restrictions
282
+ end
283
+
284
+ campaign_extension_setting = {
285
+ :campaign_id => campaign_feed[:campaign_id],
286
+ :extension_type => 'SITELINK',
287
+ :extension_setting => extension_setting
288
+ }
289
+
290
+ operation = {
291
+ :operand => campaign_extension_setting,
292
+ :operator => 'ADD'
293
+ }
294
+
295
+ campaign_extension_setting_srv.mutate([operation])
296
+ end
297
+
298
+ def delete_campaign_feed(adwords, campaign_feed)
299
+ campaign_feed_srv = adwords.service(:CampaignFeedService, API_VERSION)
300
+
301
+ operation = {
302
+ :operand => campaign_feed,
303
+ :operator => 'REMOVE'
304
+ }
305
+
306
+ campaign_feed_srv.mutate([operation])
307
+ end
308
+
309
+ def get_feed_item_ids_for_campaign(campaign_feed)
310
+ feed_item_ids = Set.new
311
+ if !campaign_feed[:matching_function][:lhs_operand].empty? &&
312
+ campaign_feed[:matching_function][:lhs_operand].first[:xsi_type] ==
313
+ 'RequestContextOperand'
314
+ request_context_operand =
315
+ campaign_feed[:matching_function][:lhs_operand].first
316
+ if request_context_operand[:context_type] == 'FEED_ITEM_ID' &&
317
+ campaign_feed[:matching_function][:operator] == 'IN'
318
+ campaign_feed[:matching_function][:rhs_operand].each do |argument|
319
+ if argument[:xsi_type] == 'ConstantOperand'
320
+ feed_item_ids.add(argument[:long_value])
321
+ end
322
+ end
323
+ end
324
+ end
325
+ return feed_item_ids
326
+ end
327
+
328
+ def get_campaign_feeds(adwords, feed, placeholder_type)
329
+ campaign_feed_srv = adwords.service(:CampaignFeedService, API_VERSION)
330
+ query = ("SELECT CampaignId, MatchingFunction, PlaceholderTypes " +
331
+ "WHERE Status = 'ENABLED' AND FeedId = %d " +
332
+ "AND PlaceholderTypes CONTAINS_ANY [%d]") % [feed[:id], placeholder_type]
333
+
334
+ campaign_feeds = []
335
+ offset = 0
336
+
337
+ begin
338
+ page_query = (query + " LIMIT %d, %d") % [offset, PAGE_SIZE]
339
+ page = campaign_feed_srv.query(page_query)
340
+
341
+ unless page[:entries].nil?
342
+ campaign_feeds += page[:entries]
343
+ end
344
+ offset += PAGE_SIZE
345
+ end while page[:total_num_entries] > offset
346
+
347
+ return campaign_feeds
348
+ end
349
+
350
+ if __FILE__ == $0
351
+ API_VERSION = :v201506
352
+ PAGE_SIZE = 500
353
+
354
+ # See the Placeholder reference page for a liste of all placeholder types
355
+ # and fields.
356
+ # https://developers.google.com/adwords/api/docs/appendix/placeholders
357
+ PLACEHOLDER_SITELINKS = 1
358
+ PLACEHOLDER_FIELD_SITELINK_LINK_TEXT = 1
359
+ PLACEHOLDER_FIELD_SITELINK_URL = 2
360
+ PLACEHOLDER_FIELD_LINE_2_TEXT = 3
361
+ PLACEHOLDER_FIELD_LINE_3_TEXT = 4
362
+ PLACEHOLDER_FIELD_FINAL_URLS = 5
363
+ PLACEHOLDER_FIELD_FINAL_MOBILE_URLS = 6
364
+ PLACEHOLDER_FIELD_TRACKING_URL_TEMPLATE = 7
365
+
366
+ begin
367
+ migrate_to_extension_settings()
368
+
369
+ # Authorization error.
370
+ rescue AdsCommon::Errors::OAuth2VerificationRequired => e
371
+ puts "Authorization credentials are not valid. Edit adwords_api.yml for " +
372
+ "OAuth2 client ID and secret and run misc/setup_oauth2.rb example " +
373
+ "to retrieve and store OAuth2 tokens."
374
+ puts "See this wiki page for more details:\n\n " +
375
+ 'http://code.google.com/p/google-api-ads-ruby/wiki/OAuth2'
376
+
377
+ # HTTP errors.
378
+ rescue AdsCommon::Errors::HttpError => e
379
+ puts "HTTP Error: %s" % e
380
+
381
+ # API errors.
382
+ rescue AdwordsApi::Errors::ApiException => e
383
+ puts "Message: %s" % e.message
384
+ puts 'Errors:'
385
+ e.errors.each_with_index do |error, index|
386
+ puts "\tError [%d]:" % (index + 1)
387
+ error.each do |field, value|
388
+ puts "\t\t%s: %s" % [field, value]
389
+ end
390
+ end
391
+ end
392
+ end