diff --git a/src/core/public/chrome/chrome_service.test.ts b/src/core/public/chrome/chrome_service.test.ts index 6e36775b90c..0a1e87a0c61 100644 --- a/src/core/public/chrome/chrome_service.test.ts +++ b/src/core/public/chrome/chrome_service.test.ts @@ -41,7 +41,7 @@ import { notificationServiceMock } from '../notifications/notifications_service. import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; import { ChromeService } from './chrome_service'; import { getAppInfo } from '../application/utils'; -import { workspacesServiceMock } from '../fatal_errors/fatal_errors_service.mock'; +import { workspacesServiceMock } from '../workspace/workspaces_service.mock'; class FakeApp implements App { public title = `${this.id} App`; diff --git a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap index 83c29874a6a..414e995db02 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header.test.tsx.snap @@ -1914,6 +1914,20 @@ exports[`Header handles visibility and lock changes 1`] = ` "borders": "none", "items": Array [ >>>>>> 356e96dfed (Integrate workspace service into saved object management (#31)) branding={ Object { "applicationTitle": "OpenSearch Dashboards", @@ -2351,6 +2365,20 @@ exports[`Header handles visibility and lock changes 1`] = ` className="euiHeaderSectionItem" > >>>>>> 356e96dfed (Integrate workspace service into saved object management (#31)) branding={ Object { "applicationTitle": "OpenSearch Dashboards", diff --git a/src/core/public/chrome/ui/header/__snapshots__/header_logo.test.tsx.snap b/src/core/public/chrome/ui/header/__snapshots__/header_logo.test.tsx.snap index 10212cd2756..99b2b0db3cc 100644 --- a/src/core/public/chrome/ui/header/__snapshots__/header_logo.test.tsx.snap +++ b/src/core/public/chrome/ui/header/__snapshots__/header_logo.test.tsx.snap @@ -2,13 +2,1000 @@ exports[`Header logo in dark mode uses custom logo dark mode URL 1`] = ` >>>>>> 356e96dfed (Integrate workspace service into saved object management (#31)) + branding={ + Object { + "applicationTitle": "custom title", + "assetFolderUrl": "base/ui/default_branding", + "darkMode": true, + "logo": Object { + "darkModeUrl": "/darkModeLogo", + "defaultUrl": "/defaultModeLogo", + }, + "mark": Object {}, + } + } + forceNavigation$={ + BehaviorSubject { + "_isScalar": false, + "_value": false, + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], + "thrownError": null, + } + } + href="/" + intl={ + Object { + "defaultFormats": Object {}, + "defaultLocale": "en", + "formatDate": [Function], + "formatHTMLMessage": [Function], + "formatMessage": [Function], + "formatNumber": [Function], + "formatPlural": [Function], + "formatRelative": [Function], + "formatTime": [Function], + "formats": Object { + "date": Object { + "full": Object { + "day": "numeric", + "month": "long", + "weekday": "long", + "year": "numeric", + }, + "long": Object { + "day": "numeric", + "month": "long", + "year": "numeric", + }, + "medium": Object { + "day": "numeric", + "month": "short", + "year": "numeric", + }, + "short": Object { + "day": "numeric", + "month": "numeric", + "year": "2-digit", + }, + }, + "number": Object { + "currency": Object { + "style": "currency", + }, + "percent": Object { + "style": "percent", + }, + }, + "relative": Object { + "days": Object { + "units": "day", + }, + "hours": Object { + "units": "hour", + }, + "minutes": Object { + "units": "minute", + }, + "months": Object { + "units": "month", + }, + "seconds": Object { + "units": "second", + }, + "years": Object { + "units": "year", + }, + }, + "time": Object { + "full": Object { + "hour": "numeric", + "minute": "numeric", + "second": "numeric", + "timeZoneName": "short", + }, + "long": Object { + "hour": "numeric", + "minute": "numeric", + "second": "numeric", + "timeZoneName": "short", + }, + "medium": Object { + "hour": "numeric", + "minute": "numeric", + "second": "numeric", + }, + "short": Object { + "hour": "numeric", + "minute": "numeric", + }, + }, + }, + "formatters": Object { + "getDateTimeFormat": [Function], + "getMessageFormat": [Function], + "getNumberFormat": [Function], + "getPluralFormat": [Function], + "getRelativeFormat": [Function], + }, + "locale": "en", + "messages": Object {}, + "now": [Function], + "onError": [Function], + "textComponent": Symbol(react.fragment), + "timeZone": null, + } + } + navLinks$={ + BehaviorSubject { + "_isScalar": false, + "_value": Array [], + "closed": false, + "hasError": false, + "isStopped": false, + "observers": Array [ + Subscriber { + "_parentOrParents": null, + "_subscriptions": Array [ + SubjectSubscription { + "_parentOrParents": [Circular], + "_subscriptions": null, + "closed": false, + "subject": [Circular], + "subscriber": [Circular], + }, + ], + "closed": false, + "destination": SafeSubscriber { + "_complete": undefined, + "_context": [Circular], + "_error": undefined, + "_next": [Function], + "_parentOrParents": null, + "_parentSubscriber": [Circular], + "_subscriptions": null, + "closed": false, + "destination": Object { + "closed": true, + "complete": [Function], + "error": [Function], + "next": [Function], + }, + "isStopped": false, + "syncErrorThrowable": false, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + "isStopped": false, + "syncErrorThrowable": true, + "syncErrorThrown": false, + "syncErrorValue": null, + }, + ], + "thrownError": null, + } + } + navigateToApp={[MockFunction]} +> + + custom title logo + + +`; + +exports[`Header logo in dark mode uses custom logo default mode URL if no dark mode logo provided 1`] = ` + + + custom title logo + + +`; + +exports[`Header logo when dark-themed uses dashboards' dark logo if branding containing no logo is provided 1`] = ` + + + custom title logo + + +`; + +exports[`Header logo when dark-themed uses dashboards' dark logo if no branding is provided 1`] = ` + + + opensearch dashboards logo + + +`; + +exports[`Header logo when dark-themed uses default-themed custom logo if branding without dark-mode logo is provided 1`] = ` + `; -exports[`Header logo in dark mode uses custom logo default mode URL if no dark mode logo provided 1`] = ` +exports[`Header logo in dark mode uses opensearch logo if custom mark provided without logo 1`] = ` `; -exports[`Header logo in dark mode uses opensearch logo if custom mark provided without logo 1`] = ` +exports[`Header logo when default-themed uses dashboards logo if branding containing a mark but not a logo is provided 1`] = ` >>>>>> 356e96dfed (Integrate workspace service into saved object management (#31)) branding={ Object { "applicationTitle": "custom title", @@ -708,6 +1720,20 @@ exports[`Header logo in dark mode uses opensearch logo if custom mark provided exports[`Header logo in dark mode uses opensearch logo if no logo provided 1`] = ` >>>>>> 356e96dfed (Integrate workspace service into saved object management (#31)) branding={ Object { "applicationTitle": "custom title", @@ -941,6 +1967,7 @@ exports[`Header logo in dark mode uses opensearch logo if no logo provided 1`] exports[`Header logo in default mode uses custom logo default mode URL 1`] = ` >>>>>> 356e96dfed (Integrate workspace service into saved object management (#31)) } } forceNavigation$={ diff --git a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx index aeaf3177ef3..0c91807c298 100644 --- a/src/core/public/chrome/ui/header/collapsible_nav.test.tsx +++ b/src/core/public/chrome/ui/header/collapsible_nav.test.tsx @@ -37,7 +37,7 @@ import { ChromeNavLink, DEFAULT_APP_CATEGORIES } from '../../..'; import { httpServiceMock } from '../../../http/http_service.mock'; import { ChromeRecentlyAccessedHistoryItem } from '../../recently_accessed'; import { CollapsibleNav } from './collapsible_nav'; -import { workspacesServiceMock } from '../../../fatal_errors/fatal_errors_service.mock'; +import { workspacesServiceMock } from '../../../workspace/workspaces_service.mock'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ htmlIdGenerator: () => () => 'mockId', diff --git a/src/core/public/chrome/ui/header/header.test.tsx b/src/core/public/chrome/ui/header/header.test.tsx index 08d05057538..12b31d53795 100644 --- a/src/core/public/chrome/ui/header/header.test.tsx +++ b/src/core/public/chrome/ui/header/header.test.tsx @@ -36,7 +36,7 @@ import { httpServiceMock } from '../../../http/http_service.mock'; import { applicationServiceMock } from '../../../mocks'; import { Header } from './header'; import { StubBrowserStorage } from 'test_utils/stub_browser_storage'; -import { workspacesServiceMock } from '../../../fatal_errors/fatal_errors_service.mock'; +import { workspacesServiceMock } from '../../../workspace/workspaces_service.mock'; jest.mock('@elastic/eui/lib/services/accessibility/html_id_generator', () => ({ htmlIdGenerator: () => () => 'mockId', diff --git a/src/core/public/fatal_errors/fatal_errors_service.mock.ts b/src/core/public/fatal_errors/fatal_errors_service.mock.ts index 8baf7d82427..ff1b252fc12 100644 --- a/src/core/public/fatal_errors/fatal_errors_service.mock.ts +++ b/src/core/public/fatal_errors/fatal_errors_service.mock.ts @@ -30,8 +30,6 @@ import type { PublicMethodsOf } from '@osd/utility-types'; import { FatalErrorsService, FatalErrorsSetup } from './fatal_errors_service'; -import { BehaviorSubject } from 'rxjs'; -import { WorkspaceAttribute } from '../workspace'; const createSetupContractMock = () => { const setupContract: jest.Mocked = { @@ -60,35 +58,3 @@ export const fatalErrorsServiceMock = { createSetupContract: createSetupContractMock, createStartContract: createStartContractMock, }; - -const currentWorkspaceId$ = new BehaviorSubject(''); -const workspaceList$ = new BehaviorSubject([]); -const currentWorkspace$ = new BehaviorSubject(null); - -const createWorkspacesSetupContractMock = () => ({ - client: { - currentWorkspaceId$, - workspaceList$, - currentWorkspace$, - init: jest.fn(), - stop: jest.fn(), - enterWorkspace: jest.fn(), - exitWorkspace: jest.fn(), - create: jest.fn(), - delete: jest.fn(), - list: jest.fn(), - getCurrentWorkspace: jest.fn(), - getCurrentWorkspaceId: jest.fn(), - get: jest.fn(), - update: jest.fn(), - }, - formatUrlWithWorkspaceId: jest.fn(), - setFormatUrlWithWorkspaceId: jest.fn(), -}); - -const createWorkspacesStartContractMock = createWorkspacesSetupContractMock; - -export const workspacesServiceMock = { - createSetupContractMock: createWorkspacesStartContractMock, - createStartContract: createWorkspacesStartContractMock, -}; diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index e863d627c80..99d9f5a517f 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -47,6 +47,7 @@ import { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; import { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; import { contextServiceMock } from './context/context_service.mock'; import { injectedMetadataServiceMock } from './injected_metadata/injected_metadata_service.mock'; +import { workspacesServiceMock } from './workspace/workspaces_service.mock'; export { chromeServiceMock } from './chrome/chrome_service.mock'; export { docLinksServiceMock } from './doc_links/doc_links_service.mock'; @@ -60,6 +61,7 @@ export { uiSettingsServiceMock } from './ui_settings/ui_settings_service.mock'; export { savedObjectsServiceMock } from './saved_objects/saved_objects_service.mock'; export { scopedHistoryMock } from './application/scoped_history.mock'; export { applicationServiceMock } from './application/application_service.mock'; +export { workspacesServiceMock } from './workspace/workspaces_service.mock'; function createCoreSetupMock({ basePath = '', @@ -85,6 +87,7 @@ function createCoreSetupMock({ getInjectedVar: injectedMetadataServiceMock.createSetupContract().getInjectedVar, getBranding: injectedMetadataServiceMock.createSetupContract().getBranding, }, + workspaces: workspacesServiceMock, }; return mock; @@ -106,6 +109,7 @@ function createCoreStartMock({ basePath = '' } = {}) { getBranding: injectedMetadataServiceMock.createStartContract().getBranding, }, fatalErrors: fatalErrorsServiceMock.createStartContract(), + workspaces: workspacesServiceMock.createStartContract(), }; return mock; diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 9c36a791332..01578523be5 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -50,10 +50,7 @@ import { applicationServiceMock } from '../application/application_service.mock' import { i18nServiceMock } from '../i18n/i18n_service.mock'; import { overlayServiceMock } from '../overlays/overlay_service.mock'; import { chromeServiceMock } from '../chrome/chrome_service.mock'; -import { - fatalErrorsServiceMock, - workspacesServiceMock, -} from '../fatal_errors/fatal_errors_service.mock'; +import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.mock'; import { uiSettingsServiceMock } from '../ui_settings/ui_settings_service.mock'; import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock'; import { httpServiceMock } from '../http/http_service.mock'; @@ -61,6 +58,7 @@ import { CoreSetup, CoreStart, PluginInitializerContext } from '..'; import { docLinksServiceMock } from '../doc_links/doc_links_service.mock'; import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock'; import { contextServiceMock } from '../context/context_service.mock'; +import { workspacesServiceMock } from '../workspace/workspaces_service.mock'; export let mockPluginInitializers: Map; diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts new file mode 100644 index 00000000000..245303cb4c9 --- /dev/null +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -0,0 +1,39 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { BehaviorSubject } from 'rxjs'; +import { WorkspaceAttribute } from '../workspace'; + +const currentWorkspaceId$ = new BehaviorSubject(''); +const workspaceList$ = new BehaviorSubject([]); +const currentWorkspace$ = new BehaviorSubject(null); + +const createWorkspacesSetupContractMock = () => ({ + client: { + currentWorkspaceId$, + workspaceList$, + currentWorkspace$, + init: jest.fn(), + stop: jest.fn(), + enterWorkspace: jest.fn(), + exitWorkspace: jest.fn(), + create: jest.fn(), + delete: jest.fn(), + list: jest.fn(), + getCurrentWorkspace: jest.fn(), + getCurrentWorkspaceId: jest.fn(), + get: jest.fn(), + update: jest.fn(), + }, + formatUrlWithWorkspaceId: jest.fn(), + setFormatUrlWithWorkspaceId: jest.fn(), +}); + +const createWorkspacesStartContractMock = createWorkspacesSetupContractMock; + +export const workspacesServiceMock = { + createSetupContractMock: createWorkspacesStartContractMock, + createStartContract: createWorkspacesStartContractMock, +}; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index ca55cc8dc1d..6a234a405e5 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -508,3 +508,5 @@ export const config = { appenders: appendersSchema as Type, }, }; + +export { formatWorkspaces, workspacesValidator } from './workspaces'; diff --git a/src/core/server/saved_objects/routes/bulk_create.ts b/src/core/server/saved_objects/routes/bulk_create.ts index 61a458d9a61..a43a6946430 100644 --- a/src/core/server/saved_objects/routes/bulk_create.ts +++ b/src/core/server/saved_objects/routes/bulk_create.ts @@ -30,7 +30,7 @@ import { schema } from '@osd/config-schema'; import { IRouter } from '../../http'; -import { formatWorkspaces, workspacesValidator } from './utils'; +import { formatWorkspaces, workspacesValidator } from '../../workspaces'; export const registerBulkCreateRoute = (router: IRouter) => { router.post( diff --git a/src/core/server/saved_objects/routes/find.ts b/src/core/server/saved_objects/routes/find.ts index 447ec8f6d7d..2ee7005f8e4 100644 --- a/src/core/server/saved_objects/routes/find.ts +++ b/src/core/server/saved_objects/routes/find.ts @@ -30,7 +30,7 @@ import { schema } from '@osd/config-schema'; import { IRouter } from '../../http'; -import { formatWorkspaces, workspacesValidator } from './utils'; +import { formatWorkspaces, workspacesValidator } from '../../workspaces'; export const registerFindRoute = (router: IRouter) => { router.get( diff --git a/src/core/server/saved_objects/routes/import.ts b/src/core/server/saved_objects/routes/import.ts index 794f8ef84a7..9675d608541 100644 --- a/src/core/server/saved_objects/routes/import.ts +++ b/src/core/server/saved_objects/routes/import.ts @@ -34,7 +34,8 @@ import { schema } from '@osd/config-schema'; import { IRouter } from '../../http'; import { importSavedObjectsFromStream } from '../import'; import { SavedObjectConfig } from '../saved_objects_config'; -import { createSavedObjectsStreamFromNdJson, formatWorkspaces, workspacesValidator } from './utils'; +import { createSavedObjectsStreamFromNdJson } from './utils'; +import { formatWorkspaces, workspacesValidator } from '../../workspaces'; interface FileStream extends Readable { hapi: { diff --git a/src/core/server/saved_objects/routes/utils.ts b/src/core/server/saved_objects/routes/utils.ts index c2b77655ff1..73bedf20b94 100644 --- a/src/core/server/saved_objects/routes/utils.ts +++ b/src/core/server/saved_objects/routes/utils.ts @@ -27,7 +27,6 @@ * specific language governing permissions and limitations * under the License. */ -import { schema } from '@osd/config-schema'; import { Readable } from 'stream'; import { SavedObject, SavedObjectsExportResultDetails } from 'src/core/server'; import { @@ -74,19 +73,3 @@ export function validateObjects( .join(', ')}`; } } - -export const workspacesValidator = schema.maybe( - schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) -); - -export function formatWorkspaces(workspaces?: string | string[]): string[] | undefined { - if (Array.isArray(workspaces)) { - return workspaces; - } - - if (!workspaces) { - return undefined; - } - - return [workspaces]; -} diff --git a/src/core/server/workspaces/index.ts b/src/core/server/workspaces/index.ts index 5441216c731..28079e7a36b 100644 --- a/src/core/server/workspaces/index.ts +++ b/src/core/server/workspaces/index.ts @@ -13,3 +13,4 @@ export { export { WorkspaceAttribute, WorkspaceFindOptions } from './types'; export { WorkspacePermissionControl } from './workspace_permission_control'; +export { workspacesValidator, formatWorkspaces } from './utils'; diff --git a/src/core/server/workspaces/utils.ts b/src/core/server/workspaces/utils.ts new file mode 100644 index 00000000000..955b49a1e5e --- /dev/null +++ b/src/core/server/workspaces/utils.ts @@ -0,0 +1,22 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { schema } from '@osd/config-schema'; + +export const workspacesValidator = schema.maybe( + schema.oneOf([schema.string(), schema.arrayOf(schema.string())]) +); + +export function formatWorkspaces(workspaces?: string | string[]): string[] | undefined { + if (Array.isArray(workspaces)) { + return workspaces; + } + + if (!workspaces) { + return undefined; + } + + return [workspaces]; +} 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 e5f716347a7..1af8ac21069 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.ts b/src/plugins/saved_objects_management/public/lib/fetch_export_objects.ts index b2e2ea0f916..43afcfec305 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 6eaaac7d35f..9039dae2be5 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 33d63ec9d19..e63241b0c3b 100644 --- a/src/plugins/saved_objects_management/public/lib/import_file.ts +++ b/src/plugins/saved_objects_management/public/lib/import_file.ts @@ -40,11 +40,11 @@ interface ImportResponse { export async function importFile( http: HttpStart, file: File, - { createNewCopies, overwrite }: ImportMode + { createNewCopies, overwrite, workspaces }: ImportMode ) { const formData = new FormData(); formData.append('file', file); - const query = createNewCopies ? { createNewCopies } : { overwrite }; + const query = createNewCopies ? { createNewCopies, workspaces } : { overwrite, workspaces }; return await http.post('/api/saved_objects/_import', { body: formData, headers: { 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 b0dd8c26903..6b243788a9e 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 @@ -92,6 +92,7 @@ export interface FlyoutProps { overlays: OverlayStart; http: HttpStart; search: DataPublicPluginStart['search']; + workspaces?: string[]; } export interface FlyoutState { @@ -183,13 +184,16 @@ 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 } = this.state; this.setState({ status: 'loading', error: undefined }); // Import the file try { - const response = await importFile(http, file!, importMode); + const response = await importFile(http, file!, { + ...importMode, + workspaces, + }); 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 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 beabcfbb630..9953b5353b8 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 @@ -51,6 +51,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 5a6bf0713d9..60791db07eb 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.createSetupContractMock(); const applications = applicationServiceMock.createStartContract(); applications.capabilities = { @@ -161,6 +164,7 @@ describe('SavedObjectsTable', () => { goInspectObject: () => {}, canGoInApp: () => true, search, + workspaces, }; findObjectsMock.mockImplementation(() => ({ @@ -279,7 +283,9 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, { + workspaces: ['public'], + }); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ title: 'Your file is downloading in the background', }); @@ -322,7 +328,9 @@ describe('SavedObjectsTable', () => { await component.instance().onExport(true); - expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true); + expect(fetchExportObjectsMock).toHaveBeenCalledWith(http, mockSelectedSavedObjects, true, { + workspaces: ['public'], + }); expect(notifications.toasts.addWarning).toHaveBeenCalledWith({ title: 'Your file is downloading in the background. ' + @@ -363,7 +371,8 @@ describe('SavedObjectsTable', () => { http, allowedTypes, undefined, - true + true, + { workspaces: ['public'] } ); expect(saveAsMock).toHaveBeenCalledWith(blob, 'export.ndjson'); expect(notifications.toasts.addSuccess).toHaveBeenCalledWith({ @@ -393,7 +402,8 @@ describe('SavedObjectsTable', () => { http, allowedTypes, 'test*', - true + true, + { workspaces: ['public'] } ); 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 412047ba66f..b91e5c0cb2a 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 @@ -65,6 +65,7 @@ import { OverlayStart, NotificationsStart, ApplicationStart, + WorkspacesStart, } from 'src/core/public'; import { RedirectAppLinks } from '../../../../opensearch_dashboards_react/public'; import { IndexPatternsContract } from '../../../../data/public'; @@ -116,6 +117,7 @@ export interface SavedObjectsTableProps { dateFormat: string; title: string; fullWidth: boolean; + workspaces: WorkspacesStart; } export interface SavedObjectsTableState { @@ -137,6 +139,7 @@ export interface SavedObjectsTableState { exportAllOptions: ExportAllOption[]; exportAllSelectedOptions: Record; isIncludeReferencesDeepChecked: boolean; + workspaceId: string | null; } export class SavedObjectsTable extends Component { @@ -167,11 +170,21 @@ export class SavedObjectsTable extends Component + this.setState({ + workspaceId, + }) + ); this.fetchSavedObjects(); this.fetchCounts(); } @@ -192,6 +205,7 @@ export class SavedObjectsTable extends Component ns.id) || []; @@ -405,7 +421,9 @@ export class SavedObjectsTable extends Component { const { editUrl } = savedObject.meta; diff --git a/src/plugins/saved_objects_management/server/routes/find.ts b/src/plugins/saved_objects_management/server/routes/find.ts index dd49fc7575d..af1f96aae58 100644 --- a/src/plugins/saved_objects_management/server/routes/find.ts +++ b/src/plugins/saved_objects_management/server/routes/find.ts @@ -34,6 +34,7 @@ import { DataSourceAttributes } from 'src/plugins/data_source/common/data_source import { getIndexPatternTitle } from '../../../data/common/index_patterns/utils'; import { injectMetaAttributes } from '../lib'; import { ISavedObjectsManagement } from '../services'; +import { formatWorkspaces, workspacesValidator } from '../../../../core/server'; export const registerFindRoute = ( router: IRouter, @@ -64,6 +65,7 @@ export const registerFindRoute = ( fields: schema.oneOf([schema.string(), schema.arrayOf(schema.string())], { defaultValue: [], }), + workspaces: workspacesValidator, }), }, }, @@ -94,6 +96,7 @@ export const registerFindRoute = ( ...req.query, fields: undefined, searchFields: [...searchFields], + workspaces: formatWorkspaces(req.query.workspaces), }); 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 63233748a89..1ce7445c40f 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())), }), }, }, @@ -53,6 +54,7 @@ export const registerScrollForCountRoute = (router: IRouter) => { const findOptions: SavedObjectsFindOptions = { type: req.body.typesToInclude, perPage: 1000, + workspaces: req.body.workspaces, }; const requestHasNamespaces =