Example #1
0
def ValidateServer(server, version):
    """Sanity check for API server.

  Args:
    server: str API server to access for this API call.
    version: str API version being used to access the server.
  """
    # Map of supported API servers and versions.
    prod = {
        'v13': 'https://adwords.google.com',
        'v200909': 'https://adwords.google.com',
        'v201003': 'https://adwords.google.com'
    }
    sandbox = {
        'v13': 'https://sandbox.google.com',
        'v200909': 'https://adwords-sandbox.google.com',
        'v201003': 'https://adwords-sandbox.google.com'
    }

    if server not in prod.values() and server not in sandbox.values():
        msg = ('Given API server, \'%s\', is not valid. Expecting one of %s.' %
               (server, sorted(prod.values() + sandbox.values())[1:]))
        raise ValidationError(msg)

    if version not in prod.keys() and version not in sandbox.keys():
        msg = (
            'Geven API version, \'%s\', is not valid. Expecting one of %s.' %
            (version, sorted(set(prod.keys() + sandbox.keys()))))
        raise ValidationError(msg)

    if server != prod[version] and server != sandbox[version]:
        msg = (
            'Given API version, \'%s\', is not compatible with given server, '
            '\'%s\'.' % (version, server))
        raise ValidationError(msg)
Example #2
0
    def __init__(self, headers, config, op_config, lock, logger):
        """Inits AccountService.

    Args:
      headers: dict dictionary object with populated authentication
               credentials.
      config: dict dictionary object with populated configuration values.
      op_config: dict dictionary object with additional configuration values for
                 this operation.
      lock: thread.lock the thread lock
      logger: Logger the instance of Logger
    """
        url = [
            op_config['server'], 'api/adwords', op_config['version'],
            self.__class__.__name__
        ]
        if glob_sanity_check.IsNewApi(op_config['version']):
            url.insert(2, 'account')
        if config['access']: url.insert(len(url) - 1, config['access'])
        self.__service = WebService(headers, config, op_config, '/'.join(url),
                                    lock, logger)
        self.__config = config
        self.__op_config = op_config
        if self.__config['soap_lib'] == SOAPPY:
            from aw_api.soappy_toolkit import MessageHandler
            from aw_api.soappy_toolkit import SanityCheck
            self.__web_services = None
            self.__message_handler = MessageHandler
        elif self.__config['soap_lib'] == ZSI:
            from aw_api import API_VERSIONS
            from aw_api.zsi_toolkit import SanityCheck
            if op_config['version'] in API_VERSIONS:
                module = '%s_services' % self.__class__.__name__
                try:
                    web_services = __import__(
                        'aw_api.zsi_toolkit.%s.%s' %
                        (op_config['version'], module), globals(), locals(),
                        [''])
                except ImportError, e:
                    # If one of library's required modules is missing, re raise exception.
                    if str(e).find(module) < 0:
                        raise ImportError(e)
                    msg = (
                        'The version \'%s\' is not compatible with \'%s\'.' %
                        (op_config['version'], self.__class__.__name__))
                    raise ValidationError(msg)
            else:
                msg = 'Invalid API version, not one of %s.' % str(
                    list(API_VERSIONS))
                raise ValidationError(msg)
            self.__web_services = web_services
            self.__loc = eval('web_services.%sLocator()' %
                              self.__class__.__name__)
