Skip to content

Commit ce3e235

Browse files
cwperkszhongnansu
authored andcommitted
Add extension point in saved object management to register namespaces and show filter (#2656)
* Create filter registry for saved object management to make filters extensible Signed-off-by: Craig Perkins <[email protected]> * WIP on making fetchCounts generic Signed-off-by: Craig Perkins <[email protected]> * First step at making scroll_counts generic Signed-off-by: Craig Perkins <[email protected]> * Work on getting other filter counts with same object count endpoint Signed-off-by: Craig Perkins <[email protected]> * Get tenant count options to display Signed-off-by: Craig Perkins <[email protected]> * Extend find to work with namespaces for saved objects Signed-off-by: Craig Perkins <[email protected]> * Add missing filterFields Signed-off-by: Craig Perkins <[email protected]> * Update jest tests Signed-off-by: Craig Perkins <[email protected]> * Update saved_objects_table snapshot Signed-off-by: Craig Perkins <[email protected]> * Append index to id to make unique Signed-off-by: Craig Perkins <[email protected]> * Add semi-colon Signed-off-by: Craig Perkins <[email protected]> * Fix saved objects table tests with new id scheme Signed-off-by: Craig Perkins <[email protected]> * Only append idx on config type to ensure Advanced Settings have a unique id across tenants Signed-off-by: Craig Perkins <[email protected]> * Remove itemsClone in favor of showing only Advanced Settings of current tenant Signed-off-by: Craig Perkins <[email protected]> * Revert snapshots in table.test.tsx Signed-off-by: Craig Perkins <[email protected]> * Add additional parse_query test Signed-off-by: Craig Perkins <[email protected]> * Add comma Signed-off-by: Craig Perkins <[email protected]> * Create namespaceRegistry to decouple security dashboards plugin and osd core Signed-off-by: Craig Perkins <[email protected]> * Add ability to register an alias Signed-off-by: Craig Perkins <[email protected]> * Update parse query and add to CHANGELOG Signed-off-by: Craig Perkins <[email protected]> * Remove commented out code Signed-off-by: Craig Perkins <[email protected]> * Address code review comments Signed-off-by: Craig Perkins <[email protected]> * Override i18n if alias is regitered Signed-off-by: Craig Perkins <[email protected]> Signed-off-by: Craig Perkins <[email protected]> (cherry picked from commit 8ac127a) Signed-off-by: Su <[email protected]>
1 parent 7792e75 commit ce3e235

File tree

19 files changed

+335
-60
lines changed

19 files changed

+335
-60
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
3030
* [Vis Builder] Rename wizard to visBuilder in i18n id and formatted message id ([#2635](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2635))
3131
* [Vis Builder] Rename wizard to visBuilder in class name, type name and function name ([#2639](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2639))
3232
* [Vis Builder] Rename wizard on save modal and visualization table ([#2645](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2645))
33+
* Add extension point in saved object management to register namespaces and show filter ([#2656](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2656))
3334

3435
### 🐛 Bug Fixes
3536
* [Vis Builder] Fixes auto bounds for timeseries bar chart visualization ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401))

src/plugins/saved_objects_management/public/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export {
3939
SavedObjectsManagementColumnServiceSetup,
4040
SavedObjectsManagementColumnServiceStart,
4141
SavedObjectsManagementColumn,
42+
SavedObjectsManagementNamespaceServiceSetup,
43+
SavedObjectsManagementNamespaceServiceStart,
44+
SavedObjectsManagementNamespace,
4245
SavedObjectsManagementRecord,
4346
ISavedObjectsManagementServiceRegistry,
4447
SavedObjectsManagementServiceRegistryEntry,

src/plugins/saved_objects_management/public/lib/get_saved_object_counts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,11 @@ import { HttpStart } from 'src/core/public';
3333
export async function getSavedObjectCounts(
3434
http: HttpStart,
3535
typesToInclude: string[],
36+
namespacesToInclude: string[],
3637
searchString?: string
3738
): Promise<Record<string, number>> {
3839
return await http.post<Record<string, number>>(
3940
`/api/opensearch-dashboards/management/saved_objects/scroll/counts`,
40-
{ body: JSON.stringify({ typesToInclude, searchString }) }
41+
{ body: JSON.stringify({ typesToInclude, namespacesToInclude, searchString }) }
4142
);
4243
}

src/plugins/saved_objects_management/public/lib/parse_query.test.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,19 @@ describe('getQueryText', () => {
3434
it('should know how to get the text out of the AST', () => {
3535
const ast = {
3636
getTermClauses: () => [{ value: 'foo' }, { value: 'bar' }],
37-
getFieldClauses: () => [{ value: 'lala' }, { value: 'lolo' }],
37+
getFieldClauses: (field) => {
38+
if (field === 'type') {
39+
return [{ value: 'lala' }, { value: 'lolo' }];
40+
} else if (field === 'namespaces') {
41+
return [{ value: 'default' }];
42+
}
43+
return [];
44+
},
3845
};
39-
expect(parseQuery({ ast } as any)).toEqual({ queryText: 'foo bar', visibleTypes: 'lala' });
46+
expect(parseQuery({ ast } as any, ['type'])).toEqual({
47+
queryText: 'foo bar',
48+
visibleTypes: 'lala',
49+
visibleNamespaces: 'default',
50+
});
4051
});
4152
});

src/plugins/saved_objects_management/public/lib/parse_query.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ interface ParsedQuery {
3838
export function parseQuery(query: Query): ParsedQuery {
3939
let queryText: string | undefined;
4040
let visibleTypes: string[] | undefined;
41+
let visibleNamespaces: string[] | undefined;
4142

4243
if (query) {
4344
if (query.ast.getTermClauses().length) {
@@ -49,10 +50,14 @@ export function parseQuery(query: Query): ParsedQuery {
4950
if (query.ast.getFieldClauses('type')) {
5051
visibleTypes = query.ast.getFieldClauses('type')[0].value as string[];
5152
}
53+
if (query.ast.getFieldClauses('namespaces')) {
54+
visibleNamespaces = query.ast.getFieldClauses('namespaces')[0].value as string[];
55+
}
5256
}
5357

5458
return {
5559
queryText,
5660
visibleTypes,
61+
visibleNamespaces,
5762
};
5863
}

src/plugins/saved_objects_management/public/management_section/mount_section.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ export const mountManagementSection = async ({
104104
serviceRegistry={serviceRegistry}
105105
actionRegistry={pluginStart.actions}
106106
columnRegistry={pluginStart.columns}
107+
namespaceRegistry={pluginStart.namespaces}
107108
allowedTypes={allowedObjectTypes}
108109
setBreadcrumbs={setBreadcrumbs}
109110
/>

src/plugins/saved_objects_management/public/management_section/objects_table/__snapshots__/saved_objects_table.test.tsx.snap

Lines changed: 27 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/plugins/saved_objects_management/public/management_section/objects_table/components/table.test.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,19 @@ const defaultProps: TableProps = {
6262
selectionConfig: {
6363
onSelectionChange: () => {},
6464
},
65-
filterOptions: [{ value: 2 }],
65+
filters: [
66+
{
67+
type: 'field_value_selection',
68+
field: 'type',
69+
name: 'Type',
70+
multiSelect: 'or',
71+
options: [
72+
{
73+
value: 2,
74+
},
75+
],
76+
},
77+
],
6678
onDelete: () => {},
6779
onActionRefresh: () => {},
6880
onExport: () => {},

src/plugins/saved_objects_management/public/management_section/objects_table/components/table.tsx

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,12 @@ export interface TableProps {
6161
basePath: IBasePath;
6262
actionRegistry: SavedObjectsManagementActionServiceStart;
6363
columnRegistry: SavedObjectsManagementColumnServiceStart;
64+
namespaceRegistry: SavedObjectsManagementNamespaceServiceStart;
6465
selectedSavedObjects: SavedObjectWithMetadata[];
6566
selectionConfig: {
6667
onSelectionChange: (selection: SavedObjectWithMetadata[]) => void;
6768
};
68-
filterOptions: any[];
69+
filters: any[];
6970
canDelete: boolean;
7071
onDelete: () => void;
7172
onActionRefresh: (object: SavedObjectWithMetadata) => void;
@@ -76,7 +77,7 @@ export interface TableProps {
7677
items: SavedObjectWithMetadata[];
7778
itemId: string | (() => string);
7879
totalItemCount: number;
79-
onQueryChange: (query: any) => void;
80+
onQueryChange: (query: any, filterFields: string[]) => void;
8081
onTableChange: (table: any) => void;
8182
isSearching: boolean;
8283
onShowRelationships: (object: SavedObjectWithMetadata) => void;
@@ -163,7 +164,7 @@ export class Table extends PureComponent<TableProps, TableState> {
163164
items,
164165
totalItemCount,
165166
isSearching,
166-
filterOptions,
167+
filters,
167168
selectionConfig: selection,
168169
onDelete,
169170
onActionRefresh,
@@ -174,6 +175,7 @@ export class Table extends PureComponent<TableProps, TableState> {
174175
basePath,
175176
actionRegistry,
176177
columnRegistry,
178+
namespaceRegistry,
177179
dateFormat,
178180
} = this.props;
179181

@@ -184,26 +186,6 @@ export class Table extends PureComponent<TableProps, TableState> {
184186
pageSizeOptions: [5, 10, 20, 50],
185187
};
186188

187-
const filters = [
188-
{
189-
type: 'field_value_selection',
190-
field: 'type',
191-
name: i18n.translate('savedObjectsManagement.objectsTable.table.typeFilterName', {
192-
defaultMessage: 'Type',
193-
}),
194-
multiSelect: 'or',
195-
options: filterOptions,
196-
},
197-
// Add this back in once we have tag support
198-
// {
199-
// type: 'field_value_selection',
200-
// field: 'tag',
201-
// name: 'Tags',
202-
// multiSelect: 'or',
203-
// options: [],
204-
// },
205-
];
206-
207189
const columns = [
208190
{
209191
field: 'type',

src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { dataPluginMock } from '../../../../data/public/mocks';
5353
import { serviceRegistryMock } from '../../services/service_registry.mock';
5454
import { actionServiceMock } from '../../services/action_service.mock';
5555
import { columnServiceMock } from '../../services/column_service.mock';
56+
import { namespaceServiceMock } from '../../services/namespace_service.mock';
5657
import {
5758
SavedObjectsTable,
5859
SavedObjectsTableProps,
@@ -147,6 +148,7 @@ describe('SavedObjectsTable', () => {
147148
serviceRegistry: serviceRegistryMock.create(),
148149
actionRegistry: actionServiceMock.createStart(),
149150
columnRegistry: columnServiceMock.createStart(),
151+
namespaceRegistry: namespaceServiceMock.createStart(),
150152
savedObjectsClient: savedObjects.client,
151153
indexPatterns: dataPluginMock.createStartContract().indexPatterns,
152154
http,

src/plugins/saved_objects_management/public/management_section/objects_table/saved_objects_table.tsx

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import {
8585
ISavedObjectsManagementServiceRegistry,
8686
SavedObjectsManagementActionServiceStart,
8787
SavedObjectsManagementColumnServiceStart,
88+
SavedObjectsManagementNamespaceServiceStart,
8889
} from '../../services';
8990
import { Header, Table, Flyout, Relationships } from './components';
9091
import { DataPublicPluginStart } from '../../../../../plugins/data/public';
@@ -99,6 +100,7 @@ export interface SavedObjectsTableProps {
99100
serviceRegistry: ISavedObjectsManagementServiceRegistry;
100101
actionRegistry: SavedObjectsManagementActionServiceStart;
101102
columnRegistry: SavedObjectsManagementColumnServiceStart;
103+
namespaceRegistry: SavedObjectsManagementNamespaceServiceStart;
102104
savedObjectsClient: SavedObjectsClientContract;
103105
indexPatterns: IndexPatternsContract;
104106
http: HttpStart;
@@ -177,7 +179,7 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
177179

178180
fetchCounts = async () => {
179181
const { allowedTypes } = this.props;
180-
const { queryText, visibleTypes } = parseQuery(this.state.activeQuery);
182+
const { queryText, visibleTypes, visibleNamespaces } = parseQuery(this.state.activeQuery);
181183

182184
const filteredTypes = allowedTypes.filter(
183185
(type) => !visibleTypes || visibleTypes.includes(type)
@@ -187,6 +189,7 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
187189
const filteredSavedObjectCounts = await getSavedObjectCounts(
188190
this.props.http,
189191
filteredTypes,
192+
visibleNamespaces,
190193
queryText
191194
);
192195

@@ -206,7 +209,12 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
206209

207210
// Fetch all the saved objects that exist so we can accurately populate the counts within
208211
// the table filter dropdown.
209-
const savedObjectCounts = await getSavedObjectCounts(this.props.http, allowedTypes, queryText);
212+
const savedObjectCounts = await getSavedObjectCounts(
213+
this.props.http,
214+
allowedTypes,
215+
[],
216+
queryText
217+
);
210218

211219
this.setState((state) => ({
212220
...state,
@@ -227,15 +235,19 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
227235
debouncedFetchObjects = debounce(async () => {
228236
const { activeQuery: query, page, perPage } = this.state;
229237
const { notifications, http, allowedTypes } = this.props;
230-
const { queryText, visibleTypes } = parseQuery(query);
238+
const { queryText, visibleTypes, visibleNamespaces } = parseQuery(query);
239+
const filteredTypes = allowedTypes.filter(
240+
(type) => !visibleTypes || visibleTypes.includes(type)
241+
);
231242
// "searchFields" is missing from the "findOptions" but gets injected via the API.
232243
// The API extracts the fields from each uiExports.savedObjectsManagement "defaultSearchField" attribute
233244
const findOptions: SavedObjectsFindOptions = {
234245
search: queryText ? `${queryText}*` : undefined,
235246
perPage,
236247
page: page + 1,
237248
fields: ['id'],
238-
type: allowedTypes.filter((type) => !visibleTypes || visibleTypes.includes(type)),
249+
type: filteredTypes,
250+
namespaces: visibleNamespaces,
239251
};
240252
if (findOptions.type.length > 1) {
241253
findOptions.sortField = 'type';
@@ -390,6 +402,7 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
390402
onExportAll = async () => {
391403
const { exportAllSelectedOptions, isIncludeReferencesDeepChecked, activeQuery } = this.state;
392404
const { notifications, http } = this.props;
405+
393406
const { queryText } = parseQuery(activeQuery);
394407
const exportTypes = Object.entries(exportAllSelectedOptions).reduce((accum, [id, selected]) => {
395408
if (selected) {
@@ -764,18 +777,55 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
764777
isSearching,
765778
savedObjectCounts,
766779
} = this.state;
767-
const { http, allowedTypes, applications } = this.props;
780+
const { http, allowedTypes, applications, namespaceRegistry } = this.props;
768781

769782
const selectionConfig = {
770783
onSelectionChange: this.onSelectionChanged,
771784
};
785+
const typeCounts = savedObjectCounts.type || {};
772786

773787
const filterOptions = allowedTypes.map((type) => ({
774788
value: type,
775789
name: type,
776-
view: `${type} (${savedObjectCounts[type] || 0})`,
790+
view: `${type} (${typeCounts[type] || 0})`,
777791
}));
778792

793+
const filters = [
794+
{
795+
type: 'field_value_selection',
796+
field: 'type',
797+
name: i18n.translate('savedObjectsManagement.objectsTable.table.typeFilterName', {
798+
defaultMessage: 'Type',
799+
}),
800+
multiSelect: 'or',
801+
options: filterOptions,
802+
},
803+
];
804+
805+
const availableNamespaces = namespaceRegistry.getAll() || [];
806+
if (availableNamespaces && availableNamespaces.length > 0) {
807+
const nsCounts = savedObjectCounts.namespaces || {};
808+
const nsFilterOptions = availableNamespaces.map((ns) => {
809+
return {
810+
name: ns.name,
811+
value: ns.id,
812+
view: `${ns.name} (${nsCounts[ns.id] || 0})`,
813+
};
814+
});
815+
816+
filters.push({
817+
type: 'field_value_selection',
818+
field: 'namespaces',
819+
name:
820+
namespaceRegistry.getAlias() ||
821+
i18n.translate('savedObjectsManagement.objectsTable.table.namespaceFilterName', {
822+
defaultMessage: 'Namespaces',
823+
}),
824+
multiSelect: 'or',
825+
options: nsFilterOptions,
826+
});
827+
}
828+
779829
return (
780830
<EuiPageContent horizontalPosition="center">
781831
{this.renderFlyout()}
@@ -799,7 +849,7 @@ export class SavedObjectsTable extends Component<SavedObjectsTableProps, SavedOb
799849
selectedSavedObjects={selectedSavedObjects}
800850
onQueryChange={this.onQueryChange}
801851
onTableChange={this.onTableChange}
802-
filterOptions={filterOptions}
852+
filters={filters}
803853
onExport={this.onExport}
804854
canDelete={applications.capabilities.savedObjectsManagement.delete as boolean}
805855
onDelete={this.onDelete}

0 commit comments

Comments
 (0)