Skip to content

Commit 3a15fc1

Browse files
authored
Merge pull request #277 from zowe/TSO
Tso issue_command
2 parents cdf6265 + f580bd6 commit 3a15fc1

File tree

11 files changed

+62
-33
lines changed

11 files changed

+62
-33
lines changed

CHANGELOG.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ All notable changes to the Zowe Client Python SDK will be documented in this fil
99
- Added logger class to core SDK [#185](https://github.com/zowe/zowe-client-python-sdk/issues/185)
1010
- 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)
1111
- Refactored tests into proper folders and files [#265](https://github.com/zowe/zowe-client-python-sdk/issues/265)
12-
- Fix the bug on `upload_file_to_dsn`. [#104](https://github.com/zowe/zowe-client-python-sdk/issues/104)
12+
13+
### Bug Fixes
14+
15+
- Fixed truncated responses when issuing TSO commands [#260](https://github.com/zowe/zowe-client-python-sdk/issues/260)
16+
- Fixed a bug on `upload_file_to_dsn` where it would not properly convert line endings on Windows. [#104](https://github.com/zowe/zowe-client-python-sdk/issues/104)
1317

1418
## `1.0.0-dev15`
1519

src/core/zowe/core_for_zowe_sdk/logger.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import os
33

4+
45
class Log:
56
"""root logger setup and a function to customize logger level"""
67

@@ -16,6 +17,7 @@ class Log:
1617
)
1718

1819
loggers = set()
20+
1921
@staticmethod
2022
def registerLogger(name: str):
2123
logger = logging.getLogger(name)

src/core/zowe/core_for_zowe_sdk/profile_manager.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,19 @@
1515
import warnings
1616
from copy import deepcopy
1717
from typing import Optional
18-
from .logger import Log
1918

2019
import jsonschema
2120
from deepmerge import always_merger
2221

2322
from .config_file import ConfigFile, Profile
24-
from .logger import Log
2523
from .credential_manager import CredentialManager
2624
from .custom_warnings import (
2725
ConfigNotFoundWarning,
2826
ProfileNotFoundWarning,
2927
SecurePropsNotFoundWarning,
3028
)
3129
from .exceptions import ProfileNotFound, SecureProfileLoadFailed, SecureValuesNotFound
30+
from .logger import Log
3231
from .profile_constants import (
3332
BASE_PROFILE,
3433
GLOBAL_CONFIG_NAME,
@@ -219,7 +218,9 @@ def get_profile(
219218
ProfileNotFoundWarning,
220219
)
221220
except Exception as exc:
222-
logger.warning(f"Could not load '{cfg.filename}' at '{cfg.filepath}'" f"because {type(exc).__name__}'{exc}'")
221+
logger.warning(
222+
f"Could not load '{cfg.filename}' at '{cfg.filepath}'" f"because {type(exc).__name__}'{exc}'"
223+
)
223224
warnings.warn(
224225
f"Could not load '{cfg.filename}' at '{cfg.filepath}'" f"because {type(exc).__name__}'{exc}'.",
225226
ConfigNotFoundWarning,
@@ -253,7 +254,7 @@ def load(
253254
If `profile_type` is not base, then we will load properties from both
254255
`profile_type` and base profiles and merge them together.
255256
"""
256-
257+
257258
if profile_name is None and profile_type is None:
258259
self.__logger.error(f"Failed to load profile as both profile_name and profile_type are not set")
259260
raise ProfileNotFound(
@@ -273,7 +274,7 @@ def load(
273274
cfg_name = None
274275
cfg_schema = None
275276
cfg_schema_dir = None
276-
277+
277278
for cfg_layer in (self.project_user_config, self.project_config, self.global_user_config, self.global_config):
278279
if cfg_layer.profiles is None:
279280
try:

src/core/zowe/core_for_zowe_sdk/request_handler.py

+12-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
import requests
1414
import urllib3
15-
from .logger import Log
1615

1716
from .exceptions import InvalidRequestMethod, RequestFailed, UnexpectedStatus
1817
from .logger import Log
@@ -30,7 +29,7 @@ class RequestHandler:
3029
List of supported request methods
3130
"""
3231

33-
def __init__(self, session_arguments, logger_name = __name__):
32+
def __init__(self, session_arguments, logger_name=__name__):
3433
"""
3534
Construct a RequestHandler object.
3635
@@ -52,7 +51,7 @@ def __handle_ssl_warnings(self):
5251
if not self.session_arguments["verify"]:
5352
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
5453

55-
def perform_request(self, method, request_arguments, expected_code=[200], stream = False):
54+
def perform_request(self, method, request_arguments, expected_code=[200], stream=False):
5655
"""Execute an HTTP/HTTPS requests from given arguments and return validated response (JSON).
5756
5857
Parameters
@@ -74,9 +73,11 @@ def perform_request(self, method, request_arguments, expected_code=[200], stream
7473
self.method = method
7574
self.request_arguments = request_arguments
7675
self.expected_code = expected_code
77-
self.__logger.debug(f"Request method: {self.method}, Request arguments: {self.request_arguments}, Expected code: {expected_code}")
76+
self.__logger.debug(
77+
f"Request method: {self.method}, Request arguments: {self.request_arguments}, Expected code: {expected_code}"
78+
)
7879
self.__validate_method()
79-
self.__send_request(stream = stream)
80+
self.__send_request(stream=stream)
8081
self.__validate_response()
8182
if stream:
8283
return self.response
@@ -114,14 +115,18 @@ def __validate_response(self):
114115
# Automatically checks if status code is between 200 and 400
115116
if self.response.ok:
116117
if self.response.status_code not in self.expected_code:
117-
self.__logger.error(f"The status code from z/OSMF was: {self.expected_code}\nExpected: {self.response.status_code}\nRequest output:{self.response.text}")
118+
self.__logger.error(
119+
f"The status code from z/OSMF was: {self.expected_code}\nExpected: {self.response.status_code}\nRequest output:{self.response.text}"
120+
)
118121
raise UnexpectedStatus(self.expected_code, self.response.status_code, self.response.text)
119122
else:
120123
output_str = str(self.response.request.url)
121124
output_str += "\n" + str(self.response.request.headers)
122125
output_str += "\n" + str(self.response.request.body)
123126
output_str += "\n" + str(self.response.text)
124-
self.__logger.error(f"HTTP Request has failed with status code {self.response.status_code}. \n {output_str}")
127+
self.__logger.error(
128+
f"HTTP Request has failed with status code {self.response.status_code}. \n {output_str}"
129+
)
125130
raise RequestFailed(self.response.status_code, output_str)
126131

127132
def __normalize_response(self):

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def get_dsn_binary_content(self, dataset_name, with_prefixes=False):
121121

122122
def get_dsn_binary_content_streamed(self, dataset_name, with_prefixes=False):
123123
"""Deprecated function. Please use ds.get_binary_content() instead"""
124-
return self.ds.get_binary_content(dataset_name, with_prefixes, stream=True)
124+
return self.ds.get_binary_content(dataset_name, stream=True, with_prefixes=with_prefixes)
125125

126126
def write_to_dsn(self, dataset_name, data, encoding=_ZOWE_FILES_DEFAULT_ENCODING):
127127
"""Deprecated function. Please use ds.write() instead"""

src/zos_files/zowe/zos_files_for_zowe_sdk/uss.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def upload(self, input_file, filepath_name, encoding=_ZOWE_FILES_DEFAULT_ENCODIN
155155
"""Upload contents of a given file and uploads it to UNIX file"""
156156
if os.path.isfile(input_file):
157157
with open(input_file, "r", encoding="utf-8") as in_file:
158-
response_json = self.write(filepath_name, in_file)
158+
response_json = self.write(filepath_name, in_file.read())
159159
else:
160160
self.logger.error(f"File {input_file} not found.")
161161
raise FileNotFound(input_file)

src/zos_tso/zowe/zos_tso_for_zowe_sdk/tso.py

+9
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ def issue_command(self, command):
6161
session_key = self.start_tso_session()
6262
command_output = self.send_tso_message(session_key, command)
6363
tso_messages = self.retrieve_tso_messages(command_output)
64+
while not any("TSO PROMPT" in message for message in command_output) or not tso_messages:
65+
command_output = self.__get_tso_data(session_key)
66+
tso_messages += self.retrieve_tso_messages(command_output)
6467
self.end_tso_session(session_key)
6568
return tso_messages
6669

@@ -202,3 +205,9 @@ def retrieve_tso_messages(self, response_json):
202205
A list containing the TSO response messages
203206
"""
204207
return [message["TSO MESSAGE"]["DATA"] for message in response_json if "TSO MESSAGE" in message]
208+
209+
def __get_tso_data(self, session_key):
210+
custom_args = self._create_custom_request_arguments()
211+
custom_args["url"] = "{}/{}".format(self.request_endpoint, session_key)
212+
command_output = self.request_handler.perform_request("GET", custom_args)["tsoData"]
213+
return command_output

tests/integration/test_zos_files.py

-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,6 @@ def test_upload_download_delete_dataset(self):
149149
def test_upload_download_delete_uss(self):
150150
self.files.upload_file_to_uss(SAMPLE_JCL_FIXTURE_PATH, self.test_uss_upload)
151151
self.files.download_uss(self.test_uss_upload, SAMPLE_JCL_FIXTURE_PATH + ".tmp")
152-
153152
with open(SAMPLE_JCL_FIXTURE_PATH, "r") as in_file:
154153
old_file_content = in_file.read()
155154
with open(SAMPLE_JCL_FIXTURE_PATH + ".tmp", "r") as in_file:

tests/unit/files/datasets/test_copy.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from unittest import TestCase, mock
2-
from zowe.zos_files_for_zowe_sdk import Files, exceptions, Datasets
2+
3+
from zowe.zos_files_for_zowe_sdk import Files
34

45

56
class TestCreateClass(TestCase):
@@ -69,4 +70,4 @@ def test_copy_dataset_or_member(self, mock_send_request):
6970
]
7071
for test_case in test_values:
7172
Files(self.test_profile).copy_dataset_or_member(**test_case)
72-
mock_send_request.assert_called()
73+
mock_send_request.assert_called()

tests/unit/files/datasets/test_delete.py

+2-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import re
22
from unittest import TestCase, mock
33

4-
from zowe.zos_files_for_zowe_sdk import Files, exceptions, Datasets
4+
from zowe.zos_files_for_zowe_sdk import Datasets, Files, exceptions
55

66

77
class TestDeleteClass(TestCase):
@@ -32,17 +32,11 @@ def test_delete_param(self, mock_send_request):
3232
mock_send_request.return_value = mock.Mock(headers={"Content-Type": "application/json"}, status_code=200)
3333
mock_send_request.return_value.json.return_value = {}
3434

35-
test_cases = [
36-
("MY.PDS", 1000, "m1"),
37-
("MY.C", 100, "m2"),
38-
("MY.D", 1000, "member"),
39-
("MY.E", 500, "extended")
40-
]
35+
test_cases = [("MY.PDS", 1000, "m1"), ("MY.C", 100, "m2"), ("MY.D", 1000, "member"), ("MY.E", 500, "extended")]
4136

4237
for dataset_name, volume, member_name in test_cases:
4338
result = self.files_instance.delete_data_set(dataset_name, volume, member_name)
4439
self.assertEqual(result, {})
4540
mock_send_request.assert_called()
4641
prepared_request = mock_send_request.call_args[0][0]
4742
self.assertEqual(prepared_request.method, "DELETE")
48-

tests/unit/test_zos_tso.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,24 @@ def test_object_should_be_instance_of_class(self):
2626
@mock.patch("requests.Session.send")
2727
def test_issue_command(self, mock_send_request):
2828
"""Test issuing a command sends a request"""
29-
fake_response = {"servletKey": None, "tsoData": "READY"}
30-
mock_send_request.return_value = mock.Mock(
31-
headers={"Content-Type": "application/json"}, status_code=200, json=lambda: fake_response
32-
)
33-
34-
Tso(self.test_profile).issue_command("TIME")
35-
self.assertEqual(mock_send_request.call_count, 3)
29+
expected = ['READY', 'GO']
30+
message = {"TSO MESSAGE": {
31+
"DATA": expected[0]
32+
}
33+
}
34+
message2 = {"TSO MESSAGE": {
35+
"DATA": expected[1]
36+
}
37+
}
38+
fake_responses = [
39+
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": [ message]}),
40+
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": [ message]}),
41+
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": ["TSO PROMPT", message2]}),
42+
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": [ message]}),
43+
]
44+
45+
mock_send_request.side_effect = fake_responses
46+
47+
result = Tso(self.test_profile).issue_command("TIME")
48+
self.assertEqual(result, expected)
49+
self.assertEqual(mock_send_request.call_count, 4)

0 commit comments

Comments
 (0)