Skip to content

Commit

Permalink
[Deployment Management] Add landing page redirect feature and impleme…
Browse files Browse the repository at this point in the history
…nt in security solution (#161060)
  • Loading branch information
sabarasaba authored Jul 4, 2023
1 parent 472d843 commit 2a71469
Show file tree
Hide file tree
Showing 15 changed files with 126 additions and 40 deletions.
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
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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 }) => (
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 @@ -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);
Expand All @@ -62,6 +62,8 @@ export class ServerlessSecurityPlugin
subscribeNavigationTree(services);
subscribeBreadcrumbs(services);

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');
});
});
}

0 comments on commit 2a71469

Please sign in to comment.