Example #3
0
    def __init__(self, headers, config, op_config, lock, logger):
        """Inits BulkMutateJobService.

    Args:
      headers: dict dictionary object with populated authentication
               credentials.
      config: dict dictionary object with populated configuration values.
      op_config: dict dictionary object with additional configuration values for
                 this operation.
      lock: thread.lock the thread lock.
      logger: Logger the instance of Logger
    """
        # NOTE(api.sgrinberg): Custom handling for BulkMutateJobService, whose
        # group in URL is 'job/' which is different from its namespace 'cm/'.
        url = [
            op_config['server'], 'api/adwords', 'job', op_config['version'],
            self.__class__.__name__
        ]
        if config['access']: url.insert(len(url) - 1, config['access'])
        self.__service = WebService(headers, config, op_config, '/'.join(url),
                                    lock, logger)
        self.__config = config
        self.__op_config = op_config
        if self.__config['soap_lib'] == SOAPPY:
            from aw_api.soappy_toolkit import SanityCheck
            self.__web_services = None
        elif self.__config['soap_lib'] == ZSI:
            from aw_api import API_VERSIONS
            from aw_api.zsi_toolkit import SanityCheck
            if op_config['version'] in API_VERSIONS:
                module = '%s_services' % self.__class__.__name__
                try:
                    web_services = __import__(
                        'aw_api.zsi_toolkit.%s.%s' %
                        (op_config['version'], module), globals(), locals(),
                        [''])
                except ImportError, e:
                    # If one of library's required modules is missing, re raise exception.
                    if str(e).find(module) < 0:
                        raise ImportError(e)
                    msg = (
                        'The version \'%s\' is not compatible with \'%s\'.' %
                        (op_config['version'], self.__class__.__name__))
                    raise ValidationError(msg)
            else:
                msg = 'Invalid API version, not one of %s.' % str(
                    list(API_VERSIONS))
                raise ValidationError(msg)
            self.__web_services = web_services
            self.__loc = eval('web_services.%sLocator()' %
                              self.__class__.__name__)
Example #4
0
def ValidateCriterion(operator, criterion, web_services):
  """Validate Criterion object.

  A Criterion object is one of Keyword, Website, Placement.

  Args:
    operator: str operator to use.
    criterion: dict Criterion object.
    web_services: module for web services.

  Returns:
    dict/Criterion updated criterion object or Criterion instance.
  """
  if IsPyClass(criterion):
    return criterion

  glob_sanity_check.ValidateTypes(((criterion, dict),))
  if operator in ('ADD', ''):
    if 'criterionType' in criterion:
      new_criterion = GetPyClass(criterion['criterionType'], web_services)
    elif 'type' in criterion:
      new_criterion = GetPyClass(criterion['type'], web_services)
    elif 'Criterion_Type' in criterion:
      new_criterion = GetPyClass(criterion['Criterion_Type'], web_services)
    else:
      msg = 'The \'criterionType\' or \'type\' of the criterion is missing.'
      raise ValidationError(msg)
  elif operator in ('SET', 'REMOVE'):
    new_criterion = GetPyClass('Criterion', web_services)
  for key in criterion:
    if criterion[key] == 'None': continue
    glob_sanity_check.ValidateTypes(((criterion[key], (str, unicode)),))
    new_criterion.__dict__.__setitem__('_%s' % key, criterion[key])

  return new_criterion
Example #5
0
def ValidateBiddingStrategy(strategy, web_services):
  """Validate BiddingStrategy object.

  A BiddingStrategy is one of BudgetOptimizer, ConversionOptimizer, ManualCPC,
  ManualCPM.

  Args:
    strategy: dict BiddingStrategy object.
    web_services: module for web services.

  Returns:
    BiddingStrategy instance.
  """
  if IsPyClass(strategy):
    return strategy

  glob_sanity_check.ValidateTypes(((strategy, dict),))
  if 'type' in strategy:
    new_strategy = GetPyClass(strategy['type'], web_services)
  elif 'BiddingStrategy_Type' in strategy:
    new_strategy = GetPyClass(strategy['BiddingStrategy_Type'], web_services)
  else:
    msg = 'The \'type\' of the bidding transition is missing.'
    raise ValidationError(msg)
  for key in strategy:
    if strategy[key] == 'None': continue
    if key in ('bidCeiling',):
      data = ValidateMoney(strategy[key], web_services)
    else:
      glob_sanity_check.ValidateTypes(((strategy[key], (str, unicode)),))
      data = strategy[key]
    new_strategy.__dict__.__setitem__('_%s' % key, data)

  return new_strategy
