From 48dc387fab4b0ebf09da9944e2b39a9b4481b3ba Mon Sep 17 00:00:00 2001 From: Steven Liu Date: Sun, 10 Nov 2024 22:44:27 +1100 Subject: [PATCH] fix: keep the tab order --- superset/dashboards/api.py | 6 +- superset/models/dashboard.py | 6 +- .../integration_tests/dashboards/api_tests.py | 227 +++++++++++++++++- 3 files changed, 232 insertions(+), 7 deletions(-) diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py index a752091cc18e6..2138d98d0315f 100644 --- a/superset/dashboards/api.py +++ b/superset/dashboards/api.py @@ -18,6 +18,7 @@ import functools import logging from datetime import datetime +from http import HTTPStatus from io import BytesIO from typing import Any, Callable, cast, Optional from zipfile import is_zipfile, ZipFile @@ -479,7 +480,10 @@ def get_tabs(self, id_or_slug: str) -> Response: try: tabs = DashboardDAO.get_tabs_for_dashboard(id_or_slug) result = self.tab_schema.dump(tabs) - return self.response(200, result=result) + response_data = json.dumps({"result": result}) + return Response( + response_data, status=HTTPStatus.OK, mimetype="application/json" + ) except (TypeError, ValueError) as err: return self.response_400( diff --git a/superset/models/dashboard.py b/superset/models/dashboard.py index 28d8aacc7bed9..926f418f81927 100644 --- a/superset/models/dashboard.py +++ b/superset/models/dashboard.py @@ -338,12 +338,16 @@ def build_tab_tree( root = get_node("ROOT_ID") tab_tree: list[dict[str, Any]] = [] all_tabs: dict[str, str] = {} + tabs_order: list[str] = [] queue: deque[tuple[dict[str, Any], list[dict[str, Any]]]] = deque() queue.append((root, tab_tree)) while queue: node, children = queue.popleft() build_tab_tree(node, children) - + if (childs := node.get("children")) and node["type"] == "TABS": + tabs_order.extend(childs) + if tabs_order: + all_tabs = {str(t): all_tabs[str(t)] for t in tabs_order} return {"all_tabs": all_tabs, "tab_tree": tab_tree} def update_thumbnail(self) -> None: diff --git a/tests/integration_tests/dashboards/api_tests.py b/tests/integration_tests/dashboards/api_tests.py index e02b5a116a39a..1428105d6bcf4 100644 --- a/tests/integration_tests/dashboards/api_tests.py +++ b/tests/integration_tests/dashboards/api_tests.py @@ -1146,15 +1146,15 @@ def test_get_dashboard_tabs(self): expected_response = { "result": { "all_tabs": { - "TAB-0TkqQRxzg7": "P2 - T1", + "TAB-qL7fSzr3jl": "Parent Tab 1", + "TAB-CjZlNL5Uz": "Parent Tab 2", "TAB-1iG_yOlKA2": "P1 - T1", "TAB-2dgADEurF": "P1 - T2", - "TAB-BJIt5SdCx3": "P1 - T2 - T1", - "TAB-CjZlNL5Uz": "Parent Tab 2", - "TAB-Nct5fiHtn": "P1 - T2 - T3", + "TAB-0TkqQRxzg7": "P2 - T1", "TAB-PumuDkWKq": "P2 - T2", + "TAB-BJIt5SdCx3": "P1 - T2 - T1", "TAB-hyTv5L7zz": "P1 - T2 - T2", - "TAB-qL7fSzr3jl": "Parent Tab 1", + "TAB-Nct5fiHtn": "P1 - T2 - T3", }, "tab_tree": [ { @@ -1210,6 +1210,223 @@ def test_get_dashboard_tabs(self): } assert rv.status_code == 200 assert response == expected_response + assert list(response["result"]["all_tabs"].keys()) == list( + expected_response["result"]["all_tabs"].keys() + ) + db.session.delete(dashboard) + db.session.commit() + + def test_get_dashboard_tabs_with_order_preserved(self): + """ + Dashboard API: Test get dashboard tabs with order preserved + """ + position_data = { + "CHART-FQquhD1W9Vw_Ixyp8Hk42": { + "children": [], + "id": "CHART-FQquhD1W9Vw_Ixyp8Hk42", + "meta": { + "chartId": 45, + "height": 50, + "sliceName": "Weekly Messages", + "uuid": "abe2c022-ceee-a60a-e601-ab93f7ee52b1", + "width": 4, + }, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "TAB-EbAyCcXvBhVSJD81V0ulw", + "ROW-u86BqqtdahHfn81osTU-Q", + ], + "type": "CHART", + }, + "CHART-MRUFS_CzRF8ARWdI2PiDC": { + "children": [], + "id": "CHART-MRUFS_CzRF8ARWdI2PiDC", + "meta": { + "chartId": 135, + "height": 50, + "sliceName": "asd", + "uuid": "2804064d-ac6c-41fe-8c98-df30c5e4e89d", + "width": 4, + }, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "TAB-ZhI42SppeSxyG388lWnqJ", + "ROW-ncflkEzOgjQjE9WAQ7F10", + ], + "type": "CHART", + }, + "CHART-_xlwygYgI84B2xZeLhDkU": { + "children": [], + "id": "CHART-_xlwygYgI84B2xZeLhDkU", + "meta": { + "chartId": 63, + "height": 50, + "sliceName": "Members per Channel", + "uuid": "d44e416d-1647-44e4-b442-6da34b44adc4", + "width": 4, + }, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "TAB-6164r4k_xPgOOM-SzGCF8", + "ROW-T8t-uUfq-ogn0fpcgTnt6", + ], + "type": "CHART", + }, + "CHART-mLUhTmUZXjJ4daksMj1TU": { + "children": [], + "id": "CHART-mLUhTmUZXjJ4daksMj1TU", + "meta": { + "chartId": 5853, + "height": 50, + "sliceName": "Breakdown of Developer Type", + "uuid": "e99f2e60-dec5-4192-b5cc-9b4670705c8f", + "width": 4, + }, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TAB-cToDj4UPcHg4W-GoAZa0U", + "ROW-2V4pbfhg8eBqVz0797kcR", + ], + "type": "CHART", + }, + "DASHBOARD_VERSION_KEY": "v2", + "GRID_ID": { + "children": ["TABS-Bn5ZJIOkCxFOFQSjluT8Q"], + "id": "GRID_ID", + "parents": ["ROOT_ID"], + "type": "GRID", + }, + "HEADER_ID": { + "id": "HEADER_ID", + "meta": {"text": "test title"}, + "type": "HEADER", + }, + "ROOT_ID": {"children": ["GRID_ID"], "id": "ROOT_ID", "type": "ROOT"}, + "ROW-2V4pbfhg8eBqVz0797kcR": { + "children": ["CHART-mLUhTmUZXjJ4daksMj1TU"], + "id": "ROW-2V4pbfhg8eBqVz0797kcR", + "meta": {"background": "BACKGROUND_TRANSPARENT"}, + "parents": ["ROOT_ID", "GRID_ID", "TAB-cToDj4UPcHg4W-GoAZa0U"], + "type": "ROW", + }, + "ROW-T8t-uUfq-ogn0fpcgTnt6": { + "children": ["CHART-_xlwygYgI84B2xZeLhDkU"], + "id": "ROW-T8t-uUfq-ogn0fpcgTnt6", + "meta": {"background": "BACKGROUND_TRANSPARENT"}, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "TAB-6164r4k_xPgOOM-SzGCF8", + ], + "type": "ROW", + }, + "ROW-ncflkEzOgjQjE9WAQ7F10": { + "children": ["CHART-MRUFS_CzRF8ARWdI2PiDC"], + "id": "ROW-ncflkEzOgjQjE9WAQ7F10", + "meta": {"background": "BACKGROUND_TRANSPARENT"}, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "TAB-ZhI42SppeSxyG388lWnqJ", + ], + "type": "ROW", + }, + "ROW-u86BqqtdahHfn81osTU-Q": { + "children": ["CHART-FQquhD1W9Vw_Ixyp8Hk42"], + "id": "ROW-u86BqqtdahHfn81osTU-Q", + "meta": {"background": "BACKGROUND_TRANSPARENT"}, + "parents": [ + "ROOT_ID", + "GRID_ID", + "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "TAB-EbAyCcXvBhVSJD81V0ulw", + ], + "type": "ROW", + }, + "TAB-6164r4k_xPgOOM-SzGCF8": { + "children": ["ROW-T8t-uUfq-ogn0fpcgTnt6"], + "id": "TAB-6164r4k_xPgOOM-SzGCF8", + "meta": { + "defaultText": "Tab title", + "placeholder": "Tab title", + "text": "3", + }, + "parents": ["ROOT_ID", "GRID_ID", "TABS-Bn5ZJIOkCxFOFQSjluT8Q"], + "type": "TAB", + }, + "TAB-EbAyCcXvBhVSJD81V0ulw": { + "children": ["ROW-u86BqqtdahHfn81osTU-Q"], + "id": "TAB-EbAyCcXvBhVSJD81V0ulw", + "meta": { + "defaultText": "Tab title", + "placeholder": "Tab title", + "text": "2", + }, + "parents": ["ROOT_ID", "GRID_ID", "TABS-Bn5ZJIOkCxFOFQSjluT8Q"], + "type": "TAB", + }, + "TAB-ZhI42SppeSxyG388lWnqJ": { + "children": ["ROW-ncflkEzOgjQjE9WAQ7F10"], + "id": "TAB-ZhI42SppeSxyG388lWnqJ", + "meta": { + "defaultText": "Tab title", + "placeholder": "Tab title", + "text": "4", + }, + "parents": ["ROOT_ID", "GRID_ID", "TABS-Bn5ZJIOkCxFOFQSjluT8Q"], + "type": "TAB", + }, + "TAB-cToDj4UPcHg4W-GoAZa0U": { + "children": ["ROW-2V4pbfhg8eBqVz0797kcR"], + "id": "TAB-cToDj4UPcHg4W-GoAZa0U", + "meta": { + "defaultText": "Tab title", + "placeholder": "Tab title", + "text": "1", + }, + "parents": ["ROOT_ID", "GRID_ID"], + "type": "TAB", + }, + "TABS-Bn5ZJIOkCxFOFQSjluT8Q": { + "children": [ + "TAB-cToDj4UPcHg4W-GoAZa0U", + "TAB-EbAyCcXvBhVSJD81V0ulw", + "TAB-6164r4k_xPgOOM-SzGCF8", + "TAB-ZhI42SppeSxyG388lWnqJ", + ], + "id": "TABS-Bn5ZJIOkCxFOFQSjluT8Q", + "meta": {}, + "parents": ["ROOT_ID", "GRID_ID"], + "type": "TABS", + }, + } + admin_id = self.get_user("admin").id + dashboard = self.insert_dashboard( + "title", "slug", [admin_id], position_json=json.dumps(position_data) + ) + self.login(ADMIN_USERNAME) + uri = f"api/v1/dashboard/{dashboard.id}/tabs" + rv = self.get_assert_metric(uri, "get_tabs") + response = json.loads(rv.data.decode("utf-8")) + expected_response = { + "TAB-cToDj4UPcHg4W-GoAZa0U": "1", + "TAB-EbAyCcXvBhVSJD81V0ulw": "2", + "TAB-6164r4k_xPgOOM-SzGCF8": "3", + "TAB-ZhI42SppeSxyG388lWnqJ": "4", + } + assert rv.status_code == 200 + assert list(response["result"]["all_tabs"].keys()) == list( + expected_response.keys() + ) db.session.delete(dashboard) db.session.commit()