Example #1
0
    def __init__(self, headers, config, op_config, lock, logger, service_name,
                 service_url, wrap_lists, buffer_class, namespace,
                 namespace_extractor):
        """Inits GenericApiService.

    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: mixed Thread lock to use to synchronize requests. May be a
            thread.lock or a threading.RLock
      logger: Logger Instance of Logger to use for logging.
      service_name: string The name of this service.
      service_url: string The URL pointing to this web service.
      wrap_lists: boolean Whether this service needs to wrap lists in an
                  additional layer of XML element tags.
      buffer_class: class The SoapBuffer subclass to use as a buffer.
      namespace: string The namespace this service uses by default.
      namespace_extractor: function A function which takes a URL and returns the
                           namespace prefix to use to represent it.

    Raises:
      Error: The WSDL for this service could not be found. Will also be raised
      if GenericApiService is initialized directly rather than by a subclass.
    """
        if self.__class__ == GenericApiService:
            raise Error('GenericApiService cannot be instantiated directly.')

        self._headers = headers
        self._config = config
        self._op_config = op_config
        self._lock = lock
        self._logger = logger
        self._service_name = service_name
        self._service_url = service_url
        self._wrap_lists = wrap_lists
        self._buffer_class = buffer_class
        self._namespace = namespace
        self._namespace_extractor = namespace_extractor
        self._method_proxies = {}

        wsdl_url = service_url + '?wsdl'
        try:
            self._soappyservice = SOAPpy.WSDL.Proxy(
                wsdl_url,
                noroot=1,
                http_proxy=self._op_config['http_proxy'],
                config=self._GetSoapConfig())
        except WSDLError:
            raise Error('Unable to locate WSDL at path \'%s\'' % wsdl_url)
        else:
            for method_key in self._soappyservice.methods:
                self._soappyservice.methods[method_key].location = service_url
Example #2
0
  def _VerifyHostName(self, hostname, certificate):
    """Checks the host name used against the host's SSL certificate.

    Args:
      hostname: string The name of the host this socket is connecting to.
      certificate: dictionary The SSL certificate returned by this host.

    Raises:
      Error: if the host name used for this connection is not listed as one of
      the names in the SSL certificate returned by this host.
    """
    if 'subjectAltName' in certificate:
      names = [name for (name_type, name) in certificate['subjectAltName']
               if name_type.lower() == 'dns']
    else:
      names = [value for ((key, value),) in certificate['subject']
               if key.lower() == 'commonname']

    for name in names:
      if re.match(name.replace('.', '\.').replace('*', '[^.]*'), hostname,
                  re.I) is not None:
        return

    raise Error('Host name "' + self.host + '" does not match any name listed '
                'in its SSL certificate!')
Example #3
0
 def connect(self):
   """Creates a socket and validates SSL certificates."""
   sock = socket.create_connection((self.host, self.port))
   try:
     self.sock = ssl.wrap_socket(sock, keyfile=self.key_file,
                                 certfile=self.cert_file,
                                 cert_reqs=self.cert_reqs,
                                 ca_certs=self.ca_certs)
   except ssl.SSLError, e:
     raise Error('Error validating SSL certificate for "' + self.host +
                 '": ' + str(e))
Example #4
0
def PurgeLog(log):
  """Clear content of a given log file.

  If the file cannot be opened, Error is raised.

  Args:
    log: str Absolute path to a log file.
  """
  try:
    fh = open(log, 'w')
    try:
      fh.write('')
    finally:
      fh.close()
  except IOError, e:
    raise Error(e)
Example #5
0
def DecodeNonASCIIText(text, encoding='utf-8'):
  """Decode a non-ASCII text into a unicode, using given encoding.

  A full list of supported encodings is available at
  http://docs.python.org/lib/standard-encodings.html. If unable to find given
  encoding, Error is raised.

  Args:
    text: str Text to encode.
    [optional]
    encoding: str Encoding format to use.

  Returns:
    tuple Decoded text with the text length, (text, len(text)).
  """
  if isinstance(text, unicode): return (text, len(text))

  dec_text = ''
  try:
    decoder = codecs.getdecoder(encoding)
    dec_text = decoder(text)
  except LookupError, e:
    msg = 'Unable to find \'%s\' encoding. %s.' % (encoding, e)
    raise Error(msg)
def main(client, places_email_address, places_access_token):
    # The placeholder type for location extensions.
    # See the Placeholder reference page for a list of all the placeholder types
    # and fields:
    # https://developers.google.com/adwords/api/docs/appendix/placeholders
    placeholder_location = 7

    # The maximum number of CustomerFeed ADD operation attempts to make before
    # throwing an exception.
    max_customer_feed_add_attempts = 10

    # Create a feed that will sync to the Google Places account specified by
    # places_email_address. Do not add FeedAttributes to this object,
    # as AdWords will add them automatically because this will be a
    # system generated feed.
    feed = {
        'name': 'Places feed #%s' % Utils.GetUniqueName(),
        'systemFeedGenerationData': {
            'xsi_type': 'PlacesLocationFeedData',
            'oAuthInfo': {
                'httpMethod': 'GET',
                'httpRequestUrl': 'https://www.google.com/local/add',
                'httpAuthorizationHeader': 'Bearer %s' % places_access_token
            },
            'emailAddress': places_email_address,
        },
        # Since this feed's feed items will be managed by AdWords, you must set
        # its origin to ADWORDS.
        'origin': 'ADWORDS'
    }

    # Create an operation to add the feed.
    places_operations = [{'operator': 'ADD', 'operand': feed}]

    places_response = client.GetFeedService(
        version='v201409').Mutate(places_operations)[0]
    added_feed = places_response['value'][0]
    print 'Added places feed with ID: %d\n' % added_feed['id']

    # Add a CustomerFeed that associates the feed with this customer for the
    # LOCATION placeholder type.
    customer_feed = {
        'feedId': added_feed['id'],
        'placeholderTypes': [placeholder_location],
        'matchingFunction': {
            'operator': 'IDENTITY',
            'lhsOperand': {
                'xsi_type': 'FunctionArgumentOperand',
                'type': 'BOOLEAN',
                'booleanValue': True
            }
        }
    }

    customer_feed_operation = {
        'xsi_type': 'CustomerFeedOperation',
        'operator': 'ADD',
        'operand': customer_feed
    }

    customer_feed_service = client.GetCustomerFeedService(version='v201409')
    added_customer_feed = None

    i = 0
    while i < max_customer_feed_add_attempts and added_customer_feed is None:
        try:
            added_customer_feed = customer_feed_service.Mutate(
                [customer_feed_operation])[0]['value'][0]
        except:
            # Wait using exponential backoff policy
            sleep_seconds = 2**i
            print(
                'Attempt %d to add the CustomerFeed was not successful.'
                'Waiting %d seconds before trying again.\n' %
                (i, sleep_seconds))
            time.sleep(sleep_seconds)
        i += 1

    if added_customer_feed is None:
        raise Error(
            'Could not create the CustomerFeed after %s attempts. Please'
            ' retry the CustomerFeed ADD operation later.' %
            max_customer_feed_add_attempts)

    print('Added CustomerFeed for feed ID %d and placeholder type %d\n' %
          (added_customer_feed['id'], added_customer_feed['placeholderTypes']))
                        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
        except AdWordsError, e:
            raise e
        except Error, e:
            if error: e = error
            raise Error(e)

    def _GetServiceName(self):
        return self.__service

    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.
