From 7e4900784a9702806438a786f2256ce947560f4e Mon Sep 17 00:00:00 2001 From: Hailong Cui Date: Fri, 11 Aug 2023 15:48:08 +0800 Subject: [PATCH] add workspace filter into saved objects page (#76) * add workspace filter into saved objects page Signed-off-by: Hailong Cui * workspace filter Signed-off-by: Hailong Cui * managment workspace filter Signed-off-by: Hailong Cui --------- Signed-off-by: Hailong Cui --- src/core/public/utils/index.ts | 2 +- .../public/lib/get_saved_object_counts.ts | 4 +- .../public/lib/parse_query.ts | 7 ++ .../objects_table/saved_objects_table.tsx | 114 +++++++++++++++--- .../server/routes/scroll_count.ts | 24 +++- 5 files changed, 129 insertions(+), 22 deletions(-) diff --git a/src/core/public/utils/index.ts b/src/core/public/utils/index.ts index a6d76a87e313..32ea0ba3c101 100644 --- a/src/core/public/utils/index.ts +++ b/src/core/public/utils/index.ts @@ -32,4 +32,4 @@ export { shareWeakReplay } from './share_weak_replay'; export { Sha256 } from './crypto'; export { MountWrapper, mountReactNode } from './mount'; export { getWorkspaceIdFromUrl, WORKSPACE_TYPE } from './workspace'; -export { WORKSPACE_PATH_PREFIX, PUBLIC_WORKSPACE } from '../../utils'; +export { WORKSPACE_PATH_PREFIX, PUBLIC_WORKSPACE, MANAGEMENT_WORKSPACE } from '../../utils'; diff --git a/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts b/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts index 9039dae2be53..374f2720b537 100644 --- a/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts +++ b/src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts @@ -40,8 +40,8 @@ export interface SavedObjectCountOptions { export async function getSavedObjectCounts( http: HttpStart, options: SavedObjectCountOptions -): Promise> { - return await http.post>( +): Promise>> { + return await http.post>>( `/api/opensearch-dashboards/management/saved_objects/scroll/counts`, { body: JSON.stringify(options) } ); diff --git a/src/plugins/saved_objects_management/public/lib/parse_query.ts b/src/plugins/saved_objects_management/public/lib/parse_query.ts index 24c35d500aaa..3db3f7fcee1c 100644 --- a/src/plugins/saved_objects_management/public/lib/parse_query.ts +++ b/src/plugins/saved_objects_management/public/lib/parse_query.ts @@ -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) { @@ -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, }; } 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 517b4a069266..fb621d13f8d0 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 @@ -66,8 +66,9 @@ import { OverlayStart, NotificationsStart, ApplicationStart, - PUBLIC_WORKSPACE, -} from '../../../../../core/public'; + WorkspaceAttribute, +} from 'src/core/public'; +import { Subscription } from 'rxjs'; import { RedirectAppLinks } from '../../../../opensearch_dashboards_react/public'; import { IndexPatternsContract } from '../../../../data/public'; import { @@ -95,6 +96,7 @@ import { import { Header, Table, Flyout, Relationships } from './components'; import { DataPublicPluginStart } from '../../../../../plugins/data/public'; import { SavedObjectsCopyModal } from './components/copy_modal'; +import { PUBLIC_WORKSPACE, MANAGEMENT_WORKSPACE } from '../../../../../core/public'; interface ExportAllOption { id: string; @@ -128,7 +130,7 @@ export interface SavedObjectsTableState { page: number; perPage: number; savedObjects: SavedObjectWithMetadata[]; - savedObjectCounts: Record; + savedObjectCounts: Record>; activeQuery: Query; selectedSavedObjects: SavedObjectWithMetadata[]; isShowingImportFlyout: boolean; @@ -144,23 +146,28 @@ export interface SavedObjectsTableState { exportAllSelectedOptions: Record; isIncludeReferencesDeepChecked: boolean; workspaceId: string | null; + availableWorkspace?: WorkspaceAttribute[]; } export class SavedObjectsTable extends Component { private _isMounted = false; + private currentWorkspaceIdSubscription?: Subscription; + private workspacesSubscription?: Subscription; constructor(props: SavedObjectsTableProps) { super(props); + const typeCounts = props.allowedTypes.reduce((typeToCountMap, type) => { + typeToCountMap[type] = 0; + return typeToCountMap; + }, {} as Record); + this.state = { totalCount: 0, page: 0, perPage: props.perPageConfig || 50, savedObjects: [], - savedObjectCounts: props.allowedTypes.reduce((typeToCountMap, type) => { - typeToCountMap[type] = 0; - return typeToCountMap; - }, {} as Record), + savedObjectCounts: { type: typeCounts } as Record>, activeQuery: Query.parse(''), selectedSavedObjects: [], isShowingImportFlyout: false, @@ -176,22 +183,37 @@ export class SavedObjectsTable extends Component ws.id); + } else if (workspaceId === PUBLIC_WORKSPACE) { + return [PUBLIC_WORKSPACE]; + } else { + return [workspaceId, PUBLIC_WORKSPACE]; + } + } + + private get wsNameIdLookup() { + const { availableWorkspace } = this.state; + // Assumption: workspace name is unique across the system + return availableWorkspace?.reduce((map, ws) => { + return map.set(ws.name, ws.id); + }, new Map()); } componentDidMount() { this._isMounted = true; - this.props.workspaces.currentWorkspaceId$.subscribe((workspaceId) => - this.setState({ - workspaceId, - }) - ); + + this.fetchWorkspace(); this.fetchSavedObjects(); this.fetchCounts(); } @@ -199,11 +221,15 @@ export class SavedObjectsTable extends Component { const { allowedTypes, namespaceRegistry } = this.props; - const { queryText, visibleTypes, visibleNamespaces } = parseQuery(this.state.activeQuery); + const { queryText, visibleTypes, visibleNamespaces, visibleWorkspaces } = parseQuery( + this.state.activeQuery + ); const filteredTypes = filterQuery(allowedTypes, visibleTypes); @@ -219,6 +245,11 @@ export class SavedObjectsTable extends Component this.wsNameIdLookup?.get(wsName) || PUBLIC_WORKSPACE + ); + } // These are the saved objects visible in the table. const filteredSavedObjectCounts = await getSavedObjectCounts( @@ -268,6 +299,19 @@ export class SavedObjectsTable extends Component { + const workspace = this.props.workspaces; + this.currentWorkspaceIdSubscription = workspace.currentWorkspaceId$.subscribe((workspaceId) => + this.setState({ + workspaceId, + }) + ); + + this.workspacesSubscription = workspace.workspaceList$.subscribe((workspaceList) => { + this.setState({ availableWorkspace: workspaceList }); + }); + }; + fetchSavedObject = (type: string, id: string) => { this.setState({ isSearching: true }, () => this.debouncedFetchObject(type, id)); }; @@ -275,7 +319,7 @@ export class SavedObjectsTable extends Component { const { activeQuery: query, page, perPage } = this.state; const { notifications, http, allowedTypes, namespaceRegistry } = this.props; - const { queryText, visibleTypes, visibleNamespaces } = parseQuery(query); + const { queryText, visibleTypes, visibleNamespaces, visibleWorkspaces } = parseQuery(query); const filteredTypes = filterQuery(allowedTypes, visibleTypes); // "searchFields" is missing from the "findOptions" but gets injected via the API. // The API extracts the fields from each uiExports.savedObjectsManagement "defaultSearchField" attribute @@ -294,6 +338,13 @@ export class SavedObjectsTable extends Component this.wsNameIdLookup?.get(wsName) || PUBLIC_WORKSPACE + ); + findOptions.workspaces = workspaceIds; + } + if (findOptions.type.length > 1) { findOptions.sortField = 'type'; } @@ -880,6 +931,7 @@ export class SavedObjectsTable extends Component { + return this.workspaceIdQuery?.includes(ws.id); + }) + .map((ws) => { + return { + name: ws.name, + value: ws.name, + view: `${ws.name} (${wsCounts[ws.id] || 0})`, + }; + }); + + filters.push({ + type: 'field_value_selection', + field: 'workspaces', + name: + namespaceRegistry.getAlias() || + i18n.translate('savedObjectsManagement.objectsTable.table.workspaceFilterName', { + defaultMessage: 'Workspaces', + }), + multiSelect: 'or', + options: wsFilterOptions, + }); + } + return ( { }, router.handleLegacyErrors(async (context, req, res) => { const { client } = context.core.savedObjects; - const counts = { + const counts: Record> = { type: {}, }; const findOptions: SavedObjectsFindOptions = { type: req.body.typesToInclude, perPage: 1000, - workspaces: req.body.workspaces, }; const requestHasNamespaces = Array.isArray(req.body.namespacesToInclude) && req.body.namespacesToInclude.length; + const requestHasWorkspaces = Array.isArray(req.body.workspaces) && req.body.workspaces.length; + if (requestHasNamespaces) { counts.namespaces = {}; findOptions.namespaces = req.body.namespacesToInclude; } + if (requestHasWorkspaces) { + counts.workspaces = {}; + findOptions.workspaces = req.body.workspaces; + } + if (req.body.searchString) { findOptions.search = `${req.body.searchString}*`; findOptions.searchFields = ['title']; @@ -84,6 +90,13 @@ export const registerScrollForCountRoute = (router: IRouter) => { counts.namespaces[ns]++; }); } + if (requestHasWorkspaces) { + const resultWorkspaces = result.workspaces || ['public']; + resultWorkspaces.forEach((ws) => { + counts.workspaces[ws] = counts.workspaces[ws] || 0; + counts.workspaces[ws]++; + }); + } counts.type[type] = counts.type[type] || 0; counts.type[type]++; }); @@ -101,6 +114,13 @@ export const registerScrollForCountRoute = (router: IRouter) => { } } + const workspacesToInclude = req.body.workspaces || []; + for (const ws of workspacesToInclude) { + if (!counts.workspaces[ws]) { + counts.workspaces[ws] = 0; + } + } + return res.ok({ body: counts, });