Skip to content

Commit 41b1d8b

Browse files
committed
allow folder lookup by path or delegate string.
1 parent fecbdf8 commit 41b1d8b

File tree

3 files changed

+112
-10
lines changed

3 files changed

+112
-10
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Unreleased
22
----------
33
**Improvements**
4-
- `folders.get_folder()` can now handle folder names that include a leading or trailing "/" character.
4+
- `folders.get_folder()` can now handle folder paths and delegates (e.g. @public).
55

66
v1.8.1 (2023-01-19)
77
----------

src/sasctl/_services/folders.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ def get_folder(cls, folder, refresh=False):
6969
Parameters
7070
----------
7171
folder : str or dict
72-
Name, ID, or dictionary representation of the folder.
72+
May be one of:
73+
- folder name
74+
- folder ID
75+
- folder path
76+
- folder delegate string
77+
- dictionary representation of the folder
7378
refresh : bool, optional
7479
Obtain an updated copy of the folder.
7580
@@ -84,10 +89,48 @@ def get_folder(cls, folder, refresh=False):
8489
returned unless `refresh` is set. This prevents unnecessary REST
8590
calls when data is already available on the client.
8691
92+
Examples
93+
--------
94+
The following four examples are all functionally equivalent.
95+
96+
>>> get_folder("Public")
97+
{"name": "Public", "id": "4a737209-5662"}
98+
99+
>>> get_folder("/Public")
100+
{"name": "Public", "id": "4a737209-5662"}
101+
102+
>>> get_folder("@public")
103+
{"name": "Public", "id": "4a737209-5662"}
104+
105+
>>> get_folder("@public")
106+
{"name": "Public", "id": "4a737209-5662"}
107+
108+
>>> get_folder("4a737209-5662")
109+
{"name": "Public", "id": "4a737209-5662"}
110+
111+
The full folder path can also be specified.
112+
113+
>>> get_folder("/Public/Demo")
114+
{"name": "Demo", "id": "148081bf-1c86"}
115+
116+
Special folders can be identified using a delegate string. Currently supported
117+
are: @myFolder, @appDataFolder, @myHistory, @myFavorites, and @public.
118+
119+
>>> get_folder("@myFolder")
120+
{"name": "My Folder", "id": "71687cd2-db4b"}
121+
87122
"""
88-
# If users pass in a folder name like "/Public" instead of "Public"
89-
# then just cleanup the folder name so that it matches what's returned by Viya.
90-
if isinstance(folder, str):
91-
folder = folder.strip("/")
123+
# If a folder path is specified, lookup folder using the full path instead of the name.
124+
if isinstance(folder, str) and "/" in folder:
125+
# Path must include a leading "/"
126+
if not folder.startswith("/"):
127+
folder = f"/{folder}"
128+
129+
return cls.get("/folders/@item", params={"path": folder})
130+
131+
# Its possible to lookup special folders by using a handle (called a delegate string in docs)
132+
# Current values (2022.09) are @myFolder, @appDataFolder, @myHistory, @myFavorites, @public.
133+
if isinstance(folder, str) and folder.startswith("@"):
134+
return cls.get(f"/folders/{folder}")
92135

93136
return cls._get_folder(folder, refresh=refresh)

tests/unit/test_folders.py

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ def test_create_folder_with_parent():
7878

7979

8080
@mock.patch("sasctl.core.Session.request")
81-
def test_get_folder_with_path(request):
82-
"""Verify that simple folder paths instead of names are handled."""
81+
def test_get_folder_by_name(request):
82+
"""Verify that looking up a folder by name works."""
8383
request.return_value.status_code = 200
8484
request.return_value.json.return_value = {"name": "Spam"}
8585

86-
folder = folders.get_folder("/Spam")
86+
folder = folders.get_folder("Spam")
8787
assert folder is not None
8888
assert folder.name == "Spam"
8989

@@ -95,4 +95,63 @@ def test_get_folder_with_path(request):
9595

9696
# / should have been stripped out of folder name
9797
assert '"Spam"' in search_filter
98-
assert '"/Spam"' not in search_filter
98+
99+
100+
@mock.patch("sasctl.core.Session.request")
101+
def test_get_folder_by_path(request):
102+
"""Verify that simple folder paths instead of names are handled."""
103+
request.return_value.status_code = 200
104+
request.return_value.json.return_value = {"name": "Spam"}
105+
106+
folder = folders.get_folder("/Spam")
107+
assert folder is not None
108+
assert folder.name == "Spam"
109+
110+
# Should not have called list_folders to search. Should have been a direct call to lookup by path.
111+
assert request.call_count == 1
112+
113+
url, params = request.call_args_list[0]
114+
query_string = params["params"]
115+
116+
assert url[1] == "/folders/folders/@item"
117+
assert query_string["path"] == "/Spam"
118+
119+
120+
@mock.patch("sasctl.core.Session.request")
121+
def test_get_folder_by_path(request):
122+
"""Verify that complex folder paths are handled."""
123+
request.return_value.status_code = 200
124+
request.return_value.json.return_value = {"name": "Spam"}
125+
126+
folder = folders.get_folder("Spam/Eggs/Spam")
127+
assert folder is not None
128+
assert folder.name == "Spam"
129+
130+
# Should not have called list_folders to search. Should have been a direct call to lookup by path.
131+
assert request.call_count == 1
132+
133+
url, params = request.call_args_list[0]
134+
query_string = params["params"]
135+
136+
assert url[1] == "/folders/folders/@item"
137+
138+
# Leading "/" should have been added
139+
assert query_string["path"] == "/Spam/Eggs/Spam"
140+
141+
142+
@mock.patch("sasctl.core.Session.request")
143+
def test_get_folder_by_delegate(request):
144+
"""Verify that folder can be found by using delegate strings (e.g. @public)."""
145+
request.return_value.status_code = 200
146+
request.return_value.json.return_value = {"name": "Spam"}
147+
148+
folder = folders.get_folder("@spam")
149+
assert folder is not None
150+
assert folder.name == "Spam"
151+
152+
# Should not have called list_folders to search. Should have been a direct call to GET by id.
153+
assert request.call_count == 1
154+
155+
url, params = request.call_args_list[0]
156+
assert url[1] == "/folders/folders/@spam"
157+
assert params == {}

0 commit comments

Comments
 (0)