Example #8
0
class WebService(object):
    """Implements WebService.

  Responsible for sending and recieving SOAP XML requests.
  """
    def __init__(self,
                 lib_sig,
                 headers,
                 config,
                 op_config,
                 url,
                 lock,
                 logger=None):
        """Inits WebService.

    Args:
      lib_sig: str Client library signature.
      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 Thread lock.
      logger: Logger Instance of Logger.
    """
        self._lib_sig = lib_sig
        self._headers = headers
        self._config = config
        self._op_config = op_config
        self._url = url
        self._lock = lock
        self._logger = logger
        self._start_time = 0
        self._stop_time = 0
        self._response = None

        if self._logger is None:
            self._logger = Logger(lib_sig, self._config['log_home'])

    def _ManageSoap(self,
                    buf,
                    log_handlers,
                    lib_url,
                    errors,
                    start_time,
                    stop_time,
                    error={}):
        """Manage SOAP XML message.

    Args:
      buf: SoapBuffer SOAP buffer.
      log_handlers: list Log handlers.
      lib_url: str URL of the project's home.
      errors: dict Map of errors available for the API.
      start_time: str Time before service call was invoked.
      stop_time: str Time after service call was invoked.
      [optional]
      error: dict Error, if any.
    """
        # Load trace errors, if any.
        if error and 'trace' in error:
            error_msg = error['trace']
        else:
            error_msg = ''

        # Check if response was successful or not.
        if error and 'data' in error:
            is_fault = True
        else:
            is_fault = False

        # Forward SOAP XML, errors, and other debugging data to console, external
        # file, both, or ignore. Each handler supports the following elements,
        #   tag: Config value for this handler. If left empty, will never write
        #        data to file.
        #   target: Target/destination represented by this handler (i.e. FILE,
        #           CONSOLE, etc.). Initially, it should be set to Logger.NONE.
        #   name: Name of the log file to use.
        #   data: Data to write.
        for handler in log_handlers:
            if handler['tag'] == 'xml_log':
                handler['target'] = Logger.NONE
                handler['data'] += str(
                    'StartTime: %s\n%s\n%s\n%s\n%s\nEndTime: %s' %
                    (start_time, buf.GetHeadersOut(), buf.GetSoapOut(),
                     buf.GetHeadersIn(), buf.GetSoapIn(), stop_time))
            elif handler['tag'] == 'request_log':
                handler['target'] = Logger.NONE
                handler['data'] += ' isFault=%s' % is_fault
            elif handler['tag'] == '':
                handler['target'] = Logger.NONE
                handler['data'] += 'DEBUG: %s' % error_msg
        for handler in log_handlers:
            if (handler['tag']
                    and Utils.BoolTypeConvert(self._config[handler['tag']])):
                handler['target'] = Logger.FILE
            # If debugging is On, raise handler's target two levels,
            #   NONE -> CONSOLE
            #   FILE -> FILE_AND_CONSOLE.
            if Utils.BoolTypeConvert(self._config['debug']):
                handler['target'] += 2

            if (handler['target'] != Logger.NONE and handler['data']
                    and handler['data'] != 'None'
                    and handler['data'] != 'DEBUG: '):
                self._logger.Log(handler['name'],
                                 handler['data'],
                                 log_level=Logger.DEBUG,
                                 log_handler=handler['target'])

        # If raw response is requested, no need to validate and throw appropriate
        # error. Up to the end user to handle successful or failed request.
        if Utils.BoolTypeConvert(self._config['raw_response']): return

        # Report SOAP fault.
        if is_fault:
            try:
                fault = buf.GetFaultAsDict()
                if not fault: msg = error['data']
            except:
                fault = None
                # An error is not a SOAP fault, but check if some other error.
                if error_msg:
                    msg = error_msg
                else:
                    msg = ('Unable to parse incoming SOAP XML. Please, file '
                           'a bug at %s/issues/list.' % lib_url)
            # Release thread lock.
            if self._lock.locked(): self._lock.release()
            if not fault and msg: return msg
            return fault
        return None

    def CallMethod(self,
                   headers,
                   config,
                   method_name,
                   params,
                   buf,
                   is_jaxb_api,
                   lib_sig,
                   lib_url,
                   service_name=None,
                   loc=None,
                   request=None):
        """Make an API call to specified method.

    Args:
      headers: dict Dictionary object with populated authentication
               credentials.
      config: dict Dictionary object with populated configuration values.
      method_name: str API method name.
      params: list List of parameters to send to the API method.
      buf: SoapBuffer SOAP buffer.
      is_jaxb_api: str Whether API uses JAXB.
      lib_sig: str Signature of the client library.
      lib_url: str URL of the project's home.
      [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.
    """
        # Temporarily redirect HTTP headers and SOAP from STDOUT into a buffer.
        if not Utils.BoolTypeConvert(self._config['raw_debug']):
            old_stdout = sys.stdout
            sys.stdout = buf

        response = ()
        error = {}
        try:
            # Fire off API request and handle the response.
            if config['soap_lib'] == SOAPPY:
                from adspygoogle.common.soappy import MessageHandler
                service = MessageHandler.GetServiceConnection(
                    headers, config, self._url, self._op_config['http_proxy'],
                    is_jaxb_api)

                if not is_jaxb_api:
                    response = MessageHandler.UnpackResponseAsDict(
                        service.invoke(method_name, params))
                else:
                    if not params: params = ('')
                    response = MessageHandler.UnpackResponseAsDict(
                        service._callWithBody(
                            MessageHandler.SetRequestParams(
                                config, method_name, params)))
            elif config['soap_lib'] == ZSI:
                from adspygoogle.common.zsi import MessageHandler
                service = MessageHandler.GetServiceConnection(
                    headers, config, self._op_config, self._url, service_name,
                    loc)
                request = MessageHandler.SetRequestParams(
                    request, params, is_jaxb_api)

                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

        # Restore STDOUT.
        if not Utils.BoolTypeConvert(self._config['raw_debug']):
            sys.stdout = old_stdout

        # When debugging mode is ON, fetch last traceback.
        if Utils.BoolTypeConvert(self._config['debug']):
            if Utils.LastStackTrace() and Utils.LastStackTrace() != 'None':
                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'])
            return Error(msg)

        if Utils.BoolTypeConvert(self._config['raw_response']):
            response = buf.GetRawSOAPIn()
        if error: response = error
        return response
Example #9
0
    def CallRawMethod(self, buf, host, soap_message):
        """Make an API call by posting raw SOAP XML message.

    Args:
      buf: SoapBuffer SOAP buffer.
      host: str Host against which to make API request.
      soap_message: str SOAP XML message.

    Returns:
      tuple Response from the API method.
    """
        http_header = {
            'post': '%s' % self._url,
            'host': host,
            'user_agent': '%s; WebService.py' % self._lib_sig,
            'content_type': 'text/xml; charset=\"UTF-8\"',
            'content_length': '%d' % len(soap_message),
            'soap_action': ''
        }

        self._start_time = time.strftime('%Y-%m-%d %H:%M:%S')
        buf.write((
            '%s Outgoing HTTP headers %s\nPOST %s\nHost: %s\nUser-Agent: '
            '%s\nContent-type: %s\nContent-length: %s\nSOAPAction: %s\n%s\n%s '
            'Outgoing SOAP %s\n%s\n%s\n' %
            ('*' * 3, '*' * 46, http_header['post'], http_header['host'],
             http_header['user_agent'], http_header['content_type'],
             http_header['content_length'], http_header['soap_action'],
             '*' * 72, '*' * 3, '*' * 54, soap_message, '*' * 72)))

        # Construct header and send SOAP message.
        web_service = httplib.HTTPS(http_header['host'])
        web_service.putrequest('POST', http_header['post'])
        web_service.putheader('Host', http_header['host'])
        web_service.putheader('User-Agent', http_header['user_agent'])
        web_service.putheader('Content-type', http_header['content_type'])
        web_service.putheader('Content-length', http_header['content_length'])
        web_service.putheader('SOAPAction', http_header['soap_action'])
        web_service.endheaders()
        web_service.send(soap_message)

        # Get response.
        status_code, status_message, header = web_service.getreply()
        self._response = web_service.getfile().read()

        header = str(header).replace('\r', '')
        buf.write(
            ('%s Incoming HTTP headers %s\n%s %s\n%s\n%s\n%s Incoming SOAP'
             ' %s\n%s\n%s\n' %
             ('*' * 3, '*' * 46, status_code, status_message, header, '*' * 72,
              '*' * 3, '*' * 54, self._response, '*' * 72)))
        self._stop_time = time.strftime('%Y-%m-%d %H:%M:%S')

        # Catch local errors prior to going down to the SOAP layer, which may not
        # exist for this error instance.
        if not buf.IsHandshakeComplete() or not buf.IsSoap():
            # The buffer contains non-XML data, most likely an HTML page. This
            # happens in the case of 502 errors.
            html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr())
            if html_error:
                msg = '%s' % html_error
            else:
                msg = 'Unknown error.'
            raise Error(msg)
