Skip to content

Commit

Permalink
SOM: hide actions for hidden types (#98290)
Browse files Browse the repository at this point in the history
* SOM: hide actions for hidden types

* fix FTR tests

* add and fix tests

* fix unit tests

* fix test types

* fix FTR test assertions

* add more FTR tests

* delete old file
  • Loading branch information
pgayvallet authored Apr 28, 2021
1 parent 6c635b3 commit 9dae1ef
Show file tree
Hide file tree
Showing 28 changed files with 895 additions and 82 deletions.
2 changes: 1 addition & 1 deletion src/plugins/saved_objects_management/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

export {
export type {
SavedObjectWithMetadata,
SavedObjectMetadata,
SavedObjectRelation,
Expand Down
1 change: 1 addition & 0 deletions src/plugins/saved_objects_management/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface SavedObjectMetadata {
editUrl?: string;
inAppUrl?: { path: string; uiCapabilitiesPath: string };
namespaceType?: SavedObjectsNamespaceType;
hiddenType?: boolean;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class SavedObjectEdition extends Component<
<EuiPageContent horizontalPosition="center" data-test-subj="savedObjectsEdit">
<Header
canEdit={canEdit}
canDelete={canDelete}
canDelete={canDelete && !object?.meta.hiddenType}
canViewInApp={canView}
type={type}
onDeleteClick={() => this.delete()}
Expand Down

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 @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

import React, { FC } from 'react';
import React, { FC, useMemo } from 'react';
import {
EuiInMemoryTable,
EuiLoadingElastic,
Expand All @@ -23,6 +23,7 @@ import {
EuiButtonEmpty,
EuiButton,
EuiSpacer,
EuiCallOut,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
Expand All @@ -42,14 +43,20 @@ export const DeleteConfirmModal: FC<DeleteConfirmModalProps> = ({
onCancel,
selectedObjects,
}) => {
const undeletableObjects = useMemo(() => {
return selectedObjects.filter((obj) => obj.meta.hiddenType);
}, [selectedObjects]);
const deletableObjects = useMemo(() => {
return selectedObjects.filter((obj) => !obj.meta.hiddenType);
}, [selectedObjects]);

if (isDeleting) {
return (
<EuiOverlayMask>
<EuiLoadingElastic size="xl" />
</EuiOverlayMask>
);
}

// can't use `EuiConfirmModal` here as the confirm modal body is wrapped
// inside a `<p>` element, causing UI glitches with the table.
return (
Expand All @@ -63,6 +70,29 @@ export const DeleteConfirmModal: FC<DeleteConfirmModalProps> = ({
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
{undeletableObjects.length > 0 && (
<>
<EuiCallOut
data-test-subj="cannotDeleteObjectsConfirmWarning"
title={
<FormattedMessage
id="savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.title"
defaultMessage="Some objects cannot deleted"
/>
}
iconType="alert"
color="warning"
>
<p>
<FormattedMessage
id="savedObjectsManagement.objectsTable.deleteConfirmModal.cannotDeleteCallout.content"
defaultMessage="Some of the selected objects cannot be deleted, and are not listed in the table summary"
/>
</p>
</EuiCallOut>
<EuiSpacer size="s" />
</>
)}
<p>
<FormattedMessage
id="savedObjectsManagement.deleteSavedObjectsConfirmModalDescription"
Expand All @@ -71,7 +101,7 @@ export const DeleteConfirmModal: FC<DeleteConfirmModalProps> = ({
</p>
<EuiSpacer size="m" />
<EuiInMemoryTable
items={selectedObjects}
items={deletableObjects}
columns={[
{
field: 'type',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,8 +479,8 @@ describe('SavedObjectsTable', () => {
const component = shallowRender();

const mockSelectedSavedObjects = [
{ id: '1', type: 'index-pattern' },
{ id: '3', type: 'dashboard' },
{ id: '1', type: 'index-pattern', meta: {} },
{ id: '3', type: 'dashboard', meta: {} },
] as SavedObjectWithMetadata[];

// Ensure all promises resolve
Expand All @@ -498,8 +498,8 @@ describe('SavedObjectsTable', () => {

it('should delete selected objects', async () => {
const mockSelectedSavedObjects = [
{ id: '1', type: 'index-pattern' },
{ id: '3', type: 'dashboard' },
{ id: '1', type: 'index-pattern', meta: {} },
{ id: '3', type: 'dashboard', meta: {} },
] as SavedObjectWithMetadata[];

const mockSavedObjects = mockSelectedSavedObjects.map((obj) => ({
Expand Down Expand Up @@ -529,7 +529,6 @@ describe('SavedObjectsTable', () => {
await component.instance().delete();

expect(defaultProps.indexPatterns.clearCache).toHaveBeenCalled();
expect(mockSavedObjectsClient.bulkGet).toHaveBeenCalledWith(mockSelectedSavedObjects);
expect(mockSavedObjectsClient.delete).toHaveBeenCalledWith(
mockSavedObjects[0].type,
mockSavedObjects[0].id,
Expand All @@ -542,5 +541,44 @@ describe('SavedObjectsTable', () => {
);
expect(component.state('selectedSavedObjects').length).toBe(0);
});

it('should not delete hidden selected objects', async () => {
const mockSelectedSavedObjects = [
{ id: '1', type: 'index-pattern', meta: {} },
{ id: '3', type: 'hidden-type', meta: { hiddenType: true } },
] 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(),
};

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().delete();

expect(defaultProps.indexPatterns.clearCache).toHaveBeenCalled();
expect(mockSavedObjectsClient.delete).toHaveBeenCalledTimes(1);
expect(mockSavedObjectsClient.delete).toHaveBeenCalledWith('index-pattern', '1', {
force: true,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -455,10 +455,9 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
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 })
);
const deletes = selectedSavedObjects
.filter((object) => !object.meta.hiddenType)
.map((object) => savedObjectsClient.delete(object.type, object.id, { force: true }));
await Promise.all(deletes);

// Unset this
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export interface SavedObjectsManagementRecord {
icon: string;
title: string;
namespaceType: SavedObjectsNamespaceType;
hiddenType: boolean;
};
references: SavedObjectReference[];
namespaces?: string[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ describe('findRelationships', () => {
title: 'title',
icon: 'icon',
editUrl: 'editUrl',
hiddenType: false,
inAppUrl: {
path: 'path',
uiCapabilitiesPath: 'uiCapabilitiesPath',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ describe('injectMetaAttributes', () => {
uiCapabilitiesPath: 'uiCapabilitiesPath',
},
namespaceType: 'single',
hiddenType: false,
},
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function injectMetaAttributes<T = unknown>(
result.meta.editUrl = savedObjectsManagement.getEditUrl(savedObject);
result.meta.inAppUrl = savedObjectsManagement.getInAppUrl(savedObject);
result.meta.namespaceType = savedObjectsManagement.getNamespaceType(savedObject);
result.meta.hiddenType = savedObjectsManagement.isHidden(savedObject);

return result;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const createManagementMock = () => {
getEditUrl: jest.fn(),
getInAppUrl: jest.fn(),
getNamespaceType: jest.fn(),
isHidden: jest.fn().mockReturnValue(false),
};
return mocked;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ export class SavedObjectsManagement {
public getNamespaceType(savedObject: SavedObject) {
return this.registry.getType(savedObject.type)?.namespaceType;
}

public isHidden(savedObject: SavedObject) {
return this.registry.getType(savedObject.type)?.hidden ?? false;
}
}
5 changes: 5 additions & 0 deletions test/api_integration/apis/saved_objects_management/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(resp.body.saved_objects[0].meta).to.eql({
icon: 'discoverApp',
title: 'OneRecord',
hiddenType: false,
editUrl:
'/management/kibana/objects/savedSearches/960372e0-3224-11e8-a572-ffca06da1357',
inAppUrl: {
Expand All @@ -259,6 +260,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(resp.body.saved_objects[0].meta).to.eql({
icon: 'dashboardApp',
title: 'Dashboard',
hiddenType: false,
editUrl:
'/management/kibana/objects/savedDashboards/b70c7ae0-3224-11e8-a572-ffca06da1357',
inAppUrl: {
Expand All @@ -278,6 +280,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(resp.body.saved_objects[0].meta).to.eql({
icon: 'visualizeApp',
title: 'VisualizationFromSavedSearch',
hiddenType: false,
editUrl:
'/management/kibana/objects/savedVisualizations/a42c0580-3224-11e8-a572-ffca06da1357',
inAppUrl: {
Expand All @@ -289,6 +292,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(resp.body.saved_objects[1].meta).to.eql({
icon: 'visualizeApp',
title: 'Visualization',
hiddenType: false,
editUrl:
'/management/kibana/objects/savedVisualizations/add810b0-3224-11e8-a572-ffca06da1357',
inAppUrl: {
Expand All @@ -308,6 +312,7 @@ export default function ({ getService }: FtrProviderContext) {
expect(resp.body.saved_objects[0].meta).to.eql({
icon: 'indexPatternApp',
title: 'saved_objects*',
hiddenType: false,
editUrl:
'/management/kibana/indexPatterns/patterns/8963ca30-3224-11e8-a572-ffca06da1357',
inAppUrl: {
Expand Down
Loading

0 comments on commit 9dae1ef

Please sign in to comment.