Example #6
0
  def __init__(self, headers, config, op_config, url, lock, logger=None):
    """Inits WebService.

    Args:
      headers: dict dictionary object with populated authentication
               credentials.
      config: dict dictionary object with populated configuration values.
      op_config: dict dictionary object with additional configuration values for
                 this operation.
      url: str url of the web service to call.
      lock: thread.lock the thread lock.
      logger: Logger the instance of Logger
    """
    self.__headers = headers
    self.__config = config
    self.__op_config = op_config
    self.__url = url
    self.__lock = lock
    self.__logger = logger

    if self.__logger is None:
      self.__logger = Logger(self.__config['log_home'])

    if (self.__config['soap_lib'] == SOAPPY and
        self.__headers.values().count(None) < 2 and
        (len(self.__headers.values()) > len(set(self.__headers.values())) and
         not self.__headers.has_key('useragent'))):
      if self.__headers.values().count('') < 2:
        msg = ('Two (or more) values in \'headers\' dict can not be the same. '
               'See, %s/issues/detail?id=27.' % LIB_URL)
        raise ValidationError(msg)
      else:
        self.__headers = {}
        for key in headers:
          if headers[key]: self.__headers[key] = headers[key]
Example #7
0
def ValidateAd(operator, ad, web_services):
  """Validate Ad object.

  An Ad object is one of DeprecatedAd, MobileAd, MobileImageAd, ImageAd,
  LocalBusinessAd, TemplateAd, TextAd,

  Args:
    operator: str operator to use.
    ad: dict ad object.
    web_services: module for web services.

  Returns:
    dict/Ad updated ad object or Ad instance.
  """
  if IsPyClass(ad):
    return ad

  glob_sanity_check.ValidateTypes(((ad, dict),))
  if operator in ('ADD', ''):
    if 'adType' in ad:
      new_ad = GetPyClass(ad['adType'], web_services)
    elif 'type' in ad:
      new_ad = GetPyClass(ad['type'], web_services)
    elif 'Ad_Type' in ad:
      new_ad = GetPyClass(ad['Ad_Type'], web_services)
    else:
      msg = 'The \'adType\' or \'type\' of the ad is missing.'
      raise ValidationError(msg)
  elif operator in ('SET', 'REMOVE'):
    new_ad = GetPyClass('Ad', web_services)
  for key in ad:
    if ad[key] == 'None': continue
    if key in ('productImage', 'image', 'businessImage', 'customIcon', 'icon'):
      data = ValidateImage(ad[key], web_services)
    elif key in ('video',):
      data = ValidateVideo(ad[key], web_services)
    elif key in ('markupLanguages', 'mobileCarriers'):
      glob_sanity_check.ValidateTypes(((ad[key], list),))
      for item in ad[key]:
        glob_sanity_check.ValidateTypes(((item, (str, unicode)),))
      data = ad[key]
    elif key in ('target',):
      data = ValidateProximityTarget(ad[key], web_services)
    elif key in ('adUnionId',):
      data = ValidateEntityId(ad[key], 'AdUnionId', web_services)
    elif key in ('templateElements',):
      glob_sanity_check.ValidateTypes(((ad[key], list),))
      elements = []
      for item in ad[key]:
        elements.append(ValidateTemplateElement(item, web_services))
      data = elements
    elif key in ('dimensions',):
      data = ValidateDimensions(ad[key], web_services)
    else:
      glob_sanity_check.ValidateTypes(((ad[key], (str, unicode)),))
      data = ad[key]
    new_ad.__dict__.__setitem__('_%s' % key, data)

  return new_ad
