diff --git a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts index 08162cedf02..aad2f9984d5 100644 --- a/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts +++ b/superset-frontend/src/explore/components/controls/DateFilterControl/utils/dateFilterUtils.ts @@ -72,8 +72,8 @@ export const fetchTimeRange = async ( try { const response = await SupersetClient.get({ endpoint }); const timeRangeString = buildTimeRangeString( - response?.json?.result?.since || '', - response?.json?.result?.until || '', + response?.json?.result[0]?.since || '', + response?.json?.result[0]?.until || '', ); return { value: formatTimeRange(timeRangeString, columnPlaceholder), diff --git a/superset/views/api.py b/superset/views/api.py index d7b1b8434e0..2e3c3b9bdd9 100644 --- a/superset/views/api.py +++ b/superset/views/api.py @@ -40,7 +40,15 @@ from superset.views.base import api, BaseSupersetView, handle_api_exception if TYPE_CHECKING: from superset.common.query_context_factory import QueryContextFactory -get_time_range_schema = {"type": "string"} +get_time_range_schema = { + "type": ["string", "array"], + "items": { + "type": "object", + "properties": { + "timeRange": {"type": "string"}, + }, + }, +} class Api(BaseSupersetView): @@ -95,15 +103,22 @@ class Api(BaseSupersetView): @expose("/v1/time_range/", methods=("GET",)) def time_range(self, **kwargs: Any) -> FlaskResponse: """Get actually time range from human-readable string or datetime expression.""" - time_range = kwargs["rison"] + time_ranges = kwargs["rison"] try: - since, until = get_since_until(time_range) - result = { - "since": since.isoformat() if since else "", - "until": until.isoformat() if until else "", - "timeRange": time_range, - } - return self.json_response({"result": result}) + if isinstance(time_ranges, str): + time_ranges = [{"timeRange": time_ranges}] + + rv = [] + for time_range in time_ranges: + since, until = get_since_until(time_range["timeRange"]) + rv.append( + { + "since": since.isoformat() if since else "", + "until": until.isoformat() if until else "", + "timeRange": time_range["timeRange"], + } + ) + return self.json_response({"result": rv}) except (ValueError, TimeRangeParseFailError, TimeRangeAmbiguousError) as error: error_msg = {"message": _("Unexpected time range: %(error)s", error=error)} return self.json_response(error_msg, 400) diff --git a/tests/integration_tests/charts/api_tests.py b/tests/integration_tests/charts/api_tests.py index d0985124e22..b5d935dd3d9 100644 --- a/tests/integration_tests/charts/api_tests.py +++ b/tests/integration_tests/charts/api_tests.py @@ -1421,7 +1421,22 @@ class TestChartApi(ApiOwnersTestCaseMixin, InsertChartMixin, SupersetTestCase): rv = self.client.get(uri) data = json.loads(rv.data.decode("utf-8")) self.assertEqual(rv.status_code, 200) - self.assertEqual(len(data["result"]), 3) + assert "since" in data["result"][0] + assert "until" in data["result"][0] + assert "timeRange" in data["result"][0] + + humanize_time_range = [ + {"timeRange": "2021-01-01 : 2022-02-01"}, + {"timeRange": "2022-01-01 : 2023-02-01"}, + ] + uri = f"api/v1/time_range/?q={prison.dumps(humanize_time_range)}" + rv = self.client.get(uri) + data = json.loads(rv.data.decode("utf-8")) + assert rv.status_code == 200 + assert len(data["result"]) == 2 + assert "since" in data["result"][0] + assert "until" in data["result"][0] + assert "timeRange" in data["result"][0] def test_query_form_data(self): """