Remove Space Selector from Serverless Top Navigation (#164461)

Closes https://github.com/elastic/kibana/issues/163024

## Summary
For Serverless projects, there will be only a single default space, with
no way to create/select additional spaces. For this reason, the spaces
navigation control has been removed for serverless only


## Changes Made
- Added isServerless boolean flag to depend on the plugin context
`buildFlavor` in the Serverless plugin
- Based on the flag
    - Disabled rendering of spaces nav control
    - Disabled the space management route
    - Disabled adding spaces to feature catalogue
- Updated plugin tests to check for serverless or traditional build
flavors

## UI Changes
|  Before | After   |
|---|---|
| <img width="280" alt="image"
src="e18367ba-adcc-4e3d-ac7a-d45d8993f67a">
| <img width="280" alt="image"
src="d51bb61b-4314-4977-a358-c7182eb9fecc">
|


## Release Notes
Security
Removed the space selector from the Serverless UI.
https://github.com/elastic/kibana/issues/163024

---------

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Sid 2023-08-30 13:43:56 +02:00 committed by GitHub
parent 4b9ee3de5b
commit 0c3d38b662
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 23 deletions

View file

@ -10,6 +10,7 @@ import type { PublicMethodsOf } from '@kbn/utility-types';
import { loggerMock } from '@kbn/logging-mocks';
import type { PluginInitializerContext } from '@kbn/core-plugins-browser';
import type { PluginsService, PluginsServiceSetup } from '@kbn/core-plugins-browser-internal';
import type { BuildFlavor } from '@kbn/config/src/types';
const createSetupContractMock = () => {
const setupContract: jest.Mocked<PluginsServiceSetup> = {
@ -27,7 +28,10 @@ const createStartContractMock = () => {
return startContract as PluginsServiceSetup;
};
const createPluginInitializerContextMock = (config: unknown = {}) => {
const createPluginInitializerContextMock = (
config: unknown = {},
{ buildFlavor = 'serverless' }: { buildFlavor?: BuildFlavor } = {}
) => {
const mock: PluginInitializerContext = {
opaqueId: Symbol(),
env: {
@ -43,7 +47,7 @@ const createPluginInitializerContextMock = (config: unknown = {}) => {
buildSha: 'buildSha',
dist: false,
buildDate: new Date('2023-05-15T23:12:09.000Z'),
buildFlavor: 'serverless',
buildFlavor,
},
},
logger: loggerMock.create(),

View file

@ -16,6 +16,7 @@
"@kbn/logging-mocks",
"@kbn/core-plugins-browser-internal",
"@kbn/core-plugins-browser",
"@kbn/config",
],
"exclude": [
"target/**/*",

View file

@ -17,10 +17,14 @@ import { SpacesPlugin } from './plugin';
describe('Spaces plugin', () => {
describe('#setup', () => {
it('should register the spaces API and the space selector app', () => {
it('should register the space selector app when buildFlavor is traditional', () => {
const coreSetup = coreMock.createSetup();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{},
{ buildFlavor: 'traditional' }
);
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext());
const plugin = new SpacesPlugin(mockInitializerContext);
plugin.setup(coreSetup, {});
expect(coreSetup.application.register).toHaveBeenCalledWith(
@ -33,7 +37,23 @@ describe('Spaces plugin', () => {
);
});
it('should register the management and feature catalogue sections when the management and home plugins are both available', () => {
it('should not register the space selector app when buildFlavor is serverless', () => {
const coreSetup = coreMock.createSetup();
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext());
plugin.setup(coreSetup, {});
expect(coreSetup.application.register).not.toHaveBeenCalledWith(
expect.objectContaining({
id: 'space_selector',
chromeless: true,
appRoute: '/spaces/space_selector',
mount: expect.any(Function),
})
);
});
it('should register the management and feature catalogue sections when the management and home plugins are both available when buildFlavor is traditional', () => {
const coreSetup = coreMock.createSetup();
const home = homePluginMock.createSetupContract();
@ -43,7 +63,12 @@ describe('Spaces plugin', () => {
management.sections.section.kibana = mockSection;
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext());
const mockInitializerContext = coreMock.createPluginInitializerContext(
{},
{ buildFlavor: 'traditional' }
);
const plugin = new SpacesPlugin(mockInitializerContext);
plugin.setup(coreSetup, {
management,
home,
@ -62,10 +87,57 @@ describe('Spaces plugin', () => {
})
);
});
it('should not register spaces in the management plugin or the feature catalog when the management and home plugins are both available when buildFlavor is serverless', () => {
const coreSetup = coreMock.createSetup();
const home = homePluginMock.createSetupContract();
const management = managementPluginMock.createSetupContract();
const mockSection = createManagementSectionMock();
mockSection.registerApp = jest.fn();
management.sections.section.kibana = mockSection;
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext());
plugin.setup(coreSetup, {
management,
home,
});
expect(mockSection.registerApp).not.toHaveBeenCalledWith(
expect.objectContaining({ id: 'spaces' })
);
expect(home.featureCatalogue.register).not.toHaveBeenCalledWith(
expect.objectContaining({
category: 'admin',
icon: 'spacesApp',
id: 'spaces',
showOnHomePage: false,
})
);
});
});
describe('#start', () => {
it('should register the spaces nav control', () => {
it('should register the spaces nav control when buildFlavor is traditional', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{},
{ buildFlavor: 'traditional' }
);
const plugin = new SpacesPlugin(mockInitializerContext);
plugin.setup(coreSetup, {});
plugin.start(coreStart);
expect(coreStart.chrome.navControls.registerLeft).toHaveBeenCalled();
});
it('should not register the spaces nav control when buildFlavor is serverless', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
@ -74,7 +146,7 @@ describe('Spaces plugin', () => {
plugin.start(coreStart);
expect(coreStart.chrome.navControls.registerLeft).toHaveBeenCalled();
expect(coreStart.chrome.navControls.registerLeft).not.toHaveBeenCalled();
});
});
});

View file

@ -45,9 +45,11 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
private managementService?: ManagementService;
private readonly config: ConfigType;
private readonly isServerless: boolean;
constructor(private readonly initializerContext: PluginInitializerContext) {
this.config = this.initializerContext.config.get<ConfigType>();
this.isServerless = this.initializerContext.env.packageInfo.buildFlavor === 'serverless';
}
public setup(core: CoreSetup<PluginsStart, SpacesPluginStart>, plugins: PluginsSetup) {
@ -61,31 +63,35 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
getActiveSpace: () => this.spacesManager.getActiveSpace(),
};
if (plugins.home) {
plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry());
}
if (!this.isServerless) {
if (plugins.home) {
plugins.home.featureCatalogue.register(createSpacesFeatureCatalogueEntry());
}
if (plugins.management) {
this.managementService = new ManagementService();
this.managementService.setup({
management: plugins.management,
if (plugins.management) {
this.managementService = new ManagementService();
this.managementService.setup({
management: plugins.management,
getStartServices: core.getStartServices,
spacesManager: this.spacesManager,
config: this.config,
});
}
spaceSelectorApp.create({
getStartServices: core.getStartServices,
application: core.application,
spacesManager: this.spacesManager,
config: this.config,
});
}
spaceSelectorApp.create({
getStartServices: core.getStartServices,
application: core.application,
spacesManager: this.spacesManager,
});
return {};
}
public start(core: CoreStart) {
initSpacesNavControl(this.spacesManager, core);
if (!this.isServerless) {
initSpacesNavControl(this.spacesManager, core);
}
return this.spacesApi;
}