diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap index 61ca7392955..da0c642b887 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap @@ -209,11 +209,13 @@ exports[`SavedObjectsTable should render normally 1`] = ` >
diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap index 1ebba756345..6b9d1038f44 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/header.test.tsx.snap @@ -10,7 +10,9 @@ exports[`Header should render normally 1`] = ` grow={false} > -

+

+ Saved Objects +

- - - - - diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap index d1a11186543..2fdad7b9887 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/table.test.tsx.snap @@ -40,11 +40,6 @@ exports[`Table prevents saved objects from being deleted 1`] = ` values={Object {}} /> , - , , - , { onExportAll: () => {}, onImport: () => {}, onRefresh: () => {}, + onCopy: () => {}, + title: 'Saved Objects', + selectedCount: 0, totalCount: 4, filteredCount: 2, + showDuplicateAll: false, + hideImport: false, }; const component = shallow(
); @@ -47,3 +52,43 @@ describe('Header', () => { expect(component).toMatchSnapshot(); }); }); + +describe('Header - workspace enabled', () => { + it('should render `Duplicate All` button when workspace enabled', () => { + const props = { + onExportAll: () => {}, + onImport: () => {}, + onRefresh: () => {}, + onCopy: () => {}, + title: 'Saved Objects', + selectedCount: 0, + totalCount: 4, + filteredCount: 2, + showDuplicateAll: true, + hideImport: false, + }; + + const component = shallow(
); + + expect(component.find('EuiButtonEmpty[data-test-subj="copyObjects"]').exists()).toBe(true); + }); + + it('should hide `Import` button for application home state', () => { + const props = { + onExportAll: () => {}, + onImport: () => {}, + onRefresh: () => {}, + onCopy: () => {}, + title: 'Saved Objects', + selectedCount: 0, + totalCount: 4, + filteredCount: 2, + showDuplicateAll: true, + hideImport: true, + }; + + const component = shallow(
); + + expect(component.find('EuiButtonEmpty[data-test-subj="importObjects"]').exists()).toBe(false); + }); +}); diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx index b6803c92d9a..fa3d36c73c1 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/header.tsx @@ -48,6 +48,8 @@ export const Header = ({ filteredCount, title, selectedCount, + hideImport = false, + showDuplicateAll = false, }: { onExportAll: () => void; onImport: () => void; @@ -56,6 +58,8 @@ export const Header = ({ filteredCount: number; title: string; selectedCount: number; + hideImport: boolean; + showDuplicateAll: boolean; }) => ( @@ -67,19 +71,21 @@ export const Header = ({ - - - - - + {showDuplicateAll && ( + + + + + + )} - - - - - + {!hideImport && ( + + + + + + )} { expect(component).toMatchSnapshot(); }); + it('should render gotoApp link correctly for workspace', () => { + const item = { + id: 'dashboard-1', + type: 'dashboard', + workspaces: ['ws-1'], + attributes: {}, + references: [], + meta: { + title: `My-Dashboard-test`, + icon: 'indexPatternApp', + editUrl: '/management/opensearch-dashboards/objects/savedDashboards/dashboard-1', + inAppUrl: { + path: '/app/dashboards#/view/dashboard-1', + uiCapabilitiesPath: 'dashboard.show', + }, + }, + }; + const props = { + ...defaultProps, + availableWorkspaces: [{ id: 'ws-1', name: 'My workspace' } as WorkspaceAttribute], + items: [item], + }; + const component = shallowWithI18nProvider(); + + const table = component.find('EuiBasicTable'); + const columns = table.prop('columns') as any[]; + const content = columns[1].render('My-Dashboard-test', item); + expect(content.props.href).toEqual('/w/ws-1/app/dashboards#/view/dashboard-1'); + }); + it('should handle query parse error', () => { const onQueryChangeMock = jest.fn(); const customizedProps = { diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx index d7a1d66ff28..ef4ebbfc4c7 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx @@ -28,7 +28,7 @@ * under the License. */ -import { IBasePath } from 'src/core/public'; +import { IBasePath, WorkspaceAttribute } from 'src/core/public'; import React, { PureComponent, Fragment } from 'react'; import moment from 'moment'; import { @@ -57,6 +57,7 @@ import { SavedObjectsManagementAction, SavedObjectsManagementColumnServiceStart, } from '../../../services'; +import { WORKSPACE_PATH_PREFIX } from '../../../../../../core/public/utils'; export interface TableProps { basePath: IBasePath; @@ -84,6 +85,8 @@ export interface TableProps { onShowRelationships: (object: SavedObjectWithMetadata) => void; canGoInApp: (obj: SavedObjectWithMetadata) => boolean; dateFormat: string; + availableWorkspaces?: WorkspaceAttribute[]; + showDuplicate: boolean; } interface TableState { @@ -178,8 +181,12 @@ export class Table extends PureComponent { actionRegistry, columnRegistry, dateFormat, + availableWorkspaces, + showDuplicate, } = this.props; + const visibleWsIds = availableWorkspaces?.map((ws) => ws.id) || []; + const pagination = { pageIndex, pageSize, @@ -227,13 +234,20 @@ export class Table extends PureComponent { sortable: false, 'data-test-subj': 'savedObjectsTableRowTitle', render: (title: string, object: SavedObjectWithMetadata) => { - const { path = '' } = object.meta.inAppUrl || {}; + let { path = '' } = object.meta.inAppUrl || {}; const canGoInApp = this.props.canGoInApp(object); if (!canGoInApp) { return {title || getDefaultTitle(object)}; } + if (object.workspaces) { + // first workspace login user have permission + const [workspaceId] = object.workspaces.filter((wsId) => visibleWsIds.includes(wsId)); + path = workspaceId ? `${WORKSPACE_PATH_PREFIX}/${workspaceId}${path}` : path; + } return ( - {title || getDefaultTitle(object)} + + {title || getDefaultTitle(object)} + ); }, } as EuiTableFieldDataColumnType>, @@ -352,6 +366,78 @@ export class Table extends PureComponent { const activeActionContents = this.state.activeAction?.render() ?? null; + const tools = [ + + + , + + + } + > + + } + checked={this.state.isIncludeReferencesDeepChecked} + onChange={this.toggleIsIncludeReferencesDeepChecked} + /> + + + + + + + , + ]; + + const duplicateButton = ( + + ); + + if (showDuplicate) { + tools.splice(1, 0, duplicateButton); + } + return ( {activeActionContents} @@ -359,70 +445,7 @@ export class Table extends PureComponent { box={{ 'data-test-subj': 'savedObjectSearchBar' }} filters={filters as any} onChange={this.onChange} - toolsRight={[ - - - , - , - - - } - > - - } - checked={this.state.isIncludeReferencesDeepChecked} - onChange={this.toggleIsIncludeReferencesDeepChecked} - /> - - - - - - - , - ]} + toolsRight={tools} /> {queryParseError} diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx index dcc54d5016b..ace6f1e4271 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx @@ -284,7 +284,7 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, { - workspaces: ['public'], + workspaces: undefined, }); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', @@ -329,7 +329,7 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, { - workspaces: ['public'], + workspaces: undefined, }); expect(notifications.toasts.addWarning).toHaveBeenCalledWith({ title: @@ -372,7 +372,7 @@ describe('SavedObjectsTable', () => { allowedTypes, undefined, true, - { workspaces: ['public'] } + { workspaces: undefined } ); expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ @@ -403,7 +403,7 @@ describe('SavedObjectsTable', () => { allowedTypes, 'test*', true, - { workspaces: ['public'] } + { workspaces: undefined } ); expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx index 33c3b9a64da..cb93ff3b499 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx @@ -146,14 +146,16 @@ export interface SavedObjectsTableState { exportAllOptions: ExportAllOption[]; exportAllSelectedOptions: Record; isIncludeReferencesDeepChecked: boolean; - workspaceId: string | null; - availableWorkspace?: WorkspaceAttribute[]; + currentWorkspaceId: string | null; + availableWorkspaces?: WorkspaceAttribute[]; + workspaceEnabled: boolean; } export class SavedObjectsTable extends Component { private _isMounted = false; private currentWorkspaceIdSubscription?: Subscription; private workspacesSubscription?: Subscription; + private workspacesEnabledSubscription?: Subscription; constructor(props: SavedObjectsTableProps) { super(props); @@ -183,30 +185,31 @@ export class SavedObjectsTable extends Component ws.id); - } else if (workspaceId === PUBLIC_WORKSPACE_ID) { - return [PUBLIC_WORKSPACE_ID]; } else { - return [workspaceId, PUBLIC_WORKSPACE_ID]; + // application home + if (!currentWorkspaceId) { + return availableWorkspaces?.map((ws) => ws.id); + } else { + return [currentWorkspaceId]; + } } } private get wsNameIdLookup() { - const { availableWorkspace } = this.state; + const { availableWorkspaces } = this.state; // Assumption: workspace name is unique across the system - return availableWorkspace?.reduce((map, ws) => { + return availableWorkspaces?.reduce((map, ws) => { return map.set(ws.name, ws.id); }, new Map()); } @@ -224,6 +227,7 @@ export class SavedObjectsTable extends Component { @@ -304,12 +308,16 @@ export class SavedObjectsTable extends Component this.setState({ - workspaceId, + currentWorkspaceId: workspaceId, }) ); this.workspacesSubscription = workspace.workspaceList$.subscribe((workspaceList) => { - this.setState({ availableWorkspace: workspaceList }); + this.setState({ availableWorkspaces: workspaceList }); + }); + + this.workspacesEnabledSubscription = workspace.workspaceEnabled$.subscribe((enabled) => { + this.setState({ workspaceEnabled: enabled }); }); }; @@ -684,16 +692,14 @@ export class SavedObjectsTable extends Component { @@ -1035,6 +1043,9 @@ export class SavedObjectsTable extends Component @@ -1080,6 +1093,8 @@ export class SavedObjectsTable extends Component