mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
[ML] Update route resolvers (#159176)
## Summary Resolves #153932 Updates route resolver callback to track license and ML capabilities requirements. - The logic for resolving the data view and saved search has been moved to the dedicated context `DataSourceContextProvider` and only applies to pages that need it. It also shows an error callout in case of an error during the data view fetch. - ML License class has been updated to track license changes and logic for redirects has been moved to the route resolver - `MlCapabilitiesService` has been updated to periodically fetch capabilities - Most of the static usages of `checkPermission` have been replaced with `usePermissionCheck` ### Notes for reviewers - Now it's obvious what license and capabilities requirements each route has. We should carefully review it because I assume legacy resolvers were not entirely correct in the same cases. ### Checklist - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios
This commit is contained in:
parent
0c4906af89
commit
bf6848888b
117 changed files with 1202 additions and 1397 deletions
|
@ -60,7 +60,6 @@ export const ML_PAGES = {
|
|||
FILTER_LISTS_MANAGE: 'settings/filter_lists',
|
||||
FILTER_LISTS_NEW: 'settings/filter_lists/new_filter_list',
|
||||
FILTER_LISTS_EDIT: 'settings/filter_lists/edit_filter_list',
|
||||
ACCESS_DENIED: 'access-denied',
|
||||
OVERVIEW: 'overview',
|
||||
NOTIFICATIONS: 'notifications',
|
||||
AIOPS: 'aiops',
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Observable, Subscription } from 'rxjs';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { ILicense } from '@kbn/licensing-plugin/common/types';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { isEqual } from 'lodash';
|
||||
import { PLUGIN_ID } from '../constants/app';
|
||||
|
||||
export const MINIMUM_LICENSE = 'basic';
|
||||
|
@ -19,6 +21,16 @@ export interface LicenseStatus {
|
|||
message?: string;
|
||||
}
|
||||
|
||||
export interface MlLicenseInfo {
|
||||
license: ILicense | null;
|
||||
isSecurityEnabled: boolean;
|
||||
hasLicenseExpired: boolean;
|
||||
isMlEnabled: boolean;
|
||||
isMinimumLicense: boolean;
|
||||
isFullLicense: boolean;
|
||||
isTrialLicense: boolean;
|
||||
}
|
||||
|
||||
export class MlLicense {
|
||||
private _licenseSubscription: Subscription | null = null;
|
||||
private _license: ILicense | null = null;
|
||||
|
@ -29,17 +41,48 @@ export class MlLicense {
|
|||
private _isFullLicense: boolean = false;
|
||||
private _isTrialLicense: boolean = false;
|
||||
|
||||
private _licenseInfo$ = new BehaviorSubject<MlLicenseInfo>({
|
||||
license: this._license,
|
||||
isSecurityEnabled: this._isSecurityEnabled,
|
||||
hasLicenseExpired: this._hasLicenseExpired,
|
||||
isMlEnabled: this._isMlEnabled,
|
||||
isMinimumLicense: this._isMinimumLicense,
|
||||
isFullLicense: this._isFullLicense,
|
||||
isTrialLicense: this._isTrialLicense,
|
||||
});
|
||||
|
||||
public licenseInfo$: Observable<MlLicenseInfo> = this._licenseInfo$.pipe(
|
||||
distinctUntilChanged(isEqual)
|
||||
);
|
||||
|
||||
public isLicenseReady$: Observable<boolean> = this._licenseInfo$.pipe(
|
||||
map((v) => !!v.license),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
public setup(license$: Observable<ILicense>, callback?: (lic: MlLicense) => void) {
|
||||
this._licenseSubscription = license$.subscribe(async (license) => {
|
||||
this._licenseSubscription = license$.subscribe((license) => {
|
||||
const { isEnabled: securityIsEnabled } = license.getFeature('security');
|
||||
|
||||
const mlLicenseUpdate = {
|
||||
license,
|
||||
isSecurityEnabled: securityIsEnabled,
|
||||
hasLicenseExpired: license.status === 'expired',
|
||||
isMlEnabled: license.getFeature(PLUGIN_ID).isEnabled,
|
||||
isMinimumLicense: isMinimumLicense(license),
|
||||
isFullLicense: isFullLicense(license),
|
||||
isTrialLicense: isTrialLicense(license),
|
||||
};
|
||||
|
||||
this._licenseInfo$.next(mlLicenseUpdate);
|
||||
|
||||
this._license = license;
|
||||
this._isSecurityEnabled = securityIsEnabled;
|
||||
this._hasLicenseExpired = this._license.status === 'expired';
|
||||
this._isMlEnabled = this._license.getFeature(PLUGIN_ID).isEnabled;
|
||||
this._isMinimumLicense = isMinimumLicense(this._license);
|
||||
this._isFullLicense = isFullLicense(this._license);
|
||||
this._isTrialLicense = isTrialLicense(this._license);
|
||||
this._isSecurityEnabled = mlLicenseUpdate.isSecurityEnabled;
|
||||
this._hasLicenseExpired = mlLicenseUpdate.hasLicenseExpired;
|
||||
this._isMlEnabled = mlLicenseUpdate.isMlEnabled;
|
||||
this._isMinimumLicense = mlLicenseUpdate.isMinimumLicense;
|
||||
this._isFullLicense = mlLicenseUpdate.isFullLicense;
|
||||
this._isTrialLicense = mlLicenseUpdate.isTrialLicense;
|
||||
|
||||
if (callback !== undefined) {
|
||||
callback(this);
|
||||
|
@ -47,6 +90,10 @@ export class MlLicense {
|
|||
});
|
||||
}
|
||||
|
||||
public getLicenseInfo() {
|
||||
return this._licenseInfo$.getValue();
|
||||
}
|
||||
|
||||
public unsubscribe() {
|
||||
if (this._licenseSubscription !== null) {
|
||||
this._licenseSubscription.unsubscribe();
|
||||
|
|
|
@ -39,6 +39,8 @@ export const userMlCapabilities = {
|
|||
canTestTrainedModels: false,
|
||||
canGetFieldInfo: false,
|
||||
canGetMlInfo: false,
|
||||
// AIOps
|
||||
canUseAiops: false,
|
||||
};
|
||||
|
||||
export const adminMlCapabilities = {
|
||||
|
|
|
@ -58,7 +58,6 @@ export type MlGenericUrlState = MLPageState<
|
|||
| typeof ML_PAGES.FILTER_LISTS_MANAGE
|
||||
| typeof ML_PAGES.FILTER_LISTS_NEW
|
||||
| typeof ML_PAGES.SETTINGS
|
||||
| typeof ML_PAGES.ACCESS_DENIED
|
||||
| typeof ML_PAGES.DATA_VISUALIZER
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_FILE
|
||||
| typeof ML_PAGES.DATA_VISUALIZER_INDEX_SELECT
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { type FC } from 'react';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
|
@ -16,14 +16,23 @@ import {
|
|||
EuiPageContent_Deprecated as EuiPageContent,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { createPermissionFailureMessage } from '../capabilities/check_capabilities';
|
||||
import { MlCapabilitiesKey } from '../../../common/types/capabilities';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
|
||||
export const Page = () => {
|
||||
export interface AccessDeniedCalloutProps {
|
||||
missingCapabilities?: MlCapabilitiesKey[];
|
||||
}
|
||||
|
||||
export const AccessDeniedCallout: FC<AccessDeniedCalloutProps> = ({ missingCapabilities }) => {
|
||||
const {
|
||||
services: { docLinks },
|
||||
} = useMlKibana();
|
||||
const helpLink = docLinks.links.ml.guide;
|
||||
|
||||
const errorMessages = (missingCapabilities ?? []).map((c) => createPermissionFailureMessage(c));
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer size="xxl" />
|
||||
|
@ -41,12 +50,19 @@ export const Page = () => {
|
|||
</h2>
|
||||
}
|
||||
body={
|
||||
<p>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.accessDenied.description"
|
||||
defaultMessage="You don’t have permission to view the Machine Learning plugin. Access to the plugin requires the Machine Learning feature to be visible in this space."
|
||||
defaultMessage="You do not have permission to view this page."
|
||||
/>
|
||||
</p>
|
||||
{errorMessages ? (
|
||||
<ul>
|
||||
{errorMessages.map((v) => (
|
||||
<li key={v}>{v}</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</EuiPageContent>
|
|
@ -5,4 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export { Page } from './page';
|
||||
export { AccessDeniedCallout } from './access_denied';
|
||||
|
|
|
@ -13,8 +13,8 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ChangePointDetection } from '@kbn/aiops-plugin/public';
|
||||
|
||||
import { useDataSource } from '../contexts/ml/data_source_context';
|
||||
import { useFieldStatsTrigger, FieldStatsFlyoutProvider } from '../components/field_stats_flyout';
|
||||
import { useMlContext } from '../contexts/ml';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { TechnicalPreviewBadge } from '../components/technical_preview_badge';
|
||||
|
@ -24,9 +24,7 @@ import { MlPageHeader } from '../components/page_header';
|
|||
export const ChangePointDetectionPage: FC = () => {
|
||||
const { services } = useMlKibana();
|
||||
|
||||
const context = useMlContext();
|
||||
const dataView = context.currentDataView;
|
||||
const savedSearch = context.selectedSavedSearch;
|
||||
const { currentDataView: dataView, selectedSavedSearch: savedSearch } = useDataSource();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -9,23 +9,18 @@ import React, { FC } from 'react';
|
|||
import { pick } from 'lodash';
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { ExplainLogRateSpikes } from '@kbn/aiops-plugin/public';
|
||||
|
||||
import { useMlContext } from '../contexts/ml';
|
||||
import { useDataSource } from '../contexts/ml/data_source_context';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { TechnicalPreviewBadge } from '../components/technical_preview_badge';
|
||||
|
||||
import { MlPageHeader } from '../components/page_header';
|
||||
|
||||
export const ExplainLogRateSpikesPage: FC = () => {
|
||||
const { services } = useMlKibana();
|
||||
|
||||
const context = useMlContext();
|
||||
const dataView = context.currentDataView;
|
||||
const savedSearch = context.selectedSavedSearch;
|
||||
const { currentDataView: dataView, selectedSavedSearch: savedSearch } = useDataSource();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -7,25 +7,19 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
||||
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { LogCategorization } from '@kbn/aiops-plugin/public';
|
||||
|
||||
import { useMlContext } from '../contexts/ml';
|
||||
import { useDataSource } from '../contexts/ml/data_source_context';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { HelpMenu } from '../components/help_menu';
|
||||
import { TechnicalPreviewBadge } from '../components/technical_preview_badge';
|
||||
|
||||
import { MlPageHeader } from '../components/page_header';
|
||||
|
||||
export const LogCategorizationPage: FC = () => {
|
||||
const { services } = useMlKibana();
|
||||
|
||||
const context = useMlContext();
|
||||
const dataView = context.currentDataView;
|
||||
const savedSearch = context.selectedSavedSearch;
|
||||
const { currentDataView: dataView, selectedSavedSearch: savedSearch } = useDataSource();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -5,28 +5,29 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import React, { type FC, useMemo } from 'react';
|
||||
import './_index.scss';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { pick } from 'lodash';
|
||||
|
||||
import { AppMountParameters, CoreStart, HttpStart } from '@kbn/core/public';
|
||||
|
||||
import type { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import { DatePickerContextProvider } from '@kbn/ml-date-picker';
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import { UI_SETTINGS } from '@kbn/data-plugin/common';
|
||||
import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public';
|
||||
import { KibanaContextProvider, KibanaThemeProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import {
|
||||
KibanaContextProvider,
|
||||
KibanaThemeProvider,
|
||||
toMountPoint,
|
||||
wrapWithTheme,
|
||||
} from '@kbn/kibana-react-plugin/public';
|
||||
import { StorageContextProvider } from '@kbn/ml-local-storage';
|
||||
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { mlCapabilities } from './capabilities/check_capabilities';
|
||||
import useLifecycles from 'react-use/lib/useLifecycles';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { MlLicense } from '../../common/license';
|
||||
import { MlCapabilitiesService } from './capabilities/check_capabilities';
|
||||
import { ML_STORAGE_KEYS } from '../../common/types/storage';
|
||||
import { ML_APP_LOCATOR, ML_PAGES } from '../../common/constants/locator';
|
||||
import type { MlSetupDependencies, MlStartDependencies } from '../plugin';
|
||||
|
||||
import { setDependencyCache, clearCache } from './util/dependency_cache';
|
||||
import { clearCache, setDependencyCache } from './util/dependency_cache';
|
||||
import { setLicenseCache } from './license';
|
||||
import { mlUsageCollectionProvider } from './services/usage_collection';
|
||||
import { MlRouter } from './routing';
|
||||
|
@ -58,12 +59,15 @@ export function isServerless() {
|
|||
*/
|
||||
export function getMlGlobalServices(httpStart: HttpStart, usageCollection?: UsageCollectionSetup) {
|
||||
const httpService = new HttpService(httpStart);
|
||||
const mlApiServices = mlApiServicesProvider(httpService);
|
||||
|
||||
return {
|
||||
httpService,
|
||||
mlApiServices: mlApiServicesProvider(httpService),
|
||||
mlApiServices,
|
||||
mlUsageCollection: mlUsageCollectionProvider(usageCollection),
|
||||
isServerless,
|
||||
mlCapabilities,
|
||||
mlCapabilities: new MlCapabilitiesService(mlApiServices),
|
||||
mlLicense: new MlLicense(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -74,54 +78,59 @@ export interface MlServicesContext {
|
|||
export type MlGlobalServices = ReturnType<typeof getMlGlobalServices>;
|
||||
|
||||
const App: FC<AppProps> = ({ coreStart, deps, appMountParams }) => {
|
||||
const redirectToMlAccessDeniedPage = async () => {
|
||||
// access maybe be denied due to an expired license, so check the license status first
|
||||
// if the license has expired, redirect to the license management page
|
||||
const license = await firstValueFrom(deps.licensing.license$);
|
||||
const redirectPage =
|
||||
license.status === 'expired'
|
||||
? deps.share.url.locators.get('LICENSE_MANAGEMENT_LOCATOR')!.getUrl({
|
||||
page: 'dashboard',
|
||||
})
|
||||
: deps.share.url.locators.get(ML_APP_LOCATOR)!.getUrl({
|
||||
page: ML_PAGES.ACCESS_DENIED,
|
||||
});
|
||||
|
||||
await coreStart.application.navigateToUrl(await redirectPage);
|
||||
};
|
||||
|
||||
const pageDeps = {
|
||||
history: appMountParams.history,
|
||||
setHeaderActionMenu: appMountParams.setHeaderActionMenu,
|
||||
dataViewsContract: deps.data.dataViews,
|
||||
config: coreStart.uiSettings!,
|
||||
setBreadcrumbs: coreStart.chrome!.setBreadcrumbs,
|
||||
redirectToMlAccessDeniedPage,
|
||||
};
|
||||
|
||||
const services = {
|
||||
kibanaVersion: deps.kibanaVersion,
|
||||
share: deps.share,
|
||||
data: deps.data,
|
||||
security: deps.security,
|
||||
licenseManagement: deps.licenseManagement,
|
||||
storage: localStorage,
|
||||
embeddable: deps.embeddable,
|
||||
maps: deps.maps,
|
||||
triggersActionsUi: deps.triggersActionsUi,
|
||||
dataVisualizer: deps.dataVisualizer,
|
||||
usageCollection: deps.usageCollection,
|
||||
fieldFormats: deps.fieldFormats,
|
||||
dashboard: deps.dashboard,
|
||||
charts: deps.charts,
|
||||
cases: deps.cases,
|
||||
unifiedSearch: deps.unifiedSearch,
|
||||
licensing: deps.licensing,
|
||||
lens: deps.lens,
|
||||
savedObjectsManagement: deps.savedObjectsManagement,
|
||||
savedSearch: deps.savedSearch,
|
||||
...coreStart,
|
||||
};
|
||||
const services = useMemo(() => {
|
||||
return {
|
||||
kibanaVersion: deps.kibanaVersion,
|
||||
share: deps.share,
|
||||
data: deps.data,
|
||||
security: deps.security,
|
||||
licenseManagement: deps.licenseManagement,
|
||||
storage: localStorage,
|
||||
embeddable: deps.embeddable,
|
||||
maps: deps.maps,
|
||||
triggersActionsUi: deps.triggersActionsUi,
|
||||
dataVisualizer: deps.dataVisualizer,
|
||||
usageCollection: deps.usageCollection,
|
||||
fieldFormats: deps.fieldFormats,
|
||||
dashboard: deps.dashboard,
|
||||
charts: deps.charts,
|
||||
cases: deps.cases,
|
||||
unifiedSearch: deps.unifiedSearch,
|
||||
licensing: deps.licensing,
|
||||
lens: deps.lens,
|
||||
savedObjectsManagement: deps.savedObjectsManagement,
|
||||
savedSearch: deps.savedSearch,
|
||||
...coreStart,
|
||||
mlServices: getMlGlobalServices(coreStart.http, deps.usageCollection),
|
||||
};
|
||||
}, [deps, coreStart]);
|
||||
|
||||
useLifecycles(
|
||||
function setupLicenseOnMount() {
|
||||
setLicenseCache(services.mlServices.mlLicense);
|
||||
services.mlServices.mlLicense.setup(deps.licensing.license$);
|
||||
},
|
||||
function destroyLicenseOnUnmount() {
|
||||
services.mlServices.mlLicense.unsubscribe();
|
||||
}
|
||||
);
|
||||
|
||||
// Wait for license and capabilities to be retrieved before rendering the app.
|
||||
const licenseReady = useObservable(services.mlServices.mlLicense.isLicenseReady$, false);
|
||||
const mlCapabilities = useObservable(
|
||||
services.mlServices.mlCapabilities.capabilities$,
|
||||
services.mlServices.mlCapabilities.getCapabilities()
|
||||
);
|
||||
|
||||
if (!licenseReady || !mlCapabilities) return null;
|
||||
|
||||
const datePickerDeps = {
|
||||
...pick(services, ['data', 'http', 'notifications', 'theme', 'uiSettings']),
|
||||
|
@ -138,12 +147,7 @@ const App: FC<AppProps> = ({ coreStart, deps, appMountParams }) => {
|
|||
<ApplicationUsageTrackingProvider>
|
||||
<I18nContext>
|
||||
<KibanaThemeProvider theme$={appMountParams.theme$}>
|
||||
<KibanaContextProvider
|
||||
services={{
|
||||
...services,
|
||||
mlServices: getMlGlobalServices(coreStart.http, deps.usageCollection),
|
||||
}}
|
||||
>
|
||||
<KibanaContextProvider services={services}>
|
||||
<StorageContextProvider storage={localStorage} storageKeys={ML_STORAGE_KEYS}>
|
||||
<DatePickerContextProvider {...datePickerDeps}>
|
||||
<MlRouter pageDeps={pageDeps} />
|
||||
|
@ -188,15 +192,12 @@ export const renderApp = (
|
|||
|
||||
appMountParams.onAppLeave((actions) => actions.default());
|
||||
|
||||
const mlLicense = setLicenseCache(deps.licensing, coreStart.application, () =>
|
||||
ReactDOM.render(
|
||||
<App coreStart={coreStart} deps={deps} appMountParams={appMountParams} />,
|
||||
appMountParams.element
|
||||
)
|
||||
ReactDOM.render(
|
||||
<App coreStart={coreStart} deps={deps} appMountParams={appMountParams} />,
|
||||
appMountParams.element
|
||||
);
|
||||
|
||||
return () => {
|
||||
mlLicense.unsubscribe();
|
||||
clearCache();
|
||||
ReactDOM.unmountComponentAtNode(appMountParams.element);
|
||||
deps.data.search.session.clear();
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
export const usePermissionCheck = jest.fn((arg: string | string[]) => {
|
||||
if (Array.isArray(arg)) {
|
||||
return arg.map((v) => true);
|
||||
}
|
||||
return true;
|
||||
});
|
|
@ -6,57 +6,110 @@
|
|||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { distinctUntilChanged } from 'rxjs/operators';
|
||||
import { BehaviorSubject, combineLatest, from, type Subscription, timer } from 'rxjs';
|
||||
import { distinctUntilChanged, retry, switchMap, tap } from 'rxjs/operators';
|
||||
import { isEqual } from 'lodash';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { useMemo, useRef } from 'react';
|
||||
import { useMlKibana } from '../contexts/kibana';
|
||||
import { hasLicenseExpired } from '../license';
|
||||
|
||||
import { MlCapabilities, getDefaultCapabilities } from '../../../common/types/capabilities';
|
||||
import {
|
||||
getDefaultCapabilities,
|
||||
MlCapabilities,
|
||||
MlCapabilitiesKey,
|
||||
} from '../../../common/types/capabilities';
|
||||
import { getCapabilities } from './get_capabilities';
|
||||
import type { MlApiServices } from '../services/ml_api_service';
|
||||
import { type MlApiServices } from '../services/ml_api_service';
|
||||
|
||||
let _capabilities: MlCapabilities = getDefaultCapabilities();
|
||||
|
||||
const CAPABILITIES_REFRESH_INTERVAL = 60000;
|
||||
|
||||
export class MlCapabilitiesService {
|
||||
private _capabilities$ = new BehaviorSubject<MlCapabilities>(getDefaultCapabilities());
|
||||
private _isLoading$ = new BehaviorSubject<boolean>(true);
|
||||
|
||||
/**
|
||||
* Updates on manual request, e.g. in the route resolver.
|
||||
* @private
|
||||
*/
|
||||
private _updateRequested$ = new BehaviorSubject<number>(Date.now());
|
||||
|
||||
private _capabilities$ = new BehaviorSubject<MlCapabilities | null>(null);
|
||||
|
||||
public capabilities$ = this._capabilities$.pipe(distinctUntilChanged(isEqual));
|
||||
|
||||
public getCapabilities(): MlCapabilities {
|
||||
private _subscription: Subscription | undefined;
|
||||
|
||||
constructor(private readonly mlApiServices: MlApiServices) {
|
||||
this.init();
|
||||
}
|
||||
|
||||
private init() {
|
||||
this._subscription = combineLatest([
|
||||
this._updateRequested$,
|
||||
timer(0, CAPABILITIES_REFRESH_INTERVAL),
|
||||
])
|
||||
.pipe(
|
||||
tap(() => {
|
||||
this._isLoading$.next(true);
|
||||
}),
|
||||
switchMap(() => from(this.mlApiServices.checkMlCapabilities())),
|
||||
retry({ delay: CAPABILITIES_REFRESH_INTERVAL })
|
||||
)
|
||||
.subscribe((results) => {
|
||||
this._capabilities$.next(results.capabilities);
|
||||
this._isLoading$.next(false);
|
||||
|
||||
/**
|
||||
* To support legacy use of {@link checkPermission}
|
||||
*/
|
||||
_capabilities = results.capabilities;
|
||||
});
|
||||
}
|
||||
|
||||
public getCapabilities(): MlCapabilities | null {
|
||||
return this._capabilities$.getValue();
|
||||
}
|
||||
|
||||
public updateCapabilities(update: MlCapabilities) {
|
||||
this._capabilities$.next(update);
|
||||
public refreshCapabilities() {
|
||||
this._updateRequested$.next(Date.now());
|
||||
}
|
||||
|
||||
public destroy() {
|
||||
if (this._subscription) {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO should be initialized in getMlGlobalServices
|
||||
* Temp solution to make it work with the current setup.
|
||||
*/
|
||||
export const mlCapabilities = new MlCapabilitiesService();
|
||||
|
||||
/**
|
||||
* Check the privilege type and the license to see whether a user has permission to access a feature.
|
||||
*
|
||||
* @param capability
|
||||
*/
|
||||
export function usePermissionCheck(capability: keyof MlCapabilities) {
|
||||
export function usePermissionCheck<T extends MlCapabilitiesKey | MlCapabilitiesKey[]>(
|
||||
capability: T
|
||||
): T extends MlCapabilitiesKey ? boolean : boolean[] {
|
||||
const {
|
||||
services: {
|
||||
mlServices: { mlCapabilities: mlCapabilitiesService },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const licenseHasExpired = hasLicenseExpired();
|
||||
// Memoize argument, in case it's an array to preserve the reference.
|
||||
const requestedCapabilities = useRef(capability);
|
||||
|
||||
const capabilities = useObservable(
|
||||
mlCapabilitiesService.capabilities$,
|
||||
mlCapabilitiesService.getCapabilities()
|
||||
);
|
||||
return capabilities[capability] && !licenseHasExpired;
|
||||
|
||||
return useMemo(() => {
|
||||
return Array.isArray(requestedCapabilities.current)
|
||||
? requestedCapabilities.current.map((c) => capabilities[c])
|
||||
: capabilities[requestedCapabilities.current];
|
||||
}, [capabilities]);
|
||||
}
|
||||
|
||||
export function checkGetManagementMlJobsResolver({ checkMlCapabilities }: MlApiServices) {
|
||||
|
@ -64,7 +117,6 @@ export function checkGetManagementMlJobsResolver({ checkMlCapabilities }: MlApiS
|
|||
checkMlCapabilities()
|
||||
.then(({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => {
|
||||
_capabilities = capabilities;
|
||||
mlCapabilities.updateCapabilities(capabilities);
|
||||
// Loop through all capabilities to ensure they are all set to true.
|
||||
const isManageML = Object.values(_capabilities).every((p) => p === true);
|
||||
|
||||
|
@ -80,33 +132,6 @@ export function checkGetManagementMlJobsResolver({ checkMlCapabilities }: MlApiS
|
|||
});
|
||||
}
|
||||
|
||||
export function checkGetJobsCapabilitiesResolver(
|
||||
redirectToMlAccessDeniedPage: () => Promise<void>
|
||||
): Promise<MlCapabilities> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getCapabilities()
|
||||
.then(async ({ capabilities, isPlatinumOrTrialLicense }) => {
|
||||
_capabilities = capabilities;
|
||||
mlCapabilities.updateCapabilities(capabilities);
|
||||
// the minimum privilege for using ML with a platinum or trial license is being able to get the transforms list.
|
||||
// all other functionality is controlled by the return capabilities object.
|
||||
// if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect,
|
||||
// allow the promise to resolve as the separate license check will redirect then user to
|
||||
// a basic feature
|
||||
if (_capabilities.canGetJobs || isPlatinumOrTrialLicense === false) {
|
||||
return resolve(_capabilities);
|
||||
} else {
|
||||
await redirectToMlAccessDeniedPage();
|
||||
return reject();
|
||||
}
|
||||
})
|
||||
.catch(async (e) => {
|
||||
await redirectToMlAccessDeniedPage();
|
||||
return reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function checkCreateJobsCapabilitiesResolver(
|
||||
redirectToJobsManagementPage: () => Promise<void>
|
||||
): Promise<MlCapabilities> {
|
||||
|
@ -114,7 +139,6 @@ export function checkCreateJobsCapabilitiesResolver(
|
|||
getCapabilities()
|
||||
.then(async ({ capabilities, isPlatinumOrTrialLicense }) => {
|
||||
_capabilities = capabilities;
|
||||
mlCapabilities.updateCapabilities(capabilities);
|
||||
// if the license is basic (isPlatinumOrTrialLicense === false) then do not redirect,
|
||||
// allow the promise to resolve as the separate license check will redirect then user to
|
||||
// a basic feature
|
||||
|
@ -134,30 +158,6 @@ export function checkCreateJobsCapabilitiesResolver(
|
|||
});
|
||||
}
|
||||
|
||||
export function checkFindFileStructurePrivilegeResolver(
|
||||
redirectToMlAccessDeniedPage: () => Promise<void>
|
||||
): Promise<MlCapabilities> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getCapabilities()
|
||||
.then(async ({ capabilities }) => {
|
||||
_capabilities = capabilities;
|
||||
mlCapabilities.updateCapabilities(capabilities);
|
||||
// the minimum privilege for using ML with a basic license is being able to use the datavisualizer.
|
||||
// all other functionality is controlled by the return _capabilities object
|
||||
if (_capabilities.canFindFileStructure) {
|
||||
return resolve(_capabilities);
|
||||
} else {
|
||||
await redirectToMlAccessDeniedPage();
|
||||
return reject();
|
||||
}
|
||||
})
|
||||
.catch(async (e) => {
|
||||
await redirectToMlAccessDeniedPage();
|
||||
return reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link usePermissionCheck} instead.
|
||||
* @param capability
|
||||
|
|
|
@ -45,7 +45,7 @@ import {
|
|||
getDateFormatTz,
|
||||
SourceIndicesWithGeoFields,
|
||||
} from '../../explorer/explorer_utils';
|
||||
import { checkPermission } from '../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import type { TimeRangeBounds } from '../../util/time_buckets';
|
||||
import { useMlKibana } from '../../contexts/kibana';
|
||||
// @ts-ignore
|
||||
|
@ -617,7 +617,8 @@ export const LinksMenuUI = (props: LinksMenuProps) => {
|
|||
};
|
||||
|
||||
const { anomaly, showViewSeriesLink } = props;
|
||||
const canConfigureRules = isRuleSupported(anomaly.source) && checkPermission('canUpdateJob');
|
||||
const canUpdateJob = usePermissionCheck('canUpdateJob');
|
||||
const canConfigureRules = isRuleSupported(anomaly.source) && canUpdateJob;
|
||||
|
||||
const contextMenuItems = useMemo(() => {
|
||||
const items = [];
|
||||
|
|
|
@ -5,27 +5,26 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC, useEffect, useCallback, useState, useRef, useMemo } from 'react';
|
||||
import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import {
|
||||
EuiBasicTableColumn,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiInMemoryTable,
|
||||
EuiLoadingSpinner,
|
||||
EuiBasicTableColumn,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import { timeFormatter } from '@kbn/ml-date-utils';
|
||||
import { checkPermission } from '../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
import { EditModelSnapshotFlyout } from './edit_model_snapshot_flyout';
|
||||
import { RevertModelSnapshotFlyout } from './revert_model_snapshot_flyout';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { JOB_STATE, DATAFEED_STATE } from '../../../../common/constants/states';
|
||||
import { DATAFEED_STATE, JOB_STATE } from '../../../../common/constants/states';
|
||||
import { CloseJobConfirm } from './close_job_confirm';
|
||||
import {
|
||||
ModelSnapshot,
|
||||
CombinedJobWithStats,
|
||||
ModelSnapshot,
|
||||
} from '../../../../common/types/anomaly_detection_jobs';
|
||||
|
||||
interface Props {
|
||||
|
@ -41,8 +40,10 @@ export enum COMBINED_JOB_STATE {
|
|||
}
|
||||
|
||||
export const ModelSnapshotTable: FC<Props> = ({ job, refreshJobList }) => {
|
||||
const canCreateJob = checkPermission('canCreateJob');
|
||||
const canStartStopDatafeed = checkPermission('canStartStopDatafeed');
|
||||
const [canCreateJob, canStartStopDatafeed] = usePermissionCheck([
|
||||
'canCreateJob',
|
||||
'canStartStopDatafeed',
|
||||
]);
|
||||
|
||||
const [snapshots, setSnapshots] = useState<ModelSnapshot[]>([]);
|
||||
const [snapshotsLoaded, setSnapshotsLoaded] = useState<boolean>(false);
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC, useEffect, useState, useCallback, useRef, useMemo } from 'react';
|
||||
import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { EuiCallOut, EuiLink, EuiSpacer } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { MlSavedObjectType } from '../../../../common/types/saved_objects';
|
||||
import { useMlApiContext } from '../../contexts/kibana';
|
||||
import { JobSpacesSyncFlyout } from '../job_spaces_sync';
|
||||
import { checkPermission } from '../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../capabilities/check_capabilities';
|
||||
|
||||
interface Props {
|
||||
mlSavedObjectType?: MlSavedObjectType;
|
||||
|
@ -31,7 +31,7 @@ export const SavedObjectsWarning: FC<Props> = ({
|
|||
const mounted = useRef(false);
|
||||
const [showWarning, setShowWarning] = useState(false);
|
||||
const [showSyncFlyout, setShowSyncFlyout] = useState(false);
|
||||
const canCreateJob = useMemo(() => checkPermission('canCreateJob'), []);
|
||||
const canCreateJob = usePermissionCheck('canCreateJob');
|
||||
|
||||
const checkStatus = useCallback(async () => {
|
||||
try {
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
export { useMlKibana } from './kibana_context';
|
||||
export { useTimefilter } from './use_timefilter';
|
||||
export { useMlApiContext } from './use_ml_api_context';
|
||||
export { useMlLicenseInfo } from './use_ml_license';
|
||||
|
|
|
@ -9,6 +9,7 @@ import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
|||
import { fieldFormatsServiceMock } from '@kbn/field-formats-plugin/public/mocks';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
import { mlApiServicesMock } from '../../../services/__mocks__/ml_api_services';
|
||||
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
|
||||
|
||||
export const chartsServiceMock = {
|
||||
theme: {
|
||||
|
@ -37,13 +38,24 @@ export const kibanaContextMock = {
|
|||
services: {
|
||||
uiSettings: { get: jest.fn() },
|
||||
chrome: { recentlyAccessed: { add: jest.fn() } },
|
||||
application: { navigateToApp: jest.fn() },
|
||||
application: { navigateToApp: jest.fn(), navigateToUrl: jest.fn() },
|
||||
http: {
|
||||
basePath: {
|
||||
get: jest.fn(),
|
||||
},
|
||||
},
|
||||
share: {
|
||||
url: {
|
||||
locators: {
|
||||
get: jest.fn(() => {
|
||||
return {
|
||||
getUrl: jest.fn(() => {
|
||||
return Promise.resolve('mock-url');
|
||||
}),
|
||||
};
|
||||
}),
|
||||
},
|
||||
},
|
||||
urlGenerators: { getUrlGenerator: jest.fn() },
|
||||
},
|
||||
data: dataPluginMock.createStartContract(),
|
||||
|
@ -51,7 +63,11 @@ export const kibanaContextMock = {
|
|||
fieldFormats: fieldFormatsServiceMock.createStartContract(),
|
||||
mlServices: {
|
||||
mlApiServices: mlApiServicesMock,
|
||||
mlCapabilities: {
|
||||
refreshCapabilities: jest.fn(),
|
||||
},
|
||||
},
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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 useMlLicenseInfo = jest.fn(() => {
|
||||
return {
|
||||
isSecurityEnabled: true,
|
||||
hasLicenseExpired: false,
|
||||
isMlEnabled: true,
|
||||
isMinimumLicense: true,
|
||||
isFullLicense: true,
|
||||
isTrialLicense: false,
|
||||
license: {},
|
||||
};
|
||||
});
|
|
@ -15,3 +15,4 @@ export { useMlLocator, useMlLink } from './use_create_url';
|
|||
export { useMlApiContext } from './use_ml_api_context';
|
||||
export { useFieldFormatter } from './use_field_formatter';
|
||||
export { useCurrentThemeVars } from './use_current_theme';
|
||||
export { useMlLicenseInfo } from './use_ml_license';
|
||||
|
|
|
@ -26,6 +26,7 @@ import type { CasesUiStart } from '@kbn/cases-plugin/public';
|
|||
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import type { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
|
||||
import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
|
||||
import type { MlServicesContext } from '../../app';
|
||||
|
||||
interface StartPlugins {
|
||||
|
@ -49,6 +50,7 @@ interface StartPlugins {
|
|||
appName: string;
|
||||
lens: LensPublicStart;
|
||||
savedObjectsManagement: SavedObjectsManagementPluginStart;
|
||||
savedSearch: SavedSearchPublicPluginStart;
|
||||
}
|
||||
export type StartServices = CoreStart &
|
||||
StartPlugins & {
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* 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 useObservable from 'react-use/lib/useObservable';
|
||||
import { useMlKibana } from './kibana_context';
|
||||
|
||||
export const useMlLicenseInfo = () => {
|
||||
const {
|
||||
services: {
|
||||
mlServices: { mlLicense },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
return useObservable(mlLicense.licenseInfo$, mlLicense.getLicenseInfo());
|
||||
};
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { type FC, useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { parse } from 'query-string';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { DataView } from '@kbn/data-views-plugin/common';
|
||||
import { SavedSearch } from '@kbn/saved-search-plugin/public';
|
||||
import { EuiEmptyPrompt } from '@elastic/eui';
|
||||
import { SavedSearchSavedObject } from '../../../../common/types/kibana';
|
||||
import { DataViewAndSavedSearch } from '../../util/index_utils';
|
||||
import { useMlKibana } from '../kibana';
|
||||
import { createSearchItems } from '../../jobs/new_job/utils/new_job_utils';
|
||||
|
||||
export interface DataSourceContextValue {
|
||||
combinedQuery: any;
|
||||
currentDataView: DataView; // TODO this should be DataView or null
|
||||
// @deprecated currentSavedSearch is of SavedSearchSavedObject type, change to selectedSavedSearch
|
||||
deprecatedSavedSearchObj: SavedSearchSavedObject | null;
|
||||
selectedSavedSearch: SavedSearch | null;
|
||||
}
|
||||
|
||||
export const DataSourceContext = React.createContext<DataSourceContextValue>(
|
||||
{} as DataSourceContextValue
|
||||
);
|
||||
|
||||
/**
|
||||
* Context provider that resolves current data view and the saved search from the URL state.
|
||||
*
|
||||
* @param children
|
||||
* @constructor
|
||||
*/
|
||||
export const DataSourceContextProvider: FC = ({ children }) => {
|
||||
const [value, setValue] = useState<DataSourceContextValue>();
|
||||
const [error, setError] = useState<Error>();
|
||||
|
||||
const location = useLocation();
|
||||
const {
|
||||
services: {
|
||||
data: { dataViews },
|
||||
savedSearch: savedSearchService,
|
||||
uiSettings,
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const { index: dataViewId, savedSearchId } = parse(location.search, {
|
||||
sort: false,
|
||||
}) as { index: string; savedSearchId: string };
|
||||
|
||||
const getDataViewAndSavedSearchCallback = useCallback(
|
||||
async (ssId: string) => {
|
||||
const resp: DataViewAndSavedSearch = {
|
||||
savedSearch: null,
|
||||
dataView: null,
|
||||
};
|
||||
|
||||
if (ssId === undefined) {
|
||||
return resp;
|
||||
}
|
||||
|
||||
const ss = await savedSearchService.get(ssId);
|
||||
if (ss === null) {
|
||||
return resp;
|
||||
}
|
||||
const dataViewIdTemp = ss.references?.find((r) => r.type === 'index-pattern')?.id;
|
||||
resp.dataView = await dataViews.get(dataViewIdTemp!);
|
||||
resp.savedSearch = ss;
|
||||
return resp;
|
||||
},
|
||||
[savedSearchService, dataViews]
|
||||
);
|
||||
|
||||
/**
|
||||
* Resolve data view or saved search if exist in the URL.
|
||||
*/
|
||||
const resolveDataSource = useCallback(async () => {
|
||||
if (dataViewId === '') {
|
||||
throw new Error(
|
||||
i18n.translate('xpack.ml.useResolver.errorIndexPatternIdEmptyString', {
|
||||
defaultMessage: 'dataViewId must not be empty string.',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
let dataViewAndSavedSearch: DataViewAndSavedSearch = {
|
||||
savedSearch: null,
|
||||
dataView: null,
|
||||
};
|
||||
let savedSearch = null;
|
||||
|
||||
if (savedSearchId !== undefined) {
|
||||
savedSearch = await savedSearchService.get(savedSearchId);
|
||||
dataViewAndSavedSearch = await getDataViewAndSavedSearchCallback(savedSearchId);
|
||||
} else if (dataViewId !== undefined) {
|
||||
dataViewAndSavedSearch.dataView = await dataViews.get(dataViewId);
|
||||
}
|
||||
|
||||
const { savedSearch: deprecatedSavedSearchObj, dataView } = dataViewAndSavedSearch;
|
||||
|
||||
const { combinedQuery } = createSearchItems(
|
||||
uiSettings,
|
||||
dataView !== null ? dataView : undefined,
|
||||
deprecatedSavedSearchObj
|
||||
);
|
||||
|
||||
return {
|
||||
combinedQuery,
|
||||
currentDataView: dataView,
|
||||
deprecatedSavedSearchObj,
|
||||
selectedSavedSearch: savedSearch,
|
||||
};
|
||||
}, [
|
||||
dataViewId,
|
||||
savedSearchId,
|
||||
uiSettings,
|
||||
dataViews,
|
||||
savedSearchService,
|
||||
getDataViewAndSavedSearchCallback,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
resolveDataSource()
|
||||
.then((result) => {
|
||||
setValue(result as DataSourceContextValue);
|
||||
})
|
||||
.catch((e) => {
|
||||
setError(e);
|
||||
});
|
||||
}, [resolveDataSource]);
|
||||
|
||||
if (!value && !error) return null;
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<EuiEmptyPrompt
|
||||
iconType="error"
|
||||
color="danger"
|
||||
title={
|
||||
<h2>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.dataSourceContext.errorTitle"
|
||||
defaultMessage="Unable to fetch data view or saved search"
|
||||
/>
|
||||
</h2>
|
||||
}
|
||||
body={<p>{error.message}</p>}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <DataSourceContext.Provider value={value!}>{children}</DataSourceContext.Provider>;
|
||||
};
|
||||
|
||||
export const useDataSource = () => {
|
||||
return useContext(DataSourceContext);
|
||||
};
|
|
@ -5,6 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export type { MlContextValue, SavedSearchQuery } from './ml_context';
|
||||
export { MlContext } from './ml_context';
|
||||
export { useMlContext } from './use_ml_context';
|
||||
export { DataSourceContextProvider, useDataSource } from './data_source_context';
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import type { SavedSearch } from '@kbn/saved-search-plugin/public';
|
||||
import type { SavedSearchSavedObject } from '../../../../common/types/kibana';
|
||||
import type { MlServicesContext } from '../../app';
|
||||
|
||||
export interface MlContextValue {
|
||||
combinedQuery: any;
|
||||
currentDataView: DataView; // TODO this should be DataView or null
|
||||
// @deprecated currentSavedSearch is of SavedSearchSavedObject type, change to selectedSavedSearch
|
||||
deprecatedSavedSearchObj: SavedSearchSavedObject | null;
|
||||
selectedSavedSearch: SavedSearch | null;
|
||||
dataViewsContract: DataViewsContract;
|
||||
kibanaConfig: any; // IUiSettingsClient;
|
||||
kibanaVersion: string;
|
||||
}
|
||||
|
||||
export type SavedSearchQuery = object;
|
||||
|
||||
// In tests, these custom hooks must not be mocked,
|
||||
// instead <UiChrome.Provider value="mocked-value">` needs
|
||||
// to be used. This guarantees that we have both properly set up
|
||||
// TypeScript support and runtime checks for these dependencies.
|
||||
// Multiple custom hooks can be created to access subsets of
|
||||
// the overall context value if necessary too,
|
||||
// see useCurrentIndexPattern() for example.
|
||||
export const MlContext = React.createContext<Partial<MlContextValue & MlServicesContext>>({});
|
|
@ -1,20 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { MlContext } from './ml_context';
|
||||
|
||||
export const useCurrentIndexPattern = () => {
|
||||
const context = useContext(MlContext);
|
||||
|
||||
if (context.currentDataView === undefined) {
|
||||
throw new Error('currentDataView is undefined');
|
||||
}
|
||||
|
||||
return context.currentDataView;
|
||||
};
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { useContext } from 'react';
|
||||
|
||||
import { MlContext, MlContextValue } from './ml_context';
|
||||
|
||||
export const useMlContext = () => {
|
||||
const context = useContext(MlContext);
|
||||
|
||||
if (
|
||||
context.combinedQuery === undefined ||
|
||||
context.currentDataView === undefined ||
|
||||
// @deprecated currentSavedSearch is of SavedSearchSavedObject type
|
||||
// and should be migrated to selectedSavedSearch
|
||||
context.deprecatedSavedSearchObj === undefined ||
|
||||
context.selectedSavedSearch === undefined ||
|
||||
context.dataViewsContract === undefined ||
|
||||
context.kibanaConfig === undefined
|
||||
) {
|
||||
throw new Error('required attribute is undefined');
|
||||
}
|
||||
|
||||
return context as MlContextValue;
|
||||
};
|
|
@ -17,15 +17,15 @@ import {
|
|||
type TrackTotalHitsSearchResponse,
|
||||
ANALYSIS_CONFIG_TYPE,
|
||||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { Dictionary } from '../../../../common/types/common';
|
||||
import { SavedSearchQuery } from '../../contexts/ml';
|
||||
|
||||
export type IndexPattern = string;
|
||||
|
||||
export interface LoadExploreDataArg {
|
||||
filterByIsTraining?: boolean;
|
||||
searchQuery: SavedSearchQuery;
|
||||
searchQuery: estypes.QueryDslQueryContainer;
|
||||
}
|
||||
|
||||
export interface ClassificationMetricItem {
|
||||
|
@ -53,7 +53,7 @@ export const getDefaultTrainingFilterQuery = (resultsField: string, isTraining:
|
|||
|
||||
export interface SearchQuery {
|
||||
track_total_hits?: boolean;
|
||||
query: SavedSearchQuery;
|
||||
query: estypes.QueryDslQueryContainer;
|
||||
sort?: any;
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,10 @@ interface QueryStringQuery {
|
|||
query_string: Dictionary<any>;
|
||||
}
|
||||
|
||||
export type ResultsSearchQuery = ResultsSearchBoolQuery | ResultsSearchTermQuery | SavedSearchQuery;
|
||||
export type ResultsSearchQuery =
|
||||
| ResultsSearchBoolQuery
|
||||
| ResultsSearchTermQuery
|
||||
| estypes.QueryDslQueryContainer;
|
||||
|
||||
export function getEvalQueryBody({
|
||||
resultsField,
|
||||
|
@ -369,7 +372,7 @@ export const loadEvalData = async ({
|
|||
interface LoadDocsCountConfig {
|
||||
ignoreDefaultQuery?: boolean;
|
||||
isTraining?: boolean;
|
||||
searchQuery: SavedSearchQuery;
|
||||
searchQuery: estypes.QueryDslQueryContainer;
|
||||
resultsField: string;
|
||||
destIndex: string;
|
||||
}
|
||||
|
|
|
@ -18,12 +18,10 @@ import {
|
|||
import { ml } from '../../services/ml_api_service';
|
||||
import { newJobCapsServiceAnalytics } from '../../services/new_job_capabilities/new_job_capabilities_service_analytics';
|
||||
|
||||
import { SavedSearchQuery } from '../../contexts/ml';
|
||||
|
||||
export const getIndexData = async (
|
||||
jobConfig: DataFrameAnalyticsConfig | undefined,
|
||||
dataGrid: UseDataGridReturnType,
|
||||
searchQuery: SavedSearchQuery,
|
||||
searchQuery: estypes.QueryDslQueryContainer,
|
||||
options: { didCancel: boolean }
|
||||
) => {
|
||||
if (jobConfig !== undefined) {
|
||||
|
|
|
@ -19,10 +19,10 @@ import {
|
|||
type TotalFeatureImportance,
|
||||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
|
||||
import { useMlKibana } from '../../contexts/kibana';
|
||||
import { getDataViewIdFromName } from '../../util/index_utils';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
import { newJobCapsServiceAnalytics } from '../../services/new_job_capabilities/new_job_capabilities_service_analytics';
|
||||
import { useMlContext } from '../../contexts/ml';
|
||||
|
||||
import { isGetDataFrameAnalyticsStatsResponseOk } from '../pages/analytics_management/services/analytics_service/get_analytics';
|
||||
import { useTrainedModelsApiService } from '../../services/ml_api_service/trained_models';
|
||||
|
@ -30,7 +30,11 @@ import { getToastNotificationService } from '../../services/toast_notification_s
|
|||
import { getDestinationIndex } from './get_destination_index';
|
||||
|
||||
export const useResultsViewConfig = (jobId: string) => {
|
||||
const mlContext = useMlContext();
|
||||
const {
|
||||
services: {
|
||||
data: { dataViews },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const trainedModelsApiService = useTrainedModelsApiService();
|
||||
|
||||
const [indexPattern, setIndexPattern] = useState<DataView | undefined>(undefined);
|
||||
|
@ -99,13 +103,13 @@ export const useResultsViewConfig = (jobId: string) => {
|
|||
let dataView: DataView | undefined;
|
||||
|
||||
try {
|
||||
dataView = await mlContext.dataViewsContract.get(destDataViewId);
|
||||
dataView = await dataViews.get(destDataViewId);
|
||||
|
||||
// Force refreshing the fields list here because a user directly coming
|
||||
// from the job creation wizard might land on the page without the
|
||||
// data view being fully initialized because it was created
|
||||
// before the analytics job populated the destination index.
|
||||
await mlContext.dataViewsContract.refreshFields(dataView);
|
||||
await dataViews.refreshFields(dataView);
|
||||
} catch (e) {
|
||||
dataView = undefined;
|
||||
}
|
||||
|
@ -115,7 +119,7 @@ export const useResultsViewConfig = (jobId: string) => {
|
|||
const sourceIndex = jobConfigUpdate.source.index[0];
|
||||
const sourceDataViewId = (await getDataViewIdFromName(sourceIndex)) ?? sourceIndex;
|
||||
try {
|
||||
dataView = await mlContext.dataViewsContract.get(sourceDataViewId);
|
||||
dataView = await dataViews.get(sourceDataViewId);
|
||||
} catch (e) {
|
||||
dataView = undefined;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,11 @@ import {
|
|||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import { ANALYSIS_CONFIG_TYPE } from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { useDataSource } from '../../../../../contexts/ml/data_source_context';
|
||||
import {
|
||||
State,
|
||||
UNSET_CONFIG_ITEM,
|
||||
} from '../../../analytics_management/hooks/use_create_analytics_form/state';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { ANALYTICS_STEPS } from '../../page';
|
||||
|
||||
const MAX_INCLUDES_LENGTH = 5;
|
||||
|
@ -30,8 +30,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export const ConfigurationStepDetails: FC<Props> = ({ setCurrentStep, state }) => {
|
||||
const mlContext = useMlContext();
|
||||
const { currentDataView } = mlContext;
|
||||
const { currentDataView } = useDataSource();
|
||||
const { form, isJobCreated } = state;
|
||||
const { dependentVariable, includes, jobConfigQueryString, jobType, trainingPercent } = form;
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ import {
|
|||
} from '../../../../../components/field_stats_flyout';
|
||||
import { FieldForStats } from '../../../../../components/field_stats_flyout/field_stats_info_button';
|
||||
import { newJobCapsServiceAnalytics } from '../../../../../services/new_job_capabilities/new_job_capabilities_service_analytics';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../../contexts/ml';
|
||||
|
||||
import { getScatterplotMatrixLegendType } from '../../../../common/get_scatterplot_matrix_legend_type';
|
||||
import { AnalyticsJobType } from '../../../analytics_management/hooks/use_create_analytics_form/state';
|
||||
|
@ -120,8 +120,7 @@ export const ConfigurationStepForm: FC<ConfigurationStepProps> = ({
|
|||
state,
|
||||
setCurrentStep,
|
||||
}) => {
|
||||
const mlContext = useMlContext();
|
||||
const { currentDataView, selectedSavedSearch } = mlContext;
|
||||
const { currentDataView, selectedSavedSearch } = useDataSource();
|
||||
const { savedSearchQuery, savedSearchQueryStr } = useSavedSearch();
|
||||
|
||||
const [fieldOptionsFetchFail, setFieldOptionsFetchFail] = useState<boolean>(false);
|
||||
|
|
|
@ -15,7 +15,8 @@ import {
|
|||
Query,
|
||||
toElasticsearchQuery,
|
||||
} from '@kbn/es-query';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { useDataSource } from '../../../../../contexts/ml';
|
||||
import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search';
|
||||
|
||||
// `undefined` is used for a non-initialized state
|
||||
|
@ -33,8 +34,11 @@ export function useSavedSearch() {
|
|||
const [savedSearchQuery, setSavedSearchQuery] = useState<SavedSearchQuery>(undefined);
|
||||
const [savedSearchQueryStr, setSavedSearchQueryStr] = useState<SavedSearchQueryStr>(undefined);
|
||||
|
||||
const mlContext = useMlContext();
|
||||
const { currentDataView, kibanaConfig, selectedSavedSearch } = mlContext;
|
||||
const {
|
||||
services: { uiSettings },
|
||||
} = useMlKibana();
|
||||
|
||||
const { currentDataView, selectedSavedSearch } = useDataSource();
|
||||
|
||||
const getQueryData = () => {
|
||||
let qry: any = {};
|
||||
|
@ -75,7 +79,7 @@ export function useSavedSearch() {
|
|||
qry.bool.must_not = [...qry.bool.must_not, ...filterQuery.must_not];
|
||||
} else {
|
||||
qry = buildEsQuery(currentDataView, [query], filter);
|
||||
decorateQuery(qry, kibanaConfig.get('query:queryString:options'));
|
||||
decorateQuery(qry, uiSettings.get('query:queryString:options'));
|
||||
}
|
||||
|
||||
setSavedSearchQuery(qry);
|
||||
|
|
|
@ -17,7 +17,7 @@ import { JOB_ID_MAX_LENGTH } from '../../../../../../../common/constants/validat
|
|||
import { ContinueButton } from '../continue_button';
|
||||
import { ANALYTICS_STEPS } from '../../page';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../../contexts/ml';
|
||||
import { DetailsStepTimeField } from './details_step_time_field';
|
||||
|
||||
const DEFAULT_RESULTS_FIELD = 'ml';
|
||||
|
@ -39,8 +39,7 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
services: { docLinks, notifications },
|
||||
} = useMlKibana();
|
||||
|
||||
const mlContext = useMlContext();
|
||||
const { currentDataView } = mlContext;
|
||||
const { currentDataView } = useDataSource();
|
||||
|
||||
const createIndexLink = docLinks.links.apis.createIndex;
|
||||
const { setFormState } = actions;
|
||||
|
|
|
@ -23,7 +23,7 @@ import { XJsonMode } from '@kbn/ace';
|
|||
import { XJson } from '@kbn/es-ui-shared-plugin/public';
|
||||
import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
||||
import { getCombinedRuntimeMappings, isRuntimeMappings } from '@kbn/ml-runtime-field-utils';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../../contexts/ml';
|
||||
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
|
||||
import { RuntimeMappingsEditor } from './runtime_mappings_editor';
|
||||
import { SwitchModal } from './switch_modal';
|
||||
|
@ -93,8 +93,7 @@ export const RuntimeMappings: FC<Props> = ({ actions, state }) => {
|
|||
xJson: advancedRuntimeMappingsConfig,
|
||||
} = useXJsonMode(runtimeMappings || '');
|
||||
|
||||
const mlContext = useMlContext();
|
||||
const { currentDataView } = mlContext;
|
||||
const { currentDataView } = useDataSource();
|
||||
|
||||
const applyChanges = () => {
|
||||
const removeRuntimeMappings = advancedRuntimeMappingsConfig === '';
|
||||
|
|
|
@ -19,7 +19,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { DataFrameAnalyticsId } from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { useMlContext } from '../../../contexts/ml';
|
||||
import { useDataSource } from '../../../contexts/ml/data_source_context';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { useCreateAnalyticsForm } from '../analytics_management/hooks/use_create_analytics_form';
|
||||
import { CreateAnalyticsAdvancedEditor } from './components/create_analytics_advanced_editor';
|
||||
|
@ -54,8 +54,7 @@ export const Page: FC<Props> = ({ jobId }) => {
|
|||
false,
|
||||
]);
|
||||
|
||||
const mlContext = useMlContext();
|
||||
const { currentDataView } = mlContext;
|
||||
const { currentDataView } = useDataSource();
|
||||
|
||||
const createAnalyticsForm = useCreateAnalyticsForm();
|
||||
const { state } = createAnalyticsForm;
|
||||
|
|
|
@ -40,11 +40,11 @@ import {
|
|||
type DataFrameAnalyticsConfig,
|
||||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { SEARCH_QUERY_LANGUAGE } from '../../../../../../../common/constants/search';
|
||||
|
||||
import { getToastNotifications } from '../../../../../util/dependency_cache';
|
||||
import { useColorRange, ColorRangeLegend } from '../../../../../components/color_range_legend';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
|
||||
import { defaultSearchQuery, renderCellPopoverFactory, SEARCH_SIZE } from '../../../../common';
|
||||
|
@ -121,7 +121,7 @@ interface ExpandableSectionResultsProps {
|
|||
jobConfig?: DataFrameAnalyticsConfig;
|
||||
needsDestIndexPattern: boolean;
|
||||
resultsField?: string;
|
||||
searchQuery: SavedSearchQuery;
|
||||
searchQuery: estypes.QueryDslQueryContainer;
|
||||
}
|
||||
|
||||
export const ExpandableSectionResults: FC<ExpandableSectionResultsProps> = ({
|
||||
|
|
|
@ -24,14 +24,13 @@ import {
|
|||
SearchQueryLanguage,
|
||||
} from '../../../../../../../common/constants/search';
|
||||
import { removeFilterFromQueryString } from '../../../../../explorer/explorer_utils';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
|
||||
export interface ExplorationQueryBarProps {
|
||||
indexPattern: DataView;
|
||||
setSearchQuery: (update: {
|
||||
queryString: string;
|
||||
query?: SavedSearchQuery;
|
||||
query?: estypes.QueryDslQueryContainer;
|
||||
language: SearchQueryLanguage;
|
||||
}) => void;
|
||||
includeQueryString?: boolean;
|
||||
|
|
|
@ -34,9 +34,9 @@ import {
|
|||
type UseIndexDataReturnType,
|
||||
} from '@kbn/ml-data-grid';
|
||||
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { MlApiServices } from '../../../../../services/ml_api_service';
|
||||
import { DataLoader } from '../../../../../datavisualizer/index_based/data_loader';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
|
||||
import { getIndexData, getIndexFields } from '../../../../common';
|
||||
import { useTrainedModelsApiService } from '../../../../../services/ml_api_service/trained_models';
|
||||
|
@ -45,7 +45,7 @@ import { useExplorationDataGrid } from './use_exploration_data_grid';
|
|||
export const useExplorationResults = (
|
||||
indexPattern: DataView | undefined,
|
||||
jobConfig: DataFrameAnalyticsConfig | undefined,
|
||||
searchQuery: SavedSearchQuery,
|
||||
searchQuery: estypes.QueryDslQueryContainer,
|
||||
toastNotifications: CoreSetup['notifications']['toasts'],
|
||||
mlApiServices: MlApiServices
|
||||
): UseIndexDataReturnType => {
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
import { MlContext } from '../../../../../contexts/ml';
|
||||
import { kibanaContextValueMock } from '../../../../../contexts/ml/__mocks__/kibana_context_value';
|
||||
|
||||
import { OutlierExploration } from './outlier_exploration';
|
||||
import { kibanaContextMock } from '../../../../../contexts/kibana/__mocks__/kibana_context';
|
||||
|
||||
// workaround to make React.memo() work with enzyme
|
||||
jest.mock('react', () => {
|
||||
|
@ -21,9 +20,9 @@ jest.mock('react', () => {
|
|||
describe('Data Frame Analytics: <Exploration />', () => {
|
||||
test('Minimal initialization', () => {
|
||||
const wrapper = shallow(
|
||||
<MlContext.Provider value={kibanaContextValueMock}>
|
||||
<KibanaContextProvider services={kibanaContextMock.services}>
|
||||
<OutlierExploration jobId="the-job-id" />
|
||||
</MlContext.Provider>
|
||||
</KibanaContextProvider>
|
||||
);
|
||||
// Without the jobConfig being loaded, the component will just return empty.
|
||||
expect(wrapper.text()).toMatch('');
|
||||
|
|
|
@ -12,13 +12,13 @@ import { EuiCallOut, EuiPanel, EuiSpacer, EuiText } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { isOutlierAnalysis, FEATURE_INFLUENCE } from '@kbn/ml-data-frame-analytics-utils';
|
||||
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import {
|
||||
useColorRange,
|
||||
COLOR_RANGE,
|
||||
COLOR_RANGE_SCALE,
|
||||
} from '../../../../../components/color_range_legend';
|
||||
import { useScatterplotFieldOptions } from '../../../../../components/scatterplot_matrix';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
|
||||
import { defaultSearchQuery, useResultsViewConfig, getDestinationIndex } from '../../../../common';
|
||||
|
||||
|
@ -45,7 +45,8 @@ export const OutlierExploration: FC<ExplorationProps> = React.memo(({ jobId }) =
|
|||
const { indexPattern, indexPatternErrorMessage, jobConfig, needsDestIndexPattern } =
|
||||
useResultsViewConfig(jobId);
|
||||
const [pageUrlState, setPageUrlState] = useExplorationUrlState();
|
||||
const [searchQuery, setSearchQuery] = useState<SavedSearchQuery>(defaultSearchQuery);
|
||||
const [searchQuery, setSearchQuery] =
|
||||
useState<estypes.QueryDslQueryContainer>(defaultSearchQuery);
|
||||
const outlierData = useOutlierData(indexPattern, jobConfig, searchQuery);
|
||||
|
||||
const searchQueryUpdateHandler: ExplorationQueryBarProps['setSearchQuery'] = useCallback(
|
||||
|
|
|
@ -26,13 +26,13 @@ import {
|
|||
type UseIndexDataReturnType,
|
||||
} from '@kbn/ml-data-grid';
|
||||
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { DataLoader } from '../../../../../datavisualizer/index_based/data_loader';
|
||||
import {
|
||||
useColorRange,
|
||||
COLOR_RANGE,
|
||||
COLOR_RANGE_SCALE,
|
||||
} from '../../../../../components/color_range_legend';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
import { getToastNotifications } from '../../../../../util/dependency_cache';
|
||||
|
||||
import { getIndexData, getIndexFields } from '../../../../common';
|
||||
|
@ -43,7 +43,7 @@ import { useExplorationDataGrid } from '../exploration_results_table/use_explora
|
|||
export const useOutlierData = (
|
||||
indexPattern: DataView | undefined,
|
||||
jobConfig: DataFrameAnalyticsConfig | undefined,
|
||||
searchQuery: SavedSearchQuery
|
||||
searchQuery: estypes.QueryDslQueryContainer
|
||||
): UseIndexDataReturnType => {
|
||||
const needsDestIndexFields =
|
||||
indexPattern !== undefined && indexPattern.title === jobConfig?.source.index[0];
|
||||
|
|
|
@ -26,8 +26,8 @@ import {
|
|||
ANALYSIS_CONFIG_TYPE,
|
||||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { SavedSearchQuery } from '../../../../../contexts/ml';
|
||||
|
||||
import { getValuesFromResponse, loadEvalData, loadDocsCount, Eval } from '../../../../common';
|
||||
import {
|
||||
|
@ -44,7 +44,7 @@ import { EvaluateStat } from './evaluate_stat';
|
|||
interface Props {
|
||||
jobConfig: DataFrameAnalyticsConfig;
|
||||
jobStatus?: DataFrameTaskStateType;
|
||||
searchQuery: SavedSearchQuery;
|
||||
searchQuery: estypes.QueryDslQueryContainer;
|
||||
}
|
||||
|
||||
const EMPTY_STATS = {
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC, useCallback, useState, useEffect } from 'react';
|
||||
import React, { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiInMemoryTable,
|
||||
EuiCallOut,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiInMemoryTable,
|
||||
EuiSearchBar,
|
||||
EuiSearchBarProps,
|
||||
EuiSpacer,
|
||||
|
@ -22,7 +22,7 @@ import {
|
|||
type DataFrameAnalyticsId,
|
||||
} from '@kbn/ml-data-frame-analytics-utils';
|
||||
import { useRefreshAnalyticsList } from '../../../../common';
|
||||
import { checkPermission } from '../../../../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../../../../capabilities/check_capabilities';
|
||||
import { useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
|
||||
|
@ -32,7 +32,7 @@ import {
|
|||
ItemIdToExpandedRowMap,
|
||||
} from './common';
|
||||
import { getAnalyticsFactory } from '../../services/analytics_service';
|
||||
import { getTaskStateBadge, getJobTypeBadge, useColumns } from './use_columns';
|
||||
import { getJobTypeBadge, getTaskStateBadge, useColumns } from './use_columns';
|
||||
import { ExpandedRow } from './expanded_row';
|
||||
import { AnalyticStatsBarStats, StatsBar } from '../../../../../components/stats_bar';
|
||||
import { CreateAnalyticsButton } from '../create_analytics_button';
|
||||
|
@ -121,9 +121,12 @@ export const DataFrameAnalyticsList: FC<Props> = ({
|
|||
|
||||
const refreshObs = useRefresh();
|
||||
|
||||
const disabled =
|
||||
!checkPermission('canCreateDataFrameAnalytics') ||
|
||||
!checkPermission('canStartStopDataFrameAnalytics');
|
||||
const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canStartStopDataFrameAnalytics',
|
||||
]);
|
||||
|
||||
const disabled = !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics;
|
||||
|
||||
const getAnalytics = getAnalyticsFactory(
|
||||
setAnalytics,
|
||||
|
|
|
@ -6,11 +6,8 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { EuiTableActionsColumnType } from '@elastic/eui';
|
||||
|
||||
import { checkPermission } from '../../../../../capabilities/check_capabilities';
|
||||
|
||||
import { usePermissionCheck } from '../../../../../capabilities/check_capabilities';
|
||||
import { DeleteSpaceAwareItemCheckModal } from '../../../../../components/delete_space_aware_item_check_modal';
|
||||
import { useCloneAction } from '../action_clone';
|
||||
import { useDeleteAction, DeleteActionModal } from '../action_delete';
|
||||
|
@ -19,7 +16,6 @@ import { useStartAction, StartActionModal } from '../action_start';
|
|||
import { useStopAction, StopActionModal } from '../action_stop';
|
||||
import { useViewAction } from '../action_view';
|
||||
import { useMapAction } from '../action_map';
|
||||
|
||||
import { DataFrameAnalyticsListRow } from './common';
|
||||
import { useRefreshAnalyticsList } from '../../../../common/analytics';
|
||||
|
||||
|
@ -27,9 +23,12 @@ export const useActions = (): {
|
|||
actions: EuiTableActionsColumnType<DataFrameAnalyticsListRow>['actions'];
|
||||
modals: JSX.Element | null;
|
||||
} => {
|
||||
const canCreateDataFrameAnalytics: boolean = checkPermission('canCreateDataFrameAnalytics');
|
||||
const canDeleteDataFrameAnalytics: boolean = checkPermission('canDeleteDataFrameAnalytics');
|
||||
const canStartStopDataFrameAnalytics: boolean = checkPermission('canStartStopDataFrameAnalytics');
|
||||
const [canCreateDataFrameAnalytics, canDeleteDataFrameAnalytics, canStartStopDataFrameAnalytics] =
|
||||
usePermissionCheck([
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canDeleteDataFrameAnalytics',
|
||||
'canStartStopDataFrameAnalytics',
|
||||
]);
|
||||
|
||||
const viewAction = useViewAction();
|
||||
const mapAction = useMapAction();
|
||||
|
|
|
@ -20,10 +20,9 @@ import { i18n } from '@kbn/i18n';
|
|||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import dfaImage from './data_frame_analytics_kibana.png';
|
||||
import { mlNodesAvailable } from '../../../../../ml_nodes_check';
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana';
|
||||
import { ML_PAGES } from '../../../../../../../common/constants/locator';
|
||||
import { checkPermission } from '../../../../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../../../../capabilities/check_capabilities';
|
||||
|
||||
export const AnalyticsEmptyPrompt: FC = () => {
|
||||
const {
|
||||
|
@ -32,10 +31,14 @@ export const AnalyticsEmptyPrompt: FC = () => {
|
|||
http: { basePath },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([
|
||||
'canCreateDataFrameAnalytics',
|
||||
'canStartStopDataFrameAnalytics',
|
||||
]);
|
||||
|
||||
const disabled =
|
||||
!mlNodesAvailable() ||
|
||||
!checkPermission('canCreateDataFrameAnalytics') ||
|
||||
!checkPermission('canStartStopDataFrameAnalytics');
|
||||
!mlNodesAvailable() || !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics;
|
||||
|
||||
const transformsLink = `${basePath.get()}/app/management/data/transform`;
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
|
|
@ -7,17 +7,17 @@
|
|||
|
||||
import React from 'react';
|
||||
import { mountHook } from '@kbn/test-jest-helpers';
|
||||
|
||||
import { MlContext } from '../../../../../contexts/ml';
|
||||
import { kibanaContextValueMock } from '../../../../../contexts/ml/__mocks__/kibana_context_value';
|
||||
|
||||
import { useCreateAnalyticsForm } from './use_create_analytics_form';
|
||||
import { kibanaContextMock } from '../../../../../contexts/kibana/__mocks__/kibana_context';
|
||||
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';
|
||||
|
||||
const getMountedHook = () =>
|
||||
mountHook(
|
||||
() => useCreateAnalyticsForm(),
|
||||
({ children }) => (
|
||||
<MlContext.Provider value={kibanaContextValueMock}>{children}</MlContext.Provider>
|
||||
<KibanaContextProvider services={kibanaContextMock.services}>
|
||||
{children}
|
||||
</KibanaContextProvider>
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ import { DuplicateDataViewError } from '@kbn/data-plugin/public';
|
|||
import { extractErrorMessage } from '@kbn/ml-error-utils';
|
||||
import type { DataFrameAnalyticsConfig } from '@kbn/ml-data-frame-analytics-utils';
|
||||
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { DeepReadonly } from '../../../../../../../common/types/common';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
|
||||
import { useRefreshAnalyticsList } from '../../../../common';
|
||||
import { extractCloningConfig, isAdvancedConfig } from '../../components/action_clone';
|
||||
|
@ -93,7 +93,11 @@ function delay(ms = 1000) {
|
|||
}
|
||||
|
||||
export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
||||
const mlContext = useMlContext();
|
||||
const {
|
||||
services: {
|
||||
data: { dataViews },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const [state, dispatch] = useReducer(reducer, getInitialState());
|
||||
const { refresh } = useRefreshAnalyticsList();
|
||||
|
||||
|
@ -175,7 +179,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
// index exists - create data view
|
||||
if (exists?.indexExists === true) {
|
||||
try {
|
||||
await mlContext.dataViewsContract.createAndSave(
|
||||
await dataViews.createAndSave(
|
||||
{
|
||||
title: dataViewName,
|
||||
...(form.timeFieldName ? { timeFieldName: form.timeFieldName } : {}),
|
||||
|
@ -264,7 +268,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
try {
|
||||
// Set the existing data view names.
|
||||
const indexPatternsMap: SourceIndexMap = {};
|
||||
const savedObjects = (await mlContext.dataViewsContract.getCache()) || [];
|
||||
const savedObjects = (await dataViews.getCache()) || [];
|
||||
savedObjects.forEach((obj) => {
|
||||
const title = obj?.attributes?.title;
|
||||
if (title !== undefined) {
|
||||
|
@ -286,7 +290,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
};
|
||||
|
||||
const initiateWizard = async () => {
|
||||
await mlContext.dataViewsContract.clearCache();
|
||||
await dataViews.clearCache();
|
||||
await prepareFormValidation();
|
||||
};
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import { DEFAULT_SAMPLER_SHARD_SIZE } from '@kbn/ml-agg-utils';
|
|||
import { OMIT_FIELDS } from '@kbn/ml-anomaly-utils';
|
||||
import { type RuntimeMappings } from '@kbn/ml-runtime-field-utils';
|
||||
|
||||
import { SavedSearchQuery } from '../../../contexts/ml';
|
||||
import * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
|
||||
import { IndexPatternTitle } from '../../../../../common/types/kibana';
|
||||
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
|
@ -35,7 +35,7 @@ export class DataLoader {
|
|||
|
||||
async loadFieldHistograms(
|
||||
fields: FieldHistogramRequestConfig[],
|
||||
query: string | SavedSearchQuery,
|
||||
query: string | estypes.QueryDslQueryContainer,
|
||||
samplerShardSize = DEFAULT_SAMPLER_SHARD_SIZE,
|
||||
editorRuntimeMappings?: RuntimeMappings
|
||||
): Promise<any[]> {
|
||||
|
|
|
@ -76,7 +76,6 @@ import type { ExplorerState } from './reducers';
|
|||
import type { TimeBuckets } from '../util/time_buckets';
|
||||
import { useToastNotificationService } from '../services/toast_notification_service';
|
||||
import { useMlKibana, useMlLocator } from '../contexts/kibana';
|
||||
import { useMlContext } from '../contexts/ml';
|
||||
import { useAnomalyExplorerContext } from './anomaly_explorer_context';
|
||||
import { ML_ANOMALY_EXPLORER_PANELS } from '../../../common/types/storage';
|
||||
|
||||
|
@ -355,12 +354,13 @@ export const Explorer: FC<ExplorerUIProps> = ({
|
|||
}, []);
|
||||
|
||||
const {
|
||||
services: { charts: chartsService },
|
||||
services: {
|
||||
charts: chartsService,
|
||||
data: { dataViews: dataViewsService },
|
||||
},
|
||||
} = useMlKibana();
|
||||
const { euiTheme } = useEuiTheme();
|
||||
const mlLocator = useMlLocator();
|
||||
const context = useMlContext();
|
||||
const dataViewsService = context.dataViewsContract;
|
||||
|
||||
const {
|
||||
annotations,
|
||||
|
|
|
@ -19,11 +19,12 @@ import {
|
|||
import adImage from './anomaly_detection_kibana.png';
|
||||
import { ML_PAGES } from '../../../../../../common/constants/locator';
|
||||
import { useMlKibana, useMlLocator, useNavigateToPath } from '../../../../contexts/kibana';
|
||||
import { checkPermission } from '../../../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../../../../ml_nodes_check';
|
||||
|
||||
export const AnomalyDetectionEmptyState: FC = () => {
|
||||
const disableCreateAnomalyDetectionJob = !checkPermission('canCreateJob') || !mlNodesAvailable();
|
||||
const canCreateJob = usePermissionCheck('canCreateJob');
|
||||
const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable();
|
||||
|
||||
const {
|
||||
services: { docLinks },
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { checkPermission } from '../../../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../../../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes';
|
||||
|
||||
import React from 'react';
|
||||
|
@ -16,7 +16,8 @@ import { useCreateAndNavigateToMlLink } from '../../../../contexts/kibana/use_cr
|
|||
import { ML_PAGES } from '../../../../../../common/constants/locator';
|
||||
|
||||
export function NewJobButton() {
|
||||
const buttonEnabled = checkPermission('canCreateJob') && mlNodesAvailable();
|
||||
const canCreateJob = usePermissionCheck('canCreateJob');
|
||||
const buttonEnabled = canCreateJob && mlNodesAvailable();
|
||||
const newJob = useCreateAndNavigateToMlLink(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX);
|
||||
|
||||
return (
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { FC, Fragment, useEffect, useState } from 'react';
|
|||
import moment, { Moment } from 'moment';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiDatePicker, EuiDatePickerRange } from '@elastic/eui';
|
||||
import { useMlContext } from '../../../../contexts/ml';
|
||||
import { useMlKibana } from '../../../../contexts/kibana';
|
||||
|
||||
const WIDTH = '512px';
|
||||
|
||||
|
@ -24,8 +24,11 @@ interface Props {
|
|||
}
|
||||
|
||||
export const TimeRangePicker: FC<Props> = ({ setTimeRange, timeRange }) => {
|
||||
const mlContext = useMlContext();
|
||||
const dateFormat: string = mlContext.kibanaConfig.get('dateFormat');
|
||||
const {
|
||||
services: { uiSettings },
|
||||
} = useMlKibana();
|
||||
// TODO should use fieldFormats instead
|
||||
const dateFormat: string = uiSettings.get('dateFormat');
|
||||
|
||||
const [startMoment, setStartMoment] = useState<Moment | undefined>(moment(timeRange.start));
|
||||
const [endMoment, setEndMoment] = useState<Moment | undefined>(moment(timeRange.end));
|
||||
|
|
|
@ -21,6 +21,9 @@ import {
|
|||
} from '../../../util/dependency_cache';
|
||||
import { getDefaultQuery } from '../utils/new_job_utils';
|
||||
|
||||
/**
|
||||
* TODO update route resolver to use Kibana context instead of the deps cache
|
||||
*/
|
||||
export async function resolver(
|
||||
lensSavedObjectId: string | undefined,
|
||||
lensSavedObjectRisonString: string | undefined,
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
isRareJobCreator,
|
||||
} from '../../../../../common/job_creator';
|
||||
import { ml } from '../../../../../../../services/ml_api_service';
|
||||
import { useMlContext } from '../../../../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../../../../contexts/ml';
|
||||
import { getToastNotificationService } from '../../../../../../../services/toast_notification_service';
|
||||
|
||||
export enum ESTIMATE_STATUS {
|
||||
|
@ -27,7 +27,7 @@ export enum ESTIMATE_STATUS {
|
|||
|
||||
export function useEstimateBucketSpan() {
|
||||
const { jobCreator, jobCreatorUpdate } = useContext(JobCreatorContext);
|
||||
const mlContext = useMlContext();
|
||||
const mlContext = useDataSource();
|
||||
|
||||
const [status, setStatus] = useState(ESTIMATE_STATUS.NOT_RUNNING);
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import { i18n } from '@kbn/i18n';
|
|||
import moment from 'moment';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiDescriptionList } from '@elastic/eui';
|
||||
import { useMlKibana } from '../../../../../../../contexts/kibana';
|
||||
import { JobCreatorContext } from '../../../job_creator_context';
|
||||
import {
|
||||
isMultiMetricJobCreator,
|
||||
|
@ -18,12 +19,14 @@ import {
|
|||
} from '../../../../../common/job_creator';
|
||||
import { getNewJobDefaults } from '../../../../../../../services/ml_server_info';
|
||||
import { ListItems, falseLabel, trueLabel, defaultLabel, Italic } from '../common';
|
||||
import { useMlContext } from '../../../../../../../contexts/ml';
|
||||
|
||||
export const JobDetails: FC = () => {
|
||||
const { jobCreator } = useContext(JobCreatorContext);
|
||||
const mlContext = useMlContext();
|
||||
const dateFormat: string = mlContext.kibanaConfig.get('dateFormat');
|
||||
const {
|
||||
services: { uiSettings },
|
||||
} = useMlKibana();
|
||||
// TODO should use fieldFormats instead
|
||||
const dateFormat: string = uiSettings.get('dateFormat');
|
||||
const { anomaly_detectors: anomalyDetectors } = getNewJobDefaults();
|
||||
|
||||
const isAdvanced = isAdvancedJobCreator(jobCreator);
|
||||
|
|
|
@ -17,7 +17,7 @@ import { ML_INTERNAL_BASE_PATH } from '../../../../../../../common/constants/app
|
|||
import { WizardNav } from '../wizard_nav';
|
||||
import { StepProps, WIZARD_STEPS } from '../step_types';
|
||||
import { JobCreatorContext } from '../job_creator_context';
|
||||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../../contexts/ml';
|
||||
import { EventRateChart } from '../charts/event_rate_chart';
|
||||
import { LineChartPoint } from '../../../common/chart_loader';
|
||||
import { JOB_TYPE } from '../../../../../../../common/constants/new_job';
|
||||
|
@ -32,7 +32,7 @@ import {
|
|||
export const TimeRangeStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) => {
|
||||
const timefilter = useTimefilter();
|
||||
const { services } = useMlKibana();
|
||||
const mlContext = useMlContext();
|
||||
const mlContext = useDataSource();
|
||||
|
||||
const { jobCreator, jobCreatorUpdate, jobCreatorUpdated, chartLoader, chartInterval } =
|
||||
useContext(JobCreatorContext);
|
||||
|
|
|
@ -20,7 +20,7 @@ import { FormattedMessage } from '@kbn/i18n-react';
|
|||
import { ES_FIELD_TYPES } from '@kbn/field-types';
|
||||
import { useMlKibana, useNavigateToPath } from '../../../../contexts/kibana';
|
||||
|
||||
import { useMlContext } from '../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../contexts/ml';
|
||||
import { DataRecognizer } from '../../../../components/data_recognizer';
|
||||
import { addItemToRecentlyAccessed } from '../../../../util/recently_accessed';
|
||||
import { timeBasedIndexCheck } from '../../../../util/index_utils';
|
||||
|
@ -37,7 +37,7 @@ export const Page: FC = () => {
|
|||
services: { share },
|
||||
} = useMlKibana();
|
||||
|
||||
const mlContext = useMlContext();
|
||||
const mlContext = useDataSource();
|
||||
const navigateToPath = useNavigateToPath();
|
||||
const onSelectDifferentIndex = useCreateAndNavigateToMlLink(
|
||||
ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_INDEX
|
||||
|
|
|
@ -34,7 +34,7 @@ import { ChartLoader } from '../../common/chart_loader';
|
|||
import { MapLoader } from '../../common/map_loader';
|
||||
import { ResultsLoader } from '../../common/results_loader';
|
||||
import { JobValidator } from '../../common/job_validator';
|
||||
import { useMlContext } from '../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../contexts/ml';
|
||||
import { useMlKibana } from '../../../../contexts/kibana';
|
||||
import { ExistingJobsAndGroups, mlJobService } from '../../../../services/job_service';
|
||||
import { newJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service';
|
||||
|
@ -53,7 +53,7 @@ export interface PageProps {
|
|||
|
||||
export const Page: FC<PageProps> = ({ existingJobsAndGroups, jobType }) => {
|
||||
const timefilter = useTimefilter();
|
||||
const mlContext = useMlContext();
|
||||
const mlContext = useDataSource();
|
||||
const {
|
||||
services: { maps: mapsPlugin },
|
||||
} = useMlKibana();
|
||||
|
|
|
@ -24,7 +24,7 @@ import { JobDetailsStep } from '../components/job_details_step';
|
|||
import { ValidationStep } from '../components/validation_step';
|
||||
import { SummaryStep } from '../components/summary_step';
|
||||
import { DatafeedStep } from '../components/datafeed_step';
|
||||
import { useMlContext } from '../../../../contexts/ml';
|
||||
import { useDataSource } from '../../../../contexts/ml';
|
||||
|
||||
interface Props {
|
||||
currentStep: WIZARD_STEPS;
|
||||
|
@ -32,7 +32,7 @@ interface Props {
|
|||
}
|
||||
|
||||
export const WizardSteps: FC<Props> = ({ currentStep, setCurrentStep }) => {
|
||||
const mlContext = useMlContext();
|
||||
const mlContext = useDataSource();
|
||||
const { services } = useMlKibana();
|
||||
const fieldStatsServices: FieldStatsServices = useMemo(() => {
|
||||
const { uiSettings, data, fieldFormats, charts } = services;
|
||||
|
|
|
@ -20,8 +20,8 @@ import {
|
|||
EuiTextAlign,
|
||||
} from '@elastic/eui';
|
||||
import { getTimeFilterRange, useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { useDataSource } from '../../../../contexts/ml/data_source_context';
|
||||
import { ModuleJobUI, SAVE_STATE } from '../page';
|
||||
import { useMlContext } from '../../../../contexts/ml';
|
||||
import {
|
||||
composeValidators,
|
||||
maxLengthValidator,
|
||||
|
@ -53,7 +53,7 @@ export const JobSettingsForm: FC<JobSettingsFormProps> = ({
|
|||
}) => {
|
||||
const timefilter = useTimefilter();
|
||||
const { from, to } = getTimeFilterRange(timefilter);
|
||||
const { currentDataView: dataView } = useMlContext();
|
||||
const { currentDataView: dataView } = useDataSource();
|
||||
|
||||
const jobPrefixValidator = useMemo(
|
||||
() =>
|
||||
|
|
|
@ -23,8 +23,8 @@ import { isPopulatedObject } from '@kbn/ml-is-populated-object';
|
|||
import { addExcludeFrozenToQuery } from '@kbn/ml-query-utils';
|
||||
import { TIME_FORMAT } from '@kbn/ml-date-utils';
|
||||
import { type RuntimeMappings } from '@kbn/ml-runtime-field-utils';
|
||||
import { useDataSource } from '../../../contexts/ml';
|
||||
import { useMlKibana, useMlLocator } from '../../../contexts/kibana';
|
||||
import { useMlContext } from '../../../contexts/ml';
|
||||
import {
|
||||
DatafeedResponse,
|
||||
JobOverride,
|
||||
|
@ -92,7 +92,7 @@ export const Page: FC<PageProps> = ({ moduleId, existingGroupIds }) => {
|
|||
const [jobsAwaitingNodeCount, setJobsAwaitingNodeCount] = useState(0);
|
||||
// #endregion
|
||||
|
||||
const { selectedSavedSearch, currentDataView: dataView, combinedQuery } = useMlContext();
|
||||
const { selectedSavedSearch, currentDataView: dataView, combinedQuery } = useDataSource();
|
||||
const pageTitle = selectedSavedSearch
|
||||
? i18n.translate('xpack.ml.newJob.recognize.savedSearchPageTitle', {
|
||||
defaultMessage: 'saved search {savedSearchTitle}',
|
||||
|
|
|
@ -5,60 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { getToastNotifications, getSavedObjectsClient } from '../../../util/dependency_cache';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { getSavedObjectsClient } from '../../../util/dependency_cache';
|
||||
import { KibanaObjects } from './page';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { CreateLinkWithUserDefaults } from '../../../components/custom_hooks/use_create_ad_links';
|
||||
|
||||
/**
|
||||
* Checks whether the jobs in a data recognizer module have been created.
|
||||
* Redirects to the Anomaly Explorer to view the jobs if they have been created,
|
||||
* or the recognizer job wizard for the module if not.
|
||||
*/
|
||||
export function checkViewOrCreateJobs(
|
||||
moduleId: string,
|
||||
dataViewId: string,
|
||||
createLinkWithUserDefaults: CreateLinkWithUserDefaults,
|
||||
navigateToPath: NavigateToPath
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Load the module, and check if the job(s) in the module have been created.
|
||||
// If so, load the jobs in the Anomaly Explorer.
|
||||
// Otherwise open the data recognizer wizard for the module.
|
||||
// Always want to call reject() so as not to load original page.
|
||||
ml.dataRecognizerModuleJobsExist({ moduleId })
|
||||
.then(async (resp: any) => {
|
||||
if (resp.jobsExist === true) {
|
||||
// also honor user's time filter setting in Advanced Settings
|
||||
const url = createLinkWithUserDefaults('explorer', resp.jobs);
|
||||
await navigateToPath(url);
|
||||
reject();
|
||||
} else {
|
||||
await navigateToPath(`/jobs/new_job/recognize?id=${moduleId}&index=${dataViewId}`);
|
||||
reject();
|
||||
}
|
||||
})
|
||||
.catch(async (err: Error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Error checking whether jobs in module ${moduleId} exists`, err);
|
||||
const toastNotifications = getToastNotifications();
|
||||
toastNotifications.addWarning({
|
||||
title: i18n.translate('xpack.ml.newJob.recognize.moduleCheckJobsExistWarningTitle', {
|
||||
defaultMessage: 'Error checking module {moduleId}',
|
||||
values: { moduleId },
|
||||
}),
|
||||
text: i18n.translate('xpack.ml.newJob.recognize.moduleCheckJobsExistWarningDescription', {
|
||||
defaultMessage:
|
||||
'An error occurred trying to check whether the jobs in the module have been created.',
|
||||
}),
|
||||
});
|
||||
await navigateToPath(`/jobs`);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets kibana objects with an existence check.
|
||||
|
|
|
@ -5,69 +5,22 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { LicensingPluginStart } from '@kbn/licensing-plugin/public';
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { MlLicense } from '../../../common/license';
|
||||
import { MlClientLicense } from './ml_client_license';
|
||||
|
||||
let mlLicense: MlClientLicense | null = null;
|
||||
let mlLicense: MlLicense | null = null;
|
||||
|
||||
/**
|
||||
* Create a new mlLicense and cache it for later checks
|
||||
*
|
||||
* @export
|
||||
* @param {LicensingPluginStart} licensingStart
|
||||
* @param application
|
||||
* @param postInitFunctions
|
||||
* @returns {MlClientLicense}
|
||||
* Cache ml license to support legacy usage.
|
||||
*/
|
||||
export function setLicenseCache(
|
||||
licensingStart: LicensingPluginStart,
|
||||
application: CoreStart['application'],
|
||||
callback?: (lic: MlLicense) => void
|
||||
) {
|
||||
mlLicense = new MlClientLicense(application);
|
||||
mlLicense.setup(licensingStart.license$, callback);
|
||||
export function setLicenseCache(mlLicenseInstance: MlLicense) {
|
||||
mlLicense = mlLicenseInstance;
|
||||
return mlLicense;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as routing resolver to stop the loading of a page if the current license
|
||||
* is a trial, platinum or enterprise.
|
||||
*
|
||||
* @export
|
||||
* @returns {Promise<void>} Promise which resolves if the license is trial, platinum or enterprise and rejects if it isn't.
|
||||
*/
|
||||
export async function checkFullLicense() {
|
||||
if (mlLicense === null) {
|
||||
// this should never happen
|
||||
console.error('ML Licensing not initialized'); // eslint-disable-line no-console
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return mlLicense.fullLicenseResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as routing resolver to stop the loading of a page if the current license
|
||||
* is at least basic.
|
||||
*
|
||||
* @export
|
||||
* @returns {Promise<void>} Promise resolves if the license is at least basic and rejects if it isn't.
|
||||
*/
|
||||
export async function checkBasicLicense() {
|
||||
if (mlLicense === null) {
|
||||
// this should never happen
|
||||
console.error('ML Licensing not initialized'); // eslint-disable-line no-console
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
return mlLicense.basicLicenseResolver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the current license has expired
|
||||
*
|
||||
* @deprecated
|
||||
* @export
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
@ -78,6 +31,7 @@ export function hasLicenseExpired() {
|
|||
/**
|
||||
* Check to see if the current license is trial, platinum or enterprise.
|
||||
*
|
||||
* @deprecated
|
||||
* @export
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
@ -90,6 +44,7 @@ export function isFullLicense() {
|
|||
* Note, this is not accurate for cloud trials.
|
||||
* For cloud trials use isCloudTrial returned from the mlInfo endpoint
|
||||
*
|
||||
* @deprecated
|
||||
* @export
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
import { toMountPoint, wrapWithTheme } from '@kbn/kibana-react-plugin/public';
|
||||
import { getOverlays, getTheme } from '../util/dependency_cache';
|
||||
|
||||
let expiredLicenseBannerId: string;
|
||||
|
||||
export function showExpiredLicenseWarning() {
|
||||
if (expiredLicenseBannerId === undefined) {
|
||||
const message = i18n.translate('xpack.ml.checkLicense.licenseHasExpiredMessage', {
|
||||
defaultMessage: 'Your Machine Learning license has expired.',
|
||||
});
|
||||
// Only show the banner once with no way to dismiss it
|
||||
const overlays = getOverlays();
|
||||
const theme = getTheme();
|
||||
|
||||
expiredLicenseBannerId = overlays.banners.add(
|
||||
toMountPoint(
|
||||
wrapWithTheme(
|
||||
<EuiCallOut iconType="iInCircle" color="warning" title={message} />,
|
||||
theme.theme$
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -5,10 +5,4 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
export {
|
||||
checkBasicLicense,
|
||||
checkFullLicense,
|
||||
hasLicenseExpired,
|
||||
isFullLicense,
|
||||
setLicenseCache,
|
||||
} from './check_license';
|
||||
export { hasLicenseExpired, isFullLicense, setLicenseCache } from './check_license';
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { ILicense } from '@kbn/licensing-plugin/common/types';
|
||||
|
||||
import { MlClientLicense } from './ml_client_license';
|
||||
import { applicationServiceMock } from '@kbn/core/public/mocks';
|
||||
|
||||
describe('MlClientLicense', () => {
|
||||
const startApplicationContractMock = applicationServiceMock.createStartContract();
|
||||
|
||||
test('should miss the license update when initialized without postInitFunction', () => {
|
||||
const mlLicense = new MlClientLicense(startApplicationContractMock);
|
||||
|
||||
// upon instantiation the full license doesn't get set
|
||||
expect(mlLicense.isFullLicense()).toBe(false);
|
||||
|
||||
const license$ = new Subject();
|
||||
|
||||
mlLicense.setup(license$ as Observable<ILicense>);
|
||||
|
||||
// if the observable wasn't triggered the full license is still not set
|
||||
expect(mlLicense.isFullLicense()).toBe(false);
|
||||
|
||||
license$.next({
|
||||
check: () => ({ state: 'valid' }),
|
||||
getFeature: () => ({ isEnabled: true }),
|
||||
status: 'valid',
|
||||
});
|
||||
|
||||
// once the observable triggered the license should be set
|
||||
expect(mlLicense.isFullLicense()).toBe(true);
|
||||
});
|
||||
|
||||
test('should not miss the license update when initialized with postInitFunction', (done) => {
|
||||
const mlLicense = new MlClientLicense(startApplicationContractMock);
|
||||
|
||||
// upon instantiation the full license doesn't get set
|
||||
expect(mlLicense.isFullLicense()).toBe(false);
|
||||
|
||||
const license$ = new Subject();
|
||||
|
||||
mlLicense.setup(license$ as Observable<ILicense>, (license) => {
|
||||
// when passed in via postInitFunction callback, the license should be valid
|
||||
// even if the license$ observable gets triggered after this setup.
|
||||
expect(license.isFullLicense()).toBe(true);
|
||||
done();
|
||||
});
|
||||
|
||||
license$.next({
|
||||
check: () => ({ state: 'valid' }),
|
||||
getFeature: () => ({ isEnabled: true }),
|
||||
status: 'valid',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { CoreStart } from '@kbn/core/public';
|
||||
import { ML_PAGES } from '../../../common/constants/locator';
|
||||
import { MlLicense } from '../../../common/license';
|
||||
import { showExpiredLicenseWarning } from './expired_warning';
|
||||
import { PLUGIN_ID } from '../../../common/constants/app';
|
||||
|
||||
export class MlClientLicense extends MlLicense {
|
||||
constructor(private application: CoreStart['application']) {
|
||||
super();
|
||||
}
|
||||
|
||||
private redirectToKibana() {
|
||||
this.application.navigateToApp('home');
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
private redirectToBasic() {
|
||||
this.application.navigateToApp(PLUGIN_ID, { path: ML_PAGES.DATA_VISUALIZER });
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
fullLicenseResolver(): Promise<void> {
|
||||
if (this.isMlEnabled() === false || this.isMinimumLicense() === false) {
|
||||
// ML is not enabled or the license isn't at least basic
|
||||
return this.redirectToKibana();
|
||||
}
|
||||
|
||||
if (this.isFullLicense() === false) {
|
||||
// ML is enabled, but only with a basic or gold license
|
||||
return this.redirectToBasic();
|
||||
}
|
||||
|
||||
// ML is enabled
|
||||
if (this.hasLicenseExpired()) {
|
||||
showExpiredLicenseWarning();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
basicLicenseResolver() {
|
||||
if (this.isMlEnabled() === false || this.isMinimumLicense() === false) {
|
||||
// ML is not enabled or the license isn't at least basic
|
||||
return this.redirectToKibana();
|
||||
}
|
||||
|
||||
// ML is enabled
|
||||
if (this.hasLicenseExpired()) {
|
||||
showExpiredLicenseWarning();
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
|
@ -38,15 +38,10 @@ export interface Group {
|
|||
|
||||
interface Props {
|
||||
anomalyTimelineService: AnomalyTimelineService;
|
||||
jobCreationDisabled: boolean;
|
||||
setLazyJobCount: React.Dispatch<React.SetStateAction<number>>;
|
||||
}
|
||||
|
||||
export const AnomalyDetectionPanel: FC<Props> = ({
|
||||
anomalyTimelineService,
|
||||
jobCreationDisabled,
|
||||
setLazyJobCount,
|
||||
}) => {
|
||||
export const AnomalyDetectionPanel: FC<Props> = ({ anomalyTimelineService, setLazyJobCount }) => {
|
||||
const {
|
||||
services: { charts: chartsService },
|
||||
} = useMlKibana();
|
||||
|
|
|
@ -51,7 +51,6 @@ export const OverviewContent: FC<Props> = ({
|
|||
<>
|
||||
<AnomalyDetectionPanel
|
||||
anomalyTimelineService={anomalyTimelineService}
|
||||
jobCreationDisabled={createAnomalyDetectionJobDisabled}
|
||||
setLazyJobCount={setAdLazyJobCount}
|
||||
/>
|
||||
<EuiSpacer size="m" />
|
||||
|
|
|
@ -9,7 +9,7 @@ import React, { FC, useState } from 'react';
|
|||
import { EuiPanel, EuiSpacer } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { checkPermission } from '../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../capabilities/check_capabilities';
|
||||
import { mlNodesAvailable } from '../ml_nodes_check';
|
||||
import { OverviewContent } from './components/content';
|
||||
import { NodeAvailableWarning } from '../components/node_available_warning';
|
||||
|
@ -25,9 +25,9 @@ import { useIsServerless } from '../contexts/kibana/use_is_serverless';
|
|||
|
||||
export const OverviewPage: FC = () => {
|
||||
const serverless = useIsServerless();
|
||||
const canViewMlNodes = checkPermission('canViewMlNodes');
|
||||
const [canViewMlNodes, canCreateJob] = usePermissionCheck(['canViewMlNodes', 'canCreateJob']);
|
||||
|
||||
const disableCreateAnomalyDetectionJob = !checkPermission('canCreateJob') || !mlNodesAvailable();
|
||||
const disableCreateAnomalyDetectionJob = !canCreateJob || !mlNodesAvailable();
|
||||
const {
|
||||
services: { docLinks },
|
||||
} = useMlKibana();
|
||||
|
|
|
@ -5,33 +5,19 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
||||
import { cacheDataViewsContract, loadSavedSearches } from '../util/index_utils';
|
||||
import { checkFullLicense } from '../license';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../ml_nodes_check/check_ml_nodes';
|
||||
import { loadMlServerInfo } from '../services/ml_server_info';
|
||||
|
||||
export interface Resolvers {
|
||||
[name: string]: () => Promise<any>;
|
||||
}
|
||||
export interface ResolverResults {
|
||||
[name: string]: any;
|
||||
}
|
||||
export type ResolverResults =
|
||||
| {
|
||||
[name: string]: any;
|
||||
}
|
||||
| undefined;
|
||||
|
||||
interface BasicResolverDependencies {
|
||||
dataViewsContract: DataViewsContract;
|
||||
redirectToMlAccessDeniedPage: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const basicResolvers = ({
|
||||
dataViewsContract,
|
||||
redirectToMlAccessDeniedPage,
|
||||
}: BasicResolverDependencies): Resolvers => ({
|
||||
checkFullLicense,
|
||||
export const basicResolvers = (): Resolvers => ({
|
||||
getMlNodeCount,
|
||||
loadMlServerInfo,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(dataViewsContract),
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
loadSavedSearches,
|
||||
});
|
||||
|
|
|
@ -20,10 +20,10 @@ import type { DataViewsContract } from '@kbn/data-views-plugin/public';
|
|||
import { EuiSkeletonText } from '@elastic/eui';
|
||||
import { UrlStateProvider } from '@kbn/ml-url-state';
|
||||
import { MlNotificationsContextProvider } from '../contexts/ml/ml_notifications_context';
|
||||
import { MlContext, MlContextValue } from '../contexts/ml';
|
||||
|
||||
import { MlPage } from '../components/ml_page';
|
||||
import { MlPages } from '../../locator';
|
||||
import { type RouteResolverContext } from './use_resolver';
|
||||
|
||||
// custom RouteProps making location non-optional
|
||||
interface MlRouteProps extends RouteProps {
|
||||
|
@ -64,13 +64,18 @@ export interface PageDependencies {
|
|||
setHeaderActionMenu: AppMountParameters['setHeaderActionMenu'];
|
||||
dataViewsContract: DataViewsContract;
|
||||
setBreadcrumbs: ChromeStart['setBreadcrumbs'];
|
||||
redirectToMlAccessDeniedPage: () => Promise<void>;
|
||||
}
|
||||
|
||||
export const PageLoader: FC<{ context: MlContextValue }> = ({ context, children }) => {
|
||||
export const PageLoader: FC<{ context: RouteResolverContext }> = ({ context, children }) => {
|
||||
const isLoading = !context.initialized;
|
||||
|
||||
if (context?.resolvedComponent) {
|
||||
return context.resolvedComponent;
|
||||
}
|
||||
|
||||
return (
|
||||
<EuiSkeletonText lines={10} isLoading={context === null}>
|
||||
<MlContext.Provider value={context}>{children}</MlContext.Provider>
|
||||
<EuiSkeletonText lines={10} isLoading={isLoading}>
|
||||
{!isLoading ? children : null}
|
||||
</EuiSkeletonText>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../router';
|
||||
import { useResolver } from '../use_resolver';
|
||||
import { Page } from '../../access_denied';
|
||||
|
||||
const breadcrumbs = [
|
||||
{
|
||||
text: i18n.translate('xpack.ml.accessDeniedLabel', {
|
||||
defaultMessage: 'Access denied',
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
export const accessDeniedRouteFactory = (): MlRoute => ({
|
||||
path: createPath(ML_PAGES.ACCESS_DENIED),
|
||||
title: i18n.translate('xpack.ml.accessDeniedLabel', {
|
||||
defaultMessage: 'Access denied',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
breadcrumbs,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {});
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
|
@ -8,15 +8,13 @@
|
|||
import { CHANGE_POINT_DETECTION_ENABLED } from '@kbn/aiops-plugin/common';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React, { FC } from 'react';
|
||||
import { parse } from 'query-string';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { MlRoute } from '../..';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { createPath, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { cacheDataViewsContract } from '../../../util/index_utils';
|
||||
import { createPath, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { ChangePointDetectionPage as Page } from '../../../aiops';
|
||||
|
||||
export const changePointDetectionRouteFactory = (
|
||||
|
@ -28,7 +26,7 @@ export const changePointDetectionRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.aiops.changePointDetection.docTitle', {
|
||||
defaultMessage: 'Change point detection',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('AIOPS_BREADCRUMB_CHANGE_POINT_DETECTION', navigateToPath, basePath),
|
||||
|
@ -41,16 +39,14 @@ export const changePointDetectionRouteFactory = (
|
|||
disabled: !CHANGE_POINT_DETECTION_ENABLED,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context } = useResolver(index, savedSearchId, deps.config, deps.dataViewsContract, {
|
||||
checkBasicLicense,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(deps.dataViewsContract),
|
||||
});
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canUseAiops']);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
<DataSourceContextProvider>
|
||||
<Page />
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,23 +6,15 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { AIOPS_ENABLED } from '@kbn/aiops-plugin/common';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { ExplainLogRateSpikesPage as Page } from '../../../aiops/explain_log_rate_spikes';
|
||||
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities';
|
||||
import { cacheDataViewsContract } from '../../../util/index_utils';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
export const explainLogRateSpikesRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
@ -33,7 +25,7 @@ export const explainLogRateSpikesRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.aiops.explainLogRateSpikes.docTitle', {
|
||||
defaultMessage: 'Explain log rate spikes',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp(
|
||||
|
@ -50,19 +42,14 @@ export const explainLogRateSpikesRouteFactory = (
|
|||
disabled: !AIOPS_ENABLED,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps, ...restProps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context } = useResolver(index, savedSearchId, deps.config, deps.dataViewsContract, {
|
||||
checkBasicLicense,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(deps.dataViewsContract),
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
});
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canUseAiops']);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
<DataSourceContextProvider>
|
||||
<Page />
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,23 +6,15 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { AIOPS_ENABLED } from '@kbn/aiops-plugin/common';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { LogCategorizationPage as Page } from '../../../aiops/log_categorization';
|
||||
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities';
|
||||
import { cacheDataViewsContract } from '../../../util/index_utils';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
export const logCategorizationRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
@ -33,7 +25,7 @@ export const logCategorizationRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.aiops.logCategorization.docTitle', {
|
||||
defaultMessage: 'Log Pattern Analysis',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('AIOPS_BREADCRUMB_LOG_PATTERN_ANALYSIS', navigateToPath, basePath),
|
||||
|
@ -46,19 +38,14 @@ export const logCategorizationRouteFactory = (
|
|||
disabled: !AIOPS_ENABLED,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context } = useResolver(index, savedSearchId, deps.config, deps.dataViewsContract, {
|
||||
checkBasicLicense,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(deps.dataViewsContract),
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
});
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canUseAiops']);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
<DataSourceContextProvider>
|
||||
<Page />
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,20 +7,18 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_creation';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import {
|
||||
loadNewJobCapabilities,
|
||||
DATA_FRAME_ANALYTICS,
|
||||
loadNewJobCapabilities,
|
||||
} from '../../../services/new_job_capabilities/load_new_job_capabilities';
|
||||
|
||||
export const analyticsJobsCreationRouteFactory = (
|
||||
|
@ -48,15 +46,21 @@ const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
|||
sort: false,
|
||||
});
|
||||
|
||||
const { context } = useResolver(index, savedSearchId, deps.config, deps.dataViewsContract, {
|
||||
...basicResolvers(deps),
|
||||
analyticsFields: () =>
|
||||
loadNewJobCapabilities(index, savedSearchId, deps.dataViewsContract, DATA_FRAME_ANALYTICS),
|
||||
});
|
||||
const { context } = useRouteResolver(
|
||||
'full',
|
||||
['canGetDataFrameAnalytics', 'canCreateDataFrameAnalytics'],
|
||||
{
|
||||
...basicResolvers(),
|
||||
analyticsFields: () =>
|
||||
loadNewJobCapabilities(index, savedSearchId, deps.dataViewsContract, DATA_FRAME_ANALYTICS),
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page jobId={jobId} />
|
||||
<DataSourceContextProvider>
|
||||
<Page jobId={jobId} />
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -6,17 +6,14 @@
|
|||
*/
|
||||
|
||||
import React, { FC } 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 { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { createPath, MlRoute, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { Page } from '../../../data_frame_analytics/pages/analytics_exploration';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
|
@ -25,7 +22,7 @@ export const analyticsJobExplorationRouteFactory = (
|
|||
basePath: string
|
||||
): MlRoute => ({
|
||||
path: createPath(ML_PAGES.DATA_FRAME_ANALYTICS_EXPLORATION),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
title: i18n.translate('xpack.ml.dataFrameAnalytics.exploration.docTitle', {
|
||||
defaultMessage: 'Results Explorer',
|
||||
}),
|
||||
|
@ -40,14 +37,8 @@ export const analyticsJobExplorationRouteFactory = (
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
);
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetDataFrameAnalytics'], basicResolvers());
|
||||
|
||||
const [globalState] = useUrlState('_g');
|
||||
const jobId: string = globalState?.ml.jobId;
|
||||
|
|
|
@ -9,8 +9,8 @@ import React, { FC } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, 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';
|
||||
|
@ -24,7 +24,7 @@ export const analyticsJobsListRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.dataFrameAnalytics.jobs.docTitle', {
|
||||
defaultMessage: 'Data Frame Analytics Jobs',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('DATA_FRAME_ANALYTICS_BREADCRUMB', navigateToPath, basePath),
|
||||
|
@ -38,14 +38,8 @@ export const analyticsJobsListRouteFactory = (
|
|||
enableDatePicker: true,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
);
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetDataFrameAnalytics'], basicResolvers());
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
|
|
|
@ -7,12 +7,10 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, 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';
|
||||
|
@ -22,7 +20,7 @@ export const analyticsMapRouteFactory = (
|
|||
basePath: string
|
||||
): MlRoute => ({
|
||||
path: createPath(ML_PAGES.DATA_FRAME_ANALYTICS_MAP),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
title: i18n.translate('xpack.ml.dataFrameAnalytics.analyticsMap.docTitle', {
|
||||
defaultMessage: 'Analytics Map',
|
||||
}),
|
||||
|
@ -39,14 +37,8 @@ export const analyticsMapRouteFactory = (
|
|||
'data-test-subj': 'mlPageAnalyticsMap',
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
);
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetDataFrameAnalytics'], basicResolvers());
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -6,14 +6,11 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, 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';
|
||||
|
@ -23,7 +20,7 @@ export const analyticsSourceSelectionRouteFactory = (
|
|||
basePath: string
|
||||
): MlRoute => ({
|
||||
path: createPath(ML_PAGES.DATA_FRAME_ANALYTICS_SOURCE_SELECTION),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
title: i18n.translate('xpack.ml.dataFrameAnalytics.sourceSelection.docTitle', {
|
||||
defaultMessage: 'Source Selection',
|
||||
}),
|
||||
|
@ -38,14 +35,8 @@ export const analyticsSourceSelectionRouteFactory = (
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
);
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetDataFrameAnalytics'], basicResolvers());
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -9,11 +9,9 @@ import React, { FC } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { DatavisualizerSelector } from '../../../datavisualizer';
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { checkFindFileStructurePrivilegeResolver } from '../../../capabilities/check_capabilities';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
export const selectorRouteFactory = (
|
||||
|
@ -25,21 +23,16 @@ export const selectorRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.dataVisualizer.docTitle', {
|
||||
defaultMessage: 'Data Visualizer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('DATA_VISUALIZER_BREADCRUMB'),
|
||||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('basic', ['canFindFileStructure']);
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkBasicLicense,
|
||||
checkFindFileStructurePrivilege: () =>
|
||||
checkFindFileStructurePrivilegeResolver(redirectToMlAccessDeniedPage),
|
||||
});
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<DatavisualizerSelector />
|
||||
|
|
|
@ -7,18 +7,12 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { FileDataVisualizerPage } from '../../../datavisualizer/file_based';
|
||||
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { checkFindFileStructurePrivilegeResolver } from '../../../capabilities/check_capabilities';
|
||||
import { cacheDataViewsContract } from '../../../util/index_utils';
|
||||
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
export const fileBasedRouteFactory = (
|
||||
|
@ -30,7 +24,7 @@ export const fileBasedRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.dataVisualizer.file.docTitle', {
|
||||
defaultMessage: 'File Data Visualizer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('DATA_VISUALIZER_BREADCRUMB', navigateToPath, basePath),
|
||||
|
@ -42,15 +36,8 @@ export const fileBasedRouteFactory = (
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkBasicLicense,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(deps.dataViewsContract),
|
||||
checkFindFileStructurePrivilege: () =>
|
||||
checkFindFileStructurePrivilegeResolver(redirectToMlAccessDeniedPage),
|
||||
});
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('basic', ['canFindFileStructure']);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -6,21 +6,14 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { createPath, MlRoute, PageLoader } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { IndexDataVisualizerPage as Page } from '../../../datavisualizer/index_based/index_data_visualizer';
|
||||
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities';
|
||||
import { cacheDataViewsContract } from '../../../util/index_utils';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml';
|
||||
|
||||
export const indexBasedRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
@ -31,7 +24,7 @@ export const indexBasedRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.dataVisualizer.dataView.docTitle', {
|
||||
defaultMessage: 'Index Data Visualizer',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('DATA_VISUALIZER_BREADCRUMB', navigateToPath, basePath),
|
||||
|
@ -43,19 +36,14 @@ export const indexBasedRouteFactory = (
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context } = useResolver(index, savedSearchId, deps.config, deps.dataViewsContract, {
|
||||
checkBasicLicense,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(deps.dataViewsContract),
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
});
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('basic', []);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
<DataSourceContextProvider>
|
||||
<Page />
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ import { EuiThemeProvider as StyledComponentsThemeProvider } from '@kbn/kibana-r
|
|||
import { useUrlState } from '@kbn/ml-url-state';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { ML_JOB_ID } from '@kbn/ml-anomaly-utils';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import { NavigateToPath, useMlKibana } from '../../contexts/kibana';
|
||||
|
||||
|
@ -22,8 +23,7 @@ import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_j
|
|||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../router';
|
||||
import { useRefresh } from '../use_refresh';
|
||||
import { useResolver } from '../use_resolver';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { Explorer } from '../../explorer';
|
||||
import { mlJobService } from '../../services/job_service';
|
||||
import { ml } from '../../services/ml_api_service';
|
||||
|
@ -42,8 +42,8 @@ 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,
|
||||
AnomalyExplorerContextProvider,
|
||||
useAnomalyExplorerContext,
|
||||
} from '../../explorer/anomaly_explorer_context';
|
||||
|
||||
export const explorerRouteFactory = (
|
||||
|
@ -69,25 +69,28 @@ export const explorerRouteFactory = (
|
|||
'data-test-subj': 'mlPageAnomalyExplorer',
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { context, results } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
{
|
||||
...basicResolvers(deps),
|
||||
jobs: mlJobService.loadJobsWrapper,
|
||||
jobsWithTimeRange: () => ml.jobs.jobsWithTimerange(getDateFormatTz()),
|
||||
}
|
||||
);
|
||||
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>
|
||||
<ExplorerUrlStateManager jobsWithTimeRange={results.jobsWithTimeRange.jobs} />
|
||||
{results ? (
|
||||
<ExplorerUrlStateManager jobsWithTimeRange={results.jobsWithTimeRange.jobs} />
|
||||
) : null}
|
||||
</AnomalyExplorerContextProvider>
|
||||
</MlAnnotationUpdatesContext.Provider>
|
||||
</PageLoader>
|
||||
|
|
|
@ -14,7 +14,6 @@ export * from './data_frame_analytics';
|
|||
export * from './aiops';
|
||||
export { timeSeriesExplorerRouteFactory } from './timeseriesexplorer';
|
||||
export * from './explorer';
|
||||
export * from './access_denied';
|
||||
export * from './trained_models';
|
||||
export * from './notifications';
|
||||
export * from './memory_usage';
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useEffect, FC, useMemo } from 'react';
|
||||
import React, { FC, useEffect, useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
|
@ -16,13 +16,13 @@ import {
|
|||
import { ML_PAGES } from '../../../locator';
|
||||
import { NavigateToPath } from '../../contexts/kibana';
|
||||
import { DEFAULT_REFRESH_INTERVAL_MS } from '../../../../common/constants/jobs_list';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../router';
|
||||
import { useResolver } from '../use_resolver';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { createPath, MlRoute, 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';
|
||||
|
||||
export const jobListRouteFactory = (navigateToPath: NavigateToPath, basePath: string): MlRoute => ({
|
||||
id: 'anomaly_detection',
|
||||
|
@ -30,7 +30,7 @@ export const jobListRouteFactory = (navigateToPath: NavigateToPath, basePath: st
|
|||
defaultMessage: 'Anomaly Detection Jobs',
|
||||
}),
|
||||
path: createPath(ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
|
@ -44,14 +44,9 @@ export const jobListRouteFactory = (navigateToPath: NavigateToPath, basePath: st
|
|||
enableDatePicker: true,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
);
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetJobs'], basicResolvers());
|
||||
|
||||
const timefilter = useTimefilter({ timeRangeSelector: false, autoRefreshSelector: true });
|
||||
|
||||
const refresh = useRefreshIntervalUpdates();
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import { NavigateToPath } from '../../contexts/kibana';
|
||||
import { MlRoute, PageLoader, PageProps, createPath } from '../router';
|
||||
import { useResolver } from '../use_resolver';
|
||||
import { basicResolvers } from '../resolvers';
|
||||
import { createPath, MlRoute, PageLoader } from '../router';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { MemoryUsagePage } from '../../memory_usage';
|
||||
|
||||
|
@ -20,7 +20,7 @@ export const nodesListRouteFactory = (
|
|||
basePath: string
|
||||
): MlRoute => ({
|
||||
path: createPath(ML_PAGES.MEMORY_USAGE),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
title: i18n.translate('xpack.ml.modelManagement.memoryUsage.docTitle', {
|
||||
defaultMessage: 'Memory Usage',
|
||||
}),
|
||||
|
@ -35,13 +35,11 @@ export const nodesListRouteFactory = (
|
|||
enableDatePicker: true,
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver(
|
||||
'full',
|
||||
['canGetJobs', 'canGetDataFrameAnalytics', 'canGetTrainedModels'],
|
||||
basicResolvers()
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -6,14 +6,11 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { resolver } from '../../../jobs/new_job/job_from_lens';
|
||||
|
||||
export const fromLensRouteFactory = (): MlRoute => ({
|
||||
|
@ -22,7 +19,7 @@ export const fromLensRouteFactory = (): MlRoute => ({
|
|||
breadcrumbs: [],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const PageWrapper: FC<PageProps> = ({ location }) => {
|
||||
const { lensId, vis, from, to, query, filters, layerIndex }: Record<string, any> = parse(
|
||||
location.search,
|
||||
{
|
||||
|
@ -30,9 +27,10 @@ const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
|||
}
|
||||
);
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
const { context } = useRouteResolver('full', ['canCreateJob'], {
|
||||
redirect: () => resolver(lensId, vis, from, to, query, filters, layerIndex),
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
{<Redirect to={createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB)} />}
|
||||
|
|
|
@ -6,14 +6,11 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { resolver } from '../../../jobs/new_job/job_from_map';
|
||||
|
||||
export const fromMapRouteFactory = (): MlRoute => ({
|
||||
|
@ -22,7 +19,7 @@ export const fromMapRouteFactory = (): MlRoute => ({
|
|||
breadcrumbs: [],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const PageWrapper: FC<PageProps> = ({ location }) => {
|
||||
const {
|
||||
dashboard,
|
||||
dataViewId,
|
||||
|
@ -36,10 +33,11 @@ const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
|||
sort: false,
|
||||
});
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
const { context } = useRouteResolver('full', ['canCreateJob'], {
|
||||
redirect: () =>
|
||||
resolver(dashboard, dataViewId, embeddable, geoField, splitField, from, to, layer),
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
{<Redirect to={createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB)} />}
|
||||
|
|
|
@ -6,20 +6,14 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath, useMlKibana } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { Page, preConfiguredJobRedirect } from '../../../jobs/new_job/pages/index_or_search';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
import { checkBasicLicense } from '../../../license';
|
||||
import { cacheDataViewsContract } from '../../../util/index_utils';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../../../capabilities/check_capabilities';
|
||||
|
||||
enum MODE {
|
||||
NEW_JOB,
|
||||
|
@ -188,25 +182,16 @@ const PageWrapper: FC<IndexOrSearchPageProps> = ({ nextStepPath, deps, mode }) =
|
|||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const newJobResolvers = {
|
||||
...basicResolvers(deps),
|
||||
...basicResolvers(),
|
||||
preConfiguredJobRedirect: () =>
|
||||
preConfiguredJobRedirect(deps.dataViewsContract, basePath.get(), navigateToUrl),
|
||||
};
|
||||
const dataVizResolvers = {
|
||||
checkBasicLicense,
|
||||
cacheDataViewsContract: () => cacheDataViewsContract(deps.dataViewsContract),
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
};
|
||||
|
||||
const { context } = useResolver(
|
||||
undefined,
|
||||
undefined,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
mode === MODE.NEW_JOB ? newJobResolvers : dataVizResolvers
|
||||
const { context } = useRouteResolver(
|
||||
mode === MODE.NEW_JOB ? 'full' : 'basic',
|
||||
mode === MODE.NEW_JOB ? ['canCreateJob'] : [],
|
||||
mode === MODE.NEW_JOB ? newJobResolvers : {}
|
||||
);
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -6,22 +6,19 @@
|
|||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import { parse } from 'query-string';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { createPath, MlRoute, 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';
|
||||
|
||||
export const jobTypeRouteFactory = (navigateToPath: NavigateToPath, basePath: string): MlRoute => ({
|
||||
path: createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_SELECT_TYPE),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
|
@ -34,18 +31,14 @@ export const jobTypeRouteFactory = (navigateToPath: NavigateToPath, basePath: st
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context } = useResolver(
|
||||
index,
|
||||
savedSearchId,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
basicResolvers(deps)
|
||||
);
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetJobs'], basicResolvers());
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page />
|
||||
<DataSourceContextProvider>
|
||||
<Page />
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -7,20 +7,17 @@
|
|||
|
||||
import { parse } from 'query-string';
|
||||
import React, { FC } from 'react';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath, useNavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath, useMlKibana, useNavigateToPath } from '../../../contexts/kibana';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { Page } from '../../../jobs/new_job/recognize';
|
||||
import { checkViewOrCreateJobs } from '../../../jobs/new_job/recognize/resolvers';
|
||||
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';
|
||||
|
||||
export const recognizeRouteFactory = (
|
||||
navigateToPath: NavigateToPath,
|
||||
|
@ -49,38 +46,87 @@ export const checkViewOrCreateRouteFactory = (): MlRoute => ({
|
|||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const { id, index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context, results } = useResolver(
|
||||
index,
|
||||
savedSearchId,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
const { id } = parse(location.search, { sort: false });
|
||||
|
||||
{
|
||||
...basicResolvers(deps),
|
||||
existingJobsAndGroups: mlJobService.getJobAndGroupIds,
|
||||
}
|
||||
);
|
||||
const { context, results } = useRouteResolver('full', ['canGetJobs'], {
|
||||
...basicResolvers(),
|
||||
existingJobsAndGroups: mlJobService.getJobAndGroupIds,
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page moduleId={id} existingGroupIds={results.existingJobsAndGroups.groupIds} />
|
||||
<DataSourceContextProvider>
|
||||
{results ? (
|
||||
<Page moduleId={id as string} existingGroupIds={results.existingJobsAndGroups.groupIds} />
|
||||
) : null}
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
||||
const CheckViewOrCreateWrapper: FC<PageProps> = ({ location, deps }) => {
|
||||
const {
|
||||
services: {
|
||||
notifications: { toasts },
|
||||
mlServices: { mlApiServices },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const { id: moduleId, index: dataViewId }: Record<string, any> = parse(location.search, {
|
||||
sort: false,
|
||||
});
|
||||
|
||||
const { createLinkWithUserDefaults } = useCreateADLinks();
|
||||
|
||||
const navigateToPath = useNavigateToPath();
|
||||
|
||||
// the single resolver checkViewOrCreateJobs redirects only. so will always reject
|
||||
useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkViewOrCreateJobs: () =>
|
||||
checkViewOrCreateJobs(moduleId, dataViewId, createLinkWithUserDefaults, navigateToPath),
|
||||
});
|
||||
/**
|
||||
* Checks whether the jobs in a data recognizer module have been created.
|
||||
* Redirects to the Anomaly Explorer to view the jobs if they have been created,
|
||||
* or the recognizer job wizard for the module if not.
|
||||
*/
|
||||
function checkViewOrCreateJobs(): Promise<unknown> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Load the module, and check if the job(s) in the module have been created.
|
||||
// If so, load the jobs in the Anomaly Explorer.
|
||||
// Otherwise open the data recognizer wizard for the module.
|
||||
// Always want to call reject() so as not to load original page.
|
||||
mlApiServices
|
||||
.dataRecognizerModuleJobsExist({ moduleId })
|
||||
.then(async (resp: any) => {
|
||||
if (resp.jobsExist === true) {
|
||||
// also honor user's time filter setting in Advanced Settings
|
||||
const url = createLinkWithUserDefaults('explorer', resp.jobs);
|
||||
await navigateToPath(url);
|
||||
reject();
|
||||
} else {
|
||||
await navigateToPath(`/jobs/new_job/recognize?id=${moduleId}&index=${dataViewId}`);
|
||||
reject();
|
||||
}
|
||||
})
|
||||
.catch(async (err: Error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Error checking whether jobs in module ${moduleId} exists`, err);
|
||||
toasts.addWarning({
|
||||
title: i18n.translate('xpack.ml.newJob.recognize.moduleCheckJobsExistWarningTitle', {
|
||||
defaultMessage: 'Error checking module {moduleId}',
|
||||
values: { moduleId },
|
||||
}),
|
||||
text: i18n.translate(
|
||||
'xpack.ml.newJob.recognize.moduleCheckJobsExistWarningDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'An error occurred trying to check whether the jobs in the module have been created.',
|
||||
}
|
||||
),
|
||||
});
|
||||
await navigateToPath(`/jobs`);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
useRouteResolver('full', ['canCreateJob'], { checkViewOrCreateJobs });
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -9,11 +9,11 @@ import { parse } from 'query-string';
|
|||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { DataSourceContextProvider } from '../../../contexts/ml/data_source_context';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { basicResolvers } from '../../resolvers';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
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';
|
||||
|
@ -194,23 +194,23 @@ const PageWrapper: FC<WizardPageProps> = ({ location, jobType, deps }) => {
|
|||
);
|
||||
|
||||
const { index, savedSearchId }: Record<string, any> = parse(location.search, { sort: false });
|
||||
const { context, results } = useResolver(
|
||||
index,
|
||||
savedSearchId,
|
||||
deps.config,
|
||||
deps.dataViewsContract,
|
||||
{
|
||||
...basicResolvers(deps),
|
||||
privileges: () => checkCreateJobsCapabilitiesResolver(redirectToJobsManagementPage),
|
||||
jobCaps: () =>
|
||||
loadNewJobCapabilities(index, savedSearchId, deps.dataViewsContract, ANOMALY_DETECTOR),
|
||||
existingJobsAndGroups: mlJobService.getJobAndGroupIds,
|
||||
}
|
||||
);
|
||||
|
||||
const { context, results } = useRouteResolver('full', ['canGetJobs', 'canCreateJob'], {
|
||||
...basicResolvers(),
|
||||
// TODO useRouteResolver should be responsible for the redirect
|
||||
privileges: () => checkCreateJobsCapabilitiesResolver(redirectToJobsManagementPage),
|
||||
jobCaps: () =>
|
||||
loadNewJobCapabilities(index, savedSearchId, deps.dataViewsContract, ANOMALY_DETECTOR),
|
||||
existingJobsAndGroups: mlJobService.getJobAndGroupIds,
|
||||
});
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
<Page jobType={jobType} existingJobsAndGroups={results.existingJobsAndGroups} />
|
||||
<DataSourceContextProvider>
|
||||
{results ? (
|
||||
<Page jobType={jobType} existingJobsAndGroups={results.existingJobsAndGroups} />
|
||||
) : null}
|
||||
</DataSourceContextProvider>
|
||||
</PageLoader>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -9,10 +9,8 @@ import React, { FC, Suspense } from 'react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import { createPath, PageLoader, PageProps } from '../router';
|
||||
import { useResolver } from '../use_resolver';
|
||||
import { checkFullLicense } from '../../license';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../../capabilities/check_capabilities';
|
||||
import { createPath, PageLoader } from '../router';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
import { getMlNodeCount } from '../../ml_nodes_check';
|
||||
import { loadMlServerInfo } from '../../services/ml_server_info';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
|
@ -31,7 +29,7 @@ export const notificationsRouteFactory = (
|
|||
defaultMessage: 'Notifications',
|
||||
}),
|
||||
enableDatePicker: true,
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
{
|
||||
|
@ -43,15 +41,12 @@ export const notificationsRouteFactory = (
|
|||
'data-test-subj': 'mlPageNotifications',
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkFullLicense,
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetJobs'], {
|
||||
getMlNodeCount,
|
||||
loadMlServerInfo,
|
||||
});
|
||||
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,23 +5,17 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC, Suspense } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
|
||||
import React, { FC, Suspense } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { ML_PAGES } from '../../../locator';
|
||||
import type { NavigateToPath } from '../../contexts/kibana';
|
||||
import { checkFullLicense } from '../../license';
|
||||
import { checkGetJobsCapabilitiesResolver } from '../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../ml_nodes_check';
|
||||
import { loadMlServerInfo } from '../../services/ml_server_info';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../router';
|
||||
import { useResolver } from '../use_resolver';
|
||||
import { getBreadcrumbWithUrlForApp } from '../breadcrumbs';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../router';
|
||||
import { useRouteResolver } from '../use_resolver';
|
||||
|
||||
const OverviewPage = React.lazy(() => import('../../overview/overview_page'));
|
||||
|
||||
|
@ -47,15 +41,12 @@ export const overviewRouteFactory = (
|
|||
'data-test-subj': 'mlPageOverview',
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkFullLicense,
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
const PageWrapper: FC<PageProps> = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetJobs'], {
|
||||
getMlNodeCount,
|
||||
loadMlServerInfo,
|
||||
});
|
||||
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
return (
|
||||
|
|
|
@ -11,12 +11,8 @@ import { useTimefilter } from '@kbn/ml-date-picker';
|
|||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { checkFullLicense } from '../../../license';
|
||||
import {
|
||||
checkGetJobsCapabilitiesResolver,
|
||||
checkPermission,
|
||||
} from '../../../capabilities/check_capabilities';
|
||||
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';
|
||||
|
@ -39,18 +35,14 @@ export const calendarListRouteFactory = (
|
|||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkFullLicense,
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
getMlNodeCount,
|
||||
});
|
||||
const { context } = useRouteResolver('full', ['canGetCalendars'], { getMlNodeCount });
|
||||
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
const canCreateCalendar = checkPermission('canCreateCalendar');
|
||||
const canDeleteCalendar = checkPermission('canDeleteCalendar');
|
||||
const [canCreateCalendar, canDeleteCalendar] = usePermissionCheck([
|
||||
'canCreateCalendar',
|
||||
'canDeleteCalendar',
|
||||
]);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -10,12 +10,8 @@ import { i18n } from '@kbn/i18n';
|
|||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { checkFullLicense } from '../../../license';
|
||||
import {
|
||||
checkGetJobsCapabilitiesResolver,
|
||||
checkPermission,
|
||||
} from '../../../capabilities/check_capabilities';
|
||||
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';
|
||||
|
@ -79,18 +75,15 @@ const PageWrapper: FC<NewCalendarPageProps> = ({ location, mode, deps }) => {
|
|||
const pathMatch: string[] | null = location.pathname.match(/.+\/(.+)$/);
|
||||
calendarId = pathMatch && pathMatch.length > 1 ? pathMatch[1] : undefined;
|
||||
}
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkFullLicense,
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
getMlNodeCount,
|
||||
});
|
||||
const { context } = useRouteResolver('full', ['canGetJobs'], { getMlNodeCount });
|
||||
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
const canCreateCalendar = checkPermission('canCreateCalendar');
|
||||
const canDeleteCalendar = checkPermission('canDeleteCalendar');
|
||||
const [canCreateCalendar, canDeleteCalendar] = usePermissionCheck([
|
||||
'canCreateCalendar',
|
||||
'canDeleteCalendar',
|
||||
]);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -10,13 +10,9 @@ import { i18n } from '@kbn/i18n';
|
|||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
import { ML_PAGES } from '../../../../locator';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { checkFullLicense } from '../../../license';
|
||||
import {
|
||||
checkGetJobsCapabilitiesResolver,
|
||||
checkPermission,
|
||||
} from '../../../capabilities/check_capabilities';
|
||||
import { createPath, MlRoute, 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';
|
||||
|
@ -29,7 +25,7 @@ export const filterListRouteFactory = (
|
|||
title: i18n.translate('xpack.ml.settings.filterList.docTitle', {
|
||||
defaultMessage: 'Filters',
|
||||
}),
|
||||
render: (props, deps) => <PageWrapper {...props} deps={deps} />,
|
||||
render: () => <PageWrapper />,
|
||||
breadcrumbs: [
|
||||
getBreadcrumbWithUrlForApp('ML_BREADCRUMB', navigateToPath, basePath),
|
||||
getBreadcrumbWithUrlForApp('ANOMALY_DETECTION_BREADCRUMB', navigateToPath, basePath),
|
||||
|
@ -38,19 +34,15 @@ export const filterListRouteFactory = (
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<PageProps> = ({ deps }) => {
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkFullLicense,
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
getMlNodeCount,
|
||||
});
|
||||
const PageWrapper: FC = () => {
|
||||
const { context } = useRouteResolver('full', ['canGetFilters'], { getMlNodeCount });
|
||||
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
const canCreateFilter = checkPermission('canCreateFilter');
|
||||
const canDeleteFilter = checkPermission('canDeleteFilter');
|
||||
const [canCreateFilter, canDeleteFilter] = usePermissionCheck([
|
||||
'canCreateFilter',
|
||||
'canDeleteFilter',
|
||||
]);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
|
@ -7,22 +7,14 @@
|
|||
|
||||
import React, { FC } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useTimefilter } from '@kbn/ml-date-picker';
|
||||
|
||||
import { ML_PAGES } from '../../../../../common/constants/locator';
|
||||
|
||||
import { checkFullLicense } from '../../../license';
|
||||
import {
|
||||
checkGetJobsCapabilitiesResolver,
|
||||
checkPermission,
|
||||
} from '../../../capabilities/check_capabilities';
|
||||
import { usePermissionCheck } from '../../../capabilities/check_capabilities';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { EditFilterList } from '../../../settings/filter_lists';
|
||||
import { NavigateToPath } from '../../../contexts/kibana';
|
||||
|
||||
import { createPath, MlRoute, PageLoader, PageProps } from '../../router';
|
||||
import { useResolver } from '../../use_resolver';
|
||||
import { useRouteResolver } from '../../use_resolver';
|
||||
import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs';
|
||||
|
||||
enum MODE {
|
||||
|
@ -77,24 +69,23 @@ export const editFilterListRouteFactory = (
|
|||
],
|
||||
});
|
||||
|
||||
const PageWrapper: FC<NewFilterPageProps> = ({ location, mode, deps }) => {
|
||||
const PageWrapper: FC<NewFilterPageProps> = ({ location, mode }) => {
|
||||
let filterId: string | undefined;
|
||||
if (mode === MODE.EDIT) {
|
||||
const pathMatch: string[] | null = location.pathname.match(/.+\/(.+)$/);
|
||||
filterId = pathMatch && pathMatch.length > 1 ? pathMatch[1] : undefined;
|
||||
}
|
||||
const { redirectToMlAccessDeniedPage } = deps;
|
||||
|
||||
const { context } = useResolver(undefined, undefined, deps.config, deps.dataViewsContract, {
|
||||
checkFullLicense,
|
||||
checkGetJobsCapabilities: () => checkGetJobsCapabilitiesResolver(redirectToMlAccessDeniedPage),
|
||||
const { context } = useRouteResolver('full', ['canGetFilters', 'canCreateFilter'], {
|
||||
getMlNodeCount,
|
||||
});
|
||||
|
||||
useTimefilter({ timeRangeSelector: false, autoRefreshSelector: false });
|
||||
|
||||
const canCreateFilter = checkPermission('canCreateFilter');
|
||||
const canDeleteFilter = checkPermission('canDeleteFilter');
|
||||
const [canCreateFilter, canDeleteFilter] = usePermissionCheck([
|
||||
'canCreateFilter',
|
||||
'canDeleteFilter',
|
||||
]);
|
||||
|
||||
return (
|
||||
<PageLoader context={context}>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue