onClone built the new panel from the source with its id intact, so the id was stored as a saved object attribute. savedObjectToCustomPanel then spread those attributes over the real saved object id, so every copy took the original's id as its row id. The table keys selection on that id, so selecting one row selected all copies, deleting a copy deleted the original, and the follow-up delete hit a missing document and surfaced as a permission error. The stale non-uuid id also routed copies down the legacy branch, so edits posted to the original.
onClone now dispatches the existing clonePanel thunk, which already strips the id and is what both panel view pages use. savedObjectToCustomPanel spreads attributes first so type, objectId and id are authoritative, which also repairs dashboards already corrupted in a cluster without a migration.
Fixes opensearch-project#2477
Signed-off-by: Chayanin Noramuttha <cnoramut@gmail.com>
Description
Duplicating an Observability Dashboard produced a copy that could not be deleted. Selecting either row selected all of them, deleting the copy deleted the original instead, and a second delete attempt failed with
Error deleting Observability Dashboards, please make sure you have the correct permission.Root cause.
onClonebuilt the new panel from the source with itsidintact, so the source id was persisted as a saved object attribute.savedObjectToCustomPanelthen spread those attributes over the real saved object id.const savedObjectToCustomPanel = (so: SimpleSavedObject<PanelType>): CustomPanelType => ({ - id: so.id, // a stale `id` in attributes wins over this - type: so.type, - objectId: so.type + ':' + so.id, - ...so.attributes, + ...so.attributes, + type: so.type, + objectId: so.type + ':' + so.id, + id: so.id, // spread first, so the real id always wins savedObject: true, });So every copy took the original's id as its row id. The table keys selection on that id, hence one click selecting all copies. Delete then targeted the original, and the follow-up delete hit a document that no longer existed, which the catch block reports as a permission error.
The stale id is not a uuid, so
isUuidrouted copies down the legacy branch. Editing a copy posted its updates to the original, and duplicating navigated to the original rather than the new copy.Fix.
onClonenow dispatches the existingclonePanelthunk, which already strips the id and is what both panel view pages use.savedObjectToCustomPanelspreads attributes first sotype,objectId, andidare authoritative.Notes for reviewers
id, the other that a staleidin attributes cannot shadow the real saved object id.clonePanelsetsdateCreatedanddateModified, which the previous table path did not, so a duplicate no longer inherits the original's Last updated value in a table sorted on that column.Before
Before.2477.mov
After
After.2477.mov
Issues Resolved
Fixes #2477
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.