diff --git a/Dockerfile b/Dockerfile index 56e63db..ade34b8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.6.5-stretch +FROM python:3.6.5-slim ENV PYTHONDONTWRITEBYTECODE 1 diff --git a/build.py b/build.py index bef1586..f495b29 100644 --- a/build.py +++ b/build.py @@ -40,7 +40,7 @@ ] summary = 'A Python class providing primitive methods for enabling consumption of REST APIs' url = 'https://github.com/soda480/rest3client' -version = '0.0.2' +version = '0.0.3' default_task = [ 'clean', 'analyze', diff --git a/src/main/python/rest3client/restclient.py b/src/main/python/rest3client/restclient.py index 1da4ac6..dca01c9 100644 --- a/src/main/python/rest3client/restclient.py +++ b/src/main/python/rest3client/restclient.py @@ -103,31 +103,7 @@ def get_headers(self, **kwargs): return headers - def request_handler(function): - """ decorator to process arguments and response for request method - """ - def _request_handler(self, *args, **kwargs): - """ decorator method to prepare and handle requests and responses - """ - noop = kwargs.pop('noop', False) - key_word_arguments = self.process_arguments(args, kwargs) - - redacted_key_word_arguments = redact(key_word_arguments) - try: - redacted = json.dumps(redact(redacted_key_word_arguments), indent=2, sort_keys=True) - except TypeError: - redacted = redacted_key_word_arguments - - logger.debug('\n{}: {} NOOP: {}\n{}'.format( - function.__name__.upper(), key_word_arguments['address'], noop, redacted)) - if noop: - return - response = function(self, *args, **key_word_arguments) - return self.process_response(response, **kwargs) - - return _request_handler - - def process_arguments(self, args, kwargs): + def get_arguments(self, endpoint, kwargs): """ return key word arguments to pass to requests method """ arguments = copy.deepcopy(kwargs) @@ -141,10 +117,22 @@ def process_arguments(self, args, kwargs): if 'verify' not in arguments or arguments.get('verify') is None: arguments['verify'] = self.cabundle - arguments['address'] = 'https://{}{}'.format(self.hostname, args[0]) + arguments['address'] = 'https://{}{}'.format(self.hostname, endpoint) arguments.pop('raw_response', None) return arguments + def log_request(self, function_name, arguments, noop): + """ log request function name and redacted arguments + """ + redacted_arguments = redact(arguments) + try: + redacted_arguments = json.dumps(redacted_arguments, indent=2, sort_keys=True) + except TypeError: + pass + + logger.debug('\n{}: {} NOOP: {}\n{}'.format( + function_name, arguments['address'], noop, redacted_arguments)) + def get_error_message(self, response): """ return error message from response """ @@ -183,6 +171,22 @@ def process_response(self, response, **kwargs): logger.debug('returning response text') return response.text + def request_handler(function): + """ decorator to process arguments and response for request method + """ + def _request_handler(self, endpoint, **kwargs): + """ decorator method to prepare and handle requests and responses + """ + noop = kwargs.pop('noop', False) + arguments = self.get_arguments(endpoint, kwargs) + self.log_request(function.__name__.upper(), arguments, noop) + if noop: + return + response = function(self, endpoint, **arguments) + return self.process_response(response, **kwargs) + + return _request_handler + @request_handler def post(self, endpoint, **kwargs): """ helper method to submit post requests @@ -191,7 +195,7 @@ def post(self, endpoint, **kwargs): @request_handler def put(self, endpoint, **kwargs): - """ helper method to submit post requests + """ helper method to submit put requests """ return requests.put(kwargs.pop('address'), **kwargs) @@ -209,7 +213,7 @@ def delete(self, endpoint, **kwargs): @request_handler def patch(self, endpoint, **kwargs): - """ helper method to submit delete requests + """ helper method to submit patch requests """ return requests.patch(kwargs.pop('address'), **kwargs) diff --git a/src/unittest/python/test_RESTclient.py b/src/unittest/python/test_RESTclient.py index 9472d50..ee927ee 100644 --- a/src/unittest/python/test_RESTclient.py +++ b/src/unittest/python/test_RESTclient.py @@ -108,13 +108,42 @@ def test__get_headers_Should_ReturnHeaders_When_BearerToken(self, *patches): } self.assertEqual(result, expected_result) + @patch('rest3client.restclient.os.access') + @patch('rest3client.restclient.redact') + @patch('rest3client.restclient.logger') + def test__log_request_Should_CallLogger_When_JsonNotSerializable(self, logger_patch, redact_patch, *patches): + redact_patch.return_value = '--redacted-arguments--' + client = RESTclient('hostname1.company.com', bearer_token='token') + arguments = { + 'address': '--address--', + 'data': Mock() + } + client.log_request('GET', arguments, True) + debug_call = call('\nGET: --address-- NOOP: True\n"--redacted-arguments--"') + self.assertTrue(debug_call in logger_patch.debug.mock_calls) + + @patch('rest3client.restclient.os.access') + @patch('rest3client.restclient.redact') + @patch('rest3client.restclient.logger') + def test__log_request_Should_CallLogger_When_JsonSerializable(self, logger_patch, redact_patch, *patches): + arguments = { + 'address': '--address--', + 'data': 'data' + } + redact_patch.return_value = arguments + client = RESTclient('hostname1.company.com', bearer_token='token') + + client.log_request('GET', arguments, True) + debug_call = call('\nGET: --address-- NOOP: True\n{\n "address": "--address--",\n "data": "data"\n}') + self.assertTrue(debug_call in logger_patch.debug.mock_calls) + @patch('rest3client.restclient.os.access') def test__request_handler_Should_CallFunctionWithArgs_When_Args(self, *patches): mock_function = Mock(__name__='mocked method') client = RESTclient('hostname1.company.com') decorated_function = RESTclient.request_handler(mock_function) - decorated_function(client, '/rest/endpoint', 'arg1', 'arg2') - expected_args = (client, '/rest/endpoint', 'arg1', 'arg2') + decorated_function(client, '/rest/endpoint', k1='arg1', k2='arg2') + expected_args = (client, '/rest/endpoint') args, _ = mock_function.call_args_list[0] self.assertEqual(args, expected_args) @@ -162,11 +191,11 @@ def test__request_handler_Should_NotCallFunctionAndReturnNone_When_FunctionSetsN @patch('rest3client.restclient.os.access') @patch('rest3client.RESTclient.get_headers', return_value={'h1': 'v1'}) - def test__get_standard_kwargs_Should_SetHeaders_When_NoHeadersSpecified(self, *patches): + def test__get_arguments_Should_SetHeaders_When_NoHeadersSpecified(self, *patches): client = RESTclient('hostname1.company.com') - args = ['/endpoint'] + endpoint = '/endpoint' kwargs = {} - result = client.process_arguments(args, kwargs) + result = client.get_arguments(endpoint, kwargs) expected_result = { 'h1': 'v1' } @@ -174,15 +203,15 @@ def test__get_standard_kwargs_Should_SetHeaders_When_NoHeadersSpecified(self, *p @patch('rest3client.restclient.os.access') @patch('rest3client.RESTclient.get_headers', return_value={'h1': 'v1'}) - def test__get_standard_kwargs_Should_UpdatedHeaders_When_HeadersSpecified(self, *patches): + def test__get_arguments_Should_UpdatedHeaders_When_HeadersSpecified(self, *patches): client = RESTclient('hostname1.company.com') - args = ['/endpoint'] + endpoint = '/endpoint' kwargs = { 'headers': { 'h2': 'v2' } } - result = client.process_arguments(args, kwargs) + result = client.get_arguments(endpoint, kwargs) expected_result = { 'h1': 'v1', 'h2': 'v2' @@ -191,42 +220,42 @@ def test__get_standard_kwargs_Should_UpdatedHeaders_When_HeadersSpecified(self, @patch('rest3client.restclient.os.access') @patch('rest3client.RESTclient.get_headers', return_value={'h1': 'v1'}) - def test__get_standard_kwargs_Should_SetVerifyToCabundle_When_VerifyNotSpecified(self, *patches): + def test__get_arguments_Should_SetVerifyToCabundle_When_VerifyNotSpecified(self, *patches): client = RESTclient('hostname1.company.com') - args = ['/endpoint'] + endpoint = '/endpoint' kwargs = {} - result = client.process_arguments(args, kwargs) + result = client.get_arguments(endpoint, kwargs) self.assertEqual(result['verify'], client.cabundle) @patch('rest3client.restclient.os.access') @patch('rest3client.RESTclient.get_headers', return_value={'h1': 'v1'}) - def test__get_standard_kwargs_Should_SetVerifyToCabundle_When_VerifyIsNone(self, *patches): + def test__get_arguments_Should_SetVerifyToCabundle_When_VerifyIsNone(self, *patches): client = RESTclient('hostname1.company.com') - args = ['/endpoint'] + endpoint = '/endpoint' kwargs = { 'verify': None } - result = client.process_arguments(args, kwargs) + result = client.get_arguments(endpoint, kwargs) self.assertEqual(result['verify'], client.cabundle) @patch('rest3client.restclient.os.access') @patch('rest3client.RESTclient.get_headers', return_value={'h1': 'v1'}) - def test__get_standard_kwargs_Should_NotSetVerify_When_VerifyIsSet(self, *patches): + def test__get_arguments_Should_NotSetVerify_When_VerifyIsSet(self, *patches): client = RESTclient('hostname1.company.com') - args = ['/endpoint'] + endpoint = '/endpoint' kwargs = { 'verify': False } - result = client.process_arguments(args, kwargs) + result = client.get_arguments(endpoint, kwargs) self.assertFalse(result['verify']) @patch('rest3client.restclient.os.access') @patch('rest3client.RESTclient.get_headers', return_value={'h1': 'v1'}) - def test__get_standard_kwargs_Should_SetAddress_When_Called(self, *patches): + def test__get_arguments_Should_SetAddress_When_Called(self, *patches): client = RESTclient('hostname1.company.com') - args = ['/endpoint'] + endpoint = '/endpoint' kwargs = {} - result = client.process_arguments(args, kwargs) + result = client.get_arguments(endpoint, kwargs) expected_result = 'https://hostname1.company.com/endpoint' self.assertEqual(result['address'], expected_result)