Example #8
0
def ValidateBids(bids, web_services):
  """Validate Bids object.

  A Bids object is on of AdGroupBids, AdGroupCriterionBids,
  BudgetOptimizerAdGroupBids, BudgetOptimizerAdGroupCriterionBids,
  ConversionOptimizerAdGroupBids, ConversionOptimizerAdGroupCriterionBids,
  ManualCPCAdGroupBids, ManualCPCAdGroupCriterionBids, ManualCPMAdGroupBids,
  ManualCPMAdGroupCriterionBids, PositionPreferenceAdGroupCriterionBids.

  Args:
    bids: dict Bids object.
    web_services: module for web services.

  Returns:
    XxxBids instance.
  """
  if IsPyClass(bids):
    return bids

  glob_sanity_check.ValidateTypes(((bids, dict),))
  if 'type' in bids:
    new_bids = GetPyClass(bids['type'], web_services)
  elif 'AdGroupBids_Type' in bids:
    new_bids = GetPyClass(bids['AdGroupBids_Type'], web_services)
  elif 'AdGroupCriterionBids_Type' in bids:
    new_bids = GetPyClass(bids['AdGroupCriterionBids_Type'], web_services)
  else:
    msg = 'The \'type\' of the bid is missing.'
    raise ValidationError(msg)
  for key in bids:
    if bids[key] == 'None': continue
    if key in ('proxyBid', 'maxCpc', 'maxCpm', 'proxyMaxCpc',
               'proxyKeywordMaxCpc', 'proxySiteMaxCpc', 'targetCpa',
               'keywordMaxCpc', 'keywordContentMaxCpc', 'siteMaxCpc',
               'targetCpa'):
      data = ValidateBid(bids[key], web_services)
    elif key in ('positionPreferenceBids',):
      glob_sanity_check.ValidateTypes(((bids[key], dict),))
      new_bids = GetPyClass('PositionPreferenceAdGroupCriterionBids',
                            web_services)
      for sub_key in bids[key]:
        if sub_key in ('proxyMaxCpc',):
          data = ValidateBid(bids[key][sub_key], web_services)
        else:
          glob_sanity_check.ValidateTypes(((bids[key][sub_key],
                                            (str, unicode)),))
          data = bids[key][sub_key]
        new_bids.__dict__.__setitem__('_%s' % sub_key, data)
      data = new_bids
    else:
      glob_sanity_check.ValidateTypes(((bids[key], (str, unicode)),))
      data = bids[key]
    new_bids.__dict__.__setitem__('_%s' % key, data)

  return new_bids
Example #9
0
def ValidateHeadersForServer(headers, server):
    """Check if provided headers match the ones expected on the provided server.

  The SOAP headers on Sandbox server are different from production.  See
  http://code.google.com/apis/adwords/docs/developer/adwords_api_sandbox.html.
  """
    fits_sandbox = False

    # The clientEmail SOAP header in Sandbox has to be of specific format, with
    # "client_" prepended (e.g., [email protected]).
    if ('clientEmail' in headers and headers['clientEmail']
            and headers['clientEmail'].find('client_', 0, 7) > -1):
        fits_sandbox = True

    # The developerToken SOAP header in Sandbox has to be same as email SOAP
    # header with appended "++" and the currency code.
    if ('email' in headers and headers['email'] and
            headers['developerToken'].find('%s++' % headers['email'], 0,
                                           len(headers['email']) + 2) > -1):
        fits_sandbox = True
    elif ('authToken' in headers and headers['authToken']
          and headers['developerToken'].find('++')
          == len(headers['developerToken']) - 5):
        fits_sandbox = True
    else:
        fits_sandbox = False

    # Sandbox server is identifying by the "sandbox" part in the URL (e.g.,
    # https://sandbox.google.com or https://adwords-sandbox.google.com).
    if server.find('sandbox') > -1:
        if not fits_sandbox:
            msg = (
                'Invalid headers for \'%s\', see http://code.google.com/apis/adwords/docs/developer/adwords_api_sandbox.html#requestheaders.'
                % server)
            raise ValidationError(msg)
    elif server.find('sandbox') < 0:
        if fits_sandbox:
            msg = (
                'Invalid headers for \'%s\', see http://code.google.com/apis/adwords/docs/developer/index.html#adwords_api_intro_request.'
                % server)
            raise ValidationError(msg)
Example #10
0
def ValidateConfigSoapLib(soap_lib):
    """Sanity check for SOAP library.

  Args:
    soap_lib: str SOAP library to use.
  """
    if (not isinstance(soap_lib, str)
            or not IsConfigUserInputValid(soap_lib, [SOAPPY, ZSI])):
        msg = (
            'Invalid input for %s \'%s\', expecting %s or %s of type <str>.' %
            (type(soap_lib), soap_lib, SOAPPY, ZSI))
        raise ValidationError(msg)
Example #11
0
def ValidateConfigXmlParser(xml_parser):
    """Sanity check for XML parser.

  Args:
    xml_parser: str XML parser to use.
  """
    if (not isinstance(xml_parser, str)
            or not IsConfigUserInputValid(xml_parser, [PYXML, ETREE])):
        msg = (
            'Invalid input for %s \'%s\', expecting %s or %s of type <str>.' %
            (type(xml_parser), xml_parser, PYXML, ETREE))
        raise ValidationError(msg)
