Skip to content

Commit 8a95b89

Browse files
committed
fix(base service): Separate out request into prepare_request and send
1 parent bf7da48 commit 8a95b89

File tree

2 files changed

+87
-119
lines changed

2 files changed

+87
-119
lines changed

ibm_cloud_sdk_core/base_service.py

Lines changed: 54 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from .utils import has_bad_first_or_last_char, remove_null_values, cleanup_values
2626
from .detailed_response import DetailedResponse
2727
from .api_exception import ApiException
28-
from .authenticators import Authenticator, BasicAuthenticator, BearerAuthenticator, CP4DAuthenticator, IAMAuthenticator, NoAuthAuthenticator
28+
from .authenticators import Authenticator, BasicAuthenticator, BearerTokenAuthenticator, CloudPakForDataAuthenticator, IamAuthenticator, NoauthAuthenticator
2929
from http.cookiejar import CookieJar
3030

3131
# Uncomment this to enable http debugging
@@ -88,7 +88,7 @@ def __init__(self,
8888
self._set_service_properties(vcap_service_credentials)
8989

9090
if not self.authenticator:
91-
self.authenticator = NoAuthAuthenticator()
91+
self.authenticator = NoauthAuthenticator()
9292

9393
def _load_from_credential_file(self, service_name, separator='='):
9494
"""
@@ -139,23 +139,23 @@ def _set_authenticator_properties(self, properties):
139139
username=properties.get('username'),
140140
password=properties.get('password'))
141141
elif auth_type == 'bearerToken':
142-
self.authenticator = BearerAuthenticator(
142+
self.authenticator = BearerTokenAuthenticator(
143143
bearer_token=properties.get('bearer_token'))
144144
elif auth_type == 'cp4d':
145-
self.authenticator = CP4DAuthenticator(
145+
self.authenticator = CloudPakForDataAuthenticator(
146146
username=properties.get('username'),
147147
password=properties.get('password'),
148148
url=properties.get('auth_url'),
149149
disable_ssl_verification=properties.get('auth_disable_ssl'))
150150
elif auth_type == 'iam':
151-
self.authenticator = IAMAuthenticator(
151+
self.authenticator = IamAuthenticator(
152152
apikey=properties.get('apikey'),
153153
url=properties.get('auth_url'),
154154
client_id=properties.get('client_id'),
155155
client_secret=properties.get('client_secret'),
156156
disable_ssl_verification=properties.get('auth_disable_ssl'))
157157
elif auth_type == 'noauth':
158-
self.authenticator = NoAuthAuthenticator()
158+
self.authenticator = NoauthAuthenticator()
159159

160160
def _set_service_properties(self, properties):
161161
if 'url' in properties:
@@ -227,59 +227,68 @@ def set_default_headers(self, headers):
227227
else:
228228
raise TypeError("headers parameter must be a dictionary")
229229

230-
def request(self,
231-
method,
232-
url,
233-
accept_json=False,
234-
headers=None,
235-
params=None,
236-
json=None,
237-
data=None,
238-
files=None,
239-
**kwargs):
240-
full_url = self.url + url
230+
def send(self, request, **kwargs):
231+
# Use a one minute timeout when our caller doesn't give a timeout.
232+
# http://docs.python-requests.org/en/master/user/quickstart/#timeouts
233+
kwargs = dict({"timeout": 60}, **kwargs)
234+
kwargs = dict(kwargs, **self.http_config)
235+
236+
if self.disable_ssl_verification:
237+
kwargs['verify'] = False
238+
239+
response = requests.request(**request, cookies=self.jar, **kwargs)
240+
print(response.headers)
241+
242+
if 200 <= response.status_code <= 299:
243+
if response.status_code == 204 or request['method'] == 'HEAD':
244+
# There is no body content for a HEAD request or a 204 response
245+
result = None
246+
elif not response.text:
247+
result = None
248+
else:
249+
try:
250+
result = response.json()
251+
except:
252+
result = response
253+
return DetailedResponse(result, response.headers,
254+
response.status_code)
255+
else:
256+
error_message = None
257+
if response.status_code == 401:
258+
error_message = 'Unauthorized: Access is denied due to ' \
259+
'invalid credentials'
260+
raise ApiException(
261+
response.status_code, error_message, http_response=response)
262+
263+
264+
def prepare_request(self, method, url, headers=None,
265+
params=None, data=None, files=None, **kwargs):
266+
request = {'method': method}
267+
268+
request['url'] = self.url + url
241269

242270
headers = remove_null_values(headers) if headers else {}
243271
headers = cleanup_values(headers)
244272
headers = CaseInsensitiveDict(headers)
245-
246273
if self.default_headers is not None:
247274
headers.update(self.default_headers)
248-
if accept_json:
249-
headers['accept'] = 'application/json'
250-
251275
if not any(key in headers for key in ['user-agent', 'User-Agent']):
252276
headers.update(self.user_agent_header)
277+
request['headers'] = headers
253278

254-
# Remove keys with None values
255279
params = remove_null_values(params)
256280
params = cleanup_values(params)
257-
json = remove_null_values(json)
258-
data = remove_null_values(data)
259-
files = remove_null_values(files)
281+
request['params'] = params
260282

283+
data = remove_null_values(data)
261284
if sys.version_info >= (3, 0) and isinstance(data, str):
262285
data = data.encode('utf-8')
286+
request['data'] = data
263287

264-
# Support versions of requests older than 2.4.2 without the json input
265-
if not data and json is not None:
266-
data = json_import.dumps(json)
267-
headers.update({'content-type': 'application/json'})
268-
269-
auth = None
270-
if self.authenticator._is_bearer_authentication():
271-
headers['Authorization'] = self.authenticator.authenticate()
272-
elif self.authenticator._is_basic_authentication():
273-
auth = self.authenticator.authenticate()
274-
275-
# Use a one minute timeout when our caller doesn't give a timeout.
276-
# http://docs.python-requests.org/en/master/user/quickstart/#timeouts
277-
kwargs = dict({"timeout": 60}, **kwargs)
278-
kwargs = dict(kwargs, **self.http_config)
279-
280-
if self.disable_ssl_verification:
281-
kwargs['verify'] = False
288+
if self.authenticator:
289+
self.authenticator.authenticate(request)
282290

291+
files = remove_null_values(files)
283292
if files is not None:
284293
for k, file_tuple in files.items():
285294
if file_tuple and len(
@@ -288,41 +297,9 @@ def request(self,
288297
if file and hasattr(file, 'name'):
289298
filename = basename(file.name)
290299
files[k] = (filename, file_tuple[1], file_tuple[2])
300+
request['files'] = files
301+
return request
291302

292-
response = requests.request(
293-
method=method,
294-
url=full_url,
295-
cookies=self.jar,
296-
auth=auth,
297-
headers=headers,
298-
params=params,
299-
data=data,
300-
files=files,
301-
**kwargs)
302-
303-
if 200 <= response.status_code <= 299:
304-
if response.status_code == 204 or method == 'HEAD':
305-
# There is no body content for a HEAD request or a 204 response
306-
return DetailedResponse(None, response.headers,
307-
response.status_code)
308-
if accept_json:
309-
try:
310-
response_json = response.json()
311-
except:
312-
# deserialization fails because there is no text
313-
return DetailedResponse(None, response.headers,
314-
response.status_code)
315-
return DetailedResponse(response_json, response.headers,
316-
response.status_code)
317-
return DetailedResponse(response, response.headers,
318-
response.status_code)
319-
else:
320-
error_message = None
321-
if response.status_code == 401:
322-
error_message = 'Unauthorized: Access is denied due to ' \
323-
'invalid credentials'
324-
raise ApiException(
325-
response.status_code, error_message, http_response=response)
326303

327304
@staticmethod
328305
def _convert_model(val, classname=None):

test/test_base_service.py

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from ibm_cloud_sdk_core import BaseService
99
from ibm_cloud_sdk_core import ApiException
1010
from ibm_cloud_sdk_core import CP4DTokenManager
11-
from ibm_cloud_sdk_core.authenticators import IAMAuthenticator, Authenticator, BasicAuthenticator, CP4DAuthenticator
11+
from ibm_cloud_sdk_core.authenticators import IamAuthenticator, Authenticator, BasicAuthenticator, CloudPakForDataAuthenticator
1212

1313

1414
class AnyServiceV1(BaseService):
@@ -36,21 +36,24 @@ def op_with_path_params(self, path0, path1):
3636
params = {'version': self.version}
3737
url = '/v1/foo/{0}/bar/{1}/baz'.format(
3838
*self._encode_path_vars(path0, path1))
39-
response = self.request(
40-
method='GET', url=url, params=params, accept_json=True)
39+
request = self.prepare_request(method='GET', url=url, params=params)
40+
response = self.send(request)
4141
return response
4242

4343
def with_http_config(self, http_config):
4444
self.set_http_config(http_config)
45-
response = self.request(method='GET', url='', accept_json=True)
45+
request = self.prepare_request(method='GET', url='', accept_json=True)
46+
response = self.send(request)
4647
return response
4748

4849
def any_service_call(self):
49-
response = self.request(method='GET', url='', accept_json=True)
50+
request = self.prepare_request(method='GET', url='', accept_json=True)
51+
response = self.send(request)
5052
return response
5153

5254
def head_request(self):
53-
response = self.request(method='HEAD', url='', accept_json=True)
55+
request = self.prepare_request(method='HEAD', url='', accept_json=True)
56+
response = self.send(request)
5457
return response
5558

5659

@@ -133,14 +136,10 @@ def test_fail_http_config():
133136

134137
@responses.activate
135138
def test_iam():
136-
url = "https://iam.cloud.ibm.com/identity/token"
137-
iam_authenticator = IAMAuthenticator('my_apikey')
139+
iam_authenticator = IamAuthenticator('my_apikey', 'https://iam-test.cloud.ibm.com/identity/token')
138140
service = AnyServiceV1('2017-07-07', authenticator=iam_authenticator)
139141
assert service.authenticator is not None
140142

141-
iam_authenticator.set_url('https://iam-test.cloud.ibm.com/identity/token')
142-
assert service.authenticator.token_manager.url == 'https://iam-test.cloud.ibm.com/identity/token'
143-
144143
response = {
145144
"access_token": get_access_token(),
146145
"token_type": "Bearer",
@@ -178,20 +177,9 @@ def __init__(self):
178177
assert isinstance(service.authenticator, Authenticator)
179178

180179

181-
def test_set_username_and_password():
182-
basic_authenticator = BasicAuthenticator('my_username', 'my_password')
183-
service = AnyServiceV1('2017-07-07', authenticator=basic_authenticator)
184-
assert service.authenticator.username == 'my_username'
185-
assert service.authenticator.password == 'my_password'
186-
187-
basic_authenticator.set_username_and_password('hello', 'ibm')
188-
assert service.authenticator.username == 'hello'
189-
assert service.authenticator.password == 'ibm'
190-
191-
192180
def test_for_cp4d():
193-
cp4d_authenticator = CP4DAuthenticator('my_username', 'my_password',
194-
'my_url')
181+
cp4d_authenticator = CloudPakForDataAuthenticator('my_username', 'my_password',
182+
'my_url')
195183
service = AnyServiceV1('2017-07-07', authenticator=cp4d_authenticator)
196184
assert service.authenticator.token_manager is not None
197185
assert service.authenticator.token_manager.username == 'my_username'
@@ -207,8 +195,8 @@ def test_disable_ssl_verification():
207195
service1.set_disable_ssl_verification(False)
208196
assert service1.disable_ssl_verification is False
209197

210-
cp4d_authenticator = CP4DAuthenticator('my_username', 'my_password',
211-
'my_url')
198+
cp4d_authenticator = CloudPakForDataAuthenticator('my_username', 'my_password',
199+
'my_url')
212200
service2 = AnyServiceV1('2017-07-07', authenticator=cp4d_authenticator)
213201
assert service2.disable_ssl_verification is False
214202
cp4d_authenticator.set_disable_ssl_verification(True)
@@ -330,7 +318,8 @@ def test_request_server_error():
330318
content_type='application/json')
331319
service = AnyServiceV1('2018-11-20')
332320
try:
333-
service.request('GET', url='')
321+
prepped = service.prepare_request('GET', url='')
322+
service.send(prepped)
334323
except ApiException as err:
335324
assert err.message == 'internal server error'
336325

@@ -345,13 +334,15 @@ def test_request_success_json():
345334
}),
346335
content_type='application/json')
347336
service = AnyServiceV1('2018-11-20')
348-
detailed_response = service.request('GET', url='', accept_json=True)
337+
prepped = service.prepare_request('GET', url='')
338+
detailed_response = service.send(prepped)
349339
assert detailed_response.get_result() == {'foo': 'bar'}
350340

351341
service = AnyServiceV1('2018-11-20', authenticator=BasicAuthenticator('my_username', 'my_password'))
352342
service.set_default_headers({'test': 'header'})
353343
service.set_disable_ssl_verification(True)
354-
detailed_response = service.request('GET', url='', accept_json=True)
344+
prepped = service.prepare_request('GET', url='')
345+
detailed_response = service.send(prepped)
355346
assert detailed_response.get_result() == {'foo': 'bar'}
356347

357348
@responses.activate
@@ -365,8 +356,9 @@ def test_request_success_response():
365356
}),
366357
content_type='application/json')
367358
service = AnyServiceV1('2018-11-20')
368-
detailed_response = service.request('GET', url='')
369-
assert detailed_response.get_result().text == '{"foo": "bar"}'
359+
prepped = service.prepare_request('GET', url='')
360+
detailed_response = service.send(prepped)
361+
assert detailed_response.get_result() == {"foo": "bar"}
370362

371363
@responses.activate
372364
def test_request_fail_401():
@@ -380,7 +372,8 @@ def test_request_fail_401():
380372
content_type='application/json')
381373
service = AnyServiceV1('2018-11-20')
382374
try:
383-
service.request('GET', url='')
375+
prepped = service.prepare_request('GET', url='')
376+
service.send(prepped)
384377
except ApiException as err:
385378
assert err.message == 'Unauthorized: Access is denied due to invalid credentials'
386379

@@ -448,18 +441,16 @@ def test_user_agent_header():
448441
responses.GET,
449442
'https://gateway.watsonplatform.net/test/api',
450443
status=200,
451-
body=json.dumps({
452-
'foo': 'bar'
453-
}),
454-
content_type='application/json')
455-
response = service.request(
456-
'GET', url='', headers={
457-
'user-agent': 'my_user_agent'
458-
})
444+
body='some text')
445+
prepped = service.prepare_request('GET', url='', headers={
446+
'user-agent': 'my_user_agent'
447+
})
448+
response = service.send(prepped)
459449
assert response.get_result().request.headers.__getitem__(
460450
'user-agent') == 'my_user_agent'
461451

462-
response = service.request('GET', url='', headers=None)
452+
prepped = service.prepare_request('GET', url='', headers=None)
453+
response = service.send(prepped)
463454
assert response.get_result().request.headers.__getitem__(
464455
'user-agent') == user_agent_header['User-Agent']
465456

@@ -481,4 +472,4 @@ def test_files():
481472
os.path.dirname(__file__), '../resources/ibm-credentials-iam.env'), 'r')
482473
form_data['file1'] = (None, file, 'application/octet-stream')
483474
form_data['string1'] = (None, 'hello', 'text.plain')
484-
service.request('GET', url='', headers={'X-opt-out': True}, files=form_data)
475+
service.prepare_request('GET', url='', headers={'X-opt-out': True}, files=form_data)

0 commit comments

Comments
 (0)