def main(client, campaign_id):
  # Initialize appropriate service.
  feed_service = client.GetFeedService(version='v201406')
  feed_item_service = client.GetFeedItemService(version='v201406')
  feed_mapping_service = client.GetFeedMappingService(version='v201406')
  campaign_feed_service = client.GetCampaignFeedService(version='v201406')

  sitelinks_data = {}

  # Create site links feed first.
  site_links_feed = {
      'name': 'Feed For Site Links - %s' % Utils.GetUniqueName(),
      'attributes': [
          {'type': 'STRING', 'name': 'Link Text'},
          {'type': 'URL', 'name': 'Link URL'},
          {'type': 'STRING', 'name': 'Line 1 Description'},
          {'type': 'STRING', 'name': 'Line 2 Description'}
      ]
  }

  response = feed_service.mutate([
      {'operator': 'ADD', 'operand': site_links_feed}
  ])[0]
  if 'value' in response:
    feed = response['value'][0]
    link_text_feed_attribute_id = feed['attributes'][0]['id']
    link_url_feed_attribute_id = feed['attributes'][1]['id']
    line_1_feed_attribute_id = feed['attributes'][2]['id']
    line_2_feed_attribute_id = feed['attributes'][3]['id']
    print ('Feed with name \'%s\' and ID \'%s\' was added with' %
           (feed['name'], feed['id']))
    print ('\tText attribute ID \'%s\' and URL attribute ID \'%s\'.' %
           (link_text_feed_attribute_id, link_url_feed_attribute_id))
    print ('\tLine 1 attribute ID \'%s\' and Line 2 attribute ID \'%s\'.' %
           (line_1_feed_attribute_id, line_2_feed_attribute_id))
    sitelinks_data['feedId'] = feed['id']
    sitelinks_data['linkTextFeedId'] = link_text_feed_attribute_id
    sitelinks_data['linkUrlFeedId'] = link_url_feed_attribute_id
    sitelinks_data['line1FeedId'] = line_1_feed_attribute_id
    sitelinks_data['line2FeedId'] = line_2_feed_attribute_id
  else:
    raise Error('No feeds were added.')

  # Create site links feed items.
  items_data = [
      {'text': 'Home', 'url': 'http://www.example.com',
       'line1': 'Home line 1', 'line2': 'Home line 2'},
      {'text': 'Stores', 'url': 'http://www.example.com/stores',
       'line1': 'Stores line 1', 'line2': 'Stores line 2'},
      {'text': 'On Sale', 'url': 'http://www.example.com/sale',
       'line1': 'On Sale line 1', 'line2': 'On Sale line 2'},
      {'text': 'Support', 'url': 'http://www.example.com/support',
       'line1': 'Support line 1', 'line2': 'Support line 2'},
      {'text': 'Products', 'url': 'http://www.example.com/products',
       'line1': 'Products line 1', 'line2': 'Products line 2'},
      {'text': 'About', 'url': 'http://www.example.com/about',
       'line1': 'About line 1', 'line2': 'About line 2'}
  ]

  feed_items = []
  for item in items_data:
    feed_items.append({
        'feedId': sitelinks_data['feedId'],
        'attributeValues': [
            {
                'feedAttributeId': sitelinks_data['linkTextFeedId'],
                'stringValue': item['text']
            },
            {
                'feedAttributeId': sitelinks_data['linkUrlFeedId'],
                'stringValue': item['url']
            },
            {
                'feedAttributeId': sitelinks_data['line1FeedId'],
                'stringValue': item['line1']
            },
            {
                'feedAttributeId': sitelinks_data['line2FeedId'],
                'stringValue': item['line2']
            }
        ],
        # Optional: use the 'startTime' and 'endTime' keys to specify the time
        # period for the feed to deliver.  The example below will make the feed
        # start now and stop in one month.
        # Make sure you specify the datetime in the customer's time zone. You
        # can retrieve this from customer['dateTimeZone'].
        #
        # ['startTime']: datetime.datetime.now().strftime('%Y%m%d %H%M%S')
        # ['endTime']: (datetime.datetime.now() +
        #               relativedelta(months=1)).strftime('%Y%m%d %H%M%S')

        # Optional: use the 'scheduling' key to specify time and days of the
        # week for feed to deliver. This is a Beta feature.
    })

  feed_items_operations = [{'operator': 'ADD', 'operand': item} for item
                           in feed_items]

  response = feed_item_service.mutate(feed_items_operations)[0]
  if 'value' in response:
    sitelinks_data['feedItemIds'] = []
    for feed_item in response['value']:
      print 'Feed item with ID %s was added.' % feed_item['feedItemId']
      sitelinks_data['feedItemIds'].append(feed_item['feedItemId'])
  else:
    raise Error('No feed items were added.')

  # Create site links feed mapping.

  feed_mapping = {
      'placeholderType': PLACEHOLDER_SITELINKS,
      'feedId': sitelinks_data['feedId'],
      'attributeFieldMappings': [
          {
              'feedAttributeId': sitelinks_data['linkTextFeedId'],
              'fieldId': PLACEHOLDER_FIELD_SITELINK_LINK_TEXT
          },
          {
              'feedAttributeId': sitelinks_data['linkUrlFeedId'],
              'fieldId': PLACEHOLDER_FIELD_SITELINK_LINK_URL
          },
          {
              'feedAttributeId': sitelinks_data['line1FeedId'],
              'fieldId': PLACEHOLDER_FIELD_LINE_1_TEXT
          },
          {
              'feedAttributeId': sitelinks_data['line2FeedId'],
              'fieldId': PLACEHOLDER_FIELD_LINE_2_TEXT
          }
      ]
  }

  response = feed_mapping_service.mutate([
      {'operator': 'ADD', 'operand': feed_mapping}
  ])[0]
  if 'value' in response:
    feed_mapping = response['value'][0]
    print ('Feed mapping with ID %s and placeholder type %s was saved for feed'
           ' with ID %s.' %
           (feed_mapping['feedMappingId'], feed_mapping['placeholderType'],
            feed_mapping['feedId']))
  else:
    raise Error('No feed mappings were added.')

  # Create site links campaign feed.
  operands = []
  for feed_item_id in sitelinks_data['feedItemIds']:
    operands.append({
        'xsi_type': 'ConstantOperand',
        'type': 'LONG',
        'longValue': feed_item_id
    })

  feed_item_function = {
      'operator': 'IN',
      'lhsOperand': [
          {'xsi_type': 'RequestContextOperand', 'contextType': 'FEED_ITEM_ID'}
      ],
      'rhsOperand': operands
  }

  # Optional: to target to a platform, define a function and 'AND' it with the
  #           feed item ID link:
  platform_function = {
      'operator': 'EQUALS',
      'lhsOperand': [
          {
              'xsi_type': 'RequestContextOperand',
              'contextType': 'DEVICE_PLATFORM'
          }
      ],
      'rhsOperand': [
          {
              'xsi_type': 'ConstantOperand',
              'type': 'STRING',
              'stringValue': 'Mobile'
          }
      ]
  }
  combined_function = {
      'operator': 'AND',
      'lhsOperand': [
          {'xsi_type': 'FunctionOperand', 'value': feed_item_function},
          {'xsi_type': 'FunctionOperand', 'value': platform_function}
      ]
  }

  campaign_feed = {
      'feedId': sitelinks_data['feedId'],
      'campaignId': campaign_id,
      'matchingFunction': combined_function,
      # Specifying placeholder types on the CampaignFeed allows the same feed
      # to be used for different placeholders in different Campaigns.
      'placeholderTypes': [PLACEHOLDER_SITELINKS]
  }

  response = campaign_feed_service.mutate([
      {'operator': 'ADD', 'operand': campaign_feed}
  ])[0]
  if 'value' in response:
    campaign_feed = response['value'][0]
    print ('Campaign with ID %s was associated with feed with ID %s.' %
           (campaign_feed['campaignId'], campaign_feed['feedId']))
  else:
    raise Error('No campaign feeds were added.')
