From 08e4b7716a0c890ed30260f708d2ab20d6c3c1f6 Mon Sep 17 00:00:00 2001 From: pem70 Date: Tue, 11 Jun 2024 13:12:12 -0400 Subject: [PATCH 1/6] Standardize response Signed-off-by: pem70 --- samples/SampleFiles.py | 2 +- .../zowe/core_for_zowe_sdk/request_handler.py | 13 ++++++------- src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py | 4 ++-- tests/integration/test_zos_files.py | 15 ++++++++------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/samples/SampleFiles.py b/samples/SampleFiles.py index 7da6912c..fa92a6dc 100644 --- a/samples/SampleFiles.py +++ b/samples/SampleFiles.py @@ -29,4 +29,4 @@ # ----------------------------------------------------- print("...content of a file\n") my_file_content = my_files.get_file_content("/z/tm891807/file.txt") -print(my_file_content["response"]) +print(my_file_content) diff --git a/src/core/zowe/core_for_zowe_sdk/request_handler.py b/src/core/zowe/core_for_zowe_sdk/request_handler.py index 064d0253..097e2b07 100644 --- a/src/core/zowe/core_for_zowe_sdk/request_handler.py +++ b/src/core/zowe/core_for_zowe_sdk/request_handler.py @@ -134,13 +134,12 @@ def __normalize_response(self): Returns ------- - A bytes object if the response content type is application/octet-stream, - a normalized JSON for the request response otherwise + A bytes object at the format based on Content-Type header """ - if self.response.headers.get("Content-Type") == "application/octet-stream": + contentType = self.response.headers.get("Content-Type") + if contentType == "application/octet-stream": return self.response.content + elif contentType and contentType.startswith("application/json"): + return self.response.json() else: - try: - return self.response.json() - except: - return {"response": self.response.text} + return self.response.text diff --git a/src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py b/src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py index bcceb8d1..b7289e0e 100644 --- a/src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py +++ b/src/zos_jobs/zowe/zos_jobs_for_zowe_sdk/jobs.py @@ -402,7 +402,7 @@ def get_job_output_as_files(self, status, output_dir): os.makedirs(output_dir, exist_ok=True) output_file = os.path.join(output_dir, job_name, job_id, "jcl.txt") data_spool_file = self.get_jcl_text(job_correlator) - dataset_content = data_spool_file["response"] + dataset_content = data_spool_file with open(output_file, "w", encoding="utf-8") as out_file: out_file.write(dataset_content) @@ -416,7 +416,7 @@ def get_job_output_as_files(self, status, output_dir): output_file = os.path.join(output_dir, job_name, job_id, stepname, ddname) data_spool_file = self.get_spool_file_contents(job_correlator, spoolfile_id) - dataset_content = data_spool_file["response"] + dataset_content = data_spool_file with open(output_file, "w", encoding="utf-8") as out_file: out_file.write(dataset_content) diff --git a/tests/integration/test_zos_files.py b/tests/integration/test_zos_files.py index 88195390..549cae8d 100644 --- a/tests/integration/test_zos_files.py +++ b/tests/integration/test_zos_files.py @@ -1,4 +1,5 @@ """Integration tests for the Zowe Python SDK z/OS Files package.""" + import json import os import unittest @@ -66,7 +67,7 @@ def test_list_members_should_return_a_list_of_members(self): def test_get_dsn_content_should_return_content_from_dataset(self): """Executing get_dsn_content should return content from dataset.""" command_output = self.files.get_dsn_content(self.test_member_jcl) - self.assertIsInstance(command_output["response"], str) + self.assertIsInstance(command_output, str) def test_get_dsn_content_streamed_should_return_response_content(self): """Executing get_dsn_content_streamed should return response object from the server.""" @@ -86,14 +87,14 @@ def test_get_file_content_streamed_should_return_response_content(self): def test_write_to_dsn_should_be_possible(self): """Executing write_to_dsn should be possible.""" command_output = self.files.write_to_dsn(self.test_member_generic, "HELLO WORLD") - self.assertTrue(command_output["response"] == "") + self.assertTrue(command_output == "") def test_copy_uss_to_dataset_should_be_possible(self): """Executing copy_uss_to_dataset should be possible.""" command_output = self.files.copy_uss_to_dataset( self.files_fixtures["TEST_USS"], self.files_fixtures["TEST_PDS"] + "(TEST2)", replace=True ) - self.assertTrue(command_output["response"] == "") + self.assertTrue(command_output == "") def test_copy_dataset_or_member_should_be_possible(self): """Executing copy_dataset_or_member should be possible.""" @@ -105,7 +106,7 @@ def test_copy_dataset_or_member_should_be_possible(self): "replace": True, } command_output = self.files.copy_dataset_or_member(**test_case) - self.assertTrue(command_output["response"] == "") + self.assertTrue(command_output == "") def test_mount_unmount_zfs_file_system(self): """Mounting a zfs filesystem should be possible""" @@ -119,7 +120,7 @@ def test_mount_unmount_zfs_file_system(self): command_output = self.files.mount_file_system( self.test2_zfs_file_system, mount_point, self.mount_zfs_file_system_options ) - self.assertTrue(command_output["response"] == "") + self.assertTrue(command_output == "") # List a zfs file system command_output = self.files.list_unix_file_systems(file_system_name=self.test2_zfs_file_system.upper()) @@ -127,11 +128,11 @@ def test_mount_unmount_zfs_file_system(self): # Unmount file system command_output = self.files.unmount_file_system(self.test2_zfs_file_system) - self.assertTrue(command_output["response"] == "") + self.assertTrue(command_output == "") # Delete file system command_output = self.files.delete_zFS_file_system(self.test2_zfs_file_system) - self.assertTrue(command_output["response"] == "") + self.assertTrue(command_output == "") def test_upload_download_delete_dataset(self): self.files.upload_file_to_dsn(SAMPLE_JCL_FIXTURE_PATH, self.test_ds_upload) From f4fdc0493dd03e7ad413aff0c8ad546ff7f3cc60 Mon Sep 17 00:00:00 2001 From: pem70 Date: Tue, 11 Jun 2024 13:19:26 -0400 Subject: [PATCH 2/6] Update changelod and remove duplicate lines Signed-off-by: pem70 --- CHANGELOG.md | 3 ++- src/core/zowe/core_for_zowe_sdk/zosmf_profile.py | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2a86d0a..7305c4bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,8 +8,9 @@ All notable changes to the Zowe Client Python SDK will be documented in this fil - Added logger class to core SDK [#185](https://github.com/zowe/zowe-client-python-sdk/issues/185) - Added classes for handling `Datasets`, `USSFiles`, and `FileSystems` in favor of the single Files class. [#264](https://github.com/zowe/zowe-client-python-sdk/issues/264) -- Refactored testings into proper folders and files and add more tests [#265](https://github.com/zowe/zowe-client-python-sdk/issues/265) +- Refactored tests into proper folders and files [#265](https://github.com/zowe/zowe-client-python-sdk/issues/265) - Fixed the bug on `upload_file_to_dsn` [#104](https://github.com/zowe/zowe-client-python-sdk/issues/104) +- Standardize `response` output [#266](https://github.com/zowe/zowe-client-python-sdk/issues/266) ### Bug Fixes diff --git a/src/core/zowe/core_for_zowe_sdk/zosmf_profile.py b/src/core/zowe/core_for_zowe_sdk/zosmf_profile.py index 623ffa35..2df61390 100644 --- a/src/core/zowe/core_for_zowe_sdk/zosmf_profile.py +++ b/src/core/zowe/core_for_zowe_sdk/zosmf_profile.py @@ -54,7 +54,6 @@ def __init__(self, profile_name): """ self.profile_name = profile_name self.__logger = Log.registerLogger(__name__) - self.__logger = Log.registerLogger(__name__) @property def profiles_dir(self): From 736401415b11af5d7178f29c39551931f40d8815 Mon Sep 17 00:00:00 2001 From: pem70 Date: Wed, 12 Jun 2024 15:47:09 -0400 Subject: [PATCH 3/6] Update request_handler.py Signed-off-by: pem70 --- src/core/zowe/core_for_zowe_sdk/request_handler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/zowe/core_for_zowe_sdk/request_handler.py b/src/core/zowe/core_for_zowe_sdk/request_handler.py index 097e2b07..ac4d1b37 100644 --- a/src/core/zowe/core_for_zowe_sdk/request_handler.py +++ b/src/core/zowe/core_for_zowe_sdk/request_handler.py @@ -139,7 +139,7 @@ def __normalize_response(self): contentType = self.response.headers.get("Content-Type") if contentType == "application/octet-stream": return self.response.content - elif contentType and contentType.startswith("application/json"): + elif contentType and contentType.startswith("application/json") and len(self.response.text): return self.response.json() else: return self.response.text From a811100c82cb2789b40f6450bcf54f96d632e6c7 Mon Sep 17 00:00:00 2001 From: pem70 Date: Wed, 12 Jun 2024 15:49:02 -0400 Subject: [PATCH 4/6] Update documentation Signed-off-by: pem70 --- CHANGELOG.md | 2 +- src/core/zowe/core_for_zowe_sdk/request_handler.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7305c4bf..be34938f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ All notable changes to the Zowe Client Python SDK will be documented in this fil - Added classes for handling `Datasets`, `USSFiles`, and `FileSystems` in favor of the single Files class. [#264](https://github.com/zowe/zowe-client-python-sdk/issues/264) - Refactored tests into proper folders and files [#265](https://github.com/zowe/zowe-client-python-sdk/issues/265) - Fixed the bug on `upload_file_to_dsn` [#104](https://github.com/zowe/zowe-client-python-sdk/issues/104) -- Standardize `response` output [#266](https://github.com/zowe/zowe-client-python-sdk/issues/266) +- **Breaking:** Standardized `response` output based on `Content-Type`. [#266](https://github.com/zowe/zowe-client-python-sdk/issues/266) ### Bug Fixes diff --git a/src/core/zowe/core_for_zowe_sdk/request_handler.py b/src/core/zowe/core_for_zowe_sdk/request_handler.py index ac4d1b37..e2e43892 100644 --- a/src/core/zowe/core_for_zowe_sdk/request_handler.py +++ b/src/core/zowe/core_for_zowe_sdk/request_handler.py @@ -134,7 +134,10 @@ def __normalize_response(self): Returns ------- - A bytes object at the format based on Content-Type header + Response object at the format based on Content-Type header: + - `bytes` when the response is binary data + - object when the response is JSON text + - `str` when the response is plain text """ contentType = self.response.headers.get("Content-Type") if contentType == "application/octet-stream": From dd527c5e475d60c9f08dd5f5b89cc0e20f56934f Mon Sep 17 00:00:00 2001 From: pem70 Date: Wed, 12 Jun 2024 16:16:00 -0400 Subject: [PATCH 5/6] Update request_handler.py Signed-off-by: pem70 --- src/core/zowe/core_for_zowe_sdk/request_handler.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/core/zowe/core_for_zowe_sdk/request_handler.py b/src/core/zowe/core_for_zowe_sdk/request_handler.py index e2e43892..0540ea89 100644 --- a/src/core/zowe/core_for_zowe_sdk/request_handler.py +++ b/src/core/zowe/core_for_zowe_sdk/request_handler.py @@ -142,7 +142,10 @@ def __normalize_response(self): contentType = self.response.headers.get("Content-Type") if contentType == "application/octet-stream": return self.response.content - elif contentType and contentType.startswith("application/json") and len(self.response.text): - return self.response.json() + elif contentType and contentType.startswith("application/json"): + try: + return self.response.json() + except requests.exceptions.JSONDecodeError: + return self.response.text else: return self.response.text From 4968781420d1e7c82be1b35960a22ad7f0f32bf5 Mon Sep 17 00:00:00 2001 From: pem70 Date: Thu, 13 Jun 2024 10:07:40 -0400 Subject: [PATCH 6/6] Update request handler and its test Signed-off-by: pem70 --- .../zowe/core_for_zowe_sdk/request_handler.py | 5 +-- tests/unit/core/test_request_handler.py | 32 ++++++++++++------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/src/core/zowe/core_for_zowe_sdk/request_handler.py b/src/core/zowe/core_for_zowe_sdk/request_handler.py index 0540ea89..537884d5 100644 --- a/src/core/zowe/core_for_zowe_sdk/request_handler.py +++ b/src/core/zowe/core_for_zowe_sdk/request_handler.py @@ -143,9 +143,6 @@ def __normalize_response(self): if contentType == "application/octet-stream": return self.response.content elif contentType and contentType.startswith("application/json"): - try: - return self.response.json() - except requests.exceptions.JSONDecodeError: - return self.response.text + return "" if self.response.text == "" else self.response.json() else: return self.response.text diff --git a/tests/unit/core/test_request_handler.py b/tests/unit/core/test_request_handler.py index 9b6d6686..de9c7ebe 100644 --- a/tests/unit/core/test_request_handler.py +++ b/tests/unit/core/test_request_handler.py @@ -4,10 +4,8 @@ import unittest from unittest import mock -from zowe.core_for_zowe_sdk import ( - RequestHandler, - exceptions -) +from zowe.core_for_zowe_sdk import RequestHandler, exceptions + class TestRequestHandlerClass(unittest.TestCase): """RequestHandler class unit tests.""" @@ -24,11 +22,13 @@ def test_object_should_be_instance_of_class(self): @mock.patch("logging.Logger.debug") @mock.patch("logging.Logger.error") @mock.patch("requests.Session.send") - def test_perform_streamed_request(self, mock_send_request, mock_logger_error: mock.MagicMock, mock_logger_debug: mock.MagicMock): + def test_perform_streamed_request( + self, mock_send_request, mock_logger_error: mock.MagicMock, mock_logger_debug: mock.MagicMock + ): """Performing a streamed request should call 'send_request' method""" mock_send_request.return_value = mock.Mock(status_code=200) request_handler = RequestHandler(self.session_arguments) - request_handler.perform_request("GET", {"url": "https://www.zowe.org"}, stream = True) + request_handler.perform_request("GET", {"url": "https://www.zowe.org"}, stream=True) mock_logger_error.assert_not_called() mock_logger_debug.assert_called() @@ -36,23 +36,22 @@ def test_perform_streamed_request(self, mock_send_request, mock_logger_error: mo mock_send_request.assert_called_once() self.assertTrue(mock_send_request.call_args[1]["stream"]) - @mock.patch("logging.Logger.error") def test_logger_unmatched_status_code(self, mock_logger_error: mock.MagicMock): """Test logger with unexpected status code""" request_handler = RequestHandler(self.session_arguments) try: - request_handler.perform_request("GET", {"url": "https://www.zowe.org"}, expected_code= [0], stream = True) + request_handler.perform_request("GET", {"url": "https://www.zowe.org"}, expected_code=[0], stream=True) except exceptions.UnexpectedStatus: mock_logger_error.assert_called_once() self.assertIn("The status code", mock_logger_error.call_args[0][0]) - + @mock.patch("logging.Logger.error") def test_logger_perform_request_invalid_method(self, mock_logger_error: mock.MagicMock): """Test logger with invalid request method""" request_handler = RequestHandler(self.session_arguments) try: - request_handler.perform_request("Invalid method", {"url": "https://www.zowe.org"}, stream = True) + request_handler.perform_request("Invalid method", {"url": "https://www.zowe.org"}, stream=True) except exceptions.InvalidRequestMethod: mock_logger_error.assert_called_once() self.assertIn("Invalid HTTP method input", mock_logger_error.call_args[0][0]) @@ -63,8 +62,17 @@ def test_logger_invalid_status_code(self, mock_send_request, mock_logger_error: mock_send_request.return_value = mock.Mock(ok=False) request_handler = RequestHandler(self.session_arguments) try: - request_handler.perform_request("GET", {"url": "https://www.zowe.org"}, stream = True) + request_handler.perform_request("GET", {"url": "https://www.zowe.org"}, stream=True) except exceptions.RequestFailed: mock_logger_error.assert_called_once() self.assertIn("HTTP Request has failed", mock_logger_error.call_args[0][0]) - mock_logger_error.assert_called_once \ No newline at end of file + mock_logger_error.assert_called_once + + @mock.patch("requests.Session.send") + def test_empty_text(self, mock_send_request): + mock_send_request.return_value = mock.Mock( + headers={"Content-Type": "application/json"}, text="", status_code=200 + ) + request_handler = RequestHandler(self.session_arguments) + response = request_handler.perform_request("GET", {"url": "https://www.zowe.org"}) + self.assertTrue(response == "")