Example #12
0
def ValidateTypes(vars_tpl):
    """Check types for a set of variables.

  Args:
    vars_tpl: tuple set of variables to check.
  """
    for var, var_types in vars_tpl:
        if not isinstance(var_types, tuple):
            var_types = (var_types, )
        for var_type in var_types:
            if IsType(var, var_type):
                return
        msg = ('The \'%s\' is of type %s, expecting one of %s.' %
               (var, type(var), var_types))
        raise ValidationError(msg)
Example #13
0
def ValidateRequiredHeaders(headers):
    """Sanity check for required authentication elements.

  All required authentication headers have to be set in order to make
  successful API request.

  Args:
    headers: dict authentication headers.
  """
    req_headers = ('email', 'password', 'userAgent', 'developerToken')
    if 'authToken' in headers and headers['authToken']:
        req_headers = ('authToken', 'userAgent', 'developerToken')
    for key in req_headers:
        if key not in headers or not headers[key]:
            msg = ('Required authentication header \'%s\' is missing.' % key)
            raise ValidationError(msg)
Example #14
0
def ValidateTarget(target, web_services):
  """Validate Target object.

  A Target object is one of AdScheduleTarget, AgeTarget, CityTarget,
  CountryTarget, DemographicTarget, GenderTarget, GeoTarget, LanguageTarget,
  MetroTarget, NetworkTarget, PlatformTarget, PolygonTarget, ProvinceTarget,
  ProximityTarget, Target.

  Args:
    target: list a target object.
    web_services: module for web services.

  Returns:
    XxxTarget instance.
  """
  if IsPyClass(target):
    return target

  glob_sanity_check.ValidateTypes(((target, dict),))
  if 'type' in target:
    new_target = GetPyClass(target['type'], web_services)
  elif 'Target_Type' in target:
    new_target = GetPyClass(target['Target_Type'], web_services)
  else:
    msg = 'The \'type\' of the target is missing.'
    raise ValidationError(msg)
  for key in target:
    if target[key] == 'None': continue
    if key in ('vertices',):
      glob_sanity_check.ValidateTypes(((target[key], list),))
      geo_points = []
      for item in target[key]:
        geo_points.append(ValidateGeoPoint(item, web_services))
      data = geo_points
    elif key in ('address',):
      data = ValidateAddress(target[key], web_services)
    elif key in ('geoPoint',):
      data = ValidateGeoPoint(target[key], web_services)
    else:
      glob_sanity_check.ValidateTypes(((target[key], (str, unicode)),))
      data = target[key]
    new_target.__dict__.__setitem__('_%s' % key, data)

  return new_target
Example #15
0
    def __LoadAuthCredentials(self):
        """Load existing authentication credentials from auth.pkl.

    Returns:
      dict dictionary object with populated authentication credentials.
    """
        auth = {}
        if os.path.exists(Client.auth_pkl):
            fh = open(Client.auth_pkl, 'r')
            try:
                auth = pickle.load(fh)
            finally:
                fh.close()

        if not auth:
            msg = 'Authentication data is missing.'
            raise ValidationError(msg)

        return auth
Example #16
0
def GetPyClass(name, web_services):
  """Return Python class for a given class name.

  Args:
    name: str name of the Python class to return.
    web_services: module for web service.

  Returns:
    Python class.
  """
  for index in xrange(MAX_TARGET_NAMESPACE):
    try:
      pyclass = eval('web_services.ns%s.%s_Def(\'%s\').pyclass' % (index, name,
                                                                   name))
      break
    except AttributeError:
      if index == MAX_TARGET_NAMESPACE - 1:
        version = web_services.__dict__['__name__'].split('.')[2]
        msg = ('Given API version, %s, is not compatible with \'%s\' class.' %
               (version, name))
        raise ValidationError(msg)

  return pyclass
Example #17
0
def ValidateJobOperation(operation, web_services):
  """Validate JobOperation object.

  Args:
    operation: dict JobOperation object.
    web_services: module for web services.

  Returns:
    JobOperation instance.
  """
  if IsPyClass(operation):
    return operation

  glob_sanity_check.ValidateTypes(((operation, dict),))
  if 'type' not in operation:
    msg = 'A job operation type is missing.'
    raise ValidationError(msg)
  operation_type = '%sOperation' % operation['type']
  new_operation = GetPyClass(operation_type, web_services)
  operation = ValidateOperation(operation, web_services)
  for key in operation:
    new_operation.__dict__.__setitem__('_%s' % key, operation[key])

  return new_operation
