mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Deployment Management] Add landing page redirect feature and implement in security solution (#161060)
This commit is contained in:
parent
472d843fbc
commit
2a71469894
15 changed files with 126 additions and 40 deletions
|
@ -38,4 +38,14 @@ If card needs to be hidden from the navigation you can specify that by using the
|
|||
});
|
||||
```
|
||||
|
||||
More specifics about the `setupCardsNavigation` can be found in `packages/kbn-management/cards_navigation/readme.mdx`.
|
||||
More specifics about the `setupCardsNavigation` can be found in `packages/kbn-management/cards_navigation/readme.mdx`.
|
||||
|
||||
## Landing page redirect
|
||||
|
||||
If the consumer wants to have a separate landing page for the management section, they can use the `setLandingPageRedirect`
|
||||
method to specify the path to the landing page:
|
||||
|
||||
|
||||
```
|
||||
management.setLandingPageRedirect('/app/security/management');
|
||||
```
|
|
@ -43,6 +43,7 @@ export interface ManagementAppDependencies {
|
|||
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void;
|
||||
isSidebarEnabled$: BehaviorSubject<boolean>;
|
||||
cardsNavigationConfig$: BehaviorSubject<NavigationCardsSubject>;
|
||||
landingPageRedirect$: BehaviorSubject<string | undefined>;
|
||||
}
|
||||
|
||||
export const ManagementApp = ({
|
||||
|
@ -51,11 +52,13 @@ export const ManagementApp = ({
|
|||
theme$,
|
||||
appBasePath,
|
||||
}: ManagementAppProps) => {
|
||||
const { setBreadcrumbs, isSidebarEnabled$, cardsNavigationConfig$ } = dependencies;
|
||||
const { setBreadcrumbs, isSidebarEnabled$, cardsNavigationConfig$, landingPageRedirect$ } =
|
||||
dependencies;
|
||||
const [selectedId, setSelectedId] = useState<string>('');
|
||||
const [sections, setSections] = useState<ManagementSection[]>();
|
||||
const isSidebarEnabled = useObservable(isSidebarEnabled$);
|
||||
const cardsNavigationConfig = useObservable(cardsNavigationConfig$);
|
||||
const landingPageRedirect = useObservable(landingPageRedirect$);
|
||||
|
||||
const onAppMounted = useCallback((id: string) => {
|
||||
setSelectedId(id);
|
||||
|
@ -131,6 +134,9 @@ export const ManagementApp = ({
|
|||
setBreadcrumbs={setBreadcrumbsScoped}
|
||||
onAppMounted={onAppMounted}
|
||||
sections={sections}
|
||||
landingPageRedirect={landingPageRedirect}
|
||||
navigateToUrl={dependencies.coreStart.application.navigateToUrl}
|
||||
basePath={dependencies.coreStart.http.basePath}
|
||||
/>
|
||||
</KibanaPageTemplate>
|
||||
</KibanaThemeProvider>
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { Router, Routes, Route } from '@kbn/shared-ux-router';
|
||||
import { AppMountParameters, ChromeBreadcrumb, ScopedHistory } from '@kbn/core/public';
|
||||
import type { ApplicationStart } from '@kbn/core-application-browser';
|
||||
import type { HttpStart } from '@kbn/core-http-browser';
|
||||
import { ManagementAppWrapper } from '../management_app_wrapper';
|
||||
import { ManagementLandingPage } from '../landing';
|
||||
import { ManagementSection } from '../../utils';
|
||||
|
@ -20,43 +22,65 @@ interface ManagementRouterProps {
|
|||
setBreadcrumbs: (crumbs?: ChromeBreadcrumb[], appHistory?: ScopedHistory) => void;
|
||||
onAppMounted: (id: string) => void;
|
||||
sections: ManagementSection[];
|
||||
landingPageRedirect: string | undefined;
|
||||
navigateToUrl: ApplicationStart['navigateToUrl'];
|
||||
basePath: HttpStart['basePath'];
|
||||
}
|
||||
|
||||
export const ManagementRouter = memo(
|
||||
({ history, setBreadcrumbs, onAppMounted, sections, theme$ }: ManagementRouterProps) => (
|
||||
<Router history={history}>
|
||||
<Routes>
|
||||
{sections.map((section) =>
|
||||
section
|
||||
.getAppsEnabled()
|
||||
.map((app) => (
|
||||
<Route
|
||||
path={`${app.basePath}`}
|
||||
component={() => (
|
||||
<ManagementAppWrapper
|
||||
app={app}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
onAppMounted={onAppMounted}
|
||||
history={history}
|
||||
theme$={theme$}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
{sections.map((section) =>
|
||||
section
|
||||
.getAppsEnabled()
|
||||
.filter((app) => app.redirectFrom)
|
||||
.map((app) => <Redirect path={`/${app.redirectFrom}*`} to={`${app.basePath}*`} />)
|
||||
)}
|
||||
<Route
|
||||
path={'/'}
|
||||
component={() => (
|
||||
<ManagementLandingPage setBreadcrumbs={setBreadcrumbs} onAppMounted={onAppMounted} />
|
||||
({
|
||||
history,
|
||||
setBreadcrumbs,
|
||||
onAppMounted,
|
||||
sections,
|
||||
theme$,
|
||||
landingPageRedirect,
|
||||
navigateToUrl,
|
||||
basePath,
|
||||
}: ManagementRouterProps) => {
|
||||
// Redirect the user to the configured landing page if there is one
|
||||
useEffect(() => {
|
||||
if (landingPageRedirect) {
|
||||
navigateToUrl(basePath.prepend(landingPageRedirect));
|
||||
}
|
||||
}, [landingPageRedirect, navigateToUrl, basePath]);
|
||||
|
||||
return (
|
||||
<Router history={history}>
|
||||
<Routes>
|
||||
{sections.map((section) =>
|
||||
section
|
||||
.getAppsEnabled()
|
||||
.map((app) => (
|
||||
<Route
|
||||
path={`${app.basePath}`}
|
||||
component={() => (
|
||||
<ManagementAppWrapper
|
||||
app={app}
|
||||
setBreadcrumbs={setBreadcrumbs}
|
||||
onAppMounted={onAppMounted}
|
||||
history={history}
|
||||
theme$={theme$}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
)
|
||||
{sections.map((section) =>
|
||||
section
|
||||
.getAppsEnabled()
|
||||
.filter((app) => app.redirectFrom)
|
||||
.map((app) => <Redirect path={`/${app.redirectFrom}*`} to={`${app.basePath}*`} />)
|
||||
)}
|
||||
|
||||
<Route
|
||||
path={'/'}
|
||||
component={() => (
|
||||
<ManagementLandingPage setBreadcrumbs={setBreadcrumbs} onAppMounted={onAppMounted} />
|
||||
)}
|
||||
/>
|
||||
</Routes>
|
||||
</Router>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
|
|
@ -44,6 +44,7 @@ const createSetupContract = (): ManagementSetup => ({
|
|||
const createStartContract = (): ManagementStart => ({
|
||||
setIsSidebarEnabled: jest.fn(),
|
||||
setupCardsNavigation: jest.fn(),
|
||||
setLandingPageRedirect: jest.fn(),
|
||||
});
|
||||
|
||||
export const managementPluginMock = {
|
||||
|
|
|
@ -72,6 +72,7 @@ export class ManagementPlugin
|
|||
private hasAnyEnabledApps = true;
|
||||
|
||||
private isSidebarEnabled$ = new BehaviorSubject<boolean>(true);
|
||||
private landingPageRedirect$ = new BehaviorSubject<string | undefined>(undefined);
|
||||
private cardsNavigationConfig$ = new BehaviorSubject<NavigationCardsSubject>({
|
||||
enabled: false,
|
||||
hideLinksTo: [],
|
||||
|
@ -124,6 +125,7 @@ export class ManagementPlugin
|
|||
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
|
||||
isSidebarEnabled$: managementPlugin.isSidebarEnabled$,
|
||||
cardsNavigationConfig$: managementPlugin.cardsNavigationConfig$,
|
||||
landingPageRedirect$: managementPlugin.landingPageRedirect$,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
@ -154,6 +156,8 @@ export class ManagementPlugin
|
|||
this.isSidebarEnabled$.next(isSidebarEnabled),
|
||||
setupCardsNavigation: ({ enabled, hideLinksTo }) =>
|
||||
this.cardsNavigationConfig$.next({ enabled, hideLinksTo }),
|
||||
setLandingPageRedirect: (landingPageRedirect: string) =>
|
||||
this.landingPageRedirect$.next(landingPageRedirect),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ export interface DefinedSections {
|
|||
|
||||
export interface ManagementStart {
|
||||
setIsSidebarEnabled: (enabled: boolean) => void;
|
||||
setLandingPageRedirect: (landingPageRedirect: string) => void;
|
||||
setupCardsNavigation: ({ enabled, hideLinksTo }: NavigationCardsSubject) => void;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@
|
|||
"@kbn/shared-ux-router",
|
||||
"@kbn/management-cards-navigation",
|
||||
"@kbn/shared-ux-link-redirect-app",
|
||||
"@kbn/test-jest-helpers"
|
||||
"@kbn/test-jest-helpers",
|
||||
"@kbn/core-application-browser",
|
||||
"@kbn/core-http-browser"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
],
|
||||
"requiredPlugins": [
|
||||
"kibanaReact",
|
||||
"management",
|
||||
"ml",
|
||||
"security",
|
||||
"securitySolution",
|
||||
|
|
|
@ -12,6 +12,7 @@ import { serverlessMock } from '@kbn/serverless/public/mocks';
|
|||
import { securityMock } from '@kbn/security-plugin/public/mocks';
|
||||
import { securitySolutionMock } from '@kbn/security-solution-plugin/public/mocks';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { managementPluginMock } from '@kbn/management-plugin/public/mocks';
|
||||
import type { ProjectNavigationLink } from './navigation/links';
|
||||
import type { Services } from './services';
|
||||
|
||||
|
@ -23,6 +24,7 @@ export const servicesMocks: Services = {
|
|||
security: securityMock.createStart(),
|
||||
securitySolution: securitySolutionMock.createStart(),
|
||||
getProjectNavLinks$: jest.fn(() => new BehaviorSubject(mockProjectNavLinks())),
|
||||
management: managementPluginMock.createStartContract(),
|
||||
};
|
||||
|
||||
export const KibanaServicesProvider = React.memo(({ children }) => (
|
||||
|
|
|
@ -48,7 +48,7 @@ export class ServerlessSecurityPlugin
|
|||
core: CoreStart,
|
||||
startDeps: ServerlessSecurityPluginStartDependencies
|
||||
): ServerlessSecurityPluginStart {
|
||||
const { securitySolution, serverless } = startDeps;
|
||||
const { securitySolution, serverless, management } = startDeps;
|
||||
const { productTypes } = this.config;
|
||||
|
||||
const services = createServices(core, startDeps);
|
||||
|
@ -62,6 +62,8 @@ export class ServerlessSecurityPlugin
|
|||
subscribeNavigationTree(services);
|
||||
subscribeBreadcrumbs(services);
|
||||
|
||||
management.setLandingPageRedirect('/app/security/manage');
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import type {
|
|||
PluginStart as SecuritySolutionPluginStart,
|
||||
} from '@kbn/security-solution-plugin/public';
|
||||
import type { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public';
|
||||
import { ManagementSetup, ManagementStart } from '@kbn/management-plugin/public';
|
||||
import type { SecurityProductTypes } from '../common/config';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
|
@ -23,12 +24,14 @@ export interface ServerlessSecurityPluginSetupDependencies {
|
|||
security: SecurityPluginSetup;
|
||||
securitySolution: SecuritySolutionPluginSetup;
|
||||
serverless: ServerlessPluginSetup;
|
||||
management: ManagementSetup;
|
||||
}
|
||||
|
||||
export interface ServerlessSecurityPluginStartDependencies {
|
||||
security: SecurityPluginStart;
|
||||
securitySolution: SecuritySolutionPluginStart;
|
||||
serverless: ServerlessPluginStart;
|
||||
management: ManagementStart;
|
||||
}
|
||||
|
||||
export interface ServerlessSecurityPublicConfig {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
"kbn_references": [
|
||||
"@kbn/core",
|
||||
"@kbn/config-schema",
|
||||
"@kbn/management-plugin",
|
||||
"@kbn/security-plugin",
|
||||
"@kbn/security-solution-plugin",
|
||||
"@kbn/serverless",
|
||||
|
|
|
@ -52,6 +52,9 @@ export function createTestConfig(options: CreateTestConfigOptions) {
|
|||
observability: {
|
||||
pathname: '/app/observability',
|
||||
},
|
||||
management: {
|
||||
pathname: '/app/management',
|
||||
},
|
||||
},
|
||||
// choose where screenshots should be saved
|
||||
screenshots: {
|
||||
|
|
|
@ -10,5 +10,6 @@ import { FtrProviderContext } from '../../ftr_provider_context';
|
|||
export default function ({ loadTestFile }: FtrProviderContext) {
|
||||
describe('serverless security UI', function () {
|
||||
loadTestFile(require.resolve('./landing_page'));
|
||||
loadTestFile(require.resolve('./management'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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 { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getPageObject }: FtrProviderContext) {
|
||||
const PageObject = getPageObject('common');
|
||||
|
||||
describe('Management', function () {
|
||||
it('redirects from common management url to security specific page', async () => {
|
||||
const SUB_URL = '';
|
||||
await PageObject.navigateToUrl('management', SUB_URL, {
|
||||
ensureCurrentUrl: false,
|
||||
shouldLoginIfPrompted: false,
|
||||
shouldUseHashForSubUrl: false,
|
||||
});
|
||||
|
||||
await PageObject.waitUntilUrlIncludes('/security/manage');
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue