feat: prevent co-edit dashboard collision (#11220)
* feat: prevent co-edit dashboard collision * fix comments
This commit is contained in:
parent
6cf698cc6a
commit
4f4367edf3
|
|
@ -290,6 +290,7 @@ class Header extends React.PureComponent {
|
|||
label_colors: labelColors,
|
||||
dashboard_title: dashboardTitle,
|
||||
refresh_frequency: refreshFrequency,
|
||||
last_modified_time: dashboardInfo.lastModifiedTime,
|
||||
};
|
||||
|
||||
// make sure positions data less than DB storage limitation:
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ class SaveModal extends React.PureComponent {
|
|||
saveType === SAVE_TYPE_NEWDASHBOARD ? newDashName : dashboardTitle,
|
||||
duplicate_slices: this.state.duplicateSlices,
|
||||
refresh_frequency: refreshFrequency,
|
||||
last_modified_time: dashboardInfo.lastModifiedTime,
|
||||
};
|
||||
|
||||
if (saveType === SAVE_TYPE_NEWDASHBOARD && !newDashName) {
|
||||
|
|
|
|||
|
|
@ -266,6 +266,7 @@ export default function getInitialState(bootstrapData) {
|
|||
id: dashboard.id,
|
||||
slug: dashboard.slug,
|
||||
metadata: dashboard.metadata,
|
||||
lastModifiedTime: dashboard.last_modified_time,
|
||||
userId: user_id,
|
||||
dash_edit_perm: dashboard.dash_edit_perm,
|
||||
dash_save_perm: dashboard.dash_save_perm,
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ class Dashboard( # pylint: disable=too-many-instance-attributes
|
|||
"slug": self.slug,
|
||||
"slices": [slc.data for slc in self.slices],
|
||||
"position_json": positions,
|
||||
"last_modified_time": self.changed_on.replace(microsecond=0).timestamp(),
|
||||
}
|
||||
|
||||
@property # type: ignore
|
||||
|
|
|
|||
|
|
@ -1019,6 +1019,11 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
|
|||
"""Copy dashboard"""
|
||||
session = db.session()
|
||||
data = json.loads(request.form["data"])
|
||||
# client-side send back last_modified_time which was set when
|
||||
# the dashboard was open. it was use to avoid mid-air collision.
|
||||
# remove it to avoid confusion.
|
||||
data.pop("last_modified_time", None)
|
||||
|
||||
dash = models.Dashboard()
|
||||
original_dash = session.query(Dashboard).get(dashboard_id)
|
||||
|
||||
|
|
@ -1065,6 +1070,21 @@ class Superset(BaseSupersetView): # pylint: disable=too-many-public-methods
|
|||
dash = session.query(Dashboard).get(dashboard_id)
|
||||
check_ownership(dash, raise_if_false=True)
|
||||
data = json.loads(request.form["data"])
|
||||
# client-side send back last_modified_time which was set when
|
||||
# the dashboard was open. it was use to avoid mid-air collision.
|
||||
remote_last_modified_time = data.get("last_modified_time")
|
||||
current_last_modified_time = dash.changed_on.replace(microsecond=0).timestamp()
|
||||
if remote_last_modified_time < current_last_modified_time:
|
||||
return json_error_response(
|
||||
__(
|
||||
"This dashboard was changed recently. "
|
||||
"Please reload dashboard to get latest version."
|
||||
),
|
||||
412,
|
||||
)
|
||||
# remove to avoid confusion.
|
||||
data.pop("last_modified_time", None)
|
||||
|
||||
DashboardDAO.set_dash_metadata(dash, data)
|
||||
session.merge(dash)
|
||||
session.commit()
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
# under the License.
|
||||
# isort:skip_file
|
||||
"""Unit tests for Superset"""
|
||||
from datetime import datetime
|
||||
import json
|
||||
import unittest
|
||||
from random import random
|
||||
|
|
@ -89,6 +90,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"expanded_slices": {},
|
||||
"positions": positions,
|
||||
"dashboard_title": dash.dashboard_title,
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
url = "/superset/save_dash/{}/".format(dash.id)
|
||||
resp = self.get_resp(url, data=dict(data=json.dumps(data)))
|
||||
|
|
@ -107,6 +110,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"positions": positions,
|
||||
"dashboard_title": dash.dashboard_title,
|
||||
"default_filters": default_filters,
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
|
||||
url = "/superset/save_dash/{}/".format(dash.id)
|
||||
|
|
@ -134,6 +139,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"positions": positions,
|
||||
"dashboard_title": dash.dashboard_title,
|
||||
"default_filters": default_filters,
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
|
||||
url = "/superset/save_dash/{}/".format(dash.id)
|
||||
|
|
@ -154,6 +161,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"expanded_slices": {},
|
||||
"positions": positions,
|
||||
"dashboard_title": "new title",
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
url = "/superset/save_dash/{}/".format(dash.id)
|
||||
self.get_resp(url, data=dict(data=json.dumps(data)))
|
||||
|
|
@ -176,6 +185,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"color_namespace": "Color Namespace Test",
|
||||
"color_scheme": "Color Scheme Test",
|
||||
"label_colors": new_label_colors,
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
url = "/superset/save_dash/{}/".format(dash.id)
|
||||
self.get_resp(url, data=dict(data=json.dumps(data)))
|
||||
|
|
@ -203,6 +214,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"color_namespace": "Color Namespace Test",
|
||||
"color_scheme": "Color Scheme Test",
|
||||
"label_colors": new_label_colors,
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
|
||||
# Save changes to Births dashboard and retrieve updated dash
|
||||
|
|
@ -271,6 +284,8 @@ class TestDashboard(SupersetTestCase):
|
|||
"expanded_slices": {},
|
||||
"positions": positions,
|
||||
"dashboard_title": dash.dashboard_title,
|
||||
# set a further modified_time for unit test
|
||||
"last_modified_time": datetime.now().timestamp() + 1000,
|
||||
}
|
||||
|
||||
# save dash
|
||||
|
|
|
|||
Loading…
Reference in New Issue