diff --git a/superset/charts/data/api.py b/superset/charts/data/api.py index c20fdde6fd94..773229ad5f75 100644 --- a/superset/charts/data/api.py +++ b/superset/charts/data/api.py @@ -89,6 +89,11 @@ def get_data(self, pk: int) -> Response: description: The type in which the data should be returned schema: type: string + - in: query + name: force + description: Should the queries be forced to load from the source + schema: + type: boolean responses: 200: description: Query result @@ -130,6 +135,7 @@ def get_data(self, pk: int) -> Response: "format", ChartDataResultFormat.JSON ) json_body["result_type"] = request.args.get("type", ChartDataResultType.FULL) + json_body["force"] = request.args.get("force") try: query_context = self._create_query_context_from_form(json_body) diff --git a/superset/charts/schemas.py b/superset/charts/schemas.py index 34dd44b38c7a..701d83ebe586 100644 --- a/superset/charts/schemas.py +++ b/superset/charts/schemas.py @@ -1204,6 +1204,7 @@ class ChartDataQueryContextSchema(Schema): force = fields.Boolean( description="Should the queries be forced to load from the source. " "Default: `false`", + allow_none=True, ) result_type = EnumField(ChartDataResultType, by_value=True) diff --git a/tests/integration_tests/charts/data/api_tests.py b/tests/integration_tests/charts/data/api_tests.py index acf44be6f561..6904bc7df45c 100644 --- a/tests/integration_tests/charts/data/api_tests.py +++ b/tests/integration_tests/charts/data/api_tests.py @@ -862,6 +862,56 @@ def test_chart_data_get(self): assert data["result"][0]["status"] == "success" assert data["result"][0]["rowcount"] == 2 + @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices") + def test_chart_data_get_forced(self): + """ + Chart data API: Test GET endpoint with force cache parameter + """ + chart = db.session.query(Slice).filter_by(slice_name="Genders").one() + chart.query_context = json.dumps( + { + "datasource": {"id": chart.table.id, "type": "table"}, + "force": False, + "queries": [ + { + "time_range": "1900-01-01T00:00:00 : 2000-01-01T00:00:00", + "granularity": "ds", + "filters": [], + "extras": { + "having": "", + "having_druid": [], + "where": "", + }, + "applied_time_extras": {}, + "columns": ["gender"], + "metrics": ["sum__num"], + "orderby": [["sum__num", False]], + "annotation_layers": [], + "row_limit": 50000, + "timeseries_limit": 0, + "order_desc": True, + "url_params": {}, + "custom_params": {}, + "custom_form_data": {}, + } + ], + "result_format": "json", + "result_type": "full", + } + ) + + self.get_assert_metric(f"api/v1/chart/{chart.id}/data/?force=true", "get_data") + + # should burst cache + rv = self.get_assert_metric( + f"api/v1/chart/{chart.id}/data/?force=true", "get_data" + ) + assert rv.json["result"][0]["is_cached"] is None + + # should get response from the cache + rv = self.get_assert_metric(f"api/v1/chart/{chart.id}/data/", "get_data") + assert rv.json["result"][0]["is_cached"] + @pytest.mark.usefixtures("load_birth_names_dashboard_with_slices") @with_feature_flags(GLOBAL_ASYNC_QUERIES=True) @mock.patch("superset.charts.data.api.QueryContextCacheLoader")