def ValidateGeoTargetV13(target): """Validate GeoTarget object. Args: target: dict GeoTarget object. Returns: dict updated GeoTarget object. """ glob_sanity_check.ValidateTypes(((target, dict),)) for key in target: if target[key] == 'None': continue if key in ('targetAll',): glob_sanity_check.ValidateTypes(((target[key], (str, unicode)),)) data = target[key] else: glob_sanity_check.ValidateTypes(((target[key], dict),)) geo_target = target[key] for sub_key in geo_target: glob_sanity_check.ValidateTypes(((geo_target[sub_key], list),)) for item in geo_target[sub_key]: if sub_key in ('circles',): circle = {} for sub_sub_key in item: glob_sanity_check.ValidateTypes(((item[sub_sub_key], (str, unicode)),)) circle[sub_sub_key] = item[sub_sub_key] item = circle else: glob_sanity_check.ValidateTypes(((item, (str, unicode)),)) # If value is an empty list, remove key from the dictionary. if not geo_target[sub_key]: geo_target = Utils.UnLoadDictKeys(geo_target, [sub_key]) data = geo_target target[key] = data return target
def CallMethod(self, method_name, params, service_name=None, loc=None, request=None): """Make an API call to specified method. Args: method_name: str API method name. params: list list of parameters to send to the API method. [optional] service_name: str API service name. loc: service locator. request: instance holder of the SOAP request. Returns: tuple/str response from the API method. If 'raw_response' flag enabled a string is returned, tuple otherwise. """ # Acquire thread lock. self.__lock.acquire() try: headers = self.__headers config = self.__config # Temporarily redirect HTTP headers and SOAP from STDOUT into a buffer. buf = SoapBuffer( xml_parser=config['xml_parser'], pretty_xml=Utils.BoolTypeConvert(config['use_pretty_xml'])) old_stdout = sys.stdout sys.stdout = buf start_time = time.strftime('%Y-%m-%d %H:%M:%S') response = () raw_response = '' error = {} try: if Utils.BoolTypeConvert(config['use_strict']): SanityCheck.ValidateHeadersForServer(headers, self.__op_config['server']) # Load/unload version specific authentication and configuration data. if SanityCheck.IsNewApi(self.__op_config['version']): # Set boolean to the format expected by the server, True => true. if 'validateOnly' in headers: headers['validateOnly'] = headers['validateOnly'].lower() # Load/set authentication token. If authentication token has expired, # regenerate it. now = time.time() if (Utils.BoolTypeConvert(config['use_auth_token']) and (('authToken' not in headers and 'auth_token_epoch' not in config) or int(now - config['auth_token_epoch']) >= AUTH_TOKEN_EXPIRE)): if ('email' not in headers or not headers['email'] or 'password' not in headers or not headers['password']): msg = ('Required authentication headers, \'email\' and ' '\'password\', are missing. Unable to regenerate ' 'authentication token.') raise ValidationError(msg) headers['authToken'] = Utils.GetAuthToken(headers['email'], headers['password']) config['auth_token_epoch'] = time.time() self.__headers = headers self.__config = config elif not Utils.BoolTypeConvert(config['use_auth_token']): msg = ('Requests via %s require use of authentication token.' % self.__op_config['version']) raise ValidationError(msg) headers = Utils.UnLoadDictKeys(Utils.CleanUpDict(headers), ['email', 'password']) name_space = '/'.join(['https://adwords.google.com/api/adwords', self.__op_config['group'], self.__op_config['version']]) config['ns_target'] = (name_space, 'RequestHeader') else: headers['useragent'] = headers['userAgent'] headers = Utils.UnLoadDictKeys(headers, ['authToken', 'userAgent']) config = Utils.UnLoadDictKeys(config, ['ns_target', 'auth_token_epoch']) # Fire off API request and handle the response. if config['soap_lib'] == SOAPPY: from aw_api.soappy_toolkit import MessageHandler service = MessageHandler.GetServiceConnection( headers, config, self.__url, self.__op_config['http_proxy'], self.__op_config['version']) if not SanityCheck.IsNewApi(self.__op_config['version']): response = MessageHandler.UnpackResponseAsDict( service.invoke(method_name, params)) else: response = MessageHandler.UnpackResponseAsDict( service._callWithBody(MessageHandler.SetRequestParams( config, method_name, params))) elif config['soap_lib'] == ZSI: from aw_api.zsi_toolkit import MessageHandler service = MessageHandler.GetServiceConnection( headers, config, self.__url, self.__op_config['http_proxy'], service_name, loc) request = MessageHandler.SetRequestParams(self.__op_config, request, params) response = MessageHandler.UnpackResponseAsTuple( eval('service.%s(request)' % method_name)) # The response should always be tuple. If it's not, there must be # something wrong with MessageHandler.UnpackResponseAsTuple(). if len(response) == 1 and isinstance(response[0], list): response = tuple(response[0]) if isinstance(response, list): response = tuple(response) elif isinstance(response, tuple): pass else: if response: response = (response,) else: response = () except Exception, e: error['data'] = e stop_time = time.strftime('%Y-%m-%d %H:%M:%S') # Restore STDOUT. sys.stdout = old_stdout # When debugging mode is ON, fetch last traceback. if Utils.BoolTypeConvert(self.__config['debug']): error['trace'] = Utils.LastStackTrace() # Catch local errors prior to going down to the SOAP layer, which may not # exist for this error instance. if 'data' in error and not buf.IsHandshakeComplete(): # Check if buffer contains non-XML data, most likely an HTML page. This # happens in the case of 502 errors (and similar). Otherwise, this is a # local error and API request was never made. html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr()) if html_error: msg = '%s' % html_error else: msg = str(error['data']) if Utils.BoolTypeConvert(self.__config['debug']): msg += '\n%s' % error['trace'] # When debugging mode is ON, store the raw content of the buffer. if Utils.BoolTypeConvert(self.__config['debug']): error['raw_data'] = buf.GetBufferAsStr() # Catch errors from AuthToken and ValidationError levels, raised during # try/except above. if isinstance(error['data'], AuthTokenError): raise AuthTokenError(msg) elif isinstance(error['data'], ValidationError): raise ValidationError(error['data']) if 'raw_data' in error: msg = '%s [RAW DATA: %s]' % (msg, error['raw_data']) raise Error(msg) if Utils.BoolTypeConvert(self.__config['raw_response']): raw_response = buf.GetRawSOAPIn() self.__ManageSoap(buf, start_time, stop_time, error)
def __init__(self, headers=None, config=None, path=None, use_mcc=False, soap_lib=None): """Inits Client. Args: [optional] headers: dict object with populated authentication credentials. config: dict object with client configuration values. path: str relative or absolute path to home directory (i.e. location of pickles and logs/). use_mcc: bool state of the API request, whether to use MCC account. soap_lib: str soap library to use. Ex: headers = { 'email': '*****@*****.**', 'password': '******', 'clientEmail': '*****@*****.**', 'clientCustomerId': '1234567890', 'userAgent': 'GoogleTest', 'developerToken': '[email protected]++USD', 'validateOnly': 'n' } config = { 'home': '/path/to/home', 'log_home': '/path/to/logs/home', 'soap_lib': ZSI, 'xml_parser': PYXML, 'debug': 'n', 'xml_log': 'y', 'request_log': 'y', 'raw_response': 'n', 'use_strict': 'y', 'use_auth_token': 'y', 'use_pretty_xml': 'y', 'access': '' } path = '/path/to/home' use_mcc = False soap_lib = SOAPPY """ self.__lock = thread.allocate_lock() self.__loc = None self.__is_mcc = use_mcc if path is not None: # Update absolute path for a given instance of Client, based on provided # relative path. if os.path.isabs(path): Client.home = path else: # NOTE(api.sgrinberg): Keep first parameter of join() as os.getcwd(), # do not change it to Client.home. Otherwise, may break when multiple # instances of Client exist during program run. Client.home = os.path.join(os.getcwd(), path) # Update location for both pickles. Client.auth_pkl = os.path.join(Client.home, 'auth.pkl') Client.config_pkl = os.path.join(Client.home, 'config.pkl') # Only load from the pickle if config wasn't specified. self.__config = config or self.__LoadConfigValues() self.__SetMissingDefaultConfigValues(self.__config) self.__config['home'] = Client.home # Load the SOAP library to use. if soap_lib is not None: SanityCheck.ValidateConfigSoapLib(soap_lib) self.__config['soap_lib'] = soap_lib # Validate XML parser to use. SanityCheck.ValidateConfigXmlParser(self.__config['xml_parser']) # Initialize units and operations for current instance of Client object # (using list to take advantage of Python's pass-by-reference). self.__config['units'] = [0] self.__config['operations'] = [0] self.__config['last_units'] = [0] self.__config['last_operations'] = [0] # Only load from the pickle if 'headers' wasn't specified. if headers is None: self.__headers = self.__LoadAuthCredentials() else: self.__headers = headers # Internally, store user agent as 'userAgent'. if 'useragent' in self.__headers: self.__headers['userAgent'] = self.__headers['useragent'] self.__headers = Utils.UnLoadDictKeys(self.__headers, ['useragent']) if Utils.BoolTypeConvert(self.__config['use_strict']): SanityCheck.ValidateRequiredHeaders(self.__headers) # Load validateOnly header, if one was set. if 'validateOnly' in self.__headers: self.__headers['validateOnly'] = str( Utils.BoolTypeConvert(self.__headers['validateOnly'])) # Load/set authentication token. try: if Utils.BoolTypeConvert(self.__config['use_auth_token']): if headers and 'authToken' in headers and headers['authToken']: self.__headers['authToken'] = headers['authToken'] elif 'email' in self.__headers and 'password' in self.__headers: self.__headers['authToken'] = Utils.GetAuthToken( self.__headers['email'], self.__headers['password']) else: msg = 'Authentication data, email or/and password, is missing.' raise ValidationError(msg) self.__config['auth_token_epoch'] = time.time() except AuthTokenError: # We would end up here if non-valid Google Account's credentials were # specified. This is useful for when dummy credentials are set in # unit tests and requests are being made against v13. If v200909 is being # used and invalid credentials specified, this will be caught in # aw_api.WebService.CallMethod(). self.__headers['authToken'] = None self.__config['auth_token_epoch'] = 0 # Insert library name and version into userAgent. if (self.__headers['userAgent'].rfind( '%s v%s' % (LIB_SHORT_NAME, LIB_VERSION)) == -1): # Make sure library name shows up only once. if (self.__headers['userAgent'].rfind(LIB_SHORT_NAME) > -1 or self.__headers['userAgent'].rfind(LIB_NAME) > -1): pattern = re.compile('.*?: ') self.__headers['userAgent'] = pattern.sub( '', self.__headers['userAgent'], 1) self.__headers['userAgent'] = ( '%s v%s: %s' % (LIB_SHORT_NAME, LIB_VERSION, self.__headers['userAgent'])) # Sync library's version in the new userAgent with the one in the pickle. if headers is None: self.__WriteUpdatedAuthValue('userAgent', self.__headers['userAgent']) # Initialize logger. self.__logger = Logger(self.__config['log_home'])