Remove spacesOss plugin (#109258)

This commit is contained in:
Joe Portner 2021-08-23 09:10:09 -04:00 committed by GitHub
parent 3a0f209bde
commit 65e04b1380
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 912 additions and 2591 deletions

View file

@ -1567,7 +1567,6 @@ module.exports = {
{
files: [
'src/plugins/security_oss/**/*.{js,mjs,ts,tsx}',
'src/plugins/spaces_oss/**/*.{js,mjs,ts,tsx}',
'src/plugins/interactive_setup/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/encrypted_saved_objects/**/*.{js,mjs,ts,tsx}',
'x-pack/plugins/security/**/*.{js,mjs,ts,tsx}',

1
.github/CODEOWNERS vendored
View file

@ -280,7 +280,6 @@
# Security
/src/core/server/csp/ @elastic/kibana-security @elastic/kibana-core
/src/plugins/security_oss/ @elastic/kibana-security
/src/plugins/spaces_oss/ @elastic/kibana-security
/src/plugins/interactive_setup/ @elastic/kibana-security
/test/security_functional/ @elastic/kibana-security
/x-pack/plugins/spaces/ @elastic/kibana-security

File diff suppressed because it is too large Load diff

View file

@ -1,41 +0,0 @@
---
id: kibSpacesOssPluginApi
slug: /kibana-dev-docs/spacesOssPluginApi
title: spacesOss
image: https://source.unsplash.com/400x175/?github
summary: API docs for the spacesOss plugin
date: 2020-11-16
tags: ['contributor', 'dev', 'apidocs', 'kibana', 'spacesOss']
warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info.
---
import spacesOssObj from './spaces_oss.json';
This plugin exposes a limited set of spaces functionality to OSS plugins.
Contact [Platform Security](https://github.com/orgs/elastic/teams/kibana-security) for questions regarding this plugin.
**Code health stats**
| Public API count | Any count | Items lacking comments | Missing exports |
|-------------------|-----------|------------------------|-----------------|
| 77 | 0 | 10 | 0 |
## Client
### Setup
<DocDefinitionList data={[spacesOssObj.client.setup]}/>
### Start
<DocDefinitionList data={[spacesOssObj.client.start]}/>
### Interfaces
<DocDefinitionList data={spacesOssObj.client.interfaces}/>
### Consts, variables and types
<DocDefinitionList data={spacesOssObj.client.misc}/>
## Common
### Interfaces
<DocDefinitionList data={spacesOssObj.common.interfaces}/>

View file

@ -227,10 +227,6 @@ so they can properly protect the data within their clusters.
generating deep links to other apps, and creating short URLs.
|{kib-repo}blob/{branch}/src/plugins/spaces_oss/README.md[spacesOss]
|Bridge plugin for consumption of the Spaces feature from OSS plugins.
|{kib-repo}blob/{branch}/src/plugins/telemetry/README.md[telemetry]
|Telemetry allows Kibana features to have usage tracked in the wild. The general term "telemetry" refers to multiple things:

View file

@ -98,7 +98,6 @@ pageLoadAssetSize:
runtimeFields: 41752
stackAlerts: 29684
presentationUtil: 94301
spacesOss: 18817
indexPatternFieldEditor: 50000
osquery: 107090
fileUpload: 25664

View file

@ -19,7 +19,7 @@
"presentationUtil",
"visualizations"
],
"optionalPlugins": ["home", "spacesOss", "savedObjectsTaggingOss", "usageCollection"],
"optionalPlugins": ["home", "spaces", "savedObjectsTaggingOss", "usageCollection"],
"server": true,
"ui": true,
"requiredBundles": ["home", "kibanaReact", "kibanaUtils", "presentationUtil"]

View file

@ -79,13 +79,13 @@ export async function mountApp({
urlForwarding,
data: dataStart,
share: shareStart,
spaces: spacesApi,
embeddable: embeddableStart,
savedObjectsTaggingOss,
visualizations,
presentationUtil,
} = pluginsStart;
const spacesApi = pluginsStart.spacesOss?.isSpacesAvailable ? pluginsStart.spacesOss : undefined;
const activeSpaceId =
spacesApi && (await spacesApi.getActiveSpace$().pipe(first()).toPromise())?.id;
let globalEmbedSettings: DashboardEmbedSettings | undefined;

View file

@ -80,7 +80,7 @@ import { UrlGeneratorState } from '../../share/public';
import { ExportCSVAction } from './application/actions/export_csv_action';
import { dashboardFeatureCatalog } from './dashboard_strings';
import { replaceUrlHashQuery } from '../../kibana_utils/public';
import { SpacesOssPluginStart } from './services/spaces';
import { SpacesPluginStart } from './services/spaces';
declare module '../../share/public' {
export interface UrlGeneratorStateMapping {
@ -118,7 +118,7 @@ export interface DashboardStartDependencies {
savedObjects: SavedObjectsStart;
presentationUtil: PresentationUtilPluginStart;
savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart;
spacesOss?: SpacesOssPluginStart;
spaces?: SpacesPluginStart;
visualizations: VisualizationsStart;
}

View file

@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
export { SpacesOssPluginStart } from '../../../spaces_oss/public';
export { SpacesPluginStart } from '../../../../../x-pack/plugins/spaces/public';

View file

@ -30,9 +30,9 @@
{ "path": "../saved_objects_tagging_oss/tsconfig.json" },
{ "path": "../saved_objects/tsconfig.json" },
{ "path": "../ui_actions/tsconfig.json" },
{ "path": "../spaces_oss/tsconfig.json" },
{ "path": "../charts/tsconfig.json" },
{ "path": "../discover/tsconfig.json" },
{ "path": "../visualizations/tsconfig.json" },
{ "path": "../../../x-pack/plugins/spaces/tsconfig.json" },
]
}

View file

@ -8,7 +8,7 @@
"server": true,
"ui": true,
"requiredPlugins": ["management", "data"],
"optionalPlugins": ["dashboard", "visualizations", "discover", "home", "savedObjectsTaggingOss", "spacesOss"],
"optionalPlugins": ["dashboard", "visualizations", "discover", "home", "savedObjectsTaggingOss", "spaces"],
"extraPublicDirs": ["public/lib"],
"requiredBundles": ["kibanaReact", "home"]
}

View file

@ -39,7 +39,7 @@ export const mountManagementSection = async ({
}: MountParams) => {
const [
coreStart,
{ data, savedObjectsTaggingOss, spacesOss },
{ data, savedObjectsTaggingOss, spaces: spacesApi },
pluginStart,
] = await core.getStartServices();
const { element, history, setBreadcrumbs } = mountParams;
@ -61,8 +61,6 @@ export const mountManagementSection = async ({
return children! as React.ReactElement;
};
const spacesApi = spacesOss?.isSpacesAvailable ? spacesOss : undefined;
ReactDOM.render(
<I18nProvider>
<Router history={history}>

View file

@ -13,10 +13,7 @@ import { Query } from '@elastic/eui';
import { parse } from 'query-string';
import { i18n } from '@kbn/i18n';
import { CoreStart, ChromeBreadcrumb } from 'src/core/public';
import type {
SpacesAvailableStartContract,
SpacesContextProps,
} from 'src/plugins/spaces_oss/public';
import type { SpacesApi, SpacesContextProps } from '../../../../../x-pack/plugins/spaces/public';
import { DataPublicPluginStart } from '../../../data/public';
import { SavedObjectsTaggingApi } from '../../../saved_objects_tagging_oss/public';
import {
@ -42,7 +39,7 @@ const SavedObjectsTablePage = ({
coreStart: CoreStart;
dataStart: DataPublicPluginStart;
taggingApi?: SavedObjectsTaggingApi;
spacesApi?: SpacesAvailableStartContract;
spacesApi?: SpacesApi;
allowedTypes: string[];
serviceRegistry: ISavedObjectsManagementServiceRegistry;
actionRegistry: SavedObjectsManagementActionServiceStart;

View file

@ -8,6 +8,7 @@
import { i18n } from '@kbn/i18n';
import { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import type { SpacesPluginStart } from '../../../../x-pack/plugins/spaces/public';
import { ManagementSetup } from '../../management/public';
import { DataPublicPluginStart } from '../../data/public';
import { DashboardStart } from '../../dashboard/public';
@ -15,7 +16,6 @@ import { DiscoverStart } from '../../discover/public';
import { HomePublicPluginSetup, FeatureCatalogueCategory } from '../../home/public';
import { VisualizationsStart } from '../../visualizations/public';
import { SavedObjectTaggingOssPluginStart } from '../../saved_objects_tagging_oss/public';
import type { SpacesOssPluginStart } from '../../spaces_oss/public';
import {
SavedObjectsManagementActionService,
SavedObjectsManagementActionServiceSetup,
@ -50,7 +50,7 @@ export interface StartDependencies {
visualizations?: VisualizationsStart;
discover?: DiscoverStart;
savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart;
spacesOss?: SpacesOssPluginStart;
spaces?: SpacesPluginStart;
}
export class SavedObjectsManagementPlugin
@ -116,9 +116,9 @@ export class SavedObjectsManagementPlugin
};
}
public start(core: CoreStart, { data }: StartDependencies) {
const actionStart = this.actionService.start();
const columnStart = this.columnService.start();
public start(_core: CoreStart, { spaces: spacesApi }: StartDependencies) {
const actionStart = this.actionService.start(spacesApi);
const columnStart = this.columnService.start(spacesApi);
return {
actions: actionStart,

View file

@ -6,6 +6,11 @@
* Side Public License, v 1.
*/
import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks';
import {
CopyToSpaceSavedObjectsManagementAction,
ShareToSpaceSavedObjectsManagementAction,
} from './actions';
import {
SavedObjectsManagementActionService,
SavedObjectsManagementActionServiceSetup,
@ -44,8 +49,12 @@ describe('SavedObjectsManagementActionRegistry', () => {
it('allows actions to be registered and retrieved', () => {
const action = createAction('foo');
setup.register(action);
const start = service.start();
expect(start.getAll()).toContain(action);
const start = service.start(spacesPluginMock.createStartContract());
expect(start.getAll()).toEqual([
action,
expect.any(ShareToSpaceSavedObjectsManagementAction),
expect.any(CopyToSpaceSavedObjectsManagementAction),
]);
});
it('does not allow actions with duplicate ids to be registered', () => {

View file

@ -6,6 +6,11 @@
* Side Public License, v 1.
*/
import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public';
import {
CopyToSpaceSavedObjectsManagementAction,
ShareToSpaceSavedObjectsManagementAction,
} from './actions';
import { SavedObjectsManagementAction } from './types';
export interface SavedObjectsManagementActionServiceSetup {
@ -40,10 +45,21 @@ export class SavedObjectsManagementActionService {
};
}
start(): SavedObjectsManagementActionServiceStart {
start(spacesApi?: SpacesApi): SavedObjectsManagementActionServiceStart {
if (spacesApi) {
registerSpacesApiActions(this, spacesApi);
}
return {
has: (actionId) => this.actions.has(actionId),
getAll: () => [...this.actions.values()],
};
}
}
function registerSpacesApiActions(
service: SavedObjectsManagementActionService,
spacesApi: SpacesApi
) {
service.setup().register(new ShareToSpaceSavedObjectsManagementAction(spacesApi.ui));
service.setup().register(new CopyToSpaceSavedObjectsManagementAction(spacesApi.ui));
}

View file

@ -0,0 +1,77 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import type {
CopyToSpaceFlyoutProps,
SpacesApiUi,
} from '../../../../../../x-pack/plugins/spaces/public';
import type { SavedObjectsManagementRecord } from '../types';
import { SavedObjectsManagementAction } from '../types';
interface WrapperProps {
spacesApiUi: SpacesApiUi;
props: CopyToSpaceFlyoutProps;
}
const Wrapper = ({ spacesApiUi, props }: WrapperProps) => {
const LazyComponent = useMemo(() => spacesApiUi.components.getCopyToSpaceFlyout, [spacesApiUi]);
return <LazyComponent {...props} />;
};
export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction {
public id: string = 'copy_saved_objects_to_space';
public euiAction = {
name: i18n.translate('savedObjectsManagement.copyToSpace.actionTitle', {
defaultMessage: 'Copy to space',
}),
description: i18n.translate('savedObjectsManagement.copyToSpace.actionDescription', {
defaultMessage: 'Make a copy of this saved object in one or more spaces',
}),
icon: 'copy',
type: 'icon',
available: (object: SavedObjectsManagementRecord) => {
return object.meta.namespaceType !== 'agnostic' && !object.meta.hiddenType;
},
onClick: (object: SavedObjectsManagementRecord) => {
this.start(object);
},
};
constructor(private readonly spacesApiUi: SpacesApiUi) {
super();
}
public render = () => {
if (!this.record) {
throw new Error('No record available! `render()` was likely called before `start()`.');
}
const props: CopyToSpaceFlyoutProps = {
onClose: this.onClose,
savedObjectTarget: {
type: this.record.type,
id: this.record.id,
namespaces: this.record.namespaces ?? [],
title: this.record.meta.title,
icon: this.record.meta.icon,
},
};
return <Wrapper spacesApiUi={this.spacesApiUi} props={props} />;
};
private onClose = () => {
this.finish();
};
}

View file

@ -6,8 +6,5 @@
* Side Public License, v 1.
*/
module.exports = {
preset: '@kbn/test',
rootDir: '../../..',
roots: ['<rootDir>/src/plugins/spaces_oss'],
};
export { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action';
export { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action';

View file

@ -1,18 +1,18 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public';
import { uiApiMock } from '../ui_api/mocks';
import { spacesPluginMock } from '../../../../../../x-pack/plugins/spaces/public/mocks';
import type { SavedObjectsManagementRecord } from '../types';
import { ShareToSpaceSavedObjectsManagementAction } from './share_saved_objects_to_space_action';
describe('ShareToSpaceSavedObjectsManagementAction', () => {
const createAction = () => {
const spacesApiUi = uiApiMock.create();
const { ui: spacesApiUi } = spacesPluginMock.createStartContract();
return new ShareToSpaceSavedObjectsManagementAction(spacesApiUi);
};
describe('#euiAction.available', () => {

View file

@ -1,17 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public';
import type { ShareToSpaceFlyoutProps, SpacesApiUi } from 'src/plugins/spaces_oss/public';
import { SavedObjectsManagementAction } from '../../../../../src/plugins/saved_objects_management/public';
import type {
ShareToSpaceFlyoutProps,
SpacesApiUi,
} from '../../../../../../x-pack/plugins/spaces/public';
import type { SavedObjectsManagementRecord } from '../types';
import { SavedObjectsManagementAction } from '../types';
interface WrapperProps {
spacesApiUi: SpacesApiUi;
@ -28,10 +32,10 @@ export class ShareToSpaceSavedObjectsManagementAction extends SavedObjectsManage
public id: string = 'share_saved_objects_to_space';
public euiAction = {
name: i18n.translate('xpack.spaces.shareToSpace.actionTitle', {
name: i18n.translate('savedObjectsManagement.shareToSpace.actionTitle', {
defaultMessage: 'Share to space',
}),
description: i18n.translate('xpack.spaces.shareToSpace.actionDescription', {
description: i18n.translate('savedObjectsManagement.shareToSpace.actionDescription', {
defaultMessage: 'Share this saved object to one or more spaces',
}),
icon: 'share',

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
import { spacesPluginMock } from '../../../../../x-pack/plugins/spaces/public/mocks';
// import { ShareToSpaceSavedObjectsManagementColumn } from './columns';
import {
SavedObjectsManagementColumnService,
SavedObjectsManagementColumnServiceSetup,
@ -40,8 +42,11 @@ describe('SavedObjectsManagementColumnRegistry', () => {
it('allows columns to be registered and retrieved', () => {
const column = createColumn('foo');
setup.register(column);
const start = service.start();
expect(start.getAll()).toContain(column);
const start = service.start(spacesPluginMock.createStartContract());
expect(start.getAll()).toEqual([
column,
// expect.any(ShareToSpaceSavedObjectsManagementColumn),
]);
});
it('does not allow columns with duplicate ids to be registered', () => {

View file

@ -6,6 +6,8 @@
* Side Public License, v 1.
*/
import type { SpacesApi } from '../../../../../x-pack/plugins/spaces/public';
// import { ShareToSpaceSavedObjectsManagementColumn } from './columns';
import { SavedObjectsManagementColumn } from './types';
export interface SavedObjectsManagementColumnServiceSetup {
@ -36,9 +38,20 @@ export class SavedObjectsManagementColumnService {
};
}
start(): SavedObjectsManagementColumnServiceStart {
start(spacesApi?: SpacesApi): SavedObjectsManagementColumnServiceStart {
if (spacesApi) {
registerSpacesApiColumns(this, spacesApi);
}
return {
getAll: () => [...this.columns.values()],
};
}
}
function registerSpacesApiColumns(
service: SavedObjectsManagementColumnService,
spacesApi: SpacesApi
) {
// Note: this column is hidden for now because no saved objects are shareable. It should be uncommented when at least one saved object type is multi-namespace.
// service.setup().register(new ShareToSpaceSavedObjectsManagementColumn(spacesApi.ui));
}

View file

@ -6,4 +6,4 @@
* Side Public License, v 1.
*/
export { Space } from './types';
export { ShareToSpaceSavedObjectsManagementColumn } from './share_saved_objects_to_space_column';

View file

@ -1,15 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import React, { useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import type { SavedObjectsManagementColumn } from 'src/plugins/saved_objects_management/public';
import type { SpaceListProps, SpacesApiUi } from 'src/plugins/spaces_oss/public';
import type { SpaceListProps, SpacesApiUi } from '../../../../../../x-pack/plugins/spaces/public';
import type { SavedObjectsManagementColumn } from '../types';
interface WrapperProps {
spacesApiUi: SpacesApiUi;
@ -28,10 +30,10 @@ export class ShareToSpaceSavedObjectsManagementColumn
public euiColumn = {
field: 'namespaces',
name: i18n.translate('xpack.spaces.shareToSpace.columnTitle', {
name: i18n.translate('savedObjectsManagement.shareToSpace.columnTitle', {
defaultMessage: 'Shared spaces',
}),
description: i18n.translate('xpack.spaces.shareToSpace.columnDescription', {
description: i18n.translate('savedObjectsManagement.shareToSpace.columnDescription', {
defaultMessage: 'The other spaces that this object is currently shared to',
}),
render: (namespaces: string[] | undefined) => {

View file

@ -20,6 +20,6 @@
{ "path": "../kibana_react/tsconfig.json" },
{ "path": "../management/tsconfig.json" },
{ "path": "../visualizations/tsconfig.json" },
{ "path": "../spaces_oss/tsconfig.json" },
{ "path": "../../../x-pack/plugins/spaces/tsconfig.json" },
]
}

View file

@ -1,3 +0,0 @@
# SpacesOss
Bridge plugin for consumption of the Spaces feature from OSS plugins.

View file

@ -1,62 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
/**
* A Space.
*/
export interface Space {
/**
* The unique identifier for this space.
* The id becomes part of the "URL Identifier" of the space.
*
* Example: an id of `marketing` would result in the URL identifier of `/s/marketing`.
*/
id: string;
/**
* Display name for this space.
*/
name: string;
/**
* Optional description for this space.
*/
description?: string;
/**
* Optional color (hex code) for this space.
* If neither `color` nor `imageUrl` is specified, then a color will be automatically generated.
*/
color?: string;
/**
* Optional display initials for this space's avatar. Supports a maximum of 2 characters.
* If initials are not provided, then they will be derived from the space name automatically.
*
* Initials are not displayed if an `imageUrl` has been specified.
*/
initials?: string;
/**
* Optional base-64 encoded data image url to show as this space's avatar.
* This setting takes precedence over any configured `color` or `initials`.
*/
imageUrl?: string;
/**
* The set of feature ids that should be hidden within this space.
*/
disabledFeatures: string[];
/**
* Indicates that this space is reserved (system controlled).
* Reserved spaces cannot be created or deleted by end-users.
* @private
*/
_reserved?: boolean;
}

View file

@ -1,13 +0,0 @@
{
"id": "spacesOss",
"owner": {
"name": "Platform Security",
"githubTeam": "kibana-security"
},
"description": "This plugin exposes a limited set of spaces functionality to OSS plugins.",
"version": "kibana",
"server": false,
"ui": true,
"requiredPlugins": [],
"optionalPlugins": []
}

View file

@ -1,302 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { ReactElement } from 'react';
import type { Observable } from 'rxjs';
import type { Space } from '../common';
/**
* Client-side Spaces API.
*/
export interface SpacesApi {
/**
* Observable representing the currently active space.
* The details of the space can change without a full page reload (such as display name, color, etc.)
*/
getActiveSpace$(): Observable<Space>;
/**
* Retrieve the currently active space.
*/
getActiveSpace(): Promise<Space>;
/**
* UI components and services to add spaces capabilities to an application.
*/
ui: SpacesApiUi;
}
/**
* Function that returns a promise for a lazy-loadable component.
*/
export type LazyComponentFn<T> = (props: T) => ReactElement;
/**
* UI components and services to add spaces capabilities to an application.
*/
export interface SpacesApiUi {
/**
* Lazy-loadable {@link SpacesApiUiComponent | React components} to support the Spaces feature.
*/
components: SpacesApiUiComponent;
/**
* Redirect the user from a legacy URL to a new URL. This needs to be used if a call to `SavedObjectsClient.resolve()` results in an
* `"aliasMatch"` outcome, which indicates that the user has loaded the page using a legacy URL. Calling this function will trigger a
* client-side redirect to the new URL, and it will display a toast to the user.
*
* Consumers need to determine the local path for the new URL on their own, based on the object ID that was used to call
* `SavedObjectsClient.resolve()` (old ID) and the object ID in the result (new ID). For example...
*
* The old object ID is `workpad-123` and the new object ID is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`.
*
* Full legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1`
*
* New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1`
*
* The protocol, hostname, port, base path, and app path are automatically included.
*
* @param path The path to use for the new URL, optionally including `search` and/or `hash` URL components.
* @param objectNoun The string that is used to describe the object in the toast, e.g., _The **object** you're looking for has a new
* location_. Default value is 'object'.
*/
redirectLegacyUrl: (path: string, objectNoun?: string) => Promise<void>;
}
/**
* React UI components to be used to display the Spaces feature in any application.
*/
export interface SpacesApiUiComponent {
/**
* Provides a context that is required to render some Spaces components.
*/
getSpacesContextProvider: LazyComponentFn<SpacesContextProps>;
/**
* Displays a flyout to edit the spaces that an object is shared to.
*
* Note: must be rendered inside of a SpacesContext.
*/
getShareToSpaceFlyout: LazyComponentFn<ShareToSpaceFlyoutProps>;
/**
* Displays a corresponding list of spaces for a given list of saved object namespaces. It shows up to five spaces (and an indicator for
* any number of spaces that the user is not authorized to see) by default. If more than five named spaces would be displayed, the extras
* (along with the unauthorized spaces indicator, if present) are hidden behind a button. If '*' (aka "All spaces") is present, it
* supersedes all of the above and just displays a single badge without a button.
*
* Note: must be rendered inside of a SpacesContext.
*/
getSpaceList: LazyComponentFn<SpaceListProps>;
/**
* Displays a callout that needs to be used if a call to `SavedObjectsClient.resolve()` results in an `"conflict"` outcome, which
* indicates that the user has loaded the page which is associated directly with one object (A), *and* with a legacy URL that points to a
* different object (B).
*
* In this case, `SavedObjectsClient.resolve()` has returned object A. This component displays a warning callout to the user explaining
* that there is a conflict, and it includes a button that will redirect the user to object B when clicked.
*
* Consumers need to determine the local path for the new URL on their own, based on the object ID that was used to call
* `SavedObjectsClient.resolve()` (A) and the `alias_target_id` value in the response (B). For example...
*
* A is `workpad-123` and B is `workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e`.
*
* Full legacy URL: `https://localhost:5601/app/canvas#/workpad/workpad-123/page/1`
*
* New URL path: `#/workpad/workpad-e08b9bdb-ec14-4339-94c4-063bddfd610e/page/1`
*/
getLegacyUrlConflict: LazyComponentFn<LegacyUrlConflictProps>;
/**
* Displays an avatar for the given space.
*/
getSpaceAvatar: LazyComponentFn<SpaceAvatarProps>;
}
/**
* Properties for the SpacesContext.
*/
export interface SpacesContextProps {
/**
* If a feature is specified, all Spaces components will treat it appropriately if the feature is disabled in a given Space.
*/
feature?: string;
}
/**
* Properties for the ShareToSpaceFlyout.
*/
export interface ShareToSpaceFlyoutProps {
/**
* The object to render the flyout for.
*/
savedObjectTarget: ShareToSpaceSavedObjectTarget;
/**
* The EUI icon that is rendered in the flyout's title.
*
* Default is 'share'.
*/
flyoutIcon?: string;
/**
* The string that is rendered in the flyout's title.
*
* Default is 'Edit spaces for object'.
*/
flyoutTitle?: string;
/**
* When enabled, if the object is not yet shared to multiple spaces, a callout will be displayed that suggests the user might want to
* create a copy instead.
*
* Default value is false.
*/
enableCreateCopyCallout?: boolean;
/**
* When enabled, if no other spaces exist _and_ the user has the appropriate privileges, a sentence will be displayed that suggests the
* user might want to create a space.
*
* Default value is false.
*/
enableCreateNewSpaceLink?: boolean;
/**
* When set to 'within-space' (default), the flyout behaves like it is running on a page within the active space, and it will prevent the
* user from removing the object from the active space.
*
* Conversely, when set to 'outside-space', the flyout behaves like it is running on a page outside of any space, so it will allow the
* user to remove the object from the active space.
*/
behaviorContext?: 'within-space' | 'outside-space';
/**
* Optional handler that is called when the user has saved changes and there are spaces to be added to and/or removed from the object and
* its relatives. If this is not defined, a default handler will be used that calls `/api/spaces/_update_objects_spaces` and displays a
* toast indicating what occurred.
*/
changeSpacesHandler?: (
objects: Array<{ type: string; id: string }>,
spacesToAdd: string[],
spacesToRemove: string[]
) => Promise<void>;
/**
* Optional callback when the target object and its relatives are updated.
*/
onUpdate?: (updatedObjects: Array<{ type: string; id: string }>) => void;
/**
* Optional callback when the flyout is closed.
*/
onClose?: () => void;
}
/**
* Describes the target saved object during a share operation.
*/
export interface ShareToSpaceSavedObjectTarget {
/**
* The object's type.
*/
type: string;
/**
* The object's ID.
*/
id: string;
/**
* The namespaces that the object currently exists in.
*/
namespaces: string[];
/**
* The EUI icon that is rendered in the flyout's subtitle.
*
* Default is 'empty'.
*/
icon?: string;
/**
* The string that is rendered in the flyout's subtitle.
*
* Default is `${type} [id=${id}]`.
*/
title?: string;
/**
* The string that is used to describe the object in several places, e.g., _Make **object** available in selected spaces only_.
*
* Default value is 'object'.
*/
noun?: string;
}
/**
* Properties for the SpaceList component.
*/
export interface SpaceListProps {
/**
* The namespaces of a saved object to render into a corresponding list of spaces.
*/
namespaces: string[];
/**
* Optional limit to the number of spaces that can be displayed in the list. If the number of spaces exceeds this limit, they will be
* hidden behind a "show more" button. Set to 0 to disable.
*
* Default value is 5.
*/
displayLimit?: number;
/**
* When set to 'within-space' (default), the space list behaves like it is running on a page within the active space, and it will omit the
* active space (e.g., it displays a list of all the _other_ spaces that an object is shared to).
*
* Conversely, when set to 'outside-space', the space list behaves like it is running on a page outside of any space, so it will not omit
* the active space.
*/
behaviorContext?: 'within-space' | 'outside-space';
}
/**
* Properties for the LegacyUrlConflict component.
*/
export interface LegacyUrlConflictProps {
/**
* The string that is used to describe the object in the callout, e.g., _There is a legacy URL for this page that points to a different
* **object**_.
*
* Default value is 'object'.
*/
objectNoun?: string;
/**
* The ID of the object that is currently shown on the page.
*/
currentObjectId: string;
/**
* The ID of the other object that the legacy URL alias points to.
*/
otherObjectId: string;
/**
* The path to use for the new URL, optionally including `search` and/or `hash` URL components.
*/
otherObjectPath: string;
}
/**
* Properties for the SpaceAvatar component.
*/
export interface SpaceAvatarProps {
/** The space to represent with an avatar. */
space: Partial<Space>;
/** The size of the avatar. */
size?: 's' | 'm' | 'l' | 'xl';
/** Optional CSS class(es) to apply. */
className?: string;
/**
* When enabled, allows EUI to provide an aria-label for this component, which is announced on screen readers.
*
* Default value is true.
*/
announceSpaceName?: boolean;
/**
* Whether or not to render the avatar in a disabled state.
*
* Default value is false.
*/
isDisabled?: boolean;
}

View file

@ -1,31 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { SpacesOssPlugin } from './plugin';
export type {
SpacesOssPluginSetup,
SpacesOssPluginStart,
SpacesAvailableStartContract,
SpacesUnavailableStartContract,
} from './types';
export type {
LazyComponentFn,
SpacesApi,
SpacesApiUi,
SpacesApiUiComponent,
SpacesContextProps,
ShareToSpaceFlyoutProps,
ShareToSpaceSavedObjectTarget,
SpaceListProps,
LegacyUrlConflictProps,
SpaceAvatarProps,
} from './api';
export const plugin = () => new SpacesOssPlugin();

View file

@ -1,24 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SpacesOssPluginSetup, SpacesOssPluginStart } from '../';
import { spacesApiMock } from '../api.mock';
const createSetupContract = (): jest.Mocked<SpacesOssPluginSetup> => ({
registerSpacesApi: jest.fn(),
});
const createStartContract = (): jest.Mocked<SpacesOssPluginStart> => ({
isSpacesAvailable: true,
...spacesApiMock.create(),
});
export const spacesOssPluginMock = {
createSetupContract,
createStartContract,
};

View file

@ -1,54 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import { spacesApiMock } from './api.mock';
import { SpacesOssPlugin } from './plugin';
describe('SpacesOssPlugin', () => {
let plugin: SpacesOssPlugin;
beforeEach(() => {
plugin = new SpacesOssPlugin();
});
describe('#setup', () => {
it('only allows the API to be registered once', async () => {
const spacesApi = spacesApiMock.create();
const { registerSpacesApi } = plugin.setup();
expect(() => registerSpacesApi(spacesApi)).not.toThrow();
expect(() => registerSpacesApi(spacesApi)).toThrowErrorMatchingInlineSnapshot(
`"Spaces API can only be registered once"`
);
});
});
describe('#start', () => {
it('returns the spaces API if registered', async () => {
const spacesApi = spacesApiMock.create();
const { registerSpacesApi } = plugin.setup();
registerSpacesApi(spacesApi);
const { isSpacesAvailable, ...api } = plugin.start();
expect(isSpacesAvailable).toBe(true);
expect(api).toStrictEqual(spacesApi);
});
it('does not return the spaces API if not registered', async () => {
plugin.setup();
const { isSpacesAvailable, ...api } = plugin.start();
expect(isSpacesAvailable).toBe(false);
expect(Object.keys(api)).toHaveLength(0);
});
});
});

View file

@ -1,42 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { Plugin } from 'src/core/public';
import type { SpacesApi } from './api';
import type { SpacesOssPluginSetup, SpacesOssPluginStart } from './types';
export class SpacesOssPlugin implements Plugin<SpacesOssPluginSetup, SpacesOssPluginStart, {}, {}> {
private api?: SpacesApi;
constructor() {}
public setup() {
return {
registerSpacesApi: (provider: SpacesApi) => {
if (this.api) {
throw new Error('Spaces API can only be registered once');
}
this.api = provider;
},
};
}
public start() {
if (this.api) {
return {
isSpacesAvailable: true as true,
...this.api!,
};
} else {
return {
isSpacesAvailable: false as false,
};
}
}
}

View file

@ -1,48 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import type { SpacesApi } from './api';
/**
* OSS Spaces plugin start contract when the Spaces feature is enabled.
*/
export interface SpacesAvailableStartContract extends SpacesApi {
/** Indicates if the Spaces feature is enabled. */
isSpacesAvailable: true;
}
/**
* OSS Spaces plugin start contract when the Spaces feature is disabled.
* @deprecated The Spaces plugin will always be enabled starting in 8.0.
* @removeBy 8.0
*/
export interface SpacesUnavailableStartContract {
/** Indicates if the Spaces feature is enabled. */
isSpacesAvailable: false;
}
/**
* OSS Spaces plugin setup contract.
*/
export interface SpacesOssPluginSetup {
/**
* Register a provider for the Spaces API.
*
* Only one provider can be registered, subsequent calls to this method will fail.
*
* @param provider the API provider.
*
* @private designed to only be consumed by the `spaces` plugin.
*/
registerSpacesApi(provider: SpacesApi): void;
}
/**
* OSS Spaces plugin start contract.
*/
export type SpacesOssPluginStart = SpacesAvailableStartContract | SpacesUnavailableStartContract;

View file

@ -1,16 +0,0 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./target/types",
"emitDeclarationOnly": true,
"declaration": true,
"declarationMap": true
},
"include": [
"common/**/*",
"public/**/*",
],
"references": [
{ "path": "../../core/tsconfig.json" },
]
}

View file

@ -16,9 +16,26 @@
"references": [
{ "path": "../../../src/core/tsconfig.json" },
{ "path": "../../../src/plugins/maps_ems/tsconfig.json" },
{ "path": "../../../src/plugins/dashboard/tsconfig.json" },
{ "path": "../../../src/plugins/inspector/tsconfig.json" },
{ "path": "../../../src/plugins/data/tsconfig.json" },
{ "path": "../../../src/plugins/ui_actions/tsconfig.json" },
{ "path": "../../../src/plugins/navigation/tsconfig.json" },
{ "path": "../../../src/plugins/expressions/tsconfig.json" },
{ "path": "../../../src/plugins/visualizations/tsconfig.json" },
{ "path": "../../../src/plugins/embeddable/tsconfig.json" },
{ "path": "../../../src/plugins/saved_objects/tsconfig.json" },
{ "path": "../../../src/plugins/share/tsconfig.json" },
{ "path": "../../../src/plugins/presentation_util/tsconfig.json" },
{ "path": "../../../src/plugins/home/tsconfig.json" },
{ "path": "../../../src/plugins/charts/tsconfig.json" },
{ "path": "../../../src/plugins/usage_collection/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_react/tsconfig.json" },
{ "path": "../../../src/plugins/kibana_utils/tsconfig.json" },
{ "path": "../features/tsconfig.json" },
{ "path": "../licensing/tsconfig.json" },
{ "path": "../file_upload/tsconfig.json" },
{ "path": "../saved_objects_tagging/tsconfig.json" },
{ "path": "../security/tsconfig.json" }
]
}

View file

@ -9,13 +9,12 @@ import React, { FC, useCallback, useState } from 'react';
import { EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import type { ShareToSpaceFlyoutProps } from 'src/plugins/spaces_oss/public';
import {
JobType,
ML_SAVED_OBJECT_TYPE,
SavedObjectResult,
} from '../../../../common/types/saved_objects';
import type { SpacesPluginStart } from '../../../../../spaces/public';
import type { SpacesPluginStart, ShareToSpaceFlyoutProps } from '../../../../../spaces/public';
import { ml } from '../../services/ml_api_service';
import { useToastNotificationService } from '../../services/toast_notification_service';

View file

@ -22,7 +22,6 @@ import {
EuiFlexItem,
} from '@elastic/eui';
import type { SpacesContextProps } from 'src/plugins/spaces_oss/public';
import type { UsageCollectionSetup } from 'src/plugins/usage_collection/public';
import type { DataPublicPluginStart } from 'src/plugins/data/public';
import { PLUGIN_ID } from '../../../../../../common/constants/app';
@ -41,7 +40,7 @@ import { DataFrameAnalyticsList } from '../../../../data_frame_analytics/pages/a
import { AccessDeniedPage } from '../access_denied_page';
import { InsufficientLicensePage } from '../insufficient_license_page';
import type { SharePluginStart } from '../../../../../../../../../src/plugins/share/public';
import type { SpacesPluginStart } from '../../../../../../../spaces/public';
import type { SpacesPluginStart, SpacesContextProps } from '../../../../../../../spaces/public';
import { JobSpacesSyncFlyout } from '../../../../components/job_spaces_sync';
import { getDefaultAnomalyDetectionJobsListState } from '../../../../jobs/jobs_list/jobs';
import { getMlGlobalServices } from '../../../../app';

View file

@ -35,12 +35,11 @@ import type {
ScopedHistory,
} from 'src/core/public';
import type { IndexPatternsContract } from 'src/plugins/data/public';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import { reactRouterNavigate } from '../../../../../../../src/plugins/kibana_react/public';
import type { KibanaFeature } from '../../../../../features/common';
import type { FeaturesPluginStart } from '../../../../../features/public';
import type { Space } from '../../../../../spaces/public';
import type { Space, SpacesApiUi } from '../../../../../spaces/public';
import type { SecurityLicense } from '../../../../common/licensing';
import type {
BuiltinESPrivileges,

View file

@ -8,9 +8,8 @@
import React, { Component } from 'react';
import type { Capabilities } from 'src/core/public';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import type { Space } from '../../../../../../../spaces/public';
import type { Space, SpacesApiUi } from '../../../../../../../spaces/public';
import type { Role } from '../../../../../../common/model';
import type { KibanaPrivileges } from '../../../model';
import { CollapsiblePanel } from '../../collapsible_panel';

View file

@ -17,9 +17,8 @@ import {
import React, { Fragment, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import type { Space } from '../../../../../../../../spaces/public';
import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public';
import type { Role } from '../../../../../../../common/model';
import type { KibanaPrivileges } from '../../../../model';
import { PrivilegeSummaryTable } from './privilege_summary_table';

View file

@ -20,9 +20,8 @@ import {
import React, { Fragment, useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import type { Space } from '../../../../../../../../spaces/public';
import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public';
import type { Role, RoleKibanaPrivilege } from '../../../../../../../common/model';
import type { KibanaPrivileges, SecuredFeature } from '../../../../model';
import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';

View file

@ -9,9 +9,8 @@ import React, { Fragment, useMemo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public';
import type { RoleKibanaPrivilege } from '../../../../../../../common/model';
import { isGlobalPrivilegeDefinition } from '../../../privilege_utils';
import { SpacesPopoverList } from '../../../spaces_popover_list';

View file

@ -20,9 +20,8 @@ import React, { Component, Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Capabilities } from 'src/core/public';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import type { Space } from '../../../../../../../../spaces/public';
import type { Space, SpacesApiUi } from '../../../../../../../../spaces/public';
import type { Role } from '../../../../../../../common/model';
import { isRoleReserved } from '../../../../../../../common/model';
import type { KibanaPrivileges } from '../../../../model';

View file

@ -17,8 +17,8 @@ import React from 'react';
import { mountWithIntl } from '@kbn/test/jest';
import { coreMock } from 'src/core/public/mocks';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../../../../spaces/public';
import { SpaceAvatarInternal } from '../../../../../../spaces/public/space_avatar/space_avatar_internal';
import { spacesManagerMock } from '../../../../../../spaces/public/spaces_manager/mocks';
import { getUiApi } from '../../../../../../spaces/public/ui_api';

View file

@ -19,10 +19,9 @@ import React, { Component, memo } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { SpacesApiUi } from 'src/plugins/spaces_oss/public';
import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../../../../spaces/common';
import type { Space, SpacesApiUi } from '../../../../../../spaces/public';
interface Props {
spaces: Space[];

View file

@ -9,6 +9,7 @@ export { isReservedSpace } from './is_reserved_space';
export { MAX_SPACE_INITIALS, SPACE_SEARCH_COUNT_THRESHOLD, ENTER_SPACE_PATH } from './constants';
export { addSpaceIdToPath, getSpaceIdFromPath } from './lib/spaces_url_parser';
export type {
Space,
GetAllSpacesOptions,
GetAllSpacesPurpose,
GetSpaceResult,

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import type { Space } from 'src/plugins/spaces_oss/common';
import { isReservedSpace } from './is_reserved_space';
import type { Space } from './types';
test('it returns true for reserved spaces', () => {
const space: Space = {

View file

@ -7,7 +7,7 @@
import { get } from 'lodash';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from './types';
/**
* Returns whether the given Space is reserved or not.

View file

@ -5,7 +5,60 @@
* 2.0.
*/
import type { Space } from 'src/plugins/spaces_oss/common';
/**
* A Space.
*/
export interface Space {
/**
* The unique identifier for this space.
* The id becomes part of the "URL Identifier" of the space.
*
* Example: an id of `marketing` would result in the URL identifier of `/s/marketing`.
*/
id: string;
/**
* Display name for this space.
*/
name: string;
/**
* Optional description for this space.
*/
description?: string;
/**
* Optional color (hex code) for this space.
* If neither `color` nor `imageUrl` is specified, then a color will be automatically generated.
*/
color?: string;
/**
* Optional display initials for this space's avatar. Supports a maximum of 2 characters.
* If initials are not provided, then they will be derived from the space name automatically.
*
* Initials are not displayed if an `imageUrl` has been specified.
*/
initials?: string;
/**
* Optional base-64 encoded data image url to show as this space's avatar.
* This setting takes precedence over any configured `color` or `initials`.
*/
imageUrl?: string;
/**
* The set of feature ids that should be hidden within this space.
*/
disabledFeatures: string[];
/**
* Indicates that this space is reserved (system controlled).
* Reserved spaces cannot be created or deleted by end-users.
* @private
*/
_reserved?: boolean;
}
/**
* Controls how spaces are retrieved.

View file

@ -8,13 +8,12 @@
"version": "8.0.0",
"kibanaVersion": "kibana",
"configPath": ["xpack", "spaces"],
"requiredPlugins": ["features", "licensing", "spacesOss"],
"requiredPlugins": ["features", "licensing"],
"optionalPlugins": [
"advancedSettings",
"home",
"management",
"usageCollection",
"savedObjectsManagement"
"usageCollection"
],
"server": true,
"ui": true,
@ -22,7 +21,6 @@
"requiredBundles": [
"esUiShared",
"kibanaReact",
"savedObjectsManagement",
"home"
]
}

View file

@ -8,8 +8,8 @@
import React from 'react';
import type { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../common';
import { AdvancedSettingsSubtitle, AdvancedSettingsTitle } from './components';
interface SetupDeps {

View file

@ -9,7 +9,8 @@ import { EuiCallOut, EuiSpacer } from '@elastic/eui';
import React, { Fragment, useEffect, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../../common';
interface Props {
getActiveSpace: () => Promise<Space>;

View file

@ -9,8 +9,8 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner, EuiTitle } from '@elastic
import React, { lazy, Suspense, useEffect, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../../common';
import { getSpaceAvatarComponent } from '../../../space_avatar';
// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana.

View file

@ -11,14 +11,14 @@ import { EuiBadge, EuiIconTip, EuiLoadingSpinner } from '@elastic/eui';
import React, { Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { SpacesDataEntry } from '../../types';
import type { SummarizedCopyToSpaceResult } from '../lib';
import type { ImportRetry } from '../types';
import { ResolveAllConflicts } from './resolve_all_conflicts';
interface Props {
space: Space;
space: SpacesDataEntry;
summarizedCopyResult: SummarizedCopyToSpaceResult;
conflictResolutionInProgress: boolean;
retries: ImportRetry[];

View file

@ -7,7 +7,7 @@
import React from 'react';
import type { CopyToSpaceFlyoutProps } from './copy_to_space_flyout_internal';
import type { CopyToSpaceFlyoutProps } from '../types';
export const getCopyToSpaceFlyoutComponent = async (): Promise<
React.FC<CopyToSpaceFlyoutProps>

View file

@ -17,11 +17,8 @@ import React, { Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type {
FailedImport,
ProcessedImportResponse,
} from 'src/plugins/saved_objects_management/public';
import type { FailedImport, ProcessedImportResponse } from '../lib';
import type { ImportRetry } from '../types';
interface Props {
@ -62,8 +59,8 @@ export const CopyToSpaceFlyoutFooter = (props: Props) => {
let pendingCount = 0;
let skippedCount = 0;
let errorCount = 0;
if (spaceResult.status === 'success') {
successCount = spaceResult.importCount;
if (spaceResult.success === true) {
successCount = spaceResult.successfulImports.length;
} else {
const uniqueResolvableErrors = spaceResult.failedImports
.filter(isResolvableError)

View file

@ -12,11 +12,11 @@ import React from 'react';
import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest';
import { coreMock } from 'src/core/public/mocks';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../common';
import { getSpacesContextProviderWrapper } from '../../spaces_context';
import { spacesManagerMock } from '../../spaces_manager/mocks';
import type { SavedObjectTarget } from '../types';
import type { CopyToSpaceSavedObjectTarget } from '../types';
import { CopyModeControl } from './copy_mode_control';
import { getCopyToSpaceFlyoutComponent } from './copy_to_space_flyout';
import { CopyToSpaceForm } from './copy_to_space_form';
@ -82,7 +82,7 @@ const setup = async (opts: SetupOpts = {}) => {
namespaces: ['default'],
icon: 'dashboard',
title: 'foo',
} as SavedObjectTarget;
} as CopyToSpaceSavedObjectTarget;
const SpacesContext = await getSpacesContextProviderWrapper({
getStartServices,

View file

@ -24,31 +24,26 @@ import React, { useEffect, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import { processImportResponse } from '../../../../../../src/plugins/saved_objects_management/public';
import { useSpaces } from '../../spaces_context';
import type { CopyOptions, ImportRetry, SavedObjectTarget } from '../types';
import type { SpacesDataEntry } from '../../types';
import { processImportResponse } from '../lib';
import type { ProcessedImportResponse } from '../lib';
import type { CopyOptions, CopyToSpaceFlyoutProps, ImportRetry } from '../types';
import { CopyToSpaceFlyoutFooter } from './copy_to_space_flyout_footer';
import { CopyToSpaceForm } from './copy_to_space_form';
import { ProcessingCopyToSpace } from './processing_copy_to_space';
export interface CopyToSpaceFlyoutProps {
onClose: () => void;
savedObjectTarget: SavedObjectTarget;
}
const INCLUDE_RELATED_DEFAULT = true;
const CREATE_NEW_COPIES_DEFAULT = true;
const OVERWRITE_ALL_DEFAULT = true;
export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => {
const { spacesManager, services } = useSpaces();
const { spacesManager, spacesDataPromise, services } = useSpaces();
const { notifications } = services;
const toastNotifications = notifications!.toasts;
const { onClose, savedObjectTarget: object } = props;
const { onClose = () => null, savedObjectTarget: object } = props;
const savedObjectTarget = useMemo(
() => ({
type: object.type,
@ -66,22 +61,21 @@ export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => {
selectedSpaceIds: [],
});
const [{ isLoading, spaces }, setSpacesState] = useState<{ isLoading: boolean; spaces: Space[] }>(
{
isLoading: true,
spaces: [],
}
);
const [{ isLoading, spaces }, setSpacesState] = useState<{
isLoading: boolean;
spaces: SpacesDataEntry[];
}>({
isLoading: true,
spaces: [],
});
useEffect(() => {
const getSpaces = spacesManager.getSpaces({ includeAuthorizedPurposes: true });
const getActiveSpace = spacesManager.getActiveSpace();
Promise.all([getSpaces, getActiveSpace])
.then(([allSpaces, activeSpace]) => {
spacesDataPromise
.then(({ spacesMap }) => {
setSpacesState({
isLoading: false,
spaces: allSpaces.filter(
({ id, authorizedPurposes }) =>
id !== activeSpace.id && authorizedPurposes?.copySavedObjectsIntoSpace !== false
spaces: [...spacesMap.values()].filter(
({ isActiveSpace, isAuthorizedForPurpose }) =>
isActiveSpace || isAuthorizedForPurpose('copySavedObjectsIntoSpace')
),
});
})
@ -92,7 +86,7 @@ export const CopyToSpaceFlyoutInternal = (props: CopyToSpaceFlyoutProps) => {
}),
});
});
}, [spacesManager, toastNotifications]);
}, [spacesDataPromise, toastNotifications]);
const [copyInProgress, setCopyInProgress] = useState(false);
const [conflictResolutionInProgress, setConflictResolutionInProgress] = useState(false);

View file

@ -9,16 +9,16 @@ import { EuiFormRow, EuiSpacer, EuiTitle } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { CopyOptions, SavedObjectTarget } from '../types';
import type { SpacesDataEntry } from '../../types';
import type { CopyOptions, CopyToSpaceSavedObjectTarget } from '../types';
import type { CopyMode } from './copy_mode_control';
import { CopyModeControl } from './copy_mode_control';
import { SelectableSpacesControl } from './selectable_spaces_control';
interface Props {
savedObjectTarget: Required<SavedObjectTarget>;
spaces: Space[];
savedObjectTarget: Required<CopyToSpaceSavedObjectTarget>;
spaces: SpacesDataEntry[];
onUpdate: (copyOptions: CopyOptions) => void;
copyOptions: CopyOptions;
}

View file

@ -6,4 +6,3 @@
*/
export { getCopyToSpaceFlyoutComponent } from './copy_to_space_flyout';
export type { CopyToSpaceFlyoutProps } from './copy_to_space_flyout_internal';

View file

@ -15,21 +15,21 @@ import {
import React, { Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { ProcessedImportResponse } from 'src/plugins/saved_objects_management/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { SpacesDataEntry } from '../../types';
import type { ProcessedImportResponse } from '../lib';
import { summarizeCopyResult } from '../lib';
import type { CopyOptions, ImportRetry, SavedObjectTarget } from '../types';
import type { CopyOptions, CopyToSpaceSavedObjectTarget, ImportRetry } from '../types';
import { SpaceResult, SpaceResultProcessing } from './space_result';
interface Props {
savedObjectTarget: Required<SavedObjectTarget>;
savedObjectTarget: Required<CopyToSpaceSavedObjectTarget>;
copyInProgress: boolean;
conflictResolutionInProgress: boolean;
copyResult: Record<string, ProcessedImportResponse>;
retries: Record<string, ImportRetry[]>;
onRetriesChange: (retries: Record<string, ImportRetry[]>) => void;
spaces: Space[];
spaces: SpacesDataEntry[];
copyOptions: CopyOptions;
}
@ -95,7 +95,7 @@ export const ProcessingCopyToSpace = (props: Props) => {
</EuiText>
<EuiSpacer size="m" />
{props.copyOptions.selectedSpaceIds.map((id) => {
const space = props.spaces.find((s) => s.id === id) as Space;
const space = props.spaces.find((s) => s.id === id)!;
const spaceCopyResult = props.copyResult[space.id];
const summarizedSpaceCopyResult = summarizeCopyResult(
props.savedObjectTarget,

View file

@ -12,10 +12,10 @@ import { EuiIconTip, EuiLoadingSpinner, EuiSelectable } from '@elastic/eui';
import React, { lazy, Suspense } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import { SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common';
import { getSpaceAvatarComponent } from '../../space_avatar';
import type { SpacesDataEntry } from '../../types';
// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana.
const LazySpaceAvatar = lazy(() =>
@ -23,7 +23,7 @@ const LazySpaceAvatar = lazy(() =>
);
interface Props {
spaces: Space[];
spaces: SpacesDataEntry[];
selectedSpaceIds: string[];
disabledSpaceIds: Set<string>;
onChange: (selectedSpaceIds: string[]) => void;

View file

@ -17,9 +17,8 @@ import {
} from '@elastic/eui';
import React, { lazy, Suspense, useState } from 'react';
import type { Space } from 'src/plugins/spaces_oss/common';
import { getSpaceAvatarComponent } from '../../space_avatar';
import type { SpacesDataEntry } from '../../types';
import type { SummarizedCopyToSpaceResult } from '../lib';
import type { ImportRetry } from '../types';
import { CopyStatusSummaryIndicator } from './copy_status_summary_indicator';
@ -31,7 +30,7 @@ const LazySpaceAvatar = lazy(() =>
);
interface Props {
space: Space;
space: SpacesDataEntry;
summarizedCopyResult: SummarizedCopyToSpaceResult;
retries: ImportRetry[];
onRetriesChange: (retries: ImportRetry[]) => void;

View file

@ -25,15 +25,15 @@ import type {
SavedObjectsImportAmbiguousConflictError,
SavedObjectsImportConflictError,
} from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { SpacesDataEntry } from '../../types';
import type { SummarizedCopyToSpaceResult } from '../lib';
import type { ImportRetry } from '../types';
import { CopyStatusIndicator } from './copy_status_indicator';
interface Props {
summarizedCopyResult: SummarizedCopyToSpaceResult;
space: Space;
space: SpacesDataEntry;
retries: ImportRetry[];
onRetriesChange: (retries: ImportRetry[]) => void;
destinationMap: Map<string, string>;

View file

@ -1,91 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { lazy } from 'react';
import useAsync from 'react-use/lib/useAsync';
import { i18n } from '@kbn/i18n';
import type { StartServicesAccessor } from 'src/core/public';
import type { SavedObjectsManagementRecord } from 'src/plugins/saved_objects_management/public';
import { SavedObjectsManagementAction } from '../../../../../src/plugins/saved_objects_management/public';
import type { PluginsStart } from '../plugin';
import { SuspenseErrorBoundary } from '../suspense_error_boundary';
import type { CopyToSpaceFlyoutProps } from './components';
import { getCopyToSpaceFlyoutComponent } from './components';
const LazyCopyToSpaceFlyout = lazy(() =>
getCopyToSpaceFlyoutComponent().then((component) => ({ default: component }))
);
interface WrapperProps {
getStartServices: StartServicesAccessor<PluginsStart>;
props: CopyToSpaceFlyoutProps;
}
const Wrapper = ({ getStartServices, props }: WrapperProps) => {
const { value: startServices = [{ notifications: undefined }] } = useAsync(getStartServices);
const [{ notifications }] = startServices;
if (!notifications) {
return null;
}
return (
<SuspenseErrorBoundary notifications={notifications}>
<LazyCopyToSpaceFlyout {...props} />
</SuspenseErrorBoundary>
);
};
export class CopyToSpaceSavedObjectsManagementAction extends SavedObjectsManagementAction {
public id: string = 'copy_saved_objects_to_space';
public euiAction = {
name: i18n.translate('xpack.spaces.management.copyToSpace.actionTitle', {
defaultMessage: 'Copy to space',
}),
description: i18n.translate('xpack.spaces.management.copyToSpace.actionDescription', {
defaultMessage: 'Make a copy of this saved object in one or more spaces',
}),
icon: 'copy',
type: 'icon',
available: (object: SavedObjectsManagementRecord) => {
return object.meta.namespaceType !== 'agnostic' && !object.meta.hiddenType;
},
onClick: (object: SavedObjectsManagementRecord) => {
this.start(object);
},
};
constructor(private getStartServices: StartServicesAccessor<PluginsStart>) {
super();
}
public render = () => {
if (!this.record) {
throw new Error('No record available! `render()` was likely called before `start()`.');
}
const props: CopyToSpaceFlyoutProps = {
onClose: this.onClose,
savedObjectTarget: {
type: this.record.type,
id: this.record.id,
namespaces: this.record.namespaces ?? [],
title: this.record.meta.title,
icon: this.record.meta.icon,
},
};
return <Wrapper getStartServices={this.getStartServices} props={props} />;
};
private onClose = () => {
this.finish();
};
}

View file

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { coreMock } from 'src/core/public/mocks';
import { savedObjectsManagementPluginMock } from 'src/plugins/saved_objects_management/public/mocks';
import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action';
import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service';
describe('CopySavedObjectsToSpaceService', () => {
describe('#setup', () => {
it('registers the CopyToSpaceSavedObjectsManagementAction', () => {
const { getStartServices } = coreMock.createSetup();
const deps = {
savedObjectsManagementSetup: savedObjectsManagementPluginMock.createSetupContract(),
getStartServices,
};
const service = new CopySavedObjectsToSpaceService();
service.setup(deps);
expect(deps.savedObjectsManagementSetup.actions.register).toHaveBeenCalledTimes(1);
expect(deps.savedObjectsManagementSetup.actions.register).toHaveBeenCalledWith(
expect.any(CopyToSpaceSavedObjectsManagementAction)
);
});
});
});

View file

@ -1,24 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { StartServicesAccessor } from 'src/core/public';
import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public';
import type { PluginsStart } from '../plugin';
import { CopyToSpaceSavedObjectsManagementAction } from './copy_saved_objects_to_space_action';
interface SetupDeps {
savedObjectsManagementSetup: SavedObjectsManagementPluginSetup;
getStartServices: StartServicesAccessor<PluginsStart>;
}
export class CopySavedObjectsToSpaceService {
public setup({ savedObjectsManagementSetup, getStartServices }: SetupDeps) {
const action = new CopyToSpaceSavedObjectsManagementAction(getStartServices);
savedObjectsManagementSetup.actions.register(action);
}
}

View file

@ -6,4 +6,4 @@
*/
export { getCopyToSpaceFlyoutComponent } from './components';
export { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space_service';
export type { CopyToSpaceFlyoutProps, CopyToSpaceSavedObjectTarget } from './types';

View file

@ -5,6 +5,9 @@
* 2.0.
*/
export type { FailedImport, ProcessedImportResponse } from './process_import_response';
export { processImportResponse } from './process_import_response';
export type {
SummarizedCopyToSpaceResult,
SummarizedSavedObjectResult,

View file

@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type {
SavedObjectsImportAmbiguousConflictError,
SavedObjectsImportConflictError,
SavedObjectsImportFailure,
SavedObjectsImportMissingReferencesError,
SavedObjectsImportResponse,
SavedObjectsImportSuccess,
SavedObjectsImportUnknownError,
SavedObjectsImportUnsupportedTypeError,
} from 'src/core/public';
export interface FailedImport {
obj: Omit<SavedObjectsImportFailure, 'error'>;
error:
| SavedObjectsImportConflictError
| SavedObjectsImportAmbiguousConflictError
| SavedObjectsImportUnsupportedTypeError
| SavedObjectsImportMissingReferencesError
| SavedObjectsImportUnknownError;
}
export interface ProcessedImportResponse {
success: boolean;
failedImports: FailedImport[];
successfulImports: SavedObjectsImportSuccess[];
}
// This is derived from the function of the same name in the savedObjectsManagement plugin
export function processImportResponse(
response: SavedObjectsImportResponse
): ProcessedImportResponse {
const { success, errors = [], successResults = [] } = response;
const failedImports = errors.map<FailedImport>(({ error, ...obj }) => ({ obj, error }));
return {
success,
failedImports,
successfulImports: successResults,
};
}

View file

@ -5,13 +5,8 @@
* 2.0.
*/
import type {
FailedImport,
ProcessedImportResponse,
SavedObjectsManagementRecord,
} from 'src/plugins/saved_objects_management/public';
import type { SavedObjectTarget } from '../types';
import type { FailedImport, ProcessedImportResponse } from '../lib';
import type { CopyToSpaceSavedObjectTarget } from '../types';
import { summarizeCopyResult } from './summarize_copy_result';
// Sample data references:
@ -29,7 +24,7 @@ const OBJECTS = {
namespaces: [],
icon: 'dashboardApp',
title: 'my-dashboard-title',
} as Required<SavedObjectTarget>,
} as Required<CopyToSpaceSavedObjectTarget>,
MY_DASHBOARD: {
type: 'dashboard',
id: 'foo',
@ -43,7 +38,7 @@ const OBJECTS = {
{ type: 'visualization', id: 'foo', name: 'Visualization foo' },
{ type: 'visualization', id: 'bar', name: 'Visualization bar' },
],
} as SavedObjectsManagementRecord,
},
VISUALIZATION_FOO: {
type: 'visualization',
id: 'bar',
@ -54,7 +49,7 @@ const OBJECTS = {
hiddenType: false,
},
references: [{ type: 'index-pattern', id: 'foo', name: 'Index pattern foo' }],
} as SavedObjectsManagementRecord,
},
VISUALIZATION_BAR: {
type: 'visualization',
id: 'baz',
@ -65,7 +60,7 @@ const OBJECTS = {
hiddenType: false,
},
references: [{ type: 'index-pattern', id: 'bar', name: 'Index pattern bar' }],
} as SavedObjectsManagementRecord,
},
INDEX_PATTERN_FOO: {
type: 'index-pattern',
id: 'foo',
@ -76,7 +71,7 @@ const OBJECTS = {
hiddenType: false,
},
references: [],
} as SavedObjectsManagementRecord,
},
INDEX_PATTERN_BAR: {
type: 'index-pattern',
id: 'bar',
@ -87,7 +82,7 @@ const OBJECTS = {
hiddenType: false,
},
references: [],
} as SavedObjectsManagementRecord,
},
};
interface ObjectProperties {

View file

@ -9,12 +9,9 @@ import type {
SavedObjectsImportAmbiguousConflictError,
SavedObjectsImportConflictError,
} from 'src/core/public';
import type {
FailedImport,
ProcessedImportResponse,
} from 'src/plugins/saved_objects_management/public';
import type { SavedObjectTarget } from '../types';
import type { FailedImport, ProcessedImportResponse } from '../lib';
import type { CopyToSpaceSavedObjectTarget } from '../types';
export interface SummarizedSavedObjectResult {
type: string;
@ -68,7 +65,7 @@ export type SummarizedCopyToSpaceResult =
| ProcessingResponse;
export function summarizeCopyResult(
savedObjectTarget: Required<SavedObjectTarget>,
savedObjectTarget: Required<CopyToSpaceSavedObjectTarget>,
copyResult: ProcessedImportResponse | undefined
): SummarizedCopyToSpaceResult {
const conflicts = copyResult?.failedImports.filter(isAnyConflict) ?? [];

View file

@ -20,7 +20,24 @@ export interface CopySavedObjectsToSpaceResponse {
[spaceId: string]: SavedObjectsImportResponse;
}
export interface SavedObjectTarget {
/**
* Properties for the CopyToSpaceFlyout.
*/
export interface CopyToSpaceFlyoutProps {
/**
* The object to render the flyout for.
*/
savedObjectTarget: CopyToSpaceSavedObjectTarget;
/**
* Optional callback when the flyout is closed.
*/
onClose?: () => void;
}
/**
* Describes the target saved object during a copy operation.
*/
export interface CopyToSpaceSavedObjectTarget {
/**
* The object's type.
*/

View file

@ -11,10 +11,28 @@ export { getSpaceColor, getSpaceImageUrl, getSpaceInitials } from './space_avata
export { SpacesPluginSetup, SpacesPluginStart } from './plugin';
export type { GetAllSpacesPurpose, GetSpaceResult } from '../common';
export type { Space, GetAllSpacesPurpose, GetSpaceResult } from '../common';
// re-export types from oss definition
export type { Space } from 'src/plugins/spaces_oss/common';
export type { SpacesData, SpacesDataEntry, SpacesApi } from './types';
export type {
CopyToSpaceFlyoutProps,
CopyToSpaceSavedObjectTarget,
} from './copy_saved_objects_to_space';
export type {
LegacyUrlConflictProps,
ShareToSpaceFlyoutProps,
ShareToSpaceSavedObjectTarget,
} from './share_saved_objects_to_space';
export type { SpaceAvatarProps } from './space_avatar';
export type { SpaceListProps } from './space_list';
export type { SpacesContextProps, SpacesReactContextValue } from './spaces_context';
export type { LazyComponentFn, SpacesApiUi, SpacesApiUiComponent } from './ui_api';
export const plugin = () => {
return new SpacesPlugin();

View file

@ -13,9 +13,9 @@ import useAsyncFn from 'react-use/lib/useAsyncFn';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import type { Space } from '../../../../common';
import type { SpacesManager } from '../../../spaces_manager';
interface Props {

View file

@ -12,8 +12,8 @@ import React, { Component, Fragment } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { NotificationsStart } from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../common';
import type { SpacesManager } from '../../spaces_manager';
import { ConfirmDeleteModal } from '../components/confirm_delete_modal';

View file

@ -11,10 +11,10 @@ import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import { useKibana } from '../../../../../../../src/plugins/kibana_react/public';
import type { KibanaFeatureConfig } from '../../../../../features/public';
import type { Space } from '../../../../common';
import { SectionPanel } from '../section_panel';
import { FeatureTable } from './feature_table';

View file

@ -25,9 +25,9 @@ import React, { Component } from 'react';
import { i18n } from '@kbn/i18n';
import type { AppCategory } from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { KibanaFeatureConfig } from '../../../../../features/public';
import type { Space } from '../../../../common';
import { getEnabledFeatures } from '../../lib/feature_utils';
interface Props {

View file

@ -23,10 +23,10 @@ import React, { Component } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Capabilities, NotificationsStart, ScopedHistory } from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import { SectionLoading } from '../../../../../../src/plugins/es_ui_shared/public';
import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public';
import type { Space } from '../../../common';
import { isReservedSpace } from '../../../common';
import { getSpacesFeatureDescription } from '../../constants';
import { getSpaceColor, getSpaceInitials } from '../../space_avatar';

View file

@ -9,8 +9,8 @@ import { EuiBadge, EuiToolTip } from '@elastic/eui';
import React from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../common';
import { isReservedSpace } from '../../../common';
interface Props {

View file

@ -5,9 +5,8 @@
* 2.0.
*/
import type { Space } from 'src/plugins/spaces_oss/common';
import type { KibanaFeatureConfig } from '../../../../features/common';
import type { Space } from '../../../common';
export function getEnabledFeatures(features: KibanaFeatureConfig[], space: Partial<Space>) {
return features.filter((feature) => !(space.disabledFeatures || []).includes(feature.id));

View file

@ -26,10 +26,10 @@ import type {
NotificationsStart,
ScopedHistory,
} from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import { reactRouterNavigate } from '../../../../../../src/plugins/kibana_react/public';
import type { FeaturesPluginStart, KibanaFeature } from '../../../../features/public';
import type { Space } from '../../../common';
import { isReservedSpace } from '../../../common';
import { DEFAULT_SPACE_ID } from '../../../common/constants';
import { getSpacesFeatureDescription } from '../../constants';

View file

@ -12,13 +12,13 @@ import { Route, Router, Switch, useParams } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import type { StartServicesAccessor } from 'src/core/public';
import type { RegisterManagementAppArgs } from 'src/plugins/management/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import { APP_WRAPPER_CLASS } from '../../../../../src/core/public';
import {
KibanaContextProvider,
RedirectAppLinks,
} from '../../../../../src/plugins/kibana_react/public';
import type { Space } from '../../common';
import type { PluginsStart } from '../plugin';
import type { SpacesManager } from '../spaces_manager';

View file

@ -1,14 +1,15 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { of } from 'rxjs';
import type { SpacesApi, SpacesApiUi, SpacesApiUiComponent } from './api';
import type { SpacesPluginStart } from './plugin';
import type { SpacesApi } from './types';
import type { SpacesApiUi, SpacesApiUiComponent } from './ui_api';
const createApiMock = (): jest.Mocked<SpacesApi> => ({
getActiveSpace$: jest.fn().mockReturnValue(of()),
@ -24,6 +25,7 @@ const createApiUiMock = () => {
const mock: SpacesApiUiMock = {
components: createApiUiComponentsMock(),
redirectLegacyUrl: jest.fn(),
useSpaces: jest.fn(),
};
return mock;
@ -35,6 +37,7 @@ const createApiUiComponentsMock = () => {
const mock: SpacesApiUiComponentMock = {
getSpacesContextProvider: jest.fn(),
getShareToSpaceFlyout: jest.fn(),
getCopyToSpaceFlyout: jest.fn(),
getSpaceList: jest.fn(),
getLegacyUrlConflict: jest.fn(),
getSpaceAvatar: jest.fn(),
@ -43,6 +46,8 @@ const createApiUiComponentsMock = () => {
return mock;
};
export const spacesApiMock = {
create: createApiMock,
const createStartContract = (): jest.Mocked<SpacesPluginStart> => createApiMock();
export const spacesPluginMock = {
createStartContract,
};

View file

@ -21,8 +21,8 @@ import React, { Component, lazy, Suspense } from 'react';
import type { InjectedIntl } from '@kbn/i18n/react';
import { FormattedMessage, injectI18n } from '@kbn/i18n/react';
import type { ApplicationStart, Capabilities } from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../common';
import { addSpaceIdToPath, ENTER_SPACE_PATH, SPACE_SEARCH_COUNT_THRESHOLD } from '../../../common';
import { getSpaceAvatarComponent } from '../../space_avatar';
import { ManageSpacesButton } from './manage_spaces_button';

View file

@ -11,8 +11,8 @@ import React, { Component, lazy, Suspense } from 'react';
import type { Subscription } from 'rxjs';
import type { ApplicationStart, Capabilities } from 'src/core/public';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../common';
import { getSpaceAvatarComponent } from '../space_avatar';
import type { SpacesManager } from '../spaces_manager';
import { SpacesDescription } from './components/spaces_description';

View file

@ -12,7 +12,6 @@ import {
createManagementSectionMock,
managementPluginMock,
} from 'src/plugins/management/public/mocks';
import { spacesOssPluginMock } from 'src/plugins/spaces_oss/public/mocks';
import { SpacesPlugin } from './plugin';
@ -20,12 +19,9 @@ describe('Spaces plugin', () => {
describe('#setup', () => {
it('should register the spaces API and the space selector app', () => {
const coreSetup = coreMock.createSetup();
const spacesOss = spacesOssPluginMock.createSetupContract();
const plugin = new SpacesPlugin();
plugin.setup(coreSetup, { spacesOss });
expect(spacesOss.registerSpacesApi).toHaveBeenCalledTimes(1);
plugin.setup(coreSetup, {});
expect(coreSetup.application.register).toHaveBeenCalledWith(
expect.objectContaining({
@ -39,7 +35,6 @@ describe('Spaces plugin', () => {
it('should register the management and feature catalogue sections when the management and home plugins are both available', () => {
const coreSetup = coreMock.createSetup();
const spacesOss = spacesOssPluginMock.createSetupContract();
const home = homePluginMock.createSetupContract();
const management = managementPluginMock.createSetupContract();
@ -50,7 +45,6 @@ describe('Spaces plugin', () => {
const plugin = new SpacesPlugin();
plugin.setup(coreSetup, {
spacesOss,
management,
home,
});
@ -71,11 +65,10 @@ describe('Spaces plugin', () => {
it('should register the advanced settings components if the advanced_settings plugin is available', () => {
const coreSetup = coreMock.createSetup();
const spacesOss = spacesOssPluginMock.createSetupContract();
const advancedSettings = advancedSettingsMock.createSetupContract();
const plugin = new SpacesPlugin();
plugin.setup(coreSetup, { spacesOss, advancedSettings });
plugin.setup(coreSetup, { advancedSettings });
expect(advancedSettings.component.register.mock.calls).toMatchInlineSnapshot(`
Array [
@ -97,11 +90,10 @@ describe('Spaces plugin', () => {
describe('#start', () => {
it('should register the spaces nav control', () => {
const coreSetup = coreMock.createSetup();
const spacesOss = spacesOssPluginMock.createSetupContract();
const coreStart = coreMock.createStart();
const plugin = new SpacesPlugin();
plugin.setup(coreSetup, { spacesOss });
plugin.setup(coreSetup, {});
plugin.start(coreStart);

View file

@ -9,26 +9,21 @@ import type { CoreSetup, CoreStart, Plugin } from 'src/core/public';
import type { AdvancedSettingsSetup } from 'src/plugins/advanced_settings/public';
import type { HomePublicPluginSetup } from 'src/plugins/home/public';
import type { ManagementSetup, ManagementStart } from 'src/plugins/management/public';
import type { SavedObjectsManagementPluginSetup } from 'src/plugins/saved_objects_management/public';
import type { SpacesApi, SpacesOssPluginSetup } from 'src/plugins/spaces_oss/public';
import type { FeaturesPluginStart } from '../../features/public';
import { AdvancedSettingsService } from './advanced_settings';
import { CopySavedObjectsToSpaceService } from './copy_saved_objects_to_space';
import { createSpacesFeatureCatalogueEntry } from './create_feature_catalogue_entry';
import { ManagementService } from './management';
import { initSpacesNavControl } from './nav_control';
import { ShareSavedObjectsToSpaceService } from './share_saved_objects_to_space';
import { spaceSelectorApp } from './space_selector';
import { SpacesManager } from './spaces_manager';
import type { SpacesApi } from './types';
import { getUiApi } from './ui_api';
export interface PluginsSetup {
spacesOss: SpacesOssPluginSetup;
advancedSettings?: AdvancedSettingsSetup;
home?: HomePublicPluginSetup;
management?: ManagementSetup;
savedObjectsManagement?: SavedObjectsManagementPluginSetup;
}
export interface PluginsStart {
@ -84,27 +79,12 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
});
}
if (plugins.savedObjectsManagement) {
const shareSavedObjectsToSpaceService = new ShareSavedObjectsToSpaceService();
shareSavedObjectsToSpaceService.setup({
savedObjectsManagementSetup: plugins.savedObjectsManagement,
spacesApiUi: this.spacesApi.ui,
});
const copySavedObjectsToSpaceService = new CopySavedObjectsToSpaceService();
copySavedObjectsToSpaceService.setup({
savedObjectsManagementSetup: plugins.savedObjectsManagement,
getStartServices: core.getStartServices,
});
}
spaceSelectorApp.create({
getStartServices: core.getStartServices,
application: core.application,
spacesManager: this.spacesManager,
});
plugins.spacesOss.registerSpacesApi(this.spacesApi);
return {};
}

View file

@ -19,7 +19,7 @@ import React, { lazy, Suspense, useMemo, useState } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import { getSpaceAvatarComponent } from '../../space_avatar';
import type { ShareToSpaceTarget } from '../../types';
import type { SpacesDataEntry } from '../../types';
import type { InternalLegacyUrlAliasTarget } from './types';
// No need to wrap LazySpaceAvatar in an error boundary, because it is one of the first chunks loaded when opening Kibana.
@ -28,7 +28,7 @@ const LazySpaceAvatar = lazy(() =>
);
interface Props {
spaces: ShareToSpaceTarget[];
spaces: SpacesDataEntry[];
aliasesToDisable: InternalLegacyUrlAliasTarget[];
}
@ -38,10 +38,7 @@ export const AliasTable: FunctionComponent<Props> = ({ spaces, aliasesToDisable
const spacesMap = useMemo(
() =>
spaces.reduce(
(acc, space) => acc.set(space.id, space),
new Map<string, ShareToSpaceTarget>()
),
spaces.reduce((acc, space) => acc.set(space.id, space), new Map<string, SpacesDataEntry>()),
[spaces]
);
const filteredAliasesToDisable = useMemo(

View file

@ -7,8 +7,7 @@
import React from 'react';
import type { LegacyUrlConflictProps } from 'src/plugins/spaces_oss/public';
import type { LegacyUrlConflictProps } from '../types';
import type { InternalProps } from './legacy_url_conflict_internal';
export const getLegacyUrlConflict = async (

View file

@ -18,9 +18,9 @@ import { first } from 'rxjs/operators';
import { FormattedMessage } from '@kbn/i18n/react';
import type { ApplicationStart, StartServicesAccessor } from 'src/core/public';
import type { LegacyUrlConflictProps } from 'src/plugins/spaces_oss/public';
import type { PluginsStart } from '../../plugin';
import type { LegacyUrlConflictProps } from '../types';
import { DEFAULT_OBJECT_NOUN } from './constants';
export interface InternalProps {

View file

@ -10,7 +10,8 @@ import React, { useMemo } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { SavedObjectReferenceWithContext } from 'src/core/public';
import type { ShareToSpaceSavedObjectTarget } from 'src/plugins/spaces_oss/public';
import type { ShareToSpaceSavedObjectTarget } from '../types';
interface Props {
savedObjectTarget: ShareToSpaceSavedObjectTarget;

View file

@ -29,7 +29,7 @@ import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants';
import { DocumentationLinksService } from '../../lib';
import { getSpaceAvatarComponent } from '../../space_avatar';
import { useSpaces } from '../../spaces_context';
import type { ShareToSpaceTarget } from '../../types';
import type { SpacesDataEntry } from '../../types';
import type { ShareOptions } from '../types';
import { NoSpacesAvailable } from './no_spaces_available';
@ -39,7 +39,7 @@ const LazySpaceAvatar = lazy(() =>
);
interface Props {
spaces: ShareToSpaceTarget[];
spaces: SpacesDataEntry[];
shareOptions: ShareOptions;
onChange: (selectedSpaceIds: string[]) => void;
enableCreateNewSpaceLink: boolean;
@ -248,7 +248,7 @@ export const SelectableSpacesControl = (props: Props) => {
* Gets additional props for the selection option.
*/
function getAdditionalProps(
space: ShareToSpaceTarget,
space: SpacesDataEntry,
activeSpaceId: string | false,
checked: boolean
) {
@ -259,7 +259,7 @@ function getAdditionalProps(
checked: 'on' as 'on',
};
}
if (space.cannotShareToSpace) {
if (!space.isAuthorizedForPurpose('shareSavedObjectsIntoSpace')) {
return {
append: (
<>
@ -280,11 +280,11 @@ function getAdditionalProps(
}
/**
* Given the active space, create a comparator to sort a ShareToSpaceTarget array so that the active space is at the beginning, and space(s) for
* Given the active space, create a comparator to sort a SpacesDataEntry array so that the active space is at the beginning, and space(s) for
* which the current feature is disabled are all at the end.
*/
function createSpacesComparator(activeSpaceId: string | false) {
return (a: ShareToSpaceTarget, b: ShareToSpaceTarget) => {
return (a: SpacesDataEntry, b: SpacesDataEntry) => {
if (a.id === activeSpaceId) {
return -1;
}

View file

@ -24,12 +24,12 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { ALL_SPACES_ID } from '../../../common/constants';
import { DocumentationLinksService } from '../../lib';
import { useSpaces } from '../../spaces_context';
import type { ShareToSpaceTarget } from '../../types';
import type { SpacesDataEntry } from '../../types';
import type { ShareOptions } from '../types';
import { SelectableSpacesControl } from './selectable_spaces_control';
interface Props {
spaces: ShareToSpaceTarget[];
spaces: SpacesDataEntry[];
objectNoun: string;
canShareToAllSpaces: boolean;
shareOptions: ShareOptions;

View file

@ -7,7 +7,7 @@
import React from 'react';
import type { ShareToSpaceFlyoutProps } from 'src/plugins/spaces_oss/public';
import type { ShareToSpaceFlyoutProps } from '../types';
export const getShareToSpaceFlyoutComponent = async (): Promise<
React.FC<ShareToSpaceFlyoutProps>

View file

@ -14,8 +14,8 @@ import React from 'react';
import { findTestSubject, mountWithIntl, nextTick } from '@kbn/test/jest';
import type { SavedObjectReferenceWithContext } from 'src/core/public';
import { coreMock } from 'src/core/public/mocks';
import type { Space } from 'src/plugins/spaces_oss/common';
import type { Space } from '../../../common';
import { ALL_SPACES_ID } from '../../../common/constants';
import { CopyToSpaceFlyoutInternal } from '../../copy_saved_objects_to_space/components/copy_to_space_flyout_internal';
import { getSpacesContextProviderWrapper } from '../../spaces_context';

View file

@ -26,17 +26,17 @@ import React, { lazy, Suspense, useEffect, useMemo, useState } from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n/react';
import type { SavedObjectReferenceWithContext, ToastsStart } from 'src/core/public';
import type {
ShareToSpaceFlyoutProps,
ShareToSpaceSavedObjectTarget,
} from 'src/plugins/spaces_oss/public';
import { ALL_SPACES_ID, UNKNOWN_SPACE } from '../../../common/constants';
import { getCopyToSpaceFlyoutComponent } from '../../copy_saved_objects_to_space';
import { useSpaces } from '../../spaces_context';
import type { SpacesManager } from '../../spaces_manager';
import type { ShareToSpaceTarget } from '../../types';
import type { ShareOptions } from '../types';
import type { SpacesDataEntry } from '../../types';
import type {
ShareOptions,
ShareToSpaceFlyoutProps,
ShareToSpaceSavedObjectTarget,
} from '../types';
import { AliasTable } from './alias_table';
import { DEFAULT_OBJECT_NOUN } from './constants';
import { RelativesFooter } from './relatives_footer';
@ -124,7 +124,7 @@ function createDefaultChangeSpacesHandler(
}
export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => {
const { spacesManager, shareToSpacesDataPromise, services } = useSpaces();
const { spacesManager, spacesDataPromise, services } = useSpaces();
const { notifications } = services;
const toastNotifications = notifications!.toasts;
@ -168,7 +168,7 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => {
const [{ isLoading, spaces, referenceGraph, aliasTargets }, setSpacesState] = useState<{
isLoading: boolean;
spaces: ShareToSpaceTarget[];
spaces: SpacesDataEntry[];
referenceGraph: SavedObjectReferenceWithContext[];
aliasTargets: InternalLegacyUrlAliasTarget[];
}>({ isLoading: true, spaces: [], referenceGraph: [], aliasTargets: [] });
@ -176,9 +176,9 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => {
const { type, id } = savedObjectTarget;
const getShareableReferences = spacesManager.getShareableReferences([{ type, id }]);
const getPermissions = spacesManager.getShareSavedObjectPermissions(type);
Promise.all([shareToSpacesDataPromise, getShareableReferences, getPermissions])
.then(([shareToSpacesData, shareableReferences, permissions]) => {
const activeSpaceId = !enableSpaceAgnosticBehavior && shareToSpacesData.activeSpaceId;
Promise.all([spacesDataPromise, getShareableReferences, getPermissions])
.then(([spacesData, shareableReferences, permissions]) => {
const activeSpaceId = !enableSpaceAgnosticBehavior && spacesData.activeSpaceId;
const selectedSpaceIds = savedObjectTarget.namespaces.filter(
(spaceId) => spaceId !== activeSpaceId
);
@ -189,13 +189,13 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => {
setCanShareToAllSpaces(permissions.shareToAllSpaces);
setSpacesState({
isLoading: false,
spaces: [...shareToSpacesData.spacesMap].map(([, spaceTarget]) => spaceTarget),
spaces: [...spacesData.spacesMap].map(([, spaceTarget]) => spaceTarget),
referenceGraph: shareableReferences.objects,
aliasTargets: shareableReferences.objects.reduce<InternalLegacyUrlAliasTarget[]>(
(acc, x) => {
for (const space of x.spacesWithMatchingAliases ?? []) {
if (space !== '?') {
const spaceExists = shareToSpacesData.spacesMap.has(space);
const spaceExists = spacesData.spacesMap.has(space);
// If the user does not have privileges to view all spaces, they will be redacted; we cannot attempt to disable aliases for redacted spaces.
acc.push({ targetSpace: space, targetType: x.type, sourceId: x.id, spaceExists });
}
@ -216,7 +216,7 @@ export const ShareToSpaceFlyoutInternal = (props: ShareToSpaceFlyoutProps) => {
}, [
savedObjectTarget,
spacesManager,
shareToSpacesDataPromise,
spacesDataPromise,
toastNotifications,
enableSpaceAgnosticBehavior,
]);

View file

@ -12,12 +12,12 @@ import React, { Fragment } from 'react';
import { FormattedMessage } from '@kbn/i18n/react';
import type { ShareToSpaceTarget } from '../../types';
import type { SpacesDataEntry } from '../../types';
import type { ShareOptions } from '../types';
import { ShareModeControl } from './share_mode_control';
interface Props {
spaces: ShareToSpaceTarget[];
spaces: SpacesDataEntry[];
objectNoun: string;
onUpdate: (shareOptions: ShareOptions) => void;
shareOptions: ShareOptions;

Some files were not shown because too many files have changed in this diff Show more