def main(client, campaign_id):
  # Initialize appropriate service.
  feed_service = client.GetFeedService(version='v201306')
  feed_item_service = client.GetFeedItemService(version='v201306')
  feed_mapping_service = client.GetFeedMappingService(version='v201306')
  campaign_feed_service = client.GetCampaignFeedService(version='v201306')

  sitelinks_data = {}

  # Create site links feed first.
  site_links_feed = {
      'name': 'Feed For Site Links',
      'attributes': [
          {'type': 'STRING', 'name': 'Link Text'},
          {'type': 'URL', 'name': 'Link URL'}
      ]
  }

  response = feed_service.mutate([
      {'operator': 'ADD', 'operand': site_links_feed}
  ])[0]
  if 'value' in response:
    feed = response['value'][0]
    link_text_feed_attribute_id = feed['attributes'][0]['id']
    link_url_feed_attribute_id = feed['attributes'][1]['id']
    print ('Feed with name \'%s\' and ID \'%s\' was added with' %
           (feed['name'], feed['id']))
    print ('\tText attribute ID \'%s\' and URL attribute ID \'%s\'.' %
           (link_text_feed_attribute_id, link_url_feed_attribute_id))
    sitelinks_data['feedId'] = feed['id']
    sitelinks_data['linkTextFeedId'] = link_text_feed_attribute_id
    sitelinks_data['linkUrlFeedId'] = link_url_feed_attribute_id
  else:
    raise Error('No feeds were added.')

  # Create site links feed items.
  items_data = [
      {'text': 'Home', 'url': 'http://www.example.com'},
      {'text': 'Stores', 'url': 'http://www.example.com/stores'},
      {'text': 'On Sale', 'url': 'http://www.example.com/sale'},
      {'text': 'Support', 'url': 'http://www.example.com/support'},
      {'text': 'Products', 'url': 'http://www.example.com/products'},
      {'text': 'About', 'url': 'http://www.example.com/about'}
  ]

  feed_items = []
  for item in items_data:
    feed_items.append({
        'feedId': sitelinks_data['feedId'],
        'attributeValues': [
            {
                'feedAttributeId': sitelinks_data['linkTextFeedId'],
                'stringValue': item['text']
            },
            {
                'feedAttributeId': sitelinks_data['linkUrlFeedId'],
                'stringValue': item['url']
            }
        ]
    })

  feed_items_operations = [{'operator': 'ADD', 'operand': item} for item
                           in feed_items]

  response = feed_item_service.mutate(feed_items_operations)[0]
  if 'value' in response:
    sitelinks_data['feedItemIds'] = []
    for feed_item in response['value']:
      print 'Feed item with ID %s was added.' % feed_item['feedItemId']
      sitelinks_data['feedItemIds'].append(feed_item['feedItemId'])
  else:
    raise Error('No feed items were added.')

  # Create site links feed mapping.

  feed_mapping = {
      'placeholderType': PLACEHOLDER_SITELINKS,
      'feedId': sitelinks_data['feedId'],
      'attributeFieldMappings': [
          {
              'feedAttributeId': sitelinks_data['linkTextFeedId'],
              'fieldId': PLACEHOLDER_FIELD_SITELINK_LINK_TEXT
          },
          {
              'feedAttributeId': sitelinks_data['linkUrlFeedId'],
              'fieldId': PLACEHOLDER_FIELD_SITELINK_LINK_URL
          }
      ]
  }

  response = feed_mapping_service.mutate([
      {'operator': 'ADD', 'operand': feed_mapping}
  ])[0]
  if 'value' in response:
    feed_mapping = response['value'][0]
    print ('Feed mapping with ID %s and placeholder type %s was saved for feed'
           ' with ID %s.' %
           (feed_mapping['feedMappingId'], feed_mapping['placeholderType'],
            feed_mapping['feedId']))
  else:
    raise Error('No feed mappings were added.')

  # Create site links campaign feed.
  operands = []
  for feed_item_id in sitelinks_data['feedItemIds']:
    operands.append({
        'xsi_type': 'ConstantOperand',
        'type': 'LONG',
        'longValue': feed_item_id
    })

  feed_item_function = {
      'operator': 'IN',
      'lhsOperand': [
          {'xsi_type': 'RequestContextOperand', 'contextType': 'FEED_ITEM_ID'}
      ],
      'rhsOperand': operands
  }

  # Optional: to target to a platform, define a function and 'AND' it with the
  #           feed item ID link:
  platform_function = {
      'operator': 'EQUALS',
      'lhsOperand': [
          {
              'xsi_type': 'RequestContextOperand',
              'contextType': 'DEVICE_PLATFORM'
          }
      ],
      'rhsOperand': [
          {
              'xsi_type': 'ConstantOperand',
              'type': 'STRING',
              'stringValue': 'Mobile'
          }
      ]
  }
  combined_function = {
      'operator': 'AND',
      'lhsOperand': [
          {'xsi_type': 'FunctionOperand', 'value': feed_item_function},
          {'xsi_type': 'FunctionOperand', 'value': platform_function}
      ]
  }

  campaign_feed = {
      'feedId': sitelinks_data['feedId'],
      'campaignId': campaign_id,
      'matchingFunction': combined_function,
      # Specifying placeholder types on the CampaignFeed allows the same feed
      # to be used for different placeholders in different Campaigns.
      'placeholderTypes': [PLACEHOLDER_SITELINKS]
  }

  response = campaign_feed_service.mutate([
      {'operator': 'ADD', 'operand': campaign_feed}
  ])[0]
  if 'value' in response:
    campaign_feed = response['value'][0]
    print ('Campaign with ID %s was associated with feed with ID %s.' %
           (campaign_feed['campaignId'], campaign_feed['feedId']))
  else:
    raise Error('No campaign feeds were added.')
