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 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/**/*.*" diff --git a/package.json b/package.json index 6228dc3f1f6ff..d876c396e3e3b 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,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/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 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: () => {}, + }, }), })); 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'), 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 ( 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', ]; 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 ( 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/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/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/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} /> )} 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, }), 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/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/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}` : ''; 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=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/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 }, + ], + }, }); } 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/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'); - }); - }); -} 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) { diff --git a/yarn.lock b/yarn.lock index 9d76788079e46..a6b3154c89d05 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": @@ -6128,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" @@ -19622,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" @@ -20922,7 +20911,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== @@ -28903,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" @@ -32079,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"