def __MakeRequest(self, url, headers=None, fileobj=None, payload=None): """Performs an HTTPS request and slightly processes the response. If fileobj is provided, saves the body to file instead of including it in the return value. Args: url: str Resource for the request line. headers: dict Headers to send along with the request. fileobj: file File to save to (optional). payload: str Xml to POST (optional). Returns: str Report data as a string if fileobj=None, otherwise None """ headers = headers or {} request_url = self._op_config['server'] + url orig_payload = payload if Utils.BoolTypeConvert(self._config['compress']): buffer = StringIO.StringIO() gzip_file = gzip.GzipFile(mode='wb', fileobj=buffer) gzip_file.write(payload) gzip_file.close() payload = buffer.getvalue() headers['Content-Length'] = str(len(payload)) start_time = time.strftime('%Y-%m-%d %H:%M:%S') request = urllib2.Request(request_url, payload, headers) try: try: response = urllib2.urlopen(request) response_code = response.code response_headers = response.info().headers if response.info().get('Content-Encoding') == 'gzip': response = gzip.GzipFile(fileobj=StringIO.StringIO( response.read()), mode='rb') if fileobj: self.__DumpToFile(response, fileobj) return None else: return response.read() except urllib2.HTTPError, e: response = e response_code = response.code response_headers = response.info().headers if response.info().get('Content-Encoding') == 'gzip': response = gzip.GzipFile(fileobj=StringIO.StringIO( response.read()), mode='rb') error = response.read() self.__CheckForXmlError(response_code, error) raise AdWordsError('%s %s' % (str(e), error)) except urllib2.URLError, e: response = e response_code = '---' response_headers = [] raise AdWordsError(str(e))
def _HandleLogsAndErrors(self, buf, start_time, stop_time, error=None): """Manage SOAP XML message. Args: buf: SoapBuffer SOAP buffer. start_time: str Time before service call was invoked. stop_time: str Time after service call was invoked. [optional] error: dict Error, if any. """ if error is None: error = {} try: # Update the number of units and operations consumed by API call. if buf.GetCallUnits() and buf.GetCallOperations(): self._config['units'][0] += int(buf.GetCallUnits()) self._config['operations'][0] += int(buf.GetCallOperations()) self._config['last_units'][0] = int(buf.GetCallUnits()) self._config['last_operations'][0] = int(buf.GetCallOperations()) handlers = self.__GetLogHandlers(buf) fault = super(GenericAdWordsService, self)._ManageSoap( buf, handlers, LIB_URL, start_time, stop_time, error) if fault: # Raise a specific error, subclass of AdWordsApiError. if 'detail' in fault and fault['detail']: if 'errors' in fault['detail']: error_type = fault['detail']['errors'][0]['type'] if error_type in ERRORS: raise ERRORS[str(error_type)](fault) if isinstance(fault, basestring): raise AdWordsError(fault) elif isinstance(fault, dict): raise AdWordsApiError(fault) except AdWordsApiError, e: raise e
def DownloadBulkJob(self, job_id, wait_secs=30, max_polls=60): """Return results of the bulk mutate job or None if there was a failure. Args: job_id: str Bulk mutate job id. wait_secs: int Time in seconds to wait between each poll. max_polls: int Maximum number of polls to perform. Returns: list Results of the bulk mutate job or None if there was a failure. """ SanityCheck.ValidateTypes(((job_id, (str, unicode)), (wait_secs, int), (max_polls, int))) # Wait for bulk muate job to complete. selector = { 'jobIds': [job_id] } job = self.Get(selector)[0] status = job['status'] num_parts = job['numRequestParts'] num_parts_recieved = job['numRequestPartsReceived'] # Were all parts of the job uploaded? if num_parts != num_parts_recieved: return None num_polls = 1 while (status != 'COMPLETED' and status != 'FAILED' and num_polls < max_polls): if Utils.BoolTypeConvert(self._config['debug']): print 'Bulk mutate job status: %s' % status time.sleep(wait_secs) status = self.Get(selector)[0]['status'] num_polls += 1 if status != 'COMPLETED' and status != 'FAILED' and num_polls >= max_polls: msg = ('The job with id \'%s\' has exceeded max_polls of \'%s\'.' % (job_id, max_polls)) raise AdWordsError(msg) if status == 'FAILED': if Utils.BoolTypeConvert(self._config['debug']): print 'Bulk mutate job failed' return None if Utils.BoolTypeConvert(self._config['debug']): print 'Bulk mutate job completed successfully' # Get results for each part of the job. res = [] for part in xrange(int(num_parts)): selector = { 'jobIds': [job_id], 'resultPartIndex': str(part) } res.append(self.Get(selector)[0]) return res
def testError(self): """Tests whether we can catch an AdWordsErrors.AdWordsError exception.""" try: try: self.__class__.dummy_client.GetAccountService( self.__class__.SERVER, self.__class__.VERSION, HTTP_PROXY).GetAccountInfo() except AdWordsError, e: raise AdWordsError(e.message) except AdWordsError, e: try: self.assertEqual(str(e), self.__class__.TRIGGER_MSG1) except: self.assertEqual(str(e), self.__class__.TRIGGER_MSG2)
def __GenerateHeaders(self, return_micros=None, skip_report_header=None, skip_report_summary=None): """Generates the headers to use for the report download. Args: return_micros: bool whether or not to use micros for money. skip_report_header: A boolean indicating whether to include a header row containing the report name and date range. If false or not specified, report output will include the header row. skip_report_summary: A boolean indicating whether to include a summary row containing the report totals. If false or not specified, report output will include the summary row. Returns: dict Dictionary containing all the headers for the request """ headers = {} if 'clientCustomerId' in self._headers: headers['clientCustomerId'] = self._headers['clientCustomerId'] # Apply OAuth2 headers self._headers['oauth2credentials'].apply(headers) if skip_report_header: headers.update({'skipReportHeader': str(skip_report_header)}) if skip_report_summary: headers.update({'skipReportSummary': str(skip_report_summary)}) if return_micros is not None: if self._op_config[ 'version'] == FINAL_RETURN_MONEY_IN_MICROS_VERSION: headers['returnMoneyInMicros'] = str(return_micros) else: raise AdWordsError( 'returnMoneyInMicros isn\'t supported in this' ' version.') headers['developerToken'] = self._headers['developerToken'] headers['User-Agent'] = self._headers['userAgent'] if Utils.BoolTypeConvert(self._config['compress']): headers['Accept-Encoding'] = 'gzip' headers['User-Agent'] += ',gzip' headers['Content-Encoding'] = 'gzip' return headers
def GetCampaigns(self, client_customer_id): """Returns a client account's Campaigns that haven't been removed. Args: client_customer_id: str Client Customer Id used to retrieve Campaigns. Returns: list List of Campaign data objects. """ self.client.SetClientCustomerId(client_customer_id) self.client.use_mcc = False # A somewhat hackish workaround for "The read operation timed out" error, # which could be triggered on AppEngine's end if the request is too large # and is taking too long. max_tries = 3 today = time.strftime('%Y%m%d', time.localtime()) for i in xrange(1, max_tries + 1): try: selector = { 'fields': ['Id', 'Name', 'Status', 'BudgetId', 'Amount'], 'predicates': [{ 'field': 'Status', 'operator': 'NOT_EQUALS', 'values': ['REMOVED'] }], 'dateRange': { 'min': today, 'max': today } } campaigns = self.client.GetCampaignService().Get(selector)[0] if int(campaigns['totalNumEntries']) > 0: return campaigns['entries'] else: return None except Exception, e: if i == max_tries: raise AdWordsError(e) continue
def __ManageSoap(self, buf, start_time, stop_time, error={}): """Manage SOAP XML message. Args: buf: SoapBuffer SOAP buffer. start_time: str Time before service call was invoked. stop_time: str Time after service call was invoked. [optional] error: dict Error, if any. """ try: # Update the number of units and operations consumed by API call. if buf.GetCallUnits() and buf.GetCallOperations(): self._config['units'][0] += int(buf.GetCallUnits()) self._config['operations'][0] += int(buf.GetCallOperations()) self._config['last_units'][0] = int(buf.GetCallUnits()) self._config['last_operations'][0] = int( buf.GetCallOperations()) # Set up log handlers. handlers = [{ 'tag': 'xml_log', 'name': 'soap_xml', 'data': '' }, { 'tag': 'request_log', 'name': 'request_info', 'data': str('host=%s service=%s method=%s operator=%s ' 'responseTime=%s operations=%s units=%s requestId=%s' % (Utils.GetNetLocFromUrl(self._url), buf.GetServiceName(), buf.GetCallName(), buf.GetOperatorName(), buf.GetCallResponseTime(), buf.GetCallOperations(), buf.GetCallUnits(), buf.GetCallRequestId())) }, { 'tag': '', 'name': 'adwords_api_lib', 'data': '' }] fault = super(AdWordsWebService, self)._ManageSoap(buf, handlers, LIB_URL, ERRORS, start_time, stop_time, error) if fault: # Raise a specific error, subclass of AdWordsApiError. if 'detail' in fault: if 'code' in fault['detail']: code = int(fault['detail']['code']) if code in ERRORS: raise ERRORS[code](fault) elif 'errors' in fault['detail']: type = fault['detail']['errors'][0]['type'] if type in ERRORS: raise ERRORS[str(type)](fault) if isinstance(fault, str): raise AdWordsError(fault) elif isinstance(fault, dict): raise AdWordsApiError(fault) except AdWordsApiError, e: raise e