Example #12
0
    def DownloadCsvReport(self, report_job_id=-1, report_xml=''):
        """Download and return report data in CSV format.

    Report consists of two parts: table and totals. Table contains column
    names and rows of data. The outgoing CSV will contain only columns and
    data rows. The totals are not included, but can be easily calculated from
    the CSV data.

    Args:
      [optional]
      report_job_id: str ID of the report job.
      report_xml: str Report in XML format. Used for testing and debugging.

    Returns:
      str Report data if all data is in ASCII.
      unicode Report data if report contains non-ASCII characters.
      None If report failed.
    """
        # Get XML report data.
        if not report_xml and report_job_id > -1:
            report_xml = self.DownloadXmlReport(report_job_id)
        if report_xml is None:
            return None

        # Prepare XML for DOM construction.
        report_xml = report_xml.replace(
            '<?xml version="1.0" standalone="yes"?>', '')
        csv_rows = []
        try:
            # Construct DOM object.
            dom = minidom.parseString(report_xml)

            # Get data columns.
            columns = []
            column_dom = dom.getElementsByTagName('column')
            for column_item in column_dom:
                if column_item.hasAttributes():
                    columns.append(column_item.attributes.item(0).value)

            # Get data rows.
            rows = []
            row_dom = dom.getElementsByTagName('row')
            for row_item in row_dom:
                if row_item.hasAttributes():
                    attrs = row_item.attributes
                    row = {}
                    for index in range(attrs.length):
                        row[attrs.item(index).name] = attrs.item(index).value
                    rows.append(row)

            # Combine columns and rows into CSV format.
            csv_rows = [[column for column in columns]]
            for row in rows:
                csv_rows.append([row[column] for column in columns])
        except:
            msg = ('Unable to parse report\'s data. Please, file a bug at '
                   'http://code.google.com/p/google-api-adwords-python-lib/'
                   'issues/list.')
            raise Error(msg)

        buffer = cStringIO.StringIO()
        # A workaround for reports that include non-ASCII characters (i.e. ΓΈ).
        try:
            csv.writer(buffer).writerows(csv_rows)
        except (UnicodeEncodeError, Exception):
            unicode_csv_rows = []
            for row in csv_rows:
                unicode_csv_rows.append(','.join(
                    [Utils.CsvEscape(item) for item in row]))
            return unicode('\n'.join(unicode_csv_rows))
        return buffer.getvalue().rstrip()
Example #13
0
    def CallRawMethod(self, soap_message):
        """Makes an API call by POSTing a raw SOAP XML message to the server.

    Args:
      soap_message: str SOAP XML message.

    Returns:
      tuple Raw XML response from the API method.

    Raises:
      Error: if the SOAP call is not successful. Most likely this is the result
      of the server sending back an HTTP error, such as a 502.
    """

        self._lock.acquire()
        try:
            buf = self._buffer_class(xml_parser=self._config['xml_parser'],
                                     pretty_xml=Utils.BoolTypeConvert(
                                         self._config['pretty_xml']))

            http_header = {
                'post': self._service_url,
                'host': Utils.GetNetLocFromUrl(self._op_config['server']),
                'user_agent': '%s; CallRawMethod' % self.__class__.__name__,
                'content_type': 'text/xml; charset=\"UTF-8\"',
                'content_length': '%d' % len(soap_message),
                'soap_action': ''
            }

            if self._headers.get('oauth2credentials'):
                self._headers['oauth2credentials'].apply(http_header)

            self._start_time = time.strftime('%Y-%m-%d %H:%M:%S')
            buf.write(
                '%s Outgoing HTTP headers %s\nPOST %s\nHost: %s\nUser-Agent: '
                '%s\nContent-type: %s\nContent-length: %s\nSOAPAction: %s\n' %
                ('*' * 3, '*' * 46, http_header['post'], http_header['host'],
                 http_header['user_agent'], http_header['content_type'],
                 http_header['content_length'], http_header['soap_action']))
            if self._headers.get('oauth2credentials'):
                buf.write('Authorization: ' + http_header['Authorization'] +
                          '\n')
            buf.write('%s\n%s Outgoing SOAP %s\n%s\n%s\n' %
                      ('*' * 72, '*' * 3, '*' * 54, soap_message, '*' * 72))

            if self._op_config['http_proxy']:
                real_address = self._op_config['http_proxy']
            else:
                real_address = http_header['host']

            # Construct header and send SOAP message.
            web_service = httplib.HTTPS(real_address)
            web_service.putrequest('POST', http_header['post'])
            web_service.putheader('Host', http_header['host'])
            web_service.putheader('User-Agent', http_header['user_agent'])
            web_service.putheader('Content-type', http_header['content_type'])
            web_service.putheader('Content-length',
                                  http_header['content_length'])
            web_service.putheader('SOAPAction', http_header['soap_action'])
            if self._headers.get('oauth2credentials'):
                web_service.putheader('Authorization',
                                      http_header['Authorization'])
            web_service.endheaders()
            web_service.send(soap_message)

            # Get response.
            status_code, status_message, header = web_service.getreply()
            response = web_service.getfile().read()

            header = str(header).replace('\r', '')
            buf.write(
                ('%s Incoming HTTP headers %s\n%s %s\n%s\n%s\n%s Incoming SOAP'
                 ' %s\n%s\n%s\n' %
                 ('*' * 3, '*' * 46, status_code, status_message, header,
                  '*' * 72, '*' * 3, '*' * 54, response, '*' * 72)))
            self._stop_time = time.strftime('%Y-%m-%d %H:%M:%S')

            # Catch local errors prior to going down to the SOAP layer, which may not
            # exist for this error instance.
            if not buf.IsHandshakeComplete() or not buf.IsSoap():
                # The buffer contains non-XML data, most likely an HTML page. This
                # happens in the case of 502 errors.
                html_error = Utils.GetErrorFromHtml(buf.GetBufferAsStr())
                if html_error:
                    msg = html_error
                else:
                    msg = 'Unknown error.'
                raise Error(msg)

            self._HandleLogsAndErrors(buf, self._start_time, self._stop_time)
        finally:
            self._lock.release()
        if self._config['wrap_in_tuple']:
            response = MessageHandler.WrapInTuple(response)
        return response
Example #14
0
        def CallMethod(*args):
            """Perform a SOAP call."""
            try:
                self._lock.acquire()
                self._ReadyOAuth()
                self._ReadyCompression()
                self._SetHeaders()

                args = self._TakeActionOnSoapCall(method_name, args)
                method_info = self._GetMethodInfo(method_name)
                method_attrs_holder = None

                if len(method_info[MethodInfoKeys.INPUTS]) > 1:
                    self._ConfigureArgOrder(method_name,
                                            method_info[MethodInfoKeys.INPUTS])

                if not method_info[MethodInfoKeys.INPUTS]:
                    # Don't put any namespaces other than this service's namespace on
                    # calls with no input params.
                    method_attrs_holder = self._soappyservice.soapproxy.methodattrs
                    self._soappyservice.soapproxy.methodattrs = {
                        'xmlns': self._namespace
                    }

                if len(args) != len(method_info[MethodInfoKeys.INPUTS]):
                    raise TypeError(''.join([
                        method_name + '() takes exactly ',
                        str(
                            len(self._soappyservice.methods[method_name].
                                inparams)), ' argument(s). (',
                        str(len(args)), ' given)'
                    ]))

                ksoap_args = {}
                for i in range(len(method_info[MethodInfoKeys.INPUTS])):
                    if Utils.BoolTypeConvert(self._config['strict']):
                        SanityCheck.SoappySanityCheck(
                            self._soappyservice, args[i], method_info[
                                MethodInfoKeys.INPUTS][i][MethodInfoKeys.NS],
                            method_info[MethodInfoKeys.INPUTS][i][
                                MethodInfoKeys.TYPE],
                            method_info[MethodInfoKeys.INPUTS][i][
                                MethodInfoKeys.MAX_OCCURS])

                    element_name = str(method_info[MethodInfoKeys.INPUTS][i][
                        MethodInfoKeys.ELEMENT_NAME])

                    ksoap_args[element_name] = MessageHandler.PackForSoappy(
                        args[i], method_info[MethodInfoKeys.INPUTS][i][
                            MethodInfoKeys.NS], method_info[
                                MethodInfoKeys.INPUTS][i][MethodInfoKeys.TYPE],
                        self._soappyservice, self._wrap_lists,
                        self._namespace_extractor)

                ksoap_args = self._TakeActionOnPackedArgs(
                    method_name, ksoap_args)

                buf = self._buffer_class(xml_parser=self._config['xml_parser'],
                                         pretty_xml=Utils.BoolTypeConvert(
                                             self._config['pretty_xml']))
                sys_stdout_monkey_lock.acquire()
                try:
                    old_stdout = sys.stdout
                    sys.stdout = buf

                    error = {}
                    response = None
                    start_time = time.strftime('%Y-%m-%d %H:%M:%S')
                    try:
                        response = MessageHandler.UnpackResponseAsDict(
                            soap_service_method(**ksoap_args))
                    except Exception, e:
                        error['data'] = e
                    stop_time = time.strftime('%Y-%m-%d %H:%M:%S')
                    # Restore stdout
                    sys.stdout = old_stdout
                finally:
                    sys_stdout_monkey_lock.release()

                if isinstance(response, Error):
                    error = response

                if not Utils.BoolTypeConvert(self._config['raw_debug']):
                    self._HandleLogsAndErrors(buf, start_time, stop_time,
                                              error)

                # When debugging mode is ON, fetch last traceback.
                if Utils.BoolTypeConvert(self._config['debug']):
                    if Utils.LastStackTrace(
                    ) and Utils.LastStackTrace() != 'None':
                        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 = 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'])
                    return Error(msg)

                if Utils.BoolTypeConvert(self._config['raw_response']):
                    response = buf.GetRawSoapIn()
                elif error:
                    response = error
                else:
                    output_types = [
                        (out_param[MethodInfoKeys.NS],
                         out_param[MethodInfoKeys.TYPE],
                         out_param[MethodInfoKeys.MAX_OCCURS])
                        for out_param in method_info[MethodInfoKeys.OUTPUTS]
                    ]
                    response = MessageHandler.RestoreListTypeWithSoappy(
                        response, self._soappyservice, output_types)

                if Utils.BoolTypeConvert(self._config['wrap_in_tuple']):
                    response = MessageHandler.WrapInTuple(response)

                # Restore method_attrs if they were over-ridden
                if method_attrs_holder:
                    self._soappyservice.soapproxy.methodattrs = method_attrs_holder

                return response