mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Monitoring] NP Migration complete client cutover (#62908)
* Final phase before the complete cutover * NP migration * lint fix * More NP stuff * Moved Stack Monitoring client plugin outside legacy and fixed all tests * ... * Removed unused files * Fix for main links * Fixed more tests * Fixed redirect when clicking on SM icon again * Code review feedback * Addressed code review feedback * Fixed return value
This commit is contained in:
parent
6e3791ea12
commit
59315bc84d
569 changed files with 11410 additions and 2967 deletions
|
@ -30,6 +30,7 @@ target
|
|||
/x-pack/legacy/plugins/canvas/canvas_plugin_src/lib/flot-charts
|
||||
/x-pack/legacy/plugins/canvas/shareable_runtime/build
|
||||
/x-pack/legacy/plugins/canvas/storybook
|
||||
/x-pack/plugins/monitoring/public/lib/jquery_flot
|
||||
/x-pack/legacy/plugins/infra/common/graphql/types.ts
|
||||
/x-pack/legacy/plugins/infra/public/graphql/types.ts
|
||||
/x-pack/legacy/plugins/infra/server/graphql/types.ts
|
||||
|
|
|
@ -963,6 +963,12 @@ module.exports = {
|
|||
jquery: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['x-pack/plugins/monitoring/public/lib/jquery_flot/**/*.js'],
|
||||
env: {
|
||||
jquery: true,
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* TSVB overrides
|
||||
|
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -84,6 +84,7 @@
|
|||
/x-pack/legacy/plugins/ingest_manager/ @elastic/ingest-management
|
||||
/x-pack/plugins/observability/ @elastic/logs-metrics-ui @elastic/apm-ui @elastic/uptime @elastic/ingest-management
|
||||
/x-pack/legacy/plugins/monitoring/ @elastic/stack-monitoring-ui
|
||||
/x-pack/plugins/monitoring/ @elastic/stack-monitoring-ui
|
||||
/x-pack/plugins/uptime @elastic/uptime
|
||||
|
||||
# Machine Learning
|
||||
|
|
|
@ -38,6 +38,7 @@ export const IGNORE_FILE_GLOBS = [
|
|||
'x-pack/legacy/plugins/apm/**/*',
|
||||
'x-pack/legacy/plugins/canvas/tasks/**/*',
|
||||
'x-pack/legacy/plugins/canvas/canvas_plugin_src/**/*',
|
||||
'x-pack/plugins/monitoring/public/lib/jquery_flot/**/*',
|
||||
'**/.*',
|
||||
'**/{webpackShims,__mocks__}/**/*',
|
||||
'x-pack/docs/**/*',
|
||||
|
@ -160,12 +161,11 @@ export const TEMPORARILY_IGNORED_PATHS = [
|
|||
'webpackShims/ui-bootstrap.js',
|
||||
'x-pack/legacy/plugins/index_management/public/lib/editSettings.js',
|
||||
'x-pack/legacy/plugins/license_management/public/store/reducers/licenseManagement.js',
|
||||
'x-pack/legacy/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js',
|
||||
'x-pack/legacy/plugins/monitoring/public/icons/alert-blue.svg',
|
||||
'x-pack/legacy/plugins/monitoring/public/icons/health-gray.svg',
|
||||
'x-pack/legacy/plugins/monitoring/public/icons/health-green.svg',
|
||||
'x-pack/legacy/plugins/monitoring/public/icons/health-red.svg',
|
||||
'x-pack/legacy/plugins/monitoring/public/icons/health-yellow.svg',
|
||||
'x-pack/plugins/monitoring/public/components/sparkline/__mocks__/plugins/xpack_main/jquery_flot.js',
|
||||
'x-pack/plugins/monitoring/public/icons/health-gray.svg',
|
||||
'x-pack/plugins/monitoring/public/icons/health-green.svg',
|
||||
'x-pack/plugins/monitoring/public/icons/health-red.svg',
|
||||
'x-pack/plugins/monitoring/public/icons/health-yellow.svg',
|
||||
'x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/pdf/assets/fonts/noto/NotoSansCJKtc-Medium.ttf',
|
||||
'x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/pdf/assets/fonts/noto/NotoSansCJKtc-Regular.ttf',
|
||||
'x-pack/legacy/plugins/reporting/export_types/printable_pdf/server/lib/pdf/assets/fonts/roboto/Roboto-Italic.ttf',
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
agent
|
||||
node_modules
|
||||
bower_components
|
|
@ -1,128 +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 expect from '@kbn/expect';
|
||||
import moment from 'moment';
|
||||
import { formatTimestampToDuration } from '../format_timestamp_to_duration';
|
||||
import { CALCULATE_DURATION_SINCE, CALCULATE_DURATION_UNTIL } from '../constants';
|
||||
|
||||
const testTime = moment('2010-05-01'); // pick a date where adding/subtracting 2 months formats roundly to '2 months 0 days'
|
||||
const getTestTime = () => moment(testTime); // clones the obj so it's not mutated with .adds and .subtracts
|
||||
|
||||
/**
|
||||
* Test the moment-duration-format template
|
||||
*/
|
||||
describe('formatTimestampToDuration', () => {
|
||||
describe('format timestamp to duration - time since', () => {
|
||||
it('should format timestamp to human-readable duration', () => {
|
||||
// time inputs are a few "moments" extra from the time advertised by name
|
||||
const fiftyNineSeconds = getTestTime().subtract(59, 'seconds');
|
||||
expect(
|
||||
formatTimestampToDuration(fiftyNineSeconds, CALCULATE_DURATION_SINCE, getTestTime())
|
||||
).to.be('59 seconds');
|
||||
|
||||
const fiveMins = getTestTime()
|
||||
.subtract(5, 'minutes')
|
||||
.subtract(30, 'seconds');
|
||||
expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'6 mins'
|
||||
);
|
||||
|
||||
const sixHours = getTestTime()
|
||||
.subtract(6, 'hours')
|
||||
.subtract(30, 'minutes');
|
||||
expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'6 hrs 30 mins'
|
||||
);
|
||||
|
||||
const sevenDays = getTestTime()
|
||||
.subtract(7, 'days')
|
||||
.subtract(6, 'hours')
|
||||
.subtract(18, 'minutes');
|
||||
expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'7 days 6 hrs 18 mins'
|
||||
);
|
||||
|
||||
const eightWeeks = getTestTime()
|
||||
.subtract(8, 'weeks')
|
||||
.subtract(7, 'days')
|
||||
.subtract(6, 'hours')
|
||||
.subtract(18, 'minutes');
|
||||
expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'2 months 2 days'
|
||||
);
|
||||
|
||||
const oneHour = getTestTime().subtract(1, 'hour'); // should trim 0 min
|
||||
expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'1 hr'
|
||||
);
|
||||
|
||||
const oneDay = getTestTime().subtract(1, 'day'); // should trim 0 hrs
|
||||
expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'1 day'
|
||||
);
|
||||
|
||||
const twoMonths = getTestTime().subtract(2, 'month'); // should trim 0 days
|
||||
expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_SINCE, getTestTime())).to.be(
|
||||
'2 months'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('format timestamp to duration - time until', () => {
|
||||
it('should format timestamp to human-readable duration', () => {
|
||||
// time inputs are a few "moments" extra from the time advertised by name
|
||||
const fiftyNineSeconds = getTestTime().add(59, 'seconds');
|
||||
expect(
|
||||
formatTimestampToDuration(fiftyNineSeconds, CALCULATE_DURATION_UNTIL, getTestTime())
|
||||
).to.be('59 seconds');
|
||||
|
||||
const fiveMins = getTestTime().add(10, 'minutes');
|
||||
expect(formatTimestampToDuration(fiveMins, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'10 mins'
|
||||
);
|
||||
|
||||
const sixHours = getTestTime()
|
||||
.add(6, 'hours')
|
||||
.add(30, 'minutes');
|
||||
expect(formatTimestampToDuration(sixHours, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'6 hrs 30 mins'
|
||||
);
|
||||
|
||||
const sevenDays = getTestTime()
|
||||
.add(7, 'days')
|
||||
.add(6, 'hours')
|
||||
.add(18, 'minutes');
|
||||
expect(formatTimestampToDuration(sevenDays, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'7 days 6 hrs 18 mins'
|
||||
);
|
||||
|
||||
const eightWeeks = getTestTime()
|
||||
.add(8, 'weeks')
|
||||
.add(7, 'days')
|
||||
.add(6, 'hours')
|
||||
.add(18, 'minutes');
|
||||
expect(formatTimestampToDuration(eightWeeks, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'2 months 2 days'
|
||||
);
|
||||
|
||||
const oneHour = getTestTime().add(1, 'hour'); // should trim 0 min
|
||||
expect(formatTimestampToDuration(oneHour, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'1 hr'
|
||||
);
|
||||
|
||||
const oneDay = getTestTime().add(1, 'day'); // should trim 0 hrs
|
||||
expect(formatTimestampToDuration(oneDay, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'1 day'
|
||||
);
|
||||
|
||||
const twoMonths = getTestTime().add(2, 'month'); // should trim 0 days
|
||||
expect(formatTimestampToDuration(twoMonths, CALCULATE_DURATION_UNTIL, getTestTime())).to.be(
|
||||
'2 months'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,70 +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.
|
||||
*/
|
||||
|
||||
export enum Status {
|
||||
Canceled,
|
||||
Failed,
|
||||
Resolved,
|
||||
Awaiting,
|
||||
Idle,
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple [PromiseWithCancel] factory
|
||||
*/
|
||||
export class PromiseWithCancel {
|
||||
private _promise: Promise<any>;
|
||||
private _status: Status = Status.Idle;
|
||||
|
||||
/**
|
||||
* @param {Promise} promise Promise you want to cancel / track
|
||||
*/
|
||||
constructor(promise: Promise<any>) {
|
||||
this._promise = promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the promise in any state
|
||||
*/
|
||||
public cancel = (): void => {
|
||||
this._status = Status.Canceled;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns status based on [Status]
|
||||
*/
|
||||
public status = (): Status => {
|
||||
return this._status;
|
||||
};
|
||||
|
||||
/**
|
||||
* @returns promise passed in [constructor]
|
||||
* This sets the state to Status.Awaiting
|
||||
*/
|
||||
public promise = (): Promise<any> => {
|
||||
if (this._status === Status.Canceled) {
|
||||
throw Error('Getting a canceled promise is not allowed');
|
||||
} else if (this._status !== Status.Idle) {
|
||||
return this._promise;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._status = Status.Awaiting;
|
||||
return this._promise
|
||||
.then(response => {
|
||||
if (this._status !== Status.Canceled) {
|
||||
this._status = Status.Resolved;
|
||||
return resolve(response);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
if (this._status !== Status.Canceled) {
|
||||
this._status = Status.Failed;
|
||||
return reject(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1,268 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Helper string to add as a tag in every logging call
|
||||
*/
|
||||
export const LOGGING_TAG = 'monitoring';
|
||||
/**
|
||||
* Helper string to add as a tag in every logging call related to Kibana monitoring
|
||||
*/
|
||||
export const KIBANA_MONITORING_LOGGING_TAG = 'kibana-monitoring';
|
||||
|
||||
/**
|
||||
* The Monitoring API version is the expected API format that we export and expect to import.
|
||||
* @type {string}
|
||||
*/
|
||||
export const MONITORING_SYSTEM_API_VERSION = '7';
|
||||
/**
|
||||
* The type name used within the Monitoring index to publish Kibana ops stats.
|
||||
* @type {string}
|
||||
*/
|
||||
export const KIBANA_STATS_TYPE_MONITORING = 'kibana_stats'; // similar to KIBANA_STATS_TYPE but rolled up into 10s stats from 5s intervals through ops_buffer
|
||||
/**
|
||||
* The type name used within the Monitoring index to publish Kibana stats.
|
||||
* @type {string}
|
||||
*/
|
||||
export const KIBANA_SETTINGS_TYPE = 'kibana_settings';
|
||||
/**
|
||||
* The type name used within the Monitoring index to publish Kibana usage stats.
|
||||
* NOTE: this string shows as-is in the stats API as a field name for the kibana usage stats
|
||||
* @type {string}
|
||||
*/
|
||||
export const KIBANA_USAGE_TYPE = 'kibana';
|
||||
|
||||
/*
|
||||
* Key for the localStorage service
|
||||
*/
|
||||
export const STORAGE_KEY = 'xpack.monitoring.data';
|
||||
|
||||
/**
|
||||
* Units for derivative metric values
|
||||
*/
|
||||
export const NORMALIZED_DERIVATIVE_UNIT = '1s';
|
||||
|
||||
/*
|
||||
* Values for column sorting in table options
|
||||
* @type {number} 1 or -1
|
||||
*/
|
||||
export const EUI_SORT_ASCENDING = 'asc';
|
||||
export const EUI_SORT_DESCENDING = 'desc';
|
||||
export const SORT_ASCENDING = 1;
|
||||
export const SORT_DESCENDING = -1;
|
||||
|
||||
/*
|
||||
* Chart colors
|
||||
* @type {string}
|
||||
*/
|
||||
export const CHART_LINE_COLOR = '#d2d2d2';
|
||||
export const CHART_TEXT_COLOR = '#9c9c9c';
|
||||
|
||||
/*
|
||||
* Number of cluster alerts to show on overview page
|
||||
* @type {number}
|
||||
*/
|
||||
export const CLUSTER_ALERTS_SEARCH_SIZE = 3;
|
||||
|
||||
/*
|
||||
* Format for moment-duration-format timestamp-to-duration template if the time diffs are gte 1 month
|
||||
* @type {string}
|
||||
*/
|
||||
export const FORMAT_DURATION_TEMPLATE_LONG = 'M [months] d [days]';
|
||||
|
||||
/*
|
||||
* Format for moment-duration-format timestamp-to-duration template if the time diffs are lt 1 month but gt 1 minute
|
||||
* @type {string}
|
||||
*/
|
||||
export const FORMAT_DURATION_TEMPLATE_SHORT = ' d [days] h [hrs] m [min]';
|
||||
|
||||
/*
|
||||
* Format for moment-duration-format timestamp-to-duration template if the time diffs are lt 1 minute
|
||||
* @type {string}
|
||||
*/
|
||||
export const FORMAT_DURATION_TEMPLATE_TINY = ' s [seconds]';
|
||||
|
||||
/*
|
||||
* Simple unique values for Timestamp to duration flags. These are used for
|
||||
* determining if calculation should be formatted as "time until" (now to
|
||||
* timestamp) or "time since" (timestamp to now)
|
||||
*/
|
||||
export const CALCULATE_DURATION_SINCE = 'since';
|
||||
export const CALCULATE_DURATION_UNTIL = 'until';
|
||||
|
||||
/**
|
||||
* In order to show ML Jobs tab in the Elasticsearch section / tab navigation, license must be supported
|
||||
*/
|
||||
export const ML_SUPPORTED_LICENSES = ['trial', 'platinum', 'enterprise'];
|
||||
|
||||
/**
|
||||
* Metadata service URLs for the different cloud services that have constant URLs (e.g., unlike GCP, which is a constant prefix).
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
export const CLOUD_METADATA_SERVICES = {
|
||||
// We explicitly call out the version, 2016-09-02, rather than 'latest' to avoid unexpected changes
|
||||
AWS_URL: 'http://169.254.169.254/2016-09-02/dynamic/instance-identity/document',
|
||||
|
||||
// 2017-04-02 is the first GA release of this API
|
||||
AZURE_URL: 'http://169.254.169.254/metadata/instance?api-version=2017-04-02',
|
||||
|
||||
// GCP documentation shows both 'metadata.google.internal' (mostly) and '169.254.169.254' (sometimes)
|
||||
// To bypass potential DNS changes, the IP was used because it's shared with other cloud services
|
||||
GCP_URL_PREFIX: 'http://169.254.169.254/computeMetadata/v1/instance',
|
||||
};
|
||||
|
||||
/**
|
||||
* Constants used by Logstash monitoring code
|
||||
*/
|
||||
export const LOGSTASH = {
|
||||
MAJOR_VER_REQD_FOR_PIPELINES: 6,
|
||||
|
||||
/*
|
||||
* Names ES keys on for different Logstash pipeline queues.
|
||||
* @type {string}
|
||||
*/
|
||||
QUEUE_TYPES: {
|
||||
MEMORY: 'memory',
|
||||
PERSISTED: 'persisted',
|
||||
},
|
||||
};
|
||||
|
||||
export const DEBOUNCE_SLOW_MS = 17; // roughly how long it takes to render a frame at 60fps
|
||||
export const DEBOUNCE_FAST_MS = 10; // roughly how long it takes to render a frame at 100fps
|
||||
|
||||
/**
|
||||
* Configuration key for setting the email address used for cluster alert notifications.
|
||||
*/
|
||||
export const CLUSTER_ALERTS_ADDRESS_CONFIG_KEY = 'cluster_alerts.email_notifications.email_address';
|
||||
|
||||
export const STANDALONE_CLUSTER_CLUSTER_UUID = '__standalone_cluster__';
|
||||
|
||||
export const INDEX_PATTERN = '.monitoring-*-6-*,.monitoring-*-7-*';
|
||||
export const INDEX_PATTERN_KIBANA = '.monitoring-kibana-6-*,.monitoring-kibana-7-*';
|
||||
export const INDEX_PATTERN_LOGSTASH = '.monitoring-logstash-6-*,.monitoring-logstash-7-*';
|
||||
export const INDEX_PATTERN_BEATS = '.monitoring-beats-6-*,.monitoring-beats-7-*';
|
||||
export const INDEX_ALERTS = '.monitoring-alerts-6,.monitoring-alerts-7';
|
||||
export const INDEX_PATTERN_ELASTICSEARCH = '.monitoring-es-6-*,.monitoring-es-7-*';
|
||||
|
||||
// This is the unique token that exists in monitoring indices collected by metricbeat
|
||||
export const METRICBEAT_INDEX_NAME_UNIQUE_TOKEN = '-mb-';
|
||||
|
||||
// We use this for metricbeat migration to identify specific products that we do not have constants for
|
||||
export const ELASTICSEARCH_SYSTEM_ID = 'elasticsearch';
|
||||
|
||||
/**
|
||||
* The id of the infra source owned by the monitoring plugin.
|
||||
*/
|
||||
export const INFRA_SOURCE_ID = 'internal-stack-monitoring';
|
||||
|
||||
/*
|
||||
* These constants represent code paths within `getClustersFromRequest`
|
||||
* that an api call wants to invoke. This is meant as an optimization to
|
||||
* avoid unnecessary ES queries (looking at you logstash) when the data
|
||||
* is not used. In the long term, it'd be nice to have separate api calls
|
||||
* instead of this path logic.
|
||||
*/
|
||||
export const CODE_PATH_ALL = 'all';
|
||||
export const CODE_PATH_ALERTS = 'alerts';
|
||||
export const CODE_PATH_KIBANA = 'kibana';
|
||||
export const CODE_PATH_ELASTICSEARCH = 'elasticsearch';
|
||||
export const CODE_PATH_ML = 'ml';
|
||||
export const CODE_PATH_BEATS = 'beats';
|
||||
export const CODE_PATH_LOGSTASH = 'logstash';
|
||||
export const CODE_PATH_APM = 'apm';
|
||||
export const CODE_PATH_LICENSE = 'license';
|
||||
export const CODE_PATH_LOGS = 'logs';
|
||||
|
||||
/**
|
||||
* The header sent by telemetry service when hitting Elasticsearch to identify query source
|
||||
* @type {string}
|
||||
*/
|
||||
export const TELEMETRY_QUERY_SOURCE = 'TELEMETRY';
|
||||
|
||||
/**
|
||||
* The name of the Kibana System ID used to publish and look up Kibana stats through the Monitoring system.
|
||||
* @type {string}
|
||||
*/
|
||||
export const KIBANA_SYSTEM_ID = 'kibana';
|
||||
|
||||
/**
|
||||
* The name of the Beats System ID used to publish and look up Beats stats through the Monitoring system.
|
||||
* @type {string}
|
||||
*/
|
||||
export const BEATS_SYSTEM_ID = 'beats';
|
||||
|
||||
/**
|
||||
* The name of the Apm System ID used to publish and look up Apm stats through the Monitoring system.
|
||||
* @type {string}
|
||||
*/
|
||||
export const APM_SYSTEM_ID = 'apm';
|
||||
|
||||
/**
|
||||
* The name of the Kibana System ID used to look up Logstash stats through the Monitoring system.
|
||||
* @type {string}
|
||||
*/
|
||||
export const LOGSTASH_SYSTEM_ID = 'logstash';
|
||||
|
||||
/**
|
||||
* The name of the Kibana System ID used to look up Reporting stats through the Monitoring system.
|
||||
* @type {string}
|
||||
*/
|
||||
export const REPORTING_SYSTEM_ID = 'reporting';
|
||||
|
||||
/**
|
||||
* The amount of time, in milliseconds, to wait between collecting kibana stats from es.
|
||||
*
|
||||
* Currently 24 hours kept in sync with reporting interval.
|
||||
* @type {Number}
|
||||
*/
|
||||
export const TELEMETRY_COLLECTION_INTERVAL = 86400000;
|
||||
|
||||
/**
|
||||
* We want to slowly rollout the migration from watcher-based cluster alerts to
|
||||
* kibana alerts and we only want to enable the kibana alerts once all
|
||||
* watcher-based cluster alerts have been migrated so this flag will serve
|
||||
* as the only way to see the new UI and actually run Kibana alerts. It will
|
||||
* be false until all alerts have been migrated, then it will be removed
|
||||
*/
|
||||
export const KIBANA_ALERTING_ENABLED = false;
|
||||
|
||||
/**
|
||||
* The prefix for all alert types used by monitoring
|
||||
*/
|
||||
export const ALERT_TYPE_PREFIX = 'monitoring_';
|
||||
|
||||
/**
|
||||
* This is the alert type id for the license expiration alert
|
||||
*/
|
||||
export const ALERT_TYPE_LICENSE_EXPIRATION = `${ALERT_TYPE_PREFIX}alert_type_license_expiration`;
|
||||
/**
|
||||
* This is the alert type id for the cluster state alert
|
||||
*/
|
||||
export const ALERT_TYPE_CLUSTER_STATE = `${ALERT_TYPE_PREFIX}alert_type_cluster_state`;
|
||||
|
||||
/**
|
||||
* A listing of all alert types
|
||||
*/
|
||||
export const ALERT_TYPES = [ALERT_TYPE_LICENSE_EXPIRATION, ALERT_TYPE_CLUSTER_STATE];
|
||||
|
||||
/**
|
||||
* Matches the id for the built-in in email action type
|
||||
* See x-pack/plugins/actions/server/builtin_action_types/email.ts
|
||||
*/
|
||||
export const ALERT_ACTION_TYPE_EMAIL = '.email';
|
||||
|
||||
/**
|
||||
* The number of alerts that have been migrated
|
||||
*/
|
||||
export const NUMBER_OF_MIGRATED_ALERTS = 2;
|
||||
|
||||
/**
|
||||
* The advanced settings config name for the email address
|
||||
*/
|
||||
export const MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS = 'monitoring:alertingEmailAddress';
|
||||
|
||||
export const ALERT_EMAIL_SERVICES = ['gmail', 'hotmail', 'icloud', 'outlook365', 'ses', 'yahoo'];
|
|
@ -1,54 +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 moment from 'moment';
|
||||
import 'moment-duration-format';
|
||||
import {
|
||||
FORMAT_DURATION_TEMPLATE_TINY,
|
||||
FORMAT_DURATION_TEMPLATE_SHORT,
|
||||
FORMAT_DURATION_TEMPLATE_LONG,
|
||||
CALCULATE_DURATION_SINCE,
|
||||
CALCULATE_DURATION_UNTIL,
|
||||
} from './constants';
|
||||
|
||||
/*
|
||||
* Formats a timestamp string
|
||||
* @param timestamp: ISO time string
|
||||
* @param calculationFlag: control "since" or "until" logic
|
||||
* @param initialTime {Object} moment object (not required)
|
||||
* @return string
|
||||
*/
|
||||
export function formatTimestampToDuration(timestamp, calculationFlag, initialTime) {
|
||||
initialTime = initialTime || moment();
|
||||
let timeDuration;
|
||||
if (calculationFlag === CALCULATE_DURATION_SINCE) {
|
||||
timeDuration = moment.duration(initialTime - moment(timestamp)); // since: now - timestamp
|
||||
} else if (calculationFlag === CALCULATE_DURATION_UNTIL) {
|
||||
timeDuration = moment.duration(moment(timestamp) - initialTime); // until: timestamp - now
|
||||
} else {
|
||||
throw new Error(
|
||||
'[formatTimestampToDuration] requires a [calculationFlag] parameter to specify format as "since" or "until" the given time.'
|
||||
);
|
||||
}
|
||||
|
||||
// See https://github.com/elastic/x-pack-kibana/issues/3554
|
||||
let duration;
|
||||
if (Math.abs(initialTime.diff(timestamp, 'months')) >= 1) {
|
||||
// time diff is greater than 1 month, show months / days
|
||||
duration = moment.duration(timeDuration).format(FORMAT_DURATION_TEMPLATE_LONG);
|
||||
} else if (Math.abs(initialTime.diff(timestamp, 'minutes')) >= 1) {
|
||||
// time diff is less than 1 month but greater than a minute, show days / hours / minutes
|
||||
duration = moment.duration(timeDuration).format(FORMAT_DURATION_TEMPLATE_SHORT);
|
||||
} else {
|
||||
// time diff is less than a minute, show seconds
|
||||
duration = moment.duration(timeDuration).format(FORMAT_DURATION_TEMPLATE_TINY);
|
||||
}
|
||||
|
||||
return duration
|
||||
.replace(/ 0 mins$/, '')
|
||||
.replace(/ 0 hrs$/, '')
|
||||
.replace(/ 0 days$/, ''); // See https://github.com/jsmreese/moment-duration-format/issues/64
|
||||
}
|
|
@ -1,35 +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 moment from 'moment-timezone';
|
||||
|
||||
export const LARGE_FLOAT = '0,0.[00]';
|
||||
export const SMALL_FLOAT = '0.[00]';
|
||||
export const LARGE_BYTES = '0,0.0 b';
|
||||
export const SMALL_BYTES = '0.0 b';
|
||||
export const LARGE_ABBREVIATED = '0,0.[0]a';
|
||||
|
||||
/**
|
||||
* Format the {@code date} in the user's expected date/time format using their <em>dateFormat:tz</em> defined time zone.
|
||||
* @param date Either a numeric Unix timestamp or a {@code Date} object
|
||||
* @returns The date formatted using 'LL LTS'
|
||||
*/
|
||||
export function formatDateTimeLocal(date, timezone) {
|
||||
if (timezone === 'Browser') {
|
||||
timezone = moment.tz.guess() || 'utc';
|
||||
}
|
||||
|
||||
return moment.tz(date, timezone).format('LL LTS');
|
||||
}
|
||||
|
||||
/**
|
||||
* Shorten a Logstash Pipeline's hash for display purposes
|
||||
* @param {string} hash The complete hash
|
||||
* @return {string} The shortened hash
|
||||
*/
|
||||
export function shortenPipelineHash(hash) {
|
||||
return hash.substr(0, 6);
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
* @param {Object} Joi - HapiJS Joi module that allows for schema validation
|
||||
* @return {Object} config schema
|
||||
*/
|
||||
export const config = Joi => {
|
||||
export const config = (Joi: any) => {
|
||||
const DEFAULT_REQUEST_HEADERS = ['authorization'];
|
||||
|
||||
return Joi.object({
|
|
@ -1,57 +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 { get } from 'lodash';
|
||||
import { resolve } from 'path';
|
||||
import { config } from './config';
|
||||
import { getUiExports } from './ui_exports';
|
||||
import { KIBANA_ALERTING_ENABLED } from './common/constants';
|
||||
|
||||
/**
|
||||
* Invokes plugin modules to instantiate the Monitoring plugin for Kibana
|
||||
* @param kibana {Object} Kibana plugin instance
|
||||
* @return {Object} Monitoring UI Kibana plugin object
|
||||
*/
|
||||
const deps = ['kibana', 'elasticsearch', 'xpack_main'];
|
||||
if (KIBANA_ALERTING_ENABLED) {
|
||||
deps.push(...['alerting', 'actions']);
|
||||
}
|
||||
export const monitoring = kibana => {
|
||||
return new kibana.Plugin({
|
||||
require: deps,
|
||||
id: 'monitoring',
|
||||
configPrefix: 'monitoring',
|
||||
publicDir: resolve(__dirname, 'public'),
|
||||
init(server) {
|
||||
const serverConfig = server.config();
|
||||
const npMonitoring = server.newPlatform.setup.plugins.monitoring;
|
||||
if (npMonitoring) {
|
||||
const kbnServerStatus = this.kbnServer.status;
|
||||
npMonitoring.registerLegacyAPI({
|
||||
getServerStatus: () => {
|
||||
const status = kbnServerStatus.toJSON();
|
||||
return get(status, 'overall.state');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
server.injectUiAppVars('monitoring', () => {
|
||||
return {
|
||||
maxBucketSize: serverConfig.get('monitoring.ui.max_bucket_size'),
|
||||
minIntervalSeconds: serverConfig.get('monitoring.ui.min_interval_seconds'),
|
||||
kbnIndex: serverConfig.get('kibana.index'),
|
||||
showLicenseExpiration: serverConfig.get('monitoring.ui.show_license_expiration'),
|
||||
showCgroupMetricsElasticsearch: serverConfig.get(
|
||||
'monitoring.ui.container.elasticsearch.enabled'
|
||||
),
|
||||
showCgroupMetricsLogstash: serverConfig.get('monitoring.ui.container.logstash.enabled'), // Note, not currently used, but see https://github.com/elastic/x-pack-kibana/issues/1559 part 2
|
||||
};
|
||||
});
|
||||
},
|
||||
config,
|
||||
uiExports: getUiExports(),
|
||||
});
|
||||
};
|
41
x-pack/legacy/plugins/monitoring/index.ts
Normal file
41
x-pack/legacy/plugins/monitoring/index.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 Hapi from 'hapi';
|
||||
import { config } from './config';
|
||||
import { KIBANA_ALERTING_ENABLED } from '../../../plugins/monitoring/common/constants';
|
||||
|
||||
/**
|
||||
* Invokes plugin modules to instantiate the Monitoring plugin for Kibana
|
||||
* @param kibana {Object} Kibana plugin instance
|
||||
* @return {Object} Monitoring UI Kibana plugin object
|
||||
*/
|
||||
const deps = ['kibana', 'elasticsearch', 'xpack_main'];
|
||||
if (KIBANA_ALERTING_ENABLED) {
|
||||
deps.push(...['alerting', 'actions']);
|
||||
}
|
||||
export const monitoring = (kibana: any) => {
|
||||
return new kibana.Plugin({
|
||||
require: deps,
|
||||
id: 'monitoring',
|
||||
configPrefix: 'monitoring',
|
||||
init(server: Hapi.Server) {
|
||||
const npMonitoring = server.newPlatform.setup.plugins.monitoring as object & {
|
||||
registerLegacyAPI: (api: unknown) => void;
|
||||
};
|
||||
if (npMonitoring) {
|
||||
const kbnServerStatus = this.kbnServer.status;
|
||||
npMonitoring.registerLegacyAPI({
|
||||
getServerStatus: () => {
|
||||
const status = kbnServerStatus.toJSON();
|
||||
return status?.overall?.state;
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
config,
|
||||
});
|
||||
};
|
|
@ -1,76 +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 React from 'react';
|
||||
import expect from '@kbn/expect';
|
||||
import { shallow } from 'enzyme';
|
||||
import { ChartTarget } from './chart_target';
|
||||
|
||||
const props = {
|
||||
seriesToShow: ['Max Heap', 'Max Heap Used'],
|
||||
series: [
|
||||
{
|
||||
color: '#3ebeb0',
|
||||
label: 'Max Heap',
|
||||
id: 'Max Heap',
|
||||
data: [
|
||||
[1562958960000, 1037959168],
|
||||
[1562958990000, 1037959168],
|
||||
[1562959020000, 1037959168],
|
||||
],
|
||||
},
|
||||
{
|
||||
color: '#3b73ac',
|
||||
label: 'Max Heap Used',
|
||||
id: 'Max Heap Used',
|
||||
data: [
|
||||
[1562958960000, 639905768],
|
||||
[1562958990000, 622312416],
|
||||
[1562959020000, 555967504],
|
||||
],
|
||||
},
|
||||
],
|
||||
timeRange: {
|
||||
min: 1562958939851,
|
||||
max: 1562962539851,
|
||||
},
|
||||
hasLegend: true,
|
||||
onBrush: () => void 0,
|
||||
tickFormatter: () => void 0,
|
||||
updateLegend: () => void 0,
|
||||
};
|
||||
|
||||
jest.mock('../../np_imports/ui/chrome', () => {
|
||||
return {
|
||||
getBasePath: () => '',
|
||||
};
|
||||
});
|
||||
|
||||
// TODO: Skipping for now, seems flaky in New Platform (needs more investigation)
|
||||
describe.skip('Test legends to toggle series: ', () => {
|
||||
const ids = props.series.map(item => item.id);
|
||||
|
||||
describe('props.series: ', () => {
|
||||
it('should toggle based on seriesToShow array', () => {
|
||||
const component = shallow(<ChartTarget {...props} />);
|
||||
|
||||
const componentClass = component.instance();
|
||||
|
||||
const seriesA = componentClass.filterData(props.series, [ids[0]]);
|
||||
expect(seriesA.length).to.be(1);
|
||||
expect(seriesA[0].id).to.be(ids[0]);
|
||||
|
||||
const seriesB = componentClass.filterData(props.series, [ids[1]]);
|
||||
expect(seriesB.length).to.be(1);
|
||||
expect(seriesB[0].id).to.be(ids[1]);
|
||||
|
||||
const seriesAB = componentClass.filterData(props.series, ids);
|
||||
expect(seriesAB.length).to.be(2);
|
||||
expect(seriesAB[0].id).to.be(ids[0]);
|
||||
expect(seriesAB[1].id).to.be(ids[1]);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,91 +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 React from 'react';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiSpacer,
|
||||
EuiCodeBlock,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
EuiLink,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiScreenReaderOnly,
|
||||
} from '@elastic/eui';
|
||||
import { LicenseStatus, AddLicense } from 'plugins/xpack_main/components';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import chrome from 'plugins/monitoring/np_imports/ui/chrome';
|
||||
|
||||
const licenseManagement = `${chrome.getBasePath()}/app/kibana#/management/elasticsearch/license_management`;
|
||||
|
||||
const LicenseUpdateInfoForPrimary = ({ isPrimaryCluster, uploadLicensePath }) => {
|
||||
if (!isPrimaryCluster) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// viewed license is for the cluster directly connected to Kibana
|
||||
return <AddLicense uploadPath={uploadLicensePath} />;
|
||||
};
|
||||
|
||||
const LicenseUpdateInfoForRemote = ({ isPrimaryCluster }) => {
|
||||
if (isPrimaryCluster) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// viewed license is for a remote monitored cluster not directly connected to Kibana
|
||||
return (
|
||||
<EuiPanel>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.monitoring.license.howToUpdateLicenseDescription"
|
||||
defaultMessage="To update the license for this cluster, provide the license file through
|
||||
the Elasticsearch {apiText}:"
|
||||
values={{
|
||||
apiText: 'API',
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<EuiSpacer />
|
||||
<EuiCodeBlock>
|
||||
{`curl -XPUT -u <user> 'https://<host>:<port>/_license' -H 'Content-Type: application/json' -d @license.json`}
|
||||
</EuiCodeBlock>
|
||||
</EuiPanel>
|
||||
);
|
||||
};
|
||||
|
||||
export function License(props) {
|
||||
const { status, type, isExpired, expiryDate } = props;
|
||||
return (
|
||||
<EuiPage>
|
||||
<EuiScreenReaderOnly>
|
||||
<h1>
|
||||
<FormattedMessage id="xpack.monitoring.license.heading" defaultMessage="License" />
|
||||
</h1>
|
||||
</EuiScreenReaderOnly>
|
||||
<EuiPageBody>
|
||||
<LicenseStatus isExpired={isExpired} status={status} type={type} expiryDate={expiryDate} />
|
||||
<EuiSpacer />
|
||||
|
||||
<EuiFlexGroup justifyContent="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<LicenseUpdateInfoForPrimary {...props} />
|
||||
<LicenseUpdateInfoForRemote {...props} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
||||
<EuiSpacer />
|
||||
<EuiText size="s" textAlign="center">
|
||||
<p>
|
||||
For more license options please visit
|
||||
<EuiLink href={licenseManagement}>License Management</EuiLink>.
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
);
|
||||
}
|
|
@ -1,10 +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 './main';
|
||||
import './elasticsearch/ml_job_listing';
|
||||
import './beats/overview';
|
||||
import './beats/beat';
|
|
@ -1,30 +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 { capitalize } from 'lodash';
|
||||
import { uiModules } from 'plugins/monitoring/np_imports/ui/modules';
|
||||
import { formatNumber, formatMetric } from 'plugins/monitoring/lib/format_number';
|
||||
import { extractIp } from 'plugins/monitoring/lib/extract_ip';
|
||||
|
||||
const uiModule = uiModules.get('monitoring/filters', []);
|
||||
|
||||
uiModule.filter('capitalize', function() {
|
||||
return function(input) {
|
||||
return capitalize(input.toLowerCase());
|
||||
};
|
||||
});
|
||||
|
||||
uiModule.filter('formatNumber', function() {
|
||||
return formatNumber;
|
||||
});
|
||||
|
||||
uiModule.filter('formatMetric', function() {
|
||||
return formatMetric;
|
||||
});
|
||||
|
||||
uiModule.filter('extractIp', function() {
|
||||
return extractIp;
|
||||
});
|
|
@ -1,9 +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 { uiModules } from 'ui/modules';
|
||||
|
||||
uiModules.get('kibana').constant('monitoringUiEnabled', true);
|
|
@ -1,16 +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 { uiModules } from 'ui/modules';
|
||||
import { npStart } from 'ui/new_platform';
|
||||
|
||||
uiModules.get('monitoring/hacks').run(monitoringUiEnabled => {
|
||||
if (monitoringUiEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
npStart.core.chrome.navLinks.update('monitoring', { hidden: true });
|
||||
});
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 21.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="20px" height="20px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M1.9,12.4h2.8l1.6-3c0.1-0.2,0.3-0.3,0.5-0.3c0.2,0,0.4,0.1,0.4,0.3l1.5,4.2l2.5-7.8c0.1-0.2,0.2-0.3,0.5-0.3
|
||||
c0.3,0,0.4,0.1,0.5,0.3l2.6,6.5h3.2l0.2-0.2c2.3-2.3,2.2-6-0.1-8.3C16,1.6,12.3,1.5,10,3.7c-2.3-2.3-6-2.3-8.3,0
|
||||
c-2.3,2.3-2.3,6.1,0,8.5L1.9,12.4z"/>
|
||||
<path class="st0" d="M14.5,13.4c-0.2,0-0.4-0.1-0.5-0.3l-2.2-5.6l-2.6,7.9c-0.1,0.2-0.3,0.3-0.5,0.3c0,0,0,0,0,0
|
||||
c-0.2,0-0.4-0.1-0.5-0.3l-1.6-4.5l-1.2,2.3c-0.1,0.2-0.3,0.3-0.4,0.3H2.9l5.8,5.9c0,0,0,0,0,0C9,19.7,9.4,19.9,9.7,20c0,0,0,0,0,0
|
||||
c0.1,0,0.1,0,0.2,0c0.1,0,0.1,0,0.2,0c0,0,0,0,0,0c0.3,0,0.6-0.2,0.9-0.4l6-6.1H14.5z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 1 KiB |
|
@ -1,27 +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 'plugins/monitoring/filters';
|
||||
import 'plugins/monitoring/services/clusters';
|
||||
import 'plugins/monitoring/services/features';
|
||||
import 'plugins/monitoring/services/executor';
|
||||
import 'plugins/monitoring/services/license';
|
||||
import 'plugins/monitoring/services/title';
|
||||
import 'plugins/monitoring/services/breadcrumbs';
|
||||
import 'plugins/monitoring/directives/all';
|
||||
import 'plugins/monitoring/views/all';
|
||||
import { npSetup, npStart } from '../public/np_imports/legacy_imports';
|
||||
import { plugin } from './np_ready';
|
||||
import { localApplicationService } from '../../../../../src/legacy/core_plugins/kibana/public/local_application_service';
|
||||
|
||||
const pluginInstance = plugin({} as any);
|
||||
pluginInstance.setup(npSetup.core, npSetup.plugins);
|
||||
pluginInstance.start(npStart.core, {
|
||||
...npStart.plugins,
|
||||
__LEGACY: {
|
||||
localApplicationService,
|
||||
},
|
||||
});
|
|
@ -1,157 +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 {
|
||||
ICompileProvider,
|
||||
IHttpProvider,
|
||||
IHttpService,
|
||||
ILocationProvider,
|
||||
IModule,
|
||||
IRootScopeService,
|
||||
} from 'angular';
|
||||
import $ from 'jquery';
|
||||
import _, { cloneDeep, forOwn, get, set } from 'lodash';
|
||||
import * as Rx from 'rxjs';
|
||||
import { CoreStart, LegacyCoreStart } from 'kibana/public';
|
||||
|
||||
const isSystemApiRequest = (request: any) =>
|
||||
Boolean(request && request.headers && !!request.headers['kbn-system-api']);
|
||||
|
||||
export const configureAppAngularModule = (angularModule: IModule, newPlatform: LegacyCoreStart) => {
|
||||
const legacyMetadata = newPlatform.injectedMetadata.getLegacyMetadata();
|
||||
|
||||
forOwn(newPlatform.injectedMetadata.getInjectedVars(), (val, name) => {
|
||||
if (name !== undefined) {
|
||||
// The legacy platform modifies some of these values, clone to an unfrozen object.
|
||||
angularModule.value(name, cloneDeep(val));
|
||||
}
|
||||
});
|
||||
|
||||
angularModule
|
||||
.value('kbnVersion', newPlatform.injectedMetadata.getKibanaVersion())
|
||||
.value('buildNum', legacyMetadata.buildNum)
|
||||
.value('buildSha', legacyMetadata.buildSha)
|
||||
.value('serverName', legacyMetadata.serverName)
|
||||
.value('esUrl', getEsUrl(newPlatform))
|
||||
.value('uiCapabilities', newPlatform.application.capabilities)
|
||||
.config(setupCompileProvider(newPlatform))
|
||||
.config(setupLocationProvider())
|
||||
.config($setupXsrfRequestInterceptor(newPlatform))
|
||||
.run(capture$httpLoadingCount(newPlatform))
|
||||
.run($setupUICapabilityRedirect(newPlatform));
|
||||
};
|
||||
|
||||
const getEsUrl = (newPlatform: CoreStart) => {
|
||||
const a = document.createElement('a');
|
||||
a.href = newPlatform.http.basePath.prepend('/elasticsearch');
|
||||
const protocolPort = /https/.test(a.protocol) ? 443 : 80;
|
||||
const port = a.port || protocolPort;
|
||||
return {
|
||||
host: a.hostname,
|
||||
port,
|
||||
protocol: a.protocol,
|
||||
pathname: a.pathname,
|
||||
};
|
||||
};
|
||||
|
||||
const setupCompileProvider = (newPlatform: LegacyCoreStart) => (
|
||||
$compileProvider: ICompileProvider
|
||||
) => {
|
||||
if (!newPlatform.injectedMetadata.getLegacyMetadata().devMode) {
|
||||
$compileProvider.debugInfoEnabled(false);
|
||||
}
|
||||
};
|
||||
|
||||
const setupLocationProvider = () => ($locationProvider: ILocationProvider) => {
|
||||
$locationProvider.html5Mode({
|
||||
enabled: false,
|
||||
requireBase: false,
|
||||
rewriteLinks: false,
|
||||
});
|
||||
|
||||
$locationProvider.hashPrefix('');
|
||||
};
|
||||
|
||||
const $setupXsrfRequestInterceptor = (newPlatform: LegacyCoreStart) => {
|
||||
const version = newPlatform.injectedMetadata.getLegacyMetadata().version;
|
||||
|
||||
// Configure jQuery prefilter
|
||||
$.ajaxPrefilter(({ kbnXsrfToken = true }: any, originalOptions, jqXHR) => {
|
||||
if (kbnXsrfToken) {
|
||||
jqXHR.setRequestHeader('kbn-version', version);
|
||||
}
|
||||
});
|
||||
|
||||
return ($httpProvider: IHttpProvider) => {
|
||||
// Configure $httpProvider interceptor
|
||||
$httpProvider.interceptors.push(() => {
|
||||
return {
|
||||
request(opts) {
|
||||
const { kbnXsrfToken = true } = opts as any;
|
||||
if (kbnXsrfToken) {
|
||||
set(opts, ['headers', 'kbn-version'], version);
|
||||
}
|
||||
return opts;
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Injected into angular module by ui/chrome angular integration
|
||||
* and adds a root-level watcher that will capture the count of
|
||||
* active $http requests on each digest loop and expose the count to
|
||||
* the core.loadingCount api
|
||||
* @param {Angular.Scope} $rootScope
|
||||
* @param {HttpService} $http
|
||||
* @return {undefined}
|
||||
*/
|
||||
const capture$httpLoadingCount = (newPlatform: CoreStart) => (
|
||||
$rootScope: IRootScopeService,
|
||||
$http: IHttpService
|
||||
) => {
|
||||
newPlatform.http.addLoadingCountSource(
|
||||
new Rx.Observable(observer => {
|
||||
const unwatch = $rootScope.$watch(() => {
|
||||
const reqs = $http.pendingRequests || [];
|
||||
observer.next(reqs.filter(req => !isSystemApiRequest(req)).length);
|
||||
});
|
||||
|
||||
return unwatch;
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* integrates with angular to automatically redirect to home if required
|
||||
* capability is not met
|
||||
*/
|
||||
const $setupUICapabilityRedirect = (newPlatform: CoreStart) => (
|
||||
$rootScope: IRootScopeService,
|
||||
$injector: any
|
||||
) => {
|
||||
const isKibanaAppRoute = window.location.pathname.endsWith('/app/kibana');
|
||||
// this feature only works within kibana app for now after everything is
|
||||
// switched to the application service, this can be changed to handle all
|
||||
// apps.
|
||||
if (!isKibanaAppRoute) {
|
||||
return;
|
||||
}
|
||||
$rootScope.$on(
|
||||
'$routeChangeStart',
|
||||
(event, { $$route: route }: { $$route?: { requireUICapability: boolean } } = {}) => {
|
||||
if (!route || !route.requireUICapability) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!get(newPlatform.application.capabilities, route.requireUICapability)) {
|
||||
$injector.get('kbnUrl').change('/home');
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
|
@ -1,48 +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 angular, { IModule } from 'angular';
|
||||
|
||||
import { AppMountContext, LegacyCoreStart } from 'kibana/public';
|
||||
|
||||
// @ts-ignore TODO: change to absolute path
|
||||
import uiRoutes from 'plugins/monitoring/np_imports/ui/routes';
|
||||
// @ts-ignore TODO: change to absolute path
|
||||
import chrome from 'plugins/monitoring/np_imports/ui/chrome';
|
||||
// @ts-ignore TODO: change to absolute path
|
||||
import { uiModules } from 'plugins/monitoring/np_imports/ui/modules';
|
||||
// @ts-ignore TODO: change to absolute path
|
||||
import { registerTimefilterWithGlobalState } from 'plugins/monitoring/np_imports/ui/timefilter';
|
||||
import { configureAppAngularModule } from './angular_config';
|
||||
|
||||
import { localAppModule, appModuleName } from './modules';
|
||||
|
||||
export class AngularApp {
|
||||
private injector?: angular.auto.IInjectorService;
|
||||
|
||||
constructor({ core }: AppMountContext, { element }: { element: HTMLElement }) {
|
||||
uiModules.addToModule();
|
||||
const app: IModule = localAppModule(core);
|
||||
app.config(($routeProvider: any) => {
|
||||
$routeProvider.eagerInstantiationEnabled(false);
|
||||
uiRoutes.addToProvider($routeProvider);
|
||||
});
|
||||
configureAppAngularModule(app, core as LegacyCoreStart);
|
||||
registerTimefilterWithGlobalState(app);
|
||||
const appElement = document.createElement('div');
|
||||
appElement.setAttribute('style', 'height: 100%');
|
||||
appElement.innerHTML = '<div ng-view style="height: 100%" id="monitoring-angular-app"></div>';
|
||||
this.injector = angular.bootstrap(appElement, [appModuleName]);
|
||||
chrome.setInjector(this.injector);
|
||||
angular.element(element).append(appElement);
|
||||
}
|
||||
|
||||
public destroy = () => {
|
||||
if (this.injector) {
|
||||
this.injector.get('$rootScope').$destroy();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,148 +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 angular, { IWindowService } from 'angular';
|
||||
// required for `ngSanitize` angular module
|
||||
import 'angular-sanitize';
|
||||
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
|
||||
|
||||
import { AppMountContext } from 'kibana/public';
|
||||
import { Storage } from '../../../../../../../src/plugins/kibana_utils/public';
|
||||
import {
|
||||
createTopNavDirective,
|
||||
createTopNavHelper,
|
||||
} from '../../../../../../../src/plugins/kibana_legacy/public';
|
||||
|
||||
import {
|
||||
GlobalStateProvider,
|
||||
StateManagementConfigProvider,
|
||||
AppStateProvider,
|
||||
KbnUrlProvider,
|
||||
npStart,
|
||||
} from '../legacy_imports';
|
||||
|
||||
// @ts-ignore
|
||||
import { PromiseServiceCreator } from './providers/promises';
|
||||
// @ts-ignore
|
||||
import { PrivateProvider } from './providers/private';
|
||||
import { getSafeForExternalLink } from '../../lib/get_safe_for_external_link';
|
||||
|
||||
type IPrivate = <T>(provider: (...injectable: any[]) => T) => T;
|
||||
|
||||
export const appModuleName = 'monitoring';
|
||||
const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react'];
|
||||
|
||||
export const localAppModule = (core: AppMountContext['core']) => {
|
||||
createLocalI18nModule();
|
||||
createLocalPrivateModule();
|
||||
createLocalPromiseModule();
|
||||
createLocalStorage();
|
||||
createLocalConfigModule(core);
|
||||
createLocalKbnUrlModule();
|
||||
createLocalStateModule();
|
||||
createLocalTopNavModule(npStart.plugins.navigation);
|
||||
createHrefModule(core);
|
||||
|
||||
const appModule = angular.module(appModuleName, [
|
||||
...thirdPartyAngularDependencies,
|
||||
'monitoring/Config',
|
||||
'monitoring/I18n',
|
||||
'monitoring/Private',
|
||||
'monitoring/TopNav',
|
||||
'monitoring/State',
|
||||
'monitoring/Storage',
|
||||
'monitoring/href',
|
||||
'monitoring/services',
|
||||
'monitoring/filters',
|
||||
'monitoring/directives',
|
||||
]);
|
||||
return appModule;
|
||||
};
|
||||
|
||||
function createLocalStateModule() {
|
||||
angular
|
||||
.module('monitoring/State', [
|
||||
'monitoring/Private',
|
||||
'monitoring/Config',
|
||||
'monitoring/KbnUrl',
|
||||
'monitoring/Promise',
|
||||
])
|
||||
.factory('AppState', function(Private: IPrivate) {
|
||||
return Private(AppStateProvider);
|
||||
})
|
||||
.service('globalState', function(Private: IPrivate) {
|
||||
return Private(GlobalStateProvider);
|
||||
});
|
||||
}
|
||||
|
||||
function createLocalKbnUrlModule() {
|
||||
angular
|
||||
.module('monitoring/KbnUrl', ['monitoring/Private', 'ngRoute'])
|
||||
.service('kbnUrl', (Private: IPrivate) => Private(KbnUrlProvider));
|
||||
}
|
||||
|
||||
function createLocalConfigModule(core: AppMountContext['core']) {
|
||||
angular
|
||||
.module('monitoring/Config', ['monitoring/Private'])
|
||||
.provider('stateManagementConfig', StateManagementConfigProvider)
|
||||
.provider('config', () => {
|
||||
return {
|
||||
$get: () => ({
|
||||
get: core.uiSettings.get.bind(core.uiSettings),
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createLocalPromiseModule() {
|
||||
angular.module('monitoring/Promise', []).service('Promise', PromiseServiceCreator);
|
||||
}
|
||||
|
||||
function createLocalStorage() {
|
||||
angular
|
||||
.module('monitoring/Storage', [])
|
||||
.service('localStorage', ($window: IWindowService) => new Storage($window.localStorage))
|
||||
.service('sessionStorage', ($window: IWindowService) => new Storage($window.sessionStorage))
|
||||
.service('sessionTimeout', () => {});
|
||||
}
|
||||
|
||||
function createLocalPrivateModule() {
|
||||
angular.module('monitoring/Private', []).provider('Private', PrivateProvider);
|
||||
}
|
||||
|
||||
function createLocalTopNavModule({ ui }: any) {
|
||||
angular
|
||||
.module('monitoring/TopNav', ['react'])
|
||||
.directive('kbnTopNav', createTopNavDirective)
|
||||
.directive('kbnTopNavHelper', createTopNavHelper(ui));
|
||||
}
|
||||
|
||||
function createLocalI18nModule() {
|
||||
angular
|
||||
.module('monitoring/I18n', [])
|
||||
.provider('i18n', I18nProvider)
|
||||
.filter('i18n', i18nFilter)
|
||||
.directive('i18nId', i18nDirective);
|
||||
}
|
||||
|
||||
function createHrefModule(core: AppMountContext['core']) {
|
||||
const name: string = 'kbnHref';
|
||||
angular.module('monitoring/href', []).directive(name, () => {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: {
|
||||
pre: (_$scope, _$el, $attr) => {
|
||||
$attr.$observe(name, val => {
|
||||
if (val) {
|
||||
const url = getSafeForExternalLink(val as string);
|
||||
$attr.$set('href', core.http.basePath.prepend(url));
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
|
@ -1,116 +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 _ from 'lodash';
|
||||
|
||||
export function PromiseServiceCreator($q, $timeout) {
|
||||
function Promise(fn) {
|
||||
if (typeof this === 'undefined')
|
||||
throw new Error('Promise constructor must be called with "new"');
|
||||
|
||||
const defer = $q.defer();
|
||||
try {
|
||||
fn(defer.resolve, defer.reject);
|
||||
} catch (e) {
|
||||
defer.reject(e);
|
||||
}
|
||||
return defer.promise;
|
||||
}
|
||||
|
||||
Promise.all = Promise.props = $q.all;
|
||||
Promise.resolve = function(val) {
|
||||
const defer = $q.defer();
|
||||
defer.resolve(val);
|
||||
return defer.promise;
|
||||
};
|
||||
Promise.reject = function(reason) {
|
||||
const defer = $q.defer();
|
||||
defer.reject(reason);
|
||||
return defer.promise;
|
||||
};
|
||||
Promise.cast = $q.when;
|
||||
Promise.delay = function(ms) {
|
||||
return $timeout(_.noop, ms);
|
||||
};
|
||||
Promise.method = function(fn) {
|
||||
return function() {
|
||||
const args = Array.prototype.slice.call(arguments);
|
||||
return Promise.try(fn, args, this);
|
||||
};
|
||||
};
|
||||
Promise.nodeify = function(promise, cb) {
|
||||
promise.then(function(val) {
|
||||
cb(void 0, val);
|
||||
}, cb);
|
||||
};
|
||||
Promise.map = function(arr, fn) {
|
||||
return Promise.all(
|
||||
arr.map(function(i, el, list) {
|
||||
return Promise.try(fn, [i, el, list]);
|
||||
})
|
||||
);
|
||||
};
|
||||
Promise.each = function(arr, fn) {
|
||||
const queue = arr.slice(0);
|
||||
let i = 0;
|
||||
return (function next() {
|
||||
if (!queue.length) return arr;
|
||||
return Promise.try(fn, [arr.shift(), i++]).then(next);
|
||||
})();
|
||||
};
|
||||
Promise.is = function(obj) {
|
||||
// $q doesn't create instances of any constructor, promises are just objects with a then function
|
||||
// https://github.com/angular/angular.js/blob/58f5da86645990ef984353418cd1ed83213b111e/src/ng/q.js#L335
|
||||
return obj && typeof obj.then === 'function';
|
||||
};
|
||||
Promise.halt = _.once(function() {
|
||||
const promise = new Promise(() => {});
|
||||
promise.then = _.constant(promise);
|
||||
promise.catch = _.constant(promise);
|
||||
return promise;
|
||||
});
|
||||
Promise.try = function(fn, args, ctx) {
|
||||
if (typeof fn !== 'function') {
|
||||
return Promise.reject(new TypeError('fn must be a function'));
|
||||
}
|
||||
|
||||
let value;
|
||||
|
||||
if (Array.isArray(args)) {
|
||||
try {
|
||||
value = fn.apply(ctx, args);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
value = fn.call(ctx, args);
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve(value);
|
||||
};
|
||||
Promise.fromNode = function(takesCbFn) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
takesCbFn(function(err, ...results) {
|
||||
if (err) reject(err);
|
||||
else if (results.length > 1) resolve(results);
|
||||
else resolve(results[0]);
|
||||
});
|
||||
});
|
||||
};
|
||||
Promise.race = function(iterable) {
|
||||
return new Promise((resolve, reject) => {
|
||||
for (const i of iterable) {
|
||||
Promise.resolve(i).then(resolve, reject);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return Promise;
|
||||
}
|
|
@ -1,22 +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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Last remaining 'ui/*' imports that will eventually be shimmed with their np alternatives
|
||||
*/
|
||||
|
||||
export { npSetup, npStart } from 'ui/new_platform';
|
||||
// @ts-ignore
|
||||
export { GlobalStateProvider } from 'ui/state_management/global_state';
|
||||
// @ts-ignore
|
||||
export { StateManagementConfigProvider } from 'ui/state_management/config_provider';
|
||||
// @ts-ignore
|
||||
export { AppStateProvider } from 'ui/state_management/app_state';
|
||||
// @ts-ignore
|
||||
export { EventsProvider } from 'ui/events';
|
||||
// @ts-ignore
|
||||
export { KbnUrlProvider } from 'ui/url';
|
||||
export { registerTimefilterWithGlobalStateFactory } from 'ui/timefilter/setup_router';
|
|
@ -1,8 +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 { npStart } from '../legacy_imports';
|
||||
export const capabilities = { get: () => npStart.core.application.capabilities };
|
|
@ -1,33 +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 angular from 'angular';
|
||||
import { npStart, npSetup } from '../legacy_imports';
|
||||
|
||||
type OptionalInjector = void | angular.auto.IInjectorService;
|
||||
|
||||
class Chrome {
|
||||
private injector?: OptionalInjector;
|
||||
|
||||
public setInjector = (injector: OptionalInjector): void => void (this.injector = injector);
|
||||
public dangerouslyGetActiveInjector = (): OptionalInjector => this.injector;
|
||||
|
||||
public getBasePath = (): string => npStart.core.http.basePath.get();
|
||||
|
||||
public getInjected = (name?: string, defaultValue?: any): string | unknown => {
|
||||
const { getInjectedVar, getInjectedVars } = npSetup.core.injectedMetadata;
|
||||
return name ? getInjectedVar(name, defaultValue) : getInjectedVars();
|
||||
};
|
||||
|
||||
public get breadcrumbs() {
|
||||
const set = (...args: any[]) => npStart.core.chrome.setBreadcrumbs.apply(this, args as any);
|
||||
return { set };
|
||||
}
|
||||
}
|
||||
|
||||
const chrome = new Chrome();
|
||||
|
||||
export default chrome; // eslint-disable-line import/no-default-export
|
|
@ -1,55 +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 angular from 'angular';
|
||||
|
||||
type PrivateProvider = (...args: any) => any;
|
||||
interface Provider {
|
||||
name: string;
|
||||
provider: PrivateProvider;
|
||||
}
|
||||
|
||||
class Modules {
|
||||
private _services: Provider[] = [];
|
||||
private _filters: Provider[] = [];
|
||||
private _directives: Provider[] = [];
|
||||
|
||||
public get = (_name: string, _dep?: string[]) => {
|
||||
return this;
|
||||
};
|
||||
|
||||
public service = (...args: any) => {
|
||||
this._services.push(args);
|
||||
};
|
||||
|
||||
public filter = (...args: any) => {
|
||||
this._filters.push(args);
|
||||
};
|
||||
|
||||
public directive = (...args: any) => {
|
||||
this._directives.push(args);
|
||||
};
|
||||
|
||||
public addToModule = () => {
|
||||
angular.module('monitoring/services', []);
|
||||
angular.module('monitoring/filters', []);
|
||||
angular.module('monitoring/directives', []);
|
||||
|
||||
this._services.forEach(args => {
|
||||
angular.module('monitoring/services').service.apply(null, args as any);
|
||||
});
|
||||
|
||||
this._filters.forEach(args => {
|
||||
angular.module('monitoring/filters').filter.apply(null, args as any);
|
||||
});
|
||||
|
||||
this._directives.forEach(args => {
|
||||
angular.module('monitoring/directives').directive.apply(null, args as any);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const uiModules = new Modules();
|
|
@ -1,31 +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 { IModule, IRootScopeService } from 'angular';
|
||||
import { npStart, registerTimefilterWithGlobalStateFactory } from '../legacy_imports';
|
||||
|
||||
const {
|
||||
core: { uiSettings },
|
||||
} = npStart;
|
||||
export const { timefilter } = npStart.plugins.data.query.timefilter;
|
||||
|
||||
uiSettings.overrideLocalDefault(
|
||||
'timepicker:refreshIntervalDefaults',
|
||||
JSON.stringify({ value: 10000, pause: false })
|
||||
);
|
||||
uiSettings.overrideLocalDefault(
|
||||
'timepicker:timeDefaults',
|
||||
JSON.stringify({ from: 'now-1h', to: 'now' })
|
||||
);
|
||||
|
||||
export const registerTimefilterWithGlobalState = (app: IModule) => {
|
||||
app.run((globalState: any, $rootScope: IRootScopeService) => {
|
||||
globalState.fetch();
|
||||
globalState.$inheritedGlobalState = true;
|
||||
globalState.save();
|
||||
registerTimefilterWithGlobalStateFactory(timefilter, globalState, $rootScope);
|
||||
});
|
||||
};
|
|
@ -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 { App, CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'kibana/public';
|
||||
|
||||
export class MonitoringPlugin implements Plugin {
|
||||
constructor(ctx: PluginInitializerContext) {}
|
||||
|
||||
public setup(core: CoreSetup, plugins: any) {
|
||||
const app: App = {
|
||||
id: 'monitoring',
|
||||
title: 'Monitoring',
|
||||
mount: async (context, params) => {
|
||||
const { AngularApp } = await import('../np_imports/angular');
|
||||
const monitoringApp = new AngularApp(context, params);
|
||||
return monitoringApp.destroy;
|
||||
},
|
||||
};
|
||||
|
||||
core.application.register(app);
|
||||
}
|
||||
|
||||
public start(core: CoreStart, plugins: any) {}
|
||||
public stop() {}
|
||||
}
|
|
@ -1,30 +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 { i18n } from '@kbn/i18n';
|
||||
import chrome from 'ui/chrome';
|
||||
import { npSetup } from 'ui/new_platform';
|
||||
import { FeatureCatalogueCategory } from '../../../../../src/plugins/home/public';
|
||||
|
||||
const {
|
||||
plugins: { home },
|
||||
} = npSetup;
|
||||
|
||||
if (chrome.getInjected('monitoringUiEnabled')) {
|
||||
home.featureCatalogue.register({
|
||||
id: 'monitoring',
|
||||
title: i18n.translate('xpack.monitoring.monitoringTitle', {
|
||||
defaultMessage: 'Monitoring',
|
||||
}),
|
||||
description: i18n.translate('xpack.monitoring.monitoringDescription', {
|
||||
defaultMessage: 'Track the real-time health and performance of your Elastic Stack.',
|
||||
}),
|
||||
icon: 'monitoringApp',
|
||||
path: '/app/monitoring',
|
||||
showOnHomePage: true,
|
||||
category: FeatureCatalogueCategory.ADMIN,
|
||||
});
|
||||
}
|
|
@ -1,10 +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 { uiModules } from 'plugins/monitoring/np_imports/ui/modules';
|
||||
import { breadcrumbsProvider } from './breadcrumbs_provider';
|
||||
const uiModule = uiModules.get('monitoring/breadcrumbs', []);
|
||||
uiModule.service('breadcrumbs', breadcrumbsProvider);
|
|
@ -1,10 +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 { uiModules } from 'plugins/monitoring/np_imports/ui/modules';
|
||||
import { executorProvider } from './executor_provider';
|
||||
const uiModule = uiModules.get('monitoring/executor', []);
|
||||
uiModule.service('$executor', executorProvider);
|
|
@ -1,153 +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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Kibana Instance
|
||||
*/
|
||||
import React from 'react';
|
||||
import { get } from 'lodash';
|
||||
import uiRoutes from 'plugins/monitoring/np_imports/ui/routes';
|
||||
import { ajaxErrorHandlersProvider } from 'plugins/monitoring/lib/ajax_error_handler';
|
||||
import { routeInitProvider } from 'plugins/monitoring/lib/route_init';
|
||||
import template from './index.html';
|
||||
import { timefilter } from 'plugins/monitoring/np_imports/ui/timefilter';
|
||||
import {
|
||||
EuiPage,
|
||||
EuiPageBody,
|
||||
EuiPageContent,
|
||||
EuiSpacer,
|
||||
EuiFlexGrid,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
} from '@elastic/eui';
|
||||
import { MonitoringTimeseriesContainer } from '../../../components/chart';
|
||||
import { DetailStatus } from 'plugins/monitoring/components/kibana/detail_status';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { MonitoringViewBaseController } from '../../base_controller';
|
||||
import { CODE_PATH_KIBANA } from '../../../../common/constants';
|
||||
|
||||
function getPageData($injector) {
|
||||
const $http = $injector.get('$http');
|
||||
const globalState = $injector.get('globalState');
|
||||
const $route = $injector.get('$route');
|
||||
const url = `../api/monitoring/v1/clusters/${globalState.cluster_uuid}/kibana/${$route.current.params.uuid}`;
|
||||
const timeBounds = timefilter.getBounds();
|
||||
|
||||
return $http
|
||||
.post(url, {
|
||||
ccs: globalState.ccs,
|
||||
timeRange: {
|
||||
min: timeBounds.min.toISOString(),
|
||||
max: timeBounds.max.toISOString(),
|
||||
},
|
||||
})
|
||||
.then(response => response.data)
|
||||
.catch(err => {
|
||||
const Private = $injector.get('Private');
|
||||
const ajaxErrorHandlers = Private(ajaxErrorHandlersProvider);
|
||||
return ajaxErrorHandlers(err);
|
||||
});
|
||||
}
|
||||
|
||||
uiRoutes.when('/kibana/instances/:uuid', {
|
||||
template,
|
||||
resolve: {
|
||||
clusters(Private) {
|
||||
const routeInit = Private(routeInitProvider);
|
||||
return routeInit({ codePaths: [CODE_PATH_KIBANA] });
|
||||
},
|
||||
pageData: getPageData,
|
||||
},
|
||||
controllerAs: 'monitoringKibanaInstanceApp',
|
||||
controller: class extends MonitoringViewBaseController {
|
||||
constructor($injector, $scope) {
|
||||
super({
|
||||
title: `Kibana - ${get($scope.pageData, 'kibanaSummary.name')}`,
|
||||
defaultData: {},
|
||||
getPageData,
|
||||
reactNodeId: 'monitoringKibanaInstanceApp',
|
||||
$scope,
|
||||
$injector,
|
||||
});
|
||||
|
||||
$scope.$watch(
|
||||
() => this.data,
|
||||
data => {
|
||||
if (!data || !data.metrics) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setTitle(`Kibana - ${get(data, 'kibanaSummary.name')}`);
|
||||
|
||||
this.renderReact(
|
||||
<I18nContext>
|
||||
<EuiPage>
|
||||
<EuiPageBody>
|
||||
<EuiPanel>
|
||||
<DetailStatus stats={data.kibanaSummary} />
|
||||
</EuiPanel>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiPageContent>
|
||||
<EuiFlexGrid columns={2} gutterSize="s">
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={data.metrics.kibana_requests}
|
||||
onBrush={this.onBrush}
|
||||
zoomInfo={this.zoomInfo}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={data.metrics.kibana_response_times}
|
||||
onBrush={this.onBrush}
|
||||
zoomInfo={this.zoomInfo}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={data.metrics.kibana_memory}
|
||||
onBrush={this.onBrush}
|
||||
zoomInfo={this.zoomInfo}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={data.metrics.kibana_average_concurrent_connections}
|
||||
onBrush={this.onBrush}
|
||||
zoomInfo={this.zoomInfo}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={data.metrics.kibana_os_load}
|
||||
onBrush={this.onBrush}
|
||||
zoomInfo={this.zoomInfo}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={true}>
|
||||
<MonitoringTimeseriesContainer
|
||||
series={data.metrics.kibana_process_delay}
|
||||
onBrush={this.onBrush}
|
||||
zoomInfo={this.zoomInfo}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGrid>
|
||||
</EuiPageContent>
|
||||
</EuiPageBody>
|
||||
</EuiPage>
|
||||
</I18nContext>
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
<monitoring-main name="loading">
|
||||
<div data-test-subj="loadingContainer">
|
||||
<div id="monitoringLoadingReactApp"></div>
|
||||
</div>
|
||||
</monitoring-main>
|
|
@ -1,51 +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 React from 'react';
|
||||
import { render, unmountComponentAtNode } from 'react-dom';
|
||||
import { PageLoading } from 'plugins/monitoring/components';
|
||||
import uiRoutes from 'plugins/monitoring/np_imports/ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import template from './index.html';
|
||||
import { CODE_PATH_LICENSE } from '../../../common/constants';
|
||||
|
||||
const REACT_DOM_ID = 'monitoringLoadingReactApp';
|
||||
|
||||
uiRoutes.when('/loading', {
|
||||
template,
|
||||
controller: class {
|
||||
constructor($injector, $scope) {
|
||||
const monitoringClusters = $injector.get('monitoringClusters');
|
||||
const kbnUrl = $injector.get('kbnUrl');
|
||||
|
||||
$scope.$on('$destroy', () => {
|
||||
unmountComponentAtNode(document.getElementById(REACT_DOM_ID));
|
||||
});
|
||||
|
||||
$scope.$$postDigest(() => {
|
||||
this.renderReact();
|
||||
});
|
||||
|
||||
monitoringClusters(undefined, undefined, [CODE_PATH_LICENSE]).then(clusters => {
|
||||
if (clusters && clusters.length) {
|
||||
kbnUrl.changePath('/home');
|
||||
return;
|
||||
}
|
||||
kbnUrl.changePath('/no-data');
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
renderReact() {
|
||||
render(
|
||||
<I18nContext>
|
||||
<PageLoading />
|
||||
</I18nContext>,
|
||||
document.getElementById(REACT_DOM_ID)
|
||||
);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,65 +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 { i18n } from '@kbn/i18n';
|
||||
import { resolve } from 'path';
|
||||
import {
|
||||
MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS,
|
||||
KIBANA_ALERTING_ENABLED,
|
||||
} from './common/constants';
|
||||
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
|
||||
|
||||
/**
|
||||
* Configuration of dependency objects for the UI, which are needed for the
|
||||
* Monitoring UI app and views and data for outside the monitoring
|
||||
* app (injectDefaultVars and hacks)
|
||||
* @return {Object} data per Kibana plugin uiExport schema
|
||||
*/
|
||||
export const getUiExports = () => {
|
||||
const uiSettingDefaults = {};
|
||||
if (KIBANA_ALERTING_ENABLED) {
|
||||
uiSettingDefaults[MONITORING_CONFIG_ALERTING_EMAIL_ADDRESS] = {
|
||||
name: i18n.translate('xpack.monitoring.alertingEmailAddress.name', {
|
||||
defaultMessage: 'Alerting email address',
|
||||
}),
|
||||
value: '',
|
||||
description: i18n.translate('xpack.monitoring.alertingEmailAddress.description', {
|
||||
defaultMessage: `The default email address to receive alerts from Stack Monitoring`,
|
||||
}),
|
||||
category: ['monitoring'],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
app: {
|
||||
title: i18n.translate('xpack.monitoring.stackMonitoringTitle', {
|
||||
defaultMessage: 'Stack Monitoring',
|
||||
}),
|
||||
order: 9002,
|
||||
description: i18n.translate('xpack.monitoring.uiExportsDescription', {
|
||||
defaultMessage: 'Monitoring for Elastic Stack',
|
||||
}),
|
||||
icon: 'plugins/monitoring/icons/monitoring.svg',
|
||||
euiIconType: 'monitoringApp',
|
||||
linkToLastSubUrl: false,
|
||||
main: 'plugins/monitoring/legacy',
|
||||
category: DEFAULT_APP_CATEGORIES.management,
|
||||
},
|
||||
injectDefaultVars(server) {
|
||||
const config = server.config();
|
||||
return {
|
||||
monitoringUiEnabled: config.get('monitoring.ui.enabled'),
|
||||
monitoringLegacyEmailAddress: config.get(
|
||||
'monitoring.cluster_alerts.email_notifications.email_address'
|
||||
),
|
||||
};
|
||||
},
|
||||
uiSettingDefaults,
|
||||
hacks: ['plugins/monitoring/hacks/toggle_app_link_in_nav'],
|
||||
home: ['plugins/monitoring/register_feature'],
|
||||
styleSheetPaths: resolve(__dirname, 'public/index.scss'),
|
||||
};
|
||||
};
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import { boomify } from 'boom';
|
||||
import { get } from 'lodash';
|
||||
import { KIBANA_SETTINGS_TYPE } from '../../../../../monitoring/common/constants';
|
||||
import { KIBANA_SETTINGS_TYPE } from '../../../../../../../plugins/monitoring/common/constants';
|
||||
|
||||
const getClusterUuid = async callCluster => {
|
||||
const { cluster_uuid: uuid } = await callCluster('info', { filterPath: 'cluster_uuid' });
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
"version": "8.0.0",
|
||||
"kibanaVersion": "kibana",
|
||||
"configPath": ["monitoring"],
|
||||
"requiredPlugins": ["licensing", "features"],
|
||||
"optionalPlugins": ["alerting", "actions", "infra", "telemetryCollectionManager", "usageCollection"],
|
||||
"requiredPlugins": ["licensing", "features", "data", "navigation", "kibanaLegacy"],
|
||||
"optionalPlugins": ["alerting", "actions", "infra", "telemetryCollectionManager", "usageCollection", "home"],
|
||||
"server": true,
|
||||
"ui": false
|
||||
"ui": true
|
||||
}
|
||||
|
|
248
x-pack/plugins/monitoring/public/angular/app_modules.ts
Normal file
248
x-pack/plugins/monitoring/public/angular/app_modules.ts
Normal file
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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 angular, { IWindowService } from 'angular';
|
||||
import '../views/all';
|
||||
// required for `ngSanitize` angular module
|
||||
import 'angular-sanitize';
|
||||
import 'angular-route';
|
||||
import '../index.scss';
|
||||
import { capitalize } from 'lodash';
|
||||
import { i18nDirective, i18nFilter, I18nProvider } from '@kbn/i18n/angular';
|
||||
import { AppMountContext } from 'kibana/public';
|
||||
import { Storage } from '../../../../../src/plugins/kibana_utils/public';
|
||||
import {
|
||||
createTopNavDirective,
|
||||
createTopNavHelper,
|
||||
} from '../../../../../src/plugins/kibana_legacy/public';
|
||||
import { MonitoringPluginDependencies } from '../types';
|
||||
import { GlobalState } from '../url_state';
|
||||
import { getSafeForExternalLink } from '../lib/get_safe_for_external_link';
|
||||
|
||||
// @ts-ignore
|
||||
import { formatNumber, formatMetric } from '../lib/format_number';
|
||||
// @ts-ignore
|
||||
import { extractIp } from '../lib/extract_ip';
|
||||
// @ts-ignore
|
||||
import { PrivateProvider } from './providers/private';
|
||||
// @ts-ignore
|
||||
import { KbnUrlProvider } from './providers/url';
|
||||
// @ts-ignore
|
||||
import { breadcrumbsProvider } from '../services/breadcrumbs';
|
||||
// @ts-ignore
|
||||
import { monitoringClustersProvider } from '../services/clusters';
|
||||
// @ts-ignore
|
||||
import { executorProvider } from '../services/executor';
|
||||
// @ts-ignore
|
||||
import { featuresProvider } from '../services/features';
|
||||
// @ts-ignore
|
||||
import { licenseProvider } from '../services/license';
|
||||
// @ts-ignore
|
||||
import { titleProvider } from '../services/title';
|
||||
// @ts-ignore
|
||||
import { monitoringBeatsBeatProvider } from '../directives/beats/beat';
|
||||
// @ts-ignore
|
||||
import { monitoringBeatsOverviewProvider } from '../directives/beats/overview';
|
||||
// @ts-ignore
|
||||
import { monitoringMlListingProvider } from '../directives/elasticsearch/ml_job_listing';
|
||||
// @ts-ignore
|
||||
import { monitoringMainProvider } from '../directives/main';
|
||||
|
||||
export const appModuleName = 'monitoring';
|
||||
|
||||
type IPrivate = <T>(provider: (...injectable: unknown[]) => T) => T;
|
||||
|
||||
const thirdPartyAngularDependencies = ['ngSanitize', 'ngRoute', 'react', 'ui.bootstrap'];
|
||||
|
||||
export const localAppModule = ({
|
||||
core,
|
||||
data: { query },
|
||||
navigation,
|
||||
externalConfig,
|
||||
}: MonitoringPluginDependencies) => {
|
||||
createLocalI18nModule();
|
||||
createLocalPrivateModule();
|
||||
createLocalStorage();
|
||||
createLocalConfigModule(core);
|
||||
createLocalKbnUrlModule();
|
||||
createLocalStateModule(query);
|
||||
createLocalTopNavModule(navigation);
|
||||
createHrefModule(core);
|
||||
createMonitoringAppServices();
|
||||
createMonitoringAppDirectives();
|
||||
createMonitoringAppConfigConstants(externalConfig);
|
||||
createMonitoringAppFilters();
|
||||
|
||||
const appModule = angular.module(appModuleName, [
|
||||
...thirdPartyAngularDependencies,
|
||||
'monitoring/I18n',
|
||||
'monitoring/Private',
|
||||
'monitoring/KbnUrl',
|
||||
'monitoring/Storage',
|
||||
'monitoring/Config',
|
||||
'monitoring/State',
|
||||
'monitoring/TopNav',
|
||||
'monitoring/href',
|
||||
'monitoring/constants',
|
||||
'monitoring/services',
|
||||
'monitoring/filters',
|
||||
'monitoring/directives',
|
||||
]);
|
||||
return appModule;
|
||||
};
|
||||
|
||||
function createMonitoringAppConfigConstants(keys: MonitoringPluginDependencies['externalConfig']) {
|
||||
let constantsModule = angular.module('monitoring/constants', []);
|
||||
keys.map(([key, value]) => (constantsModule = constantsModule.constant(key as string, value)));
|
||||
}
|
||||
|
||||
function createLocalStateModule(query: any) {
|
||||
angular
|
||||
.module('monitoring/State', ['monitoring/Private'])
|
||||
.service('globalState', function(
|
||||
Private: IPrivate,
|
||||
$rootScope: ng.IRootScopeService,
|
||||
$location: ng.ILocationService
|
||||
) {
|
||||
function GlobalStateProvider(this: any) {
|
||||
const state = new GlobalState(query, $rootScope, $location, this);
|
||||
const initialState: any = state.getState();
|
||||
for (const key in initialState) {
|
||||
if (!initialState.hasOwnProperty(key)) {
|
||||
continue;
|
||||
}
|
||||
this[key] = initialState[key];
|
||||
}
|
||||
this.save = () => {
|
||||
const newState = { ...this };
|
||||
delete newState.save;
|
||||
state.setState(newState);
|
||||
};
|
||||
}
|
||||
return Private(GlobalStateProvider);
|
||||
});
|
||||
}
|
||||
|
||||
function createLocalKbnUrlModule() {
|
||||
angular
|
||||
.module('monitoring/KbnUrl', ['monitoring/Private', 'ngRoute'])
|
||||
.service('kbnUrl', function(Private: IPrivate) {
|
||||
return Private(KbnUrlProvider);
|
||||
});
|
||||
}
|
||||
|
||||
function createMonitoringAppServices() {
|
||||
angular
|
||||
.module('monitoring/services', ['monitoring/Private'])
|
||||
.service('breadcrumbs', function(Private: IPrivate) {
|
||||
return Private(breadcrumbsProvider);
|
||||
})
|
||||
.service('monitoringClusters', function(Private: IPrivate) {
|
||||
return Private(monitoringClustersProvider);
|
||||
})
|
||||
.service('$executor', function(Private: IPrivate) {
|
||||
return Private(executorProvider);
|
||||
})
|
||||
.service('features', function(Private: IPrivate) {
|
||||
return Private(featuresProvider);
|
||||
})
|
||||
.service('license', function(Private: IPrivate) {
|
||||
return Private(licenseProvider);
|
||||
})
|
||||
.service('title', function(Private: IPrivate) {
|
||||
return Private(titleProvider);
|
||||
});
|
||||
}
|
||||
|
||||
function createMonitoringAppDirectives() {
|
||||
angular
|
||||
.module('monitoring/directives', [])
|
||||
.directive('monitoringBeatsBeat', monitoringBeatsBeatProvider)
|
||||
.directive('monitoringBeatsOverview', monitoringBeatsOverviewProvider)
|
||||
.directive('monitoringMlListing', monitoringMlListingProvider)
|
||||
.directive('monitoringMain', monitoringMainProvider);
|
||||
}
|
||||
|
||||
function createMonitoringAppFilters() {
|
||||
angular
|
||||
.module('monitoring/filters', [])
|
||||
.filter('capitalize', function() {
|
||||
return function(input: string) {
|
||||
return capitalize(input?.toLowerCase());
|
||||
};
|
||||
})
|
||||
.filter('formatNumber', function() {
|
||||
return formatNumber;
|
||||
})
|
||||
.filter('formatMetric', function() {
|
||||
return formatMetric;
|
||||
})
|
||||
.filter('extractIp', function() {
|
||||
return extractIp;
|
||||
});
|
||||
}
|
||||
|
||||
function createLocalConfigModule(core: MonitoringPluginDependencies['core']) {
|
||||
angular.module('monitoring/Config', []).provider('config', function() {
|
||||
return {
|
||||
$get: () => ({
|
||||
get: (key: string) => core.uiSettings?.get(key),
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function createLocalStorage() {
|
||||
angular
|
||||
.module('monitoring/Storage', [])
|
||||
.service('localStorage', function($window: IWindowService) {
|
||||
return new Storage($window.localStorage);
|
||||
})
|
||||
.service('sessionStorage', function($window: IWindowService) {
|
||||
return new Storage($window.sessionStorage);
|
||||
})
|
||||
.service('sessionTimeout', function() {
|
||||
return {};
|
||||
});
|
||||
}
|
||||
|
||||
function createLocalPrivateModule() {
|
||||
angular.module('monitoring/Private', []).provider('Private', PrivateProvider);
|
||||
}
|
||||
|
||||
function createLocalTopNavModule({ ui }: MonitoringPluginDependencies['navigation']) {
|
||||
angular
|
||||
.module('monitoring/TopNav', ['react'])
|
||||
.directive('kbnTopNav', createTopNavDirective)
|
||||
.directive('kbnTopNavHelper', createTopNavHelper(ui));
|
||||
}
|
||||
|
||||
function createLocalI18nModule() {
|
||||
angular
|
||||
.module('monitoring/I18n', [])
|
||||
.provider('i18n', I18nProvider)
|
||||
.filter('i18n', i18nFilter)
|
||||
.directive('i18nId', i18nDirective);
|
||||
}
|
||||
|
||||
function createHrefModule(core: AppMountContext['core']) {
|
||||
const name: string = 'kbnHref';
|
||||
angular.module('monitoring/href', []).directive(name, function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: {
|
||||
pre: (_$scope, _$el, $attr) => {
|
||||
$attr.$observe(name, val => {
|
||||
if (val) {
|
||||
const url = getSafeForExternalLink(val as string);
|
||||
$attr.$set('href', core.http.basePath.prepend(url));
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
|
@ -4,36 +4,35 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
type RouteObject = [string, any];
|
||||
type RouteObject = [string, { reloadOnSearch: boolean }];
|
||||
interface Redirect {
|
||||
redirectTo: string;
|
||||
}
|
||||
|
||||
class Routes {
|
||||
private _routes: RouteObject[] = [];
|
||||
private _redirect?: Redirect;
|
||||
private routes: RouteObject[] = [];
|
||||
public redirect?: Redirect = { redirectTo: '/no-data' };
|
||||
|
||||
public when = (...args: RouteObject) => {
|
||||
const [, routeOptions] = args;
|
||||
routeOptions.reloadOnSearch = false;
|
||||
this._routes.push(args);
|
||||
this.routes.push(args);
|
||||
return this;
|
||||
};
|
||||
|
||||
public otherwise = (redirect: Redirect) => {
|
||||
this._redirect = redirect;
|
||||
this.redirect = redirect;
|
||||
return this;
|
||||
};
|
||||
|
||||
public addToProvider = ($routeProvider: any) => {
|
||||
this._routes.forEach(args => {
|
||||
this.routes.forEach(args => {
|
||||
$routeProvider.when.apply(this, args);
|
||||
});
|
||||
|
||||
if (this._redirect) {
|
||||
$routeProvider.otherwise(this._redirect);
|
||||
if (this.redirect) {
|
||||
$routeProvider.otherwise(this.redirect);
|
||||
}
|
||||
};
|
||||
}
|
||||
const uiRoutes = new Routes();
|
||||
export default uiRoutes; // eslint-disable-line import/no-default-export
|
||||
export const uiRoutes = new Routes();
|
68
x-pack/plugins/monitoring/public/angular/index.ts
Normal file
68
x-pack/plugins/monitoring/public/angular/index.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 angular, { IModule } from 'angular';
|
||||
import { uiRoutes } from './helpers/routes';
|
||||
import { Legacy } from '../legacy_shims';
|
||||
import { configureAppAngularModule } from '../../../../../src/plugins/kibana_legacy/public';
|
||||
import { localAppModule, appModuleName } from './app_modules';
|
||||
|
||||
import { MonitoringPluginDependencies } from '../types';
|
||||
|
||||
const APP_WRAPPER_CLASS = 'monitoringApplicationWrapper';
|
||||
export class AngularApp {
|
||||
private injector?: angular.auto.IInjectorService;
|
||||
|
||||
constructor(deps: MonitoringPluginDependencies) {
|
||||
const {
|
||||
core,
|
||||
element,
|
||||
data,
|
||||
navigation,
|
||||
isCloud,
|
||||
pluginInitializerContext,
|
||||
externalConfig,
|
||||
} = deps;
|
||||
const app: IModule = localAppModule(deps);
|
||||
app.run(($injector: angular.auto.IInjectorService) => {
|
||||
this.injector = $injector;
|
||||
Legacy.init(
|
||||
{ core, element, data, navigation, isCloud, pluginInitializerContext, externalConfig },
|
||||
this.injector
|
||||
);
|
||||
});
|
||||
|
||||
app.config(($routeProvider: unknown) => uiRoutes.addToProvider($routeProvider));
|
||||
|
||||
const np = { core, env: pluginInitializerContext.env };
|
||||
configureAppAngularModule(app, np, true);
|
||||
const appElement = document.createElement('div');
|
||||
appElement.setAttribute('style', 'height: 100%');
|
||||
appElement.innerHTML = '<div ng-view style="height: 100%" id="monitoring-angular-app"></div>';
|
||||
|
||||
if (!element.classList.contains(APP_WRAPPER_CLASS)) {
|
||||
element.classList.add(APP_WRAPPER_CLASS);
|
||||
}
|
||||
|
||||
angular.bootstrap(appElement, [appModuleName]);
|
||||
angular.element(element).append(appElement);
|
||||
}
|
||||
|
||||
public destroy = () => {
|
||||
if (this.injector) {
|
||||
this.injector.get('$rootScope').$destroy();
|
||||
}
|
||||
};
|
||||
|
||||
public applyScope = () => {
|
||||
if (!this.injector) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rootScope = this.injector.get('$rootScope');
|
||||
rootScope.$applyAsync();
|
||||
};
|
||||
}
|
|
@ -193,4 +193,6 @@ export function PrivateProvider() {
|
|||
return Private;
|
||||
},
|
||||
];
|
||||
|
||||
return provider;
|
||||
}
|
217
x-pack/plugins/monitoring/public/angular/providers/url.js
vendored
Normal file
217
x-pack/plugins/monitoring/public/angular/providers/url.js
vendored
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
export function KbnUrlProvider($injector, $location, $rootScope, $parse) {
|
||||
/**
|
||||
* the `kbnUrl` service was created to smooth over some of the
|
||||
* inconsistent behavior that occurs when modifying the url via
|
||||
* the `$location` api. In general it is recommended that you use
|
||||
* the `kbnUrl` service any time you want to modify the url.
|
||||
*
|
||||
* "features" that `kbnUrl` does it's best to guarantee, which
|
||||
* are not guaranteed with the `$location` service:
|
||||
* - calling `kbnUrl.change()` with a url that resolves to the current
|
||||
* route will force a full transition (rather than just updating the
|
||||
* properties of the $route object)
|
||||
*
|
||||
* Additional features of `kbnUrl`
|
||||
* - parameterized urls
|
||||
* - easily include an app state with the url
|
||||
*
|
||||
* @type {KbnUrl}
|
||||
*/
|
||||
const self = this;
|
||||
|
||||
/**
|
||||
* Navigate to a url
|
||||
*
|
||||
* @param {String} url - the new url, can be a template. See #eval
|
||||
* @param {Object} [paramObj] - optional set of parameters for the url template
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.change = function(url, paramObj, appState) {
|
||||
self._changeLocation('url', url, paramObj, false, appState);
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as #change except only changes the url's path,
|
||||
* leaving the search string and such intact
|
||||
*
|
||||
* @param {String} path - the new path, can be a template. See #eval
|
||||
* @param {Object} [paramObj] - optional set of parameters for the path template
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.changePath = function(path, paramObj) {
|
||||
self._changeLocation('path', path, paramObj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as #change except that it removes the current url from history
|
||||
*
|
||||
* @param {String} url - the new url, can be a template. See #eval
|
||||
* @param {Object} [paramObj] - optional set of parameters for the url template
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.redirect = function(url, paramObj, appState) {
|
||||
self._changeLocation('url', url, paramObj, true, appState);
|
||||
};
|
||||
|
||||
/**
|
||||
* Same as #redirect except only changes the url's path,
|
||||
* leaving the search string and such intact
|
||||
*
|
||||
* @param {String} path - the new path, can be a template. See #eval
|
||||
* @param {Object} [paramObj] - optional set of parameters for the path template
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.redirectPath = function(path, paramObj) {
|
||||
self._changeLocation('path', path, paramObj, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate a url template. templates can contain double-curly wrapped
|
||||
* expressions that are evaluated in the context of the paramObj
|
||||
*
|
||||
* @param {String} template - the url template to evaluate
|
||||
* @param {Object} [paramObj] - the variables to expose to the template
|
||||
* @return {String} - the evaluated result
|
||||
* @throws {Error} If any of the expressions can't be parsed.
|
||||
*/
|
||||
self.eval = function(template, paramObj) {
|
||||
paramObj = paramObj || {};
|
||||
|
||||
return template.replace(/\{\{([^\}]+)\}\}/g, function(match, expr) {
|
||||
// remove filters
|
||||
const key = expr.split('|')[0].trim();
|
||||
|
||||
// verify that the expression can be evaluated
|
||||
const p = $parse(key)(paramObj);
|
||||
|
||||
// if evaluation can't be made, throw
|
||||
if (_.isUndefined(p)) {
|
||||
throw new Error(`Replacement failed, unresolved expression: ${expr}`);
|
||||
}
|
||||
|
||||
return encodeURIComponent($parse(expr)(paramObj));
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* convert an object's route to an href, compatible with
|
||||
* window.location.href= and <a href="">
|
||||
*
|
||||
* @param {Object} obj - any object that list's it's routes at obj.routes{}
|
||||
* @param {string} route - the route name
|
||||
* @return {string} - the computed href
|
||||
*/
|
||||
self.getRouteHref = function(obj, route) {
|
||||
return '#' + self.getRouteUrl(obj, route);
|
||||
};
|
||||
|
||||
/**
|
||||
* convert an object's route to a url, compatible with url.change() or $location.url()
|
||||
*
|
||||
* @param {Object} obj - any object that list's it's routes at obj.routes{}
|
||||
* @param {string} route - the route name
|
||||
* @return {string} - the computed url
|
||||
*/
|
||||
self.getRouteUrl = function(obj, route) {
|
||||
const template = obj && obj.routes && obj.routes[route];
|
||||
if (template) return self.eval(template, obj);
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to getRouteUrl, supports objects which list their routes,
|
||||
* and redirects to the named route. See #redirect
|
||||
*
|
||||
* @param {Object} obj - any object that list's it's routes at obj.routes{}
|
||||
* @param {string} route - the route name
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.redirectToRoute = function(obj, route) {
|
||||
self.redirect(self.getRouteUrl(obj, route));
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to getRouteUrl, supports objects which list their routes,
|
||||
* and changes the url to the named route. See #change
|
||||
*
|
||||
* @param {Object} obj - any object that list's it's routes at obj.routes{}
|
||||
* @param {string} route - the route name
|
||||
* @return {undefined}
|
||||
*/
|
||||
self.changeToRoute = function(obj, route) {
|
||||
self.change(self.getRouteUrl(obj, route));
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the given parameter from the url. Does so without modifying the browser
|
||||
* history.
|
||||
* @param param
|
||||
*/
|
||||
self.removeParam = function(param) {
|
||||
$location.search(param, null).replace();
|
||||
};
|
||||
|
||||
/////
|
||||
// private api
|
||||
/////
|
||||
let reloading;
|
||||
|
||||
self._changeLocation = function(type, url, paramObj, replace, appState) {
|
||||
const prev = {
|
||||
path: $location.path(),
|
||||
search: $location.search(),
|
||||
};
|
||||
|
||||
url = self.eval(url, paramObj);
|
||||
$location[type](url);
|
||||
if (replace) $location.replace();
|
||||
|
||||
if (appState) {
|
||||
$location.search(appState.getQueryParamName(), appState.toQueryParam());
|
||||
}
|
||||
|
||||
const next = {
|
||||
path: $location.path(),
|
||||
search: $location.search(),
|
||||
};
|
||||
|
||||
if ($injector.has('$route')) {
|
||||
const $route = $injector.get('$route');
|
||||
|
||||
if (self._shouldForceReload(next, prev, $route)) {
|
||||
reloading = $rootScope.$on('$locationChangeSuccess', function() {
|
||||
// call the "unlisten" function returned by $on
|
||||
reloading();
|
||||
reloading = false;
|
||||
|
||||
$route.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// determine if the router will automatically reload the route
|
||||
self._shouldForceReload = function(next, prev, $route) {
|
||||
if (reloading) return false;
|
||||
|
||||
const route = $route.current && $route.current.$$route;
|
||||
if (!route) return false;
|
||||
|
||||
// for the purposes of determining whether the router will
|
||||
// automatically be reloading, '' and '/' are equal
|
||||
const nextPath = next.path || '/';
|
||||
const prevPath = prev.path || '/';
|
||||
if (nextPath !== prevPath) return false;
|
||||
|
||||
const reloadOnSearch = route.reloadOnSearch;
|
||||
const searchSame = _.isEqual(next.search, prev.search);
|
||||
return (reloadOnSearch && searchSame) || !reloadOnSearch;
|
||||
};
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import chrome from '../../np_imports/ui/chrome';
|
||||
import { Legacy } from '../../legacy_shims';
|
||||
import { capitalize, get } from 'lodash';
|
||||
import { formatDateTimeLocal } from '../../../common/formatting';
|
||||
import { formatTimestampToDuration } from '../../../common';
|
||||
|
@ -16,8 +16,8 @@ import {
|
|||
ALERT_TYPE_CLUSTER_STATE,
|
||||
} from '../../../common/constants';
|
||||
import { mapSeverity } from './map_severity';
|
||||
import { FormattedAlert } from 'plugins/monitoring/components/alerts/formatted_alert';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { FormattedAlert } from '../../components/alerts/formatted_alert';
|
||||
import { EuiMonitoringTable } from '../../components/table';
|
||||
import { EuiHealth, EuiIcon, EuiToolTip } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
|
@ -162,7 +162,7 @@ export const Alerts = ({ alerts, angular, sorting, pagination, onTableChange })
|
|||
category: get(alert, 'metadata.link', get(alert, 'type', null)),
|
||||
}));
|
||||
|
||||
const injector = chrome.dangerouslyGetActiveInjector();
|
||||
const injector = Legacy.shims.getAngularInjector();
|
||||
const timezone = injector.get('config').get('dateFormat:tz');
|
||||
|
||||
return (
|
|
@ -7,11 +7,15 @@
|
|||
import React from 'react';
|
||||
import { mockUseEffects } from '../../../jest.helpers';
|
||||
import { shallow, ShallowWrapper } from 'enzyme';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { Legacy } from '../../../legacy_shims';
|
||||
import { AlertsConfiguration, AlertsConfigurationProps } from './configuration';
|
||||
|
||||
jest.mock('ui/kfetch', () => ({
|
||||
kfetch: jest.fn(),
|
||||
jest.mock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch: jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const defaultProps: AlertsConfigurationProps = {
|
||||
|
@ -61,7 +65,7 @@ describe('Configuration', () => {
|
|||
beforeEach(async () => {
|
||||
mockUseEffects(2);
|
||||
|
||||
(kfetch as jest.Mock).mockImplementation(() => {
|
||||
(Legacy.shims.kfetch as jest.Mock).mockImplementation(() => {
|
||||
return {
|
||||
data: [
|
||||
{
|
||||
|
@ -101,7 +105,7 @@ describe('Configuration', () => {
|
|||
describe('edit action', () => {
|
||||
let component: ShallowWrapper;
|
||||
beforeEach(async () => {
|
||||
(kfetch as jest.Mock).mockImplementation(() => {
|
||||
(Legacy.shims.kfetch as jest.Mock).mockImplementation(() => {
|
||||
return {
|
||||
data: [],
|
||||
};
|
||||
|
@ -124,7 +128,7 @@ describe('Configuration', () => {
|
|||
describe('no email address', () => {
|
||||
let component: ShallowWrapper;
|
||||
beforeEach(async () => {
|
||||
(kfetch as jest.Mock).mockImplementation(() => {
|
||||
(Legacy.shims.kfetch as jest.Mock).mockImplementation(() => {
|
||||
return {
|
||||
data: [
|
||||
{
|
|
@ -5,10 +5,10 @@
|
|||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { EuiSteps } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ActionResult } from '../../../../../../../plugins/actions/common';
|
||||
import { Legacy } from '../../../legacy_shims';
|
||||
import { ActionResult } from '../../../../../../plugins/actions/common';
|
||||
import { ALERT_ACTION_TYPE_EMAIL } from '../../../../common/constants';
|
||||
import { getMissingFieldErrors } from '../../../lib/form_validation';
|
||||
import { Step1 } from './step1';
|
||||
|
@ -59,7 +59,7 @@ export const AlertsConfiguration: React.FC<AlertsConfigurationProps> = (
|
|||
}, [emailAddress]);
|
||||
|
||||
async function fetchEmailActions() {
|
||||
const kibanaActions = await kfetch({
|
||||
const kibanaActions = await Legacy.shims.kfetch({
|
||||
method: 'GET',
|
||||
pathname: `/api/action/_getAll`,
|
||||
});
|
||||
|
@ -84,7 +84,7 @@ export const AlertsConfiguration: React.FC<AlertsConfigurationProps> = (
|
|||
setShowFormErrors(false);
|
||||
|
||||
try {
|
||||
await kfetch({
|
||||
await Legacy.shims.kfetch({
|
||||
method: 'POST',
|
||||
pathname: `/api/monitoring/v1/alerts`,
|
||||
body: JSON.stringify({ selectedEmailActionId, emailAddress }),
|
|
@ -49,9 +49,13 @@ describe('Step1', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch: () => {
|
||||
return {};
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch: () => {
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
|
@ -97,8 +101,12 @@ describe('Step1', () => {
|
|||
it('should send up the create to the server', async () => {
|
||||
const kfetch = jest.fn().mockImplementation(() => {});
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch,
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch,
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
});
|
||||
|
@ -152,8 +160,12 @@ describe('Step1', () => {
|
|||
it('should send up the edit to the server', async () => {
|
||||
const kfetch = jest.fn().mockImplementation(() => {});
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch,
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch,
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
});
|
||||
|
@ -194,13 +206,17 @@ describe('Step1', () => {
|
|||
describe('testing', () => {
|
||||
it('should allow for testing', async () => {
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch: jest.fn().mockImplementation(arg => {
|
||||
if (arg.pathname === '/api/action/1/_execute') {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch: jest.fn().mockImplementation(arg => {
|
||||
if (arg.pathname === '/api/action/1/_execute') {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
return {};
|
||||
}),
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
});
|
||||
|
@ -234,12 +250,16 @@ describe('Step1', () => {
|
|||
|
||||
it('should show a successful test', async () => {
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch: (arg: any) => {
|
||||
if (arg.pathname === '/api/action/1/_execute') {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
return {};
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch: (arg: any) => {
|
||||
if (arg.pathname === '/api/action/1/_execute') {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
|
@ -257,12 +277,16 @@ describe('Step1', () => {
|
|||
|
||||
it('should show a failed test error', async () => {
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch: (arg: any) => {
|
||||
if (arg.pathname === '/api/action/1/_execute') {
|
||||
return { message: 'Very detailed error message' };
|
||||
}
|
||||
return {};
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch: (arg: any) => {
|
||||
if (arg.pathname === '/api/action/1/_execute') {
|
||||
return { message: 'Very detailed error message' };
|
||||
}
|
||||
return {};
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
|
@ -304,8 +328,12 @@ describe('Step1', () => {
|
|||
it('should send up the delete to the server', async () => {
|
||||
const kfetch = jest.fn().mockImplementation(() => {});
|
||||
jest.isolateModules(() => {
|
||||
jest.doMock('ui/kfetch', () => ({
|
||||
kfetch,
|
||||
jest.doMock('../../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch,
|
||||
},
|
||||
},
|
||||
}));
|
||||
setModules();
|
||||
});
|
|
@ -16,10 +16,10 @@ import {
|
|||
EuiToolTip,
|
||||
EuiCallOut,
|
||||
} from '@elastic/eui';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { omit, pick } from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ActionResult, BASE_ACTION_API_PATH } from '../../../../../../../plugins/actions/common';
|
||||
import { Legacy } from '../../../legacy_shims';
|
||||
import { ActionResult, BASE_ACTION_API_PATH } from '../../../../../../plugins/actions/common';
|
||||
import { ManageEmailAction, EmailActionData } from '../manage_email_action';
|
||||
import { ALERT_ACTION_TYPE_EMAIL } from '../../../../common/constants';
|
||||
import { NEW_ACTION_ID } from './configuration';
|
||||
|
@ -42,7 +42,7 @@ export const Step1: React.FC<GetStep1Props> = (props: GetStep1Props) => {
|
|||
|
||||
async function createEmailAction(data: EmailActionData) {
|
||||
if (props.editAction) {
|
||||
await kfetch({
|
||||
await Legacy.shims.kfetch({
|
||||
method: 'PUT',
|
||||
pathname: `${BASE_ACTION_API_PATH}/${props.editAction.id}`,
|
||||
body: JSON.stringify({
|
||||
|
@ -53,7 +53,7 @@ export const Step1: React.FC<GetStep1Props> = (props: GetStep1Props) => {
|
|||
});
|
||||
props.setEditAction(null);
|
||||
} else {
|
||||
await kfetch({
|
||||
await Legacy.shims.kfetch({
|
||||
method: 'POST',
|
||||
pathname: BASE_ACTION_API_PATH,
|
||||
body: JSON.stringify({
|
||||
|
@ -73,7 +73,7 @@ export const Step1: React.FC<GetStep1Props> = (props: GetStep1Props) => {
|
|||
async function deleteEmailAction(id: string) {
|
||||
setIsDeleting(true);
|
||||
|
||||
await kfetch({
|
||||
await Legacy.shims.kfetch({
|
||||
method: 'DELETE',
|
||||
pathname: `${BASE_ACTION_API_PATH}/${id}`,
|
||||
});
|
||||
|
@ -99,7 +99,7 @@ export const Step1: React.FC<GetStep1Props> = (props: GetStep1Props) => {
|
|||
to: [props.emailAddress],
|
||||
};
|
||||
|
||||
const result = await kfetch({
|
||||
const result = await Legacy.shims.kfetch({
|
||||
method: 'POST',
|
||||
pathname: `${BASE_ACTION_API_PATH}/${props.selectedEmailActionId}/_execute`,
|
||||
body: JSON.stringify({ params }),
|
|
@ -21,7 +21,7 @@ import {
|
|||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { ActionResult } from '../../../../../../plugins/actions/common';
|
||||
import { ActionResult } from '../../../../../plugins/actions/common';
|
||||
import { getMissingFieldErrors, hasErrors, getRequiredFieldError } from '../../lib/form_validation';
|
||||
import { ALERT_EMAIL_SERVICES } from '../../../common/constants';
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import { Legacy } from '../../legacy_shims';
|
||||
import { AlertsStatus, AlertsStatusProps } from './status';
|
||||
import { ALERT_TYPES } from '../../../common/constants';
|
||||
import { getSetupModeState } from '../../lib/setup_mode';
|
||||
|
@ -18,8 +18,16 @@ jest.mock('../../lib/setup_mode', () => ({
|
|||
toggleSetupMode: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('ui/kfetch', () => ({
|
||||
kfetch: jest.fn(),
|
||||
jest.mock('../../legacy_shims', () => ({
|
||||
Legacy: {
|
||||
shims: {
|
||||
kfetch: jest.fn(),
|
||||
docLinks: {
|
||||
ELASTIC_WEBSITE_URL: 'https://www.elastic.co/',
|
||||
DOC_LINK_VERSION: 'current',
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const defaultProps: AlertsStatusProps = {
|
||||
|
@ -35,7 +43,7 @@ describe('Status', () => {
|
|||
enabled: false,
|
||||
});
|
||||
|
||||
(kfetch as jest.Mock).mockImplementation(({ pathname }) => {
|
||||
(Legacy.shims.kfetch as jest.Mock).mockImplementation(({ pathname }) => {
|
||||
if (pathname === '/internal/security/api_key/privileges') {
|
||||
return { areApiKeysEnabled: true };
|
||||
}
|
||||
|
@ -62,7 +70,7 @@ describe('Status', () => {
|
|||
});
|
||||
|
||||
it('should render a success message if all alerts have been migrated and in setup mode', async () => {
|
||||
(kfetch as jest.Mock).mockReturnValue({
|
||||
(Legacy.shims.kfetch as jest.Mock).mockReturnValue({
|
||||
data: ALERT_TYPES.map(type => ({ alertTypeId: type })),
|
||||
});
|
||||
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
import React, { Fragment } from 'react';
|
||||
import { kfetch } from 'ui/kfetch';
|
||||
import {
|
||||
EuiSpacer,
|
||||
EuiCallOut,
|
||||
|
@ -18,8 +17,8 @@ import {
|
|||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } from 'ui/documentation_links';
|
||||
import { Alert, BASE_ALERT_API_PATH } from '../../../../../../plugins/alerting/common';
|
||||
import { Legacy } from '../../legacy_shims';
|
||||
import { Alert, BASE_ALERT_API_PATH } from '../../../../../plugins/alerting/common';
|
||||
import { getSetupModeState, addSetupModeCallback, toggleSetupMode } from '../../lib/setup_mode';
|
||||
import { NUMBER_OF_MIGRATED_ALERTS, ALERT_TYPE_PREFIX } from '../../../common/constants';
|
||||
import { AlertsConfiguration } from './configuration';
|
||||
|
@ -39,7 +38,10 @@ export const AlertsStatus: React.FC<AlertsStatusProps> = (props: AlertsStatusPro
|
|||
|
||||
React.useEffect(() => {
|
||||
async function fetchAlertsStatus() {
|
||||
const alerts = await kfetch({ method: 'GET', pathname: `${BASE_ALERT_API_PATH}/_find` });
|
||||
const alerts = await Legacy.shims.kfetch({
|
||||
method: 'GET',
|
||||
pathname: `${BASE_ALERT_API_PATH}/_find`,
|
||||
});
|
||||
const monitoringAlerts = alerts.data.filter((alert: Alert) =>
|
||||
alert.alertTypeId.startsWith(ALERT_TYPE_PREFIX)
|
||||
);
|
||||
|
@ -57,7 +59,9 @@ export const AlertsStatus: React.FC<AlertsStatusProps> = (props: AlertsStatusPro
|
|||
}, [setupModeEnabled, showMigrationFlyout]);
|
||||
|
||||
async function fetchSecurityConfigured() {
|
||||
const response = await kfetch({ pathname: '/internal/security/api_key/privileges' });
|
||||
const response = await Legacy.shims.kfetch({
|
||||
pathname: '/internal/security/api_key/privileges',
|
||||
});
|
||||
setIsSecurityConfigured(response.areApiKeysEnabled);
|
||||
}
|
||||
|
||||
|
@ -72,7 +76,7 @@ export const AlertsStatus: React.FC<AlertsStatusProps> = (props: AlertsStatusPro
|
|||
if (isSecurityConfigured) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = Legacy.shims.docLinks;
|
||||
const link = `${ELASTIC_WEBSITE_URL}guide/en/elasticsearch/reference/${DOC_LINK_VERSION}/security-settings.html#api-key-service-settings`;
|
||||
return (
|
||||
<Fragment>
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { StatusIcon } from 'plugins/monitoring/components/status_icon';
|
||||
import { StatusIcon } from '../../components/status_icon';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export function ApmStatusIcon({ status, availability = true }) {
|
|
@ -14,9 +14,9 @@ import {
|
|||
EuiLink,
|
||||
EuiScreenReaderOnly,
|
||||
} from '@elastic/eui';
|
||||
import { Stats } from 'plugins/monitoring/components/beats';
|
||||
import { formatMetric } from 'plugins/monitoring/lib/format_number';
|
||||
import { EuiMonitoringTable } from 'plugins/monitoring/components/table';
|
||||
import { Stats } from '../../beats';
|
||||
import { formatMetric } from '../../../lib/format_number';
|
||||
import { EuiMonitoringTable } from '../../table';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { BEATS_SYSTEM_ID } from '../../../../common/constants';
|
||||
import { getSafeForExternalLink } from '../../../lib/get_safe_for_external_link';
|
|
@ -10,16 +10,10 @@ import { shallow } from 'enzyme';
|
|||
jest.mock('../stats', () => ({
|
||||
Stats: () => 'Stats',
|
||||
}));
|
||||
jest.mock('../../', () => ({
|
||||
jest.mock('../../chart', () => ({
|
||||
MonitoringTimeseriesContainer: () => 'MonitoringTimeseriesContainer',
|
||||
}));
|
||||
|
||||
jest.mock('../../../np_imports/ui/chrome', () => {
|
||||
return {
|
||||
getBasePath: () => '',
|
||||
};
|
||||
});
|
||||
|
||||
import { BeatsOverview } from './overview';
|
||||
|
||||
describe('Overview', () => {
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { formatMetric } from 'plugins/monitoring/lib/format_number';
|
||||
import { formatMetric } from '../../lib/format_number';
|
||||
import { SummaryStatus } from '../summary_status';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
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