mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[Fleet] Replace hash router by router with scoped history (#106267)
This commit is contained in:
parent
6042e6929c
commit
8924ff3219
29 changed files with 209 additions and 228 deletions
|
@ -106,6 +106,12 @@ export default async function ({ readConfigFile }) {
|
|||
observabilityCases: {
|
||||
pathname: '/app/observability/cases',
|
||||
},
|
||||
fleet: {
|
||||
pathname: '/app/fleet',
|
||||
},
|
||||
integrations: {
|
||||
pathname: '/app/integrations',
|
||||
},
|
||||
},
|
||||
junit: {
|
||||
reportName: 'Chrome UI Functional Tests',
|
||||
|
|
|
@ -10,7 +10,6 @@ import React, { memo, useEffect, useState } from 'react';
|
|||
import type { AppMountParameters } from 'kibana/public';
|
||||
import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui';
|
||||
import type { History } from 'history';
|
||||
import { createHashHistory } from 'history';
|
||||
import { Router, Redirect, Route, Switch, useRouteMatch } from 'react-router-dom';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -20,7 +19,10 @@ import useObservable from 'react-use/lib/useObservable';
|
|||
import type { TopNavMenuData } from 'src/plugins/navigation/public';
|
||||
|
||||
import type { FleetConfigType, FleetStartServices } from '../../plugin';
|
||||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
RedirectAppLinks,
|
||||
} from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
|
||||
|
||||
import { PackageInstallProvider, useUrlModal } from '../integrations/hooks';
|
||||
|
@ -28,7 +30,6 @@ import { PackageInstallProvider, useUrlModal } from '../integrations/hooks';
|
|||
import {
|
||||
ConfigContext,
|
||||
FleetStatusProvider,
|
||||
IntraAppStateProvider,
|
||||
KibanaVersionContext,
|
||||
sendGetPermissionsCheck,
|
||||
sendSetup,
|
||||
|
@ -215,43 +216,31 @@ export const FleetAppContext: React.FC<{
|
|||
}> = memo(
|
||||
({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => {
|
||||
const isDarkMode = useObservable<boolean>(startServices.uiSettings.get$('theme:darkMode'));
|
||||
const [routerHistoryInstance] = useState(routerHistory || createHashHistory());
|
||||
// Sync our hash history with Kibana scoped history
|
||||
useEffect(() => {
|
||||
const unlistenParentHistory = history.listen(() => {
|
||||
const newHash = createHashHistory();
|
||||
if (newHash.location.pathname !== routerHistoryInstance.location.pathname) {
|
||||
routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || '');
|
||||
}
|
||||
});
|
||||
|
||||
return unlistenParentHistory;
|
||||
}, [history, routerHistoryInstance]);
|
||||
|
||||
return (
|
||||
<startServices.i18n.Context>
|
||||
<KibanaContextProvider services={{ ...startServices }}>
|
||||
<EuiErrorBoundary>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<KibanaVersionContext.Provider value={kibanaVersion}>
|
||||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
<UIExtensionsContext.Provider value={extensions}>
|
||||
<FleetStatusProvider>
|
||||
<IntraAppStateProvider kibanaScopedHistory={history}>
|
||||
<Router history={routerHistoryInstance}>
|
||||
<RedirectAppLinks application={startServices.application}>
|
||||
<startServices.i18n.Context>
|
||||
<KibanaContextProvider services={{ ...startServices }}>
|
||||
<EuiErrorBoundary>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<KibanaVersionContext.Provider value={kibanaVersion}>
|
||||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
<UIExtensionsContext.Provider value={extensions}>
|
||||
<FleetStatusProvider>
|
||||
<Router history={history}>
|
||||
<PackageInstallProvider notifications={startServices.notifications}>
|
||||
{children}
|
||||
</PackageInstallProvider>
|
||||
</Router>
|
||||
</IntraAppStateProvider>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
</KibanaVersionContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</EuiErrorBoundary>
|
||||
</KibanaContextProvider>
|
||||
</startServices.i18n.Context>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
</KibanaVersionContext.Provider>
|
||||
</ConfigContext.Provider>
|
||||
</EuiErrorBoundary>
|
||||
</KibanaContextProvider>
|
||||
</startServices.i18n.Context>
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@ -277,7 +266,7 @@ const FleetTopNav = memo(
|
|||
defaultMessage: 'Fleet settings',
|
||||
}),
|
||||
iconType: 'gear',
|
||||
run: () => (window.location.href = getModalHref('settings')),
|
||||
run: () => services.application.navigateToUrl(getModalHref('settings')),
|
||||
},
|
||||
];
|
||||
return (
|
||||
|
@ -327,7 +316,26 @@ export const AppRoutes = memo(
|
|||
<CreatePackagePolicyPage />
|
||||
</Route>
|
||||
|
||||
<Redirect to={FLEET_ROUTING_PATHS.agents} />
|
||||
<Route
|
||||
render={({ location }) => {
|
||||
// BWC < 7.15 Fleet was using a hash router: redirect old routes using hash
|
||||
const shouldRedirectHash = location.pathname === '' && location.hash.length > 0;
|
||||
if (!shouldRedirectHash) {
|
||||
return <Redirect to={FLEET_ROUTING_PATHS.agents} />;
|
||||
}
|
||||
const pathname = location.hash.replace(/^#(\/fleet)?/, '');
|
||||
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
...location,
|
||||
pathname,
|
||||
hash: undefined,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -150,18 +150,30 @@ const breadcrumbGetters: {
|
|||
};
|
||||
|
||||
export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) {
|
||||
const { chrome, http } = useStartServices();
|
||||
const { chrome, http, application } = useStartServices();
|
||||
const breadcrumbs =
|
||||
breadcrumbGetters[page]?.(values).map((breadcrumb) => ({
|
||||
...breadcrumb,
|
||||
href: breadcrumb.href
|
||||
breadcrumbGetters[page]?.(values).map((breadcrumb) => {
|
||||
const href = breadcrumb.href
|
||||
? http.basePath.prepend(
|
||||
`${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}#${
|
||||
`${breadcrumb.useIntegrationsBasePath ? INTEGRATIONS_BASE_PATH : FLEET_BASE_PATH}${
|
||||
breadcrumb.href
|
||||
}`
|
||||
)
|
||||
: undefined,
|
||||
})) || [];
|
||||
: undefined;
|
||||
return {
|
||||
...breadcrumb,
|
||||
href,
|
||||
onClick: href
|
||||
? (ev: React.MouseEvent) => {
|
||||
if (ev.metaKey || ev.altKey || ev.ctrlKey || ev.shiftKey) {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
application.navigateToUrl(href);
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}) || [];
|
||||
const docTitle: string[] = [...breadcrumbs]
|
||||
.reverse()
|
||||
.map((breadcrumb) => breadcrumb.text as string);
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('when on the package policy create page', () => {
|
|||
beforeEach(() => {
|
||||
testRenderer = createFleetTestRendererMock();
|
||||
mockApiCalls(testRenderer.startServices.http);
|
||||
testRenderer.history.push(createPageUrlPath);
|
||||
testRenderer.mountHistory.push(createPageUrlPath);
|
||||
});
|
||||
|
||||
describe('and Route state is provided via Fleet HashRouter', () => {
|
||||
|
@ -43,7 +43,7 @@ describe('when on the package policy create page', () => {
|
|||
onCancelNavigateTo: [PLUGIN_ID, { path: '/cancel/url/here' }],
|
||||
};
|
||||
|
||||
testRenderer.history.replace({
|
||||
testRenderer.mountHistory.replace({
|
||||
pathname: createPageUrlPath,
|
||||
state: expectedRouteState,
|
||||
});
|
||||
|
@ -72,18 +72,18 @@ describe('when on the package policy create page', () => {
|
|||
expect(cancelButton.href).toBe(expectedRouteState.onCancelUrl);
|
||||
});
|
||||
|
||||
it('should redirect via Fleet HashRouter when cancel link is clicked', () => {
|
||||
it('should redirect via history when cancel link is clicked', () => {
|
||||
act(() => {
|
||||
cancelLink.click();
|
||||
});
|
||||
expect(testRenderer.history.location.pathname).toBe('/cancel/url/here');
|
||||
expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here');
|
||||
});
|
||||
|
||||
it('should redirect via Fleet HashRouter when cancel Button (button bar) is clicked', () => {
|
||||
it('should redirect via history when cancel Button (button bar) is clicked', () => {
|
||||
act(() => {
|
||||
cancelButton.click();
|
||||
});
|
||||
expect(testRenderer.history.location.pathname).toBe('/cancel/url/here');
|
||||
expect(testRenderer.mountHistory.location.pathname).toBe('/cancel/url/here');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ export const NoPackagePolicies = memo<{ policyId: string }>(({ policyId }) => {
|
|||
fill
|
||||
onClick={() =>
|
||||
application.navigateToApp(INTEGRATIONS_PLUGIN_ID, {
|
||||
path: `#${pagePathGetters.integrations_all()[1]}`,
|
||||
path: pagePathGetters.integrations_all()[1],
|
||||
state: { forAgentPolicyId: policyId },
|
||||
})
|
||||
}
|
||||
|
|
|
@ -199,7 +199,7 @@ export const PackagePoliciesTable: React.FunctionComponent<Props> = ({
|
|||
iconType="refresh"
|
||||
onClick={() => {
|
||||
application.navigateToApp(INTEGRATIONS_PLUGIN_ID, {
|
||||
path: `#${pagePathGetters.integrations_all()[1]}`,
|
||||
path: pagePathGetters.integrations_all()[1],
|
||||
state: { forAgentPolicyId: agentPolicy.id },
|
||||
});
|
||||
}}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HashRouter as Router, Switch, Route } from 'react-router-dom';
|
||||
import { Router, Switch, Route, useHistory } from 'react-router-dom';
|
||||
|
||||
import { FLEET_ROUTING_PATHS } from '../../constants';
|
||||
import { useBreadcrumbs } from '../../hooks';
|
||||
|
@ -20,9 +20,10 @@ import { EditPackagePolicyPage } from './edit_package_policy_page';
|
|||
|
||||
export const AgentPolicyApp: React.FunctionComponent = () => {
|
||||
useBreadcrumbs('policies');
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={FLEET_ROUTING_PATHS.edit_integration}>
|
||||
<EditPackagePolicyPage />
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import React, { useCallback, useEffect, useState, useMemo } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { Router, Route, Switch, useHistory } from 'react-router-dom';
|
||||
import { EuiButton, EuiFlexGroup, EuiFlexItem, EuiPortal } from '@elastic/eui';
|
||||
|
||||
import { FLEET_ROUTING_PATHS } from '../../constants';
|
||||
|
@ -30,7 +30,7 @@ import { FleetServerUpgradeModal } from './components/fleet_server_upgrade_modal
|
|||
|
||||
export const AgentsApp: React.FunctionComponent = () => {
|
||||
useBreadcrumbs('agent_list');
|
||||
|
||||
const history = useHistory();
|
||||
const { agents } = useConfig();
|
||||
const capabilities = useCapabilities();
|
||||
|
||||
|
@ -118,7 +118,7 @@ export const AgentsApp: React.FunctionComponent = () => {
|
|||
) : undefined;
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={FLEET_ROUTING_PATHS.agent_details}>
|
||||
<AgentDetailsPage />
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { HashRouter as Router, Route, Switch } from 'react-router-dom';
|
||||
import { Router, Route, Switch, useHistory } from 'react-router-dom';
|
||||
|
||||
import { FLEET_ROUTING_PATHS } from '../../constants';
|
||||
import { DefaultLayout } from '../../layouts';
|
||||
|
@ -14,8 +14,10 @@ import { DefaultLayout } from '../../layouts';
|
|||
import { DataStreamListPage } from './list_page';
|
||||
|
||||
export const DataStreamApp: React.FunctionComponent = () => {
|
||||
const history = useHistory();
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={FLEET_ROUTING_PATHS.data_streams}>
|
||||
<DefaultLayout section="data_streams">
|
||||
|
|
|
@ -9,7 +9,6 @@ import React, { memo, useEffect, useState } from 'react';
|
|||
import type { AppMountParameters } from 'kibana/public';
|
||||
import { EuiCode, EuiEmptyPrompt, EuiErrorBoundary, EuiPanel, EuiPortal } from '@elastic/eui';
|
||||
import type { History } from 'history';
|
||||
import { createHashHistory } from 'history';
|
||||
import { Router, Redirect, Route, Switch } from 'react-router-dom';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -26,7 +25,10 @@ import {
|
|||
|
||||
import type { FleetConfigType, FleetStartServices } from '../../plugin';
|
||||
|
||||
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
RedirectAppLinks,
|
||||
} from '../../../../../../src/plugins/kibana_react/public';
|
||||
import { EuiThemeProvider } from '../../../../../../src/plugins/kibana_react/common';
|
||||
|
||||
import { AgentPolicyContextProvider, useUrlModal } from './hooks';
|
||||
|
@ -39,7 +41,7 @@ import type { UIExtensionsStorage } from './types';
|
|||
import { EPMApp } from './sections/epm';
|
||||
import { DefaultLayout, WithoutHeaderLayout } from './layouts';
|
||||
import { PackageInstallProvider } from './hooks';
|
||||
import { useBreadcrumbs, IntraAppStateProvider, UIExtensionsContext } from './hooks';
|
||||
import { useBreadcrumbs, UIExtensionsContext } from './hooks';
|
||||
|
||||
const ErrorLayout = ({ children }: { children: JSX.Element }) => (
|
||||
<EuiErrorBoundary>
|
||||
|
@ -185,25 +187,12 @@ export const IntegrationsAppContext: React.FC<{
|
|||
kibanaVersion: string;
|
||||
extensions: UIExtensionsStorage;
|
||||
/** For testing purposes only */
|
||||
routerHistory?: History<any>;
|
||||
}> = memo(
|
||||
({ children, startServices, config, history, kibanaVersion, extensions, routerHistory }) => {
|
||||
const isDarkMode = useObservable<boolean>(startServices.uiSettings.get$('theme:darkMode'));
|
||||
const [routerHistoryInstance] = useState(routerHistory || createHashHistory());
|
||||
routerHistory?: History<any>; // TODO remove
|
||||
}> = memo(({ children, startServices, config, history, kibanaVersion, extensions }) => {
|
||||
const isDarkMode = useObservable<boolean>(startServices.uiSettings.get$('theme:darkMode'));
|
||||
|
||||
// Sync our hash history with Kibana scoped history
|
||||
useEffect(() => {
|
||||
const unlistenParentHistory = history.listen(() => {
|
||||
const newHash = createHashHistory();
|
||||
if (newHash.location.pathname !== routerHistoryInstance.location.pathname) {
|
||||
routerHistoryInstance.replace(newHash.location.pathname + newHash.location.search || '');
|
||||
}
|
||||
});
|
||||
|
||||
return unlistenParentHistory;
|
||||
}, [history, routerHistoryInstance]);
|
||||
|
||||
return (
|
||||
return (
|
||||
<RedirectAppLinks application={startServices.application}>
|
||||
<startServices.i18n.Context>
|
||||
<KibanaContextProvider services={{ ...startServices }}>
|
||||
<EuiErrorBoundary>
|
||||
|
@ -212,15 +201,13 @@ export const IntegrationsAppContext: React.FC<{
|
|||
<EuiThemeProvider darkMode={isDarkMode}>
|
||||
<UIExtensionsContext.Provider value={extensions}>
|
||||
<FleetStatusProvider>
|
||||
<IntraAppStateProvider kibanaScopedHistory={history}>
|
||||
<Router history={routerHistoryInstance}>
|
||||
<AgentPolicyContextProvider>
|
||||
<PackageInstallProvider notifications={startServices.notifications}>
|
||||
{children}
|
||||
</PackageInstallProvider>
|
||||
</AgentPolicyContextProvider>
|
||||
</Router>
|
||||
</IntraAppStateProvider>
|
||||
<Router history={history}>
|
||||
<AgentPolicyContextProvider>
|
||||
<PackageInstallProvider notifications={startServices.notifications}>
|
||||
{children}
|
||||
</PackageInstallProvider>
|
||||
</AgentPolicyContextProvider>
|
||||
</Router>
|
||||
</FleetStatusProvider>
|
||||
</UIExtensionsContext.Provider>
|
||||
</EuiThemeProvider>
|
||||
|
@ -229,9 +216,9 @@ export const IntegrationsAppContext: React.FC<{
|
|||
</EuiErrorBoundary>
|
||||
</KibanaContextProvider>
|
||||
</startServices.i18n.Context>
|
||||
);
|
||||
}
|
||||
);
|
||||
</RedirectAppLinks>
|
||||
);
|
||||
});
|
||||
|
||||
export const AppRoutes = memo(() => {
|
||||
const { modal, setModal } = useUrlModal();
|
||||
|
@ -250,7 +237,26 @@ export const AppRoutes = memo(() => {
|
|||
<Route path={INTEGRATIONS_ROUTING_PATHS.integrations}>
|
||||
<EPMApp />
|
||||
</Route>
|
||||
<Redirect to={INTEGRATIONS_ROUTING_PATHS.integrations_all} />
|
||||
<Route
|
||||
render={({ location }) => {
|
||||
// BWC < 7.15 Fleet was using a hash router: redirect old routes using hash
|
||||
const shouldRedirectHash = location.pathname === '' && location.hash.length > 0;
|
||||
if (!shouldRedirectHash) {
|
||||
return <Redirect to={INTEGRATIONS_ROUTING_PATHS.integrations_all} />;
|
||||
}
|
||||
const pathname = location.hash.replace(/^#/, '');
|
||||
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
...location,
|
||||
pathname,
|
||||
hash: undefined,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Switch>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -51,14 +51,23 @@ const breadcrumbGetters: {
|
|||
};
|
||||
|
||||
export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) {
|
||||
const { chrome, http } = useStartServices();
|
||||
const { chrome, http, application } = useStartServices();
|
||||
const breadcrumbs: ChromeBreadcrumb[] =
|
||||
breadcrumbGetters[page]?.(values).map((breadcrumb) => ({
|
||||
...breadcrumb,
|
||||
href: breadcrumb.href
|
||||
? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}#${breadcrumb.href}`)
|
||||
: undefined,
|
||||
})) || [];
|
||||
breadcrumbGetters[page]?.(values).map((breadcrumb) => {
|
||||
const href = breadcrumb.href
|
||||
? http.basePath.prepend(`${INTEGRATIONS_BASE_PATH}${breadcrumb.href}`)
|
||||
: undefined;
|
||||
return {
|
||||
...breadcrumb,
|
||||
href,
|
||||
onClick: href
|
||||
? (ev: React.MouseEvent) => {
|
||||
ev.preventDefault();
|
||||
application.navigateToUrl(href);
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
}) || [];
|
||||
const docTitle: string[] = [...breadcrumbs]
|
||||
.reverse()
|
||||
.map((breadcrumb) => breadcrumb.text as string);
|
||||
|
|
|
@ -45,22 +45,22 @@ describe('when on integration detail', () => {
|
|||
</Route>
|
||||
));
|
||||
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
testRenderer = createIntegrationsTestRendererMock();
|
||||
mockedApi = mockApiCalls(testRenderer.startServices.http);
|
||||
testRenderer.history.push(detailPageUrlPath);
|
||||
act(() => testRenderer.mountHistory.push(detailPageUrlPath));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
window.location.hash = '#/';
|
||||
});
|
||||
|
||||
describe('and the package is installed', () => {
|
||||
beforeEach(() => render());
|
||||
|
||||
it('should display agent policy usage count', async () => {
|
||||
await mockedApi.waitForApi();
|
||||
await act(() => mockedApi.waitForApi());
|
||||
|
||||
expect(renderResult.queryByTestId('agentPolicyCount')).not.toBeNull();
|
||||
});
|
||||
|
||||
|
@ -105,11 +105,11 @@ describe('when on integration detail', () => {
|
|||
|
||||
it('should redirect if custom url is accessed', () => {
|
||||
act(() => {
|
||||
testRenderer.history.push(
|
||||
testRenderer.mountHistory.push(
|
||||
pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1]
|
||||
);
|
||||
});
|
||||
expect(testRenderer.history.location.pathname).toEqual('/detail/nginx-0.3.7/overview');
|
||||
expect(testRenderer.mountHistory.location.pathname).toEqual('/detail/nginx-0.3.7/overview');
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -153,7 +153,7 @@ describe('when on integration detail', () => {
|
|||
|
||||
it('should display custom content when tab is clicked', async () => {
|
||||
act(() => {
|
||||
testRenderer.history.push(
|
||||
testRenderer.mountHistory.push(
|
||||
pagePathGetters.integration_details_custom({ pkgkey: 'nginx-0.3.7' })[1]
|
||||
);
|
||||
});
|
||||
|
@ -200,7 +200,7 @@ describe('when on integration detail', () => {
|
|||
|
||||
it('should display custom assets when tab is clicked', async () => {
|
||||
act(() => {
|
||||
testRenderer.history.push(
|
||||
testRenderer.mountHistory.push(
|
||||
pagePathGetters.integration_details_assets({ pkgkey: 'nginx-0.3.7' })[1]
|
||||
);
|
||||
});
|
||||
|
@ -215,7 +215,7 @@ describe('when on integration detail', () => {
|
|||
it('should link to the create page', () => {
|
||||
const addButton = renderResult.getByTestId('addIntegrationPolicyButton') as HTMLAnchorElement;
|
||||
expect(addButton.href).toEqual(
|
||||
'http://localhost/mock/app/fleet#/integrations/nginx-0.3.7/add-integration'
|
||||
'http://localhost/mock/app/fleet/integrations/nginx-0.3.7/add-integration'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -223,7 +223,7 @@ describe('when on integration detail', () => {
|
|||
describe('and on the Policies Tab', () => {
|
||||
const policiesTabURLPath = pagePathGetters.integration_details_policies({ pkgkey })[1];
|
||||
beforeEach(() => {
|
||||
testRenderer.history.push(policiesTabURLPath);
|
||||
testRenderer.mountHistory.push(policiesTabURLPath);
|
||||
render();
|
||||
});
|
||||
|
||||
|
@ -238,7 +238,7 @@ describe('when on integration detail', () => {
|
|||
'integrationNameLink'
|
||||
)[0] as HTMLAnchorElement;
|
||||
expect(firstPolicy.href).toEqual(
|
||||
'http://localhost/mock/app/integrations#/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc'
|
||||
'http://localhost/mock/app/integrations/edit-integration/e8a37031-2907-44f6-89d2-98bd493f60dc'
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -235,22 +235,18 @@ export function Detail() {
|
|||
redirectToPath = [
|
||||
PLUGIN_ID,
|
||||
{
|
||||
path: `#${
|
||||
pagePathGetters.policy_details({
|
||||
policyId: agentPolicyIdFromContext,
|
||||
})[1]
|
||||
}`,
|
||||
path: pagePathGetters.policy_details({
|
||||
policyId: agentPolicyIdFromContext,
|
||||
})[1],
|
||||
},
|
||||
];
|
||||
} else {
|
||||
redirectToPath = [
|
||||
INTEGRATIONS_PLUGIN_ID,
|
||||
{
|
||||
path: `#${
|
||||
pagePathGetters.integration_details_policies({
|
||||
pkgkey,
|
||||
})[1]
|
||||
}`,
|
||||
path: pagePathGetters.integration_details_policies({
|
||||
pkgkey,
|
||||
})[1],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
@ -260,16 +256,16 @@ export function Detail() {
|
|||
onCancelNavigateTo: [
|
||||
INTEGRATIONS_PLUGIN_ID,
|
||||
{
|
||||
path: currentPath,
|
||||
path: pagePathGetters.integration_details_overview({
|
||||
pkgkey,
|
||||
})[1],
|
||||
},
|
||||
],
|
||||
onCancelUrl: currentPath,
|
||||
};
|
||||
|
||||
services.application.navigateToApp(PLUGIN_ID, {
|
||||
// Necessary because of Fleet's HashRouter. Can be changed when
|
||||
// https://github.com/elastic/kibana/issues/96134 is resolved
|
||||
path: `#${path}`,
|
||||
path,
|
||||
state: redirectBackRouteState,
|
||||
});
|
||||
},
|
||||
|
|
|
@ -53,7 +53,7 @@ const LatestVersionLink = ({ name, version }: { name: string; version: string })
|
|||
pkgkey: `${name}-${version}`,
|
||||
});
|
||||
return (
|
||||
<EuiLink href={`#${settingsPath}`}>
|
||||
<EuiLink href={settingsPath}>
|
||||
<FormattedMessage
|
||||
id="xpack.fleet.integrations.settings.packageLatestVersionLink"
|
||||
defaultMessage="latest version"
|
||||
|
|
|
@ -5,72 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { memo, useContext, useMemo } from 'react';
|
||||
import type { AppMountParameters } from 'kibana/public';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import type { AnyIntraAppRouteState } from '../types';
|
||||
|
||||
interface IntraAppState<S extends AnyIntraAppRouteState = AnyIntraAppRouteState> {
|
||||
forRoute: string;
|
||||
routeState?: S;
|
||||
}
|
||||
|
||||
const IntraAppStateContext = React.createContext<IntraAppState>({ forRoute: '' });
|
||||
const wasHandled = new WeakSet<IntraAppState>();
|
||||
|
||||
/**
|
||||
* Provides a bridget between Kibana's ScopedHistory instance (normally used with BrowserRouter)
|
||||
* and the Hash router used within the app in order to enable state to be used between kibana
|
||||
* apps
|
||||
*/
|
||||
export const IntraAppStateProvider = memo<{
|
||||
kibanaScopedHistory: AppMountParameters['history'];
|
||||
children: React.ReactNode;
|
||||
}>(({ kibanaScopedHistory, children }) => {
|
||||
const internalAppToAppState = useMemo<IntraAppState>(() => {
|
||||
return {
|
||||
forRoute: new URL(`${kibanaScopedHistory.location.hash.substr(1)}`, 'http://localhost')
|
||||
.pathname,
|
||||
routeState: kibanaScopedHistory.location.state as AnyIntraAppRouteState,
|
||||
};
|
||||
}, [kibanaScopedHistory.location.state, kibanaScopedHistory.location.hash]);
|
||||
return (
|
||||
<IntraAppStateContext.Provider value={internalAppToAppState}>
|
||||
{children}
|
||||
</IntraAppStateContext.Provider>
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Retrieve UI Route state from the React Router History for the current URL location.
|
||||
* This state can be used by other Kibana Apps to influence certain behaviours in Ingest, for example,
|
||||
* redirecting back to an given Application after a craete action.
|
||||
*/
|
||||
export function useIntraAppState<S = AnyIntraAppRouteState>():
|
||||
| IntraAppState<S>['routeState']
|
||||
| undefined {
|
||||
export function useIntraAppState<S = AnyIntraAppRouteState>(): S | undefined {
|
||||
const location = useLocation();
|
||||
const intraAppState = useContext(IntraAppStateContext);
|
||||
if (!intraAppState) {
|
||||
throw new Error('Hook called outside of IntraAppStateContext');
|
||||
}
|
||||
return useMemo(() => {
|
||||
// Due to the use of HashRouter in Ingest, we only want state to be returned
|
||||
// once so that it does not impact navigation to the page from within the
|
||||
// ingest app. side affect is that the browser back button would not work
|
||||
// consistently either.
|
||||
|
||||
if (location.pathname === intraAppState.forRoute && !wasHandled.has(intraAppState)) {
|
||||
wasHandled.add(intraAppState);
|
||||
return intraAppState.routeState as S;
|
||||
}
|
||||
|
||||
// Default is to return the state in the Fleet HashRouter, in order to enable use of route state
|
||||
// that is used via Kibana's ScopedHistory from within the Fleet HashRouter (ex. things like
|
||||
// `core.application.navigateTo()`
|
||||
// Once this https://github.com/elastic/kibana/issues/70358 is implemented (move to BrowserHistory
|
||||
// using kibana's ScopedHistory), then this work-around can be removed.
|
||||
return location.state as S;
|
||||
}, [intraAppState, location.pathname, location.state]);
|
||||
return location.state as S;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ export const useLink = () => {
|
|||
core.http.basePath.prepend(`/plugins/${PLUGIN_ID}/assets/${path}`),
|
||||
getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => {
|
||||
const [basePath, path] = getSeparatePaths(page, values);
|
||||
return core.http.basePath.prepend(`${basePath}#${path}`);
|
||||
return core.http.basePath.prepend(`${basePath}${path}`);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { History } from 'history';
|
||||
import { createMemoryHistory, createHashHistory } from 'history';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import React, { memo } from 'react';
|
||||
import type { RenderOptions, RenderResult } from '@testing-library/react';
|
||||
import { render as reactRender, act } from '@testing-library/react';
|
||||
|
@ -47,9 +47,10 @@ export const createFleetTestRendererMock = (): TestRenderer => {
|
|||
const basePath = '/mock';
|
||||
const extensions: UIExtensionsStorage = {};
|
||||
const startServices = createStartServices(basePath);
|
||||
const history = createMemoryHistory({ initialEntries: [basePath] });
|
||||
const testRendererMocks: TestRenderer = {
|
||||
history: createHashHistory(),
|
||||
mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath),
|
||||
history,
|
||||
mountHistory: new ScopedHistory(history, basePath),
|
||||
startServices,
|
||||
config: createConfigurationMock(),
|
||||
startInterface: createStartMock(extensions),
|
||||
|
@ -89,7 +90,7 @@ export const createIntegrationsTestRendererMock = (): TestRenderer => {
|
|||
const extensions: UIExtensionsStorage = {};
|
||||
const startServices = createStartServices(basePath);
|
||||
const testRendererMocks: TestRenderer = {
|
||||
history: createHashHistory(),
|
||||
history: createMemoryHistory(),
|
||||
mountHistory: new ScopedHistory(createMemoryHistory({ initialEntries: [basePath] }), basePath),
|
||||
startServices,
|
||||
config: createConfigurationMock(),
|
||||
|
|
|
@ -104,6 +104,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
|
|||
core.application.register({
|
||||
id: INTEGRATIONS_PLUGIN_ID,
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
appRoute: '/app/integrations',
|
||||
title: i18n.translate('xpack.fleet.integrationsAppTitle', {
|
||||
defaultMessage: 'Integrations',
|
||||
}),
|
||||
|
@ -137,6 +138,7 @@ export class FleetPlugin implements Plugin<FleetSetup, FleetStart, FleetSetupDep
|
|||
title: i18n.translate('xpack.fleet.appTitle', { defaultMessage: 'Fleet' }),
|
||||
order: 9020,
|
||||
euiIconType: 'logoElastic',
|
||||
appRoute: '/app/fleet',
|
||||
mount: async (params: AppMountParameters) => {
|
||||
const [coreStartServices, startDepsServices] = (await core.getStartServices()) as [
|
||||
CoreStart,
|
||||
|
|
|
@ -92,7 +92,7 @@ describe('Package search provider', () => {
|
|||
title: 'test',
|
||||
type: 'integration',
|
||||
url: {
|
||||
path: 'undefined#/detail/test-test/overview',
|
||||
path: 'undefined/detail/test-test/overview',
|
||||
prependBasePath: false,
|
||||
},
|
||||
},
|
||||
|
@ -102,7 +102,7 @@ describe('Package search provider', () => {
|
|||
title: 'test1',
|
||||
type: 'integration',
|
||||
url: {
|
||||
path: 'undefined#/detail/test1-test1/overview',
|
||||
path: 'undefined/detail/test1-test1/overview',
|
||||
prependBasePath: false,
|
||||
},
|
||||
},
|
||||
|
@ -175,7 +175,7 @@ describe('Package search provider', () => {
|
|||
title: 'test1',
|
||||
type: 'integration',
|
||||
url: {
|
||||
path: 'undefined#/detail/test1-test1/overview',
|
||||
path: 'undefined/detail/test1-test1/overview',
|
||||
prependBasePath: false,
|
||||
},
|
||||
},
|
||||
|
@ -231,7 +231,7 @@ describe('Package search provider', () => {
|
|||
title: 'test',
|
||||
type: 'integration',
|
||||
url: {
|
||||
path: 'undefined#/detail/test-test/overview',
|
||||
path: 'undefined/detail/test-test/overview',
|
||||
prependBasePath: false,
|
||||
},
|
||||
},
|
||||
|
@ -241,7 +241,7 @@ describe('Package search provider', () => {
|
|||
title: 'test1',
|
||||
type: 'integration',
|
||||
url: {
|
||||
path: 'undefined#/detail/test1-test1/overview',
|
||||
path: 'undefined/detail/test1-test1/overview',
|
||||
prependBasePath: false,
|
||||
},
|
||||
},
|
||||
|
@ -274,7 +274,7 @@ describe('Package search provider', () => {
|
|||
title: 'test1',
|
||||
type: 'integration',
|
||||
url: {
|
||||
path: 'undefined#/detail/test1-test1/overview',
|
||||
path: 'undefined/detail/test1-test1/overview',
|
||||
prependBasePath: false,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -45,10 +45,8 @@ const toSearchResult = (
|
|||
title: pkg.title,
|
||||
score: 80,
|
||||
url: {
|
||||
// TODO: See https://github.com/elastic/kibana/issues/96134 for details about why we use '#' here. Below should be updated
|
||||
// as part of migrating to non-hash based router.
|
||||
// prettier-ignore
|
||||
path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}#${pagePathGetters.integration_details_overview({ pkgkey })[1]}`,
|
||||
path: `${application.getUrlForApp(INTEGRATIONS_PLUGIN_ID)}${pagePathGetters.integration_details_overview({ pkgkey })[1]}`,
|
||||
prependBasePath: false,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ type EventHandlerCallback = MouseEventHandler<HTMLButtonElement | HTMLAnchorElem
|
|||
*
|
||||
* @example
|
||||
*
|
||||
* const handleOnClick = useNavigateToAppEventHandler('fleet', {path: '#/policies'})
|
||||
* const handleOnClick = useNavigateToAppEventHandler('fleet', {path: '/policies'})
|
||||
* return <EuiLink onClick={handleOnClick}>See policies</EuiLink>
|
||||
*/
|
||||
export const useNavigateToAppEventHandler = <S = unknown>(
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
MANAGEMENT_STORE_GLOBAL_NAMESPACE,
|
||||
} from '../../../../common/constants';
|
||||
import { useAppUrl } from '../../../../../common/lib/kibana';
|
||||
import { pagePathGetters } from '../../../../../../../fleet/public';
|
||||
|
||||
export function useEndpointSelector<TSelected>(selector: (state: EndpointState) => TSelected) {
|
||||
return useSelector(function (state: State) {
|
||||
|
@ -47,7 +48,8 @@ export const useAgentDetailsIngestUrl = (
|
|||
): { url: string; appId: string; appPath: string } => {
|
||||
const { getAppUrl } = useAppUrl();
|
||||
return useMemo(() => {
|
||||
const appPath = `#/fleet/agents/${agentId}/activity`;
|
||||
const appPath = pagePathGetters.agent_details_logs({ agentId })[1];
|
||||
|
||||
return {
|
||||
url: `${getAppUrl({ appId: 'fleet' })}${appPath}`,
|
||||
appId: 'fleet',
|
||||
|
|
|
@ -120,13 +120,13 @@ export const useEndpointActionItems = (
|
|||
'data-test-subj': 'agentPolicyLink',
|
||||
navigateAppId: 'fleet',
|
||||
navigateOptions: {
|
||||
path: `#${
|
||||
path: `${
|
||||
pagePathGetters.policy_details({
|
||||
policyId: fleetAgentPolicies[endpointPolicyId],
|
||||
})[1]
|
||||
}`,
|
||||
},
|
||||
href: `${getAppUrl({ appId: 'fleet' })}#${
|
||||
href: `${getAppUrl({ appId: 'fleet' })}${
|
||||
pagePathGetters.policy_details({
|
||||
policyId: fleetAgentPolicies[endpointPolicyId],
|
||||
})[1]
|
||||
|
@ -145,13 +145,13 @@ export const useEndpointActionItems = (
|
|||
'data-test-subj': 'agentDetailsLink',
|
||||
navigateAppId: 'fleet',
|
||||
navigateOptions: {
|
||||
path: `#${
|
||||
path: `${
|
||||
pagePathGetters.agent_details({
|
||||
agentId: fleetAgentId,
|
||||
})[1]
|
||||
}`,
|
||||
},
|
||||
href: `${getAppUrl({ appId: 'fleet' })}#${
|
||||
href: `${getAppUrl({ appId: 'fleet' })}${
|
||||
pagePathGetters.agent_details({
|
||||
agentId: fleetAgentId,
|
||||
})[1]
|
||||
|
@ -169,17 +169,17 @@ export const useEndpointActionItems = (
|
|||
'data-test-subj': 'agentPolicyReassignLink',
|
||||
navigateAppId: 'fleet',
|
||||
navigateOptions: {
|
||||
path: `#${
|
||||
path: `${
|
||||
pagePathGetters.agent_details({
|
||||
agentId: fleetAgentId,
|
||||
})[1]
|
||||
}/activity?openReassignFlyout=true`,
|
||||
}?openReassignFlyout=true`,
|
||||
},
|
||||
href: `${getAppUrl({ appId: 'fleet' })}#${
|
||||
href: `${getAppUrl({ appId: 'fleet' })}${
|
||||
pagePathGetters.agent_details({
|
||||
agentId: fleetAgentId,
|
||||
})[1]
|
||||
}/activity?openReassignFlyout=true`,
|
||||
}?openReassignFlyout=true`,
|
||||
children: (
|
||||
<FormattedMessage
|
||||
id="xpack.securitySolution.endpoint.actions.agentPolicyReassign"
|
||||
|
|
|
@ -1248,17 +1248,17 @@ describe('when on the endpoint list page', () => {
|
|||
});
|
||||
it('navigates to the Ingest Agent Policy page', async () => {
|
||||
const agentPolicyLink = await renderResult.findByTestId('agentPolicyLink');
|
||||
expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet#/policies/${agentPolicyId}`);
|
||||
expect(agentPolicyLink.getAttribute('href')).toEqual(`/app/fleet/policies/${agentPolicyId}`);
|
||||
});
|
||||
it('navigates to the Ingest Agent Details page', async () => {
|
||||
const agentDetailsLink = await renderResult.findByTestId('agentDetailsLink');
|
||||
expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet#/agents/${agentId}`);
|
||||
expect(agentDetailsLink.getAttribute('href')).toEqual(`/app/fleet/agents/${agentId}`);
|
||||
});
|
||||
|
||||
it('navigates to the Ingest Agent Details page with policy reassign', async () => {
|
||||
const agentPolicyReassignLink = await renderResult.findByTestId('agentPolicyReassignLink');
|
||||
expect(agentPolicyReassignLink.getAttribute('href')).toEqual(
|
||||
`/app/fleet#/agents/${agentId}/activity?openReassignFlyout=true`
|
||||
`/app/fleet/agents/${agentId}?openReassignFlyout=true`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -156,7 +156,7 @@ export const EndpointList = () => {
|
|||
const handleCreatePolicyClick = useNavigateToAppEventHandler<CreatePackagePolicyRouteState>(
|
||||
'fleet',
|
||||
{
|
||||
path: `#/integrations/${
|
||||
path: `/integrations/${
|
||||
endpointPackageVersion ? `/endpoint-${endpointPackageVersion}` : ''
|
||||
}/add-integration`,
|
||||
state: {
|
||||
|
@ -203,7 +203,7 @@ export const EndpointList = () => {
|
|||
const handleDeployEndpointsClick = useNavigateToAppEventHandler<AgentPolicyDetailsDeployAgentAction>(
|
||||
'fleet',
|
||||
{
|
||||
path: `#/policies/${selectedPolicyId}?openEnrollmentFlyout=true`,
|
||||
path: `/policies/${selectedPolicyId}?openEnrollmentFlyout=true`,
|
||||
state: {
|
||||
onDoneNavigateTo: [
|
||||
'securitySolution',
|
||||
|
|
|
@ -63,7 +63,7 @@ describe('OverviewEmpty', () => {
|
|||
fill: false,
|
||||
label: 'Add Endpoint Security',
|
||||
onClick: undefined,
|
||||
url: `#/integrations/endpoint-${endpointPackageVersion}/add-integration`,
|
||||
url: `/integrations/endpoint-${endpointPackageVersion}/add-integration`,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
|
|
@ -36,7 +36,7 @@ const OverviewEmptyComponent: React.FC = () => {
|
|||
const endpointIntegrationUrlPath = endpointPackageVersion
|
||||
? `/endpoint-${endpointPackageVersion}/add-integration`
|
||||
: '';
|
||||
const endpointIntegrationUrl = `#/integrations${endpointIntegrationUrlPath}`;
|
||||
const endpointIntegrationUrl = `/integrations${endpointIntegrationUrlPath}`;
|
||||
const handleEndpointClick = useNavigateToAppEventHandler('fleet', {
|
||||
path: endpointIntegrationUrl,
|
||||
});
|
||||
|
|
|
@ -24,25 +24,17 @@ export function SyntheticsIntegrationPageProvider({
|
|||
*
|
||||
*/
|
||||
async navigateToPackagePage(packageVersion: string) {
|
||||
await pageObjects.common.navigateToUrl(
|
||||
await pageObjects.common.navigateToUrlWithBrowserHistory(
|
||||
'fleet',
|
||||
`/integrations/synthetics-${packageVersion}/add-integration`,
|
||||
{
|
||||
shouldUseHashForSubUrl: true,
|
||||
useActualUrl: true,
|
||||
}
|
||||
`/integrations/synthetics-${packageVersion}/add-integration`
|
||||
);
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
},
|
||||
|
||||
async navigateToPackageEditPage(packageId: string, agentId: string) {
|
||||
await pageObjects.common.navigateToUrl(
|
||||
await pageObjects.common.navigateToUrlWithBrowserHistory(
|
||||
'fleet',
|
||||
`/policies/${agentId}/edit-integration/${packageId}`,
|
||||
{
|
||||
shouldUseHashForSubUrl: true,
|
||||
useActualUrl: true,
|
||||
}
|
||||
`/policies/${agentId}/edit-integration/${packageId}`
|
||||
);
|
||||
await pageObjects.header.waitUntilLoadingHasFinished();
|
||||
},
|
||||
|
|
|
@ -17,9 +17,10 @@ export function FleetIntegrations({ getService, getPageObjects }: FtrProviderCon
|
|||
|
||||
return {
|
||||
async navigateToIntegrationDetails(pkgkey: string) {
|
||||
await pageObjects.common.navigateToApp(INTEGRATIONS_PLUGIN_ID, {
|
||||
hash: pagePathGetters.integration_details_overview({ pkgkey })[1],
|
||||
});
|
||||
await pageObjects.common.navigateToUrlWithBrowserHistory(
|
||||
INTEGRATIONS_PLUGIN_ID,
|
||||
pagePathGetters.integration_details_overview({ pkgkey })[1]
|
||||
);
|
||||
},
|
||||
|
||||
async integrationDetailCustomTabExistsOrFail() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue