class AdwordsApi::BatchJobUtils
Constants
- DEFAULT_HEADERS
- METHODS_BY_OPERATION_TYPE
- REQUIRED_CONTENT_LENGTH_INCREMENT
For incremental uploads, the size (in bytes) of the body of the request must be in multiples of 256k.
- SERVICES_BY_OPERATION_TYPE
- UPLOAD_XML_PREFIX
- UPLOAD_XML_SUFFIX
Public Class Methods
Default constructor.
Args:
-
api:
AdwordsApi
object -
version: API version to use
# File lib/adwords_api/batch_job_utils.rb, line 36 def initialize(api, version) @api, @version = api, version end
Public Instance Methods
Downloads the results of a batch job from the specified URL.
Args:
-
batch_job_url: The URL provided by BatchJobService to fetch the results from
Returns:
-
the results of the batch job, as a ruby hash, or nil if none yet exist
# File lib/adwords_api/batch_job_utils.rb, line 164 def get_job_results(batch_job_url) @api.utils_reporter.batch_job_utils_used() xml_response = AdsCommon::Http.get_response(batch_job_url, @api.config) begin return sanitize_result( get_nori().parse(xml_response.body)[:mutate_response][:rval]) rescue return nil end end
Initializes an upload URL to get the actual URL to which to upload operations.
Args:
-
batch_job_url: The UploadURL provided by BatchJobService
Returns:
-
The URL that should actually be used to upload operations.
# File lib/adwords_api/batch_job_utils.rb, line 85 def initialize_url(batch_job_url) headers = DEFAULT_HEADERS headers['Content-Length'] = 0 headers['x-goog-resumable'] = 'start' response = AdsCommon::Http.post_response( batch_job_url, '', @api.config, headers) return response.headers['Location'] end
Puts the provided operations to the provided URL, allowing for incremental followup puts.
Args:
-
soap_operations: An array including SOAP operations provided by
generate_soap_operations
-
batch_job_url: The UploadURL provided by BatchJobService
-
total_content_length: The total number of bytes already uploaded incrementally. Set this to 0 the first time you call the method.
-
is_last_request: Whether or not this set of uploads will conclude the full request.
Returns:
-
total content length, including what was just uploaded. Pass this back into this method on subsequent calls.
# File lib/adwords_api/batch_job_utils.rb, line 111 def put_incremental_operations( operations, batch_job_url, total_content_length = 0, is_last_request = false) @api.utils_reporter.batch_job_utils_used() headers = DEFAULT_HEADERS soap_operations = generate_soap_operations(operations) request_body = soap_operations.join is_first_request = (total_content_length == 0) if is_first_request request_body = (UPLOAD_XML_PREFIX % [@version]) + request_body end if is_last_request request_body += UPLOAD_XML_SUFFIX end request_body = add_padding(request_body) content_length = request_body.bytesize headers['Content-Length'] = content_length lower_bound = total_content_length upper_bound = total_content_length + content_length - 1 total_bytes = is_last_request ? upper_bound + 1 : '*' content_range = "bytes %d-%d/%s" % [lower_bound, upper_bound, total_bytes] headers['Content-Range'] = content_range log_request(batch_job_url, headers, request_body) # The HTTPI library fails to handle the response when uploading # incremental requests. We're not interested in the response, so just # ignore the error. begin AdsCommon::Http.put_response( batch_job_url, request_body, @api.config, headers) rescue ArgumentError end total_content_length += content_length return total_content_length end
Provides a helper to manage incremental uploads.
Args:
-
batch_job_url: The UploadURL provided by BatchJobService for new jobs,
or the upload_url from IncrementalUploadHelper
for continued jobs.
-
uploaded_bytes: The number of bytes already uploaded for this
incremental batch job. Can be retrieved from the IncrementalUploadHelper
using uploaded_bytes.
Returns:
-
an
IncrementalUploadHelper
that will accept operations and put them,
keeping track of uploaded bytes automatically.
# File lib/adwords_api/batch_job_utils.rb, line 71 def start_incremental_upload(batch_job_url, uploaded_bytes = 0) return AdwordsApi::IncrementalUploadHelper.new( self, uploaded_bytes, batch_job_url) end
Uploads the given operations for a batch job to the provided URL.
Args:
-
hash_operations: An array of ruby has operations to execute by posting them to the provided URL
-
service_name: The name of the
AdwordsApi
service as a symbol that would normally make this request -
batch_job_url: The UploadURL provided by BatchJobService
Raises:
-
InvalidBatchJobOperationError: If there is a problem converting the
given operations to SOAP.
# File lib/adwords_api/batch_job_utils.rb, line 53 def upload_operations(operations, batch_job_url) helper = start_incremental_upload(batch_job_url) helper.upload(operations, true) end
Private Instance Methods
# File lib/adwords_api/batch_job_utils.rb, line 311 def add_padding(xml) remainder = xml.bytesize % REQUIRED_CONTENT_LENGTH_INCREMENT return xml if remainder == 0 bytes_to_add = REQUIRED_CONTENT_LENGTH_INCREMENT - remainder padded_xml = xml + (' ' * bytes_to_add) return padded_xml end
# File lib/adwords_api/batch_job_utils.rb, line 267 def check_xsi_type(xml_node) xsi_type = xml_node['xsi:type'] unless xsi_type.nil? or xsi_type.start_with?('ns1:') xml_node['xsi:type'] = 'ns1:' + xsi_type end children = xml_node.children unless children.empty? children.each do |element| check_xsi_type(element) end end end
Given a full SOAP xml string, extract just the operations element from the SOAP body as a string.
# File lib/adwords_api/batch_job_utils.rb, line 257 def extract_soap_operations(full_soap_xml) doc = Nokogiri::XML(full_soap_xml) operations = doc.css('wsdl|operations') operations.attr('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance') operations.each do |element| check_xsi_type(element) end return operations.to_s end
# File lib/adwords_api/batch_job_utils.rb, line 230 def generate_soap_operations(hash_operations) unless hash_operations.is_a?(Array) raise AdwordsApi::Errors::InvalidBatchJobOperationError, 'Operations must be in an array.' end return hash_operations.map do |operation| operation_type = operation[:xsi_type] if operation_type.nil? raise AdwordsApi::Errors::InvalidBatchJobOperationError, ':xsi_type for operations must be defined ' + 'explicitly for batch jobs.' end service_name = SERVICES_BY_OPERATION_TYPE[operation_type] if service_name.nil? raise AdwordsApi::Errors::InvalidBatchJobOperationError, 'Unknown operation type: %s' % operation_type end method_name = METHODS_BY_OPERATION_TYPE[operation_type] service = @api.service(service_name, @version) full_soap_xml = service.send(method_name, [operation]) operation_xml = extract_soap_operations(full_soap_xml) operation_xml end end
# File lib/adwords_api/batch_job_utils.rb, line 319 def get_nori() return @nori if @nori nori_options = { :strip_namespaces => true, :convert_tags_to => lambda { |tag| tag.snakecase.to_sym }, :empty_tag_value => "", :advanced_typecasting => false } @nori = Nori.new(nori_options) return @nori end
Logs the request on debug level.
# File lib/adwords_api/batch_job_utils.rb, line 281 def log_request(url, headers, body) logger = @api.logger logger.debug("Report request to: '%s'" % url) logger.debug('HTTP headers: [%s]' % (headers.map { |k, v| [k, v].join(': ') }.join(', '))) logger.debug(body) end
Removes extraneous XML information from return hash.
# File lib/adwords_api/batch_job_utils.rb, line 290 def sanitize_result(results) if results.is_a?(Array) ret = [] results.each do |result| ret << sanitize_result(result) end return ret end if results.is_a?(Hash) ret = {} results.each do |k, v| v = sanitize_result(v) if v.is_a?(Hash) ret[k] = v unless k.to_s.start_with?('@') end return ret end return results end