diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e2ed4117..3fba6b81 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,11 @@ Changelog Next ==== +Version 1.2.26 - 2024-08-08 +=========================== + +- Improve generic JSON to handle arrays of strings (#466) + Version 1.2.25 - 2024-08-07 =========================== diff --git a/src/shillelagh/adapters/api/generic_json.py b/src/shillelagh/adapters/api/generic_json.py index d79cba92..a60dede0 100644 --- a/src/shillelagh/adapters/api/generic_json.py +++ b/src/shillelagh/adapters/api/generic_json.py @@ -147,6 +147,8 @@ def get_data( # pylint: disable=unused-argument, too-many-arguments for i, row in enumerate(jsonpath.findall(self.path, payload)): if isinstance(row, list): row = {f"col_{i}": value for i, value in enumerate(row)} + elif isinstance(row, str): + row = {"col_0": row} elif row is None: row = {} diff --git a/tests/adapters/api/generic_json_test.py b/tests/adapters/api/generic_json_test.py index ab5bb6b1..2c433d30 100644 --- a/tests/adapters/api/generic_json_test.py +++ b/tests/adapters/api/generic_json_test.py @@ -327,3 +327,62 @@ def test_generic_json_array(requests_mock: Mocker) -> None: ] assert cursor.description assert {t[0] for t in cursor.description} == {"col_0", "col_1", "col_2"} + + +def test_generic_json_string_array(requests_mock: Mocker) -> None: + """ + Test a query where the JSONPath returns just a string. + """ + payload = { + "lastTimestamp": "20240808022653Z", + "project_count": 243, + "roster_counts": { + "superset": {"members": 65, "owners": 35}, + }, + "projects": { + "superset": { + "createTimestamp": "20170523163111Z", + "modifyTimestamp": "20240617171611Z", + "member_count": 65, + "owner_count": 35, + "members": [ + "aafghahi", + "amitmiran", + "arivero", + "benceorlai", + "beto", + ], + "owners": [ + "amitmiran", + "beto", + "bkyryliuk", + "ccwilliams", + "craigrueda", + ], + "pmc": True, + "podling": "graduated", + }, + }, + } + url = URL("https://example.com/#$.projects.superset.members[*]") + requests_mock.head(str(url), headers={"content-type": "application/json"}) + requests_mock.get(str(url), json=payload) + + connection = connect( + ":memory:", + adapter_kwargs={"genericjsonapi": {"cache_expiration": -1}}, + ) + cursor = connection.cursor() + + sql = f'SELECT * FROM "{url}"' + rows = list(cursor.execute(sql)) + assert rows == [ + ("aafghahi",), + ("amitmiran",), + ("arivero",), + ("benceorlai",), + ("beto",), + ] + + assert cursor.description + assert {t[0] for t in cursor.description} == {"col_0"}