mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Stateful sidenav] Add yml setting to force spaces solution view (#191743)
This commit is contained in:
parent
aaa53f5a31
commit
6f6883d781
15 changed files with 224 additions and 35 deletions
|
@ -97,11 +97,11 @@ describe('Navigation Plugin', () => {
|
|||
|
||||
describe('addSolutionNavigation()', () => {
|
||||
it('should update the solution navigation definitions', async () => {
|
||||
const { plugin, coreStart, unifiedSearch, cloud } = setup();
|
||||
const { plugin, coreStart, unifiedSearch, spaces } = setup();
|
||||
|
||||
const { addSolutionNavigation } = plugin.start(coreStart, {
|
||||
unifiedSearch,
|
||||
cloud,
|
||||
spaces,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve));
|
||||
|
||||
|
@ -180,13 +180,29 @@ describe('Navigation Plugin', () => {
|
|||
});
|
||||
|
||||
describe('isSolutionNavEnabled$', () => {
|
||||
// This test will need to be changed when we remove the feature flag
|
||||
it('should be off by default', async () => {
|
||||
const { plugin, coreStart, unifiedSearch, cloud } = setup();
|
||||
it('should be off if spaces plugin not available', async () => {
|
||||
const { plugin, coreStart, unifiedSearch } = setup();
|
||||
|
||||
const { isSolutionNavEnabled$ } = plugin.start(coreStart, {
|
||||
unifiedSearch,
|
||||
cloud,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve));
|
||||
|
||||
const isEnabled = await firstValueFrom(isSolutionNavEnabled$);
|
||||
expect(isEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it('should be off if spaces plugin `isSolutionViewEnabled` = false', async () => {
|
||||
const { plugin, coreStart, unifiedSearch, spaces } = setup();
|
||||
spaces.getActiveSpace$ = jest
|
||||
.fn()
|
||||
.mockReturnValue(of({ solution: 'es' } as Pick<Space, 'solution'>));
|
||||
|
||||
spaces.isSolutionViewEnabled = false;
|
||||
|
||||
const { isSolutionNavEnabled$ } = plugin.start(coreStart, {
|
||||
unifiedSearch,
|
||||
spaces,
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve));
|
||||
|
||||
|
|
|
@ -72,10 +72,8 @@ export class NavigationPublicPlugin
|
|||
const extensions = this.topNavMenuExtensionsRegistry.getAll();
|
||||
const chrome = core.chrome as InternalChromeStart;
|
||||
const activeSpace$: Observable<Space | undefined> = spaces?.getActiveSpace$() ?? of(undefined);
|
||||
const onCloud = cloud !== undefined; // The new side nav will initially only be available to cloud users
|
||||
const isServerless = this.initializerContext.env.packageInfo.buildFlavor === 'serverless';
|
||||
|
||||
this.isSolutionNavEnabled = onCloud && !isServerless;
|
||||
this.isSolutionNavEnabled = spaces?.isSolutionViewEnabled ?? false;
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
|
@ -31,6 +31,7 @@ const mockSpacesApi: SpacesApi = {
|
|||
useSpaces: jest.fn(),
|
||||
},
|
||||
hasOnlyDefaultSpace: false,
|
||||
isSolutionViewEnabled: true,
|
||||
};
|
||||
|
||||
describe('<LegacyUrlConflictCallOut />', () => {
|
||||
|
|
|
@ -32,6 +32,7 @@ const mockSpacesApi: SpacesApi = {
|
|||
useSpaces: jest.fn(),
|
||||
},
|
||||
hasOnlyDefaultSpace: false,
|
||||
isSolutionViewEnabled: true,
|
||||
};
|
||||
|
||||
describe('useLegacyUrlRedirect', () => {
|
||||
|
|
|
@ -9,4 +9,7 @@ export interface ConfigType {
|
|||
maxSpaces: number;
|
||||
allowFeatureVisibility: boolean;
|
||||
allowSolutionVisibility: boolean;
|
||||
experimental: {
|
||||
forceSolutionVisibility: boolean;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,6 +24,9 @@ describe('ManagementService', () => {
|
|||
maxSpaces: 1000,
|
||||
allowFeatureVisibility: true,
|
||||
allowSolutionVisibility: true,
|
||||
experimental: {
|
||||
forceSolutionVisibility: false,
|
||||
},
|
||||
};
|
||||
|
||||
describe('#setup', () => {
|
||||
|
|
|
@ -31,6 +31,9 @@ const config: ConfigType = {
|
|||
maxSpaces: 1000,
|
||||
allowFeatureVisibility: true,
|
||||
allowSolutionVisibility: true,
|
||||
experimental: {
|
||||
forceSolutionVisibility: false,
|
||||
},
|
||||
};
|
||||
|
||||
const eventTracker = new EventTracker({ reportEvent: jest.fn() });
|
||||
|
|
|
@ -16,6 +16,7 @@ const createApiMock = (hasOnlyDefaultSpace: boolean): jest.Mocked<SpacesApi> =>
|
|||
getActiveSpace: jest.fn(),
|
||||
ui: createApiUiMock(),
|
||||
hasOnlyDefaultSpace,
|
||||
isSolutionViewEnabled: true,
|
||||
});
|
||||
|
||||
type SpacesApiUiMock = Omit<jest.Mocked<SpacesApiUi>, 'components'> & {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { cloudMock } from '@kbn/cloud-plugin/public/mocks';
|
||||
import { coreMock } from '@kbn/core/public/mocks';
|
||||
import { homePluginMock } from '@kbn/home-plugin/public/mocks';
|
||||
import {
|
||||
|
@ -13,7 +14,6 @@ import {
|
|||
} from '@kbn/management-plugin/public/mocks';
|
||||
|
||||
import { SpacesPlugin } from './plugin';
|
||||
// import { ConfigSchema } from './config';
|
||||
|
||||
describe('Spaces plugin', () => {
|
||||
describe('#setup', () => {
|
||||
|
@ -209,27 +209,156 @@ describe('Spaces plugin', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('determines hasOnlyDefaultSpace correctly when maxSpaces=1', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
describe('hasOnlyDefaultSpace', () => {
|
||||
it('determines hasOnlyDefaultSpace correctly when maxSpaces=1', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1 }));
|
||||
const spacesSetup = plugin.setup(coreSetup, {});
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1 }));
|
||||
const spacesSetup = plugin.setup(coreSetup, {});
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.hasOnlyDefaultSpace).toBe(true);
|
||||
expect(spacesStart.hasOnlyDefaultSpace).toBe(true);
|
||||
expect(spacesSetup.hasOnlyDefaultSpace).toBe(true);
|
||||
expect(spacesStart.hasOnlyDefaultSpace).toBe(true);
|
||||
});
|
||||
|
||||
it('determines hasOnlyDefaultSpace correctly when maxSpaces=1000', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1000 }));
|
||||
const spacesSetup = plugin.setup(coreSetup, {});
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.hasOnlyDefaultSpace).toBe(false);
|
||||
expect(spacesStart.hasOnlyDefaultSpace).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('determines hasOnlyDefaultSpace correctly when maxSpaces=1000', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
describe('isSolutionViewEnabled', () => {
|
||||
it('when onCloud, not serverless and allowSolutionVisibility is "true"', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const cloud = cloudMock.createSetup();
|
||||
cloud.isCloudEnabled = true;
|
||||
|
||||
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1000 }));
|
||||
const spacesSetup = plugin.setup(coreSetup, {});
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: true },
|
||||
{ buildFlavor: 'traditional' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, { cloud });
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.hasOnlyDefaultSpace).toBe(false);
|
||||
expect(spacesStart.hasOnlyDefaultSpace).toBe(false);
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(true);
|
||||
});
|
||||
|
||||
it('when not onCloud and allowSolutionVisibility is "true"', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
|
||||
{
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: true }, // it is true but we are not onCloud
|
||||
{ buildFlavor: 'traditional' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, {});
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(false); // so it should be false
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(false);
|
||||
}
|
||||
|
||||
{
|
||||
// unless the forceSolutionVisibility flag is set
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: false, experimental: { forceSolutionVisibility: true } },
|
||||
{ buildFlavor: 'traditional' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, {}); // we are not onCloud but forceSolutionVisibility is true
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('when onCloud, not serverless and allowSolutionVisibility is "false"', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const cloud = cloudMock.createSetup();
|
||||
cloud.isCloudEnabled = true;
|
||||
|
||||
{
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: false },
|
||||
{ buildFlavor: 'traditional' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, { cloud });
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(false);
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(false);
|
||||
}
|
||||
|
||||
{
|
||||
// unless the forceSolutionVisibility flag is set
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: false, experimental: { forceSolutionVisibility: true } },
|
||||
{ buildFlavor: 'traditional' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, { cloud });
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('when onCloud and serverless', () => {
|
||||
const coreSetup = coreMock.createSetup();
|
||||
const coreStart = coreMock.createStart();
|
||||
const cloud = cloudMock.createSetup();
|
||||
cloud.isCloudEnabled = true;
|
||||
|
||||
{
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: true },
|
||||
{ buildFlavor: 'serverless' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, { cloud });
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(false);
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(false);
|
||||
}
|
||||
|
||||
{
|
||||
// unless the forceSolutionVisibility flag is set
|
||||
const plugin = new SpacesPlugin(
|
||||
coreMock.createPluginInitializerContext(
|
||||
{ allowSolutionVisibility: true, experimental: { forceSolutionVisibility: true } },
|
||||
{ buildFlavor: 'serverless' }
|
||||
)
|
||||
);
|
||||
const spacesSetup = plugin.setup(coreSetup, { cloud });
|
||||
const spacesStart = plugin.start(coreStart);
|
||||
|
||||
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
|
||||
expect(spacesStart.isSolutionViewEnabled).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -60,6 +60,14 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
|
|||
|
||||
public setup(core: CoreSetup<PluginsStart, SpacesPluginStart>, plugins: PluginsSetup) {
|
||||
const hasOnlyDefaultSpace = this.config.maxSpaces === 1;
|
||||
const onCloud = plugins.cloud !== undefined && plugins.cloud.isCloudEnabled;
|
||||
|
||||
// We only allow "solution" to be set on cloud environments, not on prem
|
||||
// unless the forceSolutionVisibility flag is set
|
||||
const allowSolutionVisibility =
|
||||
(onCloud && !this.isServerless && this.config.allowSolutionVisibility) ||
|
||||
Boolean(this.config.experimental?.forceSolutionVisibility);
|
||||
|
||||
this.spacesManager = new SpacesManager(core.http);
|
||||
this.spacesApi = {
|
||||
ui: getUiApi({
|
||||
|
@ -69,15 +77,14 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
|
|||
getActiveSpace$: () => this.spacesManager.onActiveSpaceChange$,
|
||||
getActiveSpace: () => this.spacesManager.getActiveSpace(),
|
||||
hasOnlyDefaultSpace,
|
||||
isSolutionViewEnabled: allowSolutionVisibility,
|
||||
};
|
||||
|
||||
this.config = {
|
||||
...this.config,
|
||||
allowSolutionVisibility,
|
||||
};
|
||||
|
||||
const onCloud = plugins.cloud !== undefined && plugins.cloud.isCloudEnabled;
|
||||
if (!onCloud) {
|
||||
this.config = {
|
||||
...this.config,
|
||||
allowSolutionVisibility: false,
|
||||
};
|
||||
}
|
||||
registerSpacesEventTypes(core);
|
||||
this.eventTracker = new EventTracker(core.analytics);
|
||||
|
||||
|
@ -133,7 +140,7 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
|
|||
|
||||
registerAnalyticsContext(core.analytics, this.spacesManager.onActiveSpaceChange$);
|
||||
|
||||
return { hasOnlyDefaultSpace };
|
||||
return { hasOnlyDefaultSpace, isSolutionViewEnabled: allowSolutionVisibility };
|
||||
}
|
||||
|
||||
public start(core: CoreStart) {
|
||||
|
|
|
@ -60,4 +60,9 @@ export interface SpacesApi {
|
|||
* UI components and services to add spaces capabilities to an application.
|
||||
*/
|
||||
ui: SpacesApiUi;
|
||||
|
||||
/**
|
||||
* Indicates whether the solution view is enabled.
|
||||
*/
|
||||
isSolutionViewEnabled: boolean;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,9 @@ describe('config schema', () => {
|
|||
"allowFeatureVisibility": true,
|
||||
"allowSolutionVisibility": true,
|
||||
"enabled": true,
|
||||
"experimental": Object {
|
||||
"forceSolutionVisibility": false,
|
||||
},
|
||||
"maxSpaces": 1000,
|
||||
}
|
||||
`);
|
||||
|
@ -32,6 +35,9 @@ describe('config schema', () => {
|
|||
"allowFeatureVisibility": true,
|
||||
"allowSolutionVisibility": true,
|
||||
"enabled": true,
|
||||
"experimental": Object {
|
||||
"forceSolutionVisibility": false,
|
||||
},
|
||||
"maxSpaces": 1000,
|
||||
}
|
||||
`);
|
||||
|
@ -41,6 +47,9 @@ describe('config schema', () => {
|
|||
"allowFeatureVisibility": true,
|
||||
"allowSolutionVisibility": true,
|
||||
"enabled": true,
|
||||
"experimental": Object {
|
||||
"forceSolutionVisibility": false,
|
||||
},
|
||||
"maxSpaces": 1000,
|
||||
}
|
||||
`);
|
||||
|
|
|
@ -54,6 +54,13 @@ export const ConfigSchema = schema.object({
|
|||
defaultValue: true,
|
||||
}),
|
||||
}),
|
||||
experimental: schema.maybe(
|
||||
offeringBasedSchema({
|
||||
traditional: schema.object({
|
||||
forceSolutionVisibility: schema.boolean({ defaultValue: false }),
|
||||
}),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export function createConfig$(context: PluginInitializerContext) {
|
||||
|
|
|
@ -34,6 +34,9 @@ export const config: PluginConfigDescriptor = {
|
|||
maxSpaces: true,
|
||||
allowFeatureVisibility: true,
|
||||
allowSolutionVisibility: true,
|
||||
experimental: {
|
||||
forceSolutionVisibility: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -130,7 +130,10 @@ export class SpacesPlugin
|
|||
([config, onCloud]): ConfigType => ({
|
||||
...config,
|
||||
// We only allow "solution" to be set on cloud environments, not on prem
|
||||
allowSolutionVisibility: onCloud ? config.allowSolutionVisibility : false,
|
||||
// unless the forceSolutionVisibility flag is set.
|
||||
allowSolutionVisibility:
|
||||
(onCloud && config.allowSolutionVisibility) ||
|
||||
Boolean(config.experimental?.forceSolutionVisibility),
|
||||
})
|
||||
)
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue