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 Exception: Serves as the base exception that other service-related
17 exception objects 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)
26
28 """
29 Exception: The request could not be handled at this time. This is generally
30 a server problem.
31 """
32 pass
33
35 """
36 Exception: These are generally problems with the client-provided data.
37 """
38 pass
39
41 """
42 Exception: There is probably a problem in the data you provided.
43 """
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
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
75
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
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
109 """
110 Sets up the WebAuthenticationDetail node. This is required for all
111 requests.
112 """
113
114 WebAuthenticationCredential = self.client.factory.create('WebAuthenticationCredential')
115 WebAuthenticationCredential.Key = self.config_obj.key
116 WebAuthenticationCredential.Password = self.config_obj.password
117
118
119 WebAuthenticationDetail = self.client.factory.create('WebAuthenticationDetail')
120 WebAuthenticationDetail.UserCredential = WebAuthenticationCredential
121 self.WebAuthenticationDetail = WebAuthenticationDetail
122
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
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
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
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
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
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
189 """
190 Creates and returns a WSDL object of the specified type.
191 """
192 return self.client.factory.create(type_name)
193
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
204 try:
205
206
207 if send_function:
208
209 self.response = send_function()
210 else:
211
212 self.response = self._assemble_and_send_request()
213 except suds.WebFault:
214
215
216
217 raise SchemaValidationError()
218
219
220
221 self.__check_response_for_fedex_error()
222
223
224 self._check_response_for_request_errors()
225
226
227 self.logger.debug("== FEDEX QUERY RESULT ==")
228 self.logger.debug(self.response)
229