Skip to content

Commit

Permalink
Add workspace column/filter into saved objects page
Browse files Browse the repository at this point in the history
Signed-off-by: Hailong Cui <[email protected]>
  • Loading branch information
Hailong-am committed Mar 7, 2024
1 parent d43cbbc commit 83bda69
Show file tree
Hide file tree
Showing 16 changed files with 507 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ export interface SavedObjectCountOptions {
export async function getSavedObjectCounts(
http: HttpStart,
options: SavedObjectCountOptions
): Promise<Record<string, number>> {
return await http.post<Record<string, number>>(
): Promise<Record<string, Record<string, number>>> {
return await http.post<Record<string, Record<string, number>>>(
`/api/opensearch-dashboards/management/saved_objects/scroll/counts`,
{ body: JSON.stringify(options) }
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ describe('getQueryText', () => {
return [{ value: 'lala' }, { value: 'lolo' }];
} else if (field === 'namespaces') {
return [{ value: 'default' }];
} else if (field === 'workspaces') {
return [{ value: 'workspaces' }];
}
return [];
},
Expand All @@ -47,6 +49,7 @@ describe('getQueryText', () => {
queryText: 'foo bar',
visibleTypes: 'lala',
visibleNamespaces: 'default',
visibleWorkspaces: 'workspaces',
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,15 @@ import { Query } from '@elastic/eui';
interface ParsedQuery {
queryText?: string;
visibleTypes?: string[];
visibleNamespaces?: string[];
visibleWorkspaces?: string[];
}

export function parseQuery(query: Query): ParsedQuery {
let queryText: string | undefined;
let visibleTypes: string[] | undefined;
let visibleNamespaces: string[] | undefined;
let visibleWorkspaces: string[] | undefined;

if (query) {
if (query.ast.getTermClauses().length) {
Expand All @@ -53,11 +56,15 @@ export function parseQuery(query: Query): ParsedQuery {
if (query.ast.getFieldClauses('namespaces')) {
visibleNamespaces = query.ast.getFieldClauses('namespaces')[0].value as string[];
}
if (query.ast.getFieldClauses('workspaces')) {
visibleWorkspaces = query.ast.getFieldClauses('workspaces')[0].value as string[];
}
}

return {
queryText,
visibleTypes,
visibleNamespaces,
visibleWorkspaces,
};
}

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 @@ -40,10 +40,29 @@ describe('Header', () => {
onRefresh: () => {},
totalCount: 4,
filteredCount: 2,
title: '',
};

const component = shallow(<Header {...props} />);

expect(component).toMatchSnapshot();
});
});

describe('Header - workspace enabled', () => {
it('should hide `Import` button for application home state', () => {
const props = {
onExportAll: () => {},
onImport: () => {},
onRefresh: () => {},
totalCount: 4,
filteredCount: 2,
hideImport: true,
title: 'Saved Objectes',
};

const component = shallow(<Header {...props} />);

expect(component.find('EuiButtonEmpty[data-test-subj="importObjects"]').exists()).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,15 @@ export const Header = ({
onImport,
onRefresh,
filteredCount,
hideImport,
title,
}: {
onExportAll: () => void;
onImport: () => void;
onRefresh: () => void;
filteredCount: number;
hideImport?: boolean;
title: string;
}) => (
<Fragment>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="baseline">
Expand Down Expand Up @@ -82,19 +86,21 @@ export const Header = ({
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="s"
iconType="importAction"
data-test-subj="importObjects"
onClick={onImport}
>
<FormattedMessage
id="savedObjectsManagement.objectsTable.header.importButtonLabel"
defaultMessage="Import"
/>
</EuiButtonEmpty>
</EuiFlexItem>
{!hideImport && (
<EuiFlexItem grow={false}>
<EuiButtonEmpty
size="s"
iconType="importAction"
data-test-subj="importObjects"
onClick={onImport}
>
<FormattedMessage
id="savedObjectsManagement.objectsTable.header.importButtonLabel"
defaultMessage="Import"
/>
</EuiButtonEmpty>
</EuiFlexItem>
)}
<EuiFlexItem grow={false}>
<EuiButtonEmpty size="s" iconType="refresh" onClick={onRefresh}>
<FormattedMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { actionServiceMock } from '../../../services/action_service.mock';
import { columnServiceMock } from '../../../services/column_service.mock';
import { SavedObjectsManagementAction } from '../../..';
import { Table, TableProps } from './table';
import { WorkspaceAttribute } from 'opensearch-dashboards/public';

const defaultProps: TableProps = {
basePath: httpServiceMock.createSetupContract().basePath,
Expand Down Expand Up @@ -115,6 +116,50 @@ describe('Table', () => {
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],
};
// not in a workspace
let component = shallowWithI18nProvider(<Table {...props} />);

let table = component.find('EuiBasicTable');
let columns = table.prop('columns') as any[];
let content = columns[1].render('My-Dashboard-test', item);
expect(content.props.href).toEqual('http://localhost/w/ws-1/app/dashboards#/view/dashboard-1');

// in a workspace
const currentWorkspaceId = 'foo-ws';
component = shallowWithI18nProvider(
<Table {...props} currentWorkspaceId={currentWorkspaceId} />
);

table = component.find('EuiBasicTable');
columns = table.prop('columns') as any[];
content = columns[1].render('My-Dashboard-test', item);
expect(content.props.href).toEqual(
`http://localhost/w/${currentWorkspaceId}/app/dashboards#/view/dashboard-1`
);
});

it('should handle query parse error', () => {
const onQueryChangeMock = jest.fn();
const customizedProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -56,6 +56,7 @@ import {
SavedObjectsManagementAction,
SavedObjectsManagementColumnServiceStart,
} from '../../../services';
import { formatUrlWithWorkspaceId } from '../../../../../../core/public/utils';

export interface TableProps {
basePath: IBasePath;
Expand Down Expand Up @@ -83,6 +84,8 @@ export interface TableProps {
onShowRelationships: (object: SavedObjectWithMetadata) => void;
canGoInApp: (obj: SavedObjectWithMetadata) => boolean;
dateFormat: string;
availableWorkspaces?: WorkspaceAttribute[];
currentWorkspaceId?: string;
}

interface TableState {
Expand Down Expand Up @@ -177,8 +180,12 @@ export class Table extends PureComponent<TableProps, TableState> {
columnRegistry,
namespaceRegistry,
dateFormat,
availableWorkspaces,
currentWorkspaceId,
} = this.props;

const visibleWsIds = availableWorkspaces?.map((ws) => ws.id) || [];

const pagination = {
pageIndex,
pageSize,
Expand Down Expand Up @@ -231,9 +238,19 @@ export class Table extends PureComponent<TableProps, TableState> {
if (!canGoInApp) {
return <EuiText size="s">{title || getDefaultTitle(object)}</EuiText>;
}
return (
<EuiLink href={basePath.prepend(path)}>{title || getDefaultTitle(object)}</EuiLink>
);
let inAppUrl = basePath.prepend(path);
if (object.workspaces?.length) {
if (currentWorkspaceId) {
inAppUrl = formatUrlWithWorkspaceId(path, currentWorkspaceId, basePath);
} else {
// first workspace user have permission
const [workspaceId] = object.workspaces.filter((wsId) => visibleWsIds.includes(wsId));
if (workspaceId) {
inAppUrl = formatUrlWithWorkspaceId(path, workspaceId, basePath);
}
}
}
return <EuiLink href={inAppUrl}>{title || getDefaultTitle(object)}</EuiLink>;
},
} as EuiTableFieldDataColumnType<SavedObjectWithMetadata<any>>,
{
Expand Down
Loading

0 comments on commit 83bda69

Please sign in to comment.