From 82e0fd9b3f92464fadea0b4fff980b7686e1e38f Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 1 Apr 2020 09:29:32 -0700 Subject: [PATCH 01/17] Update ILM node attributes blacklist. (#62093) - Remove 'testattr' because this doesn't seem to be used in production by ES. - Add 'transform.node' (added via https://github.com/elastic/elasticsearch/pull/52712/files#diff-225cc2c1291b4c60a8c3412a619094e1R147). --- .../server/routes/api/nodes/constants.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/x-pack/legacy/plugins/index_lifecycle_management/server/routes/api/nodes/constants.ts b/x-pack/legacy/plugins/index_lifecycle_management/server/routes/api/nodes/constants.ts index 7a85c5e7c02d4..4392dacac8fa4 100644 --- a/x-pack/legacy/plugins/index_lifecycle_management/server/routes/api/nodes/constants.ts +++ b/x-pack/legacy/plugins/index_lifecycle_management/server/routes/api/nodes/constants.ts @@ -8,6 +8,8 @@ export const NODE_ATTRS_KEYS_TO_IGNORE: string[] = [ 'ml.enabled', 'ml.machine_memory', 'ml.max_open_jobs', - 'testattr', + // Used by ML to identify nodes that have transform enabled: + // https://github.com/elastic/elasticsearch/pull/52712/files#diff-225cc2c1291b4c60a8c3412a619094e1R147 + 'transform.node', 'xpack.installed', ]; From 12a7dd033ff6a8790f418c57dbfe1b51e05422e4 Mon Sep 17 00:00:00 2001 From: CJ Cenizal Date: Wed, 1 Apr 2020 09:36:48 -0700 Subject: [PATCH 02/17] Update CODEOWNERS with ES-UI apps, including grok debugger. (#62045) --- .github/CODEOWNERS | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f327206464090..da85fb986ae01 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -177,17 +177,23 @@ # Elasticsearch UI /src/plugins/console/ @elastic/es-ui /src/plugins/es_ui_shared/ @elastic/es-ui -/x-pack/plugins/console_extensions/ @elastic/es-ui /x-pack/legacy/plugins/cross_cluster_replication/ @elastic/es-ui /x-pack/legacy/plugins/index_lifecycle_management/ @elastic/es-ui /x-pack/legacy/plugins/index_management/ @elastic/es-ui /x-pack/legacy/plugins/license_management/ @elastic/es-ui -/x-pack/plugins/remote_clusters/ @elastic/es-ui /x-pack/legacy/plugins/rollup/ @elastic/es-ui -/x-pack/plugins/searchprofiler/ @elastic/es-ui -/x-pack/plugins/painless_lab/ @elastic/es-ui /x-pack/legacy/plugins/snapshot_restore/ @elastic/es-ui /x-pack/legacy/plugins/upgrade_assistant/ @elastic/es-ui +/x-pack/plugins/console_extensions/ @elastic/es-ui +/x-pack/plugins/es_ui_shared/ @elastic/es-ui +/x-pack/plugins/grokdebugger/ @elastic/es-ui +/x-pack/plugins/index_management/ @elastic/es-ui +/x-pack/plugins/license_management/ @elastic/es-ui +/x-pack/plugins/painless_lab/ @elastic/es-ui +/x-pack/plugins/remote_clusters/ @elastic/es-ui +/x-pack/plugins/rollup/ @elastic/es-ui +/x-pack/plugins/searchprofiler/ @elastic/es-ui +/x-pack/plugins/snapshot_restore/ @elastic/es-ui /x-pack/plugins/upgrade_assistant/ @elastic/es-ui /x-pack/plugins/watcher/ @elastic/es-ui From 1e47cb21ea944fe16697023518d4544633c2692d Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Wed, 1 Apr 2020 11:57:31 -0500 Subject: [PATCH 03/17] =?UTF-8?q?[Metrics=20Alerts]=20Fix=20action=20varia?= =?UTF-8?q?bles,=20default=20message,=20and=20EU=E2=80=A6=20(#62061)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [Metrics Alerts] Fix action variables, default message, and EUI casing * i18nize action variables --- .../alerting/metrics/alert_dropdown.tsx | 2 +- .../metrics/metric_threshold_alert_type.ts | 12 +++++- .../metric_threshold_executor.test.ts | 24 +++++++++++ .../metric_threshold_executor.ts | 15 ++++++- .../register_metric_threshold_alert_type.ts | 40 +++++++++++++++++++ 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/alert_dropdown.tsx b/x-pack/plugins/infra/public/components/alerting/metrics/alert_dropdown.tsx index 0a464d91fbe06..bb664f4067662 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/alert_dropdown.tsx +++ b/x-pack/plugins/infra/public/components/alerting/metrics/alert_dropdown.tsx @@ -38,7 +38,7 @@ export const AlertDropdown = () => { 'kibana#/management/kibana/triggersActions/alerts' )} > - + , ]; }, [kibana.services]); diff --git a/x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts b/x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts index d3b5aaa7c8796..60c4e013d79ac 100644 --- a/x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/public/components/alerting/metrics/metric_threshold_alert_type.ts @@ -15,10 +15,20 @@ export function getAlertType(): AlertTypeModel { return { id: METRIC_THRESHOLD_ALERT_TYPE_ID, name: i18n.translate('xpack.infra.metrics.alertFlyout.alertName', { - defaultMessage: 'Alert Trigger', + defaultMessage: 'Metric Threshold', }), iconClass: 'bell', alertParamsExpression: Expressions, validate: validateMetricThreshold, + defaultActionMessage: i18n.translate( + 'xpack.infra.metrics.alerting.threshold.defaultActionMessage', + { + defaultMessage: `\\{\\{alertName\\}\\} - \\{\\{context.group\\}\\} + +\\{\\{context.metricOf.condition0\\}\\} has crossed a threshold of \\{\\{context.thresholdOf.condition0\\}\\} +Current value is \\{\\{context.valueOf.condition0\\}\\} +`, + } + ), }; } diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts index feaa404ae960a..09f1702349542 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.test.ts @@ -126,6 +126,14 @@ describe('The metric threshold alert type', () => { expect(alertInstances.get(instanceID).mostRecentAction).toBe(undefined); expect(alertInstances.get(instanceID).state.alertState).toBe(AlertStates.OK); }); + test('reports expected values to the action context', async () => { + await execute(Comparator.GT, [0.75]); + const mostRecentAction = alertInstances.get(instanceID).mostRecentAction; + expect(mostRecentAction.action.group).toBe('*'); + expect(mostRecentAction.action.valueOf.condition0).toBe(1); + expect(mostRecentAction.action.thresholdOf.condition0).toStrictEqual([0.75]); + expect(mostRecentAction.action.metricOf.condition0).toBe('test.metric.1'); + }); }); describe('querying with a groupBy parameter', () => { @@ -166,6 +174,11 @@ describe('The metric threshold alert type', () => { expect(alertInstances.get(instanceIdB).mostRecentAction).toBe(undefined); expect(alertInstances.get(instanceIdB).state.alertState).toBe(AlertStates.OK); }); + test('reports group values to the action context', async () => { + await execute(Comparator.GT, [0.75]); + expect(alertInstances.get(instanceIdA).mostRecentAction.action.group).toBe('a'); + expect(alertInstances.get(instanceIdB).mostRecentAction.action.group).toBe('b'); + }); }); describe('querying with multiple criteria', () => { @@ -215,6 +228,17 @@ describe('The metric threshold alert type', () => { expect(alertInstances.get(instanceIdB).mostRecentAction).toBe(undefined); expect(alertInstances.get(instanceIdB).state.alertState).toBe(AlertStates.OK); }); + test('sends all criteria to the action context', async () => { + const instanceID = 'test-*'; + await execute(Comparator.GT_OR_EQ, [1.0], [3.0]); + const mostRecentAction = alertInstances.get(instanceID).mostRecentAction; + expect(mostRecentAction.action.valueOf.condition0).toBe(1); + expect(mostRecentAction.action.valueOf.condition1).toBe(3.5); + expect(mostRecentAction.action.thresholdOf.condition0).toStrictEqual([1.0]); + expect(mostRecentAction.action.thresholdOf.condition1).toStrictEqual([3.0]); + expect(mostRecentAction.action.metricOf.condition0).toBe('test.metric.1'); + expect(mostRecentAction.action.metricOf.condition1).toBe('test.metric.2'); + }); }); describe('querying with the count aggregator', () => { const instanceID = 'test-*'; diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts index bfe04b82b95fc..60bba61b75ef1 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/metric_threshold_executor.ts @@ -223,6 +223,17 @@ const comparatorMap = { [Comparator.LT_OR_EQ]: (a: number, [b]: number[]) => a <= b, }; +const mapToConditionsLookup = ( + list: any[], + mapFn: (value: any, index: number, array: any[]) => unknown +) => + list + .map(mapFn) + .reduce( + (result: Record, value, i) => ({ ...result, [`condition${i}`]: value }), + {} + ); + export const createMetricThresholdExecutor = (alertUUID: string) => async function({ services, params }: AlertExecutorOptions) { const { criteria, groupBy, filterQuery } = params as { @@ -261,7 +272,9 @@ export const createMetricThresholdExecutor = (alertUUID: string) => if (shouldAlertFire) { alertInstance.scheduleActions(FIRED_ACTIONS.id, { group, - value: alertResults.map(result => result[group].currentValue), + valueOf: mapToConditionsLookup(alertResults, result => result[group].currentValue), + thresholdOf: mapToConditionsLookup(criteria, criterion => criterion.threshold), + metricOf: mapToConditionsLookup(criteria, criterion => criterion.metric), }); } // Future use: ability to fetch display current alert state diff --git a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts index ed3a9b2f4fe36..57f9124db923c 100644 --- a/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts +++ b/x-pack/plugins/infra/server/lib/alerting/metric_threshold/register_metric_threshold_alert_type.ts @@ -3,6 +3,7 @@ * 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 uuid from 'uuid'; import { schema } from '@kbn/config-schema'; import { PluginSetupContract } from '../../../../../alerting/server'; @@ -49,6 +50,37 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet metric: schema.never(), }); + const groupActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.groupActionVariableDescription', + { + defaultMessage: 'Name of the group reporting data', + } + ); + + const valueOfActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.valueOfActionVariableDescription', + { + defaultMessage: + 'Record of the current value of the watched metric; grouped by condition, i.e valueOf.condition0, valueOf.condition1, etc.', + } + ); + + const thresholdOfActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.thresholdOfActionVariableDescription', + { + defaultMessage: + 'Record of the alerting threshold; grouped by condition, i.e thresholdOf.condition0, thresholdOf.condition1, etc.', + } + ); + + const metricOfActionVariableDescription = i18n.translate( + 'xpack.infra.metrics.alerting.threshold.alerting.metricOfActionVariableDescription', + { + defaultMessage: + 'Record of the watched metric; grouped by condition, i.e metricOf.condition0, metricOf.condition1, etc.', + } + ); + alertingPlugin.registerType({ id: METRIC_THRESHOLD_ALERT_TYPE_ID, name: 'Metric Alert - Threshold', @@ -62,5 +94,13 @@ export async function registerMetricThresholdAlertType(alertingPlugin: PluginSet defaultActionGroupId: FIRED_ACTIONS.id, actionGroups: [FIRED_ACTIONS], executor: createMetricThresholdExecutor(alertUUID), + actionVariables: { + context: [ + { name: 'group', description: groupActionVariableDescription }, + { name: 'valueOf', description: valueOfActionVariableDescription }, + { name: 'thresholdOf', description: thresholdOfActionVariableDescription }, + { name: 'metricOf', description: metricOfActionVariableDescription }, + ], + }, }); } From b06eaf4874b84376a504c9020675ce88ed4a1945 Mon Sep 17 00:00:00 2001 From: Aaron Caldwell Date: Wed, 1 Apr 2020 11:13:58 -0600 Subject: [PATCH 04/17] [Maps] Update ems client dependency to 7.8.0 (#62181) --- package.json | 2 +- x-pack/package.json | 2 +- yarn.lock | 11 +++++------ 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 18be64946e0a9..cdc41a2d8a3f0 100644 --- a/package.json +++ b/package.json @@ -120,7 +120,7 @@ "@elastic/apm-rum": "^4.6.0", "@elastic/charts": "^18.1.1", "@elastic/datemath": "5.0.3", - "@elastic/ems-client": "7.7.1", + "@elastic/ems-client": "7.8.0", "@elastic/eui": "21.0.1", "@elastic/filesaver": "1.1.2", "@elastic/good": "8.1.1-kibana2", diff --git a/x-pack/package.json b/x-pack/package.json index ffcb98da908a5..bbab1a96f52f4 100644 --- a/x-pack/package.json +++ b/x-pack/package.json @@ -181,7 +181,7 @@ "@babel/runtime": "^7.9.2", "@elastic/apm-rum-react": "^0.3.2", "@elastic/datemath": "5.0.3", - "@elastic/ems-client": "7.7.1", + "@elastic/ems-client": "7.8.0", "@elastic/eui": "21.0.1", "@elastic/filesaver": "1.1.2", "@elastic/maki": "6.2.0", diff --git a/yarn.lock b/yarn.lock index 9d76788079e46..2645128be08c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1232,13 +1232,12 @@ once "^1.4.0" pump "^3.0.0" -"@elastic/ems-client@7.7.1": - version "7.7.1" - resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.7.1.tgz#cda9277cb851b6d1aa0408fe2814de98f1474fb8" - integrity sha512-8ikEUbsM+wxENqi/cwrmo4+2vwZkVoFDPSIrw3bQG2mQaE3l+3w1eMPKxsvQq0r79ivzXJ51gkvr8CffBkBkGw== +"@elastic/ems-client@7.8.0": + version "7.8.0" + resolved "https://registry.yarnpkg.com/@elastic/ems-client/-/ems-client-7.8.0.tgz#8cc309bc8128c03a78e876b43bf04f4d2b4789c5" + integrity sha512-+6WjxZy/mhVWXTdSYjHd+ArsvH1s7GEummaGq9DlnCW6CyGIYkYxOISKpuytReeYhhMk7F06f/GKw+8ivi2zPQ== dependencies: lodash "^4.17.15" - node-fetch "^1.7.3" semver "^6.3.0" "@elastic/eslint-plugin-eui@0.0.2": @@ -20922,7 +20921,7 @@ node-environment-flags@1.0.6: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" -node-fetch@1.7.3, node-fetch@^1.0.1, node-fetch@^1.7.3: +node-fetch@1.7.3, node-fetch@^1.0.1: version "1.7.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ== From 2cff8b43e4dd12f2d14d80743e9fff473078403f Mon Sep 17 00:00:00 2001 From: Andrew Goldstein Date: Wed, 1 Apr 2020 11:47:59 -0600 Subject: [PATCH 05/17] [SIEM] Restores the _External alert count_ widget's subtitle (#62094) ## [SIEM] Restores the _External alert count_ widget's subtitle Fixes an issue where the _External alert count_ widget's subtitle, (e.g. `Showing: 47,642,905 external alerts`), didn't render after data is loaded ### Before ![external-alerts-before](https://user-images.githubusercontent.com/4459398/78086038-f3fe7c80-7379-11ea-8291-2ef807349aea.png) ### After ![external-alerts-after](https://user-images.githubusercontent.com/4459398/78086045-fb258a80-7379-11ea-9bc6-338dc3aba482.png) --- .../components/matrix_histogram/index.tsx | 44 +++--- .../alerts_by_category/index.test.tsx | 135 ++++++++++++++++++ .../overview/alerts_by_category/index.tsx | 8 +- 3 files changed, 158 insertions(+), 29 deletions(-) create mode 100644 x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx diff --git a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx index e3e0562424ffb..12a474009dc5b 100644 --- a/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx +++ b/x-pack/legacy/plugins/siem/public/components/matrix_histogram/index.tsx @@ -25,7 +25,6 @@ import { HistogramAggregation, MatrixHistogramQueryProps, } from './types'; -import { ChartSeriesData } from '../charts/common'; import { InspectButtonContainer } from '../inspect'; import { State, inputsSelectors, hostsModel, networkModel } from '../../store'; @@ -120,11 +119,6 @@ export const MatrixHistogramComponent: React.FC( defaultStackByOption ); - - const [titleWithStackByField, setTitle] = useState(''); - const [subtitleWithCounts, setSubtitle] = useState(''); - const [hideHistogram, setHideHistogram] = useState(hideHistogramIfEmpty); - const [barChartData, setBarChartData] = useState(null); const setSelectedChartOptionCallback = useCallback( (event: React.ChangeEvent) => { setSelectedStackByOption( @@ -146,40 +140,36 @@ export const MatrixHistogramComponent: React.FC { - if (title != null) setTitle(typeof title === 'function' ? title(selectedStackByOption) : title); - - if (subtitle != null) - setSubtitle(typeof subtitle === 'function' ? subtitle(totalCount) : subtitle); - - if (totalCount <= 0 && hideHistogramIfEmpty) { - setHideHistogram(true); - } else { - setHideHistogram(false); - } - setBarChartData(getCustomChartData(data, mapping)); + const titleWithStackByField = useMemo( + () => (title != null && typeof title === 'function' ? title(selectedStackByOption) : title), + [title, selectedStackByOption] + ); + const subtitleWithCounts = useMemo( + () => (subtitle != null && typeof subtitle === 'function' ? subtitle(totalCount) : subtitle), + [subtitle, totalCount] + ); + const hideHistogram = useMemo(() => (totalCount <= 0 && hideHistogramIfEmpty ? true : false), [ + totalCount, + hideHistogramIfEmpty, + ]); + const barChartData = useMemo(() => getCustomChartData(data, mapping), [data, mapping]); + useEffect(() => { setQuery({ id, inspect, loading, refetch }); if (isInitialLoading && !!barChartData && data) { setIsInitialLoading(false); } }, [ - subtitle, - setSubtitle, - setHideHistogram, - setBarChartData, setQuery, - hideHistogramIfEmpty, - totalCount, id, inspect, - isInspected, loading, refetch, - data, - refetch, isInitialLoading, + barChartData, + data, + setIsInitialLoading, ]); if (hideHistogram) { diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx new file mode 100644 index 0000000000000..d838b936a2d65 --- /dev/null +++ b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.test.tsx @@ -0,0 +1,135 @@ +/* + * 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. + */ + +/* eslint-disable react/display-name */ + +import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json'; +import { mount, ReactWrapper } from 'enzyme'; +import React from 'react'; +import { ThemeProvider } from 'styled-components'; + +import { useQuery } from '../../../containers/matrix_histogram'; +import { wait } from '../../../lib/helpers'; +import { mockIndexPattern, TestProviders } from '../../../mock'; + +import { AlertsByCategory } from '.'; + +jest.mock('../../../lib/kibana'); + +jest.mock('../../../containers/matrix_histogram', () => { + return { + useQuery: jest.fn(), + }; +}); + +const theme = () => ({ eui: { ...euiDarkVars, euiSizeL: '24px' }, darkMode: true }); +const from = new Date('2020-03-31T06:00:00.000Z').valueOf(); +const to = new Date('2019-03-31T06:00:00.000Z').valueOf(); + +describe('Alerts by category', () => { + let wrapper: ReactWrapper; + + describe('before loading data', () => { + beforeAll(async () => { + (useQuery as jest.Mock).mockReturnValue({ + data: null, + loading: false, + inspect: false, + totalCount: null, + }); + + wrapper = mount( + + + + + + ); + + await wait(); + wrapper.update(); + }); + + test('it renders the expected title', () => { + expect(wrapper.find('[data-test-subj="header-section-title"]').text()).toEqual( + 'External alert count' + ); + }); + + test('it does NOT render the subtitle', () => { + expect(wrapper.find('[data-test-subj="header-panel-subtitle"]').exists()).toBe(false); + }); + + test('it renders the expected filter fields', () => { + const expectedOptions = ['event.category', 'event.module']; + + expectedOptions.forEach(option => { + expect(wrapper.find(`option[value="${option}"]`).text()).toEqual(option); + }); + }); + + test('it renders the `View alerts` button', () => { + expect(wrapper.find('[data-test-subj="view-alerts"]').exists()).toBe(true); + }); + + test('it does NOT render the bar chart when data is not available', () => { + expect(wrapper.find(`.echChart`).exists()).toBe(false); + }); + }); + + describe('after loading data', () => { + beforeAll(async () => { + (useQuery as jest.Mock).mockReturnValue({ + data: [ + { x: 1, y: 2, g: 'g1' }, + { x: 2, y: 4, g: 'g1' }, + { x: 3, y: 6, g: 'g1' }, + { x: 1, y: 1, g: 'g2' }, + { x: 2, y: 3, g: 'g2' }, + { x: 3, y: 5, g: 'g2' }, + ], + loading: false, + inspect: false, + totalCount: 6, + }); + + wrapper = mount( + + + + + + ); + + await wait(); + wrapper.update(); + }); + + test('it renders the expected subtitle', () => { + expect(wrapper.find('[data-test-subj="header-panel-subtitle"]').text()).toEqual( + 'Showing: 6 external alerts' + ); + }); + + test('it renders the bar chart when data is available', () => { + expect(wrapper.find(`.echChart`).exists()).toBe(true); + }); + }); +}); diff --git a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx index e0d383c59e2ee..744102fbac4b3 100644 --- a/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/overview/alerts_by_category/index.tsx @@ -78,7 +78,11 @@ const AlertsByCategoryComponent: React.FC = ({ const urlSearch = useGetUrlSearch(navTabs.detections); const alertsCountViewAlertsButton = useMemo( - () => {i18n.VIEW_ALERTS}, + () => ( + + {i18n.VIEW_ALERTS} + + ), [urlSearch] ); @@ -87,7 +91,7 @@ const AlertsByCategoryComponent: React.FC = ({ ...histogramConfigs, defaultStackByOption: alertsStackByOptions.find(o => o.text === DEFAULT_STACK_BY) ?? alertsStackByOptions[0], - getSubtitle: (totalCount: number) => + subtitle: (totalCount: number) => `${SHOWING}: ${numeral(totalCount).format(defaultNumberFormat)} ${UNIT(totalCount)}`, legendPosition: Position.Right, }), From 2cdb9d31135fc298d52a50b428a9256b2d57fa5c Mon Sep 17 00:00:00 2001 From: Oliver Gupte Date: Wed, 1 Apr 2020 11:29:58 -0700 Subject: [PATCH 06/17] Closes #60173 by turning off client caching for the main service map API call (#62111) --- .../components/app/ServiceMap/index.tsx | 1 + .../ServicePage/ServicePage.tsx | 2 +- .../public/hooks/useDynamicIndexPattern.ts | 2 +- .../public/services/__test__/callApi.test.ts | 40 +++++++++++++++++++ .../apm/public/services/rest/callApi.ts | 6 +-- 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx index 351e039ca45df..94e42f1b91160 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/ServiceMap/index.tsx @@ -42,6 +42,7 @@ export function ServiceMap({ serviceName }: ServiceMapProps) { const { start, end, environment } = urlParams; if (start && end) { return callApmApi({ + isCachable: false, pathname: '/api/apm/service-map', params: { query: { diff --git a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx index b9f8fd86d067b..43002c79aa2b4 100644 --- a/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx +++ b/x-pack/legacy/plugins/apm/public/components/app/Settings/AgentConfigurations/AgentConfigurationCreateEdit/ServicePage/ServicePage.tsx @@ -36,7 +36,7 @@ export function ServicePage({ newConfig, setNewConfig, onClickNext }: Props) { callApmApi => { return callApmApi({ pathname: '/api/apm/settings/agent-configuration/services', - forceCache: true + isCachable: true }); }, [], diff --git a/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts b/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts index 747144690bb24..ee3d2e81f259f 100644 --- a/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts +++ b/x-pack/legacy/plugins/apm/public/hooks/useDynamicIndexPattern.ts @@ -14,7 +14,7 @@ export function useDynamicIndexPattern( callApmApi => { return callApmApi({ pathname: '/api/apm/index_pattern/dynamic', - forceCache: true, + isCachable: true, params: { query: { processorEvent diff --git a/x-pack/legacy/plugins/apm/public/services/__test__/callApi.test.ts b/x-pack/legacy/plugins/apm/public/services/__test__/callApi.test.ts index 95ebed1fcb2a6..5da237b637abc 100644 --- a/x-pack/legacy/plugins/apm/public/services/__test__/callApi.test.ts +++ b/x-pack/legacy/plugins/apm/public/services/__test__/callApi.test.ts @@ -157,6 +157,46 @@ describe('callApi', () => { expect(http.get).toHaveBeenCalledTimes(1); }); + + it('should not return cached response with `isCachable: false` option', async () => { + await callApi(http, { + isCachable: false, + pathname: `/api/kibana`, + query: { start: '2010', end: '2011' } + }); + await callApi(http, { + isCachable: false, + pathname: `/api/kibana`, + query: { start: '2010', end: '2011' } + }); + await callApi(http, { + isCachable: false, + pathname: `/api/kibana`, + query: { start: '2010', end: '2011' } + }); + + expect(http.get).toHaveBeenCalledTimes(3); + }); + + it('should return cached response with `isCachable: true` option', async () => { + await callApi(http, { + isCachable: true, + pathname: `/api/kibana`, + query: { end: '2030' } + }); + await callApi(http, { + isCachable: true, + pathname: `/api/kibana`, + query: { end: '2030' } + }); + await callApi(http, { + isCachable: true, + pathname: `/api/kibana`, + query: { end: '2030' } + }); + + expect(http.get).toHaveBeenCalledTimes(1); + }); }); }); }); diff --git a/x-pack/legacy/plugins/apm/public/services/rest/callApi.ts b/x-pack/legacy/plugins/apm/public/services/rest/callApi.ts index 43ecb860a1f1a..d8e3d27f042a9 100644 --- a/x-pack/legacy/plugins/apm/public/services/rest/callApi.ts +++ b/x-pack/legacy/plugins/apm/public/services/rest/callApi.ts @@ -11,7 +11,7 @@ import { HttpSetup, HttpFetchOptions } from 'kibana/public'; export type FetchOptions = Omit & { pathname: string; - forceCache?: boolean; + isCachable?: boolean; method?: string; body?: any; }; @@ -74,8 +74,8 @@ export async function callApi( // only cache items that has a time range with `start` and `end` params, // and where `end` is not a timestamp in the future function isCachable(fetchOptions: FetchOptions) { - if (fetchOptions.forceCache) { - return true; + if (fetchOptions.isCachable !== undefined) { + return fetchOptions.isCachable; } if ( From 3fdddea28678bf8ee24c6d60edd9c8f8246ebe8d Mon Sep 17 00:00:00 2001 From: Robert Austin Date: Wed, 1 Apr 2020 14:51:15 -0400 Subject: [PATCH 07/17] Revert "Endpoint: Add ts-node dev dependency (#61884)" (#62197) This reverts commit 4899aaf5658970ceca982975b079a37d20555985. --- x-pack/plugins/endpoint/package.json | 3 +-- x-pack/plugins/endpoint/scripts/README.md | 6 +++++- yarn.lock | 26 ----------------------- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/x-pack/plugins/endpoint/package.json b/x-pack/plugins/endpoint/package.json index 9e65f23a38860..fc4f4bd586bef 100644 --- a/x-pack/plugins/endpoint/package.json +++ b/x-pack/plugins/endpoint/package.json @@ -13,7 +13,6 @@ "devDependencies": { "@types/seedrandom": ">=2.0.0 <4.0.0", "@types/react-redux": "^7.1.0", - "redux-devtools-extension": "^2.13.8", - "ts-node": "^8.8.1" + "redux-devtools-extension": "^2.13.8" } } diff --git a/x-pack/plugins/endpoint/scripts/README.md b/x-pack/plugins/endpoint/scripts/README.md index 34d0a1ecd8ede..f0c8c5a9b0b66 100644 --- a/x-pack/plugins/endpoint/scripts/README.md +++ b/x-pack/plugins/endpoint/scripts/README.md @@ -3,7 +3,11 @@ The default behavior is to create 1 endpoint with 1 alert and a moderate number A seed value can be provided as a string for the random number generator for repeatable behavior, useful for demos etc. Use the `-d` option if you want to delete and remake the indices, otherwise it will add documents to existing indices. -Example command sequence to get ES and kibana running with sample data: +The sample data generator script depends on ts-node, install with npm: + +```npm install -g ts-node``` + +Example command sequence to get ES and kibana running with sample data after installing ts-node: ```yarn es snapshot``` -> starts ES diff --git a/yarn.lock b/yarn.lock index 2645128be08c0..a6b3154c89d05 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6127,11 +6127,6 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -19621,11 +19616,6 @@ make-dir@^3.0.2: dependencies: semver "^6.0.0" -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - make-iterator@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.0.tgz#57bef5dc85d23923ba23767324d8e8f8f3d9694b" @@ -28902,17 +28892,6 @@ ts-log@2.1.4: resolved "https://registry.yarnpkg.com/ts-log/-/ts-log-2.1.4.tgz#063c5ad1cbab5d49d258d18015963489fb6fb59a" integrity sha512-P1EJSoyV+N3bR/IWFeAqXzKPZwHpnLY6j7j58mAvewHRipo+BQM2Y1f9Y9BjEQznKwgqqZm7H8iuixmssU7tYQ== -ts-node@^8.8.1: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.8.1.tgz#7c4d3e9ed33aa703b64b28d7f9d194768be5064d" - integrity sha512-10DE9ONho06QORKAaCBpPiFCdW+tZJuY/84tyypGtl6r+/C7Asq0dhqbRZURuUlLQtZxxDvT8eoj8cGW0ha6Bg== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.6" - yn "3.1.1" - ts-pnp@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.1.4.tgz#ae27126960ebaefb874c6d7fa4729729ab200d90" @@ -32078,11 +32057,6 @@ yeoman-generator@1.1.1: user-home "^2.0.0" yeoman-environment "^1.1.0" -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - yo@2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/yo/-/yo-2.0.6.tgz#7b562f68a0434237c24a1fd3982f235035839516" From b1a39ce23cdf819011507ba3dfe696973097b415 Mon Sep 17 00:00:00 2001 From: Xavier Mouligneau <189600+XavierM@users.noreply.github.com> Date: Wed, 1 Apr 2020 16:33:51 -0400 Subject: [PATCH 08/17] [SIEM] CASES Bugs BC2 (#62170) * fix persistence between filter * Fix API filtering bug * Show username if full name is empty * fix user in avatar * do not allow push to service now when connector is none * fix types * Show errors from actions * update connector name in configure Co-authored-by: Christos Nasikas --- .../siem/public/containers/case/api.ts | 7 ++++++ .../case/configure/use_configure.tsx | 23 +++++++++++-------- .../public/containers/case/translations.ts | 7 ++++++ .../public/containers/case/use_get_cases.tsx | 15 +++++++----- .../case/components/all_cases/columns.tsx | 2 +- .../pages/case/components/all_cases/index.tsx | 6 ++--- .../components/use_push_to_service/index.tsx | 6 ++++- .../user_action_tree/user_action_item.tsx | 4 ++-- .../server/routes/api/cases/find_cases.ts | 5 ++-- 9 files changed, 51 insertions(+), 24 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/containers/case/api.ts b/x-pack/legacy/plugins/siem/public/containers/case/api.ts index bd243d0ba5f64..69e1602b3d981 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/api.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/api.ts @@ -47,6 +47,8 @@ import { decodeServiceConnectorCaseResponse, } from './utils'; +import * as i18n from './translations'; + export const getCase = async ( caseId: string, includeComments: boolean = true, @@ -240,6 +242,11 @@ export const pushToService = async ( signal, } ); + + if (response.status === 'error') { + throw new Error(response.serviceMessage ?? response.message ?? i18n.ERROR_PUSH_TO_SERVICE); + } + return decodeServiceConnectorCaseResponse(response.data); }; diff --git a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx index 6524c40a8e6e4..19d80bba1e0f8 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/configure/use_configure.tsx @@ -21,7 +21,11 @@ interface PersistCaseConfigure { export interface ReturnUseCaseConfigure { loading: boolean; refetchCaseConfigure: () => void; - persistCaseConfigure: ({ connectorId, closureType }: PersistCaseConfigure) => unknown; + persistCaseConfigure: ({ + connectorId, + connectorName, + closureType, + }: PersistCaseConfigure) => unknown; persistLoading: boolean; } @@ -97,19 +101,20 @@ export const useCaseConfigure = ({ const saveCaseConfiguration = async () => { try { setPersistLoading(true); + const connectorObj = { + connector_id: connectorId, + connector_name: connectorName, + closure_type: closureType, + }; const res = version.length === 0 - ? await postCaseConfigure( + ? await postCaseConfigure(connectorObj, abortCtrl.signal) + : await patchCaseConfigure( { - connector_id: connectorId, - connector_name: connectorName, - closure_type: closureType, + ...connectorObj, + version, }, abortCtrl.signal - ) - : await patchCaseConfigure( - { connector_id: connectorId, closure_type: closureType, version }, - abortCtrl.signal ); if (!didCancel) { setPersistLoading(false); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/translations.ts b/x-pack/legacy/plugins/siem/public/containers/case/translations.ts index a453be32480e2..d5ea287fd2cdd 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/translations.ts +++ b/x-pack/legacy/plugins/siem/public/containers/case/translations.ts @@ -63,3 +63,10 @@ export const SUCCESS_SEND_TO_EXTERNAL_SERVICE = i18n.translate( defaultMessage: 'Successfully sent to ServiceNow', } ); + +export const ERROR_PUSH_TO_SERVICE = i18n.translate( + 'xpack.siem.case.configure.errorPushingToService', + { + defaultMessage: 'Error pushing to service', + } +); diff --git a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx index 323dc23e1b24e..1cbce5af6304b 100644 --- a/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx +++ b/x-pack/legacy/plugins/siem/public/containers/case/use_get_cases.tsx @@ -35,7 +35,7 @@ export type Action = } | { type: 'FETCH_FAILURE'; payload: string } | { type: 'FETCH_UPDATE_CASE_SUCCESS' } - | { type: 'UPDATE_FILTER_OPTIONS'; payload: FilterOptions } + | { type: 'UPDATE_FILTER_OPTIONS'; payload: Partial } | { type: 'UPDATE_QUERY_PARAMS'; payload: Partial } | { type: 'UPDATE_TABLE_SELECTIONS'; payload: Case[] }; @@ -68,7 +68,10 @@ const dataFetchReducer = (state: UseGetCasesState, action: Action): UseGetCasesS case 'UPDATE_FILTER_OPTIONS': return { ...state, - filterOptions: action.payload, + filterOptions: { + ...state.filterOptions, + ...action.payload, + }, }; case 'UPDATE_QUERY_PARAMS': return { @@ -119,8 +122,8 @@ interface UseGetCases extends UseGetCasesState { refetchCasesStatus, }: UpdateCase) => void; refetchCases: (filters: FilterOptions, queryParams: QueryParams) => void; - setFilters: (filters: FilterOptions) => void; - setQueryParams: (queryParams: QueryParams) => void; + setFilters: (filters: Partial) => void; + setQueryParams: (queryParams: Partial) => void; setSelectedCases: (mySelectedCases: Case[]) => void; } @@ -139,11 +142,11 @@ export const useGetCases = (initialQueryParams?: QueryParams): UseGetCases => { dispatch({ type: 'UPDATE_TABLE_SELECTIONS', payload: mySelectedCases }); }, []); - const setQueryParams = useCallback((newQueryParams: QueryParams) => { + const setQueryParams = useCallback((newQueryParams: Partial) => { dispatch({ type: 'UPDATE_QUERY_PARAMS', payload: newQueryParams }); }, []); - const setFilters = useCallback((newFilters: FilterOptions) => { + const setFilters = useCallback((newFilters: Partial) => { dispatch({ type: 'UPDATE_FILTER_OPTIONS', payload: newFilters }); }, []); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx index f757fd33a93a8..0e12f78e29bc2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/columns.tsx @@ -77,7 +77,7 @@ export const getCasesColumns = ( size="s" /> - {createdBy.fullName ?? createdBy.username ?? ''} + {createdBy.fullName ? createdBy.fullName : createdBy.username ?? ''} ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx index 161910bb5498a..b0ff3dbada6c9 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/all_cases/index.tsx @@ -254,11 +254,11 @@ export const AllCases = React.memo(({ userCanCrud }) => { const onFilterChangedCallback = useCallback( (newFilterOptions: Partial) => { if (newFilterOptions.status && newFilterOptions.status === 'closed') { - setQueryParams({ ...queryParams, sortField: SortFieldCase.closedAt }); + setQueryParams({ sortField: SortFieldCase.closedAt }); } else if (newFilterOptions.status && newFilterOptions.status === 'open') { - setQueryParams({ ...queryParams, sortField: SortFieldCase.createdAt }); + setQueryParams({ sortField: SortFieldCase.createdAt }); } - setFilters({ ...filterOptions, ...newFilterOptions }); + setFilters(newFilterOptions); }, [filterOptions, queryParams] ); diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx index aeb694e52b7fa..4f370ec978906 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/use_push_to_service/index.tsx @@ -74,7 +74,11 @@ export const usePushToService = ({ if (actionLicense != null && !actionLicense.enabledInLicense) { errors = [...errors, getLicenseError()]; } - if (connector == null && !loadingCaseConfigure && !loadingLicense) { + if ( + (connector == null || (connector != null && connector.connectorId === 'none')) && + !loadingCaseConfigure && + !loadingLicense + ) { errors = [ ...errors, { diff --git a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx index 89b94d98f91db..bcb4edd6129a6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/case/components/user_action_tree/user_action_item.tsx @@ -134,8 +134,8 @@ export const UserActionItem = ({ - {(fullName && fullName.length > 0) || username.length > 0 ? ( - + {(fullName && fullName.length > 0) || (username && username.length > 0) ? ( + 0 ? fullName : username ?? ''} /> ) : ( )} diff --git a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts index e7b2044f2badf..b2716749e9749 100644 --- a/x-pack/plugins/case/server/routes/api/cases/find_cases.ts +++ b/x-pack/plugins/case/server/routes/api/cases/find_cases.ts @@ -31,9 +31,10 @@ const buildFilter = ( ): string => filters != null && filters.length > 0 ? Array.isArray(filters) - ? filters + ? // Be aware of the surrounding parenthesis (as string inside literal) around filters. + `(${filters .map(filter => `${CASE_SAVED_OBJECT}.attributes.${field}: ${filter}`) - ?.join(` ${operator} `) + ?.join(` ${operator} `)})` : `${CASE_SAVED_OBJECT}.attributes.${field}: ${filters}` : ''; From cb914d408ebc20a3af267a4d4e52939cb40b4983 Mon Sep 17 00:00:00 2001 From: "Devin W. Hurley" Date: Wed, 1 Apr 2020 16:45:16 -0400 Subject: [PATCH 09/17] [SIEM] [Detection Engine] Fixes all rules sorting (#62039) * fixes sorting to what it was in 7.6.1 * removes sortable=true from non-sortable columns, fixes naming of sortable column field from activate to enabled to match the field eui expects to sort on, fixes react render warning due to resetting tableRef's current field during a render --- .../public/pages/detection_engine/rules/all/columns.tsx | 4 +--- .../public/pages/detection_engine/rules/all/index.tsx | 9 ++++----- .../public/pages/detection_engine/rules/all/reducer.ts | 5 ++++- .../rules/components/all_rules_tables/index.tsx | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx index a155f3eb2803c..5157bd81403e2 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/columns.tsx @@ -144,7 +144,6 @@ export const getColumns = ({ ); }, - sortable: true, truncateText: true, width: '20%', }, @@ -180,7 +179,7 @@ export const getColumns = ({ }, { align: 'center', - field: 'activate', + field: 'enabled', name: i18n.COLUMN_ACTIVATE, render: (value: Rule['enabled'], item: Rule) => ( { ); }, - sortable: true, truncateText: true, width: '20%', }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx index 4003b71b95d77..1a98272546440 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/index.tsx @@ -41,11 +41,12 @@ import { showRulesTable } from './helpers'; import { allRulesReducer, State } from './reducer'; import { RulesTableFilters } from './rules_table_filters/rules_table_filters'; +const SORT_FIELD = 'enabled'; const initialState: State = { exportRuleIds: [], filterOptions: { filter: '', - sortField: 'enabled', + sortField: SORT_FIELD, sortOrder: 'desc', }, loadingRuleIds: [], @@ -127,9 +128,7 @@ export const AllRules = React.memo( }); const sorting = useMemo( - () => ({ - sort: { field: 'enabled', direction: filterOptions.sortOrder }, - }), + () => ({ sort: { field: 'enabled', direction: filterOptions.sortOrder } }), [filterOptions.sortOrder] ); @@ -171,7 +170,7 @@ export const AllRules = React.memo( dispatch({ type: 'updateFilterOptions', filterOptions: { - sortField: 'enabled', // Only enabled is supported for sorting currently + sortField: SORT_FIELD, // Only enabled is supported for sorting currently sortOrder: sort?.direction ?? 'desc', }, pagination: { page: page.index + 1, perPage: page.size }, diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts index 0a4d169d13154..bc5297e7628b7 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/all/reducer.ts @@ -66,7 +66,10 @@ export const allRulesReducer = ( tableRef.current != null && tableRef.current.changeSelection != null ) { - tableRef.current.changeSelection([]); + // for future devs: eui basic table is not giving us a prop to set the value, so + // we are using the ref in setTimeout to reset on the next loop so that we + // do not get a warning telling us we are trying to update during a render + window.setTimeout(() => tableRef?.current?.changeSelection([]), 0); } return { diff --git a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx index 92ccbc864ab5a..0fd07f30a00b6 100644 --- a/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx +++ b/x-pack/legacy/plugins/siem/public/pages/detection_engine/rules/components/all_rules_tables/index.tsx @@ -97,7 +97,7 @@ const AllRulesTablesComponent: React.FC = ({ onChange={tableOnChangeCallback} pagination={paginationMemo} ref={tableRef} - {...sorting} + sorting={sorting} selection={hasNoPermissions ? undefined : euiBasicTableSelectionProps} /> )} @@ -111,7 +111,7 @@ const AllRulesTablesComponent: React.FC = ({ noItemsMessage={emptyPrompt} onChange={tableOnChangeCallback} pagination={paginationMemo} - {...sorting} + sorting={sorting} /> )} From a6906690aa23ac4cd63bd3163f06bd368b44ed8c Mon Sep 17 00:00:00 2001 From: spalger Date: Wed, 1 Apr 2020 15:03:43 -0700 Subject: [PATCH 10/17] rename README.md to readme, avoiding issues with case change --- packages/elastic-datemath/{README.md => readme} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/elastic-datemath/{README.md => readme} (100%) diff --git a/packages/elastic-datemath/README.md b/packages/elastic-datemath/readme similarity index 100% rename from packages/elastic-datemath/README.md rename to packages/elastic-datemath/readme From 07be0660409f66b63e47507d9a273e48f51314f8 Mon Sep 17 00:00:00 2001 From: Rashmi Kulkarni Date: Wed, 1 Apr 2020 15:20:47 -0700 Subject: [PATCH 11/17] accessibility tests for dashboard panel ( OSS) (#62055) * accessibility tests for dashboard panel * added back the skipped test as it is still required to pass through the a11ySnapshot Co-authored-by: Elastic Machine --- test/accessibility/apps/dashboard_panel.ts | 79 ++++++++++++++++++++++ test/accessibility/config.ts | 1 + 2 files changed, 80 insertions(+) create mode 100644 test/accessibility/apps/dashboard_panel.ts diff --git a/test/accessibility/apps/dashboard_panel.ts b/test/accessibility/apps/dashboard_panel.ts new file mode 100644 index 0000000000000..04821a50df618 --- /dev/null +++ b/test/accessibility/apps/dashboard_panel.ts @@ -0,0 +1,79 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { FtrProviderContext } from '../ftr_provider_context'; + +export default function({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects(['common', 'dashboard', 'header', 'home', 'settings']); + const a11y = getService('a11y'); + const dashboardPanelActions = getService('dashboardPanelActions'); + const testSubjects = getService('testSubjects'); + const inspector = getService('inspector'); + + describe('Dashboard Panel', () => { + before(async () => { + await PageObjects.common.navigateToUrl('home', 'tutorial_directory/sampleData'); + await PageObjects.home.addSampleDataSet('flights'); + await PageObjects.common.navigateToApp('dashboard'); + await testSubjects.click('dashboardListingTitleLink-[Flights]-Global-Flight-Dashboard'); + }); + + it('dashboard panel open ', async () => { + const header = await dashboardPanelActions.getPanelHeading('[Flights] Airline Carrier'); + await dashboardPanelActions.toggleContextMenu(header); + await a11y.testAppSnapshot(); + // doing this again will close the Context Menu, so that next snapshot can start clean. + await dashboardPanelActions.toggleContextMenu(header); + }); + + it('dashboard panel inspect', async () => { + await dashboardPanelActions.openInspectorByTitle('[Flights] Airline Carrier'); + await a11y.testAppSnapshot(); + }); + + it('dashboard panel inspector view chooser ', async () => { + await testSubjects.click('inspectorViewChooser'); + await a11y.testAppSnapshot(); + await testSubjects.click('inspectorViewChooser'); + }); + + it('dashboard panel inspector request statistics ', async () => { + await inspector.openInspectorRequestsView(); + await a11y.testAppSnapshot(); + }); + + it('dashboard panel inspector request', async () => { + await testSubjects.click('inspectorRequestDetailRequest'); + await a11y.testAppSnapshot(); + }); + + it('dashboard panel inspector response', async () => { + await testSubjects.click('inspectorRequestDetailResponse'); + await a11y.testAppSnapshot(); + await inspector.close(); + }); + + it('dashboard panel full screen', async () => { + const header = await dashboardPanelActions.getPanelHeading('[Flights] Airline Carrier'); + await dashboardPanelActions.toggleContextMenu(header); + await testSubjects.click('embeddablePanelAction-togglePanel'); + await a11y.testAppSnapshot(); + }); + }); +} diff --git a/test/accessibility/config.ts b/test/accessibility/config.ts index dd8c59c1be835..bf71fd81aa199 100644 --- a/test/accessibility/config.ts +++ b/test/accessibility/config.ts @@ -30,6 +30,7 @@ export default async function({ readConfigFile }: FtrConfigProviderContext) { testFiles: [ require.resolve('./apps/discover'), require.resolve('./apps/dashboard'), + require.resolve('./apps/dashboard_panel'), require.resolve('./apps/visualize'), require.resolve('./apps/management'), require.resolve('./apps/console'), From 325f8e0ad55ef006d1ca1b0b393d0b35947a697a Mon Sep 17 00:00:00 2001 From: Lukas Olson Date: Wed, 1 Apr 2020 15:45:42 -0700 Subject: [PATCH 12/17] Remove polling delay (#62099) --- .../plugins/data_enhanced/public/search/es_search_strategy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts index 70bdcdfd3cf1f..c493e8ce86781 100644 --- a/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts +++ b/x-pack/plugins/data_enhanced/public/search/es_search_strategy.ts @@ -33,7 +33,7 @@ export const enhancedEsSearchStrategyProvider: TSearchStrategyProvider Date: Wed, 1 Apr 2020 20:21:18 -0600 Subject: [PATCH 13/17] [Maps] Separate layer wizards for Clusters and heatmap (#60870) * [Maps] source registry and register seperate clusters and heat map sources * split into to registries * add EMS file source * add geojson upload layer * register rest of sources * i18n changes * ts lint errors * fix jest test * fix pew-pew source * review feedback * import registires in plugin so they exist in embeddable * remove order parameter and move all layer registies into single file * fix functionalt est * pass constructor to sourceREgistry instead of factory * review feedback Co-authored-by: Elastic Machine --- .../layer_addpanel/import_editor/view.js | 7 +- .../layer_addpanel/source_editor/view.js | 20 +- .../source_select/source_select.js | 25 +-- .../layer_addpanel/view.js | 20 +- .../public/layers/layer_wizard_registry.ts | 30 +++ .../maps/public/layers/load_layer_wizards.js | 30 +++ .../maps/public/layers/sources/all_sources.js | 29 --- .../client_file_source/geojson_file_source.js | 177 +++++++++--------- .../sources/client_file_source/index.js | 2 +- .../ems_file_source/ems_file_source.js | 44 +++-- .../layers/sources/ems_file_source/index.js | 2 +- .../sources/ems_tms_source/ems_tms_source.js | 46 +++-- .../layers/sources/ems_tms_source/index.js | 2 +- .../create_source_editor.js | 57 +----- .../es_geo_grid_source/es_geo_grid_source.js | 97 +++++++--- .../sources/es_geo_grid_source/index.js | 6 +- .../es_geo_grid_source/render_as_select.tsx | 59 ++++++ .../es_pew_pew_source/es_pew_pew_source.js | 55 +++--- .../es_search_source/es_search_source.js | 63 ++++--- .../layers/sources/es_search_source/index.js | 2 +- .../maps/public/layers/sources/es_source.js | 2 - .../sources/kibana_regionmap_source/index.js | 2 +- .../kibana_regionmap_source.js | 50 +++-- .../sources/kibana_tilemap_source/index.js | 2 +- .../kibana_tilemap_source.js | 48 +++-- .../public/layers/sources/source_registry.ts | 35 ++++ .../public/layers/sources/wms_source/index.js | 2 +- .../layers/sources/wms_source/wms_source.js | 57 +++--- .../public/layers/sources/xyz_tms_source.js | 44 +++-- x-pack/legacy/plugins/maps/public/plugin.ts | 4 + .../maps/public/selectors/map_selectors.js | 10 +- .../public/selectors/map_selectors.test.js | 1 - x-pack/plugins/maps/common/constants.ts | 3 + .../translations/translations/ja-JP.json | 5 - .../translations/translations/zh-CN.json | 5 - .../test/functional/page_objects/gis_page.js | 2 +- 36 files changed, 629 insertions(+), 416 deletions(-) create mode 100644 x-pack/legacy/plugins/maps/public/layers/layer_wizard_registry.ts create mode 100644 x-pack/legacy/plugins/maps/public/layers/load_layer_wizards.js delete mode 100644 x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js create mode 100644 x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx create mode 100644 x-pack/legacy/plugins/maps/public/layers/sources/source_registry.ts diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js index e9ef38e17b188..762409b256286 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/import_editor/view.js @@ -5,13 +5,12 @@ */ import React, { Fragment } from 'react'; -import { GeojsonFileSource } from '../../../layers/sources/client_file_source'; import { EuiSpacer, EuiPanel, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; +import { uploadLayerWizardConfig } from '../../../layers/sources/client_file_source'; export const ImportEditor = ({ clearSource, isIndexingTriggered, ...props }) => { const editorProperties = getEditorProperties({ isIndexingTriggered, ...props }); - const editor = GeojsonFileSource.renderEditor(editorProperties); return ( {isIndexingTriggered ? null : ( @@ -25,7 +24,9 @@ export const ImportEditor = ({ clearSource, isIndexingTriggered, ...props }) => )} - {editor} + + {uploadLayerWizardConfig.renderWizard(editorProperties)} + ); }; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js index 45c508e0d5889..50312b68277fa 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_editor/view.js @@ -5,28 +5,20 @@ */ import React, { Fragment } from 'react'; -import { ALL_SOURCES } from '../../../layers/sources/all_sources'; import { EuiSpacer, EuiPanel, EuiButtonEmpty } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; export const SourceEditor = ({ clearSource, - sourceType, + layerWizard, isIndexingTriggered, inspectorAdapters, previewLayer, }) => { - const editorProperties = { - onPreviewSource: previewLayer, - inspectorAdapters, - }; - const Source = ALL_SOURCES.find(Source => { - return Source.type === sourceType; - }); - if (!Source) { - throw new Error(`Unexpected source type: ${sourceType}`); + if (!layerWizard) { + return null; } - const editor = Source.renderEditor(editorProperties); + return ( {isIndexingTriggered ? null : ( @@ -40,7 +32,9 @@ export const SourceEditor = ({ )} - {editor} + + {layerWizard.renderWizard({ onPreviewSource: previewLayer, inspectorAdapters })} + ); }; diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js index 574a57b1041a0..b34a432bec88c 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/source_select/source_select.js @@ -5,30 +5,33 @@ */ import React, { Fragment } from 'react'; -import { ALL_SOURCES } from '../../../layers/sources/all_sources'; +import { getLayerWizards } from '../../../layers/layer_wizard_registry'; import { EuiTitle, EuiSpacer, EuiCard, EuiIcon } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; import _ from 'lodash'; export function SourceSelect({ updateSourceSelection }) { - const sourceCards = ALL_SOURCES.map(Source => { - const icon = Source.icon ? : null; + const sourceCards = getLayerWizards().map(layerWizard => { + const icon = layerWizard.icon ? : null; - const sourceTitle = Source.title; + const onClick = () => { + updateSourceSelection({ + layerWizard: layerWizard, + isIndexingSource: !!layerWizard.isIndexingSource, + }); + }; return ( - + - updateSourceSelection({ type: Source.type, isIndexingSource: Source.isIndexingSource }) - } - description={Source.description} + onClick={onClick} + description={layerWizard.description} layout="horizontal" - data-test-subj={_.camelCase(Source.title)} + data-test-subj={_.camelCase(layerWizard.title)} /> ); diff --git a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js index 425cc1cae3649..a54df69471aa0 100644 --- a/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js +++ b/x-pack/legacy/plugins/maps/public/connected_components/layer_addpanel/view.js @@ -14,7 +14,7 @@ import { i18n } from '@kbn/i18n'; export class AddLayerPanel extends Component { state = { - sourceType: null, + layerWizard: null, layer: null, importView: false, layerImportAddReady: false, @@ -35,9 +35,9 @@ export class AddLayerPanel extends Component { } _getPanelDescription() { - const { sourceType, importView, layerImportAddReady } = this.state; + const { layerWizard, importView, layerImportAddReady } = this.state; let panelDescription; - if (!sourceType) { + if (!layerWizard) { panelDescription = i18n.translate('xpack.maps.addLayerPanel.selectSource', { defaultMessage: 'Select source', }); @@ -85,13 +85,13 @@ export class AddLayerPanel extends Component { this.setState({ layer: null, - ...(!keepSourceType ? { sourceType: null, importView: false } : {}), + ...(!keepSourceType ? { layerWizard: null, importView: false } : {}), }); this.props.removeTransientLayer(); }; - _onSourceSelectionChange = ({ type, isIndexingSource }) => { - this.setState({ sourceType: type, importView: isIndexingSource }); + _onSourceSelectionChange = ({ layerWizard, isIndexingSource }) => { + this.setState({ layerWizard, importView: isIndexingSource }); }; _layerAddHandler = () => { @@ -118,8 +118,8 @@ export class AddLayerPanel extends Component { }; _renderAddLayerPanel() { - const { sourceType, importView } = this.state; - if (!sourceType) { + const { layerWizard, importView } = this.state; + if (!layerWizard) { return ; } if (importView) { @@ -134,7 +134,7 @@ export class AddLayerPanel extends Component { return ( ); @@ -148,7 +148,7 @@ export class AddLayerPanel extends Component { return ( void; + inspectorAdapters: unknown; + }): unknown; + title: string; +}; + +const registry: LayerWizard[] = []; + +export function registerLayerWizard(layerWizard: LayerWizard) { + registry.push(layerWizard); +} + +export function getLayerWizards(): LayerWizard[] { + return [...registry]; +} diff --git a/x-pack/legacy/plugins/maps/public/layers/load_layer_wizards.js b/x-pack/legacy/plugins/maps/public/layers/load_layer_wizards.js new file mode 100644 index 0000000000000..d0169165eaa35 --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/load_layer_wizards.js @@ -0,0 +1,30 @@ +/* + * 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 { registerLayerWizard } from './layer_wizard_registry'; +import { uploadLayerWizardConfig } from './sources/client_file_source'; +import { esDocumentsLayerWizardConfig } from './sources/es_search_source'; +import { clustersLayerWizardConfig, heatmapLayerWizardConfig } from './sources/es_geo_grid_source'; +import { point2PointLayerWizardConfig } from './sources/es_pew_pew_source/es_pew_pew_source'; +import { emsBoundariesLayerWizardConfig } from './sources/ems_file_source'; +import { emsBaseMapLayerWizardConfig } from './sources/ems_tms_source'; +import { kibanaRegionMapLayerWizardConfig } from './sources/kibana_regionmap_source'; +import { kibanaBasemapLayerWizardConfig } from './sources/kibana_tilemap_source'; +import { tmsLayerWizardConfig } from './sources/xyz_tms_source'; +import { wmsLayerWizardConfig } from './sources/wms_source'; + +// Registration order determines display order +registerLayerWizard(uploadLayerWizardConfig); +registerLayerWizard(esDocumentsLayerWizardConfig); +registerLayerWizard(clustersLayerWizardConfig); +registerLayerWizard(heatmapLayerWizardConfig); +registerLayerWizard(point2PointLayerWizardConfig); +registerLayerWizard(emsBoundariesLayerWizardConfig); +registerLayerWizard(emsBaseMapLayerWizardConfig); +registerLayerWizard(kibanaRegionMapLayerWizardConfig); +registerLayerWizard(kibanaBasemapLayerWizardConfig); +registerLayerWizard(tmsLayerWizardConfig); +registerLayerWizard(wmsLayerWizardConfig); diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js b/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js deleted file mode 100644 index 6a518609dd77f..0000000000000 --- a/x-pack/legacy/plugins/maps/public/layers/sources/all_sources.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { EMSFileSource } from './ems_file_source'; -import { GeojsonFileSource } from './client_file_source'; -import { KibanaRegionmapSource } from './kibana_regionmap_source'; -import { XYZTMSSource } from './xyz_tms_source'; -import { EMSTMSSource } from './ems_tms_source'; -import { WMSSource } from './wms_source'; -import { KibanaTilemapSource } from './kibana_tilemap_source'; -import { ESGeoGridSource } from './es_geo_grid_source'; -import { ESSearchSource } from './es_search_source'; -import { ESPewPewSource } from './es_pew_pew_source/es_pew_pew_source'; - -export const ALL_SOURCES = [ - GeojsonFileSource, - ESSearchSource, - ESGeoGridSource, - ESPewPewSource, - EMSFileSource, - EMSTMSSource, - KibanaRegionmapSource, - KibanaTilemapSource, - XYZTMSSource, - WMSSource, -]; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js index a38669fcd1d1a..1003f8329da22 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/geojson_file_source.js @@ -16,16 +16,11 @@ import { ESSearchSource } from '../es_search_source'; import uuid from 'uuid/v4'; import _ from 'lodash'; import { i18n } from '@kbn/i18n'; +import { registerSource } from '../source_registry'; export class GeojsonFileSource extends AbstractVectorSource { static type = GEOJSON_FILE; - static title = i18n.translate('xpack.maps.source.geojsonFileTitle', { - defaultMessage: 'Uploaded GeoJSON', - }); - static description = i18n.translate('xpack.maps.source.geojsonFileDescription', { - defaultMessage: 'Upload and index GeoJSON data in Elasticsearch', - }); - static icon = 'importAction'; + static isIndexingSource = true; static createDescriptor(geoJson, name) { @@ -59,62 +54,93 @@ export class GeojsonFileSource extends AbstractVectorSource { }; } - static viewIndexedData = ( - addAndViewSource, - inspectorAdapters, - importSuccessHandler, - importErrorHandler - ) => { - return (indexResponses = {}) => { - const { indexDataResp, indexPatternResp } = indexResponses; - - const indexCreationFailed = !(indexDataResp && indexDataResp.success); - const allDocsFailed = indexDataResp.failures.length === indexDataResp.docCount; - const indexPatternCreationFailed = !(indexPatternResp && indexPatternResp.success); - - if (indexCreationFailed || allDocsFailed || indexPatternCreationFailed) { - importErrorHandler(indexResponses); - return; - } - const { fields, id } = indexPatternResp; - const geoFieldArr = fields.filter(field => - Object.values(ES_GEO_FIELD_TYPE).includes(field.type) - ); - const geoField = _.get(geoFieldArr, '[0].name'); - const indexPatternId = id; - if (!indexPatternId || !geoField) { - addAndViewSource(null); - } else { - // Only turn on bounds filter for large doc counts - const filterByMapBounds = indexDataResp.docCount > DEFAULT_MAX_RESULT_WINDOW; - const source = new ESSearchSource( - { - id: uuid(), - indexPatternId, - geoField, - filterByMapBounds, - }, - inspectorAdapters - ); - addAndViewSource(source); - importSuccessHandler(indexResponses); - } + async getGeoJsonWithMeta() { + return { + data: this._descriptor.__featureCollection, + meta: {}, }; + } + + async getDisplayName() { + return this._descriptor.name; + } + + canFormatFeatureProperties() { + return true; + } + + shouldBeIndexed() { + return GeojsonFileSource.isIndexingSource; + } +} + +const viewIndexedData = ( + addAndViewSource, + inspectorAdapters, + importSuccessHandler, + importErrorHandler +) => { + return (indexResponses = {}) => { + const { indexDataResp, indexPatternResp } = indexResponses; + + const indexCreationFailed = !(indexDataResp && indexDataResp.success); + const allDocsFailed = indexDataResp.failures.length === indexDataResp.docCount; + const indexPatternCreationFailed = !(indexPatternResp && indexPatternResp.success); + + if (indexCreationFailed || allDocsFailed || indexPatternCreationFailed) { + importErrorHandler(indexResponses); + return; + } + const { fields, id } = indexPatternResp; + const geoFieldArr = fields.filter(field => + Object.values(ES_GEO_FIELD_TYPE).includes(field.type) + ); + const geoField = _.get(geoFieldArr, '[0].name'); + const indexPatternId = id; + if (!indexPatternId || !geoField) { + addAndViewSource(null); + } else { + // Only turn on bounds filter for large doc counts + const filterByMapBounds = indexDataResp.docCount > DEFAULT_MAX_RESULT_WINDOW; + const source = new ESSearchSource( + { + id: uuid(), + indexPatternId, + geoField, + filterByMapBounds, + }, + inspectorAdapters + ); + addAndViewSource(source); + importSuccessHandler(indexResponses); + } }; +}; - static previewGeojsonFile = (onPreviewSource, inspectorAdapters) => { - return (geojsonFile, name) => { - if (!geojsonFile) { - onPreviewSource(null); - return; - } - const sourceDescriptor = GeojsonFileSource.createDescriptor(geojsonFile, name); - const source = new GeojsonFileSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; +const previewGeojsonFile = (onPreviewSource, inspectorAdapters) => { + return (geojsonFile, name) => { + if (!geojsonFile) { + onPreviewSource(null); + return; + } + const sourceDescriptor = GeojsonFileSource.createDescriptor(geojsonFile, name); + const source = new GeojsonFileSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); }; +}; + +registerSource({ + ConstructorFunction: GeojsonFileSource, + type: GEOJSON_FILE, +}); - static renderEditor({ +export const uploadLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.geojsonFileDescription', { + defaultMessage: 'Index GeoJSON data in Elasticsearch', + }), + icon: 'importAction', + isIndexingSource: true, + renderWizard: ({ onPreviewSource, inspectorAdapters, addAndViewSource, @@ -123,15 +149,12 @@ export class GeojsonFileSource extends AbstractVectorSource { onIndexReady, importSuccessHandler, importErrorHandler, - }) { + }) => { return ( ); - } - - async getGeoJsonWithMeta() { - return { - data: this._descriptor.__featureCollection, - meta: {}, - }; - } - - async getDisplayName() { - return this._descriptor.name; - } - - canFormatFeatureProperties() { - return true; - } - - shouldBeIndexed() { - return GeojsonFileSource.isIndexingSource; - } -} + }, + title: i18n.translate('xpack.maps.source.geojsonFileTitle', { + defaultMessage: 'Upload GeoJSON', + }), +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/index.js index cf0d15dcb747a..a6a31def4b231 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/client_file_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { GeojsonFileSource } from './geojson_file_source'; +export { GeojsonFileSource, uploadLayerWizardConfig } from './geojson_file_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js index 524f030862768..d3ccc0cb55821 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/ems_file_source.js @@ -14,16 +14,14 @@ import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { UpdateSourceEditor } from './update_source_editor'; import { EMSFileField } from '../../fields/ems_file_field'; +import { registerSource } from '../source_registry'; + +const sourceTitle = i18n.translate('xpack.maps.source.emsFileTitle', { + defaultMessage: 'EMS Boundaries', +}); export class EMSFileSource extends AbstractVectorSource { static type = EMS_FILE; - static title = i18n.translate('xpack.maps.source.emsFileTitle', { - defaultMessage: 'EMS Boundaries', - }); - static description = i18n.translate('xpack.maps.source.emsFileDescription', { - defaultMessage: 'Administrative boundaries from Elastic Maps Service', - }); - static icon = 'emsApp'; static createDescriptor({ id, tooltipProperties = [] }) { return { @@ -33,15 +31,6 @@ export class EMSFileSource extends AbstractVectorSource { }; } - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - const sourceDescriptor = EMSFileSource.createDescriptor(sourceConfig); - const source = new EMSFileSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - return ; - } - constructor(descriptor, inspectorAdapters) { super(EMSFileSource.createDescriptor(descriptor), inspectorAdapters); this._tooltipFields = this._descriptor.tooltipProperties.map(propertyKey => @@ -118,7 +107,7 @@ export class EMSFileSource extends AbstractVectorSource { return [ { label: getDataSourceLabel(), - value: EMSFileSource.title, + value: sourceTitle, }, { label: i18n.translate('xpack.maps.source.emsFile.layerLabel', { @@ -167,3 +156,24 @@ export class EMSFileSource extends AbstractVectorSource { return [VECTOR_SHAPE_TYPES.POLYGON]; } } + +registerSource({ + ConstructorFunction: EMSFileSource, + type: EMS_FILE, +}); + +export const emsBoundariesLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.emsFileDescription', { + defaultMessage: 'Administrative boundaries from Elastic Maps Service', + }), + icon: 'emsApp', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + const sourceDescriptor = EMSFileSource.createDescriptor(sourceConfig); + const source = new EMSFileSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/index.js index 9d0e503eb08ba..28fbc04a1a032 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_file_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EMSFileSource } from './ems_file_source'; +export { EMSFileSource, emsBoundariesLayerWizardConfig } from './ems_file_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js index 5a2124622694c..1da3680dfdc86 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/ems_tms_source.js @@ -16,16 +16,14 @@ import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import { EMS_TMS } from '../../../../common/constants'; import { getInjectedVarFunc, getUiSettings } from '../../../kibana_services'; +import { registerSource } from '../source_registry'; + +const sourceTitle = i18n.translate('xpack.maps.source.emsTileTitle', { + defaultMessage: 'EMS Basemaps', +}); export class EMSTMSSource extends AbstractTMSSource { static type = EMS_TMS; - static title = i18n.translate('xpack.maps.source.emsTileTitle', { - defaultMessage: 'EMS Basemaps', - }); - static description = i18n.translate('xpack.maps.source.emsTileDescription', { - defaultMessage: 'Tile map service from Elastic Maps Service', - }); - static icon = 'emsApp'; static createDescriptor(sourceConfig) { return { @@ -35,16 +33,6 @@ export class EMSTMSSource extends AbstractTMSSource { }; } - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - const descriptor = EMSTMSSource.createDescriptor(sourceConfig); - const source = new EMSTMSSource(descriptor, inspectorAdapters); - onPreviewSource(source); - }; - - return ; - } - constructor(descriptor, inspectorAdapters) { super( { @@ -69,7 +57,7 @@ export class EMSTMSSource extends AbstractTMSSource { return [ { label: getDataSourceLabel(), - value: EMSTMSSource.title, + value: sourceTitle, }, { label: i18n.translate('xpack.maps.source.emsTile.serviceId', { @@ -157,3 +145,25 @@ export class EMSTMSSource extends AbstractTMSSource { return isDarkMode ? emsTileLayerId.dark : emsTileLayerId.bright; } } + +registerSource({ + ConstructorFunction: EMSTMSSource, + type: EMS_TMS, +}); + +export const emsBaseMapLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.emsTileDescription', { + defaultMessage: 'Tile map service from Elastic Maps Service', + }), + icon: 'emsApp', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + const descriptor = EMSTMSSource.createDescriptor(sourceConfig); + const source = new EMSTMSSource(descriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/index.js index 81306578db4ae..60a4c9b1de891 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/ems_tms_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EMSTMSSource } from './ems_tms_source'; +export { EMSTMSSource, emsBaseMapLayerWizardConfig } from './ems_tms_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js index 148683269ef78..4aec390bec745 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/create_source_editor.js @@ -14,32 +14,12 @@ import { getIndexPatternService, getIndexPatternSelectComponent } from '../../.. import { NoIndexPatternCallout } from '../../../components/no_index_pattern_callout'; import { i18n } from '@kbn/i18n'; -import { EuiFormRow, EuiComboBox, EuiSpacer } from '@elastic/eui'; +import { EuiFormRow, EuiSpacer } from '@elastic/eui'; import { AGGREGATABLE_GEO_FIELD_TYPES, getAggregatableGeoFields, } from '../../../index_pattern_util'; - -const requestTypeOptions = [ - { - label: i18n.translate('xpack.maps.source.esGeoGrid.gridRectangleDropdownOption', { - defaultMessage: 'grid rectangles', - }), - value: RENDER_AS.GRID, - }, - { - label: i18n.translate('xpack.maps.source.esGeoGrid.heatmapDropdownOption', { - defaultMessage: 'heat map', - }), - value: RENDER_AS.HEATMAP, - }, - { - label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', { - defaultMessage: 'clusters', - }), - value: RENDER_AS.POINT, - }, -]; +import { RenderAsSelect } from './render_as_select'; export class CreateSourceEditor extends Component { static propTypes = { @@ -50,7 +30,7 @@ export class CreateSourceEditor extends Component { isLoadingIndexPattern: false, indexPatternId: '', geoField: '', - requestType: requestTypeOptions[0], + requestType: this.props.requestType, noGeoIndexPatternsExist: false, }; @@ -126,10 +106,10 @@ export class CreateSourceEditor extends Component { ); }; - _onRequestTypeSelect = selectedOptions => { + _onRequestTypeSelect = newValue => { this.setState( { - requestType: selectedOptions[0], + requestType: newValue, }, this.previewLayer ); @@ -139,9 +119,7 @@ export class CreateSourceEditor extends Component { const { indexPatternId, geoField, requestType } = this.state; const sourceConfig = - indexPatternId && geoField - ? { indexPatternId, geoField, requestType: requestType.value } - : null; + indexPatternId && geoField ? { indexPatternId, geoField, requestType } : null; this.props.onSourceConfigChange(sourceConfig); }; @@ -176,28 +154,13 @@ export class CreateSourceEditor extends Component { ); } - _renderLayerSelect() { - if (!this.state.indexPattern) { + _renderRenderAsSelect() { + if (this.state.requestType === RENDER_AS.HEATMAP || !this.state.indexPattern) { return null; } return ( - - - + ); } @@ -243,7 +206,7 @@ export class CreateSourceEditor extends Component { {this._renderNoIndexPatternWarning()} {this._renderIndexPatternSelect()} {this._renderGeoSelect()} - {this._renderLayerSelect()} + {this._renderRenderAsSelect()} ); } diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js index 405c8a61bfca6..dec802ac3cf1a 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/es_geo_grid_source.js @@ -32,17 +32,20 @@ import { AbstractESAggSource } from '../es_agg_source'; import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { StaticStyleProperty } from '../../styles/vector/properties/static_style_property'; import { DataRequestAbortError } from '../../util/data_request'; +import { registerSource } from '../source_registry'; export const MAX_GEOTILE_LEVEL = 29; +const clustersTitle = i18n.translate('xpack.maps.source.esGridClustersTitle', { + defaultMessage: 'Clusters and grids', +}); + +const heatmapTitle = i18n.translate('xpack.maps.source.esGridHeatmapTitle', { + defaultMessage: 'Heat map', +}); + export class ESGeoGridSource extends AbstractESAggSource { static type = ES_GEO_GRID; - static title = i18n.translate('xpack.maps.source.esGridTitle', { - defaultMessage: 'Grid aggregation', - }); - static description = i18n.translate('xpack.maps.source.esGridDescription', { - defaultMessage: 'Geospatial data grouped in grids with metrics for each gridded cell', - }); static createDescriptor({ indexPatternId, geoField, requestType, resolution }) { return { @@ -55,21 +58,6 @@ export class ESGeoGridSource extends AbstractESAggSource { }; } - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - if (!sourceConfig) { - onPreviewSource(null); - return; - } - - const sourceDescriptor = ESGeoGridSource.createDescriptor(sourceConfig); - const source = new ESGeoGridSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - - return ; - } - renderSourceSettingsEditor({ onChange }) { return ( { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const sourceDescriptor = ESGeoGridSource.createDescriptor(sourceConfig); + const source = new ESGeoGridSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ( + + ); + }, + title: clustersTitle, +}; + +export const heatmapLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.esGridHeatmapDescription', { + defaultMessage: 'Geospatial data grouped in grids to show density', + }), + icon: 'logoElasticsearch', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const sourceDescriptor = ESGeoGridSource.createDescriptor(sourceConfig); + const source = new ESGeoGridSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ( + + ); + }, + title: heatmapTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/index.js index 58d74c04c5552..c2fa2356b1a3e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/index.js @@ -4,4 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ESGeoGridSource } from './es_geo_grid_source'; +export { + ESGeoGridSource, + clustersLayerWizardConfig, + heatmapLayerWizardConfig, +} from './es_geo_grid_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx new file mode 100644 index 0000000000000..c82781ede186f --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_geo_grid_source/render_as_select.tsx @@ -0,0 +1,59 @@ +/* + * 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 { EuiFormRow, EuiComboBox, EuiComboBoxOptionOption } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import { RENDER_AS } from '../../../../common/constants'; + +const options = [ + { + label: i18n.translate('xpack.maps.source.esGeoGrid.pointsDropdownOption', { + defaultMessage: 'clusters', + }), + value: RENDER_AS.POINT, + }, + { + label: i18n.translate('xpack.maps.source.esGeoGrid.gridRectangleDropdownOption', { + defaultMessage: 'grids', + }), + value: RENDER_AS.GRID, + }, +]; + +export function RenderAsSelect(props: { + renderAs: RENDER_AS; + onChange: (newValue: RENDER_AS) => void; +}) { + function onChange(selectedOptions: Array>) { + if (!selectedOptions || !selectedOptions.length) { + return; + } + props.onChange(selectedOptions[0].value as RENDER_AS); + } + + const selectedOptions = []; + const selectedOption = options.find(option => option.value === props.renderAs); + if (selectedOption) { + selectedOptions.push(selectedOption); + } + + return ( + + + + ); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js index 5f6cc0a46dfb2..da2b663746b9d 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_pew_pew_source/es_pew_pew_source.js @@ -26,17 +26,16 @@ import { AbstractESAggSource } from '../es_agg_source'; import { DynamicStyleProperty } from '../../styles/vector/properties/dynamic_style_property'; import { COLOR_GRADIENTS } from '../../styles/color_utils'; import { indexPatterns } from '../../../../../../../../src/plugins/data/public'; +import { registerSource } from '../source_registry'; const MAX_GEOTILE_LEVEL = 29; +const sourceTitle = i18n.translate('xpack.maps.source.pewPewTitle', { + defaultMessage: 'Point to point', +}); + export class ESPewPewSource extends AbstractESAggSource { static type = ES_PEW_PEW; - static title = i18n.translate('xpack.maps.source.pewPewTitle', { - defaultMessage: 'Point to point', - }); - static description = i18n.translate('xpack.maps.source.pewPewDescription', { - defaultMessage: 'Aggregated data paths between the source and destination', - }); static createDescriptor({ indexPatternId, sourceGeoField, destGeoField }) { return { @@ -48,21 +47,6 @@ export class ESPewPewSource extends AbstractESAggSource { }; } - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - if (!sourceConfig) { - onPreviewSource(null); - return; - } - - const sourceDescriptor = ESPewPewSource.createDescriptor(sourceConfig); - const source = new ESPewPewSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - - return ; - } - renderSourceSettingsEditor({ onChange }) { return ( { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const sourceDescriptor = ESPewPewSource.createDescriptor(sourceConfig); + const source = new ESPewPewSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js index cd44ef49623fa..ce9932bd15cea 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/es_search_source.js @@ -32,6 +32,11 @@ import { BlendedVectorLayer } from '../../blended_vector_layer'; import { DEFAULT_FILTER_BY_MAP_BOUNDS } from './constants'; import { ESDocField } from '../../fields/es_doc_field'; import { getField, addFieldToDSL } from '../../util/es_agg_utils'; +import { registerSource } from '../source_registry'; + +const sourceTitle = i18n.translate('xpack.maps.source.esSearchTitle', { + defaultMessage: 'Documents', +}); function getDocValueAndSourceFields(indexPattern, fieldNames) { const docValueFields = []; @@ -65,31 +70,6 @@ function getDocValueAndSourceFields(indexPattern, fieldNames) { export class ESSearchSource extends AbstractESSource { static type = ES_SEARCH; - static title = i18n.translate('xpack.maps.source.esSearchTitle', { - defaultMessage: 'Documents', - }); - static description = i18n.translate('xpack.maps.source.esSearchDescription', { - defaultMessage: 'Vector data from a Kibana index pattern', - }); - - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - if (!sourceConfig) { - onPreviewSource(null); - return; - } - - const source = new ESSearchSource( - { - id: uuid(), - ...sourceConfig, - }, - inspectorAdapters - ); - onPreviewSource(source); - }; - return ; - } constructor(descriptor, inspectorAdapters) { super( @@ -206,7 +186,7 @@ export class ESSearchSource extends AbstractESSource { return [ { label: getDataSourceLabel(), - value: ESSearchSource.title, + value: sourceTitle, }, { label: i18n.translate('xpack.maps.source.esSearch.indexPatternLabel', { @@ -587,3 +567,34 @@ export class ESSearchSource extends AbstractESSource { }; } } + +registerSource({ + ConstructorFunction: ESSearchSource, + type: ES_SEARCH, +}); + +export const esDocumentsLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.esSearchDescription', { + defaultMessage: 'Vector data from a Kibana index pattern', + }), + icon: 'logoElasticsearch', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const source = new ESSearchSource( + { + id: uuid(), + ...sourceConfig, + }, + inspectorAdapters + ); + onPreviewSource(source); + }; + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/index.js index 5fea38ee274d9..2c401ac92567e 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_search_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { ESSearchSource } from './es_search_source'; +export { ESSearchSource, esDocumentsLayerWizardConfig } from './es_search_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js index 9dc3067a70436..bf04a73cfba77 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/es_source.js @@ -23,8 +23,6 @@ import { DataRequestAbortError } from '../util/data_request'; import { expandToTileBoundaries } from './es_geo_grid_source/geo_tile_utils'; export class AbstractESSource extends AbstractVectorSource { - static icon = 'logoElasticsearch'; - constructor(descriptor, inspectorAdapters) { super( { diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js index d54b135239a63..00c3bfc5f17c6 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { KibanaRegionmapSource } from './kibana_regionmap_source'; +export { KibanaRegionmapSource, kibanaRegionMapLayerWizardConfig } from './kibana_regionmap_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js index 276a3377aaae2..7f4bcfa41f7c4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_regionmap_source/kibana_regionmap_source.js @@ -10,18 +10,16 @@ import { CreateSourceEditor } from './create_source_editor'; import { getKibanaRegionList } from '../../../meta'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; -import { FIELD_ORIGIN } from '../../../../common/constants'; +import { FIELD_ORIGIN, REGIONMAP_FILE } from '../../../../common/constants'; import { KibanaRegionField } from '../../fields/kibana_region_field'; +import { registerSource } from '../source_registry'; + +const sourceTitle = i18n.translate('xpack.maps.source.kbnRegionMapTitle', { + defaultMessage: 'Configured GeoJSON', +}); export class KibanaRegionmapSource extends AbstractVectorSource { - static type = 'REGIONMAP_FILE'; - static title = i18n.translate('xpack.maps.source.kbnRegionMapTitle', { - defaultMessage: 'Configured GeoJSON', - }); - static description = i18n.translate('xpack.maps.source.kbnRegionMapDescription', { - defaultMessage: 'Vector data from hosted GeoJSON configured in kibana.yml', - }); - static icon = 'logoKibana'; + static type = REGIONMAP_FILE; static createDescriptor({ name }) { return { @@ -30,16 +28,6 @@ export class KibanaRegionmapSource extends AbstractVectorSource { }; } - static renderEditor = ({ onPreviewSource, inspectorAdapters }) => { - const onSourceConfigChange = sourceConfig => { - const sourceDescriptor = KibanaRegionmapSource.createDescriptor(sourceConfig); - const source = new KibanaRegionmapSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - - return ; - }; - createField({ fieldName }) { return new KibanaRegionField({ fieldName, @@ -52,7 +40,7 @@ export class KibanaRegionmapSource extends AbstractVectorSource { return [ { label: getDataSourceLabel(), - value: KibanaRegionmapSource.title, + value: sourceTitle, }, { label: i18n.translate('xpack.maps.source.kbnRegionMap.vectorLayerLabel', { @@ -108,3 +96,25 @@ export class KibanaRegionmapSource extends AbstractVectorSource { return true; } } + +registerSource({ + ConstructorFunction: KibanaRegionmapSource, + type: REGIONMAP_FILE, +}); + +export const kibanaRegionMapLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.kbnRegionMapDescription', { + defaultMessage: 'Vector data from hosted GeoJSON configured in kibana.yml', + }), + icon: 'logoKibana', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + const sourceDescriptor = KibanaRegionmapSource.createDescriptor(sourceConfig); + const source = new KibanaRegionmapSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js index 3226fb89b700b..9fd7f088032ca 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { KibanaTilemapSource } from './kibana_tilemap_source'; +export { KibanaTilemapSource, kibanaBasemapLayerWizardConfig } from './kibana_tilemap_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js index 21ab2ba42c7bb..b21bb6bdbbad4 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/kibana_tilemap_source/kibana_tilemap_source.js @@ -11,17 +11,15 @@ import { getKibanaTileMap } from '../../../meta'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel } from '../../../../common/i18n_getters'; import _ from 'lodash'; +import { KIBANA_TILEMAP } from '../../../../common/constants'; +import { registerSource } from '../source_registry'; -export class KibanaTilemapSource extends AbstractTMSSource { - static type = 'KIBANA_TILEMAP'; - static title = i18n.translate('xpack.maps.source.kbnTMSTitle', { - defaultMessage: 'Configured Tile Map Service', - }); - static description = i18n.translate('xpack.maps.source.kbnTMSDescription', { - defaultMessage: 'Tile map service configured in kibana.yml', - }); +const sourceTitle = i18n.translate('xpack.maps.source.kbnTMSTitle', { + defaultMessage: 'Configured Tile Map Service', +}); - static icon = 'logoKibana'; +export class KibanaTilemapSource extends AbstractTMSSource { + static type = KIBANA_TILEMAP; static createDescriptor() { return { @@ -29,20 +27,11 @@ export class KibanaTilemapSource extends AbstractTMSSource { }; } - static renderEditor = ({ onPreviewSource, inspectorAdapters }) => { - const onSourceConfigChange = () => { - const sourceDescriptor = KibanaTilemapSource.createDescriptor(); - const source = new KibanaTilemapSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - return ; - }; - async getImmutableProperties() { return [ { label: getDataSourceLabel(), - value: KibanaTilemapSource.title, + value: sourceTitle, }, { label: i18n.translate('xpack.maps.source.kbnTMS.urlLabel', { @@ -94,3 +83,24 @@ export class KibanaTilemapSource extends AbstractTMSSource { } } } + +registerSource({ + ConstructorFunction: KibanaTilemapSource, + type: KIBANA_TILEMAP, +}); + +export const kibanaBasemapLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.kbnTMSDescription', { + defaultMessage: 'Tile map service configured in kibana.yml', + }), + icon: 'logoKibana', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = () => { + const sourceDescriptor = KibanaTilemapSource.createDescriptor(); + const source = new KibanaTilemapSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/source_registry.ts b/x-pack/legacy/plugins/maps/public/layers/sources/source_registry.ts new file mode 100644 index 0000000000000..518cab68b601b --- /dev/null +++ b/x-pack/legacy/plugins/maps/public/layers/sources/source_registry.ts @@ -0,0 +1,35 @@ +/* + * 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. + */ +/* eslint-disable @typescript-eslint/consistent-type-definitions */ + +import { AbstractSourceDescriptor } from '../../../common/descriptor_types'; +import { ISource } from './source'; + +type SourceRegistryEntry = { + ConstructorFunction: new ( + sourceDescriptor: AbstractSourceDescriptor, + inspectorAdapters: unknown + ) => ISource; + type: string; +}; + +const registry: SourceRegistryEntry[] = []; + +export function registerSource(entry: SourceRegistryEntry) { + const sourceTypeExists = registry.some(({ type }: SourceRegistryEntry) => { + return entry.type === type; + }); + if (sourceTypeExists) { + throw new Error( + `Unable to register source type ${entry.type}. ${entry.type} has already been registered` + ); + } + registry.push(entry); +} + +export function getSourceByType(sourceType: string): SourceRegistryEntry | undefined { + return registry.find((source: SourceRegistryEntry) => source.type === sourceType); +} diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/index.js b/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/index.js index 22bc50e601f56..daae552a6f772 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/index.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/index.js @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { WMSSource } from './wms_source'; +export { WMSSource, wmsLayerWizardConfig } from './wms_source'; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_source.js index 61955df94e451..749560a2bb4b1 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/wms_source/wms_source.js @@ -12,16 +12,15 @@ import { WMSCreateSourceEditor } from './wms_create_source_editor'; import { i18n } from '@kbn/i18n'; import { getDataSourceLabel, getUrlLabel } from '../../../../common/i18n_getters'; import { WmsClient } from './wms_client'; +import { WMS } from '../../../../common/constants'; +import { registerSource } from '../source_registry'; + +const sourceTitle = i18n.translate('xpack.maps.source.wmsTitle', { + defaultMessage: 'Web Map Service', +}); export class WMSSource extends AbstractTMSSource { - static type = 'WMS'; - static title = i18n.translate('xpack.maps.source.wmsTitle', { - defaultMessage: 'Web Map Service', - }); - static description = i18n.translate('xpack.maps.source.wmsDescription', { - defaultMessage: 'Maps from OGC Standard WMS', - }); - static icon = 'grid'; + static type = WMS; static createDescriptor({ serviceUrl, layers, styles, attributionText, attributionUrl }) { return { @@ -34,23 +33,9 @@ export class WMSSource extends AbstractTMSSource { }; } - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - if (!sourceConfig) { - onPreviewSource(null); - return; - } - - const sourceDescriptor = WMSSource.createDescriptor(sourceConfig); - const source = new WMSSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - return ; - } - async getImmutableProperties() { return [ - { label: getDataSourceLabel(), value: WMSSource.title }, + { label: getDataSourceLabel(), value: sourceTitle }, { label: getUrlLabel(), value: this._descriptor.serviceUrl }, { label: i18n.translate('xpack.maps.source.wms.layersLabel', { @@ -104,3 +89,29 @@ export class WMSSource extends AbstractTMSSource { return client.getUrlTemplate(this._descriptor.layers, this._descriptor.styles || ''); } } + +registerSource({ + ConstructorFunction: WMSSource, + type: WMS, +}); + +export const wmsLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.wmsDescription', { + defaultMessage: 'Maps from OGC Standard WMS', + }), + icon: 'grid', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + if (!sourceConfig) { + onPreviewSource(null); + return; + } + + const sourceDescriptor = WMSSource.createDescriptor(sourceConfig); + const source = new WMSSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js b/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js index 354883372e244..d53fbffd21512 100644 --- a/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js +++ b/x-pack/legacy/plugins/maps/public/layers/sources/xyz_tms_source.js @@ -13,16 +13,14 @@ import { i18n } from '@kbn/i18n'; import { getDataSourceLabel, getUrlLabel } from '../../../common/i18n_getters'; import _ from 'lodash'; import { EMS_XYZ } from '../../../common/constants'; +import { registerSource } from './source_registry'; + +const sourceTitle = i18n.translate('xpack.maps.source.ems_xyzTitle', { + defaultMessage: 'Tile Map Service', +}); export class XYZTMSSource extends AbstractTMSSource { static type = EMS_XYZ; - static title = i18n.translate('xpack.maps.source.ems_xyzTitle', { - defaultMessage: 'Tile Map Service', - }); - static description = i18n.translate('xpack.maps.source.ems_xyzDescription', { - defaultMessage: 'Tile map service configured in interface', - }); - static icon = 'grid'; static createDescriptor({ urlTemplate, attributionText, attributionUrl }) { return { @@ -33,18 +31,9 @@ export class XYZTMSSource extends AbstractTMSSource { }; } - static renderEditor({ onPreviewSource, inspectorAdapters }) { - const onSourceConfigChange = sourceConfig => { - const sourceDescriptor = XYZTMSSource.createDescriptor(sourceConfig); - const source = new XYZTMSSource(sourceDescriptor, inspectorAdapters); - onPreviewSource(source); - }; - return ; - } - async getImmutableProperties() { return [ - { label: getDataSourceLabel(), value: XYZTMSSource.title }, + { label: getDataSourceLabel(), value: sourceTitle }, { label: getUrlLabel(), value: this._descriptor.urlTemplate }, ]; } @@ -175,3 +164,24 @@ class XYZTMSEditor extends React.Component { ); } } + +registerSource({ + ConstructorFunction: XYZTMSSource, + type: EMS_XYZ, +}); + +export const tmsLayerWizardConfig = { + description: i18n.translate('xpack.maps.source.ems_xyzDescription', { + defaultMessage: 'Tile map service configured in interface', + }), + icon: 'grid', + renderWizard: ({ onPreviewSource, inspectorAdapters }) => { + const onSourceConfigChange = sourceConfig => { + const sourceDescriptor = XYZTMSSource.createDescriptor(sourceConfig); + const source = new XYZTMSSource(sourceDescriptor, inspectorAdapters); + onPreviewSource(source); + }; + return ; + }, + title: sourceTitle, +}; diff --git a/x-pack/legacy/plugins/maps/public/plugin.ts b/x-pack/legacy/plugins/maps/public/plugin.ts index 53c951ac787e1..c08ed6fc6da61 100644 --- a/x-pack/legacy/plugins/maps/public/plugin.ts +++ b/x-pack/legacy/plugins/maps/public/plugin.ts @@ -4,6 +4,10 @@ * you may not use this file except in compliance with the Elastic License. */ +import './layers/layer_wizard_registry'; +import './layers/sources/source_registry'; +import './layers/load_layer_wizards'; + import { Plugin, CoreStart, CoreSetup } from 'src/core/public'; // @ts-ignore import { wrapInI18nContext } from 'ui/i18n'; diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js index 61eea2d172ae4..397478cfd1d1b 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.js @@ -11,7 +11,6 @@ import { VectorTileLayer } from '../layers/vector_tile_layer'; import { VectorLayer } from '../layers/vector_layer'; import { HeatmapLayer } from '../layers/heatmap_layer'; import { BlendedVectorLayer } from '../layers/blended_vector_layer'; -import { ALL_SOURCES } from '../layers/sources/all_sources'; import { getTimeFilter } from '../kibana_services'; // eslint-disable-next-line @kbn/eslint/no-restricted-paths import { getInspectorAdapters } from '../../../../../plugins/maps/public/reducers/non_serializable_instances'; @@ -21,6 +20,7 @@ import { // eslint-disable-next-line @kbn/eslint/no-restricted-paths } from '../../../../../plugins/maps/public/reducers/util'; import { InnerJoin } from '../layers/joins/inner_join'; +import { getSourceByType } from '../layers/sources/source_registry'; function createLayerInstance(layerDescriptor, inspectorAdapters) { const source = createSourceInstance(layerDescriptor.sourceDescriptor, inspectorAdapters); @@ -49,13 +49,11 @@ function createLayerInstance(layerDescriptor, inspectorAdapters) { } function createSourceInstance(sourceDescriptor, inspectorAdapters) { - const Source = ALL_SOURCES.find(Source => { - return Source.type === sourceDescriptor.type; - }); - if (!Source) { + const source = getSourceByType(sourceDescriptor.type); + if (!source) { throw new Error(`Unrecognized sourceType ${sourceDescriptor.type}`); } - return new Source(sourceDescriptor, inspectorAdapters); + return new source.ConstructorFunction(sourceDescriptor, inspectorAdapters); } export const getOpenTooltips = ({ map }) => { diff --git a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js index e7f071d5729c6..1a5ab633a569f 100644 --- a/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js +++ b/x-pack/legacy/plugins/maps/public/selectors/map_selectors.test.js @@ -8,7 +8,6 @@ jest.mock('../layers/vector_layer', () => {}); jest.mock('../layers/blended_vector_layer', () => {}); jest.mock('../layers/heatmap_layer', () => {}); jest.mock('../layers/vector_tile_layer', () => {}); -jest.mock('../layers/sources/all_sources', () => {}); jest.mock('../layers/joins/inner_join', () => {}); jest.mock('../../../../../plugins/maps/public/reducers/non_serializable_instances', () => ({ getInspectorAdapters: () => { diff --git a/x-pack/plugins/maps/common/constants.ts b/x-pack/plugins/maps/common/constants.ts index 30a3350ad754e..bd4406ef5ce63 100644 --- a/x-pack/plugins/maps/common/constants.ts +++ b/x-pack/plugins/maps/common/constants.ts @@ -62,6 +62,9 @@ export const ES_GEO_GRID = 'ES_GEO_GRID'; export const ES_SEARCH = 'ES_SEARCH'; export const ES_PEW_PEW = 'ES_PEW_PEW'; export const EMS_XYZ = 'EMS_XYZ'; // identifies a custom TMS source. Name is a little unfortunate. +export const WMS = 'WMS'; +export const KIBANA_TILEMAP = 'KIBANA_TILEMAP'; +export const REGIONMAP_FILE = 'REGIONMAP_FILE'; export enum FIELD_ORIGIN { SOURCE = 'source', diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index aea6e2ab00094..1b4810e8fdf0a 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -7137,12 +7137,10 @@ "xpack.maps.source.esGeoGrid.geofieldLabel": "地理空間フィールド", "xpack.maps.source.esGeoGrid.geofieldPlaceholder": "ジオフィールドを選択", "xpack.maps.source.esGeoGrid.gridRectangleDropdownOption": "グリッド四角", - "xpack.maps.source.esGeoGrid.heatmapDropdownOption": "ヒートマップ", "xpack.maps.source.esGeoGrid.indexPatternLabel": "インデックスパターン", "xpack.maps.source.esGeoGrid.indexPatternPlaceholder": "インデックスパターンを選択", "xpack.maps.source.esGeoGrid.pointsDropdownOption": "点", "xpack.maps.source.esGeoGrid.showAsLabel": "表示形式", - "xpack.maps.source.esGeoGrid.showAsPlaceholder": "1 つのオプションを選択", "xpack.maps.source.esGrid.coarseDropdownOption": "粗い", "xpack.maps.source.esGrid.fineDropdownOption": "細かい", "xpack.maps.source.esGrid.finestDropdownOption": "最も細かい", @@ -7151,9 +7149,6 @@ "xpack.maps.source.esGrid.metricsLabel": "メトリック", "xpack.maps.source.esGrid.noIndexPatternErrorMessage": "インデックスパターン {id} が見つかりません", "xpack.maps.source.esGrid.resolutionParamErrorMessage": "グリッド解像度パラメーターが認識されません: {resolution}", - "xpack.maps.source.esGrid.showasFieldLabel": "表示形式", - "xpack.maps.source.esGridDescription": "それぞれのグリッド付きセルのメトリックでグリッドにグループ分けされた地理空間データです。", - "xpack.maps.source.esGridTitle": "グリッド集約", "xpack.maps.source.esSearch.convertToGeoJsonErrorMsg": "検索への応答を geoJson 機能コレクションに変換できません。エラー: {errorMsg}", "xpack.maps.source.esSearch.extentFilterLabel": "マップの表示範囲でデータを動的にフィルタリング", "xpack.maps.source.esSearch.geofieldLabel": "地理空間フィールド", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index dfc5ef065732e..5b3baa3552dbe 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -7137,12 +7137,10 @@ "xpack.maps.source.esGeoGrid.geofieldLabel": "地理空间字段", "xpack.maps.source.esGeoGrid.geofieldPlaceholder": "选择地理字段", "xpack.maps.source.esGeoGrid.gridRectangleDropdownOption": "网格矩形", - "xpack.maps.source.esGeoGrid.heatmapDropdownOption": "热图", "xpack.maps.source.esGeoGrid.indexPatternLabel": "索引模式", "xpack.maps.source.esGeoGrid.indexPatternPlaceholder": "选择索引模式", "xpack.maps.source.esGeoGrid.pointsDropdownOption": "磅", "xpack.maps.source.esGeoGrid.showAsLabel": "显示为", - "xpack.maps.source.esGeoGrid.showAsPlaceholder": "选择单个选项", "xpack.maps.source.esGrid.coarseDropdownOption": "粗糙", "xpack.maps.source.esGrid.fineDropdownOption": "精致", "xpack.maps.source.esGrid.finestDropdownOption": "最精致化", @@ -7151,9 +7149,6 @@ "xpack.maps.source.esGrid.metricsLabel": "指标", "xpack.maps.source.esGrid.noIndexPatternErrorMessage": "找不到索引模式 {id}", "xpack.maps.source.esGrid.resolutionParamErrorMessage": "无法识别网格分辨率参数:{resolution}", - "xpack.maps.source.esGrid.showasFieldLabel": "显示为", - "xpack.maps.source.esGridDescription": "地理空间数据在网格中进行分组,每个网格单元格都具有指标", - "xpack.maps.source.esGridTitle": "网格聚合", "xpack.maps.source.esSearch.convertToGeoJsonErrorMsg": "无法将搜索响应转换成 geoJson 功能集合,错误:{errorMsg}", "xpack.maps.source.esSearch.extentFilterLabel": "在可见地图区域中动态筛留数据", "xpack.maps.source.esSearch.geofieldLabel": "地理空间字段", diff --git a/x-pack/test/functional/page_objects/gis_page.js b/x-pack/test/functional/page_objects/gis_page.js index f8d1808c1ef8d..700575220297e 100644 --- a/x-pack/test/functional/page_objects/gis_page.js +++ b/x-pack/test/functional/page_objects/gis_page.js @@ -511,7 +511,7 @@ export function GisPageProvider({ getService, getPageObjects }) { async selectGeoJsonUploadSource() { log.debug(`Select upload geojson source`); - await testSubjects.click('uploadedGeoJson'); + await testSubjects.click('uploadGeoJson'); } async uploadJsonFileForIndexing(path) { From df655c9a97a810639a24f12f64cd48b70396ffc8 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 2 Apr 2020 08:31:32 +0200 Subject: [PATCH 14/17] [Uptime] Optimize get latest monitor API (#61820) * update monitor status API * update fixture * fix types * fix tets * fix tests --- .../uptime/common/constants/rest_api.ts | 1 - .../monitor/status_bar_container.tsx | 12 +- .../__snapshots__/monitor.test.tsx.snap | 2 +- .../plugins/uptime/public/pages/monitor.tsx | 54 ++------- .../public/state/actions/monitor_status.ts | 4 - .../uptime/public/state/api/monitor_status.ts | 14 +-- .../public/state/effects/monitor_status.ts | 14 +-- .../public/state/reducers/monitor_status.ts | 37 ++---- .../state/selectors/__tests__/index.test.ts | 1 - .../uptime/public/state/selectors/index.ts | 4 +- .../__tests__/get_latest_monitor.test.ts | 51 +++----- .../server/lib/requests/get_latest_monitor.ts | 31 ++--- .../uptime/server/lib/requests/get_monitor.ts | 49 -------- .../uptime/server/lib/requests/index.ts | 1 - .../server/lib/requests/uptime_requests.ts | 2 - .../plugins/uptime/server/rest_api/index.ts | 2 - .../uptime/server/rest_api/monitors/index.ts | 2 +- .../uptime/server/rest_api/monitors/status.ts | 25 +--- .../rest/fixtures/monitor_latest_status.json | 114 +++++------------- .../rest/fixtures/selected_monitor.json | 28 ----- .../api_integration/apis/uptime/rest/index.ts | 1 - .../apis/uptime/rest/selected_monitor.ts | 23 ---- 22 files changed, 78 insertions(+), 394 deletions(-) delete mode 100644 x-pack/plugins/uptime/server/lib/requests/get_monitor.ts delete mode 100644 x-pack/test/api_integration/apis/uptime/rest/fixtures/selected_monitor.json delete mode 100644 x-pack/test/api_integration/apis/uptime/rest/selected_monitor.ts diff --git a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts index 7fafe6584d831..86e2b03e13f22 100644 --- a/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts +++ b/x-pack/legacy/plugins/uptime/common/constants/rest_api.ts @@ -10,7 +10,6 @@ export enum API_URLS { MONITOR_LOCATIONS = `/api/uptime/monitor/locations`, MONITOR_DURATION = `/api/uptime/monitor/duration`, MONITOR_DETAILS = `/api/uptime/monitor/details`, - MONITOR_SELECTED = `/api/uptime/monitor/selected`, MONITOR_STATUS = `/api/uptime/monitor/status`, PINGS = '/api/uptime/pings', PING_HISTOGRAM = `/api/uptime/ping/histogram`, diff --git a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx index 9e7834ae6f242..dd6f7a89cf9a3 100644 --- a/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx +++ b/x-pack/legacy/plugins/uptime/public/components/connected/monitor/status_bar_container.tsx @@ -8,9 +8,9 @@ import React, { useContext, useEffect } from 'react'; import { connect } from 'react-redux'; import { Dispatch } from 'redux'; import { AppState } from '../../../state'; -import { monitorLocationsSelector, selectMonitorStatus } from '../../../state/selectors'; +import { monitorLocationsSelector, monitorStatusSelector } from '../../../state/selectors'; import { MonitorStatusBarComponent } from '../../functional/monitor_status_details/monitor_status_bar'; -import { getMonitorStatusAction, getSelectedMonitorAction } from '../../../state/actions'; +import { getMonitorStatusAction } from '../../../state/actions'; import { useUrlParams } from '../../../hooks'; import { Ping } from '../../../../common/graphql/types'; import { MonitorLocations } from '../../../../common/runtime_types/monitor'; @@ -23,7 +23,6 @@ interface StateProps { interface DispatchProps { loadMonitorStatus: typeof getMonitorStatusAction; - loadSelectedMonitor: typeof getSelectedMonitorAction; } interface OwnProps { @@ -34,7 +33,6 @@ type Props = OwnProps & StateProps & DispatchProps; const Container: React.FC = ({ loadMonitorStatus, - loadSelectedMonitor, monitorId, monitorStatus, monitorLocations, @@ -46,8 +44,7 @@ const Container: React.FC = ({ useEffect(() => { loadMonitorStatus({ dateStart, dateEnd, monitorId }); - loadSelectedMonitor({ monitorId }); - }, [monitorId, dateStart, dateEnd, loadMonitorStatus, lastRefresh, loadSelectedMonitor]); + }, [monitorId, dateStart, dateEnd, loadMonitorStatus, lastRefresh]); return ( = ({ }; const mapStateToProps = (state: AppState, ownProps: OwnProps) => ({ - monitorStatus: selectMonitorStatus(state), + monitorStatus: monitorStatusSelector(state), monitorLocations: monitorLocationsSelector(state, ownProps.monitorId), }); const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ - loadSelectedMonitor: params => dispatch(getSelectedMonitorAction(params)), loadMonitorStatus: params => dispatch(getMonitorStatusAction(params)), }); diff --git a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap b/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap index f637af397bbeb..6064caa868bf8 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap +++ b/x-pack/legacy/plugins/uptime/public/pages/__tests__/__snapshots__/monitor.test.tsx.snap @@ -51,6 +51,6 @@ exports[`MonitorPage shallow renders expected elements for valid props 1`] = ` } } > - + `; diff --git a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx index 3de636cac6ecd..21124b7323d68 100644 --- a/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx +++ b/x-pack/legacy/plugins/uptime/public/pages/monitor.tsx @@ -5,45 +5,23 @@ */ import { EuiSpacer } from '@elastic/eui'; -import React, { useContext, useState, useEffect } from 'react'; +import React, { useContext, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { connect, MapDispatchToPropsFunction, MapStateToPropsParam } from 'react-redux'; +import { useSelector } from 'react-redux'; import { MonitorCharts, PingList } from '../components/functional'; import { UptimeRefreshContext } from '../contexts'; import { useUptimeTelemetry, useUrlParams, UptimePage } from '../hooks'; import { useTrackPageview } from '../../../../../plugins/observability/public'; import { MonitorStatusDetails } from '../components/connected'; -import { Ping } from '../../common/graphql/types'; -import { AppState } from '../state'; -import { selectSelectedMonitor } from '../state/selectors'; -import { getSelectedMonitorAction } from '../state/actions'; +import { monitorStatusSelector } from '../state/selectors'; import { PageHeader } from './page_header'; import { useBreadcrumbs } from '../hooks/use_breadcrumbs'; -interface StateProps { - selectedMonitor: Ping | null; -} - -interface DispatchProps { - dispatchGetMonitorStatus: (monitorId: string) => void; -} - -type Props = StateProps & DispatchProps; - -export const MonitorPageComponent: React.FC = ({ - selectedMonitor, - dispatchGetMonitorStatus, -}: Props) => { +export const MonitorPage: React.FC = () => { // decode 64 base string, it was decoded to make it a valid url, since monitor id can be a url let { monitorId } = useParams(); monitorId = atob(monitorId || ''); - useEffect(() => { - if (monitorId) { - dispatchGetMonitorStatus(monitorId); - } - }, [dispatchGetMonitorStatus, monitorId]); - const [pingListPageCount, setPingListPageCount] = useState(10); const { refreshApp } = useContext(UptimeRefreshContext); const [getUrlParams, updateUrlParams] = useUrlParams(); @@ -53,11 +31,13 @@ export const MonitorPageComponent: React.FC = ({ const [selectedLocation, setSelectedLocation] = useState(undefined); const [pingListIndex, setPingListIndex] = useState(0); + const selectedMonitor = useSelector(monitorStatusSelector); + const sharedVariables = { dateRangeStart, dateRangeEnd, - location: selectedLocation, monitorId, + location: selectedLocation, }; useUptimeTelemetry(UptimePage.Monitor); @@ -65,7 +45,7 @@ export const MonitorPageComponent: React.FC = ({ useTrackPageview({ app: 'uptime', path: 'monitor' }); useTrackPageview({ app: 'uptime', path: 'monitor', delay: 15000 }); - const nameOrId = selectedMonitor?.monitor?.name || selectedMonitor?.monitor?.id || ''; + const nameOrId = selectedMonitor?.monitor?.name || monitorId || ''; useBreadcrumbs([{ text: nameOrId }]); return ( <> @@ -97,21 +77,3 @@ export const MonitorPageComponent: React.FC = ({ ); }; - -const mapStateToProps: MapStateToPropsParam = state => ({ - selectedMonitor: selectSelectedMonitor(state), -}); - -const mapDispatchToProps: MapDispatchToPropsFunction = (dispatch, own) => { - return { - dispatchGetMonitorStatus: (monitorId: string) => { - dispatch( - getSelectedMonitorAction({ - monitorId, - }) - ); - }, - }; -}; - -export const MonitorPage = connect(mapStateToProps, mapDispatchToProps)(MonitorPageComponent); diff --git a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts index 7917628abf7da..a8f37d38ebae6 100644 --- a/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/actions/monitor_status.ts @@ -7,10 +7,6 @@ import { createAction } from 'redux-actions'; import { QueryParams } from './types'; import { Ping } from '../../../common/graphql/types'; -export const getSelectedMonitorAction = createAction<{ monitorId: string }>('GET_SELECTED_MONITOR'); -export const getSelectedMonitorActionSuccess = createAction('GET_SELECTED_MONITOR_SUCCESS'); -export const getSelectedMonitorActionFail = createAction('GET_SELECTED_MONITOR_FAIL'); - export const getMonitorStatusAction = createAction('GET_MONITOR_STATUS'); export const getMonitorStatusActionSuccess = createAction('GET_MONITOR_STATUS_SUCCESS'); export const getMonitorStatusActionFail = createAction('GET_MONITOR_STATUS_FAIL'); diff --git a/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts index 0f7608ba57ea7..f9e171adda334 100644 --- a/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/api/monitor_status.ts @@ -7,19 +7,7 @@ import { QueryParams } from '../actions/types'; import { Ping } from '../../../common/graphql/types'; import { apiService } from './utils'; -import { API_URLS } from '../../../common/constants/rest_api'; - -export interface APIParams { - monitorId: string; -} - -export const fetchSelectedMonitor = async ({ monitorId }: APIParams): Promise => { - const queryParams = { - monitorId, - }; - - return await apiService.get(API_URLS.MONITOR_SELECTED, queryParams); -}; +import { API_URLS } from '../../../common/constants'; export const fetchMonitorStatus = async ({ monitorId, diff --git a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts index 1207ab20bc711..2669629ed34f5 100644 --- a/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/effects/monitor_status.ts @@ -6,14 +6,11 @@ import { takeLatest } from 'redux-saga/effects'; import { - getSelectedMonitorAction, - getSelectedMonitorActionSuccess, - getSelectedMonitorActionFail, getMonitorStatusAction, getMonitorStatusActionSuccess, getMonitorStatusActionFail, } from '../actions'; -import { fetchSelectedMonitor, fetchMonitorStatus } from '../api'; +import { fetchMonitorStatus } from '../api'; import { fetchEffectFactory } from './fetch_effect'; export function* fetchMonitorStatusEffect() { @@ -25,13 +22,4 @@ export function* fetchMonitorStatusEffect() { getMonitorStatusActionFail ) ); - - yield takeLatest( - getSelectedMonitorAction, - fetchEffectFactory( - fetchSelectedMonitor, - getSelectedMonitorActionSuccess, - getSelectedMonitorActionFail - ) - ); } diff --git a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts index c2dfbd7f90ff2..6cfaa9f8f59c1 100644 --- a/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts +++ b/x-pack/legacy/plugins/uptime/public/state/reducers/monitor_status.ts @@ -5,9 +5,6 @@ */ import { handleActions, Action } from 'redux-actions'; import { - getSelectedMonitorAction, - getSelectedMonitorActionSuccess, - getSelectedMonitorActionFail, getMonitorStatusAction, getMonitorStatusActionSuccess, getMonitorStatusActionFail, @@ -17,13 +14,11 @@ import { QueryParams } from '../actions/types'; export interface MonitorStatusState { status: Ping | null; - monitor: Ping | null; loading: boolean; } const initialState: MonitorStatusState = { status: null, - monitor: null, loading: false, }; @@ -31,32 +26,22 @@ type MonitorStatusPayload = QueryParams & Ping; export const monitorStatusReducer = handleActions( { - [String(getSelectedMonitorAction)]: (state, action: Action) => ({ - ...state, - loading: true, - }), - - [String(getSelectedMonitorActionSuccess)]: (state, action: Action) => ({ - ...state, - loading: false, - monitor: { ...action.payload } as Ping, - }), - - [String(getSelectedMonitorActionFail)]: (state, action: Action) => ({ - ...state, - loading: false, - }), - [String(getMonitorStatusAction)]: (state, action: Action) => ({ ...state, loading: true, }), - [String(getMonitorStatusActionSuccess)]: (state, action: Action) => ({ - ...state, - loading: false, - status: { ...action.payload } as Ping, - }), + [String(getMonitorStatusActionSuccess)]: (state, action: Action) => { + return { + ...state, + loading: false, + // Keeping url from prev request to display, if there is no latest status + status: { + url: action.payload?.url || state.status?.url, + ...action.payload, + } as Ping, + }; + }, [String(getMonitorStatusActionFail)]: (state, action: Action) => ({ ...state, diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts index 573d5b1906082..3b4547514a11e 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/__tests__/index.test.ts @@ -46,7 +46,6 @@ describe('state selectors', () => { }, monitorStatus: { status: null, - monitor: null, loading: false, }, indexPattern: { diff --git a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts index 21e01bd7d8279..0fc3c7151cb3b 100644 --- a/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts +++ b/x-pack/legacy/plugins/uptime/public/state/selectors/index.ts @@ -22,9 +22,7 @@ export const monitorLocationsSelector = (state: AppState, monitorId: string) => return state.monitor.monitorLocationsList?.get(monitorId); }; -export const selectSelectedMonitor = (state: AppState) => state.monitorStatus.monitor; - -export const selectMonitorStatus = (state: AppState) => state.monitorStatus.status; +export const monitorStatusSelector = (state: AppState) => state.monitorStatus.status; export const selectDynamicSettings = (state: AppState) => { return state.dynamicSettings; diff --git a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts index b7e340fddbd2c..112c8e97d4c00 100644 --- a/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts +++ b/x-pack/plugins/uptime/server/lib/requests/__tests__/get_latest_monitor.test.ts @@ -31,49 +31,25 @@ describe('getLatestMonitor', () => { ], }, }, - aggs: { - by_id: { - terms: { - field: 'monitor.id', - size: 1000, - }, - aggs: { - latest: { - top_hits: { - size: 1, - sort: { - '@timestamp': { order: 'desc' }, - }, - }, - }, - }, - }, + size: 1, + _source: ['url', 'monitor', 'observer', 'tls', '@timestamp'], + sort: { + '@timestamp': { order: 'desc' }, }, - size: 0, }, }; mockEsSearchResult = { - aggregations: { - by_id: { - buckets: [ - { - latest: { - hits: { - hits: [ - { - _source: { - '@timestamp': 123456, - monitor: { - id: 'testMonitor', - }, - }, - }, - ], - }, + hits: { + hits: [ + { + _source: { + timestamp: 123456, + monitor: { + id: 'testMonitor', }, }, - ], - }, + }, + ], }, }; }); @@ -87,6 +63,7 @@ describe('getLatestMonitor', () => { dateEnd: 'now', monitorId: 'testMonitor', }); + expect(result.timestamp).toBe(123456); expect(result.monitor).not.toBeFalsy(); expect(result?.monitor?.id).toBe('testMonitor'); diff --git a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts index 176963a998685..299e3eb6ca3cf 100644 --- a/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts +++ b/x-pack/plugins/uptime/server/lib/requests/get_latest_monitor.ts @@ -26,8 +26,6 @@ export const getLatestMonitor: UMElasticsearchQueryFn { - // TODO: Write tests for this function - const params = { index: dynamicSettings.heartbeatIndices, body: { @@ -46,33 +44,20 @@ export const getLatestMonitor: UMElasticsearchQueryFn = async ({ - callES, - dynamicSettings, - monitorId, -}) => { - const params = { - index: dynamicSettings.heartbeatIndices, - body: { - size: 1, - _source: ['url', 'monitor', 'observer'], - query: { - bool: { - filter: [ - { - term: { - 'monitor.id': monitorId, - }, - }, - ], - }, - }, - sort: [ - { - '@timestamp': { - order: 'desc', - }, - }, - ], - }, - }; - - const result = await callES('search', params); - - return result.hits.hits[0]?._source; -}; diff --git a/x-pack/plugins/uptime/server/lib/requests/index.ts b/x-pack/plugins/uptime/server/lib/requests/index.ts index 7225d329d3c7f..445adc3c15a93 100644 --- a/x-pack/plugins/uptime/server/lib/requests/index.ts +++ b/x-pack/plugins/uptime/server/lib/requests/index.ts @@ -7,7 +7,6 @@ export { getFilterBar, GetFilterBarParams } from './get_filter_bar'; export { getUptimeIndexPattern as getIndexPattern } from './get_index_pattern'; export { getLatestMonitor, GetLatestMonitorParams } from './get_latest_monitor'; -export { getMonitor, GetMonitorParams } from './get_monitor'; export { getMonitorDurationChart, GetMonitorChartsParams } from './get_monitor_duration'; export { getMonitorDetails, GetMonitorDetailsParams } from './get_monitor_details'; export { getMonitorLocations, GetMonitorLocationsParams } from './get_monitor_locations'; diff --git a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts index 6eeea5ba4c3e9..9d3fa5aa08aed 100644 --- a/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts +++ b/x-pack/plugins/uptime/server/lib/requests/uptime_requests.ts @@ -9,7 +9,6 @@ import { Ping, PingResults } from '../../../../../legacy/plugins/uptime/common/g import { GetFilterBarParams, GetLatestMonitorParams, - GetMonitorParams, GetMonitorChartsParams, GetMonitorDetailsParams, GetMonitorLocationsParams, @@ -39,7 +38,6 @@ export interface UptimeRequests { getFilterBar: ESQ; getIndexPattern: ESQ<{}, {}>; getLatestMonitor: ESQ; - getMonitor: ESQ; getMonitorDurationChart: ESQ; getMonitorDetails: ESQ; getMonitorLocations: ESQ; diff --git a/x-pack/plugins/uptime/server/rest_api/index.ts b/x-pack/plugins/uptime/server/rest_api/index.ts index 561997c3567d0..c84ea71037953 100644 --- a/x-pack/plugins/uptime/server/rest_api/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/index.ts @@ -11,7 +11,6 @@ import { createLogPageViewRoute } from './telemetry'; import { createGetSnapshotCount } from './snapshot'; import { UMRestApiRouteFactory } from './types'; import { - createGetMonitorRoute, createGetMonitorDetailsRoute, createGetMonitorLocationsRoute, createGetStatusBarRoute, @@ -31,7 +30,6 @@ export const restApiRoutes: UMRestApiRouteFactory[] = [ createGetIndexStatusRoute, createGetDynamicSettingsRoute, createPostDynamicSettingsRoute, - createGetMonitorRoute, createGetMonitorDetailsRoute, createGetMonitorLocationsRoute, createGetStatusBarRoute, diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/index.ts b/x-pack/plugins/uptime/server/rest_api/monitors/index.ts index 7f1f10081dc4e..7da717b50c149 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/index.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/index.ts @@ -6,4 +6,4 @@ export { createGetMonitorDetailsRoute } from './monitors_details'; export { createGetMonitorLocationsRoute } from './monitor_locations'; -export { createGetMonitorRoute, createGetStatusBarRoute } from './status'; +export { createGetStatusBarRoute } from './status'; diff --git a/x-pack/plugins/uptime/server/rest_api/monitors/status.ts b/x-pack/plugins/uptime/server/rest_api/monitors/status.ts index e1fcaf54f2824..9bccd64c4bd65 100644 --- a/x-pack/plugins/uptime/server/rest_api/monitors/status.ts +++ b/x-pack/plugins/uptime/server/rest_api/monitors/status.ts @@ -7,30 +7,7 @@ import { schema } from '@kbn/config-schema'; import { UMServerLibs } from '../../lib/lib'; import { UMRestApiRouteFactory } from '../types'; -import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants/rest_api'; - -export const createGetMonitorRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ - method: 'GET', - path: API_URLS.MONITOR_SELECTED, - - validate: { - query: schema.object({ - monitorId: schema.string(), - }), - }, - options: { - tags: ['access:uptime-read'], - }, - handler: async ({ callES, dynamicSettings }, _context, request, response): Promise => { - const { monitorId } = request.query; - - return response.ok({ - body: { - ...(await libs.requests.getMonitor({ callES, dynamicSettings, monitorId })), - }, - }); - }, -}); +import { API_URLS } from '../../../../../legacy/plugins/uptime/common/constants'; export const createGetStatusBarRoute: UMRestApiRouteFactory = (libs: UMServerLibs) => ({ method: 'GET', diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json index 1702cb2c21007..5d41cdf611824 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json +++ b/x-pack/test/api_integration/apis/uptime/rest/fixtures/monitor_latest_status.json @@ -1,89 +1,29 @@ { - "@timestamp": "2019-09-11T03:40:34.371Z", - "agent": { - "ephemeral_id": "412a92a8-2142-4b1a-a7a2-1afd32e12f85", - "hostname": "avc-x1x", - "id": "04e1d082-65bc-4929-8d65-d0768a2621c4", - "type": "heartbeat", - "version": "8.0.0" - }, - "ecs": { - "version": "1.1.0" - }, - "event": { - "dataset": "uptime" - }, - "host": { - "name": "avc-x1x" - }, - "http": { - "response": { - "body": { - "bytes": 3, - "hash": "27badc983df1780b60c2b3fa9d3a19a00e46aac798451f0febdca52920faaddf" - }, - "status_code": 200 - }, - "rtt": { - "content": { - "us": 57 - }, - "response_header": { - "us": 262 - }, - "total": { - "us": 20331 - }, - "validate": { - "us": 319 - }, - "write_request": { - "us": 82 - } - } - }, - "monitor": { - "check_group": "d76f0762-d445-11e9-88e3-3e80641b9c71", - "duration": { - "us": 24627 - }, - "id": "0002-up", - "ip": "127.0.0.1", - "name": "", - "status": "up", - "type": "http" - }, - "observer": { - "geo": { - "location": "37.926868, -78.024902", - "name": "mpls" - }, - "hostname": "avc-x1x" - }, - "resolve": { - "ip": "127.0.0.1", - "rtt": { - "us": 4218 - } - }, - "summary": { - "down": 0, - "up": 1 - }, - "tcp": { - "rtt": { - "connect": { - "us": 103 - } - } - }, - "timestamp": "2019-09-11T03:40:34.371Z", - "url": { - "domain": "localhost", - "full": "http://localhost:5678/pattern?r=200x1", - "path": "/pattern", - "port": 5678, - "query": "r=200x1", - "scheme": "http" - } + "timestamp": "2019-09-11T03:40:34.371Z", + "observer": { + "geo": { + "name": "mpls", + "location": "37.926868, -78.024902" + }, + "hostname": "avc-x1x" + }, + "monitor": { + "duration": { + "us": 24627 + }, + "ip": "127.0.0.1", + "name": "", + "check_group": "d76f0762-d445-11e9-88e3-3e80641b9c71", + "id": "0002-up", + "type": "http", + "status": "up" + }, + "url": { + "path": "/pattern", + "scheme": "http", + "port": 5678, + "domain": "localhost", + "query": "r=200x1", + "full": "http://localhost:5678/pattern?r=200x1" } +} \ No newline at end of file diff --git a/x-pack/test/api_integration/apis/uptime/rest/fixtures/selected_monitor.json b/x-pack/test/api_integration/apis/uptime/rest/fixtures/selected_monitor.json deleted file mode 100644 index d8367ea67052f..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/rest/fixtures/selected_monitor.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "monitor": { - "check_group": "d76f0762-d445-11e9-88e3-3e80641b9c71", - "duration": { - "us": 24627 - }, - "id": "0002-up", - "ip": "127.0.0.1", - "name": "", - "status": "up", - "type": "http" - }, - "observer": { - "geo": { - "location": "37.926868, -78.024902", - "name": "mpls" - }, - "hostname": "avc-x1x" - }, - "url": { - "domain": "localhost", - "full": "http://localhost:5678/pattern?r=200x1", - "path": "/pattern", - "port": 5678, - "query": "r=200x1", - "scheme": "http" - } -} diff --git a/x-pack/test/api_integration/apis/uptime/rest/index.ts b/x-pack/test/api_integration/apis/uptime/rest/index.ts index b2236e1bb6308..f89bec589847e 100644 --- a/x-pack/test/api_integration/apis/uptime/rest/index.ts +++ b/x-pack/test/api_integration/apis/uptime/rest/index.ts @@ -47,7 +47,6 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { before('load heartbeat data', async () => await esArchiver.load('uptime/full_heartbeat')); after('unload', async () => await esArchiver.unload('uptime/full_heartbeat')); loadTestFile(require.resolve('./monitor_latest_status')); - loadTestFile(require.resolve('./selected_monitor')); loadTestFile(require.resolve('./ping_histogram')); loadTestFile(require.resolve('./monitor_duration')); loadTestFile(require.resolve('./doc_count')); diff --git a/x-pack/test/api_integration/apis/uptime/rest/selected_monitor.ts b/x-pack/test/api_integration/apis/uptime/rest/selected_monitor.ts deleted file mode 100644 index ed034f58a5f59..0000000000000 --- a/x-pack/test/api_integration/apis/uptime/rest/selected_monitor.ts +++ /dev/null @@ -1,23 +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 { expectFixtureEql } from '../graphql/helpers/expect_fixture_eql'; -import { FtrProviderContext } from '../../../ftr_provider_context'; - -export default function({ getService }: FtrProviderContext) { - describe('get selected monitor by ID', () => { - const monitorId = '0002-up'; - - const supertest = getService('supertest'); - - it('returns the monitor for give ID', async () => { - const apiResponse = await supertest.get( - `/api/uptime/monitor/selected?monitorId=${monitorId}` - ); - expectFixtureEql(apiResponse.body, 'selected_monitor'); - }); - }); -} From a9be92b0bf2d18ef2c551059be80bf5b05622a64 Mon Sep 17 00:00:00 2001 From: Vadim Dalecky Date: Thu, 2 Apr 2020 09:18:40 +0200 Subject: [PATCH 15/17] ci: remove AppArch label from ProBot path-labeler (#62211) --- .github/paths-labeller.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/paths-labeller.yml b/.github/paths-labeller.yml index 4a498af6b1c88..3d35cd74e0718 100644 --- a/.github/paths-labeller.yml +++ b/.github/paths-labeller.yml @@ -1,13 +1,4 @@ --- - - "Team:AppArch": - - "src/plugins/bfetch/**/*.*" - - "src/plugins/dashboard_embeddable_container/**/*.*" - - "src/plugins/data/**/*.*" - - "src/plugins/embeddable/**/*.*" - - "src/plugins/expressions/**/*.*" - - "src/plugins/inspector/**/*.*" - - "src/plugins/ui_actions/**/*.*" - - "src/plugins/visualizations/**/*.*" - "Feature:Embedding": - "src/plugins/embeddable/**/*.*" - "src/plugins/dashboard_embeddable_container/**/*.*" From ee3f5309f8ce8de9a6679b2b0331015e918273e6 Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 2 Apr 2020 09:26:23 +0200 Subject: [PATCH 16/17] [Lens] fix error for minInterval>computedInterval for XYChart (#61931) --- .../xy_visualization/xy_expression.test.tsx | 310 ++++++++++++++---- .../public/xy_visualization/xy_expression.tsx | 9 +- 2 files changed, 251 insertions(+), 68 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx index cdc5fc2ff1c17..54abc2c2bb667 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.test.tsx @@ -18,6 +18,10 @@ import { } from '@elastic/charts'; import { xyChart, XYChart } from './xy_expression'; import { LensMultiTable } from '../types'; +import { + KibanaDatatable, + KibanaDatatableRow, +} from '../../../../../../src/plugins/expressions/public'; import React from 'react'; import { shallow } from 'enzyme'; import { XYArgs, LegendConfig, legendConfig, layerConfig, LayerArgs } from './types'; @@ -26,57 +30,61 @@ import { mountWithIntl } from 'test_utils/enzyme_helpers'; const executeTriggerActions = jest.fn(); +const createSampleDatatableWithRows = (rows: KibanaDatatableRow[]): KibanaDatatable => ({ + type: 'kibana_datatable', + columns: [ + { + id: 'a', + name: 'a', + formatHint: { id: 'number', params: { pattern: '0,0.000' } }, + }, + { id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } }, + { + id: 'c', + name: 'c', + formatHint: { id: 'string' }, + meta: { type: 'date-histogram', aggConfigParams: { interval: '10s' } }, + }, + { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, + ], + rows, +}); + +const sampleLayer: LayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'ordinal', + yScaleType: 'linear', + isHistogram: false, +}; + +const createArgsWithLayers = (layers: LayerArgs[] = [sampleLayer]): XYArgs => ({ + xTitle: '', + yTitle: '', + legend: { + type: 'lens_xy_legendConfig', + isVisible: false, + position: Position.Top, + }, + layers, +}); + function sampleArgs() { const data: LensMultiTable = { type: 'lens_multitable', tables: { - first: { - type: 'kibana_datatable', - columns: [ - { - id: 'a', - name: 'a', - formatHint: { id: 'number', params: { pattern: '0,0.000' } }, - }, - { id: 'b', name: 'b', formatHint: { id: 'number', params: { pattern: '000,0' } } }, - { - id: 'c', - name: 'c', - formatHint: { id: 'string' }, - meta: { type: 'date-histogram', aggConfigParams: { interval: '10s' } }, - }, - { id: 'd', name: 'ColD', formatHint: { id: 'string' } }, - ], - rows: [ - { a: 1, b: 2, c: 'I', d: 'Foo' }, - { a: 1, b: 5, c: 'J', d: 'Bar' }, - ], - }, + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 1, b: 5, c: 'J', d: 'Bar' }, + ]), }, }; - const args: XYArgs = { - xTitle: '', - yTitle: '', - legend: { - type: 'lens_xy_legendConfig', - isVisible: false, - position: Position.Top, - }, - layers: [ - { - layerId: 'first', - seriesType: 'line', - xAccessor: 'c', - accessors: ['a', 'b'], - splitAccessor: 'd', - columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', - xScaleType: 'ordinal', - yScaleType: 'linear', - isHistogram: false, - }, - ], - }; + const args: XYArgs = createArgsWithLayers(); return { data, args }; } @@ -158,35 +166,205 @@ describe('xy_expression', () => { expect(component.find(LineSeries)).toHaveLength(1); }); - test('it uses the full date range', () => { - const { data, args } = sampleArgs(); + describe('date range', () => { + const timeSampleLayer: LayerArgs = { + layerId: 'first', + seriesType: 'line', + xAccessor: 'c', + accessors: ['a', 'b'], + splitAccessor: 'd', + columnToLabel: '{"a": "Label A", "b": "Label B", "d": "Label D"}', + xScaleType: 'time', + yScaleType: 'linear', + isHistogram: false, + }; + const multiLayerArgs = createArgsWithLayers([ + timeSampleLayer, + { + ...timeSampleLayer, + layerId: 'second', + seriesType: 'bar', + xScaleType: 'time', + }, + ]); + test('it uses the full date range', () => { + const { data, args } = sampleArgs(); + + const component = shallow( + + ); + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); - const component = shallow( - - ); - expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + test('it generates correct xDomain for a layer with single value and a layer with no data (1-0) ', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([]), + }, + }; + + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": 10000, + } + `); + }); + + test('it generates correct xDomain for two layers with single value(1-1)', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([{ a: 10, b: 5, c: 'J', d: 'Bar' }]), + }, + }; + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` Object { "max": 1546491600000, "min": 1546405200000, "minInterval": 10000, } `); + }); + test('it generates correct xDomain for a layer with single value and layer with multiple value data (1-n)', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([{ a: 1, b: 2, c: 'I', d: 'Foo' }]), + second: createSampleDatatableWithRows([ + { a: 10, b: 5, c: 'J', d: 'Bar' }, + { a: 8, b: 5, c: 'K', d: 'Buzz' }, + ]), + }, + }; + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); + + test('it generates correct xDomain for 2 layers with multiple value data (n-n)', () => { + const data: LensMultiTable = { + type: 'lens_multitable', + tables: { + first: createSampleDatatableWithRows([ + { a: 1, b: 2, c: 'I', d: 'Foo' }, + { a: 8, b: 5, c: 'K', d: 'Buzz' }, + { a: 9, b: 7, c: 'L', d: 'Bar' }, + { a: 10, b: 2, c: 'G', d: 'Bear' }, + ]), + second: createSampleDatatableWithRows([ + { a: 10, b: 5, c: 'J', d: 'Bar' }, + { a: 8, b: 4, c: 'K', d: 'Fi' }, + { a: 1, b: 8, c: 'O', d: 'Pi' }, + ]), + }, + }; + const component = shallow( + + ); + + expect(component.find(Settings).prop('xDomain')).toMatchInlineSnapshot(` + Object { + "max": 1546491600000, + "min": 1546405200000, + "minInterval": undefined, + } + `); + }); }); test('it does not use date range if the x is not a time scale', () => { diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx index a7d4b2a217f37..f5798688badc5 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization/xy_expression.tsx @@ -211,14 +211,19 @@ export function XYChart({ const shouldRotate = isHorizontalChart(layers); const xTitle = (xAxisColumn && xAxisColumn.name) || args.xTitle; - const interval = parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval); + + // add minInterval only for single row value as it cannot be determined from dataset + + const minInterval = layers.every(layer => data.tables[layer.layerId].rows.length <= 1) + ? parseInterval(xAxisColumn?.meta?.aggConfigParams?.interval)?.asMilliseconds() + : undefined; const xDomain = data.dateRange && layers.every(l => l.xScaleType === 'time') ? { min: data.dateRange.fromDate.getTime(), max: data.dateRange.toDate.getTime(), - minInterval: interval?.asMilliseconds(), + minInterval, } : undefined; return ( From d0e7f98f97b359a652949ea80c886f0bd843d11d Mon Sep 17 00:00:00 2001 From: Marta Bondyra Date: Thu, 2 Apr 2020 11:15:49 +0200 Subject: [PATCH 17/17] [HomeApp] Set breadcrumbs when coming back from add data dir (#62186) --- src/plugins/home/public/application/components/home.js | 4 ++++ src/plugins/home/public/application/components/home.test.js | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/plugins/home/public/application/components/home.js b/src/plugins/home/public/application/components/home.js index 6d00b1c6a5d14..77cde6a574aec 100644 --- a/src/plugins/home/public/application/components/home.js +++ b/src/plugins/home/public/application/components/home.js @@ -36,6 +36,7 @@ import { EuiPageBody, EuiScreenReaderOnly, } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; import { Welcome } from './welcome'; import { getServices } from '../kibana_services'; @@ -69,6 +70,9 @@ export class Home extends Component { componentDidMount() { this._isMounted = true; this.fetchIsNewKibanaInstance(); + + const homeTitle = i18n.translate('home.breadcrumbs.homeTitle', { defaultMessage: 'Home' }); + getServices().chrome.setBreadcrumbs([{ text: homeTitle }]); } fetchIsNewKibanaInstance = async () => { diff --git a/src/plugins/home/public/application/components/home.test.js b/src/plugins/home/public/application/components/home.test.js index ca8297800b53e..99722df18e069 100644 --- a/src/plugins/home/public/application/components/home.test.js +++ b/src/plugins/home/public/application/components/home.test.js @@ -29,6 +29,9 @@ jest.mock('../kibana_services', () => ({ getBasePath: () => 'path', tutorialVariables: () => ({}), homeConfig: { disableWelcomeScreen: false }, + chrome: { + setBreadcrumbs: () => {}, + }, }), }));