google-adwords-api 0.17.0 → 0.18.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/ChangeLog +5 -0
- data/README.md +3 -3
- data/adwords_api.yml +3 -0
- data/examples/v201509/{campaign_management → advanced_operations}/add_complete_campaigns_using_batch_job.rb +10 -16
- data/examples/v201509/advanced_operations/add_keywords_using_incremental_batch_job.rb +179 -0
- data/examples/v201509/reporting/stream_criteria_report_results.rb +97 -0
- data/lib/adwords_api/api_config.rb +0 -86
- data/lib/adwords_api/batch_job_utils.rb +124 -51
- data/lib/adwords_api/incremental_upload_helper.rb +71 -0
- data/lib/adwords_api/report_header_handler.rb +1 -1
- data/lib/adwords_api/report_stream.rb +64 -0
- data/lib/adwords_api/report_utils.rb +88 -8
- data/lib/adwords_api/version.rb +1 -1
- data/test/adwords_api/test_batch_job_utils.rb +15 -0
- metadata +18 -168
- data/examples/v201502/account_management/create_account.rb +0 -88
- data/examples/v201502/account_management/get_account_changes.rb +0 -139
- data/examples/v201502/account_management/get_account_hierarchy.rb +0 -94
- data/examples/v201502/advanced_operations/add_ad_customizers.rb +0 -184
- data/examples/v201502/advanced_operations/add_ad_group_bid_modifier.rb +0 -101
- data/examples/v201502/advanced_operations/add_click_to_download_ad.rb +0 -133
- data/examples/v201502/advanced_operations/add_text_ad_with_upgraded_urls.rb +0 -134
- data/examples/v201502/advanced_operations/create_and_attach_shared_keyword_set.rb +0 -133
- data/examples/v201502/advanced_operations/find_and_remove_criteria_from_shared_set.rb +0 -166
- data/examples/v201502/advanced_operations/get_ad_group_bid_modifiers.rb +0 -102
- data/examples/v201502/advanced_operations/upload_offline_conversions.rb +0 -113
- data/examples/v201502/advanced_operations/use_shared_bidding_strategy.rb +0 -147
- data/examples/v201502/basic_operations/add_ad_groups.rb +0 -140
- data/examples/v201502/basic_operations/add_campaigns.rb +0 -139
- data/examples/v201502/basic_operations/add_keywords.rb +0 -114
- data/examples/v201502/basic_operations/add_text_ads.rb +0 -109
- data/examples/v201502/basic_operations/get_ad_groups.rb +0 -102
- data/examples/v201502/basic_operations/get_campaigns.rb +0 -97
- data/examples/v201502/basic_operations/get_campaigns_with_awql.rb +0 -89
- data/examples/v201502/basic_operations/get_keywords.rb +0 -108
- data/examples/v201502/basic_operations/get_text_ads.rb +0 -110
- data/examples/v201502/basic_operations/pause_ad.rb +0 -88
- data/examples/v201502/basic_operations/remove_ad.rb +0 -89
- data/examples/v201502/basic_operations/remove_ad_group.rb +0 -85
- data/examples/v201502/basic_operations/remove_campaign.rb +0 -87
- data/examples/v201502/basic_operations/remove_keyword.rb +0 -94
- data/examples/v201502/basic_operations/update_ad_group.rb +0 -85
- data/examples/v201502/basic_operations/update_campaign.rb +0 -86
- data/examples/v201502/basic_operations/update_keyword.rb +0 -106
- data/examples/v201502/campaign_management/add_campaign_labels.rb +0 -82
- data/examples/v201502/campaign_management/add_experiment.rb +0 -162
- data/examples/v201502/campaign_management/add_keywords_in_bulk.rb +0 -153
- data/examples/v201502/campaign_management/get_all_disapproved_ads.rb +0 -97
- data/examples/v201502/campaign_management/get_all_disapproved_ads_with_awql.rb +0 -89
- data/examples/v201502/campaign_management/get_campaigns_by_label.rb +0 -108
- data/examples/v201502/campaign_management/promote_experiment.rb +0 -81
- data/examples/v201502/campaign_management/set_ad_parameters.rb +0 -118
- data/examples/v201502/campaign_management/set_criterion_bid_modifier.rb +0 -104
- data/examples/v201502/campaign_management/validate_text_ad.rb +0 -110
- data/examples/v201502/error_handling/handle_partial_failures.rb +0 -130
- data/examples/v201502/error_handling/handle_policy_violation_error.rb +0 -141
- data/examples/v201502/extensions/add_google_my_business_location_extensions.rb +0 -179
- data/examples/v201502/extensions/add_site_links.rb +0 -164
- data/examples/v201502/extensions/add_site_links_using_feeds.rb +0 -271
- data/examples/v201502/migration/migrate_to_extension_settings.rb +0 -386
- data/examples/v201502/migration/upgrade_ad_url.rb +0 -93
- data/examples/v201502/misc/create_ad_words_session_without_properties_file.rb +0 -92
- data/examples/v201502/misc/get_all_images_and_videos.rb +0 -104
- data/examples/v201502/misc/setup_oauth2.rb +0 -84
- data/examples/v201502/misc/upload_image.rb +0 -93
- data/examples/v201502/misc/use_oauth2_jwt.rb +0 -93
- data/examples/v201502/optimization/estimate_keyword_traffic.rb +0 -146
- data/examples/v201502/optimization/get_keyword_bid_simulations.rb +0 -95
- data/examples/v201502/optimization/get_keyword_ideas.rb +0 -126
- data/examples/v201502/remarketing/add_audience.rb +0 -118
- data/examples/v201502/remarketing/add_conversion_tracker.rb +0 -100
- data/examples/v201502/remarketing/add_rule_based_user_lists.rb +0 -167
- data/examples/v201502/reporting/download_criteria_report.rb +0 -85
- data/examples/v201502/reporting/download_criteria_report_with_awql.rb +0 -84
- data/examples/v201502/reporting/get_report_fields.rb +0 -75
- data/examples/v201502/reporting/parallel_report_download.rb +0 -166
- data/examples/v201502/shopping_campaigns/add_product_partition_tree.rb +0 -267
- data/examples/v201502/shopping_campaigns/add_product_scope.rb +0 -129
- data/examples/v201502/shopping_campaigns/add_shopping_campaign.rb +0 -129
- data/examples/v201502/shopping_campaigns/get_product_category_taxonomy.rb +0 -115
- data/examples/v201502/targeting/add_campaign_targeting_criteria.rb +0 -169
- data/examples/v201502/targeting/add_demographic_targeting_criteria.rb +0 -112
- data/examples/v201502/targeting/get_campaign_targeting_criteria.rb +0 -106
- data/examples/v201502/targeting/get_targetable_languages_and_carriers.rb +0 -89
- data/examples/v201502/targeting/lookup_location.rb +0 -108
- data/lib/adwords_api/v201502/account_label_service.rb +0 -46
- data/lib/adwords_api/v201502/account_label_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_customizer_feed_service.rb +0 -46
- data/lib/adwords_api/v201502/ad_customizer_feed_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_group_ad_service.rb +0 -70
- data/lib/adwords_api/v201502/ad_group_ad_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_group_bid_modifier_service.rb +0 -54
- data/lib/adwords_api/v201502/ad_group_bid_modifier_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_group_criterion_service.rb +0 -62
- data/lib/adwords_api/v201502/ad_group_criterion_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_group_extension_setting_service.rb +0 -54
- data/lib/adwords_api/v201502/ad_group_extension_setting_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_group_feed_service.rb +0 -54
- data/lib/adwords_api/v201502/ad_group_feed_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_group_service.rb +0 -62
- data/lib/adwords_api/v201502/ad_group_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/ad_param_service.rb +0 -46
- data/lib/adwords_api/v201502/ad_param_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/adwords_user_list_service.rb +0 -46
- data/lib/adwords_api/v201502/adwords_user_list_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/bidding_strategy_service.rb +0 -54
- data/lib/adwords_api/v201502/bidding_strategy_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/budget_order_service.rb +0 -54
- data/lib/adwords_api/v201502/budget_order_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/budget_service.rb +0 -54
- data/lib/adwords_api/v201502/budget_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/campaign_criterion_service.rb +0 -54
- data/lib/adwords_api/v201502/campaign_criterion_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/campaign_extension_setting_service.rb +0 -54
- data/lib/adwords_api/v201502/campaign_extension_setting_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/campaign_feed_service.rb +0 -54
- data/lib/adwords_api/v201502/campaign_feed_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/campaign_service.rb +0 -62
- data/lib/adwords_api/v201502/campaign_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/campaign_shared_set_service.rb +0 -46
- data/lib/adwords_api/v201502/campaign_shared_set_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/constant_data_service.rb +0 -102
- data/lib/adwords_api/v201502/constant_data_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/conversion_tracker_service.rb +0 -54
- data/lib/adwords_api/v201502/conversion_tracker_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/customer_extension_setting_service.rb +0 -54
- data/lib/adwords_api/v201502/customer_extension_setting_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/customer_feed_service.rb +0 -54
- data/lib/adwords_api/v201502/customer_feed_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/customer_service.rb +0 -46
- data/lib/adwords_api/v201502/customer_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/customer_sync_service.rb +0 -38
- data/lib/adwords_api/v201502/customer_sync_service_registry.rb +0 -47
- data/lib/adwords_api/v201502/data_service.rb +0 -78
- data/lib/adwords_api/v201502/data_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/experiment_service.rb +0 -46
- data/lib/adwords_api/v201502/experiment_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/feed_item_service.rb +0 -54
- data/lib/adwords_api/v201502/feed_item_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/feed_mapping_service.rb +0 -54
- data/lib/adwords_api/v201502/feed_mapping_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/feed_service.rb +0 -54
- data/lib/adwords_api/v201502/feed_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/geo_location_service.rb +0 -38
- data/lib/adwords_api/v201502/geo_location_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/label_service.rb +0 -54
- data/lib/adwords_api/v201502/label_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/location_criterion_service.rb +0 -46
- data/lib/adwords_api/v201502/location_criterion_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/managed_customer_service.rb +0 -78
- data/lib/adwords_api/v201502/managed_customer_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/media_service.rb +0 -54
- data/lib/adwords_api/v201502/media_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/mutate_job_service.rb +0 -54
- data/lib/adwords_api/v201502/mutate_job_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/offline_conversion_feed_service.rb +0 -38
- data/lib/adwords_api/v201502/offline_conversion_feed_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/report_definition_service.rb +0 -38
- data/lib/adwords_api/v201502/report_definition_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/shared_criterion_service.rb +0 -46
- data/lib/adwords_api/v201502/shared_criterion_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/shared_set_service.rb +0 -46
- data/lib/adwords_api/v201502/shared_set_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/targeting_idea_service.rb +0 -38
- data/lib/adwords_api/v201502/targeting_idea_service_registry.rb +0 -46
- data/lib/adwords_api/v201502/traffic_estimator_service.rb +0 -38
- data/lib/adwords_api/v201502/traffic_estimator_service_registry.rb +0 -46
- data/test/templates/v201502/basic_operations_get_campaigns.def +0 -116
- data/test/templates/v201502/misc_use_oauth2_service_account.def +0 -131
@@ -23,6 +23,7 @@ require 'nori'
|
|
23
23
|
require 'ads_common/http'
|
24
24
|
require 'ads_common/savon_service'
|
25
25
|
require 'adwords_api/errors'
|
26
|
+
require 'adwords_api/incremental_upload_helper'
|
26
27
|
|
27
28
|
module AdwordsApi
|
28
29
|
class BatchJobUtils
|
@@ -36,73 +37,99 @@ module AdwordsApi
|
|
36
37
|
@api, @version = api, version
|
37
38
|
end
|
38
39
|
|
39
|
-
#
|
40
|
-
# as a normal request.
|
40
|
+
# Uploads the given operations for a batch job to the provided URL.
|
41
41
|
#
|
42
42
|
# Args:
|
43
|
-
# - hash_operations: An array of ruby
|
43
|
+
# - hash_operations: An array of ruby has operations to execute by
|
44
|
+
# posting them to the provided URL
|
44
45
|
# - service_name: The name of the AdwordsApi service as a symbol that would
|
45
46
|
# normally make this request
|
47
|
+
# - batch_job_url: The URL provided by BatchjobService to post the provided
|
48
|
+
# operations to
|
49
|
+
#
|
50
|
+
# Raises:
|
51
|
+
# - InvalidBatchJobOperationError: If there is a problem converting the
|
52
|
+
# given operations to SOAP.
|
53
|
+
#
|
54
|
+
def upload_operations(operations, batch_job_url)
|
55
|
+
soap_operations = generate_soap_operations(operations)
|
56
|
+
post_soap_operations(soap_operations, batch_job_url)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Provides a helper to manage incremental uploads.
|
60
|
+
#
|
61
|
+
# Args:
|
62
|
+
# - batch_job_url: The URL provided by BatchJobService to put the provided
|
63
|
+
# operations to.
|
64
|
+
# - uploaded_bytes: The number of bytes already uploaded for this
|
65
|
+
# incremental batch job. Can be retrieved from the IncrementalUploadHelper
|
66
|
+
# using uploaded_bytes.
|
46
67
|
#
|
47
68
|
# Returns:
|
48
|
-
# -
|
69
|
+
# - an IncrementalUploadHelper that will accept operations and put them,
|
70
|
+
# keeping track of uploaded bytes automatically.
|
49
71
|
#
|
50
|
-
def
|
51
|
-
|
52
|
-
|
53
|
-
'Operations must be in an array.'
|
54
|
-
end
|
55
|
-
return hash_operations.map do |operation|
|
56
|
-
operation_type = operation[:xsi_type]
|
57
|
-
if operation_type.nil?
|
58
|
-
raise AdwordsApi::Errors::InvalidBatchJobOperationError,
|
59
|
-
':xsi_type for operations must be defined ' +
|
60
|
-
'explicitly for batch jobs.'
|
61
|
-
end
|
62
|
-
service_name = SERVICES_BY_OPERATION_TYPE[operation_type]
|
63
|
-
if service_name.nil?
|
64
|
-
raise AdwordsApi::Errors::InvalidBatchJobOperationError,
|
65
|
-
'Unknown operation type: %s' % operation_type
|
66
|
-
end
|
67
|
-
method_name = METHODS_BY_OPERATION_TYPE[operation_type]
|
68
|
-
service = @api.service(service_name, @version)
|
69
|
-
full_soap_xml = service.send(method_name, [operation])
|
70
|
-
operation_xml = extract_soap_operations(full_soap_xml)
|
71
|
-
operation_xml
|
72
|
-
end
|
72
|
+
def start_incremental_upload(batch_job_url, uploaded_bytes = 0)
|
73
|
+
return AdwordsApi::IncrementalUploadHelper.new(
|
74
|
+
self, uploaded_bytes, batch_job_url)
|
73
75
|
end
|
74
76
|
|
75
|
-
#
|
77
|
+
# Puts the provided operations to the provided URL, allowing
|
78
|
+
# for incremental followup puts.
|
76
79
|
#
|
77
80
|
# Args:
|
78
81
|
# - soap_operations: An array including SOAP operations provided by
|
79
82
|
# generate_soap_operations
|
80
83
|
# - batch_job_url: The URL provided by BatchJobService to post the provided
|
81
84
|
# operations to
|
85
|
+
# - total_content_length: The total number of bytes already uploaded
|
86
|
+
# incrementally. Set this to 0 the first time you call the method.
|
87
|
+
# - is_last_request: Whether or not this set of uploads will conclude the
|
88
|
+
# full request.
|
82
89
|
#
|
83
|
-
|
90
|
+
# Returns:
|
91
|
+
# - total content length, including what was just uploaded. Pass this back
|
92
|
+
# into this method on subsequent calls.
|
93
|
+
def put_incremental_operations(
|
94
|
+
operations, batch_job_url, total_content_length = 0,
|
95
|
+
is_last_request = false)
|
84
96
|
headers = DEFAULT_HEADERS
|
85
|
-
|
97
|
+
soap_operations = generate_soap_operations(operations)
|
98
|
+
request_body = soap_operations.join
|
99
|
+
is_first_request = (total_content_length == 0)
|
100
|
+
|
101
|
+
if is_first_request
|
102
|
+
request_body = (UPLOAD_XML_PREFIX % [@version]) + request_body
|
103
|
+
end
|
104
|
+
if is_last_request
|
105
|
+
request_body += UPLOAD_XML_SUFFIX
|
106
|
+
end
|
107
|
+
|
108
|
+
request_body = add_padding(request_body)
|
109
|
+
content_length = request_body.size
|
110
|
+
|
111
|
+
headers['Content-Length'] = content_length.to_s
|
112
|
+
|
113
|
+
lower_bound = total_content_length
|
114
|
+
upper_bound = total_content_length + content_length - 1
|
115
|
+
total_bytes = is_last_request ? upper_bound + 1 : '*'
|
116
|
+
content_range = "bytes %d-%d/%s" %
|
117
|
+
[lower_bound, upper_bound, total_bytes]
|
118
|
+
headers['Content-Range'] = content_range
|
119
|
+
|
86
120
|
log_request(batch_job_url, headers, request_body)
|
87
|
-
response = AdsCommon::Http.post_response(
|
88
|
-
batch_job_url, request_body, @api.config, headers)
|
89
|
-
end
|
90
121
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
#
|
103
|
-
def execute_hash_operations(hash_operations, batch_job_url)
|
104
|
-
soap_operations = generate_soap_operations(hash_operations)
|
105
|
-
post_soap_operations(soap_operations, batch_job_url)
|
122
|
+
# The HTTPI library fails to handle the response when uploading
|
123
|
+
# incremental requests. We're not interested in the response, so just
|
124
|
+
# ignore the error.
|
125
|
+
begin
|
126
|
+
AdsCommon::Http.put_response(
|
127
|
+
batch_job_url, request_body, @api.config, headers)
|
128
|
+
rescue ArgumentError
|
129
|
+
end
|
130
|
+
|
131
|
+
total_content_length += content_length
|
132
|
+
return total_content_length
|
106
133
|
end
|
107
134
|
|
108
135
|
# Downloads the results of a batch job from the specified URL.
|
@@ -126,9 +153,13 @@ module AdwordsApi
|
|
126
153
|
|
127
154
|
private
|
128
155
|
|
129
|
-
|
130
|
-
|
131
|
-
|
156
|
+
# For incremental uploads, the size (in bytes) of the body of the request
|
157
|
+
# must be in multiples of 256k.
|
158
|
+
REQUIRED_CONTENT_LENGTH_INCREMENT = 256 * 1024
|
159
|
+
|
160
|
+
UPLOAD_XML_PREFIX = '<?xml version="1.0" encoding="UTF-8"?><ns1:mutate ' +
|
161
|
+
'xmlns:ns1="https://adwords.google.com/api/adwords/cm/%s">'
|
162
|
+
UPLOAD_XML_SUFFIX = '</ns1:mutate>'
|
132
163
|
DEFAULT_HEADERS = {"Content-Type" => "application/xml"}
|
133
164
|
|
134
165
|
SERVICES_BY_OPERATION_TYPE = {
|
@@ -161,6 +192,40 @@ module AdwordsApi
|
|
161
192
|
'FeedItemOperation' => 'mutate_to_xml'
|
162
193
|
}
|
163
194
|
|
195
|
+
def generate_soap_operations(hash_operations)
|
196
|
+
unless hash_operations.is_a?(Array)
|
197
|
+
raise AdwordsApi::Errors::InvalidBatchJobOperationError,
|
198
|
+
'Operations must be in an array.'
|
199
|
+
end
|
200
|
+
return hash_operations.map do |operation|
|
201
|
+
operation_type = operation[:xsi_type]
|
202
|
+
if operation_type.nil?
|
203
|
+
raise AdwordsApi::Errors::InvalidBatchJobOperationError,
|
204
|
+
':xsi_type for operations must be defined ' +
|
205
|
+
'explicitly for batch jobs.'
|
206
|
+
end
|
207
|
+
service_name = SERVICES_BY_OPERATION_TYPE[operation_type]
|
208
|
+
if service_name.nil?
|
209
|
+
raise AdwordsApi::Errors::InvalidBatchJobOperationError,
|
210
|
+
'Unknown operation type: %s' % operation_type
|
211
|
+
end
|
212
|
+
method_name = METHODS_BY_OPERATION_TYPE[operation_type]
|
213
|
+
service = @api.service(service_name, @version)
|
214
|
+
full_soap_xml = service.send(method_name, [operation])
|
215
|
+
operation_xml = extract_soap_operations(full_soap_xml)
|
216
|
+
operation_xml
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def post_soap_operations(soap_operations, batch_job_url)
|
221
|
+
headers = DEFAULT_HEADERS
|
222
|
+
request_body = (UPLOAD_XML_PREFIX % [@version]) + soap_operations.join +
|
223
|
+
UPLOAD_XML_SUFFIX
|
224
|
+
log_request(batch_job_url, headers, request_body)
|
225
|
+
response = AdsCommon::Http.post_response(
|
226
|
+
batch_job_url, request_body, @api.config, headers)
|
227
|
+
end
|
228
|
+
|
164
229
|
# Given a full SOAP xml string, extract just the operations element
|
165
230
|
# from the SOAP body as a string.
|
166
231
|
def extract_soap_operations(full_soap_xml)
|
@@ -217,6 +282,14 @@ module AdwordsApi
|
|
217
282
|
return results
|
218
283
|
end
|
219
284
|
|
285
|
+
def add_padding(xml)
|
286
|
+
remainder = xml.size % REQUIRED_CONTENT_LENGTH_INCREMENT
|
287
|
+
return xml if remainder == 0
|
288
|
+
bytes_to_add = REQUIRED_CONTENT_LENGTH_INCREMENT - remainder
|
289
|
+
padded_xml = xml + (' ' * bytes_to_add)
|
290
|
+
return padded_xml
|
291
|
+
end
|
292
|
+
|
220
293
|
def get_nori()
|
221
294
|
return @nori if @nori
|
222
295
|
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright:: Copyright 2015, Google Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
14
|
+
# implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
# Contains helper methods to allow incremental uploads with the
|
19
|
+
# BatchJobService.
|
20
|
+
|
21
|
+
module AdwordsApi
|
22
|
+
class IncrementalUploadHelper
|
23
|
+
attr_reader :upload_url, :uploaded_bytes
|
24
|
+
|
25
|
+
# Default constructor.
|
26
|
+
#
|
27
|
+
# Args:
|
28
|
+
# - batch_job_service: The instance of BatchJobService that is providing
|
29
|
+
# this helper
|
30
|
+
# - uploaded_bytes: The number of bytes that have already been uploaded
|
31
|
+
# as part of this incremental process.
|
32
|
+
# - upload_url: The URL that should be used to upload incremental
|
33
|
+
# operations for this job.
|
34
|
+
#
|
35
|
+
def initialize(batch_job_utils, uploaded_bytes, upload_url)
|
36
|
+
@batch_job_utils = batch_job_utils
|
37
|
+
@uploaded_bytes = uploaded_bytes
|
38
|
+
@upload_url = upload_url
|
39
|
+
@finished = false
|
40
|
+
end
|
41
|
+
|
42
|
+
# Takes an array of operations and puts it to the batch job incrementally.
|
43
|
+
#
|
44
|
+
# Args:
|
45
|
+
# - hash_operations: An array of operations to put, represented in hashes
|
46
|
+
# like you would normally pass to services.
|
47
|
+
# - is_last_request: Whether this request is the last request of the
|
48
|
+
# incremental job.
|
49
|
+
#
|
50
|
+
# Raises:
|
51
|
+
# - InvalidBatchJobOperationError: If this incremental upload is already
|
52
|
+
# finished or if there is an error converting the hash operations to
|
53
|
+
# soap operations.
|
54
|
+
#
|
55
|
+
def upload(operations, is_last_request = false)
|
56
|
+
check_status()
|
57
|
+
@uploaded_bytes = @batch_job_utils.put_incremental_operations(
|
58
|
+
operations, @upload_url, @uploaded_bytes, is_last_request)
|
59
|
+
@finished = true if is_last_request
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def check_status()
|
65
|
+
if @finished
|
66
|
+
raise AdwordsApi::Errors::InvalidBatchJobOperationError,
|
67
|
+
'Cannot put new operations to completed incremental upload.'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -67,7 +67,7 @@ module AdwordsApi
|
|
67
67
|
include_zero_impressions_header =
|
68
68
|
@config.read('library.include_zero_impressions_header')
|
69
69
|
unless include_zero_impressions_header.nil?
|
70
|
-
headers['
|
70
|
+
headers['includeZeroImpressions'] = include_zero_impressions_header.to_s
|
71
71
|
end
|
72
72
|
return headers
|
73
73
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
#
|
3
|
+
# Copyright:: Copyright 2015, Google Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# License:: Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
14
|
+
# implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
# Includes a way to iterate over a report stream by line, rather than in
|
19
|
+
# arbitrary chunks.
|
20
|
+
|
21
|
+
module AdwordsApi
|
22
|
+
class ReportStream
|
23
|
+
def initialize(report_utils, report_definition, cid = nil, format = nil,
|
24
|
+
is_awql = false, &block)
|
25
|
+
@report_utils = report_utils
|
26
|
+
@report_definition = report_definition
|
27
|
+
@format = format
|
28
|
+
@cid = cid
|
29
|
+
@block = block
|
30
|
+
@is_awql = is_awql
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.set_up(report_utils, report_definition, cid = nil, &block)
|
34
|
+
return ReportStream.new(report_utils, report_definition, cid, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.set_up_with_awql(
|
38
|
+
report_utils, report_query, format, cid = nil, &block)
|
39
|
+
return ReportStream.new(
|
40
|
+
report_utils, report_query, cid, format, true, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
def each_line()
|
44
|
+
# Determine which method and arguments to use depending on whether
|
45
|
+
# this is an AWQL request or not.
|
46
|
+
method = 'download_report_as_stream'
|
47
|
+
method += '_with_awql' if @is_awql
|
48
|
+
args = [@report_definition]
|
49
|
+
args << @format if @is_awql
|
50
|
+
args << @cid
|
51
|
+
|
52
|
+
leftover_data = ''
|
53
|
+
@report_utils.send(method, *args) do |batch|
|
54
|
+
data_to_process = leftover_data + batch
|
55
|
+
lines = data_to_process.split('\n')
|
56
|
+
leftover_data = lines.pop
|
57
|
+
lines.each do |line|
|
58
|
+
@block.call(line)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
@block.call(leftover_data) unless leftover_data.empty?()
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -24,6 +24,7 @@ require 'nori'
|
|
24
24
|
require 'ads_common/http'
|
25
25
|
require 'adwords_api/errors'
|
26
26
|
require 'adwords_api/report_header_handler'
|
27
|
+
require 'adwords_api/report_stream'
|
27
28
|
|
28
29
|
module AdwordsApi
|
29
30
|
class ReportUtils
|
@@ -76,6 +77,45 @@ module AdwordsApi
|
|
76
77
|
return nil
|
77
78
|
end
|
78
79
|
|
80
|
+
# Streams a report as a string to the given block. This method will not do
|
81
|
+
# error checking on returned values.
|
82
|
+
#
|
83
|
+
# Args:
|
84
|
+
# - report_definition: definition of the report in XML text or hash
|
85
|
+
# - path: path to save report to
|
86
|
+
# - cid: optional customer ID to run against
|
87
|
+
#
|
88
|
+
# Returns:
|
89
|
+
# - nil
|
90
|
+
#
|
91
|
+
# Raises:
|
92
|
+
# - AdwordsApi::Errors::InvalidReportDefinitionError if the report
|
93
|
+
# definition is invalid
|
94
|
+
#
|
95
|
+
def download_report_as_stream(report_definition, cid = nil, &block)
|
96
|
+
return get_report_response(report_definition, cid, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Returns a helper object that can manage breaking the streamed report
|
100
|
+
# results into individual lines.
|
101
|
+
#
|
102
|
+
# Args:
|
103
|
+
# - report_definition: definition of the report in XML text or hash
|
104
|
+
# - path: path to save report to
|
105
|
+
# - cid: optional customer ID to run against
|
106
|
+
#
|
107
|
+
# Returns:
|
108
|
+
# - ReportStream object initialized to begin streaming.
|
109
|
+
#
|
110
|
+
# Raises:
|
111
|
+
# - AdwordsApi::Errors::InvalidReportDefinitionError if the report
|
112
|
+
# definition is invalid
|
113
|
+
#
|
114
|
+
def get_stream_helper(report_definition, cid = nil, &block)
|
115
|
+
return AdwordsApi::ReportStream.set_up(
|
116
|
+
self, report_definition, cid, &block)
|
117
|
+
end
|
118
|
+
|
79
119
|
# Downloads and returns a report with AWQL.
|
80
120
|
#
|
81
121
|
# Args:
|
@@ -113,6 +153,38 @@ module AdwordsApi
|
|
113
153
|
return nil
|
114
154
|
end
|
115
155
|
|
156
|
+
# Streams a report with AWQL as a string to the given block. This method
|
157
|
+
# will not do error checking on returned values.
|
158
|
+
#
|
159
|
+
# Args:
|
160
|
+
# - report_query: query for the report as string
|
161
|
+
# - format: format for the report as string
|
162
|
+
# - cid: optional customer ID to run report against
|
163
|
+
#
|
164
|
+
# Returns:
|
165
|
+
# - nil
|
166
|
+
#
|
167
|
+
def download_report_as_stream_with_awql(
|
168
|
+
report_query, format, cid = nil, &block)
|
169
|
+
return get_report_response_with_awql(report_query, format, cid, &block)
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns a helper object that can manage breaking the streamed report
|
173
|
+
# results into individual lines.
|
174
|
+
#
|
175
|
+
# Args:
|
176
|
+
# - report_query: query for the report as string
|
177
|
+
# - format: format for the report as string
|
178
|
+
# - cid: optional customer ID to run report against
|
179
|
+
#
|
180
|
+
# Returns:
|
181
|
+
# - ReportStream object initialized to begin streaming.
|
182
|
+
#
|
183
|
+
def get_stream_helper_with_awql(report_query, format, cid = nil, &block)
|
184
|
+
return AdwordsApi::ReportStream.set_up_with_awql(
|
185
|
+
self, report_query, format, cid, &block)
|
186
|
+
end
|
187
|
+
|
116
188
|
private
|
117
189
|
|
118
190
|
# Minimal set of required fields for report definition.
|
@@ -131,28 +203,36 @@ module AdwordsApi
|
|
131
203
|
}
|
132
204
|
|
133
205
|
# Send POST request for a report and returns Response object.
|
134
|
-
def get_report_response(report_definition, cid)
|
206
|
+
def get_report_response(report_definition, cid, &block)
|
135
207
|
definition_text = get_report_definition_text(report_definition)
|
136
208
|
data = '__rdxml=%s' % CGI.escape(definition_text)
|
137
|
-
return make_adhoc_request(data, cid)
|
209
|
+
return make_adhoc_request(data, cid, &block)
|
138
210
|
end
|
139
211
|
|
140
212
|
# Send POST request for a report with AWQL and returns Response object.
|
141
|
-
def get_report_response_with_awql(report_query, format, cid)
|
213
|
+
def get_report_response_with_awql(report_query, format, cid, &block)
|
142
214
|
data = '__rdquery=%s&__fmt=%s' %
|
143
215
|
[CGI.escape(report_query), CGI.escape(format)]
|
144
|
-
return make_adhoc_request(data, cid)
|
216
|
+
return make_adhoc_request(data, cid, &block)
|
145
217
|
end
|
146
218
|
|
147
219
|
# Makes request and AdHoc service and returns response.
|
148
|
-
def make_adhoc_request(data, cid)
|
220
|
+
def make_adhoc_request(data, cid, &block)
|
149
221
|
url = @api.api_config.adhoc_report_download_url(
|
150
222
|
@api.config.read('service.environment'), @version)
|
151
223
|
headers = get_report_request_headers(url, cid)
|
152
224
|
log_request(url, headers, data)
|
153
|
-
|
154
|
-
|
155
|
-
|
225
|
+
# A given block indicates that we should make a stream request and yield
|
226
|
+
# the results, rather than return a full response.
|
227
|
+
if block_given?
|
228
|
+
AdsCommon::Http.post_stream(url, data, @api.config, headers, &block)
|
229
|
+
return nil
|
230
|
+
else
|
231
|
+
response = AdsCommon::Http.post_response(
|
232
|
+
url, data, @api.config, headers)
|
233
|
+
check_for_errors(response)
|
234
|
+
return response
|
235
|
+
end
|
156
236
|
end
|
157
237
|
|
158
238
|
# Converts passed object to XML text. Currently support String (no changes)
|