Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tso issue_command #277

Merged
merged 15 commits into from
Jun 17, 2024
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ 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 tests into proper folders and files [#265](https://github.com/zowe/zowe-client-python-sdk/issues/265)
- Fix the bug on `upload_file_to_dsn`. [#104](https://github.com/zowe/zowe-client-python-sdk/issues/104)
- Fixed the bug on `upload_file_to_dsn` [#104](https://github.com/zowe/zowe-client-python-sdk/issues/104)
pem70 marked this conversation as resolved.
Show resolved Hide resolved

### Bug Fixes

- Fixed truncated responses when issuing TSO commands [#260](https://github.com/zowe/zowe-client-python-sdk/issues/260)

## `1.0.0-dev15`

Expand Down
2 changes: 2 additions & 0 deletions src/core/zowe/core_for_zowe_sdk/logger.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging
import os


class Log:
"""root logger setup and a function to customize logger level"""

Expand All @@ -16,6 +17,7 @@ class Log:
)

loggers = set()

@staticmethod
def registerLogger(name: str):
logger = logging.getLogger(name)
Expand Down
11 changes: 6 additions & 5 deletions src/core/zowe/core_for_zowe_sdk/profile_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@
import warnings
from copy import deepcopy
from typing import Optional
from .logger import Log

import jsonschema
from deepmerge import always_merger

from .config_file import ConfigFile, Profile
from .logger import Log
from .credential_manager import CredentialManager
from .custom_warnings import (
ConfigNotFoundWarning,
ProfileNotFoundWarning,
SecurePropsNotFoundWarning,
)
from .exceptions import ProfileNotFound, SecureProfileLoadFailed, SecureValuesNotFound
from .logger import Log
from .profile_constants import (
BASE_PROFILE,
GLOBAL_CONFIG_NAME,
Expand Down Expand Up @@ -219,7 +218,9 @@
ProfileNotFoundWarning,
)
except Exception as exc:
logger.warning(f"Could not load '{cfg.filename}' at '{cfg.filepath}'" f"because {type(exc).__name__}'{exc}'")
logger.warning(

Check warning on line 221 in src/core/zowe/core_for_zowe_sdk/profile_manager.py

View check run for this annotation

Codecov / codecov/patch

src/core/zowe/core_for_zowe_sdk/profile_manager.py#L221

Added line #L221 was not covered by tests
f"Could not load '{cfg.filename}' at '{cfg.filepath}'" f"because {type(exc).__name__}'{exc}'"
)
warnings.warn(
f"Could not load '{cfg.filename}' at '{cfg.filepath}'" f"because {type(exc).__name__}'{exc}'.",
ConfigNotFoundWarning,
Expand Down Expand Up @@ -253,7 +254,7 @@
If `profile_type` is not base, then we will load properties from both
`profile_type` and base profiles and merge them together.
"""

if profile_name is None and profile_type is None:
self.__logger.error(f"Failed to load profile as both profile_name and profile_type are not set")
raise ProfileNotFound(
Expand All @@ -273,7 +274,7 @@
cfg_name = None
cfg_schema = None
cfg_schema_dir = None

for cfg_layer in (self.project_user_config, self.project_config, self.global_user_config, self.global_config):
if cfg_layer.profiles is None:
try:
Expand Down
19 changes: 12 additions & 7 deletions src/core/zowe/core_for_zowe_sdk/request_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import requests
import urllib3
from .logger import Log

from .exceptions import InvalidRequestMethod, RequestFailed, UnexpectedStatus
from .logger import Log
Expand All @@ -30,7 +29,7 @@ class RequestHandler:
List of supported request methods
"""

def __init__(self, session_arguments, logger_name = __name__):
def __init__(self, session_arguments, logger_name=__name__):
"""
Construct a RequestHandler object.

Expand All @@ -52,7 +51,7 @@ def __handle_ssl_warnings(self):
if not self.session_arguments["verify"]:
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def perform_request(self, method, request_arguments, expected_code=[200], stream = False):
def perform_request(self, method, request_arguments, expected_code=[200], stream=False):
"""Execute an HTTP/HTTPS requests from given arguments and return validated response (JSON).

Parameters
Expand All @@ -74,9 +73,11 @@ def perform_request(self, method, request_arguments, expected_code=[200], stream
self.method = method
self.request_arguments = request_arguments
self.expected_code = expected_code
self.__logger.debug(f"Request method: {self.method}, Request arguments: {self.request_arguments}, Expected code: {expected_code}")
self.__logger.debug(
f"Request method: {self.method}, Request arguments: {self.request_arguments}, Expected code: {expected_code}"
)
self.__validate_method()
self.__send_request(stream = stream)
self.__send_request(stream=stream)
self.__validate_response()
if stream:
return self.response
Expand Down Expand Up @@ -114,14 +115,18 @@ def __validate_response(self):
# Automatically checks if status code is between 200 and 400
if self.response.ok:
if self.response.status_code not in self.expected_code:
self.__logger.error(f"The status code from z/OSMF was: {self.expected_code}\nExpected: {self.response.status_code}\nRequest output:{self.response.text}")
self.__logger.error(
f"The status code from z/OSMF was: {self.expected_code}\nExpected: {self.response.status_code}\nRequest output:{self.response.text}"
)
raise UnexpectedStatus(self.expected_code, self.response.status_code, self.response.text)
else:
output_str = str(self.response.request.url)
output_str += "\n" + str(self.response.request.headers)
output_str += "\n" + str(self.response.request.body)
output_str += "\n" + str(self.response.text)
self.__logger.error(f"HTTP Request has failed with status code {self.response.status_code}. \n {output_str}")
self.__logger.error(
f"HTTP Request has failed with status code {self.response.status_code}. \n {output_str}"
)
raise RequestFailed(self.response.status_code, output_str)

def __normalize_response(self):
Expand Down
2 changes: 1 addition & 1 deletion src/zos_files/zowe/zos_files_for_zowe_sdk/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@

def get_dsn_binary_content_streamed(self, dataset_name, with_prefixes=False):
"""Deprecated function. Please use ds.get_binary_content() instead"""
return self.ds.get_binary_content(dataset_name, with_prefixes, stream=True)
return self.ds.get_binary_content(dataset_name, stream=True, with_prefixes=with_prefixes)

Check warning on line 124 in src/zos_files/zowe/zos_files_for_zowe_sdk/files.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/files.py#L124

Added line #L124 was not covered by tests

def write_to_dsn(self, dataset_name, data, encoding=_ZOWE_FILES_DEFAULT_ENCODING):
"""Deprecated function. Please use ds.write() instead"""
Expand Down
2 changes: 1 addition & 1 deletion src/zos_files/zowe/zos_files_for_zowe_sdk/uss.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@
"""Upload contents of a given file and uploads it to UNIX file"""
if os.path.isfile(input_file):
with open(input_file, "r", encoding="utf-8") as in_file:
response_json = self.write(filepath_name, in_file)
response_json = self.write(filepath_name, in_file.read())

Check warning on line 158 in src/zos_files/zowe/zos_files_for_zowe_sdk/uss.py

View check run for this annotation

Codecov / codecov/patch

src/zos_files/zowe/zos_files_for_zowe_sdk/uss.py#L158

Added line #L158 was not covered by tests
else:
self.logger.error(f"File {input_file} not found.")
raise FileNotFound(input_file)
6 changes: 5 additions & 1 deletion src/zos_tso/zowe/zos_tso_for_zowe_sdk/tso.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
"""

import json

from zowe.core_for_zowe_sdk import SdkApi, constants


Expand Down Expand Up @@ -61,6 +60,11 @@ def issue_command(self, command):
session_key = self.start_tso_session()
command_output = self.send_tso_message(session_key, command)
tso_messages = self.retrieve_tso_messages(command_output)
while not any("TSO PROMPT" in message for message in command_output) or not tso_messages:
custom_args = self._create_custom_request_arguments()
custom_args["url"] = "{}/{}".format(self.request_endpoint, session_key)
command_output = self.request_handler.perform_request("GET", custom_args)["tsoData"]
pem70 marked this conversation as resolved.
Show resolved Hide resolved
tso_messages += self.retrieve_tso_messages(command_output)
self.end_tso_session(session_key)
return tso_messages

Expand Down
1 change: 0 additions & 1 deletion tests/integration/test_zos_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,6 @@ def test_upload_download_delete_dataset(self):
def test_upload_download_delete_uss(self):
self.files.upload_file_to_uss(SAMPLE_JCL_FIXTURE_PATH, self.test_uss_upload)
self.files.download_uss(self.test_uss_upload, SAMPLE_JCL_FIXTURE_PATH + ".tmp")

with open(SAMPLE_JCL_FIXTURE_PATH, "r") as in_file:
old_file_content = in_file.read()
with open(SAMPLE_JCL_FIXTURE_PATH + ".tmp", "r") as in_file:
Expand Down
5 changes: 3 additions & 2 deletions tests/unit/files/datasets/test_copy.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from unittest import TestCase, mock
from zowe.zos_files_for_zowe_sdk import Files, exceptions, Datasets

from zowe.zos_files_for_zowe_sdk import Files


class TestCreateClass(TestCase):
Expand Down Expand Up @@ -69,4 +70,4 @@ def test_copy_dataset_or_member(self, mock_send_request):
]
for test_case in test_values:
Files(self.test_profile).copy_dataset_or_member(**test_case)
mock_send_request.assert_called()
mock_send_request.assert_called()
2 changes: 1 addition & 1 deletion tests/unit/files/datasets/test_delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def test_delete(self, mock_send_request):
mock_send_request.assert_called_once()

@mock.patch("requests.Session.send")
def test_delete_param(self, mock_send_request):
def test_delete_pram(self, mock_send_request):
pem70 marked this conversation as resolved.
Show resolved Hide resolved
"""Test list members sends request"""
self.files_instance = Files(self.test_profile)
mock_send_request.return_value = mock.Mock(headers={"Content-Type": "application/json"}, status_code=200)
Expand Down
28 changes: 21 additions & 7 deletions tests/unit/test_zos_tso.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,24 @@ def test_object_should_be_instance_of_class(self):
@mock.patch("requests.Session.send")
def test_issue_command(self, mock_send_request):
"""Test issuing a command sends a request"""
fake_response = {"servletKey": None, "tsoData": "READY"}
mock_send_request.return_value = mock.Mock(
headers={"Content-Type": "application/json"}, status_code=200, json=lambda: fake_response
)

Tso(self.test_profile).issue_command("TIME")
self.assertEqual(mock_send_request.call_count, 3)
expected = ['READY', 'GO']
message = {"TSO MESSAGE": {
"DATA": expected[0]
}
}
message2 = {"TSO MESSAGE": {
"DATA": expected[1]
}
}
fake_responses = [
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": [ message]}),
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": [ message]}),
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": ["TSO PROMPT", message2]}),
mock.Mock(headers={"Content-Type": "application/json"}, status_code=200, json=lambda: {"servletKey": None, "tsoData": [ message]}),
]

mock_send_request.side_effect = fake_responses

result = Tso(self.test_profile).issue_command("TIME")
self.assertEqual(result, expected)
self.assertEqual(mock_send_request.call_count, 4)
Loading