Skip to content

Commit

Permalink
Merge branch 'master' into pr/57821
Browse files Browse the repository at this point in the history
  • Loading branch information
elasticmachine committed Feb 27, 2020
2 parents 418b287 + c97bc86 commit 73e19b2
Show file tree
Hide file tree
Showing 13 changed files with 237 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export interface RenderDeps {
savedDashboards: SavedObjectLoader;
dashboardConfig: KibanaLegacyStart['dashboardConfig'];
dashboardCapabilities: any;
embeddableCapabilities: {
visualizeCapabilities: any;
mapsCapabilities: any;
};
uiSettings: IUiSettingsClient;
chrome: ChromeStart;
addBasePath: (path: string) => string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export class DashboardAppController {
embeddable,
share,
dashboardCapabilities,
embeddableCapabilities: { visualizeCapabilities, mapsCapabilities },
data: { query: queryService },
core: {
notifications,
Expand All @@ -134,7 +135,6 @@ export class DashboardAppController {
} = syncQueryStateWithUrl(queryService, kbnUrlStateStorage);

let lastReloadRequestTime = 0;

const dash = ($scope.dash = $route.current.locals.dash);
if (dash.id) {
chrome.docTitle.change(dash.title);
Expand Down Expand Up @@ -180,11 +180,18 @@ export class DashboardAppController {
dashboardStateManager.getIsViewMode() &&
!dashboardConfig.getHideWriteControls();

const getIsEmptyInReadonlyMode = () =>
!dashboardStateManager.getPanels().length &&
!getShouldShowEditHelp() &&
!getShouldShowViewHelp() &&
dashboardConfig.getHideWriteControls();
const shouldShowUnauthorizedEmptyState = () => {
const readonlyMode =
!dashboardStateManager.getPanels().length &&
!getShouldShowEditHelp() &&
!getShouldShowViewHelp() &&
dashboardConfig.getHideWriteControls();
const userHasNoPermissions =
!dashboardStateManager.getPanels().length &&
!visualizeCapabilities.save &&
!mapsCapabilities.save;
return readonlyMode || userHasNoPermissions;
};

const addVisualization = () => {
navActions[TopNavIds.VISUALIZE]();
Expand Down Expand Up @@ -250,7 +257,7 @@ export class DashboardAppController {
}
const shouldShowEditHelp = getShouldShowEditHelp();
const shouldShowViewHelp = getShouldShowViewHelp();
const isEmptyInReadonlyMode = getIsEmptyInReadonlyMode();
const isEmptyInReadonlyMode = shouldShowUnauthorizedEmptyState();
return {
id: dashboardStateManager.savedDashboard.id || '',
filters: queryFilter.getFilters(),
Expand Down Expand Up @@ -307,7 +314,7 @@ export class DashboardAppController {
dashboardContainer.renderEmpty = () => {
const shouldShowEditHelp = getShouldShowEditHelp();
const shouldShowViewHelp = getShouldShowViewHelp();
const isEmptyInReadOnlyMode = getIsEmptyInReadonlyMode();
const isEmptyInReadOnlyMode = shouldShowUnauthorizedEmptyState();
const isEmptyState = shouldShowEditHelp || shouldShowViewHelp || isEmptyInReadOnlyMode;
return isEmptyState ? (
<DashboardEmptyScreen
Expand Down
4 changes: 4 additions & 0 deletions src/legacy/core_plugins/kibana/public/dashboard/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ export class DashboardPlugin implements Plugin {
savedQueryService: dataStart.query.savedQueries,
embeddable,
dashboardCapabilities: coreStart.application.capabilities.dashboard,
embeddableCapabilities: {
visualizeCapabilities: coreStart.application.capabilities.visualize,
mapsCapabilities: coreStart.application.capabilities.maps,
},
localStorage: new Storage(localStorage),
};
const { renderApp } = await import('./np_ready/application');
Expand Down
1 change: 0 additions & 1 deletion x-pack/legacy/plugins/maps/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import 'uiExports/inspectorViews';
import 'uiExports/search';
import 'uiExports/embeddableFactories';
import 'uiExports/embeddableActions';
import 'ui/agg_types';

import 'ui/autoload/all';
import 'react-vis/dist/style.css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@
import { InnerJoin } from './inner_join';

jest.mock('../../kibana_services', () => {});
jest.mock('ui/agg_types', () => {
class MockSchemas {}
return {
Schemas: MockSchemas,
};
});
jest.mock('ui/timefilter', () => {});
jest.mock('../vector_layer', () => {});

Expand Down
22 changes: 6 additions & 16 deletions x-pack/legacy/plugins/maps/public/layers/sources/es_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
import { createExtentFilter } from '../../elasticsearch_geo_utils';
import { timefilter } from 'ui/timefilter';
import _ from 'lodash';
import { AggConfigs } from 'ui/agg_types';
import { i18n } from '@kbn/i18n';
import uuid from 'uuid/v4';
import { copyPersistentState } from '../../reducers/util';
Expand Down Expand Up @@ -151,27 +150,18 @@ export class AbstractESSource extends AbstractVectorSource {
{ sourceQuery, query, timeFilters, filters, applyGlobalQuery },
0
);
const geoField = await this._getGeoField();
const indexPattern = await this.getIndexPattern();

const geoBoundsAgg = [
{
type: 'geo_bounds',
enabled: true,
params: {
field: geoField,
searchSource.setField('aggs', {
fitToBounds: {
geo_bounds: {
field: this._descriptor.geoField,
},
schema: 'metric',
},
];

const aggConfigs = new AggConfigs(indexPattern, geoBoundsAgg);
searchSource.setField('aggs', aggConfigs.toDsl());
});

let esBounds;
try {
const esResp = await searchSource.fetch();
esBounds = _.get(esResp, 'aggregations.1.bounds');
esBounds = _.get(esResp, 'aggregations.fitToBounds.bounds');
} catch (error) {
esBounds = {
top_left: {
Expand Down
12 changes: 11 additions & 1 deletion x-pack/plugins/infra/kibana.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,17 @@
"id": "infra",
"version": "8.0.0",
"kibanaVersion": "kibana",
"requiredPlugins": ["features", "apm", "usageCollection", "spaces", "home", "data", "data_enhanced", "metrics"],
"requiredPlugins": [
"features",
"apm",
"usageCollection",
"spaces",
"home",
"data",
"data_enhanced",
"metrics",
"alerting"
],
"server": true,
"ui": true,
"configPath": ["xpack", "infra"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { SpacesPluginSetup } from '../../../../../../plugins/spaces/server';
import { VisTypeTimeseriesSetup } from '../../../../../../../src/plugins/vis_type_timeseries/server';
import { APMPluginContract } from '../../../../../../plugins/apm/server';
import { HomeServerPluginSetup } from '../../../../../../../src/plugins/home/server';
import { PluginSetupContract as AlertingPluginContract } from '../../../../../../plugins/alerting/server';

// NP_TODO: Compose real types from plugins we depend on, no "any"
export interface InfraServerPluginDeps {
Expand All @@ -25,6 +26,7 @@ export interface InfraServerPluginDeps {
};
features: FeaturesPluginSetup;
apm: APMPluginContract;
alerting: AlertingPluginContract;
}

export interface CallWithRequestParams extends GenericParams {
Expand Down
7 changes: 7 additions & 0 deletions x-pack/plugins/infra/server/lib/alerting/index.ts
Original file line number Diff line number Diff line change
@@ -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 { registerAlertTypes } from './register_alert_types';
Original file line number Diff line number Diff line change
@@ -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 uuid from 'uuid';
import { i18n } from '@kbn/i18n';
import { schema } from '@kbn/config-schema';
import {
MetricThresholdAlertTypeParams,
Comparator,
AlertStates,
METRIC_THRESHOLD_ALERT_TYPE_ID,
} from './types';
import { AlertServices, PluginSetupContract } from '../../../../../alerting/server';

const FIRED_ACTIONS = {
id: 'metrics.threshold.fired',
name: i18n.translate('xpack.infra.metrics.alerting.threshold.fired', {
defaultMessage: 'Fired',
}),
};

async function getMetric(
{ callCluster }: AlertServices,
{ metric, aggType, timeUnit, timeSize, indexPattern }: MetricThresholdAlertTypeParams
) {
const interval = `${timeSize}${timeUnit}`;
const searchBody = {
query: {
bool: {
filter: [
{
range: {
'@timestamp': {
gte: `now-${interval}`,
},
},
exists: {
field: metric,
},
},
],
},
},
size: 0,
aggs: {
aggregatedIntervals: {
date_histogram: {
field: '@timestamp',
fixed_interval: interval,
},
aggregations: {
aggregatedValue: {
[aggType]: {
field: metric,
},
},
},
},
},
};

const result = await callCluster('search', {
body: searchBody,
index: indexPattern,
});

const { buckets } = result.aggregations.aggregatedIntervals;
const { value } = buckets[buckets.length - 1].aggregatedValue;
return value;
}

const comparatorMap = {
[Comparator.BETWEEN]: (value: number, [a, b]: number[]) =>
value >= Math.min(a, b) && value <= Math.max(a, b),
// `threshold` is always an array of numbers in case the BETWEEN comparator is
// used; all other compartors will just destructure the first value in the array
[Comparator.GT]: (a: number, [b]: number[]) => a > b,
[Comparator.LT]: (a: number, [b]: number[]) => a < b,
[Comparator.GT_OR_EQ]: (a: number, [b]: number[]) => a >= b,
[Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b,
};

export async function registerMetricThresholdAlertType(alertingPlugin: PluginSetupContract) {
if (!alertingPlugin) {
throw new Error(
'Cannot register metric threshold alert type. Both the actions and alerting plugins need to be enabled.'
);
}
const alertUUID = uuid.v4();

alertingPlugin.registerType({
id: METRIC_THRESHOLD_ALERT_TYPE_ID,
name: 'Metric Alert - Threshold',
validate: {
params: schema.object({
threshold: schema.arrayOf(schema.number()),
comparator: schema.string(),
aggType: schema.string(),
metric: schema.string(),
timeUnit: schema.string(),
timeSize: schema.number(),
indexPattern: schema.string(),
}),
},
defaultActionGroupId: FIRED_ACTIONS.id,
actionGroups: [FIRED_ACTIONS],
async executor({ services, params }) {
const { threshold, comparator } = params as MetricThresholdAlertTypeParams;
const alertInstance = services.alertInstanceFactory(alertUUID);
const currentValue = await getMetric(services, params as MetricThresholdAlertTypeParams);
if (typeof currentValue === 'undefined')
throw new Error('Could not get current value of metric');

const comparisonFunction = comparatorMap[comparator];

const isValueInAlertState = comparisonFunction(currentValue, threshold);

if (isValueInAlertState) {
alertInstance.scheduleActions(FIRED_ACTIONS.id, {
value: currentValue,
});
}

// Future use: ability to fetch display current alert state
alertInstance.replaceState({
alertState: isValueInAlertState ? AlertStates.ALERT : AlertStates.OK,
});
},
});
}
34 changes: 34 additions & 0 deletions x-pack/plugins/infra/server/lib/alerting/metric_threshold/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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 { MetricsExplorerAggregation } from '../../../../common/http_api/metrics_explorer';

export const METRIC_THRESHOLD_ALERT_TYPE_ID = 'metrics.alert.threshold';

export enum Comparator {
GT = '>',
LT = '<',
GT_OR_EQ = '>=',
LT_OR_EQ = '<=',
BETWEEN = 'between',
}

export enum AlertStates {
OK,
ALERT,
}

export type TimeUnit = 's' | 'm' | 'h' | 'd';

export interface MetricThresholdAlertTypeParams {
aggType: MetricsExplorerAggregation;
metric: string;
timeSize: number;
timeUnit: TimeUnit;
indexPattern: string;
threshold: number[];
comparator: Comparator;
}
20 changes: 20 additions & 0 deletions x-pack/plugins/infra/server/lib/alerting/register_alert_types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* 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 { PluginSetupContract } from '../../../../alerting/server';
import { registerMetricThresholdAlertType } from './metric_threshold/register_metric_threshold_alert_type';

const registerAlertTypes = (alertingPlugin: PluginSetupContract) => {
if (alertingPlugin) {
const registerFns = [registerMetricThresholdAlertType];

registerFns.forEach(fn => {
fn(alertingPlugin);
});
}
};

export { registerAlertTypes };
2 changes: 2 additions & 0 deletions x-pack/plugins/infra/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { InfraServerPluginDeps } from './lib/adapters/framework';
import { METRICS_FEATURE, LOGS_FEATURE } from './features';
import { UsageCollector } from './usage/usage_collector';
import { InfraStaticSourceConfiguration } from './lib/sources/types';
import { registerAlertTypes } from './lib/alerting';

export const config = {
schema: schema.object({
Expand Down Expand Up @@ -146,6 +147,7 @@ export class InfraServerPlugin {
]);

initInfraServer(this.libs);
registerAlertTypes(plugins.alerting);

// Telemetry
UsageCollector.registerUsageCollector(plugins.usageCollection);
Expand Down

0 comments on commit 73e19b2

Please sign in to comment.