Skip to content

Commit

Permalink
[Workspace] Fix: Show a error toast when workspace read only user del…
Browse files Browse the repository at this point in the history
…ete saved objects (#6756)

* fix: show a error toast when workspace read only user delete saved objects

Signed-off-by: yubonluo <yubonluo@amazon.com>

* Changeset file for PR #6756 created/updated

* Changeset file for PR #6756 created/updated

* optimize the code

Signed-off-by: yubonluo <yubonluo@amazon.com>

* Display the delete modal after failing to delete.

Signed-off-by: yubonluo <yubonluo@amazon.com>

* Add some unit tests

Signed-off-by: yubonluo <yubonluo@amazon.com>

* Add some state assertions

Signed-off-by: yubonluo <yubonluo@amazon.com>

---------

Signed-off-by: yubonluo <yubonluo@amazon.com>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
yubonluo and opensearch-changeset-bot[bot] authored May 14, 2024
1 parent d585755 commit c448c52
Show file tree
Hide file tree
Showing 4 changed files with 222 additions and 22 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/6756.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fix:
- Show error toast when fail to delete saved objects ([#6756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6756))

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,9 @@ describe('SavedObjectsTable', () => {

// Set some as selected
component.instance().onSelectionChanged(mockSelectedSavedObjects);
await component.instance().onDelete();
component.update();
expect(component.state('isShowingDeleteConfirmModal')).toBe(true);

await component.instance().delete();

Expand All @@ -612,6 +615,53 @@ describe('SavedObjectsTable', () => {
{ force: true }
);
expect(component.state('selectedSavedObjects').length).toBe(0);
expect(notifications.toasts.addDanger).not.toHaveBeenCalled();
expect(component.state('isDeleting')).toBe(false);
expect(component.state('isShowingDeleteConfirmModal')).toBe(false);
});

it('should show error toast when failing to delete saved objects', async () => {
const mockSelectedSavedObjects = [
{ id: '1', type: 'index-pattern' },
] as SavedObjectWithMetadata[];

const mockSavedObjects = mockSelectedSavedObjects.map((obj) => ({
id: obj.id,
type: obj.type,
source: {},
}));

const mockSavedObjectsClient = {
...defaultProps.savedObjectsClient,
bulkGet: jest.fn().mockImplementation(() => ({
savedObjects: mockSavedObjects,
})),
delete: jest.fn().mockImplementation(() => {
throw new Error('Unable to delete saved objects');
}),
};

const component = shallowRender({ savedObjectsClient: mockSavedObjectsClient });

// Ensure all promises resolve
await new Promise((resolve) => process.nextTick(resolve));
// Ensure the state changes are reflected
component.update();

// Set some as selected
component.instance().onSelectionChanged(mockSelectedSavedObjects);
await component.instance().onDelete();
component.update();
expect(component.state('isShowingDeleteConfirmModal')).toBe(true);
expect(component.find('EuiConfirmModal')).toMatchSnapshot();

await component.instance().delete();
component.update();
expect(notifications.toasts.addDanger).toHaveBeenCalled();
// If user fail to delete the saved objects, the delete modal will continue to display
expect(component.state('isShowingDeleteConfirmModal')).toBe(true);
expect(component.find('EuiConfirmModal')).toMatchSnapshot();
expect(component.state('isDeleting')).toBe(false);
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,7 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
};

delete = async () => {
const { savedObjectsClient } = this.props;
const { savedObjectsClient, notifications } = this.props;
const { selectedSavedObjects, isDeleting } = this.state;

if (isDeleting) {
Expand All @@ -599,30 +599,38 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
this.setState({ isDeleting: true });

const indexPatterns = selectedSavedObjects.filter((object) => object.type === 'index-pattern');
if (indexPatterns.length) {
await this.props.indexPatterns.clearCache();
}

const objects = await savedObjectsClient.bulkGet(selectedSavedObjects);
const deletes = objects.savedObjects.map((object) =>
savedObjectsClient.delete(object.type, object.id, { force: true })
);
await Promise.all(deletes);

// Unset this
this.setState({
selectedSavedObjects: [],
});
try {
if (indexPatterns.length) {
await this.props.indexPatterns.clearCache();
}
const objects = await savedObjectsClient.bulkGet(selectedSavedObjects);
const deletes = objects.savedObjects.map((object) =>
savedObjectsClient.delete(object.type, object.id, { force: true })
);
await Promise.all(deletes);
// Unset this
this.setState({
selectedSavedObjects: [],
});
// Fetching all data
await this.fetchSavedObjects();
await this.fetchCounts();

// Fetching all data
await this.fetchSavedObjects();
await this.fetchCounts();
// Allow the user to interact with the table once the saved objects have been re-fetched.
// If the user fails to delete the saved objects, the delete modal will continue to display.
this.setState({ isShowingDeleteConfirmModal: false });
} catch (error) {
notifications.toasts.addDanger({
title: i18n.translate(
'savedObjectsManagement.objectsTable.unableDeleteSavedObjectsNotificationMessage',
{ defaultMessage: 'Unable to delete saved objects' }
),
text: `${error}`,
});
}

// Allow the user to interact with the table once the saved objects have been re-fetched.
this.setState({
isShowingDeleteConfirmModal: false,
isDeleting: false,
});
this.setState({ isDeleting: false });
};

getRelationships = async (type: string, id: string) => {
Expand Down

0 comments on commit c448c52

Please sign in to comment.