fedex.base_service

Package fedex :: Module base_service |
[hide private]
[frames] | no frames]

# Source Code for Module fedex.base_service

      1  """
      2  The L{base_service} module contains classes that form the low level foundations
      3  of the Web Service API. Things that many different kinds of requests have in
      4  common may be found here.
      5
      6  In particular, the L{FedexBaseService} class handles most of the basic,
      7  repetetive setup work that most requests do.
      8  """
      9  import os
     10  import logging
     11  import suds
     12  from suds.client import Client
     13
     14 -class FedexBaseServiceException(Exception):
     15      """
     16      Exception: Serves as the base exception that other service-related
     17      exception objects are sub-classed from.
     18      """
     19 -    def __init__(self, error_code, value):
     20          self.error_code = error_code
     21          self.value = value
     22 -    def __unicode__(self):
     23          return "%s (Error code: %s)" % (repr(self.value), self.error_code)
     24 -    def __str__(self):
     25          return self.__unicode__()
     26
     27 -class FedexFailure(FedexBaseServiceException):
     28      """
     29      Exception: The request could not be handled at this time. This is generally
     30      a server problem.
     31      """
     32      pass
     33
     34 -class FedexError(FedexBaseServiceException):
     35      """
     36      Exception: These are generally problems with the client-provided data.
     37      """
     38      pass
     39
     40 -class SchemaValidationError(FedexBaseServiceException):
     41      """
     42      Exception: There is probably a problem in the data you provided.
     43      """
     44 -    def __init__(self):
     45          self.error_code = -1
     46          self.value = "suds encountered an error validating your data against this service's WSDL schema. Please double-check for missing or invalid values, filling all required fields."
     47
     48 -class FedexBaseService(object):
     49      """
     50      This class is the master class for all Fedex request objects. It gets all
     51      of the common SOAP objects created via suds and populates them with
     52      values from a L{FedexConfig} object, along with keyword arguments
     53      via L{__init__}.
     54
     55      @note: This object should never be used directly, use one of the included
     56          sub-classes.
     57      """
     58 -    def __init__(self, config_obj, wsdl_name, *args, **kwargs):
     59          """
     60          This constructor should only be called by children of the class. As is
     61          such, only the optional keyword arguments caught by C{**kwargs} will
     62          be documented.
     63
     64          @type customer_transaction_id: L{str}
     65          @keyword customer_transaction_id: A user-specified identifier to
     66              differentiate this transaction from others. This value will be
     67              returned with the response from Fedex.
     68          """
     69          self.logger = logging.getLogger('fedex')
     70          """@ivar: Python logger instance with name 'fedex'."""
     71          self.config_obj = config_obj
     72          """@ivar: The FedexConfig object to pull auth info from."""
     73
     74          # If the config object is set to use the test server, point
     75          # suds at the test server WSDL directory.
     76          if config_obj.use_test_server:
     77              self.logger.info("Using test server.")
     78              self.wsdl_path = os.path.join(config_obj.wsdl_path,
     79                                            'test_server_wsdl', wsdl_name)
     80          else:
     81              self.logger.info("Using production server.")
     82              self.wsdl_path = os.path.join(config_obj.wsdl_path, wsdl_name)
     83
     84          self.client = Client('file://%s' % self.wsdl_path)
     85
     86          #print self.client
     87
     88          self.VersionId = None
     89          """@ivar: Holds details on the version numbers of the WSDL."""
     90          self.WebAuthenticationDetail = None
     91          """@ivar: WSDL object that holds authentication info."""
     92          self.ClientDetail = None
     93          """@ivar: WSDL object that holds client account details."""
     94          self.response = None
     95          """@ivar: The response from Fedex. You will want to pick what you
     96              want out here here. This object does have a __str__() method,
     97              you'll want to print or log it to see what possible values
     98              you can pull."""
     99          self.TransactionDetail = None
    100          """@ivar: Holds customer-specified transaction IDs."""
    101
    102          self.__set_web_authentication_detail()
    103          self.__set_client_detail()
    104          self.__set_version_id()
    105          self.__set_transaction_detail(*args, **kwargs)
    106          self._prepare_wsdl_objects()
    107
    108 -    def __set_web_authentication_detail(self):
    109          """
    110          Sets up the WebAuthenticationDetail node. This is required for all
    111          requests.
    112          """
    113          # Start of the authentication stuff.
    114          WebAuthenticationCredential = self.client.factory.create('WebAuthenticationCredential')
    115          WebAuthenticationCredential.Key = self.config_obj.key
    116          WebAuthenticationCredential.Password = self.config_obj.password
    117
    118          # Encapsulates the auth credentials.
    119          WebAuthenticationDetail = self.client.factory.create('WebAuthenticationDetail')
    120          WebAuthenticationDetail.UserCredential = WebAuthenticationCredential
    121          self.WebAuthenticationDetail = WebAuthenticationDetail
    122
    123 -    def __set_client_detail(self):
    124          """
    125          Sets up the ClientDetail node, which is required for all shipping
    126          related requests.
    127          """
    128          ClientDetail = self.client.factory.create('ClientDetail')
    129          ClientDetail.AccountNumber = self.config_obj.account_number
    130          ClientDetail.MeterNumber = self.config_obj.meter_number
    131          ClientDetail.IntegratorId = self.config_obj.integrator_id
    132          self.ClientDetail = ClientDetail
    133
    134 -    def __set_transaction_detail(self, *args, **kwargs):
    135          """
    136          Checks kwargs for 'customer_transaction_id' and sets it if present.
    137          """
    138          customer_transaction_id = kwargs.get('customer_transaction_id', False)
    139          if customer_transaction_id:
    140              TransactionDetail = self.client.factory.create('TransactionDetail')
    141              TransactionDetail.CustomerTransactionId = customer_transaction_id
    142              self.logger.debug(TransactionDetail)
    143              self.TransactionDetail = TransactionDetail
    144
    145 -    def __set_version_id(self):
    146          """
    147          Pulles the versioning info for the request from the child request.
    148          """
    149          VersionId = self.client.factory.create('VersionId')
    150          VersionId.ServiceId = self._version_info['service_id']
    151          VersionId.Major = self._version_info['major']
    152          VersionId.Intermediate = self._version_info['intermediate']
    153          VersionId.Minor = self._version_info['minor']
    154          self.logger.debug(VersionId)
    155          self.VersionId = VersionId
    156
    157 -    def __prepare_wsdl_objects(self):
    158          """
    159          This method should be over-ridden on each sub-class. It instantiates
    160          any of the required WSDL objects so the user can just print their
    161          __str__() methods and see what they need to fill in.
    162          """
    163          pass
    164
    165 -    def __check_response_for_fedex_error(self):
    166          """
    167          This checks the response for general Fedex errors that aren't related
    168          to any one WSDL.
    169          """
    170          if self.response.HighestSeverity == "FAILURE":
    171              for notification in self.response.Notifications:
    172                  if notification.Severity == "FAILURE":
    173                      raise FedexFailure(notification.Code,
    174                                         notification.Message)
    175
    176 -    def _check_response_for_request_errors(self):
    177          """
    178          Override this in each service module to check for errors that are
    179          specific to that module. For example, invalid tracking numbers in
    180          a Tracking request.
    181          """
    182          if self.response.HighestSeverity == "ERROR":
    183              for notification in self.response.Notifications:
    184                  if notification.Severity == "ERROR":
    185                      raise FedexError(notification.Code,
    186                                       notification.Message)
    187
    188 -    def create_wsdl_object_of_type(self, type_name):
    189          """
    190          Creates and returns a WSDL object of the specified type.
    191          """
    192          return self.client.factory.create(type_name)
    193
    194 -    def send_request(self, send_function=None):
    195          """
    196          Sends the assembled request on the child object.
    197          @type send_function: function reference
    198          @keyword send_function: A function reference (passed without the
    199              parenthesis) to a function that will send the request. This
    200              allows for overriding the default function in cases such as
    201              validation requests.
    202          """
    203          # Send the request and get the response back.
    204          try:
    205              # If the user has overridden the send function, use theirs
    206              # instead of the default.
    207              if send_function:
    208                  # Follow the overridden function.
    209                  self.response = send_function()
    210              else:
    211                  # Default scenario, business as usual.
    212                  self.response = self._assemble_and_send_request()
    213          except suds.WebFault:
    214              # When this happens, throw an informative message reminding the
    215              # user to check all required variables, making sure they are
    216              # populated and valid.
    217              raise SchemaValidationError()
    218
    219          # Check the response for general Fedex errors/failures that aren't
    220          # specific to any given WSDL/request.
    221          self.__check_response_for_fedex_error()
    222          # Check the response for errors specific to the particular request.
    223          # This is handled by an overridden method on the child object.
    224          self._check_response_for_request_errors()
    225
    226          # Debug output.
    227          self.logger.debug("== FEDEX QUERY RESULT ==")
    228          self.logger.debug(self.response)
    229