Example #18
0
def ValidateBidLandscapeSelector(selector, web_services):
  """Validate BidLandscapeSelector object.

  Args:
    selector: dict selector object.
    web_services: module for web services.
  """
  if IsPyClass(selector):
    return selector

  glob_sanity_check.ValidateTypes(((selector, dict),))
  if 'type' in selector:
    new_selector = GetPyClass(selector['type'], web_services)
  elif 'BidLandscapeSelector_Type' in selector:
    new_selector = GetPyClass(selector['BidLandscapeSelector_Type'],
                              web_services)
  else:
    msg = 'The \'type\' of the bid landscape selector is missing.'
    raise ValidationError(msg)
  for key in selector:
    if key in ('idFilters',):
      glob_sanity_check.ValidateTypes(((selector[key], list),))
      filters = []
      for item in selector[key]:
        glob_sanity_check.ValidateTypes(((item, dict),))
        filter = GetPyClass('BidLandscapeIdFilter', web_services)
        for sub_key in item:
          glob_sanity_check.ValidateTypes(((item[sub_key], (str, unicode)),))
          filter.__dict__.__setitem__('_%s' % sub_key, item[sub_key])
        filters.append(filter)
      data = filters
    else:
      data = selector[key]
    new_selector.__dict__.__setitem__('_%s' % key, data)

  return new_selector
    from ZSI.version import Version as ZSI_VERSION
except ImportError:
    msg = 'ZSI v%s or newer is required.' % MIN_ZSI_VERSION
    raise MissingPackageError(msg)
else:
    if list(ZSI.version.Version) < (list(map(eval,
                                             MIN_ZSI_VERSION.split('.')))):
        msg = 'ZSI v%s or newer is required.' % MIN_ZSI_VERSION
        raise MissingPackageError(msg)
    # NOTE(api.sgrinberg): Keep this check until ZSI version higher than 2.0.0
    # fixes known bug with bad NS (see issue# 84).
    elif list(ZSI.version.Version) > (list(
            map(eval, MAX_ZSI_VERSION.split('.')))):
        msg = ('ZSI v%s is not supported. Please use v%s.' %
               ('.'.join(map(str, ZSI.version.Version)), MIN_ZSI_VERSION))
        raise ValidationError(msg)


