From 230353e3f43ca15882e71f6c57bd21ff3666cd69 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 24 Oct 2023 17:04:43 +0800 Subject: [PATCH 01/18] feat: consume current workspace in saved objects management and saved objects client Signed-off-by: SuZhou-Joe --- .../saved_objects/saved_objects_client.ts | 44 ++++++++++++- .../saved_objects_service.mock.ts | 1 + .../import/resolve_import_errors.ts | 1 + .../public/lib/fetch_export_objects.ts | 4 +- .../public/lib/get_saved_object_counts.ts | 1 + .../public/lib/import_file.ts | 4 +- .../public/lib/resolve_import_errors.ts | 10 ++- .../objects_table/components/flyout.tsx | 15 ++++- .../components/import_mode_control.tsx | 1 + .../saved_objects_table.test.tsx | 17 +++-- .../objects_table/saved_objects_table.tsx | 65 ++++++++++++++++--- .../saved_objects_table_page.tsx | 1 + .../server/routes/find.ts | 4 ++ .../server/routes/scroll_count.ts | 7 ++ src/plugins/workspace/public/plugin.ts | 23 ++++++- src/plugins/workspace/server/plugin.ts | 2 + 16 files changed, 176 insertions(+), 24 deletions(-) diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index d6b6b6b6d89c..61900877c6e3 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -61,6 +61,7 @@ export interface SavedObjectsCreateOptions { /** {@inheritDoc SavedObjectsMigrationVersion} */ migrationVersion?: SavedObjectsMigrationVersion; references?: SavedObjectReference[]; + workspaces?: string[]; } /** @@ -183,6 +184,11 @@ const getObjectsToFetch = (queue: BatchQueueEntry[]): ObjectTypeAndId[] => { export class SavedObjectsClient { private http: HttpSetup; private batchQueue: BatchQueueEntry[]; + /** + * if currentWorkspaceId is undefined, it means + * we should not carry out workspace info when doing any operation. + */ + private currentWorkspaceId: string | undefined; /** * Throttled processing of get requests into bulk requests at 100ms interval @@ -227,6 +233,11 @@ export class SavedObjectsClient { this.batchQueue = []; } + public setCurrentWorkspace(workspaceId: string): boolean { + this.currentWorkspaceId = workspaceId; + return true; + } + /** * Persists an object * @@ -249,6 +260,14 @@ export class SavedObjectsClient { overwrite: options.overwrite, }; + const currentWorkspaceId = this.currentWorkspaceId; + let finalWorkspaces; + if (options.hasOwnProperty('workspaces')) { + finalWorkspaces = options.workspaces; + } else if (typeof currentWorkspaceId === 'string') { + finalWorkspaces = [currentWorkspaceId]; + } + const createRequest: Promise> = this.savedObjectsFetch(path, { method: 'POST', query, @@ -256,6 +275,11 @@ export class SavedObjectsClient { attributes, migrationVersion: options.migrationVersion, references: options.references, + ...(finalWorkspaces + ? { + workspaces: finalWorkspaces, + } + : {}), }), }); @@ -348,7 +372,25 @@ export class SavedObjectsClient { workspaces: 'workspaces', }; - const renamedQuery = renameKeys(renameMap, options); + const currentWorkspaceId = this.currentWorkspaceId; + let finalWorkspaces; + if (options.hasOwnProperty('workspaces')) { + finalWorkspaces = options.workspaces; + } else if (typeof currentWorkspaceId === 'string') { + finalWorkspaces = Array.from(new Set([currentWorkspaceId])); + } + + const renamedQuery = renameKeys, any>( + renameMap, + { + ...options, + ...(finalWorkspaces + ? { + workspaces: finalWorkspaces, + } + : {}), + } + ); const query = pick.apply(null, [renamedQuery, ...Object.values(renameMap)]) as Partial< Record >; diff --git a/src/core/public/saved_objects/saved_objects_service.mock.ts b/src/core/public/saved_objects/saved_objects_service.mock.ts index 47bd146058f7..00ca44072958 100644 --- a/src/core/public/saved_objects/saved_objects_service.mock.ts +++ b/src/core/public/saved_objects/saved_objects_service.mock.ts @@ -41,6 +41,7 @@ const createStartContractMock = () => { find: jest.fn(), get: jest.fn(), update: jest.fn(), + setCurrentWorkspace: jest.fn(), }, }; return mock; diff --git a/src/core/server/saved_objects/import/resolve_import_errors.ts b/src/core/server/saved_objects/import/resolve_import_errors.ts index 49b7c67b5ab6..33f62b98eeb1 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.ts @@ -131,6 +131,7 @@ export async function resolveSavedObjectsImportErrors({ retries, createNewCopies, dataSourceId, + workspaces, }; const checkConflictsResult = await checkConflicts(checkConflictsParams); errorAccumulator = [...errorAccumulator, ...checkConflictsResult.errors]; diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts index b2e2ea0f9165..43afcfec3056 100644 --- a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts @@ -33,10 +33,12 @@ import { HttpStart } from 'src/core/public'; export async function fetchExportObjects( http: HttpStart, objects: any[], - includeReferencesDeep: boolean = false + includeReferencesDeep: boolean = false, + body?: Record ): Promise { return http.post('/api/saved_objects/_export', { body: JSON.stringify({ + ...body, objects, includeReferencesDeep, }), 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 6eaaac7d35f2..9039dae2be53 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 @@ -34,6 +34,7 @@ export interface SavedObjectCountOptions { typesToInclude: string[]; namespacesToInclude?: string[]; searchString?: string; + workspaces?: string[]; } export async function getSavedObjectCounts( diff --git a/src/plugins/saved_objects_management/public/lib/import_file.ts b/src/plugins/saved_objects_management/public/lib/import_file.ts index 3753a8251e10..bcf1b6911b0f 100644 --- a/src/plugins/saved_objects_management/public/lib/import_file.ts +++ b/src/plugins/saved_objects_management/public/lib/import_file.ts @@ -40,12 +40,12 @@ interface ImportResponse { export async function importFile( http: HttpStart, file: File, - { createNewCopies, overwrite }: ImportMode, + { createNewCopies, overwrite, workspaces }: ImportMode, selectedDataSourceId?: string ) { const formData = new FormData(); formData.append('file', file); - const query = createNewCopies ? { createNewCopies } : { overwrite }; + const query = createNewCopies ? { createNewCopies, workspaces } : { overwrite, workspaces }; if (selectedDataSourceId) { query.dataSourceId = selectedDataSourceId; } diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts index 585102ee5b8e..2bcaf6e9f6cf 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts @@ -90,12 +90,13 @@ async function callResolveImportErrorsApi( file: File, retries: any, createNewCopies: boolean, - selectedDataSourceId?: string + selectedDataSourceId?: string, + workspaces?: string[] ): Promise { const formData = new FormData(); formData.append('file', file); formData.append('retries', JSON.stringify(retries)); - const query = createNewCopies ? { createNewCopies } : {}; + const query = createNewCopies ? { createNewCopies, workspaces } : { workspaces }; if (selectedDataSourceId) { query.dataSourceId = selectedDataSourceId; } @@ -172,6 +173,7 @@ export async function resolveImportErrors({ getConflictResolutions, state, selectedDataSourceId, + workspaces, }: { http: HttpStart; getConflictResolutions: ( @@ -186,6 +188,7 @@ export async function resolveImportErrors({ importMode: { createNewCopies: boolean; overwrite: boolean }; }; selectedDataSourceId: string; + workspaces?: string[]; }) { const retryDecisionCache = new Map(); const replaceReferencesCache = new Map(); @@ -275,7 +278,8 @@ export async function resolveImportErrors({ file!, retries, createNewCopies, - selectedDataSourceId + selectedDataSourceId, + workspaces ); importCount = response.successCount; // reset the success count since we retry all successful results each time failedImports = []; diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index 586a573ffb53..361648881f93 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -97,6 +97,7 @@ export interface FlyoutProps { hideLocalCluster: boolean; savedObjects: SavedObjectsClientContract; notifications: NotificationsStart; + workspaces?: string[]; } export interface FlyoutState { @@ -189,13 +190,21 @@ export class Flyout extends Component { * Does the initial import of a file, resolveImportErrors then handles errors and retries */ import = async () => { - const { http } = this.props; + const { http, workspaces } = this.props; const { file, importMode, selectedDataSourceId } = this.state; this.setState({ status: 'loading', error: undefined }); // Import the file try { - const response = await importFile(http, file!, importMode, selectedDataSourceId); + const response = await importFile( + http, + file!, + { + ...importMode, + workspaces, + }, + selectedDataSourceId + ); this.setState(processImportResponse(response), () => { // Resolve import errors right away if there's no index patterns to match // This will ask about overwriting each object, etc @@ -251,6 +260,7 @@ export class Flyout extends Component { status: 'loading', loadingMessage: undefined, }); + const { workspaces } = this.props; try { const updatedState = await resolveImportErrors({ @@ -258,6 +268,7 @@ export class Flyout extends Component { state: this.state, getConflictResolutions: this.getConflictResolutions, selectedDataSourceId: this.state.selectedDataSourceId, + workspaces, }); this.setState(updatedState); } catch (e) { diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx index d0c800553996..49974c53b3d7 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx @@ -52,6 +52,7 @@ export interface ImportModeControlProps { export interface ImportMode { createNewCopies: boolean; overwrite: boolean; + workspaces?: string[]; } const createNewCopiesDisabled = { 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 5a6bf0713d95..74bd973b9cc3 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 @@ -48,6 +48,7 @@ import { notificationServiceMock, savedObjectsServiceMock, applicationServiceMock, + workspacesServiceMock, } from '../../../../../core/public/mocks'; import { dataPluginMock } from '../../../../data/public/mocks'; import { serviceRegistryMock } from '../../services/service_registry.mock'; @@ -102,6 +103,7 @@ describe('SavedObjectsTable', () => { let notifications: ReturnType; let savedObjects: ReturnType; let search: ReturnType['search']; + let workspaces: ReturnType; const shallowRender = (overrides: Partial = {}) => { return (shallowWithI18nProvider( @@ -121,6 +123,7 @@ describe('SavedObjectsTable', () => { notifications = notificationServiceMock.createStartContract(); savedObjects = savedObjectsServiceMock.createStartContract(); search = dataPluginMock.createStartContract().search; + workspaces = workspacesServiceMock.createStartContract(); const applications = applicationServiceMock.createStartContract(); applications.capabilities = { @@ -132,6 +135,9 @@ describe('SavedObjectsTable', () => { edit: false, delete: false, }, + workspaces: { + enabled: false, + }, }; http.post.mockResolvedValue([]); @@ -154,6 +160,7 @@ describe('SavedObjectsTable', () => { savedObjectsClient: savedObjects.client, indexPatterns: dataPluginMock.createStartContract().indexPatterns, http, + workspaces, overlays, notifications, applications, @@ -279,7 +286,7 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, {}); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); @@ -322,7 +329,7 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, {}); expect(notifications.toasts.addWarning).toHaveBeenCalledWith({ title: 'Your file is downloading in the background. ' + @@ -363,7 +370,8 @@ describe('SavedObjectsTable', () => { http, allowedTypes, undefined, - true + true, + {} ); expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ @@ -393,7 +401,8 @@ describe('SavedObjectsTable', () => { http, allowedTypes, 'test*', - true + true, + {} ); 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 955482cc0676..fdec01e0f29d 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 @@ -61,11 +61,13 @@ import { FormattedMessage } from '@osd/i18n/react'; import { SavedObjectsClientContract, SavedObjectsFindOptions, + WorkspacesStart, HttpStart, OverlayStart, NotificationsStart, ApplicationStart, } from 'src/core/public'; +import { Subscription } from 'rxjs'; import { RedirectAppLinks } from '../../../../opensearch_dashboards_react/public'; import { IndexPatternsContract } from '../../../../data/public'; import { @@ -106,6 +108,7 @@ export interface SavedObjectsTableProps { savedObjectsClient: SavedObjectsClientContract; indexPatterns: IndexPatternsContract; http: HttpStart; + workspaces: WorkspacesStart; search: DataPublicPluginStart['search']; overlays: OverlayStart; notifications: NotificationsStart; @@ -137,10 +140,14 @@ export interface SavedObjectsTableState { exportAllOptions: ExportAllOption[]; exportAllSelectedOptions: Record; isIncludeReferencesDeepChecked: boolean; + currentWorkspaceId: string | null; + workspaceEnabled: boolean; } export class SavedObjectsTable extends Component { private _isMounted = false; + private currentWorkspaceIdSubscription?: Subscription; + private workspacesSubscription?: Subscription; constructor(props: SavedObjectsTableProps) { super(props); @@ -167,9 +174,35 @@ export class SavedObjectsTable extends Component( + obj: T + ): T | Omit { + const { workspaces, ...others } = obj; + if (workspaces) { + return obj; + } + return others; + } + componentDidMount() { this._isMounted = true; this.fetchSavedObjects(); @@ -189,10 +222,11 @@ export class SavedObjectsTable extends Component ns.id) || []; - const filteredCountOptions: SavedObjectCountOptions = { + const filteredCountOptions: SavedObjectCountOptions = this.formatWorkspaceIdParams({ typesToInclude: filteredTypes, searchString: queryText, - }; + workspaces: this.workspaceIdQuery, + }); if (availableNamespaces.length) { const filteredNamespaces = filterQuery(availableNamespaces, visibleNamespaces); @@ -221,10 +255,11 @@ export class SavedObjectsTable extends Component ns.id) || []; if (availableNamespaces.length) { @@ -405,7 +441,14 @@ export class SavedObjectsTable extends Component().concat(req.query.workspaces) : undefined, }); const savedObjects = await Promise.all( diff --git a/src/plugins/saved_objects_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts index 63233748a896..221d39392842 100644 --- a/src/plugins/saved_objects_management/server/routes/scroll_count.ts +++ b/src/plugins/saved_objects_management/server/routes/scroll_count.ts @@ -41,6 +41,7 @@ export const registerScrollForCountRoute = (router: IRouter) => { typesToInclude: schema.arrayOf(schema.string()), namespacesToInclude: schema.maybe(schema.arrayOf(schema.string())), searchString: schema.maybe(schema.string()), + workspaces: schema.maybe(schema.arrayOf(schema.string())), }), }, }, @@ -55,6 +56,8 @@ export const registerScrollForCountRoute = (router: IRouter) => { perPage: 1000, }; + const requestHasWorkspaces = Array.isArray(req.body.workspaces) && req.body.workspaces.length; + const requestHasNamespaces = Array.isArray(req.body.namespacesToInclude) && req.body.namespacesToInclude.length; @@ -63,6 +66,10 @@ export const registerScrollForCountRoute = (router: IRouter) => { findOptions.namespaces = req.body.namespacesToInclude; } + if (requestHasWorkspaces) { + findOptions.workspaces = req.body.workspaces; + } + if (req.body.searchString) { findOptions.search = `${req.body.searchString}*`; findOptions.searchFields = ['title']; diff --git a/src/plugins/workspace/public/plugin.ts b/src/plugins/workspace/public/plugin.ts index 18e84e3a6f35..3840066fcee3 100644 --- a/src/plugins/workspace/public/plugin.ts +++ b/src/plugins/workspace/public/plugin.ts @@ -3,16 +3,33 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Plugin } from '../../../core/public'; +import type { Subscription } from 'rxjs'; +import { Plugin, CoreStart } from '../../../core/public'; export class WorkspacePlugin implements Plugin<{}, {}, {}> { + private coreStart?: CoreStart; + private currentWorkspaceSubscription?: Subscription; + private _changeSavedObjectCurrentWorkspace() { + if (this.coreStart) { + return this.coreStart.workspaces.currentWorkspaceId$.subscribe((currentWorkspaceId) => { + if (currentWorkspaceId) { + this.coreStart?.savedObjects.client.setCurrentWorkspace(currentWorkspaceId); + } + }); + } + } public async setup() { return {}; } - public start() { + public start(core: CoreStart) { + this.coreStart = core; + + this.currentWorkspaceSubscription = this._changeSavedObjectCurrentWorkspace(); return {}; } - public stop() {} + public stop() { + this.currentWorkspaceSubscription?.unsubscribe(); + } } diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index e4ed75bad615..a09e4468a120 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -46,6 +46,8 @@ export class WorkspacePlugin implements Plugin<{}, {}> { client: this.client as IWorkspaceClientImpl, }); + core.capabilities.registerProvider(() => ({ workspaces: { enabled: true } })); + return { client: this.client, }; From d7a9bd4582850bf6c09b576a89e819298003d26c Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 24 Oct 2023 18:26:59 +0800 Subject: [PATCH 02/18] feat: add unit test Signed-off-by: SuZhou-Joe --- .../saved_objects_client.test.ts | 69 +++++++++++++++++++ .../saved_objects/saved_objects_client.ts | 16 ++++- 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/src/core/public/saved_objects/saved_objects_client.test.ts b/src/core/public/saved_objects/saved_objects_client.test.ts index cc3405f246c5..300a9bc52d9d 100644 --- a/src/core/public/saved_objects/saved_objects_client.test.ts +++ b/src/core/public/saved_objects/saved_objects_client.test.ts @@ -329,6 +329,26 @@ describe('SavedObjectsClient', () => { `); }); + test('makes HTTP call with workspaces', () => { + savedObjectsClient.create('index-pattern', attributes, { + workspaces: ['foo'], + }); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/index-pattern", + Object { + "body": "{\\"attributes\\":{\\"foo\\":\\"Foo\\",\\"bar\\":\\"Bar\\"},\\"workspaces\\":[\\"foo\\"]}", + "method": "POST", + "query": Object { + "overwrite": undefined, + }, + }, + ], + ] + `); + }); + test('rejects when HTTP call fails', async () => { http.fetch.mockRejectedValueOnce(new Error('Request failed')); await expect( @@ -386,6 +406,29 @@ describe('SavedObjectsClient', () => { ] `); }); + + test('makes HTTP call with workspaces', () => { + savedObjectsClient.bulkCreate([doc], { + workspaces: ['foo'], + }); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_bulk_create", + Object { + "body": "[{\\"id\\":\\"AVwSwFxtcMV38qjDZoQg\\",\\"type\\":\\"config\\",\\"attributes\\":{\\"title\\":\\"Example title\\"},\\"version\\":\\"foo\\",\\"updated_at\\":\\"${updatedAt}\\"}]", + "method": "POST", + "query": Object { + "overwrite": undefined, + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ] + `); + }); }); describe('#bulk_update', () => { @@ -510,5 +553,31 @@ describe('SavedObjectsClient', () => { ] `); }); + + test('makes HTTP call correctly with workspaces', () => { + const options = { + invalid: true, + workspaces: ['foo'], + }; + + // @ts-expect-error + savedObjectsClient.find(options); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_find", + Object { + "body": undefined, + "method": "GET", + "query": Object { + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ] + `); + }); }); }); diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 61900877c6e3..33ddb5db5f80 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -79,6 +79,7 @@ export interface SavedObjectsBulkCreateObject extends SavedObjectsC export interface SavedObjectsBulkCreateOptions { /** If a document with the given `id` already exists, overwrite it's contents (default=false). */ overwrite?: boolean; + workspaces?: string[]; } /** @public */ @@ -295,11 +296,22 @@ export class SavedObjectsClient { * @returns The result of the create operation containing created saved objects. */ public bulkCreate = ( - objects: SavedObjectsBulkCreateObject[] = [], + objects: Array> = [], options: SavedObjectsBulkCreateOptions = { overwrite: false } ) => { const path = this.getPath(['_bulk_create']); - const query = { overwrite: options.overwrite }; + const query: HttpFetchOptions['query'] = { overwrite: options.overwrite }; + const currentWorkspaceId = this.currentWorkspaceId; + let finalWorkspaces; + if (options.hasOwnProperty('workspaces')) { + finalWorkspaces = options.workspaces; + } else if (typeof currentWorkspaceId === 'string') { + finalWorkspaces = [currentWorkspaceId]; + } + + if (finalWorkspaces) { + query.workspaces = finalWorkspaces; + } const request: ReturnType = this.savedObjectsFetch(path, { method: 'POST', From 8df3b3cf4da82dec029efdbaac42e8c58b025d7b Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 30 Oct 2023 20:35:40 +0800 Subject: [PATCH 03/18] feat: add unit test for each change Signed-off-by: SuZhou-Joe --- .../saved_objects_client.test.ts | 263 ++++++++++++++++++ .../get_sorted_objects_for_export.test.ts | 70 +++++ .../import/resolve_import_errors.test.ts | 4 +- .../lib/fetch_export_by_type_and_search.ts | 4 +- .../public/lib/fetch_export_objects.test.ts | 34 +++ .../public/lib/import_file.test.ts | 46 +++ .../public/lib/resolve_import_errors.test.ts | 83 ++++++ .../saved_objects_table.test.tsx.snap | 3 + .../__snapshots__/flyout.test.tsx.snap | 1 + .../objects_table/components/flyout.test.tsx | 24 ++ .../saved_objects_table.test.tsx | 64 +++++ .../objects_table/saved_objects_table.tsx | 5 - src/plugins/workspace/public/plugin.test.ts | 17 ++ src/plugins/workspace/server/plugin.test.ts | 29 ++ 14 files changed, 640 insertions(+), 7 deletions(-) create mode 100644 src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts create mode 100644 src/plugins/saved_objects_management/public/lib/import_file.test.ts create mode 100644 src/plugins/workspace/public/plugin.test.ts create mode 100644 src/plugins/workspace/server/plugin.test.ts diff --git a/src/core/public/saved_objects/saved_objects_client.test.ts b/src/core/public/saved_objects/saved_objects_client.test.ts index 300a9bc52d9d..b2f6d4afbb7f 100644 --- a/src/core/public/saved_objects/saved_objects_client.test.ts +++ b/src/core/public/saved_objects/saved_objects_client.test.ts @@ -581,3 +581,266 @@ describe('SavedObjectsClient', () => { }); }); }); + +describe('SavedObjectsClientWithWorkspaceSet', () => { + const updatedAt = new Date().toISOString(); + const doc = { + id: 'AVwSwFxtcMV38qjDZoQg', + type: 'config', + attributes: { title: 'Example title' }, + version: 'foo', + updated_at: updatedAt, + }; + + const http = httpServiceMock.createStartContract(); + let savedObjectsClient: SavedObjectsClient; + + beforeEach(() => { + savedObjectsClient = new SavedObjectsClient(http); + savedObjectsClient.setCurrentWorkspace('foo'); + http.fetch.mockClear(); + }); + + describe('#create', () => { + const attributes = { foo: 'Foo', bar: 'Bar' }; + + beforeEach(() => { + http.fetch.mockResolvedValue({ id: 'serverId', type: 'server-type', attributes }); + }); + + test('makes HTTP call with ID', () => { + savedObjectsClient.create('index-pattern', attributes, { id: 'myId' }); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/index-pattern/myId", + Object { + "body": "{\\"attributes\\":{\\"foo\\":\\"Foo\\",\\"bar\\":\\"Bar\\"},\\"workspaces\\":[\\"foo\\"]}", + "method": "POST", + "query": Object { + "overwrite": undefined, + }, + }, + ], + ] + `); + }); + + test('makes HTTP call without ID', () => { + savedObjectsClient.create('index-pattern', attributes); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/index-pattern", + Object { + "body": "{\\"attributes\\":{\\"foo\\":\\"Foo\\",\\"bar\\":\\"Bar\\"},\\"workspaces\\":[\\"foo\\"]}", + "method": "POST", + "query": Object { + "overwrite": undefined, + }, + }, + ], + ] + `); + }); + + test('makes HTTP call with workspaces', () => { + savedObjectsClient.create('index-pattern', attributes, { + workspaces: ['foo'], + }); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/index-pattern", + Object { + "body": "{\\"attributes\\":{\\"foo\\":\\"Foo\\",\\"bar\\":\\"Bar\\"},\\"workspaces\\":[\\"foo\\"]}", + "method": "POST", + "query": Object { + "overwrite": undefined, + }, + }, + ], + ] + `); + }); + }); + + describe('#bulk_create', () => { + beforeEach(() => { + http.fetch.mockResolvedValue({ saved_objects: [doc] }); + }); + + test('makes HTTP call', async () => { + await savedObjectsClient.bulkCreate([doc]); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_bulk_create", + Object { + "body": "[{\\"id\\":\\"AVwSwFxtcMV38qjDZoQg\\",\\"type\\":\\"config\\",\\"attributes\\":{\\"title\\":\\"Example title\\"},\\"version\\":\\"foo\\",\\"updated_at\\":\\"${updatedAt}\\"}]", + "method": "POST", + "query": Object { + "overwrite": false, + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ] + `); + }); + + test('makes HTTP call with overwrite query paramater', async () => { + await savedObjectsClient.bulkCreate([doc], { overwrite: true }); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_bulk_create", + Object { + "body": "[{\\"id\\":\\"AVwSwFxtcMV38qjDZoQg\\",\\"type\\":\\"config\\",\\"attributes\\":{\\"title\\":\\"Example title\\"},\\"version\\":\\"foo\\",\\"updated_at\\":\\"${updatedAt}\\"}]", + "method": "POST", + "query": Object { + "overwrite": true, + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ] + `); + }); + + test('makes HTTP call with workspaces', () => { + savedObjectsClient.bulkCreate([doc], { + workspaces: ['bar'], + }); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_bulk_create", + Object { + "body": "[{\\"id\\":\\"AVwSwFxtcMV38qjDZoQg\\",\\"type\\":\\"config\\",\\"attributes\\":{\\"title\\":\\"Example title\\"},\\"version\\":\\"foo\\",\\"updated_at\\":\\"${updatedAt}\\"}]", + "method": "POST", + "query": Object { + "overwrite": undefined, + "workspaces": Array [ + "bar", + ], + }, + }, + ], + ] + `); + }); + }); + + describe('#bulk_update', () => { + const bulkUpdateDoc = { + id: 'AVwSwFxtcMV38qjDZoQg', + type: 'config', + attributes: { title: 'Example title' }, + version: 'foo', + }; + beforeEach(() => { + http.fetch.mockResolvedValue({ saved_objects: [bulkUpdateDoc] }); + }); + + test('makes HTTP call', async () => { + await savedObjectsClient.bulkUpdate([bulkUpdateDoc]); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_bulk_update", + Object { + "body": "[{\\"id\\":\\"AVwSwFxtcMV38qjDZoQg\\",\\"type\\":\\"config\\",\\"attributes\\":{\\"title\\":\\"Example title\\"},\\"version\\":\\"foo\\"}]", + "method": "PUT", + "query": undefined, + }, + ], + ] + `); + }); + }); + + describe('#find', () => { + const object = { id: 'logstash-*', type: 'index-pattern', title: 'Test' }; + + beforeEach(() => { + http.fetch.mockResolvedValue({ saved_objects: [object], page: 0, per_page: 1, total: 1 }); + }); + + test('makes HTTP call correctly mapping options into snake case query parameters', () => { + const options = { + defaultSearchOperator: 'OR' as const, + fields: ['title'], + hasReference: { id: '1', type: 'reference' }, + page: 10, + perPage: 100, + search: 'what is the meaning of life?|life', + searchFields: ['title^5', 'body'], + sortField: 'sort_field', + type: 'index-pattern', + }; + + savedObjectsClient.find(options); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_find", + Object { + "body": undefined, + "method": "GET", + "query": Object { + "default_search_operator": "OR", + "fields": Array [ + "title", + ], + "has_reference": "{\\"id\\":\\"1\\",\\"type\\":\\"reference\\"}", + "page": 10, + "per_page": 100, + "search": "what is the meaning of life?|life", + "search_fields": Array [ + "title^5", + "body", + ], + "sort_field": "sort_field", + "type": "index-pattern", + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ] + `); + }); + + test('makes HTTP call correctly with workspaces', () => { + const options = { + invalid: true, + workspaces: ['bar'], + }; + + // @ts-expect-error + savedObjectsClient.find(options); + expect(http.fetch.mock.calls).toMatchInlineSnapshot(` + Array [ + Array [ + "/api/saved_objects/_find", + Object { + "body": undefined, + "method": "GET", + "query": Object { + "workspaces": Array [ + "bar", + ], + }, + }, + ], + ] + `); + }); + }); +}); diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts index cf7e1d8246a7..51e987511da2 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts @@ -812,6 +812,76 @@ describe('getSortedObjectsForExport()', () => { `); }); + test('exports selected objects when passed workspaces', async () => { + savedObjectsClient.bulkGet.mockResolvedValueOnce({ + saved_objects: [ + { + id: '2', + type: 'search', + attributes: {}, + references: [ + { + id: '1', + name: 'name', + type: 'index-pattern', + }, + ], + }, + { + id: '1', + type: 'index-pattern', + attributes: {}, + references: [], + }, + ], + }); + await exportSavedObjectsToStream({ + exportSizeLimit: 10000, + savedObjectsClient, + objects: [ + { + type: 'index-pattern', + id: '1', + }, + { + type: 'search', + id: '2', + }, + ], + workspaces: ['foo'], + }); + expect(savedObjectsClient.bulkGet).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + Array [ + Object { + id: 1, + type: index-pattern, + }, + Object { + id: 2, + type: search, + }, + ], + Object { + namespace: undefined, + workspaces: Array [ + foo, + ], + }, + ], + ], + "results": Array [ + Object { + type: return, + value: Promise {}, + }, + ], + } + `); + }); + test('export selected objects throws error when exceeding exportSizeLimit', async () => { const exportOpts = { exportSizeLimit: 1, diff --git a/src/core/server/saved_objects/import/resolve_import_errors.test.ts b/src/core/server/saved_objects/import/resolve_import_errors.test.ts index ef22155f046b..35ca022df276 100644 --- a/src/core/server/saved_objects/import/resolve_import_errors.test.ts +++ b/src/core/server/saved_objects/import/resolve_import_errors.test.ts @@ -242,7 +242,8 @@ describe('#importSavedObjectsFromStream', () => { test('checks conflicts', async () => { const createNewCopies = (Symbol() as unknown) as boolean; const retries = [createRetry()]; - const options = setupOptions(retries, createNewCopies); + const workspaces = ['foo']; + const options = { ...setupOptions(retries, createNewCopies), workspaces }; const collectedObjects = [createObject()]; getMockFn(collectSavedObjects).mockResolvedValue({ errors: [], @@ -257,6 +258,7 @@ describe('#importSavedObjectsFromStream', () => { namespace, retries, createNewCopies, + workspaces, }; expect(checkConflicts).toHaveBeenCalledWith(checkConflictsParams); }); diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts index e5f716347a76..1af8ac210696 100644 --- a/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts @@ -34,10 +34,12 @@ export async function fetchExportByTypeAndSearch( http: HttpStart, types: string[], search: string | undefined, - includeReferencesDeep: boolean = false + includeReferencesDeep: boolean = false, + body?: Record ): Promise { return http.post('/api/saved_objects/_export', { body: JSON.stringify({ + ...body, type: types, search, includeReferencesDeep, diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts new file mode 100644 index 000000000000..d37db062540c --- /dev/null +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts @@ -0,0 +1,34 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { fetchExportObjects } from './fetch_export_objects'; +import { httpServiceMock } from '../../../../core/public/mocks'; + +describe('fetchExportObjects', () => { + it('make http call with body provided', async () => { + const httpClient = httpServiceMock.createStartContract(); + await fetchExportObjects(httpClient, [], false, { + workspaces: ['foo'], + }); + expect(httpClient.post).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + "/api/saved_objects/_export", + Object { + "body": "{\\"workspaces\\":[\\"foo\\"],\\"objects\\":[],\\"includeReferencesDeep\\":false}", + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + } + `); + }); +}); diff --git a/src/plugins/saved_objects_management/public/lib/import_file.test.ts b/src/plugins/saved_objects_management/public/lib/import_file.test.ts new file mode 100644 index 000000000000..e17494ba2b20 --- /dev/null +++ b/src/plugins/saved_objects_management/public/lib/import_file.test.ts @@ -0,0 +1,46 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { importFile } from './import_file'; +import { httpServiceMock } from '../../../../core/public/mocks'; + +describe('importFile', () => { + it('make http call with body provided', async () => { + const httpClient = httpServiceMock.createStartContract(); + const blob = new Blob(['']); + await importFile(httpClient, new File([blob], 'foo.ndjson'), { + overwrite: true, + createNewCopies: false, + workspaces: ['foo'], + }); + expect(httpClient.post).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + "/api/saved_objects/_import", + Object { + "body": FormData {}, + "headers": Object { + "Content-Type": undefined, + }, + "query": Object { + "overwrite": true, + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + } + `); + }); +}); diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts index 428a1c56c50e..8d04099d8265 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts @@ -303,4 +303,87 @@ describe('resolveImportErrors', () => { } `); }); + + test('make http calls with workspaces', async () => { + httpMock.post.mockResolvedValueOnce({ + success: false, + successCount: 0, + errors: [{ type: 'a', id: '1', error: { type: 'conflict' } }], + }); + httpMock.post.mockResolvedValueOnce({ + success: true, + successCount: 1, + successResults: [{ type: 'a', id: '1' }], + }); + getConflictResolutions.mockResolvedValueOnce({}); + getConflictResolutions.mockResolvedValueOnce({ + 'a:1': { retry: true, options: { overwrite: true } }, + }); + await resolveImportErrors({ + http: httpMock, + getConflictResolutions, + state: { + importCount: 0, + unmatchedReferences: [{ existingIndexPatternId: '2', newIndexPatternId: '3', list: [] }], + failedImports: [ + { + obj: { type: 'a', id: '1', meta: {} }, + error: { type: 'missing_references', references: [{ type: 'index-pattern', id: '2' }] }, + }, + ], + importMode: { createNewCopies: false, overwrite: false }, + }, + workspaces: ['foo'], + }); + expect(httpMock.post).toMatchInlineSnapshot(` + [MockFunction] { + "calls": Array [ + Array [ + "/api/saved_objects/_resolve_import_errors", + Object { + "body": FormData {}, + "headers": Object { + "Content-Type": undefined, + }, + "query": Object { + "workspaces": Array [ + "foo", + ], + }, + }, + ], + Array [ + "/api/saved_objects/_resolve_import_errors", + Object { + "body": FormData {}, + "headers": Object { + "Content-Type": undefined, + }, + "query": Object { + "workspaces": Array [ + "foo", + ], + }, + }, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": Promise { + Symbol(async_id_symbol): 8933, + Symbol(trigger_async_id_symbol): 8919, + }, + }, + Object { + "type": "return", + "value": Promise { + Symbol(async_id_symbol): 8941, + Symbol(trigger_async_id_symbol): 8938, + }, + }, + ], + } + `); + }); }); 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 d18762f4912f..e872e970a7f6 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 @@ -231,6 +231,9 @@ exports[`SavedObjectsTable should render normally 1`] = ` "edit": false, "read": true, }, + "workspaces": Object { + "enabled": false, + }, }, "currentAppId$": Observable { "_isScalar": false, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap index 14fe1fbabd88..fcc66c2ad17c 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap @@ -246,6 +246,7 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` }, ], }, + "workspaces": undefined, }, ], ], diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index 575741708f1e..444f4e93fb3b 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -184,6 +184,29 @@ describe('Flyout', () => { ); }); + it('should call importFile / resolveImportErrors with workspaces', async () => { + const component = shallowRender({ ...defaultProps, workspaces: ['foo'] }); + + // Ensure all promises resolve + await new Promise((resolve) => process.nextTick(resolve)); + // Ensure the state changes are reflected + component.update(); + + await component.instance().import(); + expect(importFileMock.mock.calls[0][2]).toMatchInlineSnapshot(` + Object { + "createNewCopies": false, + "overwrite": true, + "workspaces": Array [ + "foo", + ], + } + `); + + await component.instance().resolveImportErrors(); + expect(resolveImportErrorsMock.mock.calls[0][0].workspaces).toEqual(['foo']); + }); + describe('conflicts', () => { beforeEach(() => { importFileMock.mockImplementation(() => ({ @@ -206,6 +229,7 @@ describe('Flyout', () => { }, ], })); + resolveImportErrorsMock.mockClear(); resolveImportErrorsMock.mockImplementation(() => ({ status: 'success', importCount: 1, 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 74bd973b9cc3..a0a6329ac5e0 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 @@ -409,6 +409,70 @@ describe('SavedObjectsTable', () => { title: 'Your file is downloading in the background', }); }); + + it('should make modules call with workspace', async () => { + getSavedObjectCountsMock.mockClear(); + findObjectsMock.mockClear(); + // @ts-expect-error + defaultProps.applications.capabilities.workspaces.enabled = true; + const mockSelectedSavedObjects = [ + { id: '1', type: 'index-pattern' }, + { id: '3', type: 'dashboard' }, + ] as SavedObjectWithMetadata[]; + + const mockSavedObjects = mockSelectedSavedObjects.map((obj) => ({ + _id: obj.id, + _type: obj.type, + _source: {}, + })); + + const mockSavedObjectsClient = { + ...defaultProps.savedObjectsClient, + bulkGet: jest.fn().mockImplementation(() => ({ + savedObjects: mockSavedObjects, + })), + }; + + const workspacesStart = workspacesServiceMock.createStartContract(); + workspacesStart.currentWorkspaceId$.next('foo'); + + const component = shallowRender({ + savedObjectsClient: mockSavedObjectsClient, + workspaces: workspacesStart, + }); + + // 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().onExport(true); + await component.instance().onExportAll(); + + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, { + workspaces: ['foo'], + }); + expect(fetchExportByTypeAndSearchMock).toHaveBeenCalledWith( + http, + ['index-pattern', 'visualization', 'dashboard', 'search'], + undefined, + true, + { + workspaces: ['foo'], + } + ); + expect( + getSavedObjectCountsMock.mock.calls.every((item) => item[1].workspaces[0] === 'foo') + ).toEqual(true); + expect(findObjectsMock.mock.calls.every((item) => item[1].workspaces[0] === 'foo')).toEqual( + true + ); + // @ts-expect-error + defaultProps.applications.capabilities.workspaces.enabled = false; + }); }); describe('import', () => { 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 fdec01e0f29d..8f45c5fe3946 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 @@ -67,7 +67,6 @@ import { NotificationsStart, ApplicationStart, } from 'src/core/public'; -import { Subscription } from 'rxjs'; import { RedirectAppLinks } from '../../../../opensearch_dashboards_react/public'; import { IndexPatternsContract } from '../../../../data/public'; import { @@ -146,8 +145,6 @@ export interface SavedObjectsTableState { export class SavedObjectsTable extends Component { private _isMounted = false; - private currentWorkspaceIdSubscription?: Subscription; - private workspacesSubscription?: Subscription; constructor(props: SavedObjectsTableProps) { super(props); @@ -849,8 +846,6 @@ export class SavedObjectsTable extends Component { + it('#call savedObjectsClient.setCurrentWorkspace when current workspace id changed', () => { + const workspacePlugin = new WorkspacePlugin(); + const coreStart = coreMock.createStart(); + workspacePlugin.start(coreStart); + coreStart.workspaces.currentWorkspaceId$.next('foo'); + expect(coreStart.savedObjects.client.setCurrentWorkspace).toHaveBeenCalledWith('foo'); + }); +}); diff --git a/src/plugins/workspace/server/plugin.test.ts b/src/plugins/workspace/server/plugin.test.ts new file mode 100644 index 000000000000..32b4b23a9f59 --- /dev/null +++ b/src/plugins/workspace/server/plugin.test.ts @@ -0,0 +1,29 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { coreMock } from '../../../core/server/mocks'; +import { WorkspacePlugin } from './plugin'; + +describe('Workspace server plugin', () => { + it('#setup', async () => { + let value; + const setupMock = coreMock.createSetup(); + const initializerContextConfigMock = coreMock.createPluginInitializerContext({ + workspace: { + enabled: true, + }, + }); + setupMock.capabilities.registerProvider.mockImplementationOnce((fn) => (value = fn())); + const workspacePlugin = new WorkspacePlugin(initializerContextConfigMock); + await workspacePlugin.setup(setupMock); + expect(value).toMatchInlineSnapshot(` + Object { + "workspaces": Object { + "enabled": true, + }, + } + `); + }); +}); From 79fb5636823be03acae320e036ad66c61e299956 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 30 Oct 2023 20:44:57 +0800 Subject: [PATCH 04/18] fix: update snapshot of unit test Signed-off-by: SuZhou-Joe --- .../__snapshots__/dashboard_listing.test.tsx.snap | 5 +++++ .../__snapshots__/dashboard_top_nav.test.tsx.snap | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap index c9ffe147e5f8..dfd9d3d3e378 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_listing/__snapshots__/dashboard_listing.test.tsx.snap @@ -932,6 +932,7 @@ exports[`dashboard listing hideWriteControls 1`] = ` "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -2064,6 +2065,7 @@ exports[`dashboard listing render table listing with initial filters from URL 1` "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -3257,6 +3259,7 @@ exports[`dashboard listing renders call to action when no dashboards exist 1`] = "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -4450,6 +4453,7 @@ exports[`dashboard listing renders table rows 1`] = ` "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -5643,6 +5647,7 @@ exports[`dashboard listing renders warning when listingLimit is exceeded 1`] = ` "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, diff --git a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap index 1954051c9474..54b40858b4f1 100644 --- a/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap +++ b/src/plugins/dashboard/public/application/components/dashboard_top_nav/__snapshots__/dashboard_top_nav.test.tsx.snap @@ -810,6 +810,7 @@ exports[`Dashboard top nav render in embed mode 1`] = ` "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -1767,6 +1768,7 @@ exports[`Dashboard top nav render in embed mode, and force hide filter bar 1`] = "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -2724,6 +2726,7 @@ exports[`Dashboard top nav render in embed mode, components can be forced show b "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -3681,6 +3684,7 @@ exports[`Dashboard top nav render in full screen mode with appended URL param bu "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -4638,6 +4642,7 @@ exports[`Dashboard top nav render in full screen mode, no componenets should be "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, @@ -5595,6 +5600,7 @@ exports[`Dashboard top nav render with all components 1`] = ` "delete": [MockFunction], "find": [MockFunction], "get": [MockFunction], + "setCurrentWorkspace": [MockFunction], "update": [MockFunction], }, }, From 31ae62d2901ba462cd70283732e01a30b174feb2 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 31 Oct 2023 07:50:25 +0800 Subject: [PATCH 05/18] fix: unit test Signed-off-by: SuZhou-Joe --- .../public/lib/resolve_import_errors.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts index 8d04099d8265..9240302e90d3 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts @@ -335,7 +335,7 @@ describe('resolveImportErrors', () => { }, workspaces: ['foo'], }); - expect(httpMock.post).toMatchInlineSnapshot(` + expect(httpMock.post.mock.calls).toMatchInlineSnapshot(` [MockFunction] { "calls": Array [ Array [ From 548718780aecdf6e771c02a152db068cfa36797c Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 31 Oct 2023 07:53:57 +0800 Subject: [PATCH 06/18] fix: unit test Signed-off-by: SuZhou-Joe --- .../public/lib/resolve_import_errors.test.ts | 62 +++++++------------ 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts index 9240302e90d3..ee4a684d5702 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts @@ -336,54 +336,36 @@ describe('resolveImportErrors', () => { workspaces: ['foo'], }); expect(httpMock.post.mock.calls).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - "/api/saved_objects/_resolve_import_errors", - Object { - "body": FormData {}, - "headers": Object { - "Content-Type": undefined, - }, - "query": Object { - "workspaces": Array [ - "foo", - ], - }, + Array [ + Array [ + "/api/saved_objects/_resolve_import_errors", + Object { + "body": FormData {}, + "headers": Object { + "Content-Type": undefined, }, - ], - Array [ - "/api/saved_objects/_resolve_import_errors", - Object { - "body": FormData {}, - "headers": Object { - "Content-Type": undefined, - }, - "query": Object { - "workspaces": Array [ - "foo", - ], - }, + "query": Object { + "workspaces": Array [ + "foo", + ], }, - ], + }, ], - "results": Array [ + Array [ + "/api/saved_objects/_resolve_import_errors", Object { - "type": "return", - "value": Promise { - Symbol(async_id_symbol): 8933, - Symbol(trigger_async_id_symbol): 8919, + "body": FormData {}, + "headers": Object { + "Content-Type": undefined, }, - }, - Object { - "type": "return", - "value": Promise { - Symbol(async_id_symbol): 8941, - Symbol(trigger_async_id_symbol): 8938, + "query": Object { + "workspaces": Array [ + "foo", + ], }, }, ], - } + ] `); }); }); From 041468be76a4a09a9ee046758656e926073f0c04 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Thu, 29 Feb 2024 16:40:32 +0800 Subject: [PATCH 07/18] fix: unit test Signed-off-by: SuZhou-Joe --- .../management_section/objects_table/components/flyout.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index 444f4e93fb3b..e5c6732cb5f5 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -195,7 +195,7 @@ describe('Flyout', () => { await component.instance().import(); expect(importFileMock.mock.calls[0][2]).toMatchInlineSnapshot(` Object { - "createNewCopies": false, + "createNewCopies": true, "overwrite": true, "workspaces": Array [ "foo", From 07d1db586b8b2676c233d593a3e498cf76268193 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Mon, 4 Mar 2024 16:53:04 +0800 Subject: [PATCH 08/18] feat: update snapshot Signed-off-by: SuZhou-Joe --- .../saved_objects/export/get_sorted_objects_for_export.test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts index 51e987511da2..da477604c029 100644 --- a/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts +++ b/src/core/server/saved_objects/export/get_sorted_objects_for_export.test.ts @@ -866,9 +866,6 @@ describe('getSortedObjectsForExport()', () => { ], Object { namespace: undefined, - workspaces: Array [ - foo, - ], }, ], ], From dbca9e78fe5b59206c7dc3b7848c548c49e7cc89 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Tue, 5 Mar 2024 10:29:06 +0800 Subject: [PATCH 09/18] revert: saved object management changes Signed-off-by: SuZhou-Joe --- .../lib/fetch_export_by_type_and_search.ts | 4 +- .../public/lib/fetch_export_objects.test.ts | 34 -------- .../public/lib/fetch_export_objects.ts | 4 +- .../public/lib/get_saved_object_counts.ts | 1 - .../public/lib/import_file.test.ts | 46 ----------- .../public/lib/import_file.ts | 4 +- .../public/lib/resolve_import_errors.test.ts | 65 --------------- .../public/lib/resolve_import_errors.ts | 10 +-- .../saved_objects_table.test.tsx.snap | 3 - .../__snapshots__/flyout.test.tsx.snap | 1 - .../objects_table/components/flyout.test.tsx | 24 ------ .../objects_table/components/flyout.tsx | 15 +--- .../components/import_mode_control.tsx | 1 - .../saved_objects_table.test.tsx | 81 +------------------ .../objects_table/saved_objects_table.tsx | 60 ++------------ .../saved_objects_table_page.tsx | 1 - .../server/routes/find.ts | 4 - .../server/routes/scroll_count.ts | 7 -- src/plugins/workspace/server/plugin.test.ts | 29 ------- src/plugins/workspace/server/plugin.ts | 2 - 20 files changed, 21 insertions(+), 375 deletions(-) delete mode 100644 src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts delete mode 100644 src/plugins/saved_objects_management/public/lib/import_file.test.ts delete mode 100644 src/plugins/workspace/server/plugin.test.ts diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts index 1af8ac210696..e5f716347a76 100644 --- a/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_by_type_and_search.ts @@ -34,12 +34,10 @@ export async function fetchExportByTypeAndSearch( http: HttpStart, types: string[], search: string | undefined, - includeReferencesDeep: boolean = false, - body?: Record + includeReferencesDeep: boolean = false ): Promise { return http.post('/api/saved_objects/_export', { body: JSON.stringify({ - ...body, type: types, search, includeReferencesDeep, diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts deleted file mode 100644 index d37db062540c..000000000000 --- a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { fetchExportObjects } from './fetch_export_objects'; -import { httpServiceMock } from '../../../../core/public/mocks'; - -describe('fetchExportObjects', () => { - it('make http call with body provided', async () => { - const httpClient = httpServiceMock.createStartContract(); - await fetchExportObjects(httpClient, [], false, { - workspaces: ['foo'], - }); - expect(httpClient.post).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - "/api/saved_objects/_export", - Object { - "body": "{\\"workspaces\\":[\\"foo\\"],\\"objects\\":[],\\"includeReferencesDeep\\":false}", - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - } - `); - }); -}); diff --git a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts index 43afcfec3056..b2e2ea0f9165 100644 --- a/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts +++ b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts @@ -33,12 +33,10 @@ import { HttpStart } from 'src/core/public'; export async function fetchExportObjects( http: HttpStart, objects: any[], - includeReferencesDeep: boolean = false, - body?: Record + includeReferencesDeep: boolean = false ): Promise { return http.post('/api/saved_objects/_export', { body: JSON.stringify({ - ...body, objects, includeReferencesDeep, }), 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..6eaaac7d35f2 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 @@ -34,7 +34,6 @@ export interface SavedObjectCountOptions { typesToInclude: string[]; namespacesToInclude?: string[]; searchString?: string; - workspaces?: string[]; } export async function getSavedObjectCounts( diff --git a/src/plugins/saved_objects_management/public/lib/import_file.test.ts b/src/plugins/saved_objects_management/public/lib/import_file.test.ts deleted file mode 100644 index e17494ba2b20..000000000000 --- a/src/plugins/saved_objects_management/public/lib/import_file.test.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { importFile } from './import_file'; -import { httpServiceMock } from '../../../../core/public/mocks'; - -describe('importFile', () => { - it('make http call with body provided', async () => { - const httpClient = httpServiceMock.createStartContract(); - const blob = new Blob(['']); - await importFile(httpClient, new File([blob], 'foo.ndjson'), { - overwrite: true, - createNewCopies: false, - workspaces: ['foo'], - }); - expect(httpClient.post).toMatchInlineSnapshot(` - [MockFunction] { - "calls": Array [ - Array [ - "/api/saved_objects/_import", - Object { - "body": FormData {}, - "headers": Object { - "Content-Type": undefined, - }, - "query": Object { - "overwrite": true, - "workspaces": Array [ - "foo", - ], - }, - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - } - `); - }); -}); diff --git a/src/plugins/saved_objects_management/public/lib/import_file.ts b/src/plugins/saved_objects_management/public/lib/import_file.ts index bcf1b6911b0f..3753a8251e10 100644 --- a/src/plugins/saved_objects_management/public/lib/import_file.ts +++ b/src/plugins/saved_objects_management/public/lib/import_file.ts @@ -40,12 +40,12 @@ interface ImportResponse { export async function importFile( http: HttpStart, file: File, - { createNewCopies, overwrite, workspaces }: ImportMode, + { createNewCopies, overwrite }: ImportMode, selectedDataSourceId?: string ) { const formData = new FormData(); formData.append('file', file); - const query = createNewCopies ? { createNewCopies, workspaces } : { overwrite, workspaces }; + const query = createNewCopies ? { createNewCopies } : { overwrite }; if (selectedDataSourceId) { query.dataSourceId = selectedDataSourceId; } diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts index ee4a684d5702..428a1c56c50e 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.test.ts @@ -303,69 +303,4 @@ describe('resolveImportErrors', () => { } `); }); - - test('make http calls with workspaces', async () => { - httpMock.post.mockResolvedValueOnce({ - success: false, - successCount: 0, - errors: [{ type: 'a', id: '1', error: { type: 'conflict' } }], - }); - httpMock.post.mockResolvedValueOnce({ - success: true, - successCount: 1, - successResults: [{ type: 'a', id: '1' }], - }); - getConflictResolutions.mockResolvedValueOnce({}); - getConflictResolutions.mockResolvedValueOnce({ - 'a:1': { retry: true, options: { overwrite: true } }, - }); - await resolveImportErrors({ - http: httpMock, - getConflictResolutions, - state: { - importCount: 0, - unmatchedReferences: [{ existingIndexPatternId: '2', newIndexPatternId: '3', list: [] }], - failedImports: [ - { - obj: { type: 'a', id: '1', meta: {} }, - error: { type: 'missing_references', references: [{ type: 'index-pattern', id: '2' }] }, - }, - ], - importMode: { createNewCopies: false, overwrite: false }, - }, - workspaces: ['foo'], - }); - expect(httpMock.post.mock.calls).toMatchInlineSnapshot(` - Array [ - Array [ - "/api/saved_objects/_resolve_import_errors", - Object { - "body": FormData {}, - "headers": Object { - "Content-Type": undefined, - }, - "query": Object { - "workspaces": Array [ - "foo", - ], - }, - }, - ], - Array [ - "/api/saved_objects/_resolve_import_errors", - Object { - "body": FormData {}, - "headers": Object { - "Content-Type": undefined, - }, - "query": Object { - "workspaces": Array [ - "foo", - ], - }, - }, - ], - ] - `); - }); }); diff --git a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts index 2bcaf6e9f6cf..585102ee5b8e 100644 --- a/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts +++ b/src/plugins/saved_objects_management/public/lib/resolve_import_errors.ts @@ -90,13 +90,12 @@ async function callResolveImportErrorsApi( file: File, retries: any, createNewCopies: boolean, - selectedDataSourceId?: string, - workspaces?: string[] + selectedDataSourceId?: string ): Promise { const formData = new FormData(); formData.append('file', file); formData.append('retries', JSON.stringify(retries)); - const query = createNewCopies ? { createNewCopies, workspaces } : { workspaces }; + const query = createNewCopies ? { createNewCopies } : {}; if (selectedDataSourceId) { query.dataSourceId = selectedDataSourceId; } @@ -173,7 +172,6 @@ export async function resolveImportErrors({ getConflictResolutions, state, selectedDataSourceId, - workspaces, }: { http: HttpStart; getConflictResolutions: ( @@ -188,7 +186,6 @@ export async function resolveImportErrors({ importMode: { createNewCopies: boolean; overwrite: boolean }; }; selectedDataSourceId: string; - workspaces?: string[]; }) { const retryDecisionCache = new Map(); const replaceReferencesCache = new Map(); @@ -278,8 +275,7 @@ export async function resolveImportErrors({ file!, retries, createNewCopies, - selectedDataSourceId, - workspaces + selectedDataSourceId ); importCount = response.successCount; // reset the success count since we retry all successful results each time failedImports = []; 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 e872e970a7f6..d18762f4912f 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 @@ -231,9 +231,6 @@ exports[`SavedObjectsTable should render normally 1`] = ` "edit": false, "read": true, }, - "workspaces": Object { - "enabled": false, - }, }, "currentAppId$": Observable { "_isScalar": false, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap index fcc66c2ad17c..14fe1fbabd88 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/__snapshots__/flyout.test.tsx.snap @@ -246,7 +246,6 @@ exports[`Flyout conflicts should allow conflict resolution 2`] = ` }, ], }, - "workspaces": undefined, }, ], ], diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx index e5c6732cb5f5..575741708f1e 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.test.tsx @@ -184,29 +184,6 @@ describe('Flyout', () => { ); }); - it('should call importFile / resolveImportErrors with workspaces', async () => { - const component = shallowRender({ ...defaultProps, workspaces: ['foo'] }); - - // Ensure all promises resolve - await new Promise((resolve) => process.nextTick(resolve)); - // Ensure the state changes are reflected - component.update(); - - await component.instance().import(); - expect(importFileMock.mock.calls[0][2]).toMatchInlineSnapshot(` - Object { - "createNewCopies": true, - "overwrite": true, - "workspaces": Array [ - "foo", - ], - } - `); - - await component.instance().resolveImportErrors(); - expect(resolveImportErrorsMock.mock.calls[0][0].workspaces).toEqual(['foo']); - }); - describe('conflicts', () => { beforeEach(() => { importFileMock.mockImplementation(() => ({ @@ -229,7 +206,6 @@ describe('Flyout', () => { }, ], })); - resolveImportErrorsMock.mockClear(); resolveImportErrorsMock.mockImplementation(() => ({ status: 'success', importCount: 1, diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx index 361648881f93..586a573ffb53 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/flyout.tsx @@ -97,7 +97,6 @@ export interface FlyoutProps { hideLocalCluster: boolean; savedObjects: SavedObjectsClientContract; notifications: NotificationsStart; - workspaces?: string[]; } export interface FlyoutState { @@ -190,21 +189,13 @@ export class Flyout extends Component { * Does the initial import of a file, resolveImportErrors then handles errors and retries */ import = async () => { - const { http, workspaces } = this.props; + const { http } = this.props; const { file, importMode, selectedDataSourceId } = this.state; this.setState({ status: 'loading', error: undefined }); // Import the file try { - const response = await importFile( - http, - file!, - { - ...importMode, - workspaces, - }, - selectedDataSourceId - ); + const response = await importFile(http, file!, importMode, selectedDataSourceId); this.setState(processImportResponse(response), () => { // Resolve import errors right away if there's no index patterns to match // This will ask about overwriting each object, etc @@ -260,7 +251,6 @@ export class Flyout extends Component { status: 'loading', loadingMessage: undefined, }); - const { workspaces } = this.props; try { const updatedState = await resolveImportErrors({ @@ -268,7 +258,6 @@ export class Flyout extends Component { state: this.state, getConflictResolutions: this.getConflictResolutions, selectedDataSourceId: this.state.selectedDataSourceId, - workspaces, }); this.setState(updatedState); } catch (e) { diff --git a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx index 49974c53b3d7..d0c800553996 100644 --- a/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx +++ b/src/plugins/saved_objects_management/public/management_section/objects_table/components/import_mode_control.tsx @@ -52,7 +52,6 @@ export interface ImportModeControlProps { export interface ImportMode { createNewCopies: boolean; overwrite: boolean; - workspaces?: string[]; } const createNewCopiesDisabled = { 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 a0a6329ac5e0..5a6bf0713d95 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 @@ -48,7 +48,6 @@ import { notificationServiceMock, savedObjectsServiceMock, applicationServiceMock, - workspacesServiceMock, } from '../../../../../core/public/mocks'; import { dataPluginMock } from '../../../../data/public/mocks'; import { serviceRegistryMock } from '../../services/service_registry.mock'; @@ -103,7 +102,6 @@ describe('SavedObjectsTable', () => { let notifications: ReturnType; let savedObjects: ReturnType; let search: ReturnType['search']; - let workspaces: ReturnType; const shallowRender = (overrides: Partial = {}) => { return (shallowWithI18nProvider( @@ -123,7 +121,6 @@ describe('SavedObjectsTable', () => { notifications = notificationServiceMock.createStartContract(); savedObjects = savedObjectsServiceMock.createStartContract(); search = dataPluginMock.createStartContract().search; - workspaces = workspacesServiceMock.createStartContract(); const applications = applicationServiceMock.createStartContract(); applications.capabilities = { @@ -135,9 +132,6 @@ describe('SavedObjectsTable', () => { edit: false, delete: false, }, - workspaces: { - enabled: false, - }, }; http.post.mockResolvedValue([]); @@ -160,7 +154,6 @@ describe('SavedObjectsTable', () => { savedObjectsClient: savedObjects.client, indexPatterns: dataPluginMock.createStartContract().indexPatterns, http, - workspaces, overlays, notifications, applications, @@ -286,7 +279,7 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, {}); + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); @@ -329,7 +322,7 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, {}); + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); expect(notifications.toasts.addWarning).toHaveBeenCalledWith({ title: 'Your file is downloading in the background. ' + @@ -370,8 +363,7 @@ describe('SavedObjectsTable', () => { http, allowedTypes, undefined, - true, - {} + true ); expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ @@ -401,78 +393,13 @@ describe('SavedObjectsTable', () => { http, allowedTypes, 'test*', - true, - {} + true ); expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); }); - - it('should make modules call with workspace', async () => { - getSavedObjectCountsMock.mockClear(); - findObjectsMock.mockClear(); - // @ts-expect-error - defaultProps.applications.capabilities.workspaces.enabled = true; - const mockSelectedSavedObjects = [ - { id: '1', type: 'index-pattern' }, - { id: '3', type: 'dashboard' }, - ] as SavedObjectWithMetadata[]; - - const mockSavedObjects = mockSelectedSavedObjects.map((obj) => ({ - _id: obj.id, - _type: obj.type, - _source: {}, - })); - - const mockSavedObjectsClient = { - ...defaultProps.savedObjectsClient, - bulkGet: jest.fn().mockImplementation(() => ({ - savedObjects: mockSavedObjects, - })), - }; - - const workspacesStart = workspacesServiceMock.createStartContract(); - workspacesStart.currentWorkspaceId$.next('foo'); - - const component = shallowRender({ - savedObjectsClient: mockSavedObjectsClient, - workspaces: workspacesStart, - }); - - // 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().onExport(true); - await component.instance().onExportAll(); - - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, { - workspaces: ['foo'], - }); - expect(fetchExportByTypeAndSearchMock).toHaveBeenCalledWith( - http, - ['index-pattern', 'visualization', 'dashboard', 'search'], - undefined, - true, - { - workspaces: ['foo'], - } - ); - expect( - getSavedObjectCountsMock.mock.calls.every((item) => item[1].workspaces[0] === 'foo') - ).toEqual(true); - expect(findObjectsMock.mock.calls.every((item) => item[1].workspaces[0] === 'foo')).toEqual( - true - ); - // @ts-expect-error - defaultProps.applications.capabilities.workspaces.enabled = false; - }); }); describe('import', () => { 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 8f45c5fe3946..955482cc0676 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 @@ -61,7 +61,6 @@ import { FormattedMessage } from '@osd/i18n/react'; import { SavedObjectsClientContract, SavedObjectsFindOptions, - WorkspacesStart, HttpStart, OverlayStart, NotificationsStart, @@ -107,7 +106,6 @@ export interface SavedObjectsTableProps { savedObjectsClient: SavedObjectsClientContract; indexPatterns: IndexPatternsContract; http: HttpStart; - workspaces: WorkspacesStart; search: DataPublicPluginStart['search']; overlays: OverlayStart; notifications: NotificationsStart; @@ -139,8 +137,6 @@ export interface SavedObjectsTableState { exportAllOptions: ExportAllOption[]; exportAllSelectedOptions: Record; isIncludeReferencesDeepChecked: boolean; - currentWorkspaceId: string | null; - workspaceEnabled: boolean; } export class SavedObjectsTable extends Component { @@ -171,35 +167,9 @@ export class SavedObjectsTable extends Component( - obj: T - ): T | Omit { - const { workspaces, ...others } = obj; - if (workspaces) { - return obj; - } - return others; - } - componentDidMount() { this._isMounted = true; this.fetchSavedObjects(); @@ -219,11 +189,10 @@ export class SavedObjectsTable extends Component ns.id) || []; - const filteredCountOptions: SavedObjectCountOptions = this.formatWorkspaceIdParams({ + const filteredCountOptions: SavedObjectCountOptions = { typesToInclude: filteredTypes, searchString: queryText, - workspaces: this.workspaceIdQuery, - }); + }; if (availableNamespaces.length) { const filteredNamespaces = filterQuery(availableNamespaces, visibleNamespaces); @@ -252,11 +221,10 @@ export class SavedObjectsTable extends Component ns.id) || []; if (availableNamespaces.length) { @@ -438,14 +405,7 @@ export class SavedObjectsTable extends Component().concat(req.query.workspaces) : undefined, }); const savedObjects = await Promise.all( diff --git a/src/plugins/saved_objects_management/server/routes/scroll_count.ts b/src/plugins/saved_objects_management/server/routes/scroll_count.ts index 221d39392842..63233748a896 100644 --- a/src/plugins/saved_objects_management/server/routes/scroll_count.ts +++ b/src/plugins/saved_objects_management/server/routes/scroll_count.ts @@ -41,7 +41,6 @@ export const registerScrollForCountRoute = (router: IRouter) => { typesToInclude: schema.arrayOf(schema.string()), namespacesToInclude: schema.maybe(schema.arrayOf(schema.string())), searchString: schema.maybe(schema.string()), - workspaces: schema.maybe(schema.arrayOf(schema.string())), }), }, }, @@ -56,8 +55,6 @@ export const registerScrollForCountRoute = (router: IRouter) => { perPage: 1000, }; - const requestHasWorkspaces = Array.isArray(req.body.workspaces) && req.body.workspaces.length; - const requestHasNamespaces = Array.isArray(req.body.namespacesToInclude) && req.body.namespacesToInclude.length; @@ -66,10 +63,6 @@ export const registerScrollForCountRoute = (router: IRouter) => { findOptions.namespaces = req.body.namespacesToInclude; } - if (requestHasWorkspaces) { - findOptions.workspaces = req.body.workspaces; - } - if (req.body.searchString) { findOptions.search = `${req.body.searchString}*`; findOptions.searchFields = ['title']; diff --git a/src/plugins/workspace/server/plugin.test.ts b/src/plugins/workspace/server/plugin.test.ts deleted file mode 100644 index 32b4b23a9f59..000000000000 --- a/src/plugins/workspace/server/plugin.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { coreMock } from '../../../core/server/mocks'; -import { WorkspacePlugin } from './plugin'; - -describe('Workspace server plugin', () => { - it('#setup', async () => { - let value; - const setupMock = coreMock.createSetup(); - const initializerContextConfigMock = coreMock.createPluginInitializerContext({ - workspace: { - enabled: true, - }, - }); - setupMock.capabilities.registerProvider.mockImplementationOnce((fn) => (value = fn())); - const workspacePlugin = new WorkspacePlugin(initializerContextConfigMock); - await workspacePlugin.setup(setupMock); - expect(value).toMatchInlineSnapshot(` - Object { - "workspaces": Object { - "enabled": true, - }, - } - `); - }); -}); diff --git a/src/plugins/workspace/server/plugin.ts b/src/plugins/workspace/server/plugin.ts index a09e4468a120..e4ed75bad615 100644 --- a/src/plugins/workspace/server/plugin.ts +++ b/src/plugins/workspace/server/plugin.ts @@ -46,8 +46,6 @@ export class WorkspacePlugin implements Plugin<{}, {}> { client: this.client as IWorkspaceClientImpl, }); - core.capabilities.registerProvider(() => ({ workspaces: { enabled: true } })); - return { client: this.client, }; From dfd4899cc199eabeb48e29634c3a5c3de73ae995 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Wed, 6 Mar 2024 18:26:36 +0800 Subject: [PATCH 10/18] feat: add CHANGELOG Signed-off-by: SuZhou-Joe --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b07ea1dde7b..6fe64eccfe54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,17 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Discover] Enhanced the data source selector with added sorting functionality ([#5609](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5609)) - [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756)) - [Multiple Datasource] Add datasource picker to import saved object flyout when multiple data source is enabled ([#5781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5781)) +- [Multiple Datasource] Add interfaces to register add-on authentication method from plug-in module ([#5851](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5851)) +- [Multiple Datasource] Able to Hide "Local Cluster" option from datasource DropDown ([#5827](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5827)) +- [Multiple Datasource] Add api registry and allow it to be added into client config in data source plugin ([#5895](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5895)) +- [Multiple Datasource] Concatenate data source name with index pattern name and change delimiter to double colon ([#5907](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5907)) +- [Multiple Datasource] Refactor client and legacy client to use authentication registry ([#5881](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5881)) +- [Multiple Datasource] Improved error handling for the search API when a null value is passed for the dataSourceId ([#5882](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5882)) +- [Multiple Datasource] Hide/Show authentication method in multi data source plugin based on configuration ([#5916](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5916)) +- [[Dynamic Configurations] Add support for dynamic application configurations ([#5855](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5855)) +- [Workspace] Optional workspaces params in repository ([#5949](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5949)) +- [Multiple Datasource] Refactoring create and edit form to use authentication registry ([#6002](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6002)) +- [Workspace] Consume workspace id in saved object client ([#6014](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6014)) ### 🐛 Bug Fixes From 732f36d1df8953677f32c109488765a8dd988a22 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 10:42:18 +0800 Subject: [PATCH 11/18] feat: address some comment Signed-off-by: SuZhou-Joe --- CHANGELOG.md | 3 ++- .../saved_objects/saved_objects_client.ts | 22 ++++++++----------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe64eccfe54..59a694737a2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Handle form values(request payload) if the selected type is available in the authentication registry ([#6049](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6049)) - [Multiple Datasource] Add Vega support to MDS by specifying a data source name in the Vega spec ([#5975](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5975)) +- [Workspace] Consume workspace id in saved object client ([#6014](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6014)) + ### 🐛 Bug Fixes - [BUG][Discover] Allow saved sort from search embeddable to load in Dashboard ([#5934](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5934)) @@ -106,7 +108,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [[Dynamic Configurations] Add support for dynamic application configurations ([#5855](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5855)) - [Workspace] Optional workspaces params in repository ([#5949](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5949)) - [Multiple Datasource] Refactoring create and edit form to use authentication registry ([#6002](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6002)) -- [Workspace] Consume workspace id in saved object client ([#6014](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6014)) ### 🐛 Bug Fixes diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 33ddb5db5f80..82b1ec5b882b 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -186,8 +186,7 @@ export class SavedObjectsClient { private http: HttpSetup; private batchQueue: BatchQueueEntry[]; /** - * if currentWorkspaceId is undefined, it means - * we should not carry out workspace info when doing any operation. + * The currentWorkspaceId may be undefiend when workspace plugin is not enabled. */ private currentWorkspaceId: string | undefined; @@ -392,17 +391,14 @@ export class SavedObjectsClient { finalWorkspaces = Array.from(new Set([currentWorkspaceId])); } - const renamedQuery = renameKeys, any>( - renameMap, - { - ...options, - ...(finalWorkspaces - ? { - workspaces: finalWorkspaces, - } - : {}), - } - ); + const renamedQuery = renameKeys(renameMap, { + ...options, + ...(finalWorkspaces + ? { + workspaces: finalWorkspaces, + } + : {}), + }); const query = pick.apply(null, [renamedQuery, ...Object.values(renameMap)]) as Partial< Record >; From 58d31b286807fd05cfe5974edce9ef256c06edb2 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 10:54:53 +0800 Subject: [PATCH 12/18] feat: address comment Signed-off-by: SuZhou-Joe --- .../saved_objects/saved_objects_client.ts | 45 ++++++++++--------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 82b1ec5b882b..49c972ec0903 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -190,6 +190,25 @@ export class SavedObjectsClient { */ private currentWorkspaceId: string | undefined; + /** + * Check if workspaces field present in given options, if so, overwrite the current workspace id. + * @param options + * @returns + */ + private formatWorkspacesParams(options: { + workspaces?: SavedObjectsCreateOptions['workspaces']; + }) { + const currentWorkspaceId = this.currentWorkspaceId; + let finalWorkspaces; + if (options.hasOwnProperty('workspaces')) { + finalWorkspaces = options.workspaces; + } else if (typeof currentWorkspaceId === 'string') { + finalWorkspaces = [currentWorkspaceId]; + } + + return finalWorkspaces; + } + /** * Throttled processing of get requests into bulk requests at 100ms interval */ @@ -260,13 +279,7 @@ export class SavedObjectsClient { overwrite: options.overwrite, }; - const currentWorkspaceId = this.currentWorkspaceId; - let finalWorkspaces; - if (options.hasOwnProperty('workspaces')) { - finalWorkspaces = options.workspaces; - } else if (typeof currentWorkspaceId === 'string') { - finalWorkspaces = [currentWorkspaceId]; - } + const finalWorkspaces = this.formatWorkspacesParams(options); const createRequest: Promise> = this.savedObjectsFetch(path, { method: 'POST', @@ -295,18 +308,12 @@ export class SavedObjectsClient { * @returns The result of the create operation containing created saved objects. */ public bulkCreate = ( - objects: Array> = [], + objects: SavedObjectsBulkCreateObject[] = [], options: SavedObjectsBulkCreateOptions = { overwrite: false } ) => { const path = this.getPath(['_bulk_create']); const query: HttpFetchOptions['query'] = { overwrite: options.overwrite }; - const currentWorkspaceId = this.currentWorkspaceId; - let finalWorkspaces; - if (options.hasOwnProperty('workspaces')) { - finalWorkspaces = options.workspaces; - } else if (typeof currentWorkspaceId === 'string') { - finalWorkspaces = [currentWorkspaceId]; - } + const finalWorkspaces = this.formatWorkspacesParams(options); if (finalWorkspaces) { query.workspaces = finalWorkspaces; @@ -383,13 +390,7 @@ export class SavedObjectsClient { workspaces: 'workspaces', }; - const currentWorkspaceId = this.currentWorkspaceId; - let finalWorkspaces; - if (options.hasOwnProperty('workspaces')) { - finalWorkspaces = options.workspaces; - } else if (typeof currentWorkspaceId === 'string') { - finalWorkspaces = Array.from(new Set([currentWorkspaceId])); - } + const finalWorkspaces = this.formatWorkspacesParams(options); const renamedQuery = renameKeys(renameMap, { ...options, From 4ff89e8a100658c42e5a71c1046e08e7ace1e935 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 10:59:13 +0800 Subject: [PATCH 13/18] feat: remove useless return Signed-off-by: SuZhou-Joe --- src/core/public/saved_objects/saved_objects_client.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 49c972ec0903..b51f19c88d2b 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -254,7 +254,6 @@ export class SavedObjectsClient { public setCurrentWorkspace(workspaceId: string): boolean { this.currentWorkspaceId = workspaceId; - return true; } /** From 2ef13f8e34c36c7ae2785b6a8503b63ebac7a6d2 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 11:17:22 +0800 Subject: [PATCH 14/18] fix: bootstrap error Signed-off-by: SuZhou-Joe --- src/core/public/saved_objects/saved_objects_client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index b51f19c88d2b..4fa6d1f87d0a 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -252,7 +252,7 @@ export class SavedObjectsClient { this.batchQueue = []; } - public setCurrentWorkspace(workspaceId: string): boolean { + public setCurrentWorkspace(workspaceId: string) { this.currentWorkspaceId = workspaceId; } From ee508ad666ecb00d5588d671eed28e3a35835a59 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 11:19:11 +0800 Subject: [PATCH 15/18] feat: update CHANGELOG Signed-off-by: SuZhou-Joe --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59a694737a2b..1378b867ccca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Multiple Datasource] Create data source menu component able to be mount to nav bar ([#6082](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6082)) - [Multiple Datasource] Handle form values(request payload) if the selected type is available in the authentication registry ([#6049](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6049)) - [Multiple Datasource] Add Vega support to MDS by specifying a data source name in the Vega spec ([#5975](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5975)) - - [Workspace] Consume workspace id in saved object client ([#6014](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6014)) ### 🐛 Bug Fixes From e202085929e10b094135e87b2fc42eadddb4a7ca Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 11:19:59 +0800 Subject: [PATCH 16/18] feat: update CHANGELOG Signed-off-by: SuZhou-Joe --- CHANGELOG.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1378b867ccca..830f213036d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,16 +97,6 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) - [Discover] Enhanced the data source selector with added sorting functionality ([#5609](https://github.com/opensearch-project/OpenSearch-Dashboards/issues/5609)) - [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756)) - [Multiple Datasource] Add datasource picker to import saved object flyout when multiple data source is enabled ([#5781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5781)) -- [Multiple Datasource] Add interfaces to register add-on authentication method from plug-in module ([#5851](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5851)) -- [Multiple Datasource] Able to Hide "Local Cluster" option from datasource DropDown ([#5827](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5827)) -- [Multiple Datasource] Add api registry and allow it to be added into client config in data source plugin ([#5895](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5895)) -- [Multiple Datasource] Concatenate data source name with index pattern name and change delimiter to double colon ([#5907](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5907)) -- [Multiple Datasource] Refactor client and legacy client to use authentication registry ([#5881](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5881)) -- [Multiple Datasource] Improved error handling for the search API when a null value is passed for the dataSourceId ([#5882](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5882)) -- [Multiple Datasource] Hide/Show authentication method in multi data source plugin based on configuration ([#5916](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5916)) -- [[Dynamic Configurations] Add support for dynamic application configurations ([#5855](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5855)) -- [Workspace] Optional workspaces params in repository ([#5949](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5949)) -- [Multiple Datasource] Refactoring create and edit form to use authentication registry ([#6002](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6002)) ### 🐛 Bug Fixes From 9685b7111afc0544991a506f75526dc6e422b9d8 Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 15:58:57 +0800 Subject: [PATCH 17/18] feat: optimize code Signed-off-by: SuZhou-Joe --- .../saved_objects/saved_objects_client.ts | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index 4fa6d1f87d0a..f07d3890b7fa 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -197,7 +197,7 @@ export class SavedObjectsClient { */ private formatWorkspacesParams(options: { workspaces?: SavedObjectsCreateOptions['workspaces']; - }) { + }): { workspaces: string[] } | {} { const currentWorkspaceId = this.currentWorkspaceId; let finalWorkspaces; if (options.hasOwnProperty('workspaces')) { @@ -206,7 +206,13 @@ export class SavedObjectsClient { finalWorkspaces = [currentWorkspaceId]; } - return finalWorkspaces; + if (finalWorkspaces) { + return { + workspaces: finalWorkspaces, + }; + } + + return {}; } /** @@ -278,8 +284,6 @@ export class SavedObjectsClient { overwrite: options.overwrite, }; - const finalWorkspaces = this.formatWorkspacesParams(options); - const createRequest: Promise> = this.savedObjectsFetch(path, { method: 'POST', query, @@ -287,11 +291,7 @@ export class SavedObjectsClient { attributes, migrationVersion: options.migrationVersion, references: options.references, - ...(finalWorkspaces - ? { - workspaces: finalWorkspaces, - } - : {}), + ...this.formatWorkspacesParams(options), }), }); @@ -312,15 +312,13 @@ export class SavedObjectsClient { ) => { const path = this.getPath(['_bulk_create']); const query: HttpFetchOptions['query'] = { overwrite: options.overwrite }; - const finalWorkspaces = this.formatWorkspacesParams(options); - - if (finalWorkspaces) { - query.workspaces = finalWorkspaces; - } const request: ReturnType = this.savedObjectsFetch(path, { method: 'POST', - query, + query: { + ...query, + ...this.formatWorkspacesParams(options), + }, body: JSON.stringify(objects), }); return request.then((resp) => { @@ -389,15 +387,9 @@ export class SavedObjectsClient { workspaces: 'workspaces', }; - const finalWorkspaces = this.formatWorkspacesParams(options); - const renamedQuery = renameKeys(renameMap, { ...options, - ...(finalWorkspaces - ? { - workspaces: finalWorkspaces, - } - : {}), + ...this.formatWorkspacesParams(options), }); const query = pick.apply(null, [renamedQuery, ...Object.values(renameMap)]) as Partial< Record From c553a36fd6e19350bbc4d9a619cc87248ca0803f Mon Sep 17 00:00:00 2001 From: SuZhou-Joe Date: Fri, 8 Mar 2024 16:01:44 +0800 Subject: [PATCH 18/18] feat: update comment Signed-off-by: SuZhou-Joe --- src/core/public/saved_objects/saved_objects_client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/public/saved_objects/saved_objects_client.ts b/src/core/public/saved_objects/saved_objects_client.ts index f07d3890b7fa..44e8be470c32 100644 --- a/src/core/public/saved_objects/saved_objects_client.ts +++ b/src/core/public/saved_objects/saved_objects_client.ts @@ -186,7 +186,7 @@ export class SavedObjectsClient { private http: HttpSetup; private batchQueue: BatchQueueEntry[]; /** - * The currentWorkspaceId may be undefiend when workspace plugin is not enabled. + * The currentWorkspaceId may be undefined when workspace plugin is not enabled. */ private currentWorkspaceId: string | undefined;