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