Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spaces - Client NP Migration, Phase 1 #40856

Merged
merged 66 commits into from
Dec 16, 2019
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
f80f888
shimming NP for spaces client-side plugin
legrego Jul 11, 2019
452d592
refresh active space in nav control when updated
legrego Jul 11, 2019
b41073c
fix advanced settings screen
legrego Jul 11, 2019
1677dc4
allow npStart from unauthed routes
legrego Jul 11, 2019
a014d72
use NP for deriving space management url
legrego Jul 11, 2019
b0bf4dd
remove security's usage of SpacesManager
legrego Jul 11, 2019
c46f283
remove usages of ui/capabilities
legrego Jul 11, 2019
aed3888
fix tests
legrego Jul 11, 2019
2aa3b71
implement NP plugin interface
legrego Jul 11, 2019
b039a99
remove hack in favor of convention in migration guide
legrego Jul 11, 2019
8c161f2
shim feature catalogue registration
legrego Jul 11, 2019
3a010f1
streamline nav control, and handle async loading more gracefully
legrego Jul 12, 2019
8efbeab
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Aug 2, 2019
a3e1e59
adding opaqueId
legrego Aug 2, 2019
86a4379
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Aug 7, 2019
8f398c6
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Aug 13, 2019
f866d7d
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Aug 16, 2019
a551a50
Merge branch 'np/spaces-nav-control' of github.com:legrego/kibana int…
legrego Aug 16, 2019
e9bdc21
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Aug 23, 2019
8df1838
fixes from merge
legrego Aug 23, 2019
9dc0957
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Sep 19, 2019
6517a60
fix merge from master
legrego Sep 19, 2019
e314536
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Sep 20, 2019
75c0b02
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Oct 1, 2019
67eea7b
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Oct 8, 2019
3acca45
fixing merge from master
legrego Oct 8, 2019
40b6c9e
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Oct 28, 2019
444dbb6
move _active_space route to NP
legrego Oct 28, 2019
13cc9ae
moving to the NP feature catalogue registry
legrego Oct 28, 2019
1b50503
moving setup to setup phase
legrego Oct 28, 2019
6f123b6
optimizing active space retrieval
legrego Oct 29, 2019
6192de1
reverting test isolation change
legrego Oct 29, 2019
e41dc60
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Oct 29, 2019
9482b47
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Oct 31, 2019
86bc36d
Apply suggestions from code review
legrego Oct 31, 2019
e80e91b
Merge branch 'np/spaces-nav-control' of github.com:legrego/kibana int…
legrego Oct 31, 2019
0d1d283
removing unnecessary PluginInitializerContext
legrego Oct 31, 2019
c849fde
updating advanced settings subtitle
legrego Oct 31, 2019
e4c0e43
using NP anonymousPaths service
legrego Oct 31, 2019
03e93da
additional nav_control_popover cleanup
legrego Oct 31, 2019
7d15190
additional cleanup
legrego Oct 31, 2019
c3cb211
testing out onActiveSpaceChange$ property
legrego Oct 31, 2019
6cf91d4
make the linter happy
legrego Oct 31, 2019
8146ddb
make the type checker happy
legrego Oct 31, 2019
b593c70
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Nov 20, 2019
ebd3147
fixing types
legrego Nov 20, 2019
d2b2452
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Dec 9, 2019
54f8ce7
fix merge from master
legrego Dec 9, 2019
f6249ff
spaces LP init should run on all pages, not just the kibana app
legrego Dec 9, 2019
b27c7b7
address nits
legrego Dec 9, 2019
eb2714b
fix infra/logs, and the spaces disabled scenario
legrego Dec 9, 2019
839b9a7
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Dec 9, 2019
1871ef7
fix typescript errors
legrego Dec 9, 2019
d771522
revert changes to infra plugin
legrego Dec 10, 2019
0186d5f
reintroducing activeSpace injected var for legacy plugins
legrego Dec 10, 2019
5b2c16a
fixing react deprecation warning and unhandled promise rejection
legrego Dec 11, 2019
03f1687
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Dec 11, 2019
e29437b
restore activeSpace default var
legrego Dec 11, 2019
71865a9
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Dec 11, 2019
5cbb6d9
Merge branch 'master' into np/spaces-nav-control
elasticmachine Dec 13, 2019
2ff6cb8
spaces does not need to check its own enabled status
legrego Dec 13, 2019
7cfdcb1
Merge branch 'np/spaces-nav-control' of github.com:legrego/kibana int…
legrego Dec 13, 2019
bbfc0bd
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Dec 13, 2019
ffb8a7b
fix from merge
legrego Dec 13, 2019
884e545
Merge branch 'master' of github.com:elastic/kibana into np/spaces-nav…
legrego Dec 13, 2019
cb44020
Merge branch 'master' into np/spaces-nav-control
elasticmachine Dec 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import 'plugins/security/services/shield_indices';

