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

[Deployment Management] Add landing page redirect feature and implement in security solution #161060

12 changes: 11 additions & 1 deletion src/plugins/management/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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');
```
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface ManagementAppDependencies {
setBreadcrumbs: (newBreadcrumbs: ChromeBreadcrumb[]) => void;
isSidebarEnabled$: BehaviorSubject<boolean>;
cardsNavigationConfig$: BehaviorSubject<NavigationCardsSubject>;
landingPageRedirect$: BehaviorSubject<string | undefined>;
}

export const ManagementApp = ({
Expand All @@ -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);
Expand Down Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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$}
/>
)}
/>
))
)}
{sections.map((section) =>
section
.getAppsEnabled()
.filter((app) => app.redirectFrom)
.map((app) => <Redirect path={`/${app.redirectFrom}*`} to={`${app.basePath}*`} />)
)}
/>
</Routes>
</Router>
)

<Route
path={'/'}
component={() => (
<ManagementLandingPage setBreadcrumbs={setBreadcrumbs} onAppMounted={onAppMounted} />
)}
/>
</Routes>
</Router>
);
}
);
1 change: 1 addition & 0 deletions src/plugins/management/public/mocks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const createSetupContract = (): ManagementSetup => ({
const createStartContract = (): ManagementStart => ({
setIsSidebarEnabled: jest.fn(),
setupCardsNavigation: jest.fn(),
setLandingPageRedirect: jest.fn(),
});

export const managementPluginMock = {
Expand Down
4 changes: 4 additions & 0 deletions src/plugins/management/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
Expand Down Expand Up @@ -124,6 +125,7 @@ export class ManagementPlugin
setBreadcrumbs: coreStart.chrome.setBreadcrumbs,
isSidebarEnabled$: managementPlugin.isSidebarEnabled$,
cardsNavigationConfig$: managementPlugin.cardsNavigationConfig$,
landingPageRedirect$: managementPlugin.landingPageRedirect$,
});
},
});
Expand Down Expand Up @@ -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),
};
}
}
1 change: 1 addition & 0 deletions src/plugins/management/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface DefinedSections {

export interface ManagementStart {
setIsSidebarEnabled: (enabled: boolean) => void;
setLandingPageRedirect: (landingPageRedirect: string) => void;
setupCardsNavigation: ({ enabled, hideLinksTo }: NavigationCardsSubject) => void;
}

Expand Down
4 changes: 3 additions & 1 deletion src/plugins/management/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -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/**/*",
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/serverless_security/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
],
"requiredPlugins": [
"kibanaReact",
"management",
"ml",
"security",
"securitySolution",
Expand Down
4 changes: 3 additions & 1 deletion x-pack/plugins/serverless_security/public/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,15 @@ export class ServerlessSecurityPlugin
core: CoreStart,
startDeps: ServerlessSecurityPluginStartDependencies
): ServerlessSecurityPluginStart {
const { securitySolution, serverless } = startDeps;
const { securitySolution, serverless, management } = startDeps;

securitySolution.setIsSidebarEnabled(false);
securitySolution.setGetStartedPage(getSecurityGetStartedComponent(core, startDeps));
serverless.setProjectHome('/app/security');
serverless.setSideNavComponent(getSecuritySideNavComponent(core, startDeps));

management.setLandingPageRedirect('/app/security/manage');

return {};
}

Expand Down
3 changes: 3 additions & 0 deletions x-pack/plugins/serverless_security/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/serverless_security/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"kbn_references": [
"@kbn/core",
"@kbn/config-schema",
"@kbn/management-plugin",
"@kbn/security-plugin",
"@kbn/security-solution-plugin",
"@kbn/serverless",
Expand Down
3 changes: 3 additions & 0 deletions x-pack/test_serverless/functional/config.base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ export function createTestConfig(options: CreateTestConfigOptions) {
observability: {
pathname: '/app/observability',
},
management: {
pathname: '/app/management',
},
},
// choose where screenshots should be saved
screenshots: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
});
}
Original file line number Diff line number Diff line change
@@ -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');
});
});
}