Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions src/core/zowe/core_for_zowe_sdk/sdk_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,13 @@ def _encode_uri_path_for_zos(self, zos_uri_path: str) -> str:
"""
Encode a z/OS resource (dataset, job, or volser) path for the path component of a URI.

None of the documented z/OS resource naming special characters require encoding
to be processed successfully by z/OSMF. API-ML rejects a literal "#" with an
HTTP 400 error unless it is encoded, so it is the only character adjusted here.
Dot-segments are resolved against the service root so a caller-supplied name such as
"../../restjobs/jobs/OTHER" always resolves to a path under the intended resource. A
literal "?" is always percent-encoded so a name such as "X?fsname=Y" is parsed as a
single path value rather than a path followed by query parameters. None of the other
documented z/OS resource naming special characters require encoding to be processed
successfully by z/OSMF. API-ML rejects a literal "#" with an HTTP 400 error unless it is
encoded, so it is also adjusted here when routed through API-ML.

Parameters
----------
Expand All @@ -161,11 +165,15 @@ def _encode_uri_path_for_zos(self, zos_uri_path: str) -> str:
Returns
-------
str
The path, with "#" encoded when the session is routed through API-ML
The normalized path, with "?" always encoded and "#" encoded when the session is
routed through API-ML
"""
# Normalizing against root collapses ".." segments without escaping the service path
normalized = posixpath.normpath("/" + zos_uri_path).lstrip("/")
encoded = normalized.replace("?", "%3F")
if self._is_using_apiml():
return zos_uri_path.replace("#", "%23")
return zos_uri_path
encoded = encoded.replace("#", "%23")
return encoded

def _encode_uri_path_for_uss(self, uss_uri_path: str) -> str:
"""
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/core/test_sdk_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ def test_encode_uri_path_for_zos_encodes_hash_for_apiml(self):

self.assertEqual(sdk_api._encode_uri_path_for_zos("MY.DS#NAME$HERE"), "MY.DS%23NAME$HERE")

def test_encode_uri_path_for_zos_encodes_question_mark(self):
"""A literal '?' must be encoded so the name is parsed as a single path value, not a query string."""
sdk_api = SdkApi(self.basic_props, self.default_url)

self.assertEqual(sdk_api._encode_uri_path_for_zos("X?fsname=Y"), "X%3Ffsname=Y")

def test_encode_uri_path_for_zos_resolves_dot_segments(self):
"""Dot-segments must be resolved against the service root so the request always targets the intended resource."""
sdk_api = SdkApi(self.basic_props, self.default_url)

self.assertEqual(
sdk_api._encode_uri_path_for_zos("../../restjobs/jobs/OTHERJOB/JOB00001"),
"restjobs/jobs/OTHERJOB/JOB00001",
)

def test_encode_uri_path_for_uss_normalizes_path(self):
"""USS paths should be normalized and stripped of their leading slash."""
sdk_api = SdkApi(self.basic_props, self.default_url)
Expand Down
Loading