Skip to content

Commit 3ccff71

Browse files
authored
Merge pull request #32 from WEHI-ResearchComputing/base-url-compat
Support old rest.php URLs
2 parents ec98e27 + 1be75a2 commit 3ccff71

File tree

3 files changed

+136
-3
lines changed

3 files changed

+136
-3
lines changed

docs/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Version 2.1.1
44

5+
### Changed
6+
7+
* Providing a base URL that ends in `/rest.php` is now deprecated, but is still supported [[#33]](https://github.com/WEHI-ResearchComputing/FileSenderCli/pull/29).
8+
59
### Fixed
610

711
* Base URL was not being set correctly for download operations [[#29]](https://github.com/WEHI-ResearchComputing/FileSenderCli/pull/29).

filesender/api.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from dataclasses import dataclass
21
from typing import Any, Iterable, List, Optional, Tuple, AsyncIterator, Union
32
from filesender.download import files_from_page, DownloadFile
43
import filesender.response_types as response
@@ -100,10 +99,17 @@ def iter_files(paths: Iterable[Path], root: Optional[Path] = None) -> Iterable[T
10099
# If this is a nested file, use the relative path from the root directory as the name
101100
yield str(path.relative_to(root)), path
102101

103-
@dataclass
104102
class EndpointHandler:
105103
base: str
106104

105+
def __init__(self, base: str) -> None:
106+
# Backwards compatibility to support the old way of specifying the base URL
107+
base = base.rstrip("/")
108+
if base.endswith("/rest.php"):
109+
logger.warning("The base URL should not include /rest.php. This will no longer be supported in a future version.")
110+
base = base.removesuffix("/rest.php")
111+
self.base = base
112+
107113
def api(self) -> str:
108114
return f"{self.base}/rest.php"
109115

test/test_client.py

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,137 @@
55
import pytest
66
from filesender.request_types import GuestOptions
77
from filesender.benchmark import make_tempfile, make_tempfiles, benchmark
8-
from unittest.mock import MagicMock, patch
8+
from unittest.mock import MagicMock
99

1010
def count_files_recursively(path: Path) -> int:
1111
"""
1212
Returns a recursive count of the number of files within a directory. Subdirectories are not counted.
1313
"""
1414
return sum([1 if child.is_file() else 0 for child in path.rglob("*")])
1515

16+
def test_base_url_rest(base_url: str, username: str, apikey: str, recipient: str):
17+
"""
18+
This tests the case where the user provides a base URL that ends in /rest.php, which isn't
19+
encouraged, but needs to be supported for backwards compatibility.
20+
"""
21+
22+
user_client = FileSenderClient(
23+
base_url=base_url + "/rest.php", auth=UserAuth(api_key=apikey, username=username)
24+
)
25+
assert user_client.urls.server_info() == f"{base_url}/rest.php/info"
26+
27+
28+
@pytest.mark.asyncio
29+
async def test_round_trip_dir(base_url: str, username: str, apikey: str, recipient: str):
30+
"""
31+
This tests uploading two 1MB files in a directory
32+
"""
33+
34+
user_client = FileSenderClient(
35+
base_url=base_url, auth=UserAuth(api_key=apikey, username=username)
36+
)
37+
await user_client.prepare()
38+
39+
with tempfile.TemporaryDirectory() as tempdir:
40+
with make_tempfiles(size=1024**2, n=2, suffix=".dat", dir = tempdir):
41+
# The user uploads the entire directory
42+
transfer = await user_client.upload_workflow(
43+
files=[Path(tempdir)], transfer_args={"recipients": [recipient], "from": username}
44+
)
45+
46+
download_client = FileSenderClient(base_url=base_url)
47+
48+
with tempfile.TemporaryDirectory() as download_dir:
49+
await download_client.download_files(
50+
token=transfer["recipients"][0]["token"],
51+
out_dir=Path(download_dir),
52+
)
53+
assert count_files_recursively(Path(download_dir)) == 2
54+
55+
56+
@pytest.mark.asyncio
57+
@pytest.mark.parametrize("guest_opts", [{}, {"can_only_send_to_me": False}])
58+
async def test_voucher_round_trip(
59+
base_url: str, username: str, apikey: str, recipient: str, guest_opts: GuestOptions
60+
):
61+
"""
62+
This tests uploading a 1GB file, with ensures that the chunking behaviour is correct,
63+
but also the multithreaded uploading
64+
"""
65+
user_client = FileSenderClient(
66+
base_url=base_url, auth=UserAuth(api_key=apikey, username=username)
67+
)
68+
69+
# Invite the guest
70+
guest = await user_client.create_guest({
71+
"recipient": recipient,
72+
"from": username,
73+
"options": {
74+
"guest": {
75+
# See https://github.com/filesender/filesender/issues/1889
76+
"can_only_send_to_me": True
77+
},
78+
}
79+
})
80+
81+
guest_auth = GuestAuth(guest_token=guest["token"])
82+
guest_client = FileSenderClient(
83+
base_url=base_url,
84+
auth=guest_auth,
85+
)
86+
await guest_client.prepare()
87+
await guest_auth.prepare(guest_client.http_client)
88+
89+
with make_tempfile(size=1024**2, suffix=".dat") as path:
90+
# The guest uploads the file
91+
transfer = await guest_client.upload_workflow(
92+
files=[path],
93+
# FileSender will accept basically any recipients array here, but the argument can't be missing
94+
transfer_args={"recipients": []}
95+
)
96+
97+
with tempfile.TemporaryDirectory() as download_dir:
98+
# The user downloads the file
99+
await user_client.download_file(
100+
token=transfer["recipients"][0]["token"],
101+
file_id=transfer["files"][0]["id"],
102+
out_dir=Path(download_dir),
103+
)
104+
assert count_files_recursively(Path(download_dir)) == 1
105+
106+
107+
@pytest.mark.asyncio
108+
@pytest.mark.parametrize("guest_opts", [{}, {"can_only_send_to_me": False}])
109+
async def test_guest_creation(
110+
base_url: str, username: str, apikey: str, recipient: str, guest_opts: GuestOptions
111+
):
112+
user_client = FileSenderClient(
113+
base_url=base_url, auth=UserAuth(api_key=apikey, username=username)
114+
)
115+
116+
# Invite the guest
117+
guest = await user_client.create_guest(
118+
{"recipient": recipient, "from": username, "options": {"guest": guest_opts}}
119+
)
120+
121+
# Check that the options were acknowledged by the server
122+
for key, value in guest_opts.items():
123+
assert guest["options"][key] == value
124+
125+
126+
@pytest.mark.skip("This is inconsistent")
127+
@pytest.mark.asyncio
128+
async def test_upload_semaphore(
129+
base_url: str, username: str, apikey: str, recipient: str
130+
):
131+
"""
132+
Tests that limiting the concurrency of the client increases the runtime but decreases the memory usage
133+
"""
134+
with make_tempfiles(size=100_000_000, n=3) as paths:
135+
limited, unlimited = benchmark(paths, [1, float("inf")], [1, float("inf")], base_url, username, apikey, recipient)
136+
assert unlimited.time < limited.time
137+
assert unlimited.memory > limited.memory
138+
16139
@pytest.mark.asyncio
17140
async def test_round_trip(base_url: str, username: str, apikey: str, recipient: str):
18141
"""

0 commit comments

Comments
 (0)