Skip to content

Commit 78707f8

Browse files
committed
Wrap urljoin for different behavior when it comes to base_url usage.
1 parent bd80d98 commit 78707f8

File tree

2 files changed

+37
-9
lines changed

2 files changed

+37
-9
lines changed

servicestack/clients.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,20 @@ def has_request_body(method: str):
110110
return not (method == "GET" or method == "DELETE" or method == "HEAD" or method == "OPTIONS")
111111

112112

113+
def combine_with(base_url: str, relative_url: str):
114+
if not base_url:
115+
return relative_url
116+
if not relative_url:
117+
return base_url
118+
# We want to handle relative urls different by maintaining the base_url path at all times
119+
temp_relative_url = relative_url
120+
temp_base_url = base_url
121+
if not temp_base_url.endswith("/"):
122+
temp_base_url += "/"
123+
if temp_relative_url.startswith("/"):
124+
temp_relative_url = temp_relative_url[1:]
125+
return urljoin(temp_base_url, temp_relative_url)
126+
113127
@dataclass_json
114128
@dataclass
115129
class SendContext:
@@ -213,11 +227,11 @@ def __init__(self, base_url:str):
213227

214228
def set_base_path(self, base_path:str=''):
215229
if not base_path:
216-
self.reply_base_url = urljoin(self.base_url, 'json/reply') + "/"
217-
self.oneway_base_url = urljoin(self.base_url, 'json/oneway') + "/"
230+
self.reply_base_url = combine_with(self.base_url, 'json/reply') + "/"
231+
self.oneway_base_url = combine_with(self.base_url, 'json/oneway') + "/"
218232
else:
219-
self.reply_base_url = urljoin(self.base_url, base_path) + "/"
220-
self.oneway_base_url = urljoin(self.base_url, base_path) + "/"
233+
self.reply_base_url = combine_with(self.base_url, base_path) + "/"
234+
self.oneway_base_url = combine_with(self.base_url, base_path) + "/"
221235
return self
222236

223237
def set_credentials(self, username:str, password:str):
@@ -247,7 +261,7 @@ def refresh_token_cookie(self):
247261
return self._get_cookie_value(SS_REFRESH_TOKEN_COOKIE)
248262

249263
def create_url_from_dto(self, method: str, request: Any):
250-
url = urljoin(self.reply_base_url, nameof(request))
264+
url = combine_with(self.reply_base_url, nameof(request))
251265
if not has_request_body(method):
252266
url = append_querystring(url, to_dict(request, key_case=clean_camelcase))
253267
return url
@@ -276,7 +290,7 @@ def head(self, request: IReturn[T], args: Dict[str, Any] = None) -> T:
276290
def to_absolute_url(self, path_or_url: str):
277291
if path_or_url.startswith("http://") or path_or_url.startswith("https://"):
278292
return path_or_url
279-
return urljoin(self.base_url, path_or_url)
293+
return combine_with(self.base_url, path_or_url)
280294

281295
def get_url(self, path: str, response_as: Type, args: Dict[str, Any] = None):
282296
return self.send_url(path, "GET", response_as, None, args)
@@ -356,7 +370,7 @@ def assert_valid_batch_request(request_dtos: list):
356370

357371
def send_all(self, request_dtos: List[IReturn[T]]):
358372
request, item_response_as = self.assert_valid_batch_request(request_dtos)
359-
url = urljoin(self.reply_base_url, nameof(request) + "[]")
373+
url = combine_with(self.reply_base_url, nameof(request) + "[]")
360374

361375
return self.send_request(SendContext(
362376
session=self._session,
@@ -371,7 +385,7 @@ def send_all(self, request_dtos: List[IReturn[T]]):
371385

372386
def send_all_oneway(self, request_dtos: list):
373387
request, item_response_as = self.assert_valid_batch_request(request_dtos)
374-
url = urljoin(self.oneway_base_url, nameof(request) + "[]")
388+
url = combine_with(self.oneway_base_url, nameof(request) + "[]")
375389

376390
self.send_request(SendContext(
377391
session=self._session,
@@ -482,7 +496,7 @@ def create_request(self, info: SendContext):
482496
if not url:
483497
body_not_request_dto = info.request and info.body
484498
if body_not_request_dto:
485-
url = urljoin(self.reply_base_url, nameof(info.request))
499+
url = combine_with(self.reply_base_url, nameof(info.request))
486500
url = append_querystring(url, to_dict(info.request, key_case=clean_camelcase))
487501
else:
488502
url = self.create_url_from_dto(info.method, body)
@@ -564,3 +578,4 @@ def send_request(self, info: SendContext):
564578
return res_dto
565579

566580
return self._handle_error(response, e)
581+

tests/test_client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,19 @@ def test_can_put_HelloAllTypes(self):
221221
response: HelloAllTypesResponse = client.put(request)
222222
self.assert_HelloAllTypesResponse(response)
223223

224+
def test_can_use_base_url_with_path(self):
225+
custom_client = JsonServiceClient("https://openai.servicestack.net/v1/")
226+
self.assertTrue(custom_client.base_url.startswith("https://openai.servicestack.net"))
227+
self.assertIn("/v1",custom_client.reply_base_url)
228+
self.assertIn("/v1",custom_client.oneway_base_url)
229+
230+
custom_client = JsonServiceClient("https://openai.servicestack.net")
231+
self.assertTrue(custom_client.base_url.startswith("https://openai.servicestack.net"))
232+
self.assertTrue(custom_client.reply_base_url.endswith("/api/"))
233+
self.assertTrue(custom_client.oneway_base_url.endswith("/api/"))
234+
235+
236+
224237
def test_does_handle_404_error(self):
225238
request = ThrowType(type="NotFound", message="not here")
226239
try:

0 commit comments

Comments
 (0)