Skip to content

Commit b40edde

Browse files
author
Mike Kistler
committed
feat: add get_query_param utility method to support pagination
1 parent 060dc7e commit b40edde

File tree

3 files changed

+69
-1
lines changed

3 files changed

+69
-1
lines changed

ibm_cloud_sdk_core/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
string_to_date: De-serializes a string to a date.
3030
convert_model: Convert a model object into an equivalent dict.
3131
convert_list: Convert a list of strings into comma-separated string.
32+
get_query_param: Return a query parameter value from a URL
3233
read_external_sources: Get config object from external sources.
3334
get_authenticator_from_environment: Get authenticator from external sources.
3435
"""
@@ -42,4 +43,5 @@
4243
from .utils import datetime_to_string, string_to_datetime, read_external_sources
4344
from .utils import date_to_string, string_to_date
4445
from .utils import convert_model, convert_list
46+
from .utils import get_query_param
4547
from .get_authenticator import get_authenticator_from_environment

ibm_cloud_sdk_core/utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from os import getenv, environ, getcwd
2020
from os.path import isfile, join, expanduser
2121
from typing import List, Union
22+
from urllib.parse import urlparse, parse_qs
2223

2324
import dateutil.parser as date_parser
2425

@@ -141,6 +142,29 @@ def string_to_date(string: str) -> datetime.date:
141142
"""
142143
return date_parser.parse(string).date()
143144

145+
def get_query_param(url_str: str, param: str) -> str:
146+
"""Return a query parameter value from url_str
147+
148+
Args:
149+
url_str: the URL from which to extract the query
150+
parameter value
151+
param: the name of the query parameter whose value
152+
should be returned
153+
154+
Returns:
155+
the value of the `param` query parameter as a string
156+
157+
Raises:
158+
ValueError: if errors are encountered parsing `url_str`
159+
"""
160+
if not url_str:
161+
return None
162+
url = urlparse(url_str)
163+
if not url.query:
164+
return None
165+
query = parse_qs(url.query, strict_parsing=True)
166+
values = query.get(param)
167+
return values[0] if values else None
144168

145169
def convert_model(val: any) -> dict:
146170
"""Convert a model object into an equivalent dict.

test/test_utils.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@
1717

1818
import os
1919
import datetime
20-
2120
from typing import Optional
21+
import pytest
22+
2223
from ibm_cloud_sdk_core import string_to_datetime, datetime_to_string, get_authenticator_from_environment
2324
from ibm_cloud_sdk_core import string_to_date, date_to_string
2425
from ibm_cloud_sdk_core import convert_model, convert_list
26+
from ibm_cloud_sdk_core import get_query_param
2527
from ibm_cloud_sdk_core import read_external_sources
2628
from ibm_cloud_sdk_core.authenticators import BasicAuthenticator, IAMAuthenticator
2729

@@ -122,6 +124,46 @@ def test_date_conversion():
122124
assert res == '2017-03-06'
123125
assert date_to_string(None) is None
124126

127+
def test_get_query_param():
128+
# Relative URL
129+
next_url = '/api/v1/offerings?start=foo&limit=10'
130+
page_token = get_query_param(next_url, 'start')
131+
assert page_token == 'foo'
132+
# Absolute URL
133+
next_url = 'https://acme.com/api/v1/offerings?start=bar&limit=10'
134+
page_token = get_query_param(next_url, 'start')
135+
assert page_token == 'bar'
136+
# Missing param
137+
next_url = 'https://acme.com/api/v1/offerings?start=bar&limit=10'
138+
page_token = get_query_param(next_url, 'token')
139+
assert page_token is None
140+
# No URL
141+
page_token = get_query_param(None, 'start')
142+
assert page_token is None
143+
# Empty URL
144+
page_token = get_query_param('', 'start')
145+
assert page_token is None
146+
# No query string
147+
next_url = '/api/v1/offerings'
148+
page_token = get_query_param(next_url, 'start')
149+
assert page_token is None
150+
# Bad query string
151+
next_url = '/api/v1/offerings?start%XXfoo'
152+
with pytest.raises(ValueError):
153+
page_token = get_query_param(next_url, 'start')
154+
# Duplicate param
155+
next_url = '/api/v1/offerings?start=foo&start=bar&limit=10'
156+
page_token = get_query_param(next_url, 'start')
157+
assert page_token == 'foo'
158+
# Bad URL - since the behavior for this case varies based on the version of Python
159+
# we allow _either_ a ValueError or that the illegal chars are just ignored
160+
next_url = 'https://foo.bar\u2100/api/v1/offerings?start=foo'
161+
try:
162+
page_token = get_query_param(next_url, 'start')
163+
assert page_token == 'foo'
164+
except ValueError:
165+
# This is okay.
166+
pass
125167

126168
def test_convert_model():
127169
class MockModel:

0 commit comments

Comments
 (0)