def GetServiceConnection(headers, config, url, http_proxy, service_name, loc):
    """Get SOAP service connection.

  Args:
    headers: dict dictionary object with populated authentication
             credentials.
    config: dict dictionary object with populated configuration values.
    url: str url of the web service to call.
    http_proxy: str HTTP proxy to use for this API call.
    service_name: str API service name.
    loc: service locator.

  Returns:
Example #20
0
    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'])
Example #21
0
def ValidateSearchParameter(param, web_services):
  """Validate SearchParameter object.

  A SearchParameter is one of AdTypeSearchParameter,
  AverageTargetedMonthlySearchesSearchParameter, CompetitionSearchParameter,
  CountryTargetSearchParameter, ExcludedKeywordSearchParameter,
  GlobalMonthlySearchesSearchParameter, IncludeAdultContentSearchParameter,
  KeywordCategoryIdSearchParameter, KeywordMatchTypeSearchParameter,
  LanguageTargetSearchParameter, MobileSearchParameter,
  NgramGroupsSearchParameter, PlacementTypeSearchParameter,
  RelatedToKeywordSearchParameter, RelatedToUrlSearchParameter,
  SeedAdGroupIdSearchParameter

  Args:
    search_parameter: dict SearchParameter object.
    web_services: module for web services.

  Returns:
    XxxSearchParameter instance.
  """
  if IsPyClass(param):
    return param

  glob_sanity_check.ValidateTypes(((param, dict),))
  if 'type' in param:
    new_param = GetPyClass(param['type'], web_services)
  elif 'SearchParameter_Type' in param:
    new_param = GetPyClass('SearchParameter_Type', web_services)
  else:
    msg = 'The \'type\' of the search parameter is missing.'
    raise ValidationError(msg)
  for key in param:
    if param[key] == 'None': continue
    if key in ('adTypes', 'levels', 'keywordMatchTypes', 'ngramGroups',
               'categoryIds', 'placementTypes', 'urls', 'included', 'excluded'):
      glob_sanity_check.ValidateTypes(((param[key], list),))
      items = []
      for item in param[key]:
        glob_sanity_check.ValidateTypes(((item, (str, unicode)),))
        items.append(item)
      data = items
    elif key in ('operation',):
      data = ValidateLongComparisonOperation(param[key], web_services)
    elif key in ('keywords',):
      glob_sanity_check.ValidateTypes(((param[key], list),))
      kws = []
      for item in param[key]:
        kws.append(ValidateKeyword(item, web_services))
      data = kws
    elif key in ('countryTargets',):
      glob_sanity_check.ValidateTypes(((param[key], list),))
      targets = []
      for item in param[key]:
        targets.append(ValidateCountryTarget(item, web_services))
      data = targets
    elif key in ('languageTargets',):
      glob_sanity_check.ValidateTypes(((param[key], list),))
      targets = []
      for item in param[key]:
        targets.append(ValidateLanguageTarget(item, web_services))
      data = targets
    else:
      glob_sanity_check.ValidateTypes(((param[key], (str, unicode)),))
      data = param[key]
    new_param.__dict__.__setitem__('_%s' % key, data)

  return new_param
Example #22
0
  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)
Example #23
0
def ValidateOperation(operation, web_services):
  """Validate Operation object.

  Args:
    operation: dict operation object.
    web_services: module for web services.

  Returns:
    dict updated Operation object.
  """
  if IsPyClass(operation):
    return operation

  glob_sanity_check.ValidateTypes(((operation, dict),))
  caller = {
    'name': web_services.__name__.split('.')[-1].split('Service')[0],
    'typed': False
  }
  if 'type' in operation: caller['name'] = operation['type']
  # Custom handler for services that require concrete types for ADD and SET
  # operators.
  if caller['name'] in ('AdGroupCriterion', 'BulkMutateJob',
                        'CampaignAdExtension', 'CampaignCriterion',
                        'CampaignTarget'):
    caller['typed'] = True
  operator = ''
  for key in operation:
    if key in ('operator',):
      glob_sanity_check.ValidateTypes(((operation[key], (str, unicode)),))
      operator = operation[key]
    elif key in ('operand',):
      glob_sanity_check.ValidateTypes(((operation[key], dict),))
      operand = operation[key]
      if (caller['typed'] and 'type' in operand) or 'type' in operation:
        caller['typed'] = True
        if 'type' in operation and 'type' in operand:
          type = operand['type']
        elif 'type' in operation:
          type = operation['type']
        else:
          type = operand['type']
        new_operand = GetPyClass(type, web_services)
      elif not caller['typed']:
        pass
      else:
        msg = 'The \'type\' of the operand is missing.'
        raise ValidationError(msg)
      for sub_key in operand:
        if sub_key in ('ad',):
          data = ValidateAd(operator, operand[sub_key], web_services)
        elif sub_key in ('bids',):
          data = ValidateBids(operand[sub_key], web_services)
        elif sub_key in ('criterion',):
          data = ValidateCriterion(operator, operand[sub_key], web_services)
        elif sub_key in ('minBids',):
          glob_sanity_check.ValidateTypes(((operand[sub_key], list),))
          bids = []
          for item in operand[sub_key]:
            bids.append(ValidateBid(item, web_services))
          data = bids
        elif sub_key in ('budget',):
          data = ValidateBudget(operand[sub_key], web_services)
        elif sub_key in ('biddingStrategy',):
          data = ValidateBiddingStrategy(operand[sub_key], web_services)
        elif sub_key in ('frequencyCap',):
          data = ValidateFrequencyCap(operand[sub_key], web_services)
        elif sub_key in ('targets',):
          glob_sanity_check.ValidateTypes(((operand[sub_key], list),))
          targets = []
          for item in operand[sub_key]:
            targets.append(ValidateTarget(item, web_services))
          data = targets
        elif sub_key in ('request',):
          data = ValidateBulkMutateRequest(operand[sub_key], web_services)
        elif sub_key in ('adExtension',):
          data = ValidateAdExtension(operand[sub_key], web_services)
        elif sub_key in ('overrideInfo',):
          data = ValidateOverrideInfo(operand[sub_key], web_services)
        elif sub_key in ('customerIds',):
          glob_sanity_check.ValidateTypes(((operand[sub_key], list),))
          for item in operand[sub_key]:
            glob_sanity_check.ValidateTypes(((item, (str, unicode)),))
        elif sub_key in ('selector',):
          ValidateSelector(operand[sub_key], web_services)
          data = operand[sub_key]
        else:
          data = operand[sub_key]
        if caller['typed']:
          new_operand.__dict__.__setitem__('_%s' % sub_key, data)
        else:
          operand[sub_key] = data
      if caller['typed']:
        new_operand.__dict__.__setitem__('_%s' % key, operand)
        operation[key] = new_operand
    elif key in ('exemptionRequests',):
      glob_sanity_check.ValidateTypes(((operation[key], list),))
      for item in operation[key]:
        ValidateExemptionRequest(item)
      data = operation[key]
    elif key in ('biddingTransition',):
      glob_sanity_check.ValidateTypes(((operation[key], dict),))
      for sub_key in operation[key]:
        if sub_key in ('targetBiddingStrategy',):
          operation[key][sub_key] = \
              ValidateBiddingStrategy(operation[key][sub_key], web_services)
        elif sub_key in ('explicitAdGroupBids',):
          operation[key][sub_key] = \
              ValidateBids(operation[key][sub_key], web_services)
        else:
          glob_sanity_check.ValidateTypes(((operation[key][sub_key],
                                            (str, unicode)),))

  return operation
Example #24
0
    def CallMethod(self, url, method, params, http_proxy):
        """Call API method directly, using its service's URL.

    For API calls performed with this method, outgoing data is not run through
    library's validation logic.

    Args:
      url: str URL of the API service for the method to call.
      method: str name of the API method to call.
      params: list list of parameters to send to the API method.
      http_proxy: str HTTP proxy to use for this API call.

    Returns:
      tuple response from the API method.
    """
        headers = self.__GetAuthCredentialsForAccessLevel()

        # Load additional configuration data.
        op_config = {
            'server': Utils.GetServerFromUrl(url),
            'version': Utils.GetVersionFromUrl(url),
            'http_proxy': http_proxy
        }

        service = WebService(headers, self.__config, op_config, url,
                             self.__lock, self.__logger)

        if self.__config['soap_lib'] == ZSI:
            # Check format of parameters. Example of valid formats,
            # - ()
            # - ({'dummy': 0},)
            # - ({'campaignIds': ['11111']},
            #    {'startDay': '2008-07-01'},
            #    {'endDay': '2008-07-31'})
            #
            # TODO(api.sgrinberg: Figure out how to match the order of params with
            # those in Holder object below. Then, we don't need to require client code
            # to provide key/value pairs, just values will be enough (see, issue# 31).
            try:
                SanityCheck.ValidateTypes(((params, tuple), ))
                for item in params:
                    SanityCheck.ValidateTypes(((item, dict), ))
            except ValidationError:
                msg = 'Invalid format of parameters, expecting a tuple of dicts.'
                raise ValidationError(msg)

            # From the URL, get service being accessed and version used.
            url_parts = url.split('/')
            service_name = url_parts[len(url_parts) - 1].split('Service')[0]
            version = url_parts[len(url_parts) - 2]

            from aw_api import API_VERSIONS
            if version in API_VERSIONS:
                web_services = __import__(
                    'aw_api.zsi_toolkit.%s.%sService_services' %
                    (version, service_name), globals(), locals(), [''])
            else:
                msg = 'Invalid API version, not one of %s.' % str(
                    list(API_VERSIONS))
                raise ValidationError(msg)
            eval('%sService' % service_name).web_services = web_services
            self.__loc = eval(('%sService.web_services.%sServiceLocator()' %
                               (service_name, service_name)))
            request = eval('%sService.web_services.%sRequest()' %
                           (service_name, method))
            return service.CallMethod(method, (params), service_name,
                                      self.__loc, request)
        else:
            return service.CallMethod(method, (params))