Move anomaly jobs fetching to context (#94135)
* Revert "[APM] Hoist HeaderMenuPortal to prevent unmounts (#92012)" Revert the change from #92012 to put the `HeaderMenuPortal` back within the routing context so getting data from the path params works. Use a context to fetch the list of jobs and use it in the callout and the header menu icon. This makes it so the fetch only happens once. A refetch function is exposed from the context and called when a new job is created.
This commit is contained in:
parent
931b54f636
commit
b6f8d3f8b6
|
@ -8,7 +8,8 @@
|
|||
import React from 'react';
|
||||
import { render, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { MissingJobsAlert } from './anomaly_detection_setup_link';
|
||||
import * as hooks from '../../hooks/use_fetcher';
|
||||
import * as hooks from '../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
|
||||
import { FETCH_STATUS } from '../../hooks/use_fetcher';
|
||||
|
||||
async function renderTooltipAnchor({
|
||||
jobs,
|
||||
|
@ -18,10 +19,10 @@ async function renderTooltipAnchor({
|
|||
environment?: string;
|
||||
}) {
|
||||
// mock api response
|
||||
jest.spyOn(hooks, 'useFetcher').mockReturnValue({
|
||||
data: { jobs },
|
||||
status: hooks.FETCH_STATUS.SUCCESS,
|
||||
refetch: jest.fn(),
|
||||
jest.spyOn(hooks, 'useAnomalyDetectionJobsContext').mockReturnValue({
|
||||
anomalyDetectionJobsData: { jobs, hasLegacyJobs: false },
|
||||
anomalyDetectionJobsStatus: FETCH_STATUS.SUCCESS,
|
||||
anomalyDetectionJobsRefetch: () => {},
|
||||
});
|
||||
|
||||
const { baseElement, container } = render(
|
||||
|
|
|
@ -18,10 +18,11 @@ import {
|
|||
getEnvironmentLabel,
|
||||
} from '../../../common/environment_filter_values';
|
||||
import { getAPMHref } from '../../components/shared/Links/apm/APMLink';
|
||||
import { useAnomalyDetectionJobsContext } from '../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
|
||||
import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context';
|
||||
import { FETCH_STATUS, useFetcher } from '../../hooks/use_fetcher';
|
||||
import { useLicenseContext } from '../../context/license/use_license_context';
|
||||
import { useUrlParams } from '../../context/url_params_context/use_url_params';
|
||||
import { FETCH_STATUS } from '../../hooks/use_fetcher';
|
||||
import { APIReturnType } from '../../services/rest/createCallApmApi';
|
||||
import { units } from '../../style/variables';
|
||||
|
||||
|
@ -58,22 +59,18 @@ export function AnomalyDetectionSetupLink() {
|
|||
}
|
||||
|
||||
export function MissingJobsAlert({ environment }: { environment?: string }) {
|
||||
const { data = DEFAULT_DATA, status } = useFetcher(
|
||||
(callApmApi) =>
|
||||
callApmApi({
|
||||
endpoint: `GET /api/apm/settings/anomaly-detection/jobs`,
|
||||
}),
|
||||
[],
|
||||
{ preservePreviousData: false, showToastOnError: false }
|
||||
);
|
||||
const {
|
||||
anomalyDetectionJobsData = DEFAULT_DATA,
|
||||
anomalyDetectionJobsStatus,
|
||||
} = useAnomalyDetectionJobsContext();
|
||||
|
||||
const defaultIcon = <EuiIcon type="inspect" color="primary" />;
|
||||
|
||||
if (status === FETCH_STATUS.LOADING) {
|
||||
if (anomalyDetectionJobsStatus === FETCH_STATUS.LOADING) {
|
||||
return <EuiLoadingSpinner />;
|
||||
}
|
||||
|
||||
if (status !== FETCH_STATUS.SUCCESS) {
|
||||
if (anomalyDetectionJobsStatus !== FETCH_STATUS.SUCCESS) {
|
||||
return defaultIcon;
|
||||
}
|
||||
|
||||
|
@ -81,14 +78,14 @@ export function MissingJobsAlert({ environment }: { environment?: string }) {
|
|||
environment && environment !== ENVIRONMENT_ALL.value;
|
||||
|
||||
// there are jobs for at least one environment
|
||||
if (!isEnvironmentSelected && data.jobs.length > 0) {
|
||||
if (!isEnvironmentSelected && anomalyDetectionJobsData.jobs.length > 0) {
|
||||
return defaultIcon;
|
||||
}
|
||||
|
||||
// there are jobs for the selected environment
|
||||
if (
|
||||
isEnvironmentSelected &&
|
||||
data.jobs.some((job) => environment === job.environment)
|
||||
anomalyDetectionJobsData.jobs.some((job) => environment === job.environment)
|
||||
) {
|
||||
return defaultIcon;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ import ReactDOM from 'react-dom';
|
|||
import { Route, Router, Switch } from 'react-router-dom';
|
||||
import 'react-vis/dist/style.css';
|
||||
import { DefaultTheme, ThemeProvider } from 'styled-components';
|
||||
import { HeaderMenuPortal } from '../../../observability/public';
|
||||
import { euiStyled } from '../../../../../src/plugins/kibana_react/common';
|
||||
import { ConfigSchema } from '../';
|
||||
import { AppMountParameters, CoreStart } from '../../../../../src/core/public';
|
||||
|
@ -36,8 +35,7 @@ import { createCallApmApi } from '../services/rest/createCallApmApi';
|
|||
import { createStaticIndexPattern } from '../services/rest/index_pattern';
|
||||
import { setHelpExtension } from '../setHelpExtension';
|
||||
import { setReadonlyBadge } from '../updateBadge';
|
||||
import { useApmPluginContext } from '../context/apm_plugin/use_apm_plugin_context';
|
||||
import { ActionMenu } from './action_menu';
|
||||
import { AnomalyDetectionJobsContextProvider } from '../context/anomaly_detection_jobs/anomaly_detection_jobs_context';
|
||||
|
||||
const MainContainer = euiStyled.div`
|
||||
height: 100%;
|
||||
|
@ -45,7 +43,6 @@ const MainContainer = euiStyled.div`
|
|||
|
||||
function App() {
|
||||
const [darkMode] = useUiSetting$<boolean>('theme:darkMode');
|
||||
const { appMountParameters } = useApmPluginContext();
|
||||
|
||||
useBreadcrumbs(routes);
|
||||
|
||||
|
@ -58,11 +55,6 @@ function App() {
|
|||
})}
|
||||
>
|
||||
<MainContainer data-test-subj="apmMainContainer" role="main">
|
||||
<HeaderMenuPortal
|
||||
setHeaderActionMenu={appMountParameters.setHeaderActionMenu}
|
||||
>
|
||||
<ActionMenu />
|
||||
</HeaderMenuPortal>
|
||||
<Route component={ScrollToTopOnPathChange} />
|
||||
<Switch>
|
||||
{routes.map((route, i) => (
|
||||
|
@ -93,7 +85,9 @@ export function ApmAppRoot({
|
|||
<Router history={history}>
|
||||
<UrlParamsProvider>
|
||||
<LicenseProvider>
|
||||
<App />
|
||||
<AnomalyDetectionJobsContextProvider>
|
||||
<App />
|
||||
</AnomalyDetectionJobsContextProvider>
|
||||
</LicenseProvider>
|
||||
</UrlParamsProvider>
|
||||
</Router>
|
||||
|
|
|
@ -26,6 +26,7 @@ import { useFetcher, FETCH_STATUS } from '../../../../hooks/use_fetcher';
|
|||
import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context';
|
||||
import { createJobs } from './create_jobs';
|
||||
import { getEnvironmentLabel } from '../../../../../common/environment_filter_values';
|
||||
import { useAnomalyDetectionJobsContext } from '../../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
|
||||
|
||||
interface Props {
|
||||
currentEnvironments: string[];
|
||||
|
@ -38,6 +39,7 @@ export function AddEnvironments({
|
|||
onCancel,
|
||||
}: Props) {
|
||||
const { notifications, application } = useApmPluginContext().core;
|
||||
const { anomalyDetectionJobsRefetch } = useAnomalyDetectionJobsContext();
|
||||
const canCreateJob = !!application.capabilities.ml.canCreateJob;
|
||||
const { toasts } = notifications;
|
||||
const { data = [], status } = useFetcher(
|
||||
|
@ -158,6 +160,7 @@ export function AddEnvironments({
|
|||
toasts,
|
||||
});
|
||||
if (success) {
|
||||
anomalyDetectionJobsRefetch();
|
||||
onCreateJobSuccess();
|
||||
}
|
||||
setIsSaving(false);
|
||||
|
|
|
@ -16,6 +16,8 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React, { ReactNode, useState } from 'react';
|
||||
import { RouteComponentProps } from 'react-router-dom';
|
||||
import { HeaderMenuPortal } from '../../../../../observability/public';
|
||||
import { ActionMenu } from '../../../application/action_menu';
|
||||
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
|
||||
import { getAPMHref } from '../../shared/Links/apm/APMLink';
|
||||
import { HomeLink } from '../../shared/Links/apm/HomeLink';
|
||||
|
@ -25,7 +27,7 @@ interface SettingsProps extends RouteComponentProps<{}> {
|
|||
}
|
||||
|
||||
export function Settings({ children, location }: SettingsProps) {
|
||||
const { core } = useApmPluginContext();
|
||||
const { appMountParameters, core } = useApmPluginContext();
|
||||
const { basePath } = core.http;
|
||||
const canAccessML = !!core.application.capabilities.ml?.canAccessML;
|
||||
const { search, pathname } = location;
|
||||
|
@ -42,6 +44,11 @@ export function Settings({ children, location }: SettingsProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<HeaderMenuPortal
|
||||
setHeaderActionMenu={appMountParameters.setHeaderActionMenu}
|
||||
>
|
||||
<ActionMenu />
|
||||
</HeaderMenuPortal>
|
||||
<EuiPage>
|
||||
<EuiPageSideBar>
|
||||
<HomeLink>
|
||||
|
|
|
@ -16,6 +16,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import React, { useEffect } from 'react';
|
||||
import { toMountPoint } from '../../../../../../../src/plugins/kibana_react/public';
|
||||
import { useTrackPageview } from '../../../../../observability/public';
|
||||
import { useAnomalyDetectionJobsContext } from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
|
||||
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
|
||||
import { useUrlParams } from '../../../context/url_params_context/use_url_params';
|
||||
import { useLocalStorage } from '../../../hooks/useLocalStorage';
|
||||
|
@ -25,7 +26,6 @@ import { SearchBar } from '../../shared/search_bar';
|
|||
import { NoServicesMessage } from './no_services_message';
|
||||
import { ServiceList } from './ServiceList';
|
||||
import { MLCallout } from './ServiceList/MLCallout';
|
||||
import { useAnomalyDetectionJobsFetcher } from './use_anomaly_detection_jobs_fetcher';
|
||||
|
||||
const initialData = {
|
||||
items: [],
|
||||
|
@ -108,7 +108,7 @@ export function ServiceInventory() {
|
|||
const {
|
||||
anomalyDetectionJobsData,
|
||||
anomalyDetectionJobsStatus,
|
||||
} = useAnomalyDetectionJobsFetcher();
|
||||
} = useAnomalyDetectionJobsContext();
|
||||
|
||||
const [userHasDismissedCallout, setUserHasDismissedCallout] = useLocalStorage(
|
||||
'apm.userHasDismissedServiceInventoryMlCallout',
|
||||
|
|
|
@ -24,7 +24,7 @@ import { clearCache } from '../../../services/rest/callApi';
|
|||
import * as useDynamicIndexPatternHooks from '../../../hooks/use_dynamic_index_pattern';
|
||||
import { SessionStorageMock } from '../../../services/__mocks__/SessionStorageMock';
|
||||
import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider';
|
||||
import * as hook from './use_anomaly_detection_jobs_fetcher';
|
||||
import * as hook from '../../../context/anomaly_detection_jobs/use_anomaly_detection_jobs_context';
|
||||
import { TimeRangeComparisonType } from '../../shared/time_comparison/get_time_range_comparison';
|
||||
|
||||
const KibanaReactContext = createKibanaReactContext({
|
||||
|
@ -78,9 +78,10 @@ describe('ServiceInventory', () => {
|
|||
global.sessionStorage = new SessionStorageMock();
|
||||
clearCache();
|
||||
|
||||
jest.spyOn(hook, 'useAnomalyDetectionJobsFetcher').mockReturnValue({
|
||||
anomalyDetectionJobsStatus: FETCH_STATUS.SUCCESS,
|
||||
jest.spyOn(hook, 'useAnomalyDetectionJobsContext').mockReturnValue({
|
||||
anomalyDetectionJobsData: { jobs: [], hasLegacyJobs: false },
|
||||
anomalyDetectionJobsStatus: FETCH_STATUS.SUCCESS,
|
||||
anomalyDetectionJobsRefetch: () => {},
|
||||
});
|
||||
|
||||
jest
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* 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 { useFetcher } from '../../../hooks/use_fetcher';
|
||||
|
||||
export function useAnomalyDetectionJobsFetcher() {
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) =>
|
||||
callApmApi({
|
||||
endpoint: `GET /api/apm/settings/anomaly-detection/jobs`,
|
||||
}),
|
||||
[],
|
||||
{ showToastOnError: false }
|
||||
);
|
||||
|
||||
return { anomalyDetectionJobsData: data, anomalyDetectionJobsStatus: status };
|
||||
}
|
|
@ -8,6 +8,9 @@
|
|||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common';
|
||||
import { HeaderMenuPortal } from '../../../../../observability/public';
|
||||
import { ActionMenu } from '../../../application/action_menu';
|
||||
import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context';
|
||||
import { EnvironmentFilter } from '../EnvironmentFilter';
|
||||
|
||||
const HeaderFlexGroup = euiStyled(EuiFlexGroup)`
|
||||
|
@ -17,8 +20,13 @@ const HeaderFlexGroup = euiStyled(EuiFlexGroup)`
|
|||
`;
|
||||
|
||||
export function ApmHeader({ children }: { children: ReactNode }) {
|
||||
const { setHeaderActionMenu } = useApmPluginContext().appMountParameters;
|
||||
|
||||
return (
|
||||
<HeaderFlexGroup alignItems="center" gutterSize="s" wrap={true}>
|
||||
<HeaderMenuPortal setHeaderActionMenu={setHeaderActionMenu}>
|
||||
<ActionMenu />
|
||||
</HeaderMenuPortal>
|
||||
<EuiFlexItem>{children}</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EnvironmentFilter />
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 React, { createContext, ReactChild, useState } from 'react';
|
||||
import { FETCH_STATUS, useFetcher } from '../../hooks/use_fetcher';
|
||||
import { APIReturnType } from '../../services/rest/createCallApmApi';
|
||||
|
||||
export interface AnomalyDetectionJobsContextValue {
|
||||
anomalyDetectionJobsData?: APIReturnType<'GET /api/apm/settings/anomaly-detection/jobs'>;
|
||||
anomalyDetectionJobsStatus: FETCH_STATUS;
|
||||
anomalyDetectionJobsRefetch: () => void;
|
||||
}
|
||||
|
||||
export const AnomalyDetectionJobsContext = createContext(
|
||||
{} as AnomalyDetectionJobsContextValue
|
||||
);
|
||||
|
||||
export function AnomalyDetectionJobsContextProvider({
|
||||
children,
|
||||
}: {
|
||||
children: ReactChild;
|
||||
}) {
|
||||
const [fetchId, setFetchId] = useState(0);
|
||||
const refetch = () => setFetchId((id) => id + 1);
|
||||
|
||||
const { data, status } = useFetcher(
|
||||
(callApmApi) =>
|
||||
callApmApi({
|
||||
endpoint: `GET /api/apm/settings/anomaly-detection/jobs`,
|
||||
}),
|
||||
[fetchId], // eslint-disable-line react-hooks/exhaustive-deps
|
||||
{ showToastOnError: false }
|
||||
);
|
||||
|
||||
return (
|
||||
<AnomalyDetectionJobsContext.Provider
|
||||
value={{
|
||||
anomalyDetectionJobsData: data,
|
||||
anomalyDetectionJobsStatus: status,
|
||||
anomalyDetectionJobsRefetch: refetch,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AnomalyDetectionJobsContext.Provider>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { useContext } from 'react';
|
||||
import { AnomalyDetectionJobsContext } from './anomaly_detection_jobs_context';
|
||||
|
||||
export function useAnomalyDetectionJobsContext() {
|
||||
return useContext(AnomalyDetectionJobsContext);
|
||||
}
|
Loading…
Reference in a new issue