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:
Mike Côté 2020-02-06 10:50:26 -05:00 committed by GitHub
parent e92ead7324
commit 8951424f82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
107 changed files with 870 additions and 277 deletions

View file

@ -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",

View file

@ -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();

View file

@ -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';

View file

@ -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}`,
});
}

View file

@ -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);
}
});
};

View file

@ -2,5 +2,6 @@
"id": "triggers_actions_ui",
"version": "kibana",
"server": false,
"ui": true
"ui": true,
"requiredPlugins": ["management", "charts", "data"]
}

View file

@ -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>;

View file

@ -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
);

View file

@ -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);
});
});

View file

@ -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);
}

View file

@ -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,
};
}

View file

@ -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;

View file

@ -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';

View file

@ -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 };

View file

@ -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 (

View file

@ -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>

View file

@ -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,

View file

@ -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,

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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;

View file

@ -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,
};

View file

@ -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,
};

View file

@ -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