import { IndexPatternsProvider } from 'ui/index_patterns';
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
import { SpacesManager } from '../../../../../spaces/public/lib';
import { ROLES_PATH, CLONE_ROLES_PATH, EDIT_ROLES_PATH } from '../management_urls';
import { getEditRoleBreadcrumbs, getCreateRoleBreadcrumbs } from '../breadcrumbs';

Expand Down Expand Up @@ -81,7 +80,7 @@ const routeDefinition = (action) => ({
},
spaces(spacesEnabled) {
if (spacesEnabled) {
return new SpacesManager().getSpaces();
return kfetch({ method: 'get', pathname: '/api/spaces/space' });
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reducing security's dependency on the spaces plugin, at least for now.

}
return [];
},
Expand Down
37 changes: 1 addition & 36 deletions x-pack/legacy/plugins/spaces/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ import { createOptionalPlugin } from '../../server/lib/optional_plugin';
// @ts-ignore
import { AuditLogger } from '../../server/lib/audit_logger';
import mappings from './mappings.json';
import { wrapError } from './server/lib/errors';
import { getActiveSpace } from './server/lib/get_active_space';
import { getSpaceSelectorUrl } from './server/lib/get_space_selector_url';
import { migrateToKibana660 } from './server/lib/migrations';
import { plugin } from './server/new_platform';
import {
Expand Down Expand Up @@ -61,7 +58,6 @@ export const spaces = (kibana: Record<string, any>) =>
},

uiExports: {
chromeNavControls: ['plugins/spaces/views/nav_control'],
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
managementSections: ['plugins/spaces/views/management'],
apps: [
Expand All @@ -86,38 +82,7 @@ export const spaces = (kibana: Record<string, any>) =>
hidden: true,
},
},
home: ['plugins/spaces/register_feature'],
injectDefaultVars(server: any) {
return {
spaces: [],
activeSpace: null,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No more activeSpace means that the client-side must fetch this on demand. I've moved this functionality to the SpacesManager, which fetches it once, and caches it for its lifetime.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch! I've restored this variable solely for their use in 0186d5f

spaceSelectorURL: getSpaceSelectorUrl(server.config()),
};
},
async replaceInjectedVars(
vars: Record<string, any>,
request: Record<string, any>,
server: Record<string, any>
) {
const spacesClient = await server.plugins.spaces.getScopedSpacesClient(request);
try {
vars.activeSpace = {
valid: true,
space: await getActiveSpace(
spacesClient,
request.getBasePath(),
server.config().get('server.basePath')
),
};
} catch (e) {
vars.activeSpace = {
valid: false,
error: wrapError(e).output.payload,
};
}

return vars;
},
home: ['plugins/spaces/legacy'],
},

async init(server: Server) {
Expand Down
24 changes: 0 additions & 24 deletions x-pack/legacy/plugins/spaces/public/__mocks__/ui_capabilities.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,40 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { setMockCapabilities } from '../__mocks__/ui_capabilities';
import React from 'react';
import { shallowWithIntl } from 'test_utils/enzyme_helpers';
import { ManageSpacesButton } from './manage_spaces_button';

describe('ManageSpacesButton', () => {
it('renders as expected', () => {
setMockCapabilities({
navLinks: {},
management: {},
catalogue: {},
spaces: {
manage: true,
},
});

const component = <ManageSpacesButton />;
const component = (
<ManageSpacesButton
capabilities={{
navLinks: {},
management: {},
catalogue: {},
spaces: {
manage: true,
},
}}
/>
);
expect(shallowWithIntl(component)).toMatchSnapshot();
});

it(`doesn't render if user profile forbids managing spaces`, () => {
setMockCapabilities({
navLinks: {},
management: {},
catalogue: {},
spaces: {
manage: false,
},
});

const component = <ManageSpacesButton />;
const component = (
<ManageSpacesButton
capabilities={{
navLinks: {},
management: {},
catalogue: {},
spaces: {
manage: false,
},
}}
/>
);
expect(shallowWithIntl(component)).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,21 @@
import { EuiButton } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import React, { Component, CSSProperties } from 'react';
import { capabilities } from 'ui/capabilities';
import { MANAGE_SPACES_URL } from '../lib/constants';
import { Capabilities } from 'src/core/public';
import { getManageSpacesUrl } from '../lib/constants';

interface Props {
isDisabled?: boolean;
className?: string;
size?: 's' | 'l';
style?: CSSProperties;
onClick?: () => void;
capabilities: Capabilities;
}

export class ManageSpacesButton extends Component<Props, {}> {
public render() {
if (!capabilities.get().spaces.manage) {
if (!this.props.capabilities.spaces.manage) {
return null;
}

Expand All @@ -44,6 +45,6 @@ export class ManageSpacesButton extends Component<Props, {}> {
if (this.props.onClick) {
this.props.onClick();
}
window.location.replace(MANAGE_SPACES_URL);
window.location.replace(getManageSpacesUrl());
};
}
11 changes: 11 additions & 0 deletions x-pack/legacy/plugins/spaces/public/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { PluginInitializerContext } from 'src/core/public';
import { SpacesPlugin } from './plugin';

export const plugin = (initializerContext: PluginInitializerContext) => {
return new SpacesPlugin(initializerContext);
};
23 changes: 23 additions & 0 deletions x-pack/legacy/plugins/spaces/public/legacy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { npSetup, npStart } from 'ui/new_platform';
import { FeatureCatalogueRegistryProvider } from 'ui/registry/feature_catalogue';
import { plugin } from './index';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional nit:

Suggested change
import { plugin } from './index';
import { plugin } from '.';

import { SpacesPlugin, PluginsSetup } from './plugin';

const spacesPlugin: SpacesPlugin = plugin({ opaqueId: Symbol('spaces plugin') });

const plugins: PluginsSetup = {
kibana: {
registerCatalogueFeature: fn => {
FeatureCatalogueRegistryProvider.register(fn);
},
},
};

export const setup = spacesPlugin.setup(npSetup.core);
export const start = spacesPlugin.start(npStart.core, plugins);
5 changes: 3 additions & 2 deletions x-pack/legacy/plugins/spaces/public/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/

import { i18n } from '@kbn/i18n';
import chrome from 'ui/chrome';
import { npSetup } from 'ui/new_platform';

let spacesFeatureDescription: string;

Expand All @@ -20,4 +20,5 @@ export const getSpacesFeatureDescription = () => {
return spacesFeatureDescription;
};

export const MANAGE_SPACES_URL = chrome.addBasePath(`/app/kibana#/management/spaces/list`);
export const getManageSpacesUrl = () =>
npSetup.core.http.basePath.prepend(`/app/kibana#/management/spaces/list`);
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ function createSpacesManagerMock() {
return ({
getSpaces: jest.fn().mockResolvedValue([]),
getSpace: jest.fn().mockResolvedValue(undefined),
getActiveSpace: jest.fn().mockResolvedValue(undefined),
createSpace: jest.fn().mockResolvedValue(undefined),
updateSpace: jest.fn().mockResolvedValue(undefined),
deleteSpace: jest.fn().mockResolvedValue(undefined),
Expand Down
64 changes: 35 additions & 29 deletions x-pack/legacy/plugins/spaces/public/lib/spaces_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,58 +4,64 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { EventEmitter } from 'events';
import { kfetch } from 'ui/kfetch';
import { NotificationsSetup, HttpSetup } from 'src/core/public';
import { Space } from '../../common/model/space';

export class SpacesManager extends EventEmitter {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: probably we can discuss this in the scope of another PR, but since there are no other consumers at this point, I'm wondering if having SpacesManager that extends EventEmitter directly is going to be a problem for us and consumers in the future. In the past I had quite a few issues with that approach when the list of the supported event types is growing:

  • Consumers may not detect a typo in the event name until it's too late, or we just rename it and forget to update all the places where it's used. In short there is no compile-time check that event type used is the supported one. That's one of the reasons we started to support "knownEvents" (when TS wasn't a thing yet)
  • Anyone can emit event with any type of payload
  • There is no easy way for consumer to know which events are supported etc.

What about exposing something like refreshRequest$ of type Observable instead? Alternatively we could continue using EventEmitter, but as an internal "event bus" instead and expose specific methods to emit and/or listen for a particular event?

Also depending on our plans, we can expose spaces$, activeSpace$ or even changes$ that would include newly created space or removed one so that consumers don't have to fetch entire list for every change etc. instead.

I admit that this type of complexity may not be needed in this particular case though, but since it's in a public plugin contract we may want to have a reference NP implementation for things like this at some point :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You raise some very good points here, thanks for taking the time to explain your thoughts! I'll think on this some more and see if I can get rid of the EventEmitter altogether

private spaceSelectorURL: string;
private activeSpace: Space | undefined;

constructor(spaceSelectorURL: string) {
constructor(
private readonly spaceSelectorURL: string,
private readonly http: HttpSetup,
private readonly notifications: NotificationsSetup
) {
super();
this.spaceSelectorURL = spaceSelectorURL;
}

public async getSpaces(): Promise<Space[]> {
return await kfetch({ pathname: '/api/spaces/space' });
return await this.http.get('/api/spaces/space');
}

public async getSpace(id: string): Promise<Space> {
return await kfetch({ pathname: `/api/spaces/space/${encodeURIComponent(id)}` });
return await this.http.get(`/api/spaces/space/${encodeURIComponent(id)}`);
}

public async getActiveSpace(forceRefresh: boolean = false): Promise<Space> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since we don't use await and return/argument types should be properly inferred automatically:

Suggested change
public async getActiveSpace(forceRefresh: boolean = false): Promise<Space> {
public getActiveSpace(forceRefresh = false) {

if (!this.activeSpace || forceRefresh) {
this.activeSpace = (await this.http.get('/api/spaces/v1/activeSpace')) as Space;
}
return this.activeSpace;
}

public async createSpace(space: Space) {
legrego marked this conversation as resolved.
Show resolved Hide resolved
return await kfetch({
pathname: `/api/spaces/space`,
method: 'POST',
body: JSON.stringify(space),
});
return this.http
.post(`/api/spaces/space`, {
body: JSON.stringify(space),
})
.then(() => this.requestRefresh());
}

public async updateSpace(space: Space) {
return await kfetch({
pathname: `/api/spaces/space/${encodeURIComponent(space.id)}`,
query: {
overwrite: true,
},
method: 'PUT',
body: JSON.stringify(space),
});
return this.http
.put(`/api/spaces/space/${encodeURIComponent(space.id)}`, {
query: {
overwrite: true,
},
body: JSON.stringify(space),
})
.then(() => this.requestRefresh());
}

public async deleteSpace(space: Space) {
return await kfetch({
pathname: `/api/spaces/space/${encodeURIComponent(space.id)}`,
method: 'DELETE',
});
return this.http
.delete(`/api/spaces/space/${encodeURIComponent(space.id)}`)
.then(() => this.requestRefresh());
}

public async changeSelectedSpace(space: Space) {
await kfetch({
pathname: `/api/spaces/v1/space/${encodeURIComponent(space.id)}/select`,
method: 'POST',
})
await this.http
.post(`/api/spaces/v1/space/${encodeURIComponent(space.id)}/select`)
.then(response => {
if (response.location) {
window.location = response.location;
Expand All @@ -75,7 +81,7 @@ export class SpacesManager extends EventEmitter {
}

public _displayError() {
toastNotifications.addDanger({
this.notifications.toasts.addDanger({
title: i18n.translate('xpack.spaces.spacesManager.unableToChangeSpaceWarningTitle', {
defaultMessage: 'Unable to change your Space',
}),
Expand Down
42 changes: 42 additions & 0 deletions x-pack/legacy/plugins/spaces/public/plugin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

import { PluginInitializerContext, CoreSetup, CoreStart, Plugin } from 'src/core/public';
import { FeatureCatalogueRegistryFunction } from 'ui/registry/feature_catalogue';
import { SpacesManager } from './lib';
import { initSpacesNavControl } from './views/nav_control';
import { createSpacesFeatureCatalogueEntry } from './register_feature';

export interface SpacesPluginStart {
spacesManager: SpacesManager;
}

export interface PluginsSetup {
kibana: {
registerCatalogueFeature: (fn: FeatureCatalogueRegistryFunction) => void;
};
}

export class SpacesPlugin implements Plugin<{}, SpacesPluginStart, PluginsSetup> {
legrego marked this conversation as resolved.
Show resolved Hide resolved
private spacesManager: SpacesManager | undefined;
legrego marked this conversation as resolved.
Show resolved Hide resolved

// @ts-ignore
constructor(private readonly initializerContext: PluginInitializerContext) {}

public async start(core: CoreStart, plugins: PluginsSetup) {
const { spaceSelectorUrl } = await core.http.get('/api/spaces/v1/npStart');
this.spacesManager = new SpacesManager(spaceSelectorUrl, core.http, core.notifications);

initSpacesNavControl(this.spacesManager, core);
plugins.kibana.registerCatalogueFeature(createSpacesFeatureCatalogueEntry);

return {
spacesManager: this.spacesManager,
};
}

public async setup(core: CoreSetup) {}
}
Loading