Skip to content

Commit f7db3b0

Browse files
Adding Trailing Slash, Fix Double Slash Issue in _get_url, and Edge Case Tests (#1598)
* adding trailing slash * prevent double slash before appending the API method * tests if adds or preserves the trailing slash * testing _get_url * improving tests and _get_url() * removing duplicated test case * test /api * Update tests/slack_sdk_async/web/test_web_client_url_format.py Co-authored-by: William Bergamin <[email protected]> * Update tests/slack_sdk_async/web/test_web_client_url_format.py Co-authored-by: William Bergamin <[email protected]> --------- Co-authored-by: William Bergamin <[email protected]>
1 parent 5464f89 commit f7db3b0

File tree

9 files changed

+92
-0
lines changed

9 files changed

+92
-0
lines changed

slack_sdk/web/async_base_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ def __init__(
4646
):
4747
self.token = None if token is None else token.strip()
4848
"""A string specifying an `xoxp-*` or `xoxb-*` token."""
49+
if not base_url.endswith("/"):
50+
base_url += "/"
4951
self.base_url = base_url
5052
"""A string representing the Slack API base URL.
5153
Default is `'https://slack.com/api/'`."""

slack_sdk/web/base_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ def __init__(
5959
):
6060
self.token = None if token is None else token.strip()
6161
"""A string specifying an `xoxp-*` or `xoxb-*` token."""
62+
if not base_url.endswith("/"):
63+
base_url += "/"
6264
self.base_url = base_url
6365
"""A string representing the Slack API base URL.
6466
Default is `'https://slack.com/api/'`."""

slack_sdk/web/internal_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ def _get_url(base_url: str, api_method: str) -> str:
6767
The absolute API URL.
6868
e.g. 'https://slack.com/api/chat.postMessage'
6969
"""
70+
# Ensure no leading slash in api_method to prevent double slashes
71+
api_method = api_method.lstrip("/")
7072
return urljoin(base_url, api_method)
7173

7274

slack_sdk/web/legacy_base_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ def __init__(
6262
):
6363
self.token = None if token is None else token.strip()
6464
"""A string specifying an `xoxp-*` or `xoxb-*` token."""
65+
if not base_url.endswith("/"):
66+
base_url += "/"
6567
self.base_url = base_url
6668
"""A string representing the Slack API base URL.
6769
Default is `'https://slack.com/api/'`."""

tests/slack_sdk/web/test_internal_utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
_parse_web_class_objects,
1313
_to_v2_file_upload_item,
1414
_next_cursor_is_present,
15+
_get_url,
1516
)
1617

1718

@@ -108,3 +109,20 @@ def test_next_cursor_is_present(self):
108109
assert _next_cursor_is_present({"response_metadata": {"next_cursor": ""}}) is False
109110
assert _next_cursor_is_present({"response_metadata": {"next_cursor": None}}) is False
110111
assert _next_cursor_is_present({"something_else": {"next_cursor": "next-page"}}) is False
112+
113+
def test_get_url_prevent_double_slash(self):
114+
# Test case: Prevent double slash when both base_url and api_method include slashes
115+
api_url = _get_url("https://slack.com/api/", "/chat.postMessage")
116+
self.assertEqual(
117+
api_url,
118+
"https://slack.com/api/chat.postMessage",
119+
"Should correctly handle and remove double slashes between base_url and api_method",
120+
)
121+
122+
# Test case: Handle api_method without leading slash
123+
api_url = _get_url("https://slack.com/api/", "chat.postMessage")
124+
self.assertEqual(
125+
api_url,
126+
"https://slack.com/api/chat.postMessage",
127+
"Should correctly handle api_method without a leading slash",
128+
)

tests/slack_sdk/web/test_legacy_web_client_url_format.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ def setUp(self):
1010
setup_mock_web_api_server(self, MockHandler)
1111
self.client = LegacyWebClient(token="xoxb-api_test", base_url="http://localhost:8888")
1212
self.client_base_url_slash = LegacyWebClient(token="xoxb-api_test", base_url="http://localhost:8888/")
13+
self.client_api = LegacyWebClient(token="xoxb-api_test", base_url="http://localhost:8888/api")
14+
self.client_api_slash = LegacyWebClient(token="xoxb-api_test", base_url="http://localhost:8888/api/")
1315

1416
def tearDown(self):
1517
cleanup_mock_web_api_server(self)
@@ -33,3 +35,19 @@ def test_base_url_with_slash_api_method_with_slash(self):
3335
def test_base_url_without_slash_api_method_with_slash_and_trailing_slash(self):
3436
self.client.api_call("/chat.postMessage/")
3537
assert_received_request_count(self, "/chat.postMessage/", 1)
38+
39+
def test_base_url_with_api(self):
40+
self.client_api.api_call("chat.postMessage")
41+
assert_received_request_count(self, "/api/chat.postMessage", 1)
42+
43+
def test_base_url_with_api_method_without_slash_method_with_slash(self):
44+
self.client_api.api_call("/chat.postMessage")
45+
assert_received_request_count(self, "/api/chat.postMessage", 1)
46+
47+
def test_base_url_with_api_slash(self):
48+
self.client_api_slash.api_call("chat.postMessage")
49+
assert_received_request_count(self, "/api/chat.postMessage", 1)
50+
51+
def test_base_url_with_api_slash_and_method_with_slash(self):
52+
self.client_api_slash.api_call("/chat.postMessage")
53+
assert_received_request_count(self, "/api/chat.postMessage", 1)

tests/slack_sdk/web/test_web_client.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,11 @@ def test_user_auth_blocks(self):
227227
user_auth_blocks=[DividerBlock(), DividerBlock()],
228228
)
229229
self.assertIsNone(new_message.get("error"))
230+
231+
def test_base_url_appends_trailing_slash_issue_15141(self):
232+
client = self.client
233+
self.assertEqual(client.base_url, "http://localhost:8888/")
234+
235+
def test_base_url_preserves_trailing_slash_issue_15141(self):
236+
client = WebClient(base_url="http://localhost:8888/")
237+
self.assertEqual(client.base_url, "http://localhost:8888/")

tests/slack_sdk/web/test_web_client_url_format.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ def setUp(self):
1010
setup_mock_web_api_server(self, MockHandler)
1111
self.client = WebClient(token="xoxb-api_test", base_url="http://localhost:8888")
1212
self.client_base_url_slash = WebClient(token="xoxb-api_test", base_url="http://localhost:8888/")
13+
self.client_api = WebClient(token="xoxb-api_test", base_url="http://localhost:8888/api")
14+
self.client_api_slash = WebClient(token="xoxb-api_test", base_url="http://localhost:8888/api/")
1315

1416
def tearDown(self):
1517
cleanup_mock_web_api_server(self)
@@ -33,3 +35,19 @@ def test_base_url_with_slash_api_method_with_slash(self):
3335
def test_base_url_without_slash_api_method_with_slash_and_trailing_slash(self):
3436
self.client.api_call("/chat.postMessage/")
3537
assert_received_request_count(self, "/chat.postMessage/", 1)
38+
39+
def test_base_url_with_api(self):
40+
self.client_api.api_call("chat.postMessage")
41+
assert_received_request_count(self, "/api/chat.postMessage", 1)
42+
43+
def test_base_url_with_api_method_with_slash(self):
44+
self.client_api.api_call("/chat.postMessage")
45+
assert_received_request_count(self, "/api/chat.postMessage", 1)
46+
47+
def test_base_url_with_api_slash(self):
48+
self.client_api_slash.api_call("chat.postMessage")
49+
assert_received_request_count(self, "/api/chat.postMessage", 1)
50+
51+
def test_base_url_with_api_slash_and_method_with_slash(self):
52+
self.client_api_slash.api_call("/chat.postMessage")
53+
assert_received_request_count(self, "/api/chat.postMessage", 1)

tests/slack_sdk_async/web/test_web_client_url_format.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ def setUp(self):
1515
setup_mock_web_api_server_async(self, MockHandler)
1616
self.client = AsyncWebClient(token="xoxb-api_test", base_url="http://localhost:8888")
1717
self.client_base_url_slash = AsyncWebClient(token="xoxb-api_test", base_url="http://localhost:8888/")
18+
self.client_api = AsyncWebClient(token="xoxb-api_test", base_url="http://localhost:8888/api")
19+
self.client_api_slash = AsyncWebClient(token="xoxb-api_test", base_url="http://localhost:8888/api/")
1820

1921
def tearDown(self):
2022
cleanup_mock_web_api_server_async(self)
@@ -43,3 +45,23 @@ async def test_base_url_with_slash_api_method_with_slash(self):
4345
async def test_base_url_without_slash_api_method_with_slash_and_trailing_slash(self):
4446
await self.client.api_call("/chat.postMessage/")
4547
await assert_received_request_count_async(self, "/chat.postMessage/", 1)
48+
49+
@async_test
50+
async def test_base_url_with_api(self):
51+
await self.client_api.api_call("chat.postMessage")
52+
await assert_received_request_count_async(self, "/api/chat.postMessage", 1)
53+
54+
@async_test
55+
async def test_base_url_with_api_method_without_slash_method_with_slash(self):
56+
await self.client_api.api_call("/chat.postMessage")
57+
await assert_received_request_count_async(self, "/api/chat.postMessage", 1)
58+
59+
@async_test
60+
async def test_base_url_with_api_slash(self):
61+
await self.client_api_slash.api_call("chat.postMessage")
62+
await assert_received_request_count_async(self, "/api/chat.postMessage", 1)
63+
64+
@async_test
65+
async def test_base_url_with_api_slash_and_method_with_slash(self):
66+
await self.client_api_slash.api_call("/chat.postMessage")
67+
await assert_received_request_count_async(self, "/api/chat.postMessage", 1)

0 commit comments

Comments
 (0)