Skip to content

Commit 17a0ec8

Browse files
committed
<fix>[testlib]: fix multiple bugs in Python SDK template
1. Rewrite async poll URL to use client-configured hostname 2. Fix _error() field: dual-write err.description + err.desc 3. Fix mutable default arg headers={} in _json_http 4. Fix re.findall regex missing escape for path param validation 5. Fix min_length validation using > instead of < 6. Fix path param check to 'if u not in params' 7. Fix bare except to except Exception 8. Remove duplicate import time 9. Decode rsp.data bytes to str for readable error messages 10. Handle IPv6 addresses in poll URL with bracket notation 11. Fix missing bracket and format arg in poll error message Resolves: ZSTAC-83925 Change-Id: I7a7062676b676a6f67646c7862706365666a6b6e
1 parent 3bcfed2 commit 17a0ec8

1 file changed

Lines changed: 34 additions & 9 deletions

File tree

testlib/src/main/resources/zssdk.py

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import hmac
1919
from hashlib import sha1
2020
import datetime
21-
import time
2221

2322
try:
2423
int_types = (int, long)
@@ -58,7 +57,7 @@ def _exception_safe(func):
5857
def wrap(*args, **kwargs):
5958
try:
6059
func(*args, **kwargs)
61-
except:
60+
except Exception:
6261
print(traceback.format_exc())
6362

6463
return wrap
@@ -80,6 +79,7 @@ def _http_error(status, body=None):
8079
def _error(code, desc, details):
8180
err = ErrorCode()
8281
err.code = code
82+
err.description = desc
8383
err.desc = desc
8484
err.details = details
8585
return {'error': err}
@@ -181,7 +181,7 @@ def _check_params(self):
181181
if value is not None and isinstance(value, str) and annotation.max_length and len(value) > annotation.max_length:
182182
raise SdkError('invalid length[%s] of the parameter[%s], the max allowed length is %s' % (len(value), param_name, annotation.max_length))
183183

184-
if value is not None and isinstance(value, str) and annotation.min_length and len(value) > annotation.min_length:
184+
if value is not None and isinstance(value, str) and annotation.min_length and len(value) < annotation.min_length:
185185
raise SdkError('invalid length[%s] of the parameter[%s], the minimal allowed length is %s' % (len(value), param_name, annotation.min_length))
186186

187187
if value is not None and isinstance(value, list) and annotation.non_empty is True and len(value) == 0:
@@ -235,11 +235,11 @@ def _url(self):
235235
elements.append('/v1')
236236

237237
path = self.PATH.replace('{', '${')
238-
unresolved = re.findall('${(.+?)}', path)
238+
unresolved = re.findall(r'\$\{(.+?)\}', path)
239239
params = self._params()
240240
if unresolved:
241241
for u in unresolved:
242-
if u in params:
242+
if u not in params:
243243
raise SdkError('missing a mandatory parameter[%s]' % u)
244244

245245
path = string.Template(path).substitute(params)
@@ -362,7 +362,21 @@ def _poll_result(self, rsp, cb):
362362
m = json.loads(rsp.data)
363363
location = m[LOCATION]
364364
if not location:
365-
raise SdkError("Internal Error] the api[%s] is an async API but the server doesn't return the polling location url")
365+
raise SdkError("[Internal Error] the api[%s] is an async API but the server doesn't return the polling location url" % self.PATH)
366+
367+
# Rewrite poll URL to use client-configured hostname:port,
368+
# in case server returns an internal IP unreachable from client
369+
try:
370+
from urllib.parse import urlparse, urlunparse
371+
except ImportError:
372+
from urlparse import urlparse, urlunparse
373+
parsed = urlparse(location)
374+
configured_host = __config__[CONFIG_HOSTNAME]
375+
configured_port = str(__config__[CONFIG_PORT])
376+
if ':' in configured_host and not configured_host.startswith('['):
377+
configured_host = '[%s]' % configured_host
378+
location = urlunparse(parsed._replace(
379+
netloc='%s:%s' % (configured_host, configured_port)))
366380

367381
if cb:
368382
# async polling
@@ -480,11 +494,13 @@ def _uuid():
480494
def _json_http(
481495
uri,
482496
body=None,
483-
headers={},
497+
headers=None,
484498
method='POST',
485499
timeout=120.0
486500
):
487501
pool = urllib3.PoolManager(timeout=timeout, retries=urllib3.util.retry.Retry(15))
502+
if headers is None:
503+
headers = {}
488504
headers.update({'Content-Type': 'application/json', 'Connection': 'close'})
489505

490506
if body is not None and not isinstance(body, str):
@@ -497,6 +513,15 @@ def _json_http(
497513
else:
498514
rsp = pool.request(method, uri, headers=headers)
499515

500-
print('[Response to %s %s]: status: %s, body: %s' % (method, uri, rsp.status, rsp.data))
501-
return rsp
516+
data = rsp.data
517+
if isinstance(data, bytes):
518+
data = data.decode('utf-8')
519+
print('[Response to %s %s]: status: %s, body: %s' % (method, uri, rsp.status, data))
520+
521+
class _Rsp(object):
522+
pass
523+
r = _Rsp()
524+
r.status = rsp.status
525+
r.data = data
526+
return r
502527

0 commit comments

Comments
 (0)