mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Convert triggers and actions UI to Kibana Platform (#56388)
* Convert triggers and actions UI to Kibana Platform * Copy ui/time_buckets over * Fix i18n * Fix scss * Fix UI from not loading anymore
This commit is contained in:
parent
e92ead7324
commit
8951424f82
107 changed files with 870 additions and 277 deletions
|
@ -4,7 +4,7 @@
|
|||
"xpack.actions": "plugins/actions",
|
||||
"xpack.advancedUiActions": "plugins/advanced_ui_actions",
|
||||
"xpack.alerting": "legacy/plugins/alerting",
|
||||
"xpack.triggersActionsUI": "legacy/plugins/triggers_actions_ui",
|
||||
"xpack.triggersActionsUI": "plugins/triggers_actions_ui",
|
||||
"xpack.apm": ["legacy/plugins/apm", "plugins/apm"],
|
||||
"xpack.beatsManagement": "legacy/plugins/beats_management",
|
||||
"xpack.canvas": "legacy/plugins/canvas",
|
||||
|
|
|
@ -29,7 +29,6 @@ export function triggersActionsUI(kibana: any) {
|
|||
.default();
|
||||
},
|
||||
uiExports: {
|
||||
managementSections: ['plugins/triggers_actions_ui/legacy'],
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
injectDefaultVars(server: Legacy.Server) {
|
||||
const serverConfig = server.config();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@import 'src/legacy/ui/public/styles/_styling_constants';
|
||||
|
||||
// Styling within the app
|
||||
@import '../np_ready/public/application/sections/actions_connectors_list/components/index';
|
||||
@import '../../../../plugins/triggers_actions_ui/public/application/sections/actions_connectors_list/components/index';
|
||||
|
||||
@import '../np_ready/public/application/sections/action_connector_form/index';
|
||||
@import '../../../../plugins/triggers_actions_ui/public/application/sections/action_connector_form/index';
|
||||
|
||||
|
|
|
@ -1,96 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { CoreSetup, App, AppUnmount } from 'src/core/public';
|
||||
import { capabilities } from 'ui/capabilities';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
/* Legacy UI imports */
|
||||
import { npSetup, npStart } from 'ui/new_platform';
|
||||
import routes from 'ui/routes';
|
||||
import { management, MANAGEMENT_BREADCRUMB } from 'ui/management';
|
||||
// @ts-ignore
|
||||
import { xpackInfo } from 'plugins/xpack_main/services/xpack_info';
|
||||
/* Legacy UI imports */
|
||||
|
||||
import { plugin } from '../np_ready/public';
|
||||
import { manageAngularLifecycle } from './manage_angular_lifecycle';
|
||||
import { BASE_PATH } from '../np_ready/public/application/constants';
|
||||
import {
|
||||
hasShowActionsCapability,
|
||||
hasShowAlertsCapability,
|
||||
} from '../np_ready/public/application/lib/capabilities';
|
||||
|
||||
const REACT_ROOT_ID = 'triggersActionsRoot';
|
||||
const canShowActions = hasShowActionsCapability(capabilities.get());
|
||||
const canShowAlerts = hasShowAlertsCapability(capabilities.get());
|
||||
|
||||
const template = `<kbn-management-app section="kibana/triggersActions">
|
||||
<div id="triggersActionsRoot"></div>
|
||||
</kbn-management-app>`;
|
||||
|
||||
let elem: HTMLElement;
|
||||
let mountApp: () => AppUnmount | Promise<AppUnmount>;
|
||||
let unmountApp: AppUnmount | Promise<AppUnmount>;
|
||||
routes.when(`${BASE_PATH}:section?/:subsection?/:view?/:id?`, {
|
||||
template,
|
||||
controller: (() => {
|
||||
return ($route: any, $scope: any) => {
|
||||
const shimCore: CoreSetup = {
|
||||
...npSetup.core,
|
||||
application: {
|
||||
...npSetup.core.application,
|
||||
register(app: App): void {
|
||||
mountApp = () =>
|
||||
app.mount(npStart as any, {
|
||||
element: elem,
|
||||
appBasePath: BASE_PATH,
|
||||
onAppLeave: () => undefined,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// clean up previously rendered React app if one exists
|
||||
// this happens because of React Router redirects
|
||||
if (elem) {
|
||||
((unmountApp as unknown) as AppUnmount)();
|
||||
}
|
||||
|
||||
$scope.$$postDigest(() => {
|
||||
elem = document.getElementById(REACT_ROOT_ID)!;
|
||||
const instance = plugin({} as any);
|
||||
instance.setup(shimCore, {
|
||||
...(npSetup.plugins as typeof npSetup.plugins),
|
||||
__LEGACY: {
|
||||
MANAGEMENT_BREADCRUMB,
|
||||
},
|
||||
});
|
||||
|
||||
instance.start(npStart.core, {
|
||||
...(npSetup.plugins as typeof npSetup.plugins),
|
||||
__LEGACY: {
|
||||
MANAGEMENT_BREADCRUMB,
|
||||
},
|
||||
});
|
||||
|
||||
(mountApp() as Promise<AppUnmount>).then(fn => (unmountApp = fn));
|
||||
|
||||
manageAngularLifecycle($scope, $route, elem);
|
||||
});
|
||||
};
|
||||
})(),
|
||||
});
|
||||
|
||||
if (canShowActions || canShowAlerts) {
|
||||
management.getSection('kibana').register('triggersActions', {
|
||||
display: i18n.translate('xpack.triggersActionsUI.managementSection.displayName', {
|
||||
defaultMessage: 'Alerts and Actions',
|
||||
}),
|
||||
order: 7,
|
||||
url: `#${BASE_PATH}`,
|
||||
});
|
||||
}
|
|
@ -1,28 +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;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { unmountComponentAtNode } from 'react-dom';
|
||||
|
||||
export const manageAngularLifecycle = ($scope: any, $route: any, elem: HTMLElement) => {
|
||||
const lastRoute = $route.current;
|
||||
|
||||
const deregister = $scope.$on('$locationChangeSuccess', () => {
|
||||
const currentRoute = $route.current;
|
||||
if (lastRoute.$$route.template === currentRoute.$$route.template) {
|
||||
$route.current = lastRoute;
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', () => {
|
||||
if (deregister) {
|
||||
deregister();
|
||||
}
|
||||
|
||||
if (elem) {
|
||||
unmountComponentAtNode(elem);
|
||||
}
|
||||
});
|
||||
};
|
|
@ -2,5 +2,6 @@
|
|||
"id": "triggers_actions_ui",
|
||||
"version": "kibana",
|
||||
"server": false,
|
||||
"ui": true
|
||||
"ui": true,
|
||||
"requiredPlugins": ["management", "charts", "data"]
|
||||
}
|
|
@ -12,23 +12,28 @@ import {
|
|||
HttpSetup,
|
||||
IUiSettingsClient,
|
||||
ApplicationStart,
|
||||
ChromeBreadcrumb,
|
||||
} from 'kibana/public';
|
||||
import { BASE_PATH, Section, routeToAlertDetails } from './constants';
|
||||
import { TriggersActionsUIHome } from './home';
|
||||
import { AppContextProvider, useAppDependencies } from './app_context';
|
||||
import { hasShowAlertsCapability } from './lib/capabilities';
|
||||
import { LegacyDependencies, ActionTypeModel, AlertTypeModel } from '../types';
|
||||
import { ActionTypeModel, AlertTypeModel } from '../types';
|
||||
import { TypeRegistry } from './type_registry';
|
||||
import { AlertDetailsRouteWithApi as AlertDetailsRoute } from './sections/alert_details/components/alert_details_route';
|
||||
import { ChartsPluginStart } from '../../../../../src/plugins/charts/public';
|
||||
import { DataPublicPluginStart } from '../../../../../src/plugins/data/public';
|
||||
|
||||
export interface AppDeps {
|
||||
dataPlugin: DataPublicPluginStart;
|
||||
charts: ChartsPluginStart;
|
||||
chrome: ChromeStart;
|
||||
docLinks: DocLinksStart;
|
||||
toastNotifications: ToastsSetup;
|
||||
injectedMetadata: any;
|
||||
http: HttpSetup;
|
||||
uiSettings: IUiSettingsClient;
|
||||
legacy: LegacyDependencies;
|
||||
setBreadcrumbs: (crumbs: ChromeBreadcrumb[]) => void;
|
||||
capabilities: ApplicationStart['capabilities'];
|
||||
actionTypeRegistry: TypeRegistry<ActionTypeModel>;
|
||||
alertTypeRegistry: TypeRegistry<AlertTypeModel>;
|
|
@ -10,23 +10,21 @@ import { SavedObjectsClientContract } from 'src/core/public';
|
|||
|
||||
import { App, AppDeps } from './app';
|
||||
import { setSavedObjectsClient } from '../application/components/builtin_alert_types/threshold/lib/api';
|
||||
import { LegacyDependencies } from '../types';
|
||||
|
||||
interface BootDeps extends AppDeps {
|
||||
element: HTMLElement;
|
||||
savedObjects: SavedObjectsClientContract;
|
||||
I18nContext: any;
|
||||
legacy: LegacyDependencies;
|
||||
}
|
||||
|
||||
export const boot = (bootDeps: BootDeps) => {
|
||||
const { I18nContext, element, legacy, savedObjects, ...appDeps } = bootDeps;
|
||||
const { I18nContext, element, savedObjects, ...appDeps } = bootDeps;
|
||||
|
||||
setSavedObjectsClient(savedObjects);
|
||||
|
||||
render(
|
||||
<I18nContext>
|
||||
<App {...appDeps} legacy={legacy} />
|
||||
<App {...appDeps} />
|
||||
</I18nContext>,
|
||||
element
|
||||
);
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval';
|
||||
|
||||
describe('calcAutoIntervalNear', () => {
|
||||
test('1h/0 buckets = 0ms buckets', () => {
|
||||
const interval = calcAutoIntervalNear(0, Number(moment.duration(1, 'h')));
|
||||
expect(interval.asMilliseconds()).toBe(0);
|
||||
});
|
||||
|
||||
test('undefined/100 buckets = 0ms buckets', () => {
|
||||
const interval = calcAutoIntervalNear(0, undefined as any);
|
||||
expect(interval.asMilliseconds()).toBe(0);
|
||||
});
|
||||
|
||||
test('1ms/100 buckets = 1ms buckets', () => {
|
||||
const interval = calcAutoIntervalNear(100, Number(moment.duration(1, 'ms')));
|
||||
expect(interval.asMilliseconds()).toBe(1);
|
||||
});
|
||||
|
||||
test('200ms/100 buckets = 2ms buckets', () => {
|
||||
const interval = calcAutoIntervalNear(100, Number(moment.duration(200, 'ms')));
|
||||
expect(interval.asMilliseconds()).toBe(2);
|
||||
});
|
||||
|
||||
test('1s/1000 buckets = 1ms buckets', () => {
|
||||
const interval = calcAutoIntervalNear(1000, Number(moment.duration(1, 's')));
|
||||
expect(interval.asMilliseconds()).toBe(1);
|
||||
});
|
||||
|
||||
test('1000h/1000 buckets = 1h buckets', () => {
|
||||
const interval = calcAutoIntervalNear(1000, Number(moment.duration(1000, 'hours')));
|
||||
expect(interval.asHours()).toBe(1);
|
||||
});
|
||||
|
||||
test('1h/100 buckets = 30s buckets', () => {
|
||||
const interval = calcAutoIntervalNear(100, Number(moment.duration(1, 'hours')));
|
||||
expect(interval.asSeconds()).toBe(30);
|
||||
});
|
||||
|
||||
test('1d/25 buckets = 1h buckets', () => {
|
||||
const interval = calcAutoIntervalNear(25, Number(moment.duration(1, 'day')));
|
||||
expect(interval.asHours()).toBe(1);
|
||||
});
|
||||
|
||||
test('1y/1000 buckets = 12h buckets', () => {
|
||||
const interval = calcAutoIntervalNear(1000, Number(moment.duration(1, 'year')));
|
||||
expect(interval.asHours()).toBe(12);
|
||||
});
|
||||
|
||||
test('1y/10000 buckets = 1h buckets', () => {
|
||||
const interval = calcAutoIntervalNear(10000, Number(moment.duration(1, 'year')));
|
||||
expect(interval.asHours()).toBe(1);
|
||||
});
|
||||
|
||||
test('1y/100000 buckets = 5m buckets', () => {
|
||||
const interval = calcAutoIntervalNear(100000, Number(moment.duration(1, 'year')));
|
||||
expect(interval.asMinutes()).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calcAutoIntervalLessThan', () => {
|
||||
test('1h/0 buckets = 0ms buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(0, Number(moment.duration(1, 'h')));
|
||||
expect(interval.asMilliseconds()).toBe(0);
|
||||
});
|
||||
|
||||
test('undefined/100 buckets = 0ms buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(0, undefined as any);
|
||||
expect(interval.asMilliseconds()).toBe(0);
|
||||
});
|
||||
|
||||
test('1ms/100 buckets = 1ms buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(100, Number(moment.duration(1, 'ms')));
|
||||
expect(interval.asMilliseconds()).toBe(1);
|
||||
});
|
||||
|
||||
test('200ms/100 buckets = 2ms buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(100, Number(moment.duration(200, 'ms')));
|
||||
expect(interval.asMilliseconds()).toBe(2);
|
||||
});
|
||||
|
||||
test('1s/1000 buckets = 1ms buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(1000, Number(moment.duration(1, 's')));
|
||||
expect(interval.asMilliseconds()).toBe(1);
|
||||
});
|
||||
|
||||
test('1000h/1000 buckets = 1h buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(1000, Number(moment.duration(1000, 'hours')));
|
||||
expect(interval.asHours()).toBe(1);
|
||||
});
|
||||
|
||||
test('1h/100 buckets = 30s buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(100, Number(moment.duration(1, 'hours')));
|
||||
expect(interval.asSeconds()).toBe(30);
|
||||
});
|
||||
|
||||
test('1d/25 buckets = 30m buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(25, Number(moment.duration(1, 'day')));
|
||||
expect(interval.asMinutes()).toBe(30);
|
||||
});
|
||||
|
||||
test('1y/1000 buckets = 3h buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(1000, Number(moment.duration(1, 'year')));
|
||||
expect(interval.asHours()).toBe(3);
|
||||
});
|
||||
|
||||
test('1y/10000 buckets = 30m buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(10000, Number(moment.duration(1, 'year')));
|
||||
expect(interval.asMinutes()).toBe(30);
|
||||
});
|
||||
|
||||
test('1y/100000 buckets = 5m buckets', () => {
|
||||
const interval = calcAutoIntervalLessThan(100000, Number(moment.duration(1, 'year')));
|
||||
expect(interval.asMinutes()).toBe(5);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import moment from 'moment';
|
||||
|
||||
const boundsDescending = [
|
||||
{
|
||||
bound: Infinity,
|
||||
interval: Number(moment.duration(1, 'year')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(1, 'year')),
|
||||
interval: Number(moment.duration(1, 'month')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(3, 'week')),
|
||||
interval: Number(moment.duration(1, 'week')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(1, 'week')),
|
||||
interval: Number(moment.duration(1, 'd')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(24, 'hour')),
|
||||
interval: Number(moment.duration(12, 'hour')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(6, 'hour')),
|
||||
interval: Number(moment.duration(3, 'hour')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(2, 'hour')),
|
||||
interval: Number(moment.duration(1, 'hour')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(45, 'minute')),
|
||||
interval: Number(moment.duration(30, 'minute')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(20, 'minute')),
|
||||
interval: Number(moment.duration(10, 'minute')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(9, 'minute')),
|
||||
interval: Number(moment.duration(5, 'minute')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(3, 'minute')),
|
||||
interval: Number(moment.duration(1, 'minute')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(45, 'second')),
|
||||
interval: Number(moment.duration(30, 'second')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(15, 'second')),
|
||||
interval: Number(moment.duration(10, 'second')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(7.5, 'second')),
|
||||
interval: Number(moment.duration(5, 'second')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(5, 'second')),
|
||||
interval: Number(moment.duration(1, 'second')),
|
||||
},
|
||||
{
|
||||
bound: Number(moment.duration(500, 'ms')),
|
||||
interval: Number(moment.duration(100, 'ms')),
|
||||
},
|
||||
];
|
||||
|
||||
function getPerBucketMs(count: number, duration: number) {
|
||||
const ms = duration / count;
|
||||
return isFinite(ms) ? ms : NaN;
|
||||
}
|
||||
|
||||
function normalizeMinimumInterval(targetMs: number) {
|
||||
const value = isNaN(targetMs) ? 0 : Math.max(Math.floor(targetMs), 1);
|
||||
return moment.duration(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using some simple rules we pick a "pretty" interval that will
|
||||
* produce around the number of buckets desired given a time range.
|
||||
*
|
||||
* @param targetBucketCount desired number of buckets
|
||||
* @param duration time range the agg covers
|
||||
*/
|
||||
export function calcAutoIntervalNear(targetBucketCount: number, duration: number) {
|
||||
const targetPerBucketMs = getPerBucketMs(targetBucketCount, duration);
|
||||
|
||||
// Find the first bound which is smaller than our target.
|
||||
const lowerBoundIndex = boundsDescending.findIndex(({ bound }) => {
|
||||
const boundMs = Number(bound);
|
||||
return boundMs <= targetPerBucketMs;
|
||||
});
|
||||
|
||||
// The bound immediately preceeding that lower bound contains the
|
||||
// interval most closely matching our target.
|
||||
if (lowerBoundIndex !== -1) {
|
||||
const nearestInterval = boundsDescending[lowerBoundIndex - 1].interval;
|
||||
return moment.duration(nearestInterval);
|
||||
}
|
||||
|
||||
// If the target is smaller than any of our bounds, then we'll use it for the interval as-is.
|
||||
return normalizeMinimumInterval(targetPerBucketMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a "pretty" interval that produces no more than the maxBucketCount
|
||||
* for the given time range.
|
||||
*
|
||||
* @param maxBucketCount maximum number of buckets to create
|
||||
* @param duration amount of time covered by the agg
|
||||
*/
|
||||
export function calcAutoIntervalLessThan(maxBucketCount: number, duration: number) {
|
||||
const maxPerBucketMs = getPerBucketMs(maxBucketCount, duration);
|
||||
|
||||
for (const { interval } of boundsDescending) {
|
||||
// Find the highest interval which meets our per bucket limitation.
|
||||
if (interval <= maxPerBucketMs) {
|
||||
return moment.duration(interval);
|
||||
}
|
||||
}
|
||||
|
||||
// If the max is smaller than any of our intervals, then we'll use it for the interval as-is.
|
||||
return normalizeMinimumInterval(maxPerBucketMs);
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import dateMath from '@elastic/datemath';
|
||||
|
||||
import { parseEsInterval } from '../../../../../../../../../../src/plugins/data/public';
|
||||
|
||||
const unitsDesc = dateMath.unitsDesc;
|
||||
const largeMax = unitsDesc.indexOf('M');
|
||||
|
||||
/**
|
||||
* Convert a moment.duration into an es
|
||||
* compatible expression, and provide
|
||||
* associated metadata
|
||||
*
|
||||
* @param {moment.duration} duration
|
||||
* @return {object}
|
||||
*/
|
||||
export function convertDurationToNormalizedEsInterval(duration) {
|
||||
for (let i = 0; i < unitsDesc.length; i++) {
|
||||
const unit = unitsDesc[i];
|
||||
const val = duration.as(unit);
|
||||
// find a unit that rounds neatly
|
||||
if (val >= 1 && Math.floor(val) === val) {
|
||||
// if the unit is "large", like years, but
|
||||
// isn't set to 1 ES will puke. So keep going until
|
||||
// we get out of the "large" units
|
||||
if (i <= largeMax && val !== 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return {
|
||||
value: val,
|
||||
unit: unit,
|
||||
expression: val + unit,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const ms = duration.as('ms');
|
||||
return {
|
||||
value: ms,
|
||||
unit: 'ms',
|
||||
expression: ms + 'ms',
|
||||
};
|
||||
}
|
||||
|
||||
export function convertIntervalToEsInterval(interval) {
|
||||
const { value, unit } = parseEsInterval(interval);
|
||||
return {
|
||||
value,
|
||||
unit,
|
||||
expression: interval,
|
||||
};
|
||||
}
|
|
@ -4,6 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export async function getDateFormat(req) {
|
||||
return await req.getUiSettingsService().get('dateFormat');
|
||||
}
|
||||
export const TimeBuckets: any;
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export { TimeBuckets } from './time_buckets';
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { calcAutoIntervalLessThan, calcAutoIntervalNear } from './calc_auto_interval';
|
||||
import {
|
||||
convertDurationToNormalizedEsInterval,
|
||||
convertIntervalToEsInterval,
|
||||
} from './calc_es_interval';
|
||||
import { fieldFormats, parseInterval } from '../../../../../../../../../../src/plugins/data/public';
|
||||
|
||||
function isValidMoment(m) {
|
||||
return m && 'isValid' in m && m.isValid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for wrapping the concept of an "Interval",
|
||||
* which describes a timespan that will separate moments.
|
||||
*
|
||||
* @param {state} object - one of ""
|
||||
* @param {[type]} display [description]
|
||||
*/
|
||||
function TimeBuckets(uiSettings, dataPlugin) {
|
||||
this.uiSettings = uiSettings;
|
||||
this.dataPlugin = dataPlugin;
|
||||
return TimeBuckets.__cached__(this);
|
||||
}
|
||||
|
||||
/****
|
||||
* PUBLIC API
|
||||
****/
|
||||
|
||||
/**
|
||||
* Set the bounds that these buckets are expected to cover.
|
||||
* This is required to support interval "auto" as well
|
||||
* as interval scaling.
|
||||
*
|
||||
* @param {object} input - an object with properties min and max,
|
||||
* representing the edges for the time span
|
||||
* we should cover
|
||||
*
|
||||
* @returns {undefined}
|
||||
*/
|
||||
TimeBuckets.prototype.setBounds = function(input) {
|
||||
if (!input) return this.clearBounds();
|
||||
|
||||
let bounds;
|
||||
if (_.isPlainObject(input)) {
|
||||
// accept the response from timefilter.getActiveBounds()
|
||||
bounds = [input.min, input.max];
|
||||
} else {
|
||||
bounds = Array.isArray(input) ? input : [];
|
||||
}
|
||||
|
||||
const moments = _(bounds)
|
||||
.map(_.ary(moment, 1))
|
||||
.sortBy(Number);
|
||||
|
||||
const valid = moments.size() === 2 && moments.every(isValidMoment);
|
||||
if (!valid) {
|
||||
this.clearBounds();
|
||||
throw new Error('invalid bounds set: ' + input);
|
||||
}
|
||||
|
||||
this._lb = moments.shift();
|
||||
this._ub = moments.pop();
|
||||
if (this.getDuration().asSeconds() < 0) {
|
||||
throw new TypeError('Intervals must be positive');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear the stored bounds
|
||||
*
|
||||
* @return {undefined}
|
||||
*/
|
||||
TimeBuckets.prototype.clearBounds = function() {
|
||||
this._lb = this._ub = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check to see if we have received bounds yet
|
||||
*
|
||||
* @return {Boolean}
|
||||
*/
|
||||
TimeBuckets.prototype.hasBounds = function() {
|
||||
return isValidMoment(this._ub) && isValidMoment(this._lb);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the current bounds, if we have any.
|
||||
*
|
||||
* THIS DOES NOT CLONE THE BOUNDS, so editing them
|
||||
* may have unexpected side-effects. Always
|
||||
* call bounds.min.clone() before editing
|
||||
*
|
||||
* @return {object|undefined} - If bounds are not defined, this
|
||||
* returns undefined, else it returns the bounds
|
||||
* for these buckets. This object has two props,
|
||||
* min and max. Each property will be a moment()
|
||||
* object
|
||||
*
|
||||
*/
|
||||
TimeBuckets.prototype.getBounds = function() {
|
||||
if (!this.hasBounds()) return;
|
||||
return {
|
||||
min: this._lb,
|
||||
max: this._ub,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a moment duration object representing
|
||||
* the distance between the bounds, if the bounds
|
||||
* are set.
|
||||
*
|
||||
* @return {moment.duration|undefined}
|
||||
*/
|
||||
TimeBuckets.prototype.getDuration = function() {
|
||||
if (!this.hasBounds()) return;
|
||||
return moment.duration(this._ub - this._lb, 'ms');
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the interval at which buckets should be
|
||||
* generated.
|
||||
*
|
||||
* Input can be one of the following:
|
||||
* - Any object from src/legacy/ui/agg_types/buckets/_interval_options.js
|
||||
* - "auto"
|
||||
* - Pass a valid moment unit
|
||||
* - a moment.duration object.
|
||||
*
|
||||
* @param {object|string|moment.duration} input - see desc
|
||||
*/
|
||||
TimeBuckets.prototype.setInterval = function(input) {
|
||||
// Preserve the original units because they're lost when the interval is converted to a
|
||||
// moment duration object.
|
||||
this.originalInterval = input;
|
||||
|
||||
let interval = input;
|
||||
|
||||
// selection object -> val
|
||||
if (_.isObject(input)) {
|
||||
interval = input.val;
|
||||
}
|
||||
|
||||
if (!interval || interval === 'auto') {
|
||||
this._i = 'auto';
|
||||
return;
|
||||
}
|
||||
|
||||
if (_.isString(interval)) {
|
||||
input = interval;
|
||||
interval = parseInterval(interval);
|
||||
if (+interval === 0) {
|
||||
interval = null;
|
||||
}
|
||||
}
|
||||
|
||||
// if the value wasn't converted to a duration, and isn't
|
||||
// already a duration, we have a problem
|
||||
if (!moment.isDuration(interval)) {
|
||||
throw new TypeError('"' + input + '" is not a valid interval.');
|
||||
}
|
||||
|
||||
this._i = interval;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the interval for the buckets. If the
|
||||
* number of buckets created by the interval set
|
||||
* is larger than config:histogram:maxBars then the
|
||||
* interval will be scaled up. If the number of buckets
|
||||
* created is less than one, the interval is scaled back.
|
||||
*
|
||||
* The interval object returned is a moment.duration
|
||||
* object that has been decorated with the following
|
||||
* properties.
|
||||
*
|
||||
* interval.description: a text description of the interval.
|
||||
* designed to be used list "field per {{ desc }}".
|
||||
* - "minute"
|
||||
* - "10 days"
|
||||
* - "3 years"
|
||||
*
|
||||
* interval.expr: the elasticsearch expression that creates this
|
||||
* interval. If the interval does not properly form an elasticsearch
|
||||
* expression it will be forced into one.
|
||||
*
|
||||
* interval.scaled: the interval was adjusted to
|
||||
* accommodate the maxBars setting.
|
||||
*
|
||||
* interval.scale: the number that y-values should be
|
||||
* multiplied by
|
||||
*
|
||||
* interval.scaleDescription: a description that reflects
|
||||
* the values which will be produced by using the
|
||||
* interval.scale.
|
||||
*
|
||||
*
|
||||
* @return {[type]} [description]
|
||||
*/
|
||||
TimeBuckets.prototype.getInterval = function(useNormalizedEsInterval = true) {
|
||||
const self = this;
|
||||
const duration = self.getDuration();
|
||||
const parsedInterval = readInterval();
|
||||
|
||||
if (useNormalizedEsInterval) {
|
||||
return decorateInterval(maybeScaleInterval(parsedInterval));
|
||||
} else {
|
||||
return decorateInterval(parsedInterval);
|
||||
}
|
||||
|
||||
// either pull the interval from state or calculate the auto-interval
|
||||
function readInterval() {
|
||||
const interval = self._i;
|
||||
if (moment.isDuration(interval)) return interval;
|
||||
return calcAutoIntervalNear(this.uiSettings.get('histogram:barTarget'), Number(duration));
|
||||
}
|
||||
|
||||
// check to see if the interval should be scaled, and scale it if so
|
||||
function maybeScaleInterval(interval) {
|
||||
if (!self.hasBounds()) return interval;
|
||||
|
||||
const maxLength = this.uiSettings.get('histogram:maxBars');
|
||||
const approxLen = duration / interval;
|
||||
let scaled;
|
||||
|
||||
if (approxLen > maxLength) {
|
||||
scaled = calcAutoIntervalLessThan(maxLength, Number(duration));
|
||||
} else {
|
||||
return interval;
|
||||
}
|
||||
|
||||
if (+scaled === +interval) return interval;
|
||||
|
||||
decorateInterval(interval);
|
||||
return _.assign(scaled, {
|
||||
preScaled: interval,
|
||||
scale: interval / scaled,
|
||||
scaled: true,
|
||||
});
|
||||
}
|
||||
|
||||
// append some TimeBuckets specific props to the interval
|
||||
function decorateInterval(interval) {
|
||||
const esInterval = useNormalizedEsInterval
|
||||
? convertDurationToNormalizedEsInterval(interval)
|
||||
: convertIntervalToEsInterval(self.originalInterval);
|
||||
interval.esValue = esInterval.value;
|
||||
interval.esUnit = esInterval.unit;
|
||||
interval.expression = esInterval.expression;
|
||||
interval.overflow = duration > interval ? moment.duration(interval - duration) : false;
|
||||
|
||||
const prettyUnits = moment.normalizeUnits(esInterval.unit);
|
||||
if (esInterval.value === 1) {
|
||||
interval.description = prettyUnits;
|
||||
} else {
|
||||
interval.description = esInterval.value + ' ' + prettyUnits + 's';
|
||||
}
|
||||
|
||||
return interval;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a date format string that will represent dates that
|
||||
* progress at our interval.
|
||||
*
|
||||
* Since our interval can be as small as 1ms, the default
|
||||
* date format is usually way too much. with `dateFormat:scaled`
|
||||
* users can modify how dates are formatted within series
|
||||
* produced by TimeBuckets
|
||||
*
|
||||
* @return {string}
|
||||
*/
|
||||
TimeBuckets.prototype.getScaledDateFormat = function() {
|
||||
const interval = this.getInterval();
|
||||
const rules = this.uiSettings.get('dateFormat:scaled');
|
||||
|
||||
for (let i = rules.length - 1; i >= 0; i--) {
|
||||
const rule = rules[i];
|
||||
if (!rule[0] || interval >= moment.duration(rule[0])) {
|
||||
return rule[1];
|
||||
}
|
||||
}
|
||||
|
||||
return this.uiSettings.get('dateFormat');
|
||||
};
|
||||
|
||||
TimeBuckets.prototype.getScaledDateFormatter = function() {
|
||||
const fieldFormatsService = this.dataPlugin.fieldFormats;
|
||||
const DateFieldFormat = fieldFormatsService.getType(fieldFormats.FIELD_FORMAT_IDS.DATE);
|
||||
|
||||
return new DateFieldFormat(
|
||||
{
|
||||
pattern: this.getScaledDateFormat(),
|
||||
},
|
||||
configPath => this.uiSettings.get(configPath)
|
||||
);
|
||||
};
|
||||
|
||||
TimeBuckets.__cached__ = function(self) {
|
||||
let cache = {};
|
||||
const sameMoment = same(moment.isMoment);
|
||||
const sameDuration = same(moment.isDuration);
|
||||
|
||||
const desc = {
|
||||
__cached__: {
|
||||
value: self,
|
||||
},
|
||||
};
|
||||
|
||||
const breakers = {
|
||||
setBounds: 'bounds',
|
||||
clearBounds: 'bounds',
|
||||
setInterval: 'interval',
|
||||
};
|
||||
|
||||
const resources = {
|
||||
bounds: {
|
||||
setup: function() {
|
||||
return [self._lb, self._ub];
|
||||
},
|
||||
changes: function(prev) {
|
||||
return !sameMoment(prev[0], self._lb) || !sameMoment(prev[1], self._ub);
|
||||
},
|
||||
},
|
||||
interval: {
|
||||
setup: function() {
|
||||
return self._i;
|
||||
},
|
||||
changes: function(prev) {
|
||||
return !sameDuration(prev, this._i);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function cachedGetter(prop) {
|
||||
return {
|
||||
value: function cachedGetter(...rest) {
|
||||
if (cache.hasOwnProperty(prop)) {
|
||||
return cache[prop];
|
||||
}
|
||||
|
||||
return (cache[prop] = self[prop](...rest));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function cacheBreaker(prop) {
|
||||
const resource = resources[breakers[prop]];
|
||||
const setup = resource.setup;
|
||||
const changes = resource.changes;
|
||||
const fn = self[prop];
|
||||
|
||||
return {
|
||||
value: function cacheBreaker() {
|
||||
const prev = setup.call(self);
|
||||
const ret = fn.apply(self, arguments);
|
||||
|
||||
if (changes.call(self, prev)) {
|
||||
cache = {};
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function same(checkType) {
|
||||
return function(a, b) {
|
||||
if (a === b) return true;
|
||||
if (checkType(a) === checkType(b)) return +a === +b;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
_.forOwn(TimeBuckets.prototype, function(fn, prop) {
|
||||
if (prop[0] === '_') return;
|
||||
|
||||
if (breakers.hasOwnProperty(prop)) {
|
||||
desc[prop] = cacheBreaker(prop);
|
||||
} else {
|
||||
desc[prop] = cachedGetter(prop);
|
||||
}
|
||||
});
|
||||
|
||||
return Object.create(self, desc);
|
||||
};
|
||||
|
||||
export { TimeBuckets };
|
|
@ -19,16 +19,17 @@ import {
|
|||
ScaleType,
|
||||
Settings,
|
||||
} from '@elastic/charts';
|
||||
import { TimeBuckets } from 'ui/time_buckets';
|
||||
import dateMath from '@elastic/datemath';
|
||||
import moment from 'moment-timezone';
|
||||
import { EuiCallOut, EuiLoadingChart, EuiSpacer, EuiEmptyPrompt, EuiText } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
/* TODO: This file was copied from ui/time_buckets for NP migration. We should clean this up and add TS support */
|
||||
import { TimeBuckets } from './lib/time_buckets';
|
||||
import { getThresholdAlertVisualizationData } from './lib/api';
|
||||
import { comparators, aggregationTypes } from './expression';
|
||||
import { useAppDependencies } from '../../../app_context';
|
||||
import { Alert } from '../../../../types';
|
||||
import { DataPublicPluginStart } from '../../../../../../../../src/plugins/data/public';
|
||||
|
||||
const customTheme = () => {
|
||||
return {
|
||||
|
@ -83,9 +84,13 @@ const getThreshold = (alertParams: any) => {
|
|||
);
|
||||
};
|
||||
|
||||
const getTimeBuckets = (alertParams: any) => {
|
||||
const getTimeBuckets = (
|
||||
uiSettings: IUiSettingsClient,
|
||||
dataPlugin: DataPublicPluginStart,
|
||||
alertParams: any
|
||||
) => {
|
||||
const domain = getDomain(alertParams);
|
||||
const timeBuckets = new TimeBuckets();
|
||||
const timeBuckets = new TimeBuckets(uiSettings, dataPlugin);
|
||||
timeBuckets.setBounds(domain);
|
||||
return timeBuckets;
|
||||
};
|
||||
|
@ -95,12 +100,12 @@ interface Props {
|
|||
}
|
||||
|
||||
export const ThresholdVisualization: React.FunctionComponent<Props> = ({ alert }) => {
|
||||
const { http, uiSettings, toastNotifications } = useAppDependencies();
|
||||
const { http, uiSettings, toastNotifications, charts, dataPlugin } = useAppDependencies();
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<undefined | any>(undefined);
|
||||
const [visualizationData, setVisualizationData] = useState<Record<string, any>>([]);
|
||||
|
||||
const chartsTheme = npStart.plugins.charts.theme.useChartsTheme();
|
||||
const chartsTheme = charts.theme.useChartsTheme();
|
||||
const {
|
||||
index,
|
||||
timeField,
|
||||
|
@ -118,7 +123,7 @@ export const ThresholdVisualization: React.FunctionComponent<Props> = ({ alert }
|
|||
} = alert.params;
|
||||
|
||||
const domain = getDomain(alert.params);
|
||||
const timeBuckets = new TimeBuckets();
|
||||
const timeBuckets = new TimeBuckets(uiSettings, dataPlugin);
|
||||
timeBuckets.setBounds(domain);
|
||||
const interval = timeBuckets.getInterval().expression;
|
||||
const visualizeOptions = {
|
||||
|
@ -226,7 +231,7 @@ export const ThresholdVisualization: React.FunctionComponent<Props> = ({ alert }
|
|||
const dateFormatter = (d: number) => {
|
||||
return moment(d)
|
||||
.tz(timezone)
|
||||
.format(getTimeBuckets(alert.params).getScaledDateFormat());
|
||||
.format(getTimeBuckets(uiSettings, dataPlugin, alert.params).getScaledDateFormat());
|
||||
};
|
||||
const aggLabel = aggregationTypes[aggType].text;
|
||||
return (
|
|
@ -37,11 +37,7 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
|
|||
},
|
||||
history,
|
||||
}) => {
|
||||
const {
|
||||
chrome,
|
||||
capabilities,
|
||||
legacy: { MANAGEMENT_BREADCRUMB },
|
||||
} = useAppDependencies();
|
||||
const { chrome, capabilities, setBreadcrumbs } = useAppDependencies();
|
||||
|
||||
const canShowActions = hasShowActionsCapability(capabilities);
|
||||
const canShowAlerts = hasShowAlertsCapability(capabilities);
|
||||
|
@ -80,9 +76,9 @@ export const TriggersActionsUIHome: React.FunctionComponent<RouteComponentProps<
|
|||
|
||||
// Set breadcrumb and page title
|
||||
useEffect(() => {
|
||||
chrome.setBreadcrumbs([MANAGEMENT_BREADCRUMB, getCurrentBreadcrumb(section || 'home')]);
|
||||
setBreadcrumbs([getCurrentBreadcrumb(section || 'home')]);
|
||||
chrome.docTitle.change(getCurrentDocTitle(section || 'home'));
|
||||
}, [section, chrome, MANAGEMENT_BREADCRUMB]);
|
||||
}, [section, chrome, setBreadcrumbs]);
|
||||
|
||||
return (
|
||||
<EuiPageBody>
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { ActionConnector, ActionConnectorWithoutId, ActionType } from '../../types';
|
||||
import { httpServiceMock } from '../../../../../../../../src/core/public/mocks';
|
||||
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
|
||||
import {
|
||||
createActionConnector,
|
||||
deleteActions,
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { Alert, AlertType } from '../../types';
|
||||
import { httpServiceMock } from '../../../../../../../../src/core/public/mocks';
|
||||
import { httpServiceMock } from '../../../../../../src/core/public/mocks';
|
||||
import {
|
||||
createAlert,
|
||||
deleteAlert,
|
|
@ -5,11 +5,14 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ValidationResult, ActionConnector } from '../../../types';
|
||||
import { ActionConnectorForm } from './action_connector_form';
|
||||
import { AppContextProvider } from '../../app_context';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
|
||||
describe('action_connector_form', () => {
|
||||
|
@ -26,6 +29,8 @@ describe('action_connector_form', () => {
|
|||
deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -38,9 +43,7 @@ describe('action_connector_form', () => {
|
|||
show: true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: {} as any,
|
||||
};
|
|
@ -5,12 +5,15 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ActionTypeMenu } from './action_type_menu';
|
||||
import { ValidationResult } from '../../../types';
|
||||
import { AppContextProvider } from '../../app_context';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
|
||||
describe('connector_add_flyout', () => {
|
||||
|
@ -28,6 +31,8 @@ describe('connector_add_flyout', () => {
|
|||
deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -40,9 +45,7 @@ describe('connector_add_flyout', () => {
|
|||
show: true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: {} as any,
|
||||
};
|
|
@ -5,13 +5,16 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { ConnectorAddFlyout } from './connector_add_flyout';
|
||||
import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ValidationResult } from '../../../types';
|
||||
import { AppContextProvider } from '../../app_context';
|
||||
import { AppDeps } from '../../app';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
|
||||
describe('connector_add_flyout', () => {
|
||||
|
@ -29,6 +32,8 @@ describe('connector_add_flyout', () => {
|
|||
deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -41,9 +46,7 @@ describe('connector_add_flyout', () => {
|
|||
show: true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: {} as any,
|
||||
};
|
|
@ -5,12 +5,14 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { ConnectorAddModal } from './connector_add_modal';
|
||||
import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ValidationResult } from '../../../types';
|
||||
import { AppContextProvider } from '../../app_context';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
import { AppDeps } from '../../app';
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
|
||||
|
@ -33,6 +35,8 @@ describe('connector_add_modal', () => {
|
|||
injectedMetadata: mocks.injectedMetadata,
|
||||
http: mocks.http,
|
||||
uiSettings: mocks.uiSettings,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
capabilities: {
|
||||
...capabilities,
|
||||
actions: {
|
||||
|
@ -41,9 +45,7 @@ describe('connector_add_modal', () => {
|
|||
show: true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: {} as any,
|
||||
};
|
|
@ -5,12 +5,15 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { ActionsConnectorsContextProvider } from '../../context/actions_connectors_context';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ValidationResult } from '../../../types';
|
||||
import { ConnectorEditFlyout } from './connector_edit_flyout';
|
||||
import { AppContextProvider } from '../../app_context';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
let deps: any;
|
||||
|
||||
|
@ -27,6 +30,8 @@ describe('connector_edit_flyout', () => {
|
|||
deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -39,9 +44,7 @@ describe('connector_edit_flyout', () => {
|
|||
show: true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: {} as any,
|
||||
};
|
|
@ -6,11 +6,14 @@
|
|||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { ActionsConnectorsList } from './actions_connectors_list';
|
||||
import { coreMock } from '../../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../../src/core/public/mocks';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { actionTypeRegistryMock } from '../../../action_type_registry.mock';
|
||||
import { AppContextProvider } from '../../../app_context';
|
||||
import { chartPluginMock } from '../../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';
|
||||
|
||||
jest.mock('../../../lib/action_connector_api', () => ({
|
||||
loadAllActions: jest.fn(),
|
||||
loadActionTypes: jest.fn(),
|
||||
|
@ -52,6 +55,8 @@ describe('actions_connectors_list component empty', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -64,9 +69,7 @@ describe('actions_connectors_list component empty', () => {
|
|||
'actions:delete': true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: {} as any,
|
||||
};
|
||||
|
@ -149,6 +152,8 @@ describe('actions_connectors_list component with items', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -161,9 +166,7 @@ describe('actions_connectors_list component with items', () => {
|
|||
'actions:delete': true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: {
|
||||
get() {
|
||||
return null;
|
||||
|
@ -233,6 +236,8 @@ describe('actions_connectors_list component empty with show only capability', ()
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -245,9 +250,7 @@ describe('actions_connectors_list component empty with show only capability', ()
|
|||
'actions:delete': false,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: {
|
||||
get() {
|
||||
return null;
|
||||
|
@ -322,6 +325,8 @@ describe('actions_connectors_list with show only capability', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
|
@ -334,9 +339,7 @@ describe('actions_connectors_list with show only capability', () => {
|
|||
'actions:delete': false,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: {
|
||||
get() {
|
||||
return null;
|
|
@ -6,7 +6,7 @@
|
|||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { AlertAdd } from './alert_add';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
import { ValidationResult } from '../../../types';
|
||||
|
@ -14,6 +14,8 @@ import { AppContextProvider } from '../../app_context';
|
|||
import { AppDeps } from '../../app';
|
||||
import { AlertsContextProvider } from '../../context/alerts_context';
|
||||
import { alertTypeRegistryMock } from '../../alert_type_registry.mock';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
|
@ -38,6 +40,8 @@ describe('alert_add', () => {
|
|||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
uiSettings: mockes.uiSettings,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
capabilities: {
|
||||
...capabilities,
|
||||
alerting: {
|
||||
|
@ -46,9 +50,7 @@ describe('alert_add', () => {
|
|||
show: true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: alertTypeRegistry as any,
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import React, { Fragment } from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { actionTypeRegistryMock } from '../../action_type_registry.mock';
|
||||
|
@ -14,6 +14,8 @@ import { ValidationResult, Alert } from '../../../types';
|
|||
import { AlertForm } from './alert_form';
|
||||
import { AppContextProvider } from '../../app_context';
|
||||
import { AppDeps } from '../../app';
|
||||
import { chartPluginMock } from '../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../src/plugins/data/public/mocks';
|
||||
const actionTypeRegistry = actionTypeRegistryMock.create();
|
||||
const alertTypeRegistry = alertTypeRegistryMock.create();
|
||||
describe('alert_form', () => {
|
||||
|
@ -58,6 +60,8 @@ describe('alert_form', () => {
|
|||
injectedMetadata: mockes.injectedMetadata,
|
||||
http: mockes.http,
|
||||
uiSettings: mockes.uiSettings,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
capabilities: {
|
||||
...capabilities,
|
||||
siem: {
|
||||
|
@ -66,9 +70,7 @@ describe('alert_form', () => {
|
|||
'alerting:delete': false,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: alertTypeRegistry as any,
|
||||
};
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
import * as React from 'react';
|
||||
import { mountWithIntl } from 'test_utils/enzyme_helpers';
|
||||
import { coreMock } from '../../../../../../../../../../src/core/public/mocks';
|
||||
import { coreMock } from '../../../../../../../../src/core/public/mocks';
|
||||
import { ReactWrapper } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import { actionTypeRegistryMock } from '../../../action_type_registry.mock';
|
||||
|
@ -13,6 +13,9 @@ import { alertTypeRegistryMock } from '../../../alert_type_registry.mock';
|
|||
import { AlertsList } from './alerts_list';
|
||||
import { ValidationResult } from '../../../../types';
|
||||
import { AppContextProvider } from '../../../app_context';
|
||||
import { chartPluginMock } from '../../../../../../../../src/plugins/charts/public/mocks';
|
||||
import { dataPluginMock } from '../../../../../../../../src/plugins/data/public/mocks';
|
||||
|
||||
jest.mock('../../../lib/action_connector_api', () => ({
|
||||
loadActionTypes: jest.fn(),
|
||||
loadAllActions: jest.fn(),
|
||||
|
@ -86,6 +89,8 @@ describe('alerts_list component empty', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: {
|
||||
getInjectedVar(name: string) {
|
||||
|
@ -104,9 +109,7 @@ describe('alerts_list component empty', () => {
|
|||
'alerting:delete': true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: alertTypeRegistry as any,
|
||||
};
|
||||
|
@ -211,6 +214,8 @@ describe('alerts_list component with items', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: {
|
||||
getInjectedVar(name: string) {
|
||||
|
@ -229,9 +234,7 @@ describe('alerts_list component with items', () => {
|
|||
'alerting:delete': true,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: alertTypeRegistry as any,
|
||||
};
|
||||
|
@ -298,6 +301,8 @@ describe('alerts_list component empty with show only capability', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: {
|
||||
getInjectedVar(name: string) {
|
||||
|
@ -316,9 +321,7 @@ describe('alerts_list component empty with show only capability', () => {
|
|||
'alerting:delete': false,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: {
|
||||
get() {
|
||||
return null;
|
||||
|
@ -420,6 +423,8 @@ describe('alerts_list with show only capability', () => {
|
|||
const deps = {
|
||||
chrome,
|
||||
docLinks,
|
||||
dataPlugin: dataPluginMock.createStartContract(),
|
||||
charts: chartPluginMock.createStartContract(),
|
||||
toastNotifications: mockes.notifications.toasts,
|
||||
injectedMetadata: {
|
||||
getInjectedVar(name: string) {
|
||||
|
@ -438,9 +443,7 @@ describe('alerts_list with show only capability', () => {
|
|||
'alerting:delete': false,
|
||||
},
|
||||
},
|
||||
legacy: {
|
||||
MANAGEMENT_BREADCRUMB: { set: () => {} } as any,
|
||||
},
|
||||
setBreadcrumbs: jest.fn(),
|
||||
actionTypeRegistry: actionTypeRegistry as any,
|
||||
alertTypeRegistry: alertTypeRegistry as any,
|
||||
};
|
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