mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ml] Optimize bundles, reduce page load async chunks by 74% (#179311)
## Summary The `AnomalyExplorerChartsService` was importing the `SWIM_LANE_LABEL_WIDTH` numerical constant from `swimlane_container.tsx`. As a result, the _entirety_ of that file was being included in the async bundle. `AnomalyExplorerChartsService` is loaded asynchronously on page load by the embeddable. By relocating the constant to its own file-- as well as other optimizations (see below)-- we reduce the async page load by 74%, (in dev mode). ### Before - `351k` <img width="1220" alt="Screenshot 2024-03-24 at 10 09 31 AM" src="6cea41f5
-fb10-41c3-8951-a4be897174f9"> ### After - `93.4k` <img width="1471" alt="Screenshot 2024-04-05 at 11 45 45 AM" src="a07e7a19
-c1af-4b45-a195-69730fc61b0c"> Unfortunately, this change led to a bloating of async modules, the cause of which is still unclear. The application async chunk weighed in at 2.2 MB compressed! To get this PR to a shippable state, I refactored additional code to bring down duplication and bloat. The result is an `ml` experience that fetches small bundles on demand as someone interacts with it:  More work can be done to continue to optimize the plugin, of course, but this set of changes is an excellent start, (and case study on bundle load). ### Other optimizations in this PR - Registration of some `start` services are conditional, and contain their own async calls. I've removed these from the register helper, (which itself is a brute-force offload of code from the plugin, but is still loaded every time), and loaded them async if the conditions apply. - Routing in `ml` use factories to create individual routes. In a lot of cases, the pages these routes displayed were not asynchronously loaded, adding tremendous amounts of React code to the root application. - I made all pages loaded by routes async. - In some cases, the components themselves were colocated with the route factory. I moved those to their own files for async import. - Similarly, some state managers were colocated. Those were moved to their own files, as well. - Moved flyouts, modals, expanding rows to `React.lazy` async modules, (using `dynamic` from `@kbn/shared-ux-utility`. - Refactored `export * from` directives from `public/shared.ts` to accurately reflect what is being consumed outside the `ml` plugin, (and also reduce the size of _that_ bundle. - Refactored `lodash` imports to submodule imports to enable tree-shaking, (if ever enabled in webpack). - Moved `getMlGlobalServices` off of the `app.tsx` file for import by others, (to avoid depending on the rest of the code there). - Moved some types to `/common` to avoid importing code, (though, admittedly, types are compiled away). But it felt cleaner to move them out. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
7c6f4fe7d7
commit
67ab4f7732
74 changed files with 960 additions and 761 deletions
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import get from 'lodash/get';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import startsWith from 'lodash/startsWith';
|
||||
import { startsWith } from 'lodash';
|
||||
import type { Reducer, ReducerAction } from 'react';
|
||||
|
||||
import type { HttpSetup, HttpFetchOptions } from '@kbn/core/public';
|
||||
|
|
|
@ -10,3 +10,4 @@ export { getDefaultCapabilities as getDefaultMlCapabilities } from './types/capa
|
|||
export { DATAFEED_STATE, JOB_STATE } from './constants/states';
|
||||
export type { MlSummaryJob, SummaryJobState } from './types/anomaly_detection_jobs';
|
||||
export { ML_ALERT_TYPES } from './constants/alerts';
|
||||
export type { Job, Datafeed } from './types/anomaly_detection_jobs';
|
||||
|
|
|
@ -10,9 +10,7 @@ import './_index.scss';
|
|||
import ReactDOM from 'react-dom';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import type { AppMountParameters, CoreStart, HttpStart } from '@kbn/core/public';
|
||||
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import type { AppMountParameters, CoreStart } from '@kbn/core/public';
|
||||
import { DatePickerContextProvider, type DatePickerDependencies } from '@kbn/ml-date-picker';
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import { UI_SETTINGS } from '@kbn/data-plugin/common';
|
||||
|
@ -21,21 +19,15 @@ import { StorageContextProvider } from '@kbn/ml-local-storage';
|
|||
import useLifecycles from 'react-use/lib/useLifecycles';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import type { ExperimentalFeatures, MlFeatures } from '../../common/constants/app';
|
||||
import { MlLicense } from '../../common/license';
|
||||
import { MlCapabilitiesService } from './capabilities/check_capabilities';
|
||||
import { ML_STORAGE_KEYS } from '../../common/types/storage';
|
||||
import type { MlSetupDependencies, MlStartDependencies } from '../plugin';
|
||||
import { clearCache, setDependencyCache } from './util/dependency_cache';
|
||||
import { setLicenseCache } from './license';
|
||||
import { mlUsageCollectionProvider } from './services/usage_collection';
|
||||
import { MlRouter } from './routing';
|
||||
import { mlApiServicesProvider } from './services/ml_api_service';
|
||||
import { HttpService } from './services/http_service';
|
||||
import type { PageDependencies } from './routing/router';
|
||||
import { EnabledFeaturesContextProvider } from './contexts/ml';
|
||||
import type { StartServices } from './contexts/kibana';
|
||||
import { fieldFormatServiceFactory } from './services/field_format_service_factory';
|
||||
import { indexServiceFactory } from './util/index_service';
|
||||
import { getMlGlobalServices } from './util/get_services';
|
||||
|
||||
export type MlDependencies = Omit<
|
||||
MlSetupDependencies,
|
||||
|
@ -54,38 +46,6 @@ interface AppProps {
|
|||
|
||||
const localStorage = new Storage(window.localStorage);
|
||||
|
||||
/**
|
||||
* Provides global services available across the entire ML app.
|
||||
*/
|
||||
export function getMlGlobalServices(
|
||||
httpStart: HttpStart,
|
||||
dataViews: DataViewsContract,
|
||||
usageCollection?: UsageCollectionSetup
|
||||
) {
|
||||
const httpService = new HttpService(httpStart);
|
||||
const mlApiServices = mlApiServicesProvider(httpService);
|
||||
// Note on the following services:
|
||||
// - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`,
|
||||
// but it's not being made available as part of global services. Since it's just
|
||||
// some stateless utils `useMlIndexUtils()` should be used from within components.
|
||||
// - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache",
|
||||
// so because of its own state it needs to be made available as a global service.
|
||||
// In the long run we should again try to get rid of it here and make it available via
|
||||
// its own context or possibly without having a singleton like state at all, since the
|
||||
// way this manages its own state right now doesn't consider React component lifecycles.
|
||||
const mlIndexUtils = indexServiceFactory(dataViews);
|
||||
const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils);
|
||||
|
||||
return {
|
||||
httpService,
|
||||
mlApiServices,
|
||||
mlFieldFormatService,
|
||||
mlUsageCollection: mlUsageCollectionProvider(usageCollection),
|
||||
mlCapabilities: new MlCapabilitiesService(mlApiServices),
|
||||
mlLicense: new MlLicense(),
|
||||
};
|
||||
}
|
||||
|
||||
export interface MlServicesContext {
|
||||
mlServices: MlGlobalServices;
|
||||
}
|
||||
|
|
|
@ -6,240 +6,30 @@
|
|||
*/
|
||||
|
||||
import type { FC } from 'react';
|
||||
import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { EuiButtonEmpty } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiFlyout,
|
||||
EuiFlyoutFooter,
|
||||
EuiFlyoutHeader,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiButtonEmpty,
|
||||
EuiButton,
|
||||
EuiFlyoutBody,
|
||||
EuiTitle,
|
||||
EuiSpacer,
|
||||
EuiCheckbox,
|
||||
EuiTabs,
|
||||
EuiTab,
|
||||
EuiLoadingSpinner,
|
||||
EuiConfirmModal,
|
||||
} from '@elastic/eui';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
import { ExportJobDependenciesWarningCallout } from './export_job_warning_callout';
|
||||
import { JobsExportService } from './jobs_export_service';
|
||||
import type { JobDependencies } from './jobs_export_service';
|
||||
import { toastNotificationServiceProvider } from '../../../services/toast_notification_service';
|
||||
import type { JobType } from '../../../../../common/types/saved_objects';
|
||||
import { useEnabledFeatures } from '../../../contexts/ml';
|
||||
import type { ExportJobsFlyoutContentProps } from './export_jobs_flyout_content';
|
||||
|
||||
interface Props {
|
||||
export interface Props extends Pick<ExportJobsFlyoutContentProps, 'currentTab'> {
|
||||
isDisabled: boolean;
|
||||
currentTab: JobType;
|
||||
}
|
||||
|
||||
export const ExportJobsFlyout: FC<Props> = ({ isDisabled, currentTab }) => {
|
||||
const {
|
||||
services: {
|
||||
notifications: { toasts },
|
||||
mlServices: { mlUsageCollection, mlApiServices },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const ExportJobsFlyoutContent = dynamic(async () => ({
|
||||
default: (await import('./export_jobs_flyout_content')).ExportJobsFlyoutContent,
|
||||
}));
|
||||
|
||||
const {
|
||||
getJobs,
|
||||
dataFrameAnalytics: { getDataFrameAnalytics },
|
||||
} = mlApiServices;
|
||||
|
||||
const jobsExportService = useMemo(() => new JobsExportService(mlApiServices), [mlApiServices]);
|
||||
|
||||
const [loadingADJobs, setLoadingADJobs] = useState(true);
|
||||
const [loadingDFAJobs, setLoadingDFAJobs] = useState(true);
|
||||
export const ExportJobsFlyout: FC<Props> = ({ isDisabled, ...rest }) => {
|
||||
const [showFlyout, setShowFlyout] = useState(false);
|
||||
const [adJobIds, setAdJobIds] = useState<string[]>([]);
|
||||
const [dfaJobIds, setDfaJobIds] = useState<string[]>([]);
|
||||
const [selectedJobIds, setSelectedJobIds] = useState<string[]>([]);
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [selectedJobType, setSelectedJobType] = useState<JobType>(currentTab);
|
||||
const [switchTabConfirmVisible, setSwitchTabConfirmVisible] = useState(false);
|
||||
const [switchTabNextTab, setSwitchTabNextTab] = useState<JobType>(currentTab);
|
||||
const { displayErrorToast, displaySuccessToast } = useMemo(
|
||||
() => toastNotificationServiceProvider(toasts),
|
||||
[toasts]
|
||||
);
|
||||
const { isADEnabled, isDFAEnabled } = useEnabledFeatures();
|
||||
|
||||
const [jobDependencies, setJobDependencies] = useState<JobDependencies>([]);
|
||||
const [selectedJobDependencies, setSelectedJobDependencies] = useState<JobDependencies>([]);
|
||||
|
||||
const isMounted = useRef(true);
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(
|
||||
function onFlyoutChange() {
|
||||
setLoadingADJobs(true);
|
||||
setLoadingDFAJobs(true);
|
||||
setAdJobIds([]);
|
||||
setSelectedJobIds([]);
|
||||
setExporting(false);
|
||||
setSelectedJobType(currentTab);
|
||||
setSwitchTabConfirmVisible(false);
|
||||
|
||||
if (showFlyout) {
|
||||
if (isADEnabled) {
|
||||
getJobs()
|
||||
.then(({ jobs }) => {
|
||||
if (isMounted.current === false) return;
|
||||
setLoadingADJobs(false);
|
||||
setAdJobIds(jobs.map((j) => j.job_id));
|
||||
|
||||
jobsExportService
|
||||
.getJobDependencies(jobs)
|
||||
.then((jobDeps) => {
|
||||
if (isMounted.current === false) return;
|
||||
setJobDependencies(jobDeps);
|
||||
setLoadingADJobs(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (isMounted.current === false) return;
|
||||
const errorTitle = i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.calendarsError',
|
||||
{
|
||||
defaultMessage: 'Could not load calendars',
|
||||
}
|
||||
);
|
||||
displayErrorToast(error, errorTitle);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
if (isMounted.current === false) return;
|
||||
const errorTitle = i18n.translate('xpack.ml.importExport.exportFlyout.adJobsError', {
|
||||
defaultMessage: 'Could not load anomaly detection jobs',
|
||||
});
|
||||
displayErrorToast(error, errorTitle);
|
||||
});
|
||||
}
|
||||
|
||||
if (isDFAEnabled) {
|
||||
getDataFrameAnalytics()
|
||||
.then(({ data_frame_analytics: dataFrameAnalytics }) => {
|
||||
if (isMounted.current === false) return;
|
||||
setLoadingDFAJobs(false);
|
||||
setDfaJobIds(dataFrameAnalytics.map((j) => j.id));
|
||||
})
|
||||
.catch((error) => {
|
||||
if (isMounted.current === false) return;
|
||||
|
||||
const errorTitle = i18n.translate('xpack.ml.importExport.exportFlyout.dfaJobsError', {
|
||||
defaultMessage: 'Could not load data frame analytics jobs',
|
||||
});
|
||||
displayErrorToast(error, errorTitle);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
currentTab,
|
||||
displayErrorToast,
|
||||
getDataFrameAnalytics,
|
||||
getJobs,
|
||||
isADEnabled,
|
||||
isDFAEnabled,
|
||||
jobsExportService,
|
||||
showFlyout,
|
||||
]
|
||||
);
|
||||
|
||||
function toggleFlyout() {
|
||||
setShowFlyout(!showFlyout);
|
||||
}
|
||||
|
||||
async function onExport() {
|
||||
setExporting(true);
|
||||
const title = i18n.translate('xpack.ml.importExport.exportFlyout.exportDownloading', {
|
||||
defaultMessage: 'Your file is downloading in the background',
|
||||
});
|
||||
displaySuccessToast(title);
|
||||
|
||||
try {
|
||||
if (selectedJobType === 'anomaly-detector') {
|
||||
await jobsExportService.exportAnomalyDetectionJobs(selectedJobIds);
|
||||
} else {
|
||||
await jobsExportService.exportDataframeAnalyticsJobs(selectedJobIds);
|
||||
}
|
||||
|
||||
mlUsageCollection.count(
|
||||
selectedJobType === 'anomaly-detector'
|
||||
? 'exported_anomaly_detector_jobs'
|
||||
: 'exported_data_frame_analytics_jobs',
|
||||
selectedJobIds.length
|
||||
);
|
||||
|
||||
setExporting(false);
|
||||
setShowFlyout(false);
|
||||
} catch (error) {
|
||||
const errorTitle = i18n.translate('xpack.ml.importExport.exportFlyout.exportError', {
|
||||
defaultMessage: 'Could not export selected jobs',
|
||||
});
|
||||
displayErrorToast(error, errorTitle);
|
||||
setExporting(false);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelectedJob(checked: boolean, id: string) {
|
||||
if (checked) {
|
||||
setSelectedJobIds([...selectedJobIds, id]);
|
||||
} else {
|
||||
setSelectedJobIds(selectedJobIds.filter((id2) => id2 !== id));
|
||||
}
|
||||
}
|
||||
|
||||
const attemptTabSwitch = useCallback(
|
||||
(jobType: JobType) => {
|
||||
if (jobType === selectedJobType) {
|
||||
return;
|
||||
}
|
||||
// if the user has already selected some jobs, open a confirm modal
|
||||
// rather than changing tabs
|
||||
if (selectedJobIds.length > 0) {
|
||||
setSwitchTabNextTab(jobType);
|
||||
setSwitchTabConfirmVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
switchTab(jobType);
|
||||
},
|
||||
|
||||
[selectedJobIds, selectedJobType]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedJobDependencies(
|
||||
jobDependencies.filter(({ jobId }) => selectedJobIds.includes(jobId))
|
||||
);
|
||||
}, [jobDependencies, selectedJobIds]);
|
||||
|
||||
function switchTab(jobType: JobType) {
|
||||
setSwitchTabConfirmVisible(false);
|
||||
setSelectedJobIds([]);
|
||||
setSelectedJobType(jobType);
|
||||
}
|
||||
|
||||
function onSelectAll() {
|
||||
const ids = selectedJobType === 'anomaly-detector' ? adJobIds : dfaJobIds;
|
||||
if (selectedJobIds.length === ids.length) {
|
||||
setSelectedJobIds([]);
|
||||
} else {
|
||||
setSelectedJobIds([...ids]);
|
||||
}
|
||||
}
|
||||
|
||||
if (isADEnabled === false && isDFAEnabled === false) {
|
||||
return null;
|
||||
}
|
||||
|
@ -249,182 +39,10 @@ export const ExportJobsFlyout: FC<Props> = ({ isDisabled, currentTab }) => {
|
|||
<FlyoutButton onClick={toggleFlyout} isDisabled={isDisabled} />
|
||||
|
||||
{showFlyout === true && isDisabled === false && (
|
||||
<>
|
||||
<EuiFlyout
|
||||
onClose={() => setShowFlyout(false)}
|
||||
hideCloseButton
|
||||
size="s"
|
||||
data-test-subj="mlJobMgmtExportJobsFlyout"
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.flyoutHeader"
|
||||
defaultMessage="Export jobs"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ExportJobDependenciesWarningCallout jobs={selectedJobDependencies} />
|
||||
<EuiTabs size="s">
|
||||
{isADEnabled === true ? (
|
||||
<EuiTab
|
||||
isSelected={selectedJobType === 'anomaly-detector'}
|
||||
onClick={() => attemptTabSwitch('anomaly-detector')}
|
||||
disabled={exporting}
|
||||
data-test-subj="mlJobMgmtExportJobsADTab"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adTab"
|
||||
defaultMessage="Anomaly detection"
|
||||
/>
|
||||
</EuiTab>
|
||||
) : null}
|
||||
{isDFAEnabled === true ? (
|
||||
<EuiTab
|
||||
isSelected={selectedJobType === 'data-frame-analytics'}
|
||||
onClick={() => attemptTabSwitch('data-frame-analytics')}
|
||||
disabled={exporting}
|
||||
data-test-subj="mlJobMgmtExportJobsDFATab"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaTab"
|
||||
defaultMessage="Analytics"
|
||||
/>
|
||||
</EuiTab>
|
||||
) : null}
|
||||
</EuiTabs>
|
||||
<EuiSpacer size="s" />
|
||||
<>
|
||||
{isADEnabled === true && selectedJobType === 'anomaly-detector' && (
|
||||
<>
|
||||
{loadingADJobs === true ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
onClick={onSelectAll}
|
||||
isDisabled={isDisabled}
|
||||
data-test-subj="mlJobMgmtExportJobsSelectAllButton"
|
||||
>
|
||||
{selectedJobIds.length === adJobIds.length ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adDeselectAllButton"
|
||||
defaultMessage="Deselect all"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adSelectAllButton"
|
||||
defaultMessage="Select all"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
|
||||
<div data-test-subj="mlJobMgmtExportJobsADJobList">
|
||||
{adJobIds.map((id) => (
|
||||
<div key={id}>
|
||||
<EuiCheckbox
|
||||
id={id}
|
||||
label={id}
|
||||
checked={selectedJobIds.includes(id)}
|
||||
onChange={(e) => toggleSelectedJob(e.target.checked, id)}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isDFAEnabled === true && selectedJobType === 'data-frame-analytics' && (
|
||||
<>
|
||||
{loadingDFAJobs === true ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
onClick={onSelectAll}
|
||||
isDisabled={isDisabled}
|
||||
data-test-subj="mlJobMgmtExportJobsSelectAllButton"
|
||||
>
|
||||
{selectedJobIds.length === dfaJobIds.length ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaDeselectAllButton"
|
||||
defaultMessage="Deselect all"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaSelectAllButton"
|
||||
defaultMessage="Select all"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
<div data-test-subj="mlJobMgmtExportJobsDFAJobList">
|
||||
{dfaJobIds.map((id) => (
|
||||
<div key={id}>
|
||||
<EuiCheckbox
|
||||
id={id}
|
||||
label={id}
|
||||
checked={selectedJobIds.includes(id)}
|
||||
onChange={(e) => toggleSelectedJob(e.target.checked, id)}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty
|
||||
iconType="cross"
|
||||
onClick={setShowFlyout.bind(null, false)}
|
||||
flush="left"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.closeButton"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
disabled={selectedJobIds.length === 0 || exporting === true}
|
||||
onClick={onExport}
|
||||
fill
|
||||
data-test-subj="mlJobMgmtExportExportButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.exportButton"
|
||||
defaultMessage="Export"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
|
||||
{switchTabConfirmVisible === true ? (
|
||||
<SwitchTabsConfirm
|
||||
onCancel={setSwitchTabConfirmVisible.bind(null, false)}
|
||||
onConfirm={() => switchTab(switchTabNextTab)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
<ExportJobsFlyoutContent
|
||||
onClose={() => setShowFlyout(false)}
|
||||
{...{ isADEnabled, isDFAEnabled, ...rest }}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@ -442,47 +60,3 @@ const FlyoutButton: FC<{ isDisabled: boolean; onClick(): void }> = ({ isDisabled
|
|||
</EuiButtonEmpty>
|
||||
);
|
||||
};
|
||||
|
||||
const LoadingSpinner: FC = () => (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="l" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
);
|
||||
|
||||
const SwitchTabsConfirm: FC<{ onCancel: () => void; onConfirm: () => void }> = ({
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}) => (
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate('xpack.ml.importExport.exportFlyout.switchTabsConfirm.title', {
|
||||
defaultMessage: 'Change tabs?',
|
||||
})}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.switchTabsConfirm.cancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.switchTabsConfirm.confirmButton',
|
||||
{
|
||||
defaultMessage: 'Confirm',
|
||||
}
|
||||
)}
|
||||
defaultFocusedButton="confirm"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.switchTabsConfirm.text"
|
||||
defaultMessage="Changing tabs will clear currently selected jobs"
|
||||
/>
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* 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 {
|
||||
EuiButton,
|
||||
EuiButtonEmpty,
|
||||
EuiCheckbox,
|
||||
EuiConfirmModal,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiFlyout,
|
||||
EuiFlyoutBody,
|
||||
EuiFlyoutFooter,
|
||||
EuiFlyoutHeader,
|
||||
EuiLoadingSpinner,
|
||||
EuiSpacer,
|
||||
EuiTab,
|
||||
EuiTabs,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import type { FC } from 'react';
|
||||
import { useEffect, useMemo, useCallback } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ExportJobDependenciesWarningCallout } from './export_job_warning_callout';
|
||||
import type { JobType } from '../../../../../common/types/saved_objects';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
import { JobsExportService } from './jobs_export_service';
|
||||
import type { JobDependencies } from './jobs_export_service';
|
||||
import { toastNotificationServiceProvider } from '../../../services/toast_notification_service';
|
||||
|
||||
const LoadingSpinner: FC = () => (
|
||||
<>
|
||||
<EuiSpacer size="l" />
|
||||
<EuiFlexGroup justifyContent="spaceAround">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="l" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
);
|
||||
|
||||
const SwitchTabsConfirm: FC<{ onCancel: () => void; onConfirm: () => void }> = ({
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}) => (
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate('xpack.ml.importExport.exportFlyout.switchTabsConfirm.title', {
|
||||
defaultMessage: 'Change tabs?',
|
||||
})}
|
||||
onCancel={onCancel}
|
||||
onConfirm={onConfirm}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.switchTabsConfirm.cancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.switchTabsConfirm.confirmButton',
|
||||
{
|
||||
defaultMessage: 'Confirm',
|
||||
}
|
||||
)}
|
||||
defaultFocusedButton="confirm"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.switchTabsConfirm.text"
|
||||
defaultMessage="Changing tabs will clear currently selected jobs"
|
||||
/>
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
);
|
||||
|
||||
export interface ExportJobsFlyoutContentProps {
|
||||
currentTab: JobType;
|
||||
isADEnabled: boolean;
|
||||
isDFAEnabled: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const ExportJobsFlyoutContent = ({
|
||||
currentTab,
|
||||
isADEnabled,
|
||||
isDFAEnabled,
|
||||
onClose,
|
||||
}: ExportJobsFlyoutContentProps) => {
|
||||
const {
|
||||
services: {
|
||||
notifications: { toasts },
|
||||
mlServices: { mlUsageCollection, mlApiServices },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const {
|
||||
getJobs,
|
||||
dataFrameAnalytics: { getDataFrameAnalytics },
|
||||
} = mlApiServices;
|
||||
|
||||
const jobsExportService = useMemo(() => new JobsExportService(mlApiServices), [mlApiServices]);
|
||||
const [adJobIds, setAdJobIds] = useState<string[]>([]);
|
||||
const [dfaJobIds, setDfaJobIds] = useState<string[]>([]);
|
||||
const [selectedJobIds, setSelectedJobIds] = useState<string[]>([]);
|
||||
const [selectedJobType, setSelectedJobType] = useState<JobType>(currentTab);
|
||||
const { displayErrorToast, displaySuccessToast } = useMemo(
|
||||
() => toastNotificationServiceProvider(toasts),
|
||||
[toasts]
|
||||
);
|
||||
const [jobDependencies, setJobDependencies] = useState<JobDependencies>([]);
|
||||
const [loadingADJobs, setLoadingADJobs] = useState(true);
|
||||
const [loadingDFAJobs, setLoadingDFAJobs] = useState(true);
|
||||
|
||||
const [exporting, setExporting] = useState(false);
|
||||
const [switchTabConfirmVisible, setSwitchTabConfirmVisible] = useState(false);
|
||||
const [switchTabNextTab, setSwitchTabNextTab] = useState<JobType>(currentTab);
|
||||
const [selectedJobDependencies, setSelectedJobDependencies] = useState<JobDependencies>([]);
|
||||
|
||||
async function onExport() {
|
||||
setExporting(true);
|
||||
const title = i18n.translate('xpack.ml.importExport.exportFlyout.exportDownloading', {
|
||||
defaultMessage: 'Your file is downloading in the background',
|
||||
});
|
||||
|
||||
displaySuccessToast(title);
|
||||
|
||||
try {
|
||||
if (selectedJobType === 'anomaly-detector') {
|
||||
await jobsExportService.exportAnomalyDetectionJobs(selectedJobIds);
|
||||
} else {
|
||||
await jobsExportService.exportDataframeAnalyticsJobs(selectedJobIds);
|
||||
}
|
||||
|
||||
mlUsageCollection.count(
|
||||
selectedJobType === 'anomaly-detector'
|
||||
? 'exported_anomaly_detector_jobs'
|
||||
: 'exported_data_frame_analytics_jobs',
|
||||
selectedJobIds.length
|
||||
);
|
||||
|
||||
setExporting(false);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
const errorTitle = i18n.translate('xpack.ml.importExport.exportFlyout.exportError', {
|
||||
defaultMessage: 'Could not export selected jobs',
|
||||
});
|
||||
displayErrorToast(error, errorTitle);
|
||||
setExporting(false);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleSelectedJob(checked: boolean, id: string) {
|
||||
if (checked) {
|
||||
setSelectedJobIds([...selectedJobIds, id]);
|
||||
} else {
|
||||
setSelectedJobIds(selectedJobIds.filter((id2) => id2 !== id));
|
||||
}
|
||||
}
|
||||
|
||||
function switchTab(jobType: JobType) {
|
||||
setSwitchTabConfirmVisible(false);
|
||||
setSelectedJobIds([]);
|
||||
setSelectedJobType(jobType);
|
||||
}
|
||||
|
||||
function onSelectAll() {
|
||||
const ids = selectedJobType === 'anomaly-detector' ? adJobIds : dfaJobIds;
|
||||
if (selectedJobIds.length === ids.length) {
|
||||
setSelectedJobIds([]);
|
||||
} else {
|
||||
setSelectedJobIds([...ids]);
|
||||
}
|
||||
}
|
||||
|
||||
const attemptTabSwitch = useCallback(
|
||||
(jobType: JobType) => {
|
||||
if (jobType === selectedJobType) {
|
||||
return;
|
||||
}
|
||||
// if the user has already selected some jobs, open a confirm modal
|
||||
// rather than changing tabs
|
||||
if (selectedJobIds.length > 0) {
|
||||
setSwitchTabNextTab(jobType);
|
||||
setSwitchTabConfirmVisible(true);
|
||||
return;
|
||||
}
|
||||
|
||||
switchTab(jobType);
|
||||
},
|
||||
|
||||
[selectedJobIds, selectedJobType]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedJobDependencies(
|
||||
jobDependencies.filter(({ jobId }) => selectedJobIds.includes(jobId))
|
||||
);
|
||||
}, [jobDependencies, selectedJobIds]);
|
||||
|
||||
useEffect(
|
||||
function onFlyoutChange() {
|
||||
setLoadingADJobs(true);
|
||||
setLoadingDFAJobs(true);
|
||||
setExporting(false);
|
||||
setSwitchTabConfirmVisible(false);
|
||||
setAdJobIds([]);
|
||||
setSelectedJobIds([]);
|
||||
setSelectedJobType(currentTab);
|
||||
|
||||
if (isADEnabled) {
|
||||
getJobs()
|
||||
.then(({ jobs }) => {
|
||||
setLoadingADJobs(false);
|
||||
setAdJobIds(jobs.map((j) => j.job_id));
|
||||
|
||||
jobsExportService
|
||||
.getJobDependencies(jobs)
|
||||
.then((jobDeps) => {
|
||||
setJobDependencies(jobDeps);
|
||||
setLoadingADJobs(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorTitle = i18n.translate(
|
||||
'xpack.ml.importExport.exportFlyout.calendarsError',
|
||||
{
|
||||
defaultMessage: 'Could not load calendars',
|
||||
}
|
||||
);
|
||||
displayErrorToast(error, errorTitle);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorTitle = i18n.translate('xpack.ml.importExport.exportFlyout.adJobsError', {
|
||||
defaultMessage: 'Could not load anomaly detection jobs',
|
||||
});
|
||||
displayErrorToast(error, errorTitle);
|
||||
});
|
||||
}
|
||||
|
||||
if (isDFAEnabled) {
|
||||
getDataFrameAnalytics()
|
||||
.then(({ data_frame_analytics: dataFrameAnalytics }) => {
|
||||
setLoadingDFAJobs(false);
|
||||
setDfaJobIds(dataFrameAnalytics.map((j) => j.id));
|
||||
})
|
||||
.catch((error) => {
|
||||
const errorTitle = i18n.translate('xpack.ml.importExport.exportFlyout.dfaJobsError', {
|
||||
defaultMessage: 'Could not load data frame analytics jobs',
|
||||
});
|
||||
displayErrorToast(error, errorTitle);
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
currentTab,
|
||||
displayErrorToast,
|
||||
getDataFrameAnalytics,
|
||||
getJobs,
|
||||
isADEnabled,
|
||||
isDFAEnabled,
|
||||
jobsExportService,
|
||||
]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlyout
|
||||
onClose={onClose}
|
||||
hideCloseButton
|
||||
size="s"
|
||||
data-test-subj="mlJobMgmtExportJobsFlyout"
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.flyoutHeader"
|
||||
defaultMessage="Export jobs"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ExportJobDependenciesWarningCallout jobs={selectedJobDependencies} />
|
||||
<EuiTabs size="s">
|
||||
{isADEnabled === true ? (
|
||||
<EuiTab
|
||||
isSelected={selectedJobType === 'anomaly-detector'}
|
||||
onClick={() => attemptTabSwitch('anomaly-detector')}
|
||||
disabled={exporting}
|
||||
data-test-subj="mlJobMgmtExportJobsADTab"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adTab"
|
||||
defaultMessage="Anomaly detection"
|
||||
/>
|
||||
</EuiTab>
|
||||
) : null}
|
||||
{isDFAEnabled === true ? (
|
||||
<EuiTab
|
||||
isSelected={selectedJobType === 'data-frame-analytics'}
|
||||
onClick={() => attemptTabSwitch('data-frame-analytics')}
|
||||
disabled={exporting}
|
||||
data-test-subj="mlJobMgmtExportJobsDFATab"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaTab"
|
||||
defaultMessage="Analytics"
|
||||
/>
|
||||
</EuiTab>
|
||||
) : null}
|
||||
</EuiTabs>
|
||||
<EuiSpacer size="s" />
|
||||
<>
|
||||
{isADEnabled === true && selectedJobType === 'anomaly-detector' && (
|
||||
<>
|
||||
{loadingADJobs === true ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
onClick={onSelectAll}
|
||||
data-test-subj="mlJobMgmtExportJobsSelectAllButton"
|
||||
>
|
||||
{selectedJobIds.length === adJobIds.length ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adDeselectAllButton"
|
||||
defaultMessage="Deselect all"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.adSelectAllButton"
|
||||
defaultMessage="Select all"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
|
||||
<div data-test-subj="mlJobMgmtExportJobsADJobList">
|
||||
{adJobIds.map((id) => (
|
||||
<div key={id}>
|
||||
<EuiCheckbox
|
||||
id={id}
|
||||
label={id}
|
||||
checked={selectedJobIds.includes(id)}
|
||||
onChange={(e) => toggleSelectedJob(e.target.checked, id)}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isDFAEnabled === true && selectedJobType === 'data-frame-analytics' && (
|
||||
<>
|
||||
{loadingDFAJobs === true ? (
|
||||
<LoadingSpinner />
|
||||
) : (
|
||||
<>
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
onClick={onSelectAll}
|
||||
data-test-subj="mlJobMgmtExportJobsSelectAllButton"
|
||||
>
|
||||
{selectedJobIds.length === dfaJobIds.length ? (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaDeselectAllButton"
|
||||
defaultMessage="Deselect all"
|
||||
/>
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.dfaSelectAllButton"
|
||||
defaultMessage="Select all"
|
||||
/>
|
||||
)}
|
||||
</EuiButtonEmpty>
|
||||
|
||||
<EuiSpacer size="xs" />
|
||||
<div data-test-subj="mlJobMgmtExportJobsDFAJobList">
|
||||
{dfaJobIds.map((id) => (
|
||||
<div key={id}>
|
||||
<EuiCheckbox
|
||||
id={id}
|
||||
label={id}
|
||||
checked={selectedJobIds.includes(id)}
|
||||
onChange={(e) => toggleSelectedJob(e.target.checked, id)}
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
</EuiFlyoutBody>
|
||||
<EuiFlyoutFooter>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="cross" onClick={onClose.bind(null)} flush="left">
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.closeButton"
|
||||
defaultMessage="Close"
|
||||
/>
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
disabled={selectedJobIds.length === 0 || exporting === true}
|
||||
onClick={onExport}
|
||||
fill
|
||||
data-test-subj="mlJobMgmtExportExportButton"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.importExport.exportFlyout.exportButton"
|
||||
defaultMessage="Export"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlyoutFooter>
|
||||
</EuiFlyout>
|
||||
|
||||
{switchTabConfirmVisible === true ? (
|
||||
<SwitchTabsConfirm
|
||||
onCancel={setSwitchTabConfirmVisible.bind(null, false)}
|
||||
onConfirm={() => switchTab(switchTabNextTab)}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -5,4 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { ExportJobsFlyout } from './export_jobs_flyout';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
|
||||
export const ExportJobsFlyout = dynamic(async () => ({
|
||||
default: (await import('./export_jobs_flyout')).ExportJobsFlyout,
|
||||
}));
|
||||
|
|
|
@ -43,9 +43,10 @@ import { useValidateIds } from './validate';
|
|||
import type { ImportedAdJob, JobIdObject, SkippedJobs } from './jobs_import_service';
|
||||
import { useEnabledFeatures } from '../../../contexts/ml';
|
||||
|
||||
interface Props {
|
||||
export interface Props {
|
||||
isDisabled: boolean;
|
||||
}
|
||||
|
||||
export const ImportJobsFlyout: FC<Props> = ({ isDisabled }) => {
|
||||
const {
|
||||
services: {
|
||||
|
|
|
@ -5,4 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { ImportJobsFlyout } from './import_jobs_flyout';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
|
||||
export const ImportJobsFlyout = dynamic(async () => ({
|
||||
default: (await import('./import_jobs_flyout')).ImportJobsFlyout,
|
||||
}));
|
||||
|
|
|
@ -5,4 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { JobSpacesSyncFlyout } from './job_spaces_sync_flyout';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
|
||||
export const JobSpacesSyncFlyout = dynamic(async () => ({
|
||||
default: (await import('./job_spaces_sync_flyout')).JobSpacesSyncFlyout,
|
||||
}));
|
||||
|
|
|
@ -29,9 +29,10 @@ import type { SyncSavedObjectResponse, SyncResult } from '../../../../common/typ
|
|||
import { SyncList } from './sync_list';
|
||||
import { useToastNotificationService } from '../../services/toast_notification_service';
|
||||
|
||||
interface Props {
|
||||
export interface Props {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const JobSpacesSyncFlyout: FC<Props> = ({ onClose }) => {
|
||||
const { displayErrorToast, displaySuccessToast } = useToastNotificationService();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
|
|
@ -21,8 +21,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { MlInferenceState } from '../types';
|
||||
import { PipelineDetailsTitle } from '../../shared';
|
||||
import { PipelineNameAndDescription } from '../../shared';
|
||||
import { PipelineDetailsTitle, PipelineNameAndDescription } from '../../shared';
|
||||
|
||||
interface Props {
|
||||
handlePipelineConfigUpdate: (configUpdate: Partial<MlInferenceState>) => void;
|
||||
|
|
|
@ -28,7 +28,7 @@ import type { DataFrameAnalyticsConfig } from '@kbn/ml-data-frame-analytics-util
|
|||
import { useTrainedModelsApiService } from '../../../../services/ml_api_service/trained_models';
|
||||
import type { GetDataFrameAnalyticsResponse } from '../../../../services/ml_api_service/data_frame_analytics';
|
||||
import { useToastNotificationService } from '../../../../services/toast_notification_service';
|
||||
import { ModelsTableToConfigMapping } from '../../../../model_management';
|
||||
import { ModelsTableToConfigMapping } from '../../../../model_management/config_mapping';
|
||||
import { useMlApiContext } from '../../../../contexts/kibana';
|
||||
import type { TrainedModelConfigResponse } from '../../../../../../common/types/trained_models';
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ import type { TypedLensByValueInput } from '@kbn/lens-plugin/public';
|
|||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { css } from '@emotion/react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Y_AXIS_LABEL_WIDTH } from '../swimlane_annotation_container';
|
||||
import { useAnomalyExplorerContext } from '../anomaly_explorer_context';
|
||||
import { useMlKibana } from '../../contexts/kibana';
|
||||
import { Y_AXIS_LABEL_WIDTH } from '../constants';
|
||||
|
||||
export interface AnomalyDetectionAlertsOverviewChart {
|
||||
seriesType?: 'bar_stacked' | 'line';
|
||||
|
|
|
@ -44,9 +44,9 @@ import {
|
|||
import { useMlKibana } from '../../contexts/kibana';
|
||||
import { useAnomalyExplorerContext } from '../anomaly_explorer_context';
|
||||
import type { AppStateSelectedCells, SwimlaneData } from '../explorer_utils';
|
||||
import { Y_AXIS_LABEL_WIDTH } from '../swimlane_annotation_container';
|
||||
import { CELL_HEIGHT } from '../swimlane_container';
|
||||
import { statusNameMap } from './const';
|
||||
import { Y_AXIS_LABEL_WIDTH } from '../constants';
|
||||
|
||||
export interface SwimLaneWrapperProps {
|
||||
selection?: AppStateSelectedCells | null;
|
||||
|
|
|
@ -10,6 +10,7 @@ import d3 from 'd3';
|
|||
import { scaleTime } from 'd3-scale';
|
||||
import { type ChartTooltipService, type TooltipData } from '../components/chart_tooltip';
|
||||
import { useCurrentThemeVars } from '../contexts/kibana';
|
||||
import { Y_AXIS_LABEL_PADDING, Y_AXIS_LABEL_WIDTH } from './constants';
|
||||
|
||||
export interface AnnotationTimelineProps<T extends { timestamp: number; end_timestamp?: number }> {
|
||||
label: string;
|
||||
|
@ -23,8 +24,6 @@ export interface AnnotationTimelineProps<T extends { timestamp: number; end_time
|
|||
chartWidth: number;
|
||||
}
|
||||
|
||||
export const Y_AXIS_LABEL_WIDTH = 170;
|
||||
export const Y_AXIS_LABEL_PADDING = 8;
|
||||
const ANNOTATION_CONTAINER_HEIGHT = 12;
|
||||
const ANNOTATION_MIN_WIDTH = 8;
|
||||
|
||||
|
|
|
@ -61,11 +61,12 @@ import { NoOverallData } from './components/no_overall_data';
|
|||
import { SeverityControl } from '../components/severity_control';
|
||||
import { AnomalyTimelineHelpPopover } from './anomaly_timeline_help_popover';
|
||||
import { MlTooltipComponent } from '../components/chart_tooltip';
|
||||
import { SwimlaneAnnotationContainer, Y_AXIS_LABEL_WIDTH } from './swimlane_annotation_container';
|
||||
import { SwimlaneAnnotationContainer } from './swimlane_annotation_container';
|
||||
import { AnomalyTimelineService } from '../services/anomaly_timeline_service';
|
||||
import { useAnomalyExplorerContext } from './anomaly_explorer_context';
|
||||
import { getTimeBoundsFromSelection } from './hooks/use_selected_cells';
|
||||
import { SwimLaneWrapper } from './alerts';
|
||||
import { Y_AXIS_LABEL_WIDTH } from './constants';
|
||||
|
||||
function mapSwimlaneOptionsToEuiOptions(options: string[]) {
|
||||
return options.map((option) => ({
|
||||
|
|
10
x-pack/plugins/ml/public/application/explorer/constants.ts
Normal file
10
x-pack/plugins/ml/public/application/explorer/constants.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export const Y_AXIS_LABEL_WIDTH = 170;
|
||||
export const Y_AXIS_LABEL_PADDING = 8;
|
||||
export const SWIM_LANE_LABEL_WIDTH = Y_AXIS_LABEL_WIDTH + 2 * Y_AXIS_LABEL_PADDING;
|
|
@ -35,8 +35,8 @@ import type { TimefilterContract } from '@kbn/data-plugin/public';
|
|||
import { useStorage } from '@kbn/ml-local-storage';
|
||||
import { isDefined } from '@kbn/ml-is-defined';
|
||||
import type { TimeBuckets } from '@kbn/ml-time-buckets';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { HelpPopover } from '../components/help_popover';
|
||||
import { AnnotationFlyout } from '../components/annotations/annotation_flyout';
|
||||
// @ts-ignore
|
||||
import { AnnotationsTable } from '../components/annotations/annotations_table';
|
||||
import { ExplorerNoJobsSelected, ExplorerNoResultsFound } from './components';
|
||||
|
@ -66,14 +66,9 @@ import {
|
|||
import { AnomalyTimeline } from './anomaly_timeline';
|
||||
import type { FilterAction } from './explorer_constants';
|
||||
import { FILTER_ACTION } from './explorer_constants';
|
||||
// Explorer Charts
|
||||
// @ts-ignore
|
||||
import { ExplorerChartsContainer } from './explorer_charts/explorer_charts_container';
|
||||
// Anomalies Table
|
||||
// @ts-ignore
|
||||
import { AnomaliesTable } from '../components/anomalies_table/anomalies_table';
|
||||
// Anomalies Map
|
||||
import { AnomaliesMap } from './anomalies_map';
|
||||
import { ANOMALY_DETECTION_DEFAULT_TIME_RANGE } from '../../../common/constants/settings';
|
||||
import { AnomalyContextMenu } from './anomaly_context_menu';
|
||||
import type { JobSelectorProps } from '../components/job_selector/job_selector';
|
||||
|
@ -85,6 +80,18 @@ import { ML_ANOMALY_EXPLORER_PANELS } from '../../../common/types/storage';
|
|||
import { AlertsPanel } from './alerts';
|
||||
import { useMlIndexUtils } from '../util/index_service';
|
||||
|
||||
const AnnotationFlyout = dynamic(async () => ({
|
||||
default: (await import('../components/annotations/annotation_flyout')).AnnotationFlyout,
|
||||
}));
|
||||
|
||||
const AnomaliesMap = dynamic(async () => ({
|
||||
default: (await import('./anomalies_map')).AnomaliesMap,
|
||||
}));
|
||||
|
||||
const ExplorerChartsContainer = dynamic(async () => ({
|
||||
default: (await import('./explorer_charts/explorer_charts_container')).ExplorerChartsContainer,
|
||||
}));
|
||||
|
||||
interface ExplorerPageProps {
|
||||
jobSelectorProps: JobSelectorProps;
|
||||
noInfluencersConfigured?: boolean;
|
||||
|
|
|
@ -14,9 +14,8 @@ import moment from 'moment';
|
|||
import { useCurrentThemeVars } from '../contexts/kibana';
|
||||
import type { Annotation, AnnotationsTable } from '../../../common/types/annotations';
|
||||
import type { ChartTooltipService } from '../components/chart_tooltip';
|
||||
import { Y_AXIS_LABEL_PADDING, Y_AXIS_LABEL_WIDTH } from './constants';
|
||||
|
||||
export const Y_AXIS_LABEL_WIDTH = 170;
|
||||
export const Y_AXIS_LABEL_PADDING = 8;
|
||||
const ANNOTATION_CONTAINER_HEIGHT = 12;
|
||||
const ANNOTATION_MIN_WIDTH = 8;
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ import { mlEscape } from '../util/string_utils';
|
|||
import { FormattedTooltip } from '../components/chart_tooltip/chart_tooltip';
|
||||
import './_explorer.scss';
|
||||
import { EMPTY_FIELD_VALUE_LABEL } from '../timeseriesexplorer/components/entity_control/entity_control';
|
||||
import { Y_AXIS_LABEL_PADDING, Y_AXIS_LABEL_WIDTH } from './swimlane_annotation_container';
|
||||
import { SWIM_LANE_LABEL_WIDTH, Y_AXIS_LABEL_PADDING } from './constants';
|
||||
import { useCurrentThemeVars, useMlKibana } from '../contexts/kibana';
|
||||
|
||||
declare global {
|
||||
|
@ -82,8 +82,6 @@ export const CELL_HEIGHT = 30;
|
|||
const LEGEND_HEIGHT = 34;
|
||||
const X_AXIS_HEIGHT = 24;
|
||||
|
||||
export const SWIM_LANE_LABEL_WIDTH = Y_AXIS_LABEL_WIDTH + 2 * Y_AXIS_LABEL_PADDING;
|
||||
|
||||
export function isViewBySwimLaneData(arg: any): arg is ViewBySwimLaneData {
|
||||
return arg && arg.hasOwnProperty('cardinality');
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { ML_DATA_PREVIEW_COUNT } from '../../../../../../common/util/job_utils';
|
||||
import { useMlApiContext } from '../../../../contexts/kibana';
|
||||
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
|
||||
import type { CombinedJob } from '../../../../../shared';
|
||||
import { MLJobEditor } from '../ml_job_editor';
|
||||
import type { CombinedJob } from '../../../../../../common/types/anomaly_detection_jobs';
|
||||
|
||||
interface Props {
|
||||
job: CombinedJob;
|
||||
|
|
|
@ -28,6 +28,7 @@ import { RedirectAppLinks } from '@kbn/shared-ux-link-redirect-app';
|
|||
import type { SharePluginStart } from '@kbn/share-plugin/public';
|
||||
import type { SpacesContextProps, SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { getMlGlobalServices } from '../../../../util/get_services';
|
||||
import { EnabledFeaturesContextProvider } from '../../../../contexts/ml';
|
||||
import { type MlFeatures, PLUGIN_ID } from '../../../../../../common/constants/app';
|
||||
|
||||
|
@ -36,7 +37,6 @@ import { checkGetManagementMlJobsResolver } from '../../../../capabilities/check
|
|||
import { AccessDeniedPage } from '../access_denied_page';
|
||||
import { InsufficientLicensePage } from '../insufficient_license_page';
|
||||
import { JobSpacesSyncFlyout } from '../../../../components/job_spaces_sync';
|
||||
import { getMlGlobalServices } from '../../../../app';
|
||||
import { ExportJobsFlyout, ImportJobsFlyout } from '../../../../components/import_export_jobs';
|
||||
import type { MlSavedObjectType } from '../../../../../../common/types/saved_objects';
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import type { EuiBasicTableColumn } from '@elastic/eui';
|
||||
import { TrainedModelLink } from '../../../../../model_management';
|
||||
import { TrainedModelLink } from '../../../../../model_management/model_link';
|
||||
import type { MlSavedObjectType } from '../../../../../../../common/types/saved_objects';
|
||||
import type {
|
||||
AnalyticsManagementItems,
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from './models_list';
|
||||
|
||||
export const ModelsTableToConfigMapping = {
|
||||
id: 'model_id',
|
||||
description: 'description',
|
||||
|
@ -14,5 +12,3 @@ export const ModelsTableToConfigMapping = {
|
|||
type: 'type',
|
||||
modelType: 'model_type',
|
||||
} as const;
|
||||
|
||||
export { TrainedModelLink } from './model_link';
|
|
@ -27,8 +27,8 @@ import { FIELD_FORMAT_IDS } from '@kbn/field-formats-plugin/common';
|
|||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import { isDefined } from '@kbn/ml-is-defined';
|
||||
import { TRAINED_MODEL_TYPE } from '@kbn/ml-trained-models-utils';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { InferenceApi } from './inference_api_tab';
|
||||
import { JobMap } from '../data_frame_analytics/pages/job_map';
|
||||
import type { ModelItemFull } from './models_list';
|
||||
import { ModelPipelines } from './pipelines';
|
||||
import { AllocatedModels } from '../memory_usage/nodes_overview/allocated_models';
|
||||
|
@ -40,6 +40,10 @@ interface ExpandedRowProps {
|
|||
item: ModelItemFull;
|
||||
}
|
||||
|
||||
const JobMap = dynamic(async () => ({
|
||||
default: (await import('../data_frame_analytics/pages/job_map')).JobMap,
|
||||
}));
|
||||
|
||||
const useBadgeFormatter = () => {
|
||||
const xs = useEuiPaddingSize('xs');
|
||||
|
||||
|
|
|
@ -46,12 +46,12 @@ import {
|
|||
} from '@kbn/ml-trained-models-utils';
|
||||
import { isDefined } from '@kbn/ml-is-defined';
|
||||
import { useStorage } from '@kbn/ml-local-storage';
|
||||
import { AddModelFlyout } from './add_model_flyout';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { getModelStateColor } from './get_model_state_color';
|
||||
import { ML_ELSER_CALLOUT_DISMISSED } from '../../../common/types/storage';
|
||||
import { TechnicalPreviewBadge } from '../components/technical_preview_badge';
|
||||
import { useModelActions } from './model_actions';
|
||||
import { ModelsTableToConfigMapping } from '.';
|
||||
import { ModelsTableToConfigMapping } from './config_mapping';
|
||||
import type { ModelsBarStats } from '../components/stats_bar';
|
||||
import { StatsBar } from '../components/stats_bar';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
|
@ -65,7 +65,6 @@ import type {
|
|||
import { DeleteModelsModal } from './delete_models_modal';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import type { ListingPageUrlState } from '../../../common/types/common';
|
||||
import { ExpandedRow } from './expanded_row';
|
||||
import { useTableSettings } from '../data_frame_analytics/pages/analytics_management/components/analytics_list/use_table_settings';
|
||||
import { useToastNotificationService } from '../services/toast_notification_service';
|
||||
import { useFieldFormatter } from '../contexts/kibana/use_field_formatter';
|
||||
|
@ -104,6 +103,14 @@ interface PageUrlState {
|
|||
pageUrlState: ListingPageUrlState;
|
||||
}
|
||||
|
||||
const ExpandedRow = dynamic(async () => ({
|
||||
default: (await import('./expanded_row')).ExpandedRow,
|
||||
}));
|
||||
|
||||
const AddModelFlyout = dynamic(async () => ({
|
||||
default: (await import('./add_model_flyout')).AddModelFlyout,
|
||||
}));
|
||||
|
||||
const modelIdColumnName = i18n.translate('xpack.ml.trainedModels.modelsList.modelIdHeader', {
|
||||
defaultMessage: 'ID',
|
||||
});
|
||||
|
|
|
@ -9,6 +9,7 @@ import { CHANGE_POINT_DETECTION_ENABLED } from '@kbn/aiops-change-point-detectio
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
@ -16,7 +17,10 @@ import type { MlRoute } from '../..';
|
|||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { ChangePointDetectionPage as Page } from '../../../aiops';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../aiops')).ChangePointDetectionPage,
|
||||
}));
|
||||
|
||||
export const changePointDetectionRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { LogCategorizationPage as Page } from '../../../aiops/log_categorization';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../aiops/log_categorization')).LogCategorizationPage,
|
||||
}));
|
||||
|
||||
export const logCategorizationRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -9,15 +9,19 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { useLocation, Redirect } from 'react-router-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { LogRateAnalysisPage as Page } from '../../../aiops/log_rate_analysis';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../aiops/log_rate_analysis')).LogRateAnalysisPage,
|
||||
}));
|
||||
|
||||
export const logRateAnalysisRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { parse } from 'query-string';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
@ -17,13 +18,15 @@ import type { MlRoute, PageProps } from '../../router';
|
|||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_creation';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import {
|
||||
DATA_FRAME_ANALYTICS,
|
||||
loadNewJobCapabilities,
|
||||
} from '../../../services/new_job_capabilities/load_new_job_capabilities';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../data_frame_analytics/pages/analytics_creation')).Page,
|
||||
}));
|
||||
export const analyticsJobsCreationRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -10,15 +10,19 @@ import React from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import type { DataFrameAnalysisConfigType } from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_exploration';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../data_frame_analytics/pages/analytics_exploration')).Page,
|
||||
}));
|
||||
|
||||
export const analyticsJobExplorationRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_management';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../data_frame_analytics/pages/analytics_management')).Page,
|
||||
}));
|
||||
|
||||
export const analyticsJobsListRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/job_map/page';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../data_frame_analytics/pages/job_map/page')).Page,
|
||||
}));
|
||||
|
||||
export const analyticsMapRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/source_selection';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../data_frame_analytics/pages/source_selection')).Page,
|
||||
}));
|
||||
|
||||
export const analyticsSourceSelectionRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DataDriftPage } from '../../../datavisualizer/data_drift/data_drift_page';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
@ -23,6 +23,10 @@ import {
|
|||
} from '../../breadcrumbs';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
|
||||
const DataDriftPage = dynamic(async () => ({
|
||||
default: (await import('../../../datavisualizer/data_drift/data_drift_page')).DataDriftPage,
|
||||
}));
|
||||
|
||||
export const dataDriftRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,14 +8,18 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { DatavisualizerSelector } from '../../../datavisualizer';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const DatavisualizerSelector = dynamic(async () => ({
|
||||
default: (await import('../../../datavisualizer')).DatavisualizerSelector,
|
||||
}));
|
||||
|
||||
export const selectorRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,15 +8,19 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { FileDataVisualizerPage } from '../../../datavisualizer/file_based';
|
||||
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const FileDataVisualizerPage = dynamic(async () => ({
|
||||
default: (await import('../../../datavisualizer/file_based')).FileDataVisualizerPage,
|
||||
}));
|
||||
|
||||
export const fileBasedRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -8,15 +8,20 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { IndexDataVisualizerPage as Page } from '../../../datavisualizer/index_based/index_data_visualizer';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../datavisualizer/index_based/index_data_visualizer'))
|
||||
.IndexDataVisualizerPage,
|
||||
}));
|
||||
|
||||
export const indexBasedRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* 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 type { FC } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
|
||||
import type { MlRoute, PageProps } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import { getDateFormatTz } from '../../../explorer/explorer_utils';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { MlAnnotationUpdatesContext } from '../../../contexts/ml/ml_annotation_updates_context';
|
||||
import { AnnotationUpdatesService } from '../../../services/annotations_service';
|
||||
import { AnomalyExplorerContextProvider } from '../../../explorer/anomaly_explorer_context';
|
||||
|
||||
const ExplorerUrlStateManager = dynamic(async () => ({
|
||||
default: (await import('./state_manager')).ExplorerUrlStateManager,
|
||||
}));
|
||||
|
||||
export const explorerRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
): MlRoute => ({
|
||||
id: 'explorer',
|
||||
path: createPath(ML_PAGES.ANOMALY_EXPLORER),
|
||||
title: i18n.translate('xpack.ml.anomalyDetection.anomalyExplorer.docTitle', {
|
||||
defaultMessage: 'Anomaly Explorer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.anomalyDetection.anomalyExplorerLabel', {
|
||||
defaultMessage: 'Anomaly Explorer',
|
||||
}),
|
||||
},
|
||||
],
|
||||
enableDatePicker: true,
|
||||
'data-test-subj': 'mlPageAnomalyExplorer',
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = () => {
|
||||
const {
|
||||
services: {
|
||||
mlServices: { mlApiServices },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const { context, results } = useRouteResolver('full', ['canGetJobs'], {
|
||||
...basicResolvers(),
|
||||
jobs: mlJobService.loadJobsWrapper,
|
||||
jobsWithTimeRange: () => mlApiServices.jobs.jobsWithTimerange(getDateFormatTz()),
|
||||
});
|
||||
|
||||
const annotationUpdatesService = useMemo(() => new AnnotationUpdatesService(), []);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<MlAnnotationUpdatesContext.Provider value={annotationUpdatesService}>
|
||||
<AnomalyExplorerContextProvider>
|
||||
{results ? (
|
||||
<ExplorerUrlStateManager jobsWithTimeRange={results.jobsWithTimeRange.jobs} />
|
||||
) : null}
|
||||
</AnomalyExplorerContextProvider>
|
||||
</MlAnnotationUpdatesContext.Provider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { explorerRouteFactory } from './explorer';
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { FC } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
@ -17,93 +17,29 @@ import { useUrlState } from '@kbn/ml-url-state';
|
|||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { ML_JOB_ID } from '@kbn/ml-anomaly-utils';
|
||||
import { useTimeBuckets } from '@kbn/ml-time-buckets';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import type { NavigateToPath } from '../../contexts/kibana';
|
||||
import { useMlKibana } from '../../contexts/kibana';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
|
||||
import type { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs';
|
||||
import type { MlJobWithTimeRange } from '../../../../../common/types/anomaly_detection_jobs';
|
||||
import { useRefresh } from '../../use_refresh';
|
||||
import { Explorer } from '../../../explorer';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { useExplorerData } from '../../../explorer/actions';
|
||||
import { useJobSelection } from '../../../components/job_selector/use_job_selection';
|
||||
import { useTableInterval } from '../../../components/controls/select_interval';
|
||||
import { useTableSeverity } from '../../../components/controls/select_severity';
|
||||
import { MlPageHeader } from '../../../components/page_header';
|
||||
import { PageTitle } from '../../../components/page_title';
|
||||
import { AnomalyResultsViewSelector } from '../../../components/anomaly_results_view_selector';
|
||||
import { AnomalyDetectionEmptyState } from '../../../jobs/jobs_list/components/anomaly_detection_empty_state';
|
||||
import { useAnomalyExplorerContext } from '../../../explorer/anomaly_explorer_context';
|
||||
|
||||
import type { MlRoute, PageProps } from '../router';
|
||||
import { createPath, PageLoader } from '../router';
|
||||
import { useRefresh } from '../use_refresh';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { Explorer } from '../../explorer';
|
||||
import { mlJobService } from '../../services/job_service';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { useExplorerData } from '../../explorer/actions';
|
||||
import { getDateFormatTz } from '../../explorer/explorer_utils';
|
||||
import { useJobSelection } from '../../components/job_selector/use_job_selection';
|
||||
import { useTableInterval } from '../../components/controls/select_interval';
|
||||
import { useTableSeverity } from '../../components/controls/select_severity';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { MlAnnotationUpdatesContext } from '../../contexts/ml/ml_annotation_updates_context';
|
||||
import { AnnotationUpdatesService } from '../../services/annotations_service';
|
||||
import { MlPageHeader } from '../../components/page_header';
|
||||
import { PageTitle } from '../../components/page_title';
|
||||
import { AnomalyResultsViewSelector } from '../../components/anomaly_results_view_selector';
|
||||
import { AnomalyDetectionEmptyState } from '../../jobs/jobs_list/components/anomaly_detection_empty_state';
|
||||
import {
|
||||
AnomalyExplorerContextProvider,
|
||||
useAnomalyExplorerContext,
|
||||
} from '../../explorer/anomaly_explorer_context';
|
||||
|
||||
export const explorerRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
): MlRoute => ({
|
||||
id: 'explorer',
|
||||
path: createPath(ML_PAGES.ANOMALY_EXPLORER),
|
||||
title: i18n.translate('xpack.ml.anomalyDetection.anomalyExplorer.docTitle', {
|
||||
defaultMessage: 'Anomaly Explorer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.anomalyDetection.anomalyExplorerLabel', {
|
||||
defaultMessage: 'Anomaly Explorer',
|
||||
}),
|
||||
},
|
||||
],
|
||||
enableDatePicker: true,
|
||||
'data-test-subj': 'mlPageAnomalyExplorer',
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = () => {
|
||||
const {
|
||||
services: {
|
||||
mlServices: { mlApiServices },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const { context, results } = useRouteResolver('full', ['canGetJobs'], {
|
||||
...basicResolvers(),
|
||||
jobs: mlJobService.loadJobsWrapper,
|
||||
jobsWithTimeRange: () => mlApiServices.jobs.jobsWithTimerange(getDateFormatTz()),
|
||||
});
|
||||
|
||||
const annotationUpdatesService = useMemo(() => new AnnotationUpdatesService(), []);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<MlAnnotationUpdatesContext.Provider value={annotationUpdatesService}>
|
||||
<AnomalyExplorerContextProvider>
|
||||
{results ? (
|
||||
<ExplorerUrlStateManager jobsWithTimeRange={results.jobsWithTimeRange.jobs} />
|
||||
) : null}
|
||||
</AnomalyExplorerContextProvider>
|
||||
</MlAnnotationUpdatesContext.Provider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
||||
interface ExplorerUrlStateManagerProps {
|
||||
export interface ExplorerUrlStateManagerProps {
|
||||
jobsWithTimeRange: MlJobWithTimeRange[];
|
||||
}
|
||||
|
||||
const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({ jobsWithTimeRange }) => {
|
||||
export const ExplorerUrlStateManager: FC<ExplorerUrlStateManagerProps> = ({
|
||||
jobsWithTimeRange,
|
||||
}) => {
|
||||
const {
|
||||
services: { cases, presentationUtil, uiSettings },
|
||||
} = useMlKibana();
|
|
@ -14,18 +14,22 @@ import {
|
|||
useRefreshIntervalUpdates,
|
||||
useTimefilter,
|
||||
} from '@kbn/ml-date-picker';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import type { NavigateToPath } from '../../contexts/kibana';
|
||||
import { DEFAULT_REFRESH_INTERVAL_MS } from '../../../../common/constants/jobs_list';
|
||||
import type { MlRoute } from '../router';
|
||||
import { createPath, PageLoader } from '../router';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { JobsPage } from '../../jobs/jobs_list';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { AnnotationUpdatesService } from '../../services/annotations_service';
|
||||
import { MlAnnotationUpdatesContext } from '../../contexts/ml/ml_annotation_updates_context';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
|
||||
const JobsPage = dynamic(async () => ({
|
||||
default: (await import('../../jobs/jobs_list')).JobsPage,
|
||||
}));
|
||||
|
||||
export const jobListRouteFactory = (navigateToPath: NavigateToPath, basePath: string): MlRoute => ({
|
||||
id: 'anomaly_detection',
|
||||
title: i18n.translate('xpack.ml.anomalyDetection.jobs.docTitle', {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import type { NavigateToPath } from '../../contexts/kibana';
|
||||
|
@ -15,7 +16,10 @@ import type { MlRoute } from '../router';
|
|||
import { createPath, PageLoader } from '../router';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { MemoryUsagePage } from '../../memory_usage';
|
||||
|
||||
const MemoryUsagePage = dynamic(async () => ({
|
||||
default: (await import('../../memory_usage')).MemoryUsagePage,
|
||||
}));
|
||||
|
||||
export const nodesListRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import { Redirect } from 'react-router-dom';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
|
@ -17,7 +18,7 @@ import type { MlRoute, PageProps } from '../../router';
|
|||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page, preConfiguredJobRedirect } from '../../../jobs/new_job/pages/index_or_search';
|
||||
import { preConfiguredJobRedirect } from '../../../jobs/new_job/pages/index_or_search';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { NavigateToPageButton } from '../../components/navigate_to_page_button';
|
||||
|
||||
|
@ -26,6 +27,10 @@ enum MODE {
|
|||
DATAVISUALIZER,
|
||||
}
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../jobs/new_job/pages/index_or_search')).Page,
|
||||
}));
|
||||
|
||||
interface IndexOrSearchPageProps extends PageProps {
|
||||
nextStepPath: string;
|
||||
mode: MODE;
|
||||
|
|
|
@ -8,16 +8,20 @@
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { Page } from '../../../jobs/new_job/pages/job_type';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../jobs/new_job/pages/job_type')).Page,
|
||||
}));
|
||||
|
||||
export const jobTypeRouteFactory = (navigateToPath: NavigateToPath, basePath: string): MlRoute => ({
|
||||
path: createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE),
|
||||
render: () => <PageWrapper />,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { parse } from 'query-string';
|
|||
import type { FC } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
@ -16,12 +17,15 @@ import { useMlKibana, useNavigateToPath } from '../../../contexts/kibana';
|
|||
import type { MlRoute, PageProps } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { Page } from '../../../jobs/new_job/recognize';
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { useCreateADLinks } from '../../../components/custom_hooks/use_create_ad_links';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../jobs/new_job/recognize')).Page,
|
||||
}));
|
||||
|
||||
export const recognizeRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -10,6 +10,7 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml/data_source_context';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { useMlKibana } from '../../../contexts/kibana';
|
||||
|
@ -17,7 +18,6 @@ import { basicResolvers } from '../../resolvers';
|
|||
import type { MlRoute, PageProps } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { Page } from '../../../jobs/new_job/pages/new_job';
|
||||
import { JOB_TYPE } from '../../../../../common/constants/new_job';
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import {
|
||||
|
@ -33,6 +33,10 @@ interface WizardPageProps extends PageProps {
|
|||
jobType: JOB_TYPE;
|
||||
}
|
||||
|
||||
const Page = dynamic(async () => ({
|
||||
default: (await import('../../../jobs/new_job/pages/new_job')).Page,
|
||||
}));
|
||||
|
||||
const getBaseBreadcrumbs = (navigateToPath: NavigateToPath, basePath: string) => [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute, PageProps } from '../../router';
|
||||
|
@ -16,9 +17,12 @@ import { createPath, PageLoader } from '../../router';
|
|||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { CalendarsList } from '../../../settings/calendars';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const CalendarsList = dynamic(async () => ({
|
||||
default: (await import('../../../settings/calendars')).CalendarsList,
|
||||
}));
|
||||
|
||||
export const calendarListRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -9,12 +9,12 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute, PageProps } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { NewCalendar } from '../../../settings/calendars';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check';
|
||||
|
@ -28,6 +28,10 @@ interface NewCalendarPageProps extends PageProps {
|
|||
mode: MODE;
|
||||
}
|
||||
|
||||
const NewCalendar = dynamic(async () => ({
|
||||
default: (await import('../../../settings/calendars')).NewCalendar,
|
||||
}));
|
||||
|
||||
export const newCalendarRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
|
@ -16,9 +17,12 @@ import { createPath, PageLoader } from '../../router';
|
|||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { FilterLists } from '../../../settings/filter_lists';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const FilterLists = dynamic(async () => ({
|
||||
default: (await import('../../../settings/filter_lists')).FilterLists,
|
||||
}));
|
||||
|
||||
export const filterListRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -9,10 +9,10 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { EditFilterList } from '../../../settings/filter_lists';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute, PageProps } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
|
@ -28,6 +28,10 @@ interface NewFilterPageProps extends PageProps {
|
|||
mode: MODE;
|
||||
}
|
||||
|
||||
const EditFilterList = dynamic(async () => ({
|
||||
default: (await import('../../../settings/filter_lists')).EditFilterList,
|
||||
}));
|
||||
|
||||
export const newFilterListRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -9,6 +9,7 @@ import type { FC } from 'react';
|
|||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
|
@ -16,9 +17,13 @@ import { createPath, PageLoader } from '../../router';
|
|||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { AnomalyDetectionSettingsContext, Settings } from '../../../settings';
|
||||
import { AnomalyDetectionSettingsContext } from '../../../settings';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
const Settings = dynamic(async () => ({
|
||||
default: (await import('../../../settings')).Settings,
|
||||
}));
|
||||
|
||||
export const settingsRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { timeSeriesExplorerRouteFactory } from './timeseriesexplorer';
|
|
@ -7,100 +7,37 @@
|
|||
|
||||
import { isEqual } from 'lodash';
|
||||
import type { FC } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import usePrevious from 'react-use/lib/usePrevious';
|
||||
import moment from 'moment';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import type { IUiSettingsClient } from '@kbn/core/public';
|
||||
import type { TimeRangeBounds } from '@kbn/ml-time-buckets';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import { getViewableDetectors } from '../../timeseriesexplorer/timeseriesexplorer_utils/get_viewable_detectors';
|
||||
import type { NavigateToPath } from '../../contexts/kibana';
|
||||
import {
|
||||
useMlApiContext,
|
||||
useMlKibana,
|
||||
useNotifications,
|
||||
useUiSettings,
|
||||
} from '../../contexts/kibana';
|
||||
import type { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs';
|
||||
import { isTimeSeriesViewJob } from '../../../../common/util/job_utils';
|
||||
import { TimeSeriesExplorer } from '../../timeseriesexplorer';
|
||||
import { getDateFormatTz } from '../../explorer/explorer_utils';
|
||||
import { mlJobService } from '../../services/job_service';
|
||||
import { useForecastService } from '../../services/forecast_service';
|
||||
import { useTimeSeriesExplorerService } from '../../util/time_series_explorer_service';
|
||||
import { APP_STATE_ACTION } from '../../timeseriesexplorer/timeseriesexplorer_constants';
|
||||
import { validateJobSelection } from '../../timeseriesexplorer/timeseriesexplorer_utils';
|
||||
import { TimeSeriesExplorerPage } from '../../timeseriesexplorer/timeseriesexplorer_page';
|
||||
import { TimeseriesexplorerNoJobsFound } from '../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found';
|
||||
import { useTableInterval } from '../../components/controls/select_interval';
|
||||
import { useTableSeverity } from '../../components/controls/select_severity';
|
||||
import type { MlRoute, PageProps } from '../router';
|
||||
import { createPath, PageLoader } from '../router';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { useToastNotificationService } from '../../services/toast_notification_service';
|
||||
import { AnnotationUpdatesService } from '../../services/annotations_service';
|
||||
import { MlAnnotationUpdatesContext } from '../../contexts/ml/ml_annotation_updates_context';
|
||||
import { useTimeSeriesExplorerUrlState } from '../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state';
|
||||
import type { TimeSeriesExplorerAppState } from '../../../../common/types/locator';
|
||||
import { useJobSelectionFlyout } from '../../contexts/ml/use_job_selection_flyout';
|
||||
import { useRefresh } from '../use_refresh';
|
||||
import { TimeseriesexplorerNoChartData } from '../../timeseriesexplorer/components/timeseriesexplorer_no_chart_data';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
|
||||
export const timeSeriesExplorerRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
): MlRoute => ({
|
||||
id: 'timeseriesexplorer',
|
||||
path: createPath(ML_PAGES.SINGLE_METRIC_VIEWER),
|
||||
title: i18n.translate('xpack.ml.anomalyDetection.singleMetricViewerLabel', {
|
||||
defaultMessage: 'Single Metric Viewer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.anomalyDetection.singleMetricViewerLabel', {
|
||||
defaultMessage: 'Single Metric Viewer',
|
||||
}),
|
||||
},
|
||||
],
|
||||
enableDatePicker: true,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const mlApi = useMlApiContext();
|
||||
const uiSettings = useUiSettings();
|
||||
const { context, results } = useRouteResolver('full', ['canGetJobs'], {
|
||||
...basicResolvers(),
|
||||
jobs: mlJobService.loadJobsWrapper,
|
||||
jobsWithTimeRange: () => mlApi.jobs.jobsWithTimerange(getDateFormatTz()),
|
||||
});
|
||||
|
||||
const annotationUpdatesService = useMemo(() => new AnnotationUpdatesService(), []);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<MlAnnotationUpdatesContext.Provider value={annotationUpdatesService}>
|
||||
{results ? (
|
||||
<TimeSeriesExplorerUrlStateManager
|
||||
config={uiSettings}
|
||||
jobsWithTimeRange={results.jobsWithTimeRange.jobs}
|
||||
/>
|
||||
) : null}
|
||||
</MlAnnotationUpdatesContext.Provider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
import { getViewableDetectors } from '../../../timeseriesexplorer/timeseriesexplorer_utils/get_viewable_detectors';
|
||||
import { useMlKibana, useNotifications } from '../../../contexts/kibana';
|
||||
import type { MlJobWithTimeRange } from '../../../../../common/types/anomaly_detection_jobs';
|
||||
import { isTimeSeriesViewJob } from '../../../../../common/util/job_utils';
|
||||
import { TimeSeriesExplorer } from '../../../timeseriesexplorer';
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import { useForecastService } from '../../../services/forecast_service';
|
||||
import { useTimeSeriesExplorerService } from '../../../util/time_series_explorer_service';
|
||||
import { APP_STATE_ACTION } from '../../../timeseriesexplorer/timeseriesexplorer_constants';
|
||||
import { validateJobSelection } from '../../../timeseriesexplorer/timeseriesexplorer_utils';
|
||||
import { TimeSeriesExplorerPage } from '../../../timeseriesexplorer/timeseriesexplorer_page';
|
||||
import { TimeseriesexplorerNoJobsFound } from '../../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found';
|
||||
import { useTableInterval } from '../../../components/controls/select_interval';
|
||||
import { useTableSeverity } from '../../../components/controls/select_severity';
|
||||
import { useToastNotificationService } from '../../../services/toast_notification_service';
|
||||
import { useTimeSeriesExplorerUrlState } from '../../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state';
|
||||
import type { TimeSeriesExplorerAppState } from '../../../../../common/types/locator';
|
||||
import { useJobSelectionFlyout } from '../../../contexts/ml/use_job_selection_flyout';
|
||||
import { useRefresh } from '../../use_refresh';
|
||||
import { TimeseriesexplorerNoChartData } from '../../../timeseriesexplorer/components/timeseriesexplorer_no_chart_data';
|
||||
|
||||
type AppStateZoom = Exclude<TimeSeriesExplorerAppState['mlTimeSeriesExplorer'], undefined>['zoom'];
|
||||
|
||||
interface TimeSeriesExplorerUrlStateManager {
|
||||
export interface TimeSeriesExplorerUrlStateManager {
|
||||
config: IUiSettingsClient;
|
||||
jobsWithTimeRange: MlJobWithTimeRange[];
|
||||
}
|
|
@ -8,28 +8,28 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { I18nProvider } from '@kbn/i18n-react';
|
||||
import { TimeSeriesExplorerUrlStateManager } from './timeseriesexplorer';
|
||||
import { TimeSeriesExplorer } from '../../timeseriesexplorer';
|
||||
import { TimeSeriesExplorerPage } from '../../timeseriesexplorer/timeseriesexplorer_page';
|
||||
import { TimeseriesexplorerNoJobsFound } from '../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found';
|
||||
import { TimeSeriesExplorerUrlStateManager } from './state_manager';
|
||||
import { TimeSeriesExplorer } from '../../../timeseriesexplorer';
|
||||
import { TimeSeriesExplorerPage } from '../../../timeseriesexplorer/timeseriesexplorer_page';
|
||||
import { TimeseriesexplorerNoJobsFound } from '../../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found';
|
||||
import { DatePickerContextProvider, type DatePickerDependencies } from '@kbn/ml-date-picker';
|
||||
import type { IUiSettingsClient } from '@kbn/core/public';
|
||||
|
||||
jest.mock('../../services/toast_notification_service');
|
||||
jest.mock('../../../services/toast_notification_service');
|
||||
|
||||
jest.mock('../../timeseriesexplorer', () => ({
|
||||
jest.mock('../../../timeseriesexplorer', () => ({
|
||||
TimeSeriesExplorer: jest.fn(() => {
|
||||
return null;
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../timeseriesexplorer/timeseriesexplorer_page', () => ({
|
||||
jest.mock('../../../timeseriesexplorer/timeseriesexplorer_page', () => ({
|
||||
TimeSeriesExplorerPage: jest.fn(({ children }) => {
|
||||
return <>{children}</>;
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found', () => ({
|
||||
jest.mock('../../../timeseriesexplorer/components/timeseriesexplorer_no_jobs_found', () => ({
|
||||
TimeseriesexplorerNoJobsFound: jest.fn(() => {
|
||||
return null;
|
||||
}),
|
||||
|
@ -90,13 +90,13 @@ jest.mock('@kbn/ml-url-state', () => {
|
|||
};
|
||||
});
|
||||
|
||||
jest.mock('../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state');
|
||||
jest.mock('../../../timeseriesexplorer/hooks/use_timeseriesexplorer_url_state');
|
||||
|
||||
jest.mock('../../components/help_menu', () => ({
|
||||
jest.mock('../../../components/help_menu', () => ({
|
||||
HelpMenu: () => <div id="mockHelpMenu" />,
|
||||
}));
|
||||
|
||||
jest.mock('../../contexts/kibana/kibana_context', () => {
|
||||
jest.mock('../../../contexts/kibana/kibana_context', () => {
|
||||
return {
|
||||
useMlKibana: () => {
|
||||
return {
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* 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 type { FC } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { useMlApiContext, useUiSettings } from '../../../contexts/kibana';
|
||||
import { getDateFormatTz } from '../../../explorer/explorer_utils';
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import type { MlRoute, PageProps } from '../../router';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { AnnotationUpdatesService } from '../../../services/annotations_service';
|
||||
import { MlAnnotationUpdatesContext } from '../../../contexts/ml/ml_annotation_updates_context';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
|
||||
const TimeSeriesExplorerUrlStateManager = dynamic(async () => ({
|
||||
default: (await import('./state_manager')).TimeSeriesExplorerUrlStateManager,
|
||||
}));
|
||||
|
||||
export const timeSeriesExplorerRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
): MlRoute => ({
|
||||
id: 'timeseriesexplorer',
|
||||
path: createPath(ML_PAGES.SINGLE_METRIC_VIEWER),
|
||||
title: i18n.translate('xpack.ml.anomalyDetection.singleMetricViewerLabel', {
|
||||
defaultMessage: 'Single Metric Viewer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.anomalyDetection.singleMetricViewerLabel', {
|
||||
defaultMessage: 'Single Metric Viewer',
|
||||
}),
|
||||
},
|
||||
],
|
||||
enableDatePicker: true,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const mlApi = useMlApiContext();
|
||||
const uiSettings = useUiSettings();
|
||||
const { context, results } = useRouteResolver('full', ['canGetJobs'], {
|
||||
...basicResolvers(),
|
||||
jobs: mlJobService.loadJobsWrapper,
|
||||
jobsWithTimeRange: () => mlApi.jobs.jobsWithTimerange(getDateFormatTz()),
|
||||
});
|
||||
|
||||
const annotationUpdatesService = useMemo(() => new AnnotationUpdatesService(), []);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<MlAnnotationUpdatesContext.Provider value={annotationUpdatesService}>
|
||||
{results ? (
|
||||
<TimeSeriesExplorerUrlStateManager
|
||||
config={uiSettings}
|
||||
jobsWithTimeRange={results.jobsWithTimeRange.jobs}
|
||||
/>
|
||||
) : null}
|
||||
</MlAnnotationUpdatesContext.Provider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import type { NavigateToPath } from '../../../contexts/kibana';
|
||||
import type { MlRoute } from '../../router';
|
||||
|
@ -17,9 +18,12 @@ import { createPath, PageLoader } from '../../router';
|
|||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { ModelsList } from '../../../model_management';
|
||||
import { MlPageHeader } from '../../../components/page_header';
|
||||
|
||||
const ModelsList = dynamic(async () => ({
|
||||
default: (await import('../../../model_management/models_list')).ModelsList,
|
||||
}));
|
||||
|
||||
export const modelsListRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
basePath: string
|
||||
|
|
|
@ -23,7 +23,7 @@ import type { SeriesConfigWithMetadata } from '../../../common/types/results';
|
|||
|
||||
import type { ExplorerChartsData } from '../explorer/explorer_charts/explorer_charts_container_service';
|
||||
import type { AppStateSelectedCells } from '../explorer/explorer_utils';
|
||||
import { SWIM_LANE_LABEL_WIDTH } from '../explorer/swimlane_container';
|
||||
import { SWIM_LANE_LABEL_WIDTH } from '../explorer/constants';
|
||||
|
||||
import type { MlApiServices } from './ml_api_service';
|
||||
import type { MlResultsService } from './results_service';
|
||||
|
|
51
x-pack/plugins/ml/public/application/util/get_services.ts
Normal file
51
x-pack/plugins/ml/public/application/util/get_services.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 type { HttpStart } from '@kbn/core-http-browser';
|
||||
|
||||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
|
||||
import { MlLicense } from '../../../common/license';
|
||||
import { MlCapabilitiesService } from '../capabilities/check_capabilities';
|
||||
import { fieldFormatServiceFactory } from '../services/field_format_service_factory';
|
||||
import { HttpService } from '../services/http_service';
|
||||
import { mlApiServicesProvider } from '../services/ml_api_service';
|
||||
import { mlUsageCollectionProvider } from '../services/usage_collection';
|
||||
import { indexServiceFactory } from './index_service';
|
||||
|
||||
/**
|
||||
* Provides global services available across the entire ML app.
|
||||
*/
|
||||
export function getMlGlobalServices(
|
||||
httpStart: HttpStart,
|
||||
dataViews: DataViewsContract,
|
||||
usageCollection?: UsageCollectionSetup
|
||||
) {
|
||||
const httpService = new HttpService(httpStart);
|
||||
const mlApiServices = mlApiServicesProvider(httpService);
|
||||
// Note on the following services:
|
||||
// - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`,
|
||||
// but it's not being made available as part of global services. Since it's just
|
||||
// some stateless utils `useMlIndexUtils()` should be used from within components.
|
||||
// - `mlFieldFormatService` is a stateful legacy service that relied on "dependency cache",
|
||||
// so because of its own state it needs to be made available as a global service.
|
||||
// In the long run we should again try to get rid of it here and make it available via
|
||||
// its own context or possibly without having a singleton like state at all, since the
|
||||
// way this manages its own state right now doesn't consider React component lifecycles.
|
||||
const mlIndexUtils = indexServiceFactory(dataViews);
|
||||
const mlFieldFormatService = fieldFormatServiceFactory(mlApiServices, mlIndexUtils);
|
||||
|
||||
return {
|
||||
httpService,
|
||||
mlApiServices,
|
||||
mlFieldFormatService,
|
||||
mlUsageCollection: mlUsageCollectionProvider(usageCollection),
|
||||
mlCapabilities: new MlCapabilitiesService(mlApiServices),
|
||||
mlLicense: new MlLicense(),
|
||||
};
|
||||
}
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import type { MlEntityField } from '@kbn/ml-anomaly-utils';
|
||||
import type { HasType, PublishingSubject } from '@kbn/presentation-publishing';
|
||||
import type { JobId } from '../../shared';
|
||||
import type { AnomalyExplorerChartsEmbeddableType } from '../constants';
|
||||
import type { MlEmbeddableBaseApi } from '../types';
|
||||
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
|
||||
|
||||
export interface AnomalyChartsFieldSelectionApi {
|
||||
jobIds: PublishingSubject<JobId[]>;
|
||||
|
|
|
@ -13,7 +13,6 @@ import type { Observable } from 'rxjs';
|
|||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { css } from '@emotion/react';
|
||||
import { Y_AXIS_LABEL_WIDTH } from '../../application/explorer/swimlane_annotation_container';
|
||||
import { useEmbeddableExecutionContext } from '../common/use_embeddable_execution_context';
|
||||
import type { IAnomalySwimlaneEmbeddable } from './anomaly_swimlane_embeddable';
|
||||
import { useSwimlaneInputResolver } from './swimlane_input_resolver';
|
||||
|
@ -31,6 +30,7 @@ import type {
|
|||
AnomalySwimlaneServices,
|
||||
} from '..';
|
||||
import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from '..';
|
||||
import { Y_AXIS_LABEL_WIDTH } from '../../application/explorer/constants';
|
||||
|
||||
export interface ExplorerSwimlaneContainerProps {
|
||||
id: string;
|
||||
|
|
|
@ -13,11 +13,11 @@ import type {
|
|||
import { apiIsOfType } from '@kbn/presentation-publishing';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import type { SwimlaneType } from '../../application/explorer/explorer_constants';
|
||||
import type { JobId } from '../../shared';
|
||||
import type { AnomalySwimLaneEmbeddableType } from '../constants';
|
||||
import { ANOMALY_SWIMLANE_EMBEDDABLE_TYPE } from '../constants';
|
||||
import type { AnomalySwimlaneEmbeddableUserInput, MlEmbeddableBaseApi } from '../types';
|
||||
import type { AppStateSelectedCells } from '../../application/explorer/explorer_utils';
|
||||
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
|
||||
|
||||
export interface AnomalySwimLaneComponentApi {
|
||||
jobIds: PublishingSubject<JobId[]>;
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import type { BehaviorSubject } from 'rxjs';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { type AnomalyDetectorService } from '../../application/services/anomaly_detector_service';
|
||||
import type { JobId } from '../../shared';
|
||||
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
|
||||
|
||||
export type CommonInput = { jobIds: string[] } & EmbeddableInput;
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
|||
import { toMountPoint } from '@kbn/react-kibana-mount';
|
||||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import { getInitialGroupsMap } from '../../application/components/job_selector/job_selector';
|
||||
import { getMlGlobalServices } from '../../application/app';
|
||||
import type { JobId } from '../../../common/types/anomaly_detection_jobs';
|
||||
import { JobSelectorFlyout } from './components/job_selector_flyout';
|
||||
import { getMlGlobalServices } from '../../application/util/get_services';
|
||||
|
||||
/**
|
||||
* Handles Anomaly detection jobs selection by a user.
|
||||
|
|
|
@ -15,8 +15,7 @@ import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
|||
import type { CoreStart } from '@kbn/core/public';
|
||||
import type { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import type { DashboardStart } from '@kbn/dashboard-plugin/public';
|
||||
|
||||
import { getMlGlobalServices } from '../../../application/app';
|
||||
import { getMlGlobalServices } from '../../../application/util/get_services';
|
||||
|
||||
export interface FlyoutComponentProps {
|
||||
onClose: () => void;
|
||||
|
|
|
@ -26,13 +26,11 @@ export const plugin: PluginInitializer<
|
|||
> = (initializerContext: PluginInitializerContext) => new MlPlugin(initializerContext);
|
||||
|
||||
export type { MlPluginSetup, MlPluginStart };
|
||||
export type {
|
||||
DataRecognizerConfigResponse,
|
||||
JobExistResult,
|
||||
JobStat,
|
||||
MlCapabilitiesResponse,
|
||||
MlSummaryJob,
|
||||
} from './shared';
|
||||
|
||||
export type { MlCapabilitiesResponse } from '../common/types/capabilities';
|
||||
export type { MlSummaryJob } from '../common/types/anomaly_detection_jobs';
|
||||
export type { JobExistResult, JobStat } from '../common/types/data_recognizer';
|
||||
export type { DataRecognizerConfigResponse } from '../common/types/modules';
|
||||
|
||||
export type { AnomalySwimlaneEmbeddableInput } from './embeddables';
|
||||
|
||||
|
@ -47,12 +45,10 @@ export { useMlHref, ML_PAGES, MlLocatorDefinition } from './locator';
|
|||
export const getMlSharedImports = async () => {
|
||||
return await import('./shared');
|
||||
};
|
||||
|
||||
// Helper to get Type returned by getMlSharedImports.
|
||||
type AwaitReturnType<T> = T extends PromiseLike<infer U> ? U : T;
|
||||
export type GetMlSharedImportsReturnType = AwaitReturnType<ReturnType<typeof getMlSharedImports>>;
|
||||
|
||||
export { MLJobsAwaitingNodeWarning } from './application/components/jobs_awaiting_node_warning/new_job_awaiting_node_shared';
|
||||
export {
|
||||
MlNodeAvailableWarningShared,
|
||||
useMlNodeAvailableCheck,
|
||||
} from './application/components/node_available_warning';
|
||||
export { MlNodeAvailableWarningShared } from './application/components/node_available_warning';
|
||||
|
|
|
@ -68,9 +68,9 @@ import {
|
|||
type ExperimentalFeatures,
|
||||
initExperimentalFeatures,
|
||||
} from '../common/constants/app';
|
||||
import type { MlCapabilities } from './shared';
|
||||
import type { ElasticModels } from './application/services/elastic_models_service';
|
||||
import type { MlApiServices } from './application/services/ml_api_service';
|
||||
import type { MlCapabilities } from '../common/types/capabilities';
|
||||
|
||||
export interface MlStartDependencies {
|
||||
cases?: CasesPublicStart;
|
||||
|
@ -233,8 +233,6 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
|
|||
registerEmbeddables,
|
||||
registerMlUiActions,
|
||||
registerSearchLinks,
|
||||
registerMlAlerts,
|
||||
registerMapExtension,
|
||||
registerCasesAttachments,
|
||||
} = await import('./register_helper');
|
||||
registerSearchLinks(this.appUpdater$, fullLicense, mlCapabilities, this.isServerless);
|
||||
|
@ -245,6 +243,10 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
|
|||
// Register rules for basic license to show them in the UI as disabled
|
||||
!fullLicense)
|
||||
) {
|
||||
// This module contains async imports itself, and it is conditionally loaded based on the license. We'll save
|
||||
// traffic if we load it async.
|
||||
const { registerMlAlerts } = await import('./alerting/register_ml_alerts');
|
||||
|
||||
registerMlAlerts(
|
||||
pluginsSetup.triggersActionsUi,
|
||||
core.getStartServices,
|
||||
|
@ -264,6 +266,10 @@ export class MlPlugin implements Plugin<MlPluginSetup, MlPluginStart> {
|
|||
}
|
||||
|
||||
if (pluginsSetup.maps) {
|
||||
// This module contains async imports itself, and it is conditionally loaded if maps is enabled. We'll save
|
||||
// traffic if we load it async.
|
||||
const { registerMapExtension } = await import('./maps/register_map_extension');
|
||||
|
||||
// Pass canGetJobs as minimum permission to show anomalies card in maps layers
|
||||
await registerMapExtension(pluginsSetup.maps, core, {
|
||||
canGetJobs: mlCapabilities.canGetJobs,
|
||||
|
|
|
@ -5,10 +5,9 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
// These register helper functions have no async imports themselves, so they
|
||||
// can be bundled together for a single async chunk.
|
||||
export { registerEmbeddables } from '../embeddables';
|
||||
export { registerManagementSection } from '../application/management';
|
||||
export { registerMlUiActions } from '../ui_actions';
|
||||
export { registerSearchLinks } from './register_search_links';
|
||||
export { registerMlAlerts } from '../alerting';
|
||||
export { registerMapExtension } from '../maps/register_map_extension';
|
||||
export { registerCasesAttachments } from '../cases';
|
||||
|
|
|
@ -9,8 +9,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import type { BehaviorSubject } from 'rxjs';
|
||||
|
||||
import type { AppUpdater } from '@kbn/core/public';
|
||||
import type { MlCapabilities } from '../../../common/types/capabilities';
|
||||
import { getDeepLinks } from './search_deep_links';
|
||||
import type { MlCapabilities } from '../../shared';
|
||||
|
||||
export function registerSearchLinks(
|
||||
appUpdater: BehaviorSubject<AppUpdater>,
|
||||
|
|
|
@ -9,8 +9,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import type { LinkId } from '@kbn/deeplinks-ml';
|
||||
|
||||
import { type AppDeepLink } from '@kbn/core/public';
|
||||
import type { MlCapabilities } from '../../../common/types/capabilities';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import type { MlCapabilities } from '../../shared';
|
||||
|
||||
function createDeepLinks(
|
||||
mlCapabilities: MlCapabilities,
|
||||
|
|
|
@ -5,16 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export * from '../common/types/data_recognizer';
|
||||
export * from '../common/types/capabilities';
|
||||
export * from '../common/types/anomaly_detection_jobs';
|
||||
export * from '../common/types/modules';
|
||||
export * from '../common/types/audit_message';
|
||||
|
||||
export * from '../common/util/validators';
|
||||
|
||||
export * from '../common/util/metric_change_description';
|
||||
export * from './application/components/field_stats_flyout';
|
||||
export * from './application/data_frame_analytics/common';
|
||||
|
||||
export { FieldStatsInfoButton } from './application/components/field_stats_flyout/field_stats_info_button';
|
||||
export { useFieldStatsTrigger } from './application/components/field_stats_flyout/use_field_stats_trigger';
|
||||
export { FieldStatsFlyoutProvider } from './application/components/field_stats_flyout/field_stats_flyout_provider';
|
||||
export { useFieldStatsFlyoutContext } from './application/components/field_stats_flyout/use_field_stats_flytout_context';
|
||||
|
|
|
@ -120,5 +120,6 @@
|
|||
"@kbn/core-elasticsearch-client-server-mocks",
|
||||
"@kbn/ml-time-buckets",
|
||||
"@kbn/aiops-change-point-detection",
|
||||
"@kbn/shared-ux-utility",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
import { Job, Datafeed } from '@kbn/ml-plugin/public/shared';
|
||||
import { Job, Datafeed } from '@kbn/ml-plugin/common';
|
||||
import { DATAFEED_STATE, JOB_STATE } from '@kbn/ml-plugin/common';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { USER } from '../../../../functional/services/ml/security_common';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue