@@ -120,6 +120,9 @@ def __init__(self):
120120 #: Exception any exceptions that got caught
121121 self .exception = None
122122
123+ #: String Url parameters used in the Rest transport
124+ self .params = None
125+
123126
124127class SoftLayerListResult (list ):
125128 """A SoftLayer API list result."""
@@ -165,8 +168,9 @@ def __call__(self, request):
165168 headers [header_name ] = {'id' : request .identifier }
166169
167170 if request .mask is not None :
168- headers .update (_format_object_mask_xmlrpc (request .mask ,
169- request .service ))
171+ request .mask = _format_object_mask (request .mask )
172+ headers .update (_format_object_mask_xmlrpc (request .mask , request .service ))
173+
170174
171175 if request .filter is not None :
172176 headers ['%sObjectFilter' % request .service ] = request .filter
@@ -261,7 +265,9 @@ def print_reproduceable(self, request):
261265ElementTree.dump(xml)
262266==========================''' )
263267
268+
264269 safe_payload = re .sub (r'<string>[a-z0-9]{64}</string>' , r'<string>API_KEY_GOES_HERE</string>' , request .payload )
270+ safe_payload = re .sub (r'(\s+)' , r' ' , safe_payload )
265271 substitutions = dict (url = request .url , payload = safe_payload , transport_headers = request .transport_headers ,
266272 timeout = self .timeout , verify = request .verify , cert = request .cert , proxy = _proxies_dict (self .proxy ))
267273 return output .substitute (substitutions )
@@ -301,7 +307,8 @@ def __call__(self, request):
301307 """
302308 params = request .headers .copy ()
303309 if request .mask :
304- params ['objectMask' ] = _format_object_mask (request .mask )
310+ request .mask = _format_object_mask (request .mask )
311+ params ['objectMask' ] = request .mask
305312
306313 if request .limit :
307314 params ['limit' ] = request .limit
@@ -312,6 +319,8 @@ def __call__(self, request):
312319 if request .filter :
313320 params ['objectFilter' ] = json .dumps (request .filter )
314321
322+ request .params = params
323+
315324 auth = None
316325 if request .transport_user :
317326 auth = requests .auth .HTTPBasicAuth (
@@ -331,9 +340,9 @@ def __call__(self, request):
331340 method = 'POST'
332341 body ['parameters' ] = request .args
333342
334- raw_body = None
343+
335344 if body :
336- raw_body = json .dumps (body )
345+ request . payload = json .dumps (body )
337346
338347 url_parts = [self .endpoint_url , request .service ]
339348 if request .identifier is not None :
@@ -342,32 +351,29 @@ def __call__(self, request):
342351 if request .method is not None :
343352 url_parts .append (request .method )
344353
345- url = '%s.%s' % ('/' .join (url_parts ), 'json' )
354+ request . url = '%s.%s' % ('/' .join (url_parts ), 'json' )
346355
347356 # Prefer the request setting, if it's not None
348- verify = request .verify
349- if verify is None :
350- verify = self .verify
351357
352- LOGGER .debug ("=== REQUEST ===" )
353- LOGGER .debug (url )
354- LOGGER .debug (request .transport_headers )
355- LOGGER .debug (raw_body )
358+ if request .verify is None :
359+ request .verify = self .verify
360+
356361 try :
357- resp = self .client .request (method , url ,
362+ resp = self .client .request (method , request . url ,
358363 auth = auth ,
359364 headers = request .transport_headers ,
360- params = params ,
361- data = raw_body ,
365+ params = request . params ,
366+ data = request . payload ,
362367 timeout = self .timeout ,
363- verify = verify ,
368+ verify = request . verify ,
364369 cert = request .cert ,
365370 proxies = _proxies_dict (self .proxy ))
366- LOGGER . debug ( "=== RESPONSE ===" )
367- LOGGER . debug ( resp .headers )
368- LOGGER . debug ( resp . text )
371+
372+ request . url = resp .url
373+
369374 resp .raise_for_status ()
370375 result = json .loads (resp .text )
376+ request .result = result
371377
372378 if isinstance (result , list ):
373379 return SoftLayerListResult (
@@ -376,11 +382,36 @@ def __call__(self, request):
376382 return result
377383 except requests .HTTPError as ex :
378384 message = json .loads (ex .response .text )['error' ]
379- raise exceptions . SoftLayerAPIError ( ex .response .status_code ,
380- message )
385+ request . url = ex .response .url
386+ raise exceptions . SoftLayerAPIError ( ex . response . status_code , message )
381387 except requests .RequestException as ex :
388+ request .url = ex .response .url
382389 raise exceptions .TransportError (0 , str (ex ))
383390
391+ def print_reproduceable (self , request ):
392+ """Prints out the minimal python code to reproduce a specific request
393+
394+ The will also automatically replace the API key so its not accidently exposed.
395+
396+ :param request request: Request object
397+ """
398+ command = "curl -u $SL_USER:$SL_APIKEY -X {method} -H {headers} {data} '{uri}'"
399+
400+ method = REST_SPECIAL_METHODS .get (request .method )
401+
402+ if method is None :
403+ method = 'GET'
404+ if request .args :
405+ method = 'POST'
406+
407+ data = ''
408+ if request .payload is not None :
409+ data = "-d '{}'" .format (request .payload )
410+
411+ headers = ['"{0}: {1}"' .format (k , v ) for k , v in request .transport_headers .items ()]
412+ headers = " -H " .join (headers )
413+ return command .format (method = method , headers = headers , data = data , uri = request .url )
414+
384415
385416class DebugTransport (object ):
386417 """Transport that records API call timings."""
@@ -414,11 +445,14 @@ def pre_transport_log(self, call):
414445 LOGGER .warning ("Calling: {}::{}(id={})" .format (call .service , call .method , call .identifier ))
415446
416447 def post_transport_log (self , call ):
417- LOGGER .debug (self . transport . print_reproduceable (call ))
448+ LOGGER .debug ("Returned Data: \n {}" . format (call . result ))
418449
419450 def get_last_calls (self ):
420451 return self .requests
421452
453+ def print_reproduceable (self , call ):
454+ return self .transport .print_reproduceable (call )
455+
422456class TimingTransport (object ):
423457 """Transport that records API call timings."""
424458
@@ -480,7 +514,6 @@ def _format_object_mask_xmlrpc(objectmask, service):
480514 mheader = '%sObjectMask' % service
481515 else :
482516 mheader = 'SoftLayer_ObjectMask'
483- objectmask = _format_object_mask (objectmask )
484517
485518 return {mheader : {'mask' : objectmask }}
486519
@@ -495,6 +528,7 @@ def _format_object_mask(objectmask):
495528
496529 """
497530 objectmask = objectmask .strip ()
531+ objectmask = re .sub (r'(\s+)' , r' ' , objectmask )
498532 if (not objectmask .startswith ('mask' ) and
499533 not objectmask .startswith ('[' )):
500534 objectmask = "mask[%s]" % objectmask
0 commit comments