Skip to content

Commit 5496948

Browse files
committed
fix: Combine multiple ending slashes to one
BREAKING CHANGE: Fixing the request URL like this will break compatibility with previous generator versions
1 parent 4c02d4b commit 5496948

File tree

3 files changed

+38
-16
lines changed

3 files changed

+38
-16
lines changed

ibm_cloud_sdk_core/base_service.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,13 @@
2626
from requests.structures import CaseInsensitiveDict
2727
from ibm_cloud_sdk_core.authenticators import Authenticator
2828
from .version import __version__
29-
from .utils import has_bad_first_or_last_char, remove_null_values, cleanup_values, read_external_sources
29+
from .utils import (
30+
has_bad_first_or_last_char,
31+
remove_null_values,
32+
cleanup_values,
33+
read_external_sources,
34+
strip_extra_slashes
35+
)
3036
from .detailed_response import DetailedResponse
3137
from .api_exception import ApiException
3238
from .token_manager import TokenManager
@@ -234,7 +240,6 @@ def send(self, request: requests.Request, **kwargs) -> DetailedResponse:
234240
logging.exception('Error in service call')
235241
raise
236242

237-
238243
def prepare_request(self,
239244
method: str,
240245
url: str,
@@ -272,7 +277,7 @@ def prepare_request(self,
272277
# validate the service url is set
273278
if not self.service_url:
274279
raise ValueError('The service_url is required')
275-
request['url'] = self.service_url + url
280+
request['url'] = strip_extra_slashes(self.service_url + url)
276281

277282
headers = remove_null_values(headers) if headers else {}
278283
headers = cleanup_values(headers)

ibm_cloud_sdk_core/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ def cleanup_value(value: any) -> any:
6868
return 'true' if value else 'false'
6969
return value
7070

71+
def strip_extra_slashes(value: str) -> str:
72+
"""Combine multiple trailing slashes to a single slash"""
73+
if value.endswith('//'):
74+
return value.rstrip('/') + '/'
75+
return value
76+
7177
def datetime_to_string(val: datetime.datetime) -> str:
7278
"""Convert a datetime object to string.
7379

test/test_base_service.py

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from ibm_cloud_sdk_core.authenticators import (IAMAuthenticator, NoAuthAuthenticator, Authenticator,
1515
BasicAuthenticator, CloudPakForDataAuthenticator)
1616
from ibm_cloud_sdk_core import get_authenticator_from_environment
17+
from ibm_cloud_sdk_core.utils import strip_extra_slashes
1718

1819

1920
class IncludeExternalConfigService(BaseService):
@@ -518,27 +519,37 @@ def test_json():
518519
req = service.prepare_request('POST', url='', headers={'X-opt-out': True}, data={'hello': 'world'})
519520
assert req.get('data') == "{\"hello\": \"world\"}"
520521

521-
# For v2.x this test expects to see trailing slashes, this should be changed in v3.x
522522
def test_trailing_slash():
523-
service = AnyServiceV1('2018-11-20', service_url='https://trailingSlash.com/', authenticator=NoAuthAuthenticator())
524-
assert service.service_url == 'https://trailingSlash.com/'
525-
service.set_service_url('https://trailingSlash.com/')
526-
assert service.service_url == 'https://trailingSlash.com/'
523+
assert strip_extra_slashes('') == ''
524+
assert strip_extra_slashes('//') == '/'
525+
assert strip_extra_slashes('/////') == '/'
526+
assert strip_extra_slashes('https://host') == 'https://host'
527+
assert strip_extra_slashes('https://host/') == 'https://host/'
528+
assert strip_extra_slashes('https://host//') == 'https://host/'
529+
assert strip_extra_slashes('https://host/path') == 'https://host/path'
530+
assert strip_extra_slashes('https://host/path/') == 'https://host/path/'
531+
assert strip_extra_slashes('https://host/path//') == 'https://host/path/'
532+
assert strip_extra_slashes('https://host//path//') == 'https://host//path/'
533+
534+
service = AnyServiceV1('2018-11-20', service_url='https://host/', authenticator=NoAuthAuthenticator())
535+
assert service.service_url == 'https://host/'
536+
service.set_service_url('https://host/')
537+
assert service.service_url == 'https://host/'
527538
req = service.prepare_request('POST',
528-
url='/trailingSlashPath/',
539+
url='/path/',
529540
headers={'X-opt-out': True},
530541
data={'hello': 'world'})
531-
assert req.get('url') == 'https://trailingSlash.com//trailingSlashPath/'
542+
assert req.get('url') == 'https://host//path/'
532543

533-
service = AnyServiceV1('2018-11-20', service_url='https://trailingSlash.com/', authenticator=NoAuthAuthenticator())
534-
assert service.service_url == 'https://trailingSlash.com/'
535-
service.set_service_url('https://trailingSlash.com/')
536-
assert service.service_url == 'https://trailingSlash.com/'
544+
service = AnyServiceV1('2018-11-20', service_url='https://host/', authenticator=NoAuthAuthenticator())
545+
assert service.service_url == 'https://host/'
546+
service.set_service_url('https://host/')
547+
assert service.service_url == 'https://host/'
537548
req = service.prepare_request('POST',
538549
url='/',
539550
headers={'X-opt-out': True},
540551
data={'hello': 'world'})
541-
assert req.get('url') == 'https://trailingSlash.com//'
552+
assert req.get('url') == 'https://host/'
542553

543554
service.set_service_url(None)
544555
assert service.service_url is None
@@ -551,7 +562,7 @@ def test_trailing_slash():
551562
url='/',
552563
headers={'X-opt-out': True},
553564
data={'hello': 'world'})
554-
assert req.get('url') == '//'
565+
assert req.get('url') == '/'
555566

556567
def test_service_url_not_set():
557568
service = BaseService(service_url='', authenticator=NoAuthAuthenticator())

0 commit